@anton.andrusenko/shopify-mcp-admin 2.1.2 → 2.2.1
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/README.md +400 -18
- package/dist/{chunk-QXLLD2A7.js → chunk-5QMYOO4B.js} +20 -4
- package/dist/{chunk-UXI33LQD.js → chunk-JU5IFCVJ.js} +1 -1
- package/dist/chunk-LMFNHULG.js +14035 -0
- package/dist/chunk-PQKNBYJN.js +254 -0
- package/dist/dashboard/assets/index-BfNrQS4y.js +120 -0
- package/dist/dashboard/assets/index-BfNrQS4y.js.map +1 -0
- package/dist/dashboard/assets/index-HBHxyHsM.css +1 -0
- package/dist/dashboard/index.html +5 -2
- package/dist/dashboard/mcp-icon.svg +36 -0
- package/dist/index.js +5702 -16274
- package/dist/mcp-auth-F25V6FEY.js +24 -0
- package/dist/{security-XP6MXK5B.js → security-44M6F2QU.js} +17 -3
- package/dist/{store-FM7HCQVW.js → store-JK2ZU6DR.js} +2 -2
- package/dist/tools-HVUCP53D.js +82 -0
- package/package.json +4 -2
- package/dist/chunk-DCQTHXKI.js +0 -124
- package/dist/dashboard/assets/index-BSh6M640.js +0 -107
- package/dist/dashboard/assets/index-BSh6M640.js.map +0 -1
- package/dist/dashboard/assets/index-DLoVESj2.css +0 -1
- package/dist/mcp-auth-2WVQELV5.js +0 -16
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MCP_AUTH_ERRORS,
|
|
3
|
+
createJsonRpcError,
|
|
4
|
+
createMcpAuthMiddleware,
|
|
5
|
+
getWwwAuthenticateHeader,
|
|
6
|
+
isOAuthAccessToken,
|
|
7
|
+
parseBearerToken,
|
|
8
|
+
validateMcpApiKey,
|
|
9
|
+
validateMcpBearerToken,
|
|
10
|
+
validateOAuthAccessToken
|
|
11
|
+
} from "./chunk-PQKNBYJN.js";
|
|
12
|
+
import "./chunk-5QMYOO4B.js";
|
|
13
|
+
import "./chunk-EGGOXEIC.js";
|
|
14
|
+
export {
|
|
15
|
+
MCP_AUTH_ERRORS,
|
|
16
|
+
createJsonRpcError,
|
|
17
|
+
createMcpAuthMiddleware,
|
|
18
|
+
getWwwAuthenticateHeader,
|
|
19
|
+
isOAuthAccessToken,
|
|
20
|
+
parseBearerToken,
|
|
21
|
+
validateMcpApiKey,
|
|
22
|
+
validateMcpBearerToken,
|
|
23
|
+
validateOAuthAccessToken
|
|
24
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
log
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5QMYOO4B.js";
|
|
4
4
|
import "./chunk-EGGOXEIC.js";
|
|
5
5
|
|
|
6
6
|
// src/middleware/security.ts
|
|
@@ -12,7 +12,12 @@ var DEFAULT_SECURITY_OPTIONS = {
|
|
|
12
12
|
enableSecurityHeaders: true
|
|
13
13
|
};
|
|
14
14
|
var CORS_ALLOWED_METHODS = ["GET", "POST", "DELETE", "OPTIONS"];
|
|
15
|
-
var CORS_ALLOWED_HEADERS = [
|
|
15
|
+
var CORS_ALLOWED_HEADERS = [
|
|
16
|
+
"Content-Type",
|
|
17
|
+
"Authorization",
|
|
18
|
+
"Mcp-Session-Id",
|
|
19
|
+
"MCP-Protocol-Version"
|
|
20
|
+
];
|
|
16
21
|
var CORS_MAX_AGE = 86400;
|
|
17
22
|
var SECURITY_HEADERS = {
|
|
18
23
|
"X-Frame-Options": "DENY",
|
|
@@ -51,7 +56,16 @@ function createCorsMiddleware(options) {
|
|
|
51
56
|
// Allowed HTTP methods
|
|
52
57
|
methods: CORS_ALLOWED_METHODS,
|
|
53
58
|
// Allowed request headers
|
|
54
|
-
|
|
59
|
+
//
|
|
60
|
+
// IMPORTANT: For browser-based MCP clients (Claude custom connectors), the client may
|
|
61
|
+
// include additional non-simple headers (e.g. tracing or client metadata). If we
|
|
62
|
+
// hardcode allowedHeaders, CORS preflight can fail and the connector will remain
|
|
63
|
+
// "Disconnected" even though OAuth succeeds.
|
|
64
|
+
//
|
|
65
|
+
// The `cors` package will, by default, reflect `Access-Control-Request-Headers` from
|
|
66
|
+
// the preflight request when `allowedHeaders` is not explicitly set.
|
|
67
|
+
//
|
|
68
|
+
// We keep CORS_ALLOWED_HEADERS exported for documentation/tests, but do not enforce it.
|
|
55
69
|
// Headers exposed to browser JavaScript
|
|
56
70
|
exposedHeaders: opts.exposedHeaders,
|
|
57
71
|
// Allow credentials (cookies, authorization headers)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GID_PATTERN,
|
|
3
|
+
LOCALE_CODE_PATTERN,
|
|
4
|
+
articleIdSchema,
|
|
5
|
+
blogIdSchema,
|
|
6
|
+
clearRegisteredTools,
|
|
7
|
+
collectionIdSchema,
|
|
8
|
+
convertZodToJsonSchema,
|
|
9
|
+
createHandlerWithContext,
|
|
10
|
+
deriveDefaultAnnotations,
|
|
11
|
+
disableTool,
|
|
12
|
+
enableTool,
|
|
13
|
+
getAllRegisteredToolNames,
|
|
14
|
+
getRegisteredTools,
|
|
15
|
+
getToolByName,
|
|
16
|
+
getToolCount,
|
|
17
|
+
getToolNames,
|
|
18
|
+
gidSchema,
|
|
19
|
+
imageIdSchema,
|
|
20
|
+
inventoryItemIdSchema,
|
|
21
|
+
isToolEnabled,
|
|
22
|
+
isToolRegistered,
|
|
23
|
+
isValidToolName,
|
|
24
|
+
localeCodeSchema,
|
|
25
|
+
locationIdSchema,
|
|
26
|
+
marketIdSchema,
|
|
27
|
+
pageIdSchema,
|
|
28
|
+
productIdSchema,
|
|
29
|
+
redirectIdSchema,
|
|
30
|
+
registerAllTools,
|
|
31
|
+
registerContextAwareTool,
|
|
32
|
+
registerTool,
|
|
33
|
+
resetToolEnabledState,
|
|
34
|
+
setToolListChangedCallback,
|
|
35
|
+
validateInput,
|
|
36
|
+
validateToolName,
|
|
37
|
+
variantIdSchema,
|
|
38
|
+
webPresenceIdSchema,
|
|
39
|
+
wrapToolHandler
|
|
40
|
+
} from "./chunk-LMFNHULG.js";
|
|
41
|
+
import "./chunk-5QMYOO4B.js";
|
|
42
|
+
import "./chunk-EGGOXEIC.js";
|
|
43
|
+
export {
|
|
44
|
+
GID_PATTERN,
|
|
45
|
+
LOCALE_CODE_PATTERN,
|
|
46
|
+
articleIdSchema,
|
|
47
|
+
blogIdSchema,
|
|
48
|
+
clearRegisteredTools,
|
|
49
|
+
collectionIdSchema,
|
|
50
|
+
convertZodToJsonSchema,
|
|
51
|
+
createHandlerWithContext,
|
|
52
|
+
deriveDefaultAnnotations,
|
|
53
|
+
disableTool,
|
|
54
|
+
enableTool,
|
|
55
|
+
getAllRegisteredToolNames,
|
|
56
|
+
getRegisteredTools,
|
|
57
|
+
getToolByName,
|
|
58
|
+
getToolCount,
|
|
59
|
+
getToolNames,
|
|
60
|
+
gidSchema,
|
|
61
|
+
imageIdSchema,
|
|
62
|
+
inventoryItemIdSchema,
|
|
63
|
+
isToolEnabled,
|
|
64
|
+
isToolRegistered,
|
|
65
|
+
isValidToolName,
|
|
66
|
+
localeCodeSchema,
|
|
67
|
+
locationIdSchema,
|
|
68
|
+
marketIdSchema,
|
|
69
|
+
pageIdSchema,
|
|
70
|
+
productIdSchema,
|
|
71
|
+
redirectIdSchema,
|
|
72
|
+
registerAllTools,
|
|
73
|
+
registerContextAwareTool,
|
|
74
|
+
registerTool,
|
|
75
|
+
resetToolEnabledState,
|
|
76
|
+
setToolListChangedCallback,
|
|
77
|
+
validateInput,
|
|
78
|
+
validateToolName,
|
|
79
|
+
variantIdSchema,
|
|
80
|
+
webPresenceIdSchema,
|
|
81
|
+
wrapToolHandler
|
|
82
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anton.andrusenko/shopify-mcp-admin",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "MCP server for Shopify Admin API - enables AI agents to manage Shopify stores with 79 tools for products, inventory, collections, content, SEO, metafields, markets & translations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -35,7 +35,9 @@
|
|
|
35
35
|
"ci": "npm run lint && npm run typecheck && npm run test && npm run build",
|
|
36
36
|
"version:patch": "npm version patch --no-git-tag-version",
|
|
37
37
|
"version:minor": "npm version minor --no-git-tag-version",
|
|
38
|
-
"version:major": "npm version major --no-git-tag-version"
|
|
38
|
+
"version:major": "npm version major --no-git-tag-version",
|
|
39
|
+
"seed:oauth": "npx tsx scripts/seed-oauth-test-data.ts",
|
|
40
|
+
"db:studio": "npx prisma studio"
|
|
39
41
|
},
|
|
40
42
|
"keywords": [
|
|
41
43
|
"shopify",
|
package/dist/chunk-DCQTHXKI.js
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
log
|
|
3
|
-
} from "./chunk-QXLLD2A7.js";
|
|
4
|
-
|
|
5
|
-
// src/middleware/mcp-auth.ts
|
|
6
|
-
var MCP_AUTH_ERROR_CODES = {
|
|
7
|
-
AUTH_REQUIRED: -32001,
|
|
8
|
-
// Missing authentication
|
|
9
|
-
AUTH_INVALID: -32002,
|
|
10
|
-
// Invalid API key format or key
|
|
11
|
-
AUTH_REVOKED: -32003,
|
|
12
|
-
// API key has been revoked
|
|
13
|
-
AUTH_FORBIDDEN: -32004
|
|
14
|
-
// Tenant suspended or no access
|
|
15
|
-
};
|
|
16
|
-
function createJsonRpcError(code, message, hint) {
|
|
17
|
-
const error = {
|
|
18
|
-
jsonrpc: "2.0",
|
|
19
|
-
error: {
|
|
20
|
-
code,
|
|
21
|
-
message
|
|
22
|
-
},
|
|
23
|
-
id: null
|
|
24
|
-
};
|
|
25
|
-
if (hint) {
|
|
26
|
-
error.error.data = { hint };
|
|
27
|
-
}
|
|
28
|
-
return error;
|
|
29
|
-
}
|
|
30
|
-
function parseBearerToken(authHeader) {
|
|
31
|
-
if (!authHeader) {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
const parts = authHeader.split(" ");
|
|
35
|
-
if (parts.length !== 2 || parts[0].toLowerCase() !== "bearer") {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
const token = parts[1];
|
|
39
|
-
if (!token || token.trim() === "") {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
return token;
|
|
43
|
-
}
|
|
44
|
-
async function validateMcpApiKey(authHeader, apiKeyService, prisma) {
|
|
45
|
-
const token = parseBearerToken(authHeader);
|
|
46
|
-
if (!token) {
|
|
47
|
-
return {
|
|
48
|
-
valid: false,
|
|
49
|
-
error: "Authentication required",
|
|
50
|
-
httpStatus: 401
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
const validationResult = await apiKeyService.validate(token);
|
|
54
|
-
if (!validationResult.valid) {
|
|
55
|
-
const isRevoked = validationResult.error?.includes("revoked");
|
|
56
|
-
return {
|
|
57
|
-
valid: false,
|
|
58
|
-
error: validationResult.error || "Invalid API key",
|
|
59
|
-
httpStatus: isRevoked ? 403 : 401
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
const tenant = validationResult.tenant;
|
|
63
|
-
const tenantShops = await prisma.tenantShop.findMany({
|
|
64
|
-
where: {
|
|
65
|
-
tenantId: tenant.tenantId,
|
|
66
|
-
uninstalledAt: null
|
|
67
|
-
// Only active shops
|
|
68
|
-
},
|
|
69
|
-
select: {
|
|
70
|
-
shopDomain: true,
|
|
71
|
-
scopes: true
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
const allowedShops = tenantShops.map((shop) => shop.shopDomain);
|
|
75
|
-
const mcpTenantContext = {
|
|
76
|
-
tenantId: tenant.tenantId,
|
|
77
|
-
email: tenant.email,
|
|
78
|
-
defaultShop: validationResult.shop,
|
|
79
|
-
allowedShops
|
|
80
|
-
};
|
|
81
|
-
return {
|
|
82
|
-
valid: true,
|
|
83
|
-
tenant: mcpTenantContext
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
function createMcpAuthMiddleware(options) {
|
|
87
|
-
const { apiKeyService, prisma, isRemote } = options;
|
|
88
|
-
return async (req, res, next) => {
|
|
89
|
-
if (!isRemote) {
|
|
90
|
-
log.debug("[mcp-auth] Local mode: skipping API key validation");
|
|
91
|
-
next();
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
const authResult = await validateMcpApiKey(req.headers.authorization, apiKeyService, prisma);
|
|
95
|
-
if (!authResult.valid) {
|
|
96
|
-
log.debug(`[mcp-auth] Auth failed: ${authResult.error}`);
|
|
97
|
-
let errorCode;
|
|
98
|
-
let hint;
|
|
99
|
-
if (authResult.httpStatus === 401) {
|
|
100
|
-
errorCode = authResult.error === "Authentication required" ? MCP_AUTH_ERROR_CODES.AUTH_REQUIRED : MCP_AUTH_ERROR_CODES.AUTH_INVALID;
|
|
101
|
-
hint = "Include Authorization: Bearer sk_live_xxx header";
|
|
102
|
-
} else {
|
|
103
|
-
errorCode = authResult.error?.includes("revoked") ? MCP_AUTH_ERROR_CODES.AUTH_REVOKED : MCP_AUTH_ERROR_CODES.AUTH_FORBIDDEN;
|
|
104
|
-
hint = "API key has been revoked or tenant is inactive";
|
|
105
|
-
}
|
|
106
|
-
res.status(authResult.httpStatus || 401).json(createJsonRpcError(errorCode, authResult.error || "Authentication failed", hint));
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
req.mcpTenantContext = authResult.tenant;
|
|
110
|
-
log.debug(
|
|
111
|
-
`[mcp-auth] Auth successful for tenant: ${authResult.tenant?.tenantId?.substring(0, 8)}...`
|
|
112
|
-
);
|
|
113
|
-
next();
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
var MCP_AUTH_ERRORS = MCP_AUTH_ERROR_CODES;
|
|
117
|
-
|
|
118
|
-
export {
|
|
119
|
-
createJsonRpcError,
|
|
120
|
-
parseBearerToken,
|
|
121
|
-
validateMcpApiKey,
|
|
122
|
-
createMcpAuthMiddleware,
|
|
123
|
-
MCP_AUTH_ERRORS
|
|
124
|
-
};
|