@clawpify/skills 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/agent.js CHANGED
@@ -96,4 +96,3 @@ When the user asks about products, orders, customers, or any Shopify data, use t
96
96
  export {
97
97
  ShopifyAgent
98
98
  };
99
-
package/dist/auth.js CHANGED
@@ -54,4 +54,3 @@ export {
54
54
  getAccessToken,
55
55
  createAuthenticatedConfig
56
56
  };
57
-
package/dist/index.js CHANGED
@@ -1,17 +1,219 @@
1
- import {
2
- ShopifyAgent
3
- } from "./agent.js";
4
- import {
5
- ShopifyClient,
6
- createShopifyClient
7
- } from "./shopify.js";
8
- import {
9
- createAuthenticatedConfig,
10
- getAccessToken
11
- } from "./auth.js";
12
- import {
13
- loadSkills
14
- } from "./skills.js";
1
+ // src/shopify.ts
2
+ class ShopifyClient {
3
+ storeUrl;
4
+ accessToken;
5
+ apiVersion;
6
+ constructor(config) {
7
+ this.storeUrl = config.storeUrl.replace(/^https?:\/\//, "").replace(/\/$/, "");
8
+ this.accessToken = config.accessToken;
9
+ this.apiVersion = config.apiVersion ?? "2026-01";
10
+ }
11
+ async graphql(query, variables) {
12
+ const url = `https://${this.storeUrl}/admin/api/${this.apiVersion}/graphql.json`;
13
+ const response = await fetch(url, {
14
+ method: "POST",
15
+ headers: {
16
+ "Content-Type": "application/json",
17
+ "X-Shopify-Access-Token": this.accessToken
18
+ },
19
+ body: JSON.stringify({ query, variables })
20
+ });
21
+ if (!response.ok) {
22
+ const text = await response.text();
23
+ throw new Error(`Shopify API error (${response.status}): ${text}`);
24
+ }
25
+ return response.json();
26
+ }
27
+ }
28
+ function createShopifyClient() {
29
+ const storeUrl = process.env.SHOPIFY_STORE_URL;
30
+ const accessToken = process.env.SHOPIFY_ACCESS_TOKEN;
31
+ if (!storeUrl || !accessToken) {
32
+ throw new Error("Missing required environment variables: SHOPIFY_STORE_URL and SHOPIFY_ACCESS_TOKEN");
33
+ }
34
+ return new ShopifyClient({ storeUrl, accessToken });
35
+ }
36
+
37
+ // src/auth.ts
38
+ async function getAccessToken(config) {
39
+ const storeUrl = config.storeUrl.replace(/^https?:\/\//, "").replace(/\/$/, "");
40
+ const response = await fetch(`https://${storeUrl}/admin/oauth/access_token`, {
41
+ method: "POST",
42
+ headers: {
43
+ "Content-Type": "application/x-www-form-urlencoded"
44
+ },
45
+ body: new URLSearchParams({
46
+ grant_type: "client_credentials",
47
+ client_id: config.clientId,
48
+ client_secret: config.clientSecret
49
+ })
50
+ });
51
+ if (!response.ok) {
52
+ const text = await response.text();
53
+ throw new Error(`OAuth error (${response.status}): ${text}`);
54
+ }
55
+ const data = await response.json();
56
+ return {
57
+ accessToken: data.access_token,
58
+ scope: data.scope,
59
+ expiresIn: data.expires_in
60
+ };
61
+ }
62
+ async function createAuthenticatedConfig() {
63
+ const storeUrl = process.env.SHOPIFY_STORE_URL;
64
+ const directToken = process.env.SHOPIFY_ACCESS_TOKEN;
65
+ const clientId = process.env.SHOPIFY_CLIENT_ID;
66
+ const clientSecret = process.env.SHOPIFY_CLIENT_SECRET;
67
+ if (!storeUrl) {
68
+ throw new Error("Missing required environment variable: SHOPIFY_STORE_URL");
69
+ }
70
+ if (directToken) {
71
+ return {
72
+ storeUrl,
73
+ accessToken: directToken
74
+ };
75
+ }
76
+ if (clientId && clientSecret) {
77
+ const token = await getAccessToken({ storeUrl, clientId, clientSecret });
78
+ return {
79
+ storeUrl,
80
+ accessToken: token.accessToken,
81
+ scope: token.scope,
82
+ expiresIn: token.expiresIn
83
+ };
84
+ }
85
+ throw new Error(`Missing authentication credentials. Provide either:
86
+ ` + ` - SHOPIFY_ACCESS_TOKEN (direct token), or
87
+ ` + " - SHOPIFY_CLIENT_ID and SHOPIFY_CLIENT_SECRET (OAuth client credentials)");
88
+ }
89
+
90
+ // src/agent.ts
91
+ import Anthropic from "@anthropic-ai/sdk";
92
+ var SHOPIFY_GRAPHQL_TOOL = {
93
+ name: "shopify_graphql",
94
+ description: "Execute a GraphQL query against the Shopify Admin API. Use this to interact with products, orders, customers, inventory, and other Shopify resources.",
95
+ input_schema: {
96
+ type: "object",
97
+ properties: {
98
+ query: {
99
+ type: "string",
100
+ description: "The GraphQL query or mutation to execute"
101
+ },
102
+ variables: {
103
+ type: "object",
104
+ description: "Optional variables for the GraphQL query"
105
+ }
106
+ },
107
+ required: ["query"]
108
+ }
109
+ };
110
+
111
+ class ShopifyAgent {
112
+ anthropic;
113
+ shopify;
114
+ skillContent;
115
+ model;
116
+ constructor(config) {
117
+ this.anthropic = new Anthropic;
118
+ this.shopify = config.shopify;
119
+ this.skillContent = config.skillContent;
120
+ this.model = config.model ?? "claude-sonnet-4-5";
121
+ }
122
+ async chat(userMessage, conversationHistory = []) {
123
+ const systemPrompt = `You are a helpful Shopify assistant that can query and manage a Shopify store using the GraphQL Admin API.
124
+
125
+ ${this.skillContent}
126
+
127
+ When the user asks about products, orders, customers, or any Shopify data, use the shopify_graphql tool to fetch or modify the data. Always explain what you're doing and present results clearly.`;
128
+ const messages = [
129
+ ...conversationHistory,
130
+ { role: "user", content: userMessage }
131
+ ];
132
+ let response = await this.anthropic.messages.create({
133
+ model: this.model,
134
+ max_tokens: 4096,
135
+ system: systemPrompt,
136
+ tools: [SHOPIFY_GRAPHQL_TOOL],
137
+ messages
138
+ });
139
+ const assistantMessages = [];
140
+ while (response.stop_reason === "tool_use") {
141
+ const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
142
+ assistantMessages.push(...response.content);
143
+ const toolResults = [];
144
+ for (const toolUse of toolUseBlocks) {
145
+ if (toolUse.name === "shopify_graphql") {
146
+ const input = toolUse.input;
147
+ try {
148
+ const result = await this.shopify.graphql(input.query, input.variables);
149
+ toolResults.push({
150
+ type: "tool_result",
151
+ tool_use_id: toolUse.id,
152
+ content: JSON.stringify(result, null, 2)
153
+ });
154
+ } catch (error) {
155
+ toolResults.push({
156
+ type: "tool_result",
157
+ tool_use_id: toolUse.id,
158
+ content: `Error: ${error instanceof Error ? error.message : String(error)}`,
159
+ is_error: true
160
+ });
161
+ }
162
+ }
163
+ }
164
+ messages.push({ role: "assistant", content: assistantMessages.slice() });
165
+ messages.push({ role: "user", content: toolResults });
166
+ assistantMessages.length = 0;
167
+ response = await this.anthropic.messages.create({
168
+ model: this.model,
169
+ max_tokens: 4096,
170
+ system: systemPrompt,
171
+ tools: [SHOPIFY_GRAPHQL_TOOL],
172
+ messages
173
+ });
174
+ }
175
+ const textBlocks = response.content.filter((block) => block.type === "text");
176
+ const finalResponse = textBlocks.map((b) => b.text).join(`
177
+ `);
178
+ const updatedHistory = [
179
+ ...messages,
180
+ { role: "assistant", content: response.content }
181
+ ];
182
+ return { response: finalResponse, history: updatedHistory };
183
+ }
184
+ }
185
+
186
+ // src/skills.ts
187
+ import { readdir, readFile } from "node:fs/promises";
188
+ import { join } from "node:path";
189
+ import { fileURLToPath } from "node:url";
190
+ async function loadSkills(customDir) {
191
+ const baseDir = customDir ?? join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
192
+ const skillParts = [];
193
+ await collectMarkdown(baseDir, skillParts);
194
+ return skillParts.join(`
195
+
196
+ ---
197
+
198
+ `);
199
+ }
200
+ async function collectMarkdown(dir, parts) {
201
+ let entries;
202
+ try {
203
+ entries = await readdir(dir, { withFileTypes: true });
204
+ } catch {
205
+ return;
206
+ }
207
+ for (const entry of entries) {
208
+ const fullPath = join(dir, entry.name);
209
+ if (entry.isDirectory()) {
210
+ await collectMarkdown(fullPath, parts);
211
+ } else if (entry.name.endsWith(".md")) {
212
+ const content = await readFile(fullPath, "utf-8");
213
+ parts.push(content);
214
+ }
215
+ }
216
+ }
15
217
  export {
16
218
  loadSkills,
17
219
  getAccessToken,
@@ -1,14 +1,40 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // src/mcp-server.ts
4
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
- import {
7
- CallToolRequestSchema,
8
- ListResourcesRequestSchema,
9
- ReadResourceRequestSchema,
10
- ListToolsRequestSchema
11
- } from "@modelcontextprotocol/sdk/types.js";
3
+ // src/shopify.ts
4
+ class ShopifyClient {
5
+ storeUrl;
6
+ accessToken;
7
+ apiVersion;
8
+ constructor(config) {
9
+ this.storeUrl = config.storeUrl.replace(/^https?:\/\//, "").replace(/\/$/, "");
10
+ this.accessToken = config.accessToken;
11
+ this.apiVersion = config.apiVersion ?? "2026-01";
12
+ }
13
+ async graphql(query, variables) {
14
+ const url = `https://${this.storeUrl}/admin/api/${this.apiVersion}/graphql.json`;
15
+ const response = await fetch(url, {
16
+ method: "POST",
17
+ headers: {
18
+ "Content-Type": "application/json",
19
+ "X-Shopify-Access-Token": this.accessToken
20
+ },
21
+ body: JSON.stringify({ query, variables })
22
+ });
23
+ if (!response.ok) {
24
+ const text = await response.text();
25
+ throw new Error(`Shopify API error (${response.status}): ${text}`);
26
+ }
27
+ return response.json();
28
+ }
29
+ }
30
+ function createShopifyClient() {
31
+ const storeUrl = process.env.SHOPIFY_STORE_URL;
32
+ const accessToken = process.env.SHOPIFY_ACCESS_TOKEN;
33
+ if (!storeUrl || !accessToken) {
34
+ throw new Error("Missing required environment variables: SHOPIFY_STORE_URL and SHOPIFY_ACCESS_TOKEN");
35
+ }
36
+ return new ShopifyClient({ storeUrl, accessToken });
37
+ }
12
38
 
13
39
  // src/auth.ts
14
40
  async function getAccessToken(config) {
@@ -63,35 +89,15 @@ async function createAuthenticatedConfig() {
63
89
  ` + " - SHOPIFY_CLIENT_ID and SHOPIFY_CLIENT_SECRET (OAuth client credentials)");
64
90
  }
65
91
 
66
- // src/shopify.ts
67
- class ShopifyClient {
68
- storeUrl;
69
- accessToken;
70
- apiVersion;
71
- constructor(config) {
72
- this.storeUrl = config.storeUrl.replace(/^https?:\/\//, "").replace(/\/$/, "");
73
- this.accessToken = config.accessToken;
74
- this.apiVersion = config.apiVersion ?? "2026-01";
75
- }
76
- async graphql(query, variables) {
77
- const url = `https://${this.storeUrl}/admin/api/${this.apiVersion}/graphql.json`;
78
- const response = await fetch(url, {
79
- method: "POST",
80
- headers: {
81
- "Content-Type": "application/json",
82
- "X-Shopify-Access-Token": this.accessToken
83
- },
84
- body: JSON.stringify({ query, variables })
85
- });
86
- if (!response.ok) {
87
- const text = await response.text();
88
- throw new Error(`Shopify API error (${response.status}): ${text}`);
89
- }
90
- return response.json();
91
- }
92
- }
93
-
94
92
  // src/mcp-server.ts
93
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
94
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
95
+ import {
96
+ CallToolRequestSchema,
97
+ ListResourcesRequestSchema,
98
+ ReadResourceRequestSchema,
99
+ ListToolsRequestSchema
100
+ } from "@modelcontextprotocol/sdk/types.js";
95
101
  import { readdir, readFile } from "node:fs/promises";
96
102
  import { join, dirname } from "node:path";
97
103
  import { fileURLToPath } from "node:url";
package/dist/shopify.js CHANGED
@@ -37,4 +37,3 @@ export {
37
37
  createShopifyClient,
38
38
  ShopifyClient
39
39
  };
40
-
package/dist/skills.js CHANGED
@@ -32,4 +32,3 @@ async function collectMarkdown(dir, parts) {
32
32
  export {
33
33
  loadSkills
34
34
  };
35
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawpify/skills",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Shopify Agent SDK — query and manage Shopify stores via GraphQL Admin API with AI agents and MCP",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -46,12 +46,7 @@
46
46
  "clawpify"
47
47
  ],
48
48
  "scripts": {
49
- "build": "bun run build:clean && bun run build:js && bun run build:bin && bun run build:fix && bun run build:types",
50
- "build:clean": "rm -rf dist",
51
- "build:js": "bun build ./src/index.ts ./src/agent.ts ./src/shopify.ts ./src/auth.ts ./src/skills.ts --outdir ./dist --target node --format esm --splitting --external @anthropic-ai/sdk --external @modelcontextprotocol/sdk",
52
- "build:bin": "bun build ./src/mcp-server.ts --outdir ./dist --target node --format esm --external @anthropic-ai/sdk --external @modelcontextprotocol/sdk",
53
- "build:fix": "bun scripts/fix-exports.ts",
54
- "build:types": "tsc -p tsconfig.build.json",
49
+ "build": "rm -rf dist && bun build ./src/index.ts ./src/agent.ts ./src/shopify.ts ./src/auth.ts ./src/skills.ts ./src/mcp-server.ts --outdir ./dist --target node --format esm --external @anthropic-ai/sdk --external @modelcontextprotocol/sdk && tsc -p tsconfig.build.json",
55
50
  "prepublishOnly": "bun run build",
56
51
  "start": "bun run examples/server.ts"
57
52
  },