diff --git a/src/toolkits/toolkits/etsy/base.ts b/src/toolkits/toolkits/etsy/base.ts index d585bf7d..54a49acb 100644 --- a/src/toolkits/toolkits/etsy/base.ts +++ b/src/toolkits/toolkits/etsy/base.ts @@ -3,6 +3,7 @@ import { z } from "zod"; import { EtsyTools } from "./tools/tools"; import { getListings } from "@/toolkits/toolkits/etsy/tools/get-listings/base"; +import { getShopInfo } from "@/toolkits/toolkits/etsy/tools/get-shop-info/base"; import type { ToolkitConfig } from "@/toolkits/types"; @@ -14,6 +15,7 @@ export const baseEtsyToolkitConfig: ToolkitConfig< > = { tools: { [EtsyTools.getListings]: getListings, + [EtsyTools.getShopInfo]: getShopInfo, }, parameters: etsyParameters, }; diff --git a/src/toolkits/toolkits/etsy/client.tsx b/src/toolkits/toolkits/etsy/client.tsx index dc79b09b..85c9efe2 100644 --- a/src/toolkits/toolkits/etsy/client.tsx +++ b/src/toolkits/toolkits/etsy/client.tsx @@ -9,6 +9,7 @@ import { baseEtsyToolkitConfig } from "./base"; import { createClientToolkit } from "@/toolkits/create-toolkit"; import { getListingsClientConfig } from "@/toolkits/toolkits/etsy/tools/get-listings/client"; +import { getShopInfoClientConfig } from "@/toolkits/toolkits/etsy/tools/get-shop-info/client"; import { ToolkitGroups } from "@/toolkits/types"; import { EtsyTools } from "./tools/tools"; @@ -37,5 +38,6 @@ export const etsyClientToolkit = createClientToolkit( }, { [EtsyTools.getListings]: getListingsClientConfig, + [EtsyTools.getShopInfo]: getShopInfoClientConfig, }, ); diff --git a/src/toolkits/toolkits/etsy/server.ts b/src/toolkits/toolkits/etsy/server.ts index a2cc94f5..611875b7 100644 --- a/src/toolkits/toolkits/etsy/server.ts +++ b/src/toolkits/toolkits/etsy/server.ts @@ -10,11 +10,13 @@ import { EtsyTools } from "./tools/tools"; import { EtsySecurityDataStorage } from "./security-data-storage"; import { getListingsServerConfig } from "@/toolkits/toolkits/etsy/tools/get-listings/server"; +import { getShopInfoServerConfig } from "@/toolkits/toolkits/etsy/tools/get-shop-info/server"; export const etsyToolkitServer = createServerToolkit( baseEtsyToolkitConfig, "You have access to the Etsy toolkit for general account management. Currently, this toolkit provides:\n" + - "- **Get Listings**: Retrieves all listings and their image URLs associated with the shop associated with the signed-in user.\n\n", + "- **Get Listings**: Retrieves all listings and their image URLs associated with the shop associated with the signed-in user.\n" + + "- **Get Shop Info**: Retrieves the signed-in user's Etsy shop profile, status, and summary metrics.\n\n", async () => { const account = await api.accounts.getAccountByProvider("etsy"); @@ -33,6 +35,7 @@ export const etsyToolkitServer = createServerToolkit( return { [EtsyTools.getListings]: getListingsServerConfig(etsy), + [EtsyTools.getShopInfo]: getShopInfoServerConfig(etsy), }; }, ); diff --git a/src/toolkits/toolkits/etsy/tools/get-shop-info/base.ts b/src/toolkits/toolkits/etsy/tools/get-shop-info/base.ts new file mode 100644 index 00000000..54d64761 --- /dev/null +++ b/src/toolkits/toolkits/etsy/tools/get-shop-info/base.ts @@ -0,0 +1,13 @@ +import { z } from "zod"; +import { createBaseTool } from "@/toolkits/create-tool"; + +import type { IShop } from "etsy-ts"; + +export const getShopInfo = createBaseTool({ + description: + "Fetches the Etsy shop profile and summary metrics for the authenticated user.", + inputSchema: z.object({}), + outputSchema: z.object({ + shop: z.custom(), + }), +}); diff --git a/src/toolkits/toolkits/etsy/tools/get-shop-info/client.tsx b/src/toolkits/toolkits/etsy/tools/get-shop-info/client.tsx new file mode 100644 index 00000000..203798a6 --- /dev/null +++ b/src/toolkits/toolkits/etsy/tools/get-shop-info/client.tsx @@ -0,0 +1,111 @@ +import { + ExternalLink, + Heart, + Package, + ShoppingBag, + Star, + Store, +} from "lucide-react"; + +import type { ClientToolConfig } from "@/toolkits/types"; +import type { getShopInfo } from "./base"; +import type React from "react"; + +export const getShopInfoClientConfig: ClientToolConfig< + typeof getShopInfo.inputSchema.shape, + typeof getShopInfo.outputSchema.shape +> = { + CallComponent: ({ isPartial }) => ( +
+ + Fetching Etsy shop info + {isPartial && ...} +
+ ), + ResultComponent: ({ result: { shop } }) => ( +
+
+
+ +
+
+

+ {shop.shop_name ?? "Etsy shop"} +

+ {shop.title && ( +

+ {shop.title} +

+ )} +
+ {shop.is_vacation ? "Vacation mode" : "Open"} /{" "} + {shop.currency_code ?? "Currency unavailable"} +
+
+
+ +
+ + + + +
+ + {shop.url && ( + + Open shop + + + )} +
+ ), +}; + +const ShopMetric = ({ + icon: Icon, + label, + value, +}: { + icon: React.ComponentType<{ className?: string }>; + label: string; + value: string; +}) => ( +
+
+ + {label} +
+
{value}
+
+); + +const formatCount = (value?: number | null) => value?.toLocaleString() ?? "0"; + +const formatRating = (rating?: number | null, count?: number | null) => { + if (rating === undefined || rating === null) return "No reviews"; + + const reviewCount = count?.toLocaleString() ?? "0"; + + return `${rating.toFixed(1)} (${reviewCount})`; +}; diff --git a/src/toolkits/toolkits/etsy/tools/get-shop-info/server.ts b/src/toolkits/toolkits/etsy/tools/get-shop-info/server.ts new file mode 100644 index 00000000..c252c591 --- /dev/null +++ b/src/toolkits/toolkits/etsy/tools/get-shop-info/server.ts @@ -0,0 +1,36 @@ +import type { Etsy } from "etsy-ts"; + +import type { ServerToolConfig } from "@/toolkits/types"; +import type { getShopInfo } from "./base"; + +export const getShopInfoServerConfig = ( + etsy: Etsy, +): ServerToolConfig< + typeof getShopInfo.inputSchema.shape, + typeof getShopInfo.outputSchema.shape +> => { + return { + callback: async () => { + try { + const user = await etsy.User.getMe(); + const userId = user.data.user_id; + + if (!userId) throw new Error("Missing Etsy user ID"); + + const shop = await etsy.Shop.getShopByOwnerUserId(userId); + + if (!shop.data.shop_id) throw new Error("Missing Etsy shop ID"); + + return { + shop: shop.data, + }; + } catch (error) { + console.error("Etsy API error:", error); + throw new Error("Failed to fetch shop info from Etsy"); + } + }, + message: + "Successfully retrieved the Etsy shop info. The user is shown the response in the UI. Do not reiterate it. " + + "If you called this tool because the user asked a question, answer the question.", + }; +}; diff --git a/src/toolkits/toolkits/etsy/tools/tools.ts b/src/toolkits/toolkits/etsy/tools/tools.ts index 8af37ff8..589521f8 100644 --- a/src/toolkits/toolkits/etsy/tools/tools.ts +++ b/src/toolkits/toolkits/etsy/tools/tools.ts @@ -1,3 +1,4 @@ export enum EtsyTools { getListings = "get-listings", + getShopInfo = "get-shop-info", }