@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 +0 -1
- package/dist/auth.js +0 -1
- package/dist/index.js +216 -14
- package/dist/mcp-server.js +43 -37
- package/dist/shopify.js +0 -1
- package/dist/skills.js +0 -1
- package/package.json +2 -7
package/dist/agent.js
CHANGED
package/dist/auth.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,17 +1,219 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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,
|
package/dist/mcp-server.js
CHANGED
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// src/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
package/dist/skills.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clawpify/skills",
|
|
3
|
-
"version": "1.0.
|
|
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": "
|
|
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
|
},
|