@01.software/cli 0.7.1 → 0.8.0
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/index.js +2 -6
- package/dist/index.js.map +1 -1
- package/dist/mcp/{chunk-3ZSKJM43.js → chunk-45ZCPS57.js} +340 -292
- package/dist/mcp/chunk-45ZCPS57.js.map +1 -0
- package/dist/mcp/http.js +261 -120
- package/dist/mcp/http.js.map +1 -1
- package/dist/mcp/stdio.js +1 -1
- package/dist/mcp/vercel.js +602 -415
- package/package.json +3 -3
- package/dist/mcp/chunk-3ZSKJM43.js.map +0 -1
package/dist/mcp/http.js
CHANGED
|
@@ -1,30 +1,214 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createServer,
|
|
3
|
+
mcpServicePublicJwks,
|
|
3
4
|
requestContext
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-45ZCPS57.js";
|
|
5
6
|
|
|
6
7
|
// src/transport/http.ts
|
|
7
8
|
import { createServer as createServer2 } from "http";
|
|
8
9
|
|
|
9
10
|
// src/handler.ts
|
|
10
11
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
12
|
+
import {
|
|
13
|
+
MCP_OAUTH_ISSUER as MCP_OAUTH_ISSUER2,
|
|
14
|
+
MCP_PROTECTED_RESOURCE_METADATA_PATH,
|
|
15
|
+
MCP_RESOURCE_AUDIENCE as MCP_RESOURCE_AUDIENCE2,
|
|
16
|
+
MCP_SCOPES as MCP_SCOPES2
|
|
17
|
+
} from "@01.software/auth-contracts";
|
|
11
18
|
|
|
12
19
|
// src/auth.ts
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
import { createPublicKey, verify as verifySignature } from "crypto";
|
|
21
|
+
import {
|
|
22
|
+
MCP_OAUTH_ISSUER,
|
|
23
|
+
MCP_RESOURCE_AUDIENCE,
|
|
24
|
+
MCP_SCOPES,
|
|
25
|
+
MCP_TENANT_CLAIM,
|
|
26
|
+
MCP_TENANT_ROLE_CLAIM
|
|
27
|
+
} from "@01.software/auth-contracts";
|
|
28
|
+
var ALLOWED_ALGORITHMS = /* @__PURE__ */ new Set(["RS256", "ES256"]);
|
|
29
|
+
var DEFAULT_CLOCK_SKEW_SECONDS = 30;
|
|
30
|
+
var DEFAULT_JWKS_URI = `${MCP_OAUTH_ISSUER}/.well-known/jwks.json`;
|
|
31
|
+
var MAX_ACCESS_TOKEN_LIFETIME_SECONDS = 300;
|
|
32
|
+
function invalid(errorDescription) {
|
|
33
|
+
return { valid: false, error: "invalid_token", errorDescription };
|
|
34
|
+
}
|
|
35
|
+
function isProduction() {
|
|
36
|
+
return process.env.NODE_ENV === "production";
|
|
37
|
+
}
|
|
38
|
+
function insufficientScope(errorDescription) {
|
|
39
|
+
return { valid: false, error: "insufficient_scope", errorDescription };
|
|
40
|
+
}
|
|
41
|
+
function decodeBase64UrlJson(value) {
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
16
46
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
47
|
+
}
|
|
48
|
+
function parseScopes(payload) {
|
|
49
|
+
const rawScopes = typeof payload.scope === "string" ? payload.scope.split(/\s+/).filter(Boolean) : Array.isArray(payload.scp) ? payload.scp : [];
|
|
50
|
+
return rawScopes.filter(
|
|
51
|
+
(scope) => scope === MCP_SCOPES.read || scope === MCP_SCOPES.write
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
function audienceMatches(aud, expected) {
|
|
55
|
+
if (typeof aud === "string") return aud === expected;
|
|
56
|
+
return Array.isArray(aud) && aud.includes(expected);
|
|
57
|
+
}
|
|
58
|
+
function verifyJwtSignature(alg, jwk, signingInput, signature) {
|
|
59
|
+
const key = createPublicKey({ key: jwk, format: "jwk" });
|
|
60
|
+
const input = Buffer.from(signingInput);
|
|
61
|
+
if (alg === "RS256") {
|
|
62
|
+
return verifySignature("RSA-SHA256", input, key, signature);
|
|
22
63
|
}
|
|
23
|
-
|
|
64
|
+
if (alg === "ES256") {
|
|
65
|
+
return verifySignature("SHA256", input, { key, dsaEncoding: "ieee-p1363" }, signature);
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
var cachedRemoteJwks = null;
|
|
70
|
+
function jwksUriFor(options) {
|
|
71
|
+
const raw = isProduction() ? DEFAULT_JWKS_URI : options.jwksUri ?? DEFAULT_JWKS_URI;
|
|
72
|
+
let url;
|
|
73
|
+
try {
|
|
74
|
+
url = new URL(raw);
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
return url.toString();
|
|
82
|
+
}
|
|
83
|
+
async function remoteJwks(options = {}, forceRefresh = false) {
|
|
84
|
+
const uri = jwksUriFor(options);
|
|
85
|
+
if (!uri) return null;
|
|
86
|
+
const now = Date.now();
|
|
87
|
+
if (!forceRefresh && cachedRemoteJwks && cachedRemoteJwks.uri === uri && cachedRemoteJwks.expiresAt > now) {
|
|
88
|
+
return cachedRemoteJwks.jwks;
|
|
89
|
+
}
|
|
90
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
91
|
+
let response;
|
|
92
|
+
try {
|
|
93
|
+
response = await fetchImpl(uri, {
|
|
94
|
+
headers: { Accept: "application/json" }
|
|
95
|
+
});
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
if (!response.ok) return null;
|
|
100
|
+
const parsed = await response.json().catch(() => null);
|
|
101
|
+
if (!parsed || !Array.isArray(parsed.keys)) return null;
|
|
102
|
+
cachedRemoteJwks = {
|
|
103
|
+
expiresAt: now + 3e5,
|
|
104
|
+
jwks: parsed,
|
|
105
|
+
uri
|
|
106
|
+
};
|
|
107
|
+
return parsed;
|
|
108
|
+
}
|
|
109
|
+
function validateAccessToken(token, options = {}) {
|
|
110
|
+
const parts = token.split(".");
|
|
111
|
+
if (parts.length !== 3 || parts.some((part) => part.length === 0)) {
|
|
112
|
+
return invalid("Bearer token must be a compact JWT");
|
|
113
|
+
}
|
|
114
|
+
const [encodedHeader, encodedPayload, encodedSignature] = parts;
|
|
115
|
+
const header = decodeBase64UrlJson(encodedHeader);
|
|
116
|
+
const payload = decodeBase64UrlJson(encodedPayload);
|
|
117
|
+
if (!header || !payload) return invalid("Bearer token contains invalid JSON");
|
|
118
|
+
if (typeof header.alg !== "string" || !ALLOWED_ALGORITHMS.has(header.alg)) {
|
|
119
|
+
return invalid("Bearer token uses an unsupported signing algorithm");
|
|
120
|
+
}
|
|
121
|
+
if (typeof header.kid !== "string" || header.kid.length === 0) {
|
|
122
|
+
return invalid("Bearer token is missing kid");
|
|
123
|
+
}
|
|
124
|
+
const jwks = options.jwks;
|
|
125
|
+
if (!jwks) return invalid("JWKS is not configured");
|
|
126
|
+
const jwk = jwks.keys.find((key) => key.kid === header.kid);
|
|
127
|
+
if (!jwk) return invalid("Bearer token kid is unknown");
|
|
128
|
+
const signingInput = `${encodedHeader}.${encodedPayload}`;
|
|
129
|
+
const signature = Buffer.from(encodedSignature, "base64url");
|
|
130
|
+
if (!verifyJwtSignature(header.alg, jwk, signingInput, signature)) {
|
|
131
|
+
return invalid("Bearer token signature is invalid");
|
|
132
|
+
}
|
|
133
|
+
const issuer = options.issuer ?? MCP_OAUTH_ISSUER;
|
|
134
|
+
if (payload.iss !== issuer) return invalid("Bearer token issuer is invalid");
|
|
135
|
+
const audience = options.audience ?? MCP_RESOURCE_AUDIENCE;
|
|
136
|
+
if (!audienceMatches(payload.aud, audience)) {
|
|
137
|
+
return invalid("Bearer token audience is invalid");
|
|
138
|
+
}
|
|
139
|
+
if (typeof payload.iat !== "number") return invalid("Bearer token is missing iat");
|
|
140
|
+
if (typeof payload.exp !== "number") return invalid("Bearer token is missing exp");
|
|
141
|
+
if (payload.exp <= payload.iat) {
|
|
142
|
+
return invalid("Bearer token lifetime is invalid");
|
|
143
|
+
}
|
|
144
|
+
if (payload.exp - payload.iat > MAX_ACCESS_TOKEN_LIFETIME_SECONDS) {
|
|
145
|
+
return invalid("Bearer token lifetime exceeds 300 seconds");
|
|
146
|
+
}
|
|
147
|
+
const nowSeconds = Math.floor((options.now ?? /* @__PURE__ */ new Date()).getTime() / 1e3);
|
|
148
|
+
const leeway = options.clockSkewSeconds ?? DEFAULT_CLOCK_SKEW_SECONDS;
|
|
149
|
+
if (payload.iat > nowSeconds + leeway) {
|
|
150
|
+
return invalid("Bearer token iat is too far in the future");
|
|
151
|
+
}
|
|
152
|
+
if (typeof payload.nbf === "number" && payload.nbf > nowSeconds + leeway) {
|
|
153
|
+
return invalid("Bearer token is not yet valid");
|
|
154
|
+
}
|
|
155
|
+
if (payload.exp < nowSeconds - leeway) return invalid("Bearer token is expired");
|
|
156
|
+
const tenantId = payload[MCP_TENANT_CLAIM];
|
|
157
|
+
if (typeof tenantId !== "string" || tenantId.length === 0) {
|
|
158
|
+
return invalid("Bearer token tenant_id claim is invalid");
|
|
159
|
+
}
|
|
160
|
+
const tenantRole = payload[MCP_TENANT_ROLE_CLAIM];
|
|
161
|
+
if (tenantRole !== "tenant-admin" && tenantRole !== "tenant-editor" && tenantRole !== "tenant-viewer") {
|
|
162
|
+
return invalid("Bearer token tenant_role claim is invalid");
|
|
163
|
+
}
|
|
164
|
+
if (typeof payload.sub !== "string" || payload.sub.length === 0) {
|
|
165
|
+
return invalid("Bearer token subject claim is invalid");
|
|
166
|
+
}
|
|
167
|
+
const scopes = parseScopes(payload);
|
|
168
|
+
const requiredScopes = options.requiredScopes ?? [MCP_SCOPES.read];
|
|
169
|
+
const missingScope = requiredScopes.find((scope) => !scopes.includes(scope));
|
|
170
|
+
if (missingScope) return insufficientScope(`Bearer token is missing ${missingScope}`);
|
|
171
|
+
return {
|
|
172
|
+
valid: true,
|
|
173
|
+
context: {
|
|
174
|
+
tenantId,
|
|
175
|
+
principalId: payload.sub,
|
|
176
|
+
scopes,
|
|
177
|
+
tenantRole,
|
|
178
|
+
authMode: "oauth"
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function shouldRefreshRemoteJwks(result) {
|
|
183
|
+
return !result.valid && (result.errorDescription === "Bearer token kid is unknown" || result.errorDescription === "Bearer token signature is invalid");
|
|
184
|
+
}
|
|
185
|
+
async function validateBearerAuthorizationHeaderAsync(authorization, options) {
|
|
186
|
+
if (!authorization) return invalid("Authorization Bearer token is required");
|
|
187
|
+
const match = authorization.match(/^Bearer\s+(.+)$/i);
|
|
188
|
+
if (!match) return invalid("Authorization header must use Bearer");
|
|
189
|
+
if (options?.jwks) {
|
|
190
|
+
return validateAccessToken(match[1], options);
|
|
191
|
+
}
|
|
192
|
+
const jwks = await remoteJwks(options);
|
|
193
|
+
const result = validateAccessToken(match[1], {
|
|
194
|
+
...options,
|
|
195
|
+
jwks: jwks ?? void 0
|
|
196
|
+
});
|
|
197
|
+
if (shouldRefreshRemoteJwks(result)) {
|
|
198
|
+
const refreshedJwks = await remoteJwks(options, true);
|
|
199
|
+
if (refreshedJwks) {
|
|
200
|
+
return validateAccessToken(match[1], {
|
|
201
|
+
...options,
|
|
202
|
+
jwks: refreshedJwks
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
24
207
|
}
|
|
25
208
|
|
|
26
209
|
// src/handler.ts
|
|
27
210
|
var MAX_REQUEST_BODY_BYTES = 1024 * 1024;
|
|
211
|
+
var MCP_ALLOWED_BROWSER_ORIGIN = "https://01.software";
|
|
28
212
|
var METHOD_NOT_ALLOWED = JSON.stringify({
|
|
29
213
|
jsonrpc: "2.0",
|
|
30
214
|
error: {
|
|
@@ -34,11 +218,11 @@ var METHOD_NOT_ALLOWED = JSON.stringify({
|
|
|
34
218
|
id: null
|
|
35
219
|
});
|
|
36
220
|
function setCors(res) {
|
|
37
|
-
res.setHeader("Access-Control-Allow-Origin",
|
|
221
|
+
res.setHeader("Access-Control-Allow-Origin", MCP_ALLOWED_BROWSER_ORIGIN);
|
|
38
222
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
39
223
|
res.setHeader(
|
|
40
224
|
"Access-Control-Allow-Headers",
|
|
41
|
-
"Content-Type,
|
|
225
|
+
"Authorization, Content-Type, mcp-session-id"
|
|
42
226
|
);
|
|
43
227
|
res.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id, Mcp-Protocol-Version");
|
|
44
228
|
}
|
|
@@ -68,70 +252,35 @@ var HOME_PAGE = `01.software MCP Server
|
|
|
68
252
|
======================
|
|
69
253
|
|
|
70
254
|
MCP server for AI agents to interact with the 01.software API.
|
|
71
|
-
|
|
255
|
+
Connect over HTTP with OAuth, or use the local CLI stdio transport for full
|
|
256
|
+
server-key operations.
|
|
72
257
|
|
|
73
258
|
|
|
74
259
|
Authentication
|
|
75
260
|
--------------
|
|
76
261
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
x-publishable-key publishable key for routing, rate limits, and quota enforcement
|
|
80
|
-
|
|
81
|
-
Use x-client-key as a legacy alias for x-publishable-key.
|
|
82
|
-
|
|
83
|
-
Accepted formats:
|
|
84
|
-
sk01_{40hex} tenant API key (Console > Settings > API Keys)
|
|
85
|
-
pat01_{40hex} personal access token (user-scoped local workflow)
|
|
86
|
-
pk01_{...} publishable key
|
|
87
|
-
|
|
88
|
-
export SOFTWARE_PUBLISHABLE_KEY=pk01_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
89
|
-
export SOFTWARE_SECRET_KEY=sk01_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
90
|
-
|
|
262
|
+
HTTP MCP uses OAuth discovery and Authorization Code + PKCE.
|
|
263
|
+
Clients should connect to:
|
|
91
264
|
|
|
92
265
|
Connect
|
|
93
266
|
-------
|
|
94
267
|
|
|
95
268
|
Claude Code:
|
|
96
269
|
|
|
97
|
-
claude mcp add --transport http
|
|
98
|
-
--header "x-api-key: $SOFTWARE_SECRET_KEY" \\
|
|
99
|
-
--header "x-publishable-key: $SOFTWARE_PUBLISHABLE_KEY" \\
|
|
100
|
-
01software https://mcp.01.software/mcp
|
|
270
|
+
claude mcp add --transport http 01software https://mcp.01.software/mcp
|
|
101
271
|
|
|
102
|
-
Codex (.codex/config.toml
|
|
272
|
+
Codex (.codex/config.toml):
|
|
103
273
|
|
|
104
274
|
[mcp_servers.01software]
|
|
105
275
|
url = "https://mcp.01.software/mcp"
|
|
106
276
|
|
|
107
|
-
|
|
108
|
-
x-api-key = "SOFTWARE_SECRET_KEY"
|
|
109
|
-
x-publishable-key = "SOFTWARE_PUBLISHABLE_KEY"
|
|
277
|
+
Cursor / Claude Desktop / other JSON clients:
|
|
110
278
|
|
|
111
|
-
// or .mcp.json
|
|
112
279
|
{
|
|
113
280
|
"mcpServers": {
|
|
114
281
|
"01software": {
|
|
115
282
|
"type": "http",
|
|
116
|
-
"url": "https://mcp.01.software/mcp"
|
|
117
|
-
"headers": {
|
|
118
|
-
"x-api-key": "\${env:SOFTWARE_SECRET_KEY}",
|
|
119
|
-
"x-publishable-key": "\${env:SOFTWARE_PUBLISHABLE_KEY}"
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
Cursor (.cursor/mcp.json):
|
|
126
|
-
|
|
127
|
-
{
|
|
128
|
-
"mcpServers": {
|
|
129
|
-
"01software": {
|
|
130
|
-
"url": "https://mcp.01.software/mcp",
|
|
131
|
-
"headers": {
|
|
132
|
-
"x-api-key": "\${env:SOFTWARE_SECRET_KEY}",
|
|
133
|
-
"x-publishable-key": "\${env:SOFTWARE_PUBLISHABLE_KEY}"
|
|
134
|
-
}
|
|
283
|
+
"url": "https://mcp.01.software/mcp"
|
|
135
284
|
}
|
|
136
285
|
}
|
|
137
286
|
}
|
|
@@ -141,40 +290,7 @@ Windsurf (~/.codeium/windsurf/mcp_config.json):
|
|
|
141
290
|
{
|
|
142
291
|
"mcpServers": {
|
|
143
292
|
"01software": {
|
|
144
|
-
"serverUrl": "https://mcp.01.software/mcp"
|
|
145
|
-
"headers": {
|
|
146
|
-
"x-api-key": "\${env:SOFTWARE_SECRET_KEY}",
|
|
147
|
-
"x-publishable-key": "\${env:SOFTWARE_PUBLISHABLE_KEY}"
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
VS Code (.vscode/mcp.json):
|
|
154
|
-
|
|
155
|
-
{
|
|
156
|
-
"servers": {
|
|
157
|
-
"01software": {
|
|
158
|
-
"type": "http",
|
|
159
|
-
"url": "https://mcp.01.software/mcp",
|
|
160
|
-
"headers": {
|
|
161
|
-
"x-api-key": "\${input:01software-api-key}",
|
|
162
|
-
"x-publishable-key": "\${input:01software-publishable-key}"
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
Claude Desktop (claude_desktop_config.json):
|
|
169
|
-
|
|
170
|
-
{
|
|
171
|
-
"mcpServers": {
|
|
172
|
-
"01software": {
|
|
173
|
-
"url": "https://mcp.01.software/mcp",
|
|
174
|
-
"headers": {
|
|
175
|
-
"x-api-key": "\${env:SOFTWARE_SECRET_KEY}",
|
|
176
|
-
"x-publishable-key": "\${env:SOFTWARE_PUBLISHABLE_KEY}"
|
|
177
|
-
}
|
|
293
|
+
"serverUrl": "https://mcp.01.software/mcp"
|
|
178
294
|
}
|
|
179
295
|
}
|
|
180
296
|
}
|
|
@@ -182,29 +298,23 @@ Claude Desktop (claude_desktop_config.json):
|
|
|
182
298
|
CLI (stdio):
|
|
183
299
|
|
|
184
300
|
npx @01.software/cli mcp
|
|
185
|
-
# Reads SOFTWARE_SECRET_KEY=sk01_... or pat01_...
|
|
186
|
-
# Also reads SOFTWARE_PUBLISHABLE_KEY for CDN routing, rate limits, and quota enforcement
|
|
187
|
-
|
|
188
|
-
Security: never commit raw sk01_... or pat01_... tokens to repo-local MCP
|
|
189
|
-
config files. Prefer client secret prompts, environment interpolation, OS secret
|
|
190
|
-
managers, or ignored local files. Avoid passing real tokens directly on
|
|
191
|
-
shared-machine command lines because shell history and process listings can
|
|
192
|
-
expose them.
|
|
193
301
|
|
|
194
302
|
|
|
195
|
-
Tools
|
|
196
|
-
|
|
303
|
+
HTTP OAuth Tools
|
|
304
|
+
----------------
|
|
197
305
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
Returns create-return, update-return, return-with-refund
|
|
201
|
-
Cart add-cart-item, update-cart-item, remove-cart-item, clear-cart, apply-discount, remove-discount
|
|
202
|
-
Validation validate-discount, calculate-shipping
|
|
203
|
-
Products stock-check
|
|
204
|
-
Schema get-collection-schema
|
|
205
|
-
Context get-tenant-context
|
|
306
|
+
Schema get-collection-schema
|
|
307
|
+
Context get-tenant-context
|
|
206
308
|
Field Config list-configurable-fields, update-field-config
|
|
207
|
-
Guidance
|
|
309
|
+
Guidance sdk-get-recipe, sdk-search-docs, sdk-get-auth-setup, sdk-get-collection-pattern
|
|
310
|
+
|
|
311
|
+
Full Tool Surface
|
|
312
|
+
-----------------
|
|
313
|
+
|
|
314
|
+
The local CLI stdio transport exposes CRUD, commerce, cart, validation, product,
|
|
315
|
+
schema, context, field-config, and guidance tools:
|
|
316
|
+
|
|
317
|
+
npx @01.software/cli mcp
|
|
208
318
|
|
|
209
319
|
Prompts (4): sdk-usage-guide, collection-query-help, order-flow-guide, feature-setup-guide
|
|
210
320
|
Resources (12): config, collections-schema, getting-started, guides, api, query-builder, react-query, server-api, customer-auth, browser-vs-server, file-upload, webhook
|
|
@@ -218,6 +328,19 @@ SDK https://01.software/docs/sdk/client
|
|
|
218
328
|
API Reference https://01.software/docs/api/rest-api
|
|
219
329
|
Console https://console.01.software
|
|
220
330
|
`;
|
|
331
|
+
var PROTECTED_RESOURCE_METADATA = JSON.stringify({
|
|
332
|
+
resource: MCP_RESOURCE_AUDIENCE2,
|
|
333
|
+
authorization_servers: [MCP_OAUTH_ISSUER2],
|
|
334
|
+
scopes_supported: [MCP_SCOPES2.read, MCP_SCOPES2.write]
|
|
335
|
+
});
|
|
336
|
+
var SERVICE_JWKS_PATH = "/.well-known/service-jwks.json";
|
|
337
|
+
function writeOAuthError(res, status, error, description) {
|
|
338
|
+
res.writeHead(status, {
|
|
339
|
+
"Content-Type": "application/json",
|
|
340
|
+
"WWW-Authenticate": `Bearer resource_metadata="${MCP_PROTECTED_RESOURCE_METADATA_PATH}", error="${error}", error_description="${description}"`
|
|
341
|
+
});
|
|
342
|
+
res.end(JSON.stringify({ error, error_description: description }));
|
|
343
|
+
}
|
|
221
344
|
async function handler(req, res) {
|
|
222
345
|
setCors(res);
|
|
223
346
|
if (req.method === "OPTIONS") {
|
|
@@ -226,6 +349,30 @@ async function handler(req, res) {
|
|
|
226
349
|
return;
|
|
227
350
|
}
|
|
228
351
|
if (req.method === "GET") {
|
|
352
|
+
const pathname = new URL(req.url ?? "/", MCP_RESOURCE_AUDIENCE2).pathname;
|
|
353
|
+
if (pathname === MCP_PROTECTED_RESOURCE_METADATA_PATH) {
|
|
354
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
355
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
356
|
+
res.end(PROTECTED_RESOURCE_METADATA);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
if (pathname === SERVICE_JWKS_PATH) {
|
|
360
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
361
|
+
try {
|
|
362
|
+
res.writeHead(200, {
|
|
363
|
+
"Cache-Control": "public, max-age=60",
|
|
364
|
+
"Content-Type": "application/json"
|
|
365
|
+
});
|
|
366
|
+
res.end(JSON.stringify(mcpServicePublicJwks()));
|
|
367
|
+
} catch {
|
|
368
|
+
res.writeHead(503, {
|
|
369
|
+
"Cache-Control": "no-store",
|
|
370
|
+
"Content-Type": "application/json"
|
|
371
|
+
});
|
|
372
|
+
res.end(JSON.stringify({ keys: [] }));
|
|
373
|
+
}
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
229
376
|
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
|
|
230
377
|
res.end(HOME_PAGE);
|
|
231
378
|
return;
|
|
@@ -235,20 +382,14 @@ async function handler(req, res) {
|
|
|
235
382
|
res.end(METHOD_NOT_ALLOWED);
|
|
236
383
|
return;
|
|
237
384
|
}
|
|
238
|
-
const
|
|
239
|
-
|
|
385
|
+
const auth = await validateBearerAuthorizationHeaderAsync(
|
|
386
|
+
getHeaderValue(req.headers, "authorization")
|
|
387
|
+
);
|
|
240
388
|
if (!auth.valid) {
|
|
241
|
-
res
|
|
242
|
-
res.end(JSON.stringify({ error: auth.error }));
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
const publishableKey = getHeaderValue(req.headers, "x-publishable-key") ?? getHeaderValue(req.headers, "x-client-key");
|
|
246
|
-
if (!publishableKey) {
|
|
247
|
-
res.writeHead(401, { "Content-Type": "application/json" });
|
|
248
|
-
res.end(JSON.stringify({ error: "x-publishable-key header is required" }));
|
|
389
|
+
writeOAuthError(res, auth.error === "insufficient_scope" ? 403 : 401, auth.error, auth.errorDescription);
|
|
249
390
|
return;
|
|
250
391
|
}
|
|
251
|
-
const server = createServer();
|
|
392
|
+
const server = createServer({ toolSurface: "oauth" });
|
|
252
393
|
const transport = new StreamableHTTPServerTransport({
|
|
253
394
|
sessionIdGenerator: void 0
|
|
254
395
|
});
|
|
@@ -268,7 +409,7 @@ async function handler(req, res) {
|
|
|
268
409
|
if (typeof value === "string") headers.set(key, value);
|
|
269
410
|
else if (Array.isArray(value)) headers.set(key, value.join(", "));
|
|
270
411
|
}
|
|
271
|
-
await requestContext.run({ headers }, async () => {
|
|
412
|
+
await requestContext.run({ headers, auth: auth.context }, async () => {
|
|
272
413
|
try {
|
|
273
414
|
const body = req.body ?? JSON.parse(await readBody(req));
|
|
274
415
|
await transport.handleRequest(req, res, body);
|
package/dist/mcp/http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/transport/http.ts","../src/handler.ts","../src/auth.ts"],"sourcesContent":["import { createServer } from 'node:http'\nimport handler from '../handler'\n\nconst port = parseInt(process.env.PORT || '3001', 10)\n\ncreateServer(handler).listen(port, () => {\n console.log(`MCP server listening on http://localhost:${port}`)\n})\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'\nimport { createServer } from './server'\nimport { requestContext } from './lib/request-context'\nimport { validateApiKey } from './auth'\n\nconst MAX_REQUEST_BODY_BYTES = 1024 * 1024\nconst METHOD_NOT_ALLOWED = JSON.stringify({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Method not allowed.',\n },\n id: null,\n})\n\nfunction setCors(res: ServerResponse) {\n res.setHeader('Access-Control-Allow-Origin', '*')\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS')\n res.setHeader(\n 'Access-Control-Allow-Headers',\n 'Content-Type, x-api-key, x-publishable-key, x-client-key, mcp-session-id',\n )\n res.setHeader('Access-Control-Expose-Headers', 'Mcp-Session-Id, Mcp-Protocol-Version')\n}\n\nfunction getHeaderValue(\n headers: IncomingMessage['headers'],\n name: string,\n): string | undefined {\n const value = headers[name.toLowerCase()]\n if (Array.isArray(value)) return value[0]\n return value\n}\n\nfunction readBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = []\n let size = 0\n req.on('data', (chunk: Buffer) => {\n size += chunk.length\n if (size > MAX_REQUEST_BODY_BYTES) {\n req.destroy()\n reject(new Error('Request body too large'))\n return\n }\n chunks.push(chunk)\n })\n req.on('end', () => resolve(Buffer.concat(chunks).toString()))\n req.on('error', reject)\n })\n}\n\nconst HOME_PAGE = `01.software MCP Server\n======================\n\nMCP server for AI agents to interact with the 01.software API.\nManage content, products, orders, and more.\n\n\nAuthentication\n--------------\n\nAll requests require both:\n x-api-key opaque bearer token\n x-publishable-key publishable key for routing, rate limits, and quota enforcement\n\nUse x-client-key as a legacy alias for x-publishable-key.\n\nAccepted formats:\n sk01_{40hex} tenant API key (Console > Settings > API Keys)\n pat01_{40hex} personal access token (user-scoped local workflow)\n pk01_{...} publishable key\n\n export SOFTWARE_PUBLISHABLE_KEY=pk01_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n export SOFTWARE_SECRET_KEY=sk01_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\n\nConnect\n-------\n\nClaude Code:\n\n claude mcp add --transport http \\\\\n --header \"x-api-key: $SOFTWARE_SECRET_KEY\" \\\\\n --header \"x-publishable-key: $SOFTWARE_PUBLISHABLE_KEY\" \\\\\n 01software https://mcp.01.software/mcp\n\nCodex (.codex/config.toml, project-safe when using env vars):\n\n [mcp_servers.01software]\n url = \"https://mcp.01.software/mcp\"\n\n [mcp_servers.01software.env_http_headers]\n x-api-key = \"SOFTWARE_SECRET_KEY\"\n x-publishable-key = \"SOFTWARE_PUBLISHABLE_KEY\"\n\n // or .mcp.json\n {\n \"mcpServers\": {\n \"01software\": {\n \"type\": \"http\",\n \"url\": \"https://mcp.01.software/mcp\",\n \"headers\": {\n \"x-api-key\": \"\\${env:SOFTWARE_SECRET_KEY}\",\n \"x-publishable-key\": \"\\${env:SOFTWARE_PUBLISHABLE_KEY}\"\n }\n }\n }\n }\n\nCursor (.cursor/mcp.json):\n\n {\n \"mcpServers\": {\n \"01software\": {\n \"url\": \"https://mcp.01.software/mcp\",\n \"headers\": {\n \"x-api-key\": \"\\${env:SOFTWARE_SECRET_KEY}\",\n \"x-publishable-key\": \"\\${env:SOFTWARE_PUBLISHABLE_KEY}\"\n }\n }\n }\n }\n\nWindsurf (~/.codeium/windsurf/mcp_config.json):\n\n {\n \"mcpServers\": {\n \"01software\": {\n \"serverUrl\": \"https://mcp.01.software/mcp\",\n \"headers\": {\n \"x-api-key\": \"\\${env:SOFTWARE_SECRET_KEY}\",\n \"x-publishable-key\": \"\\${env:SOFTWARE_PUBLISHABLE_KEY}\"\n }\n }\n }\n }\n\nVS Code (.vscode/mcp.json):\n\n {\n \"servers\": {\n \"01software\": {\n \"type\": \"http\",\n \"url\": \"https://mcp.01.software/mcp\",\n \"headers\": {\n \"x-api-key\": \"\\${input:01software-api-key}\",\n \"x-publishable-key\": \"\\${input:01software-publishable-key}\"\n }\n }\n }\n }\n\nClaude Desktop (claude_desktop_config.json):\n\n {\n \"mcpServers\": {\n \"01software\": {\n \"url\": \"https://mcp.01.software/mcp\",\n \"headers\": {\n \"x-api-key\": \"\\${env:SOFTWARE_SECRET_KEY}\",\n \"x-publishable-key\": \"\\${env:SOFTWARE_PUBLISHABLE_KEY}\"\n }\n }\n }\n }\n\nCLI (stdio):\n\n npx @01.software/cli mcp\n # Reads SOFTWARE_SECRET_KEY=sk01_... or pat01_...\n # Also reads SOFTWARE_PUBLISHABLE_KEY for CDN routing, rate limits, and quota enforcement\n\nSecurity: never commit raw sk01_... or pat01_... tokens to repo-local MCP\nconfig files. Prefer client secret prompts, environment interpolation, OS secret\nmanagers, or ignored local files. Avoid passing real tokens directly on\nshared-machine command lines because shell history and process listings can\nexpose them.\n\n\nTools (34)\n----------\n\nCRUD query, get, create, update, delete, delete-many, update-many\nOrders create-order, checkout, get-order, update-order, create-fulfillment, update-fulfillment, update-transaction\nReturns create-return, update-return, return-with-refund\nCart add-cart-item, update-cart-item, remove-cart-item, clear-cart, apply-discount, remove-discount\nValidation validate-discount, calculate-shipping\nProducts stock-check\nSchema get-collection-schema\nContext get-tenant-context\nField Config list-configurable-fields, update-field-config\nGuidance sdk-get-recipe, sdk-search-docs, sdk-get-auth-setup, sdk-get-collection-pattern\n\nPrompts (4): sdk-usage-guide, collection-query-help, order-flow-guide, feature-setup-guide\nResources (12): config, collections-schema, getting-started, guides, api, query-builder, react-query, server-api, customer-auth, browser-vs-server, file-upload, webhook\n\n\nLinks\n-----\n\nDocs https://01.software/docs/integrations/mcp\nSDK https://01.software/docs/sdk/client\nAPI Reference https://01.software/docs/api/rest-api\nConsole https://console.01.software\n`\n\nexport default async function handler(req: IncomingMessage, res: ServerResponse) {\n setCors(res)\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204)\n res.end()\n return\n }\n\n if (req.method === 'GET') {\n res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' })\n res.end(HOME_PAGE)\n return\n }\n\n // Stateless Streamable HTTP should only accept POST requests.\n if (req.method !== 'POST') {\n res.writeHead(405, { 'Content-Type': 'application/json' })\n res.end(METHOD_NOT_ALLOWED)\n return\n }\n\n const apiKey = getHeaderValue(req.headers, 'x-api-key')\n const auth = validateApiKey(apiKey)\n if (!auth.valid) {\n res.writeHead(401, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify({ error: auth.error }))\n return\n }\n\n const publishableKey =\n getHeaderValue(req.headers, 'x-publishable-key') ??\n getHeaderValue(req.headers, 'x-client-key')\n if (!publishableKey) {\n res.writeHead(401, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify({ error: 'x-publishable-key header is required' }))\n return\n }\n\n const server = createServer()\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n })\n let closed = false\n\n const close = async () => {\n if (closed) return\n closed = true\n await transport.close()\n await server.close()\n }\n\n res.on('close', () => {\n void close()\n })\n\n await server.connect(transport)\n\n const headers = new Headers()\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value === 'string') headers.set(key, value)\n else if (Array.isArray(value)) headers.set(key, value.join(', '))\n }\n\n await requestContext.run({ headers }, async () => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const body = (req as any).body ?? JSON.parse(await readBody(req))\n await transport.handleRequest(req, res, body)\n } catch {\n if (!res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify({ error: 'Internal server error' }))\n }\n } finally {\n await close()\n }\n })\n}\n","export interface AuthResult {\n valid: boolean\n error?: string\n}\n\n/**\n * Validate an API key from the x-api-key header.\n * Expects a raw `sk01_...` or `pat01_...` bearer token.\n */\nexport function validateApiKey(apiKey: string | null | undefined): AuthResult {\n if (!apiKey || apiKey.length === 0) {\n return { valid: false, error: 'x-api-key header is required' }\n }\n if (!apiKey.startsWith('sk01_') && !apiKey.startsWith('pat01_')) {\n return {\n valid: false,\n error: 'Invalid API key format. Expected sk01_ or pat01_ token.',\n }\n }\n return { valid: true }\n}\n"],"mappings":"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","names":["createServer","createServer"]}
|
|
1
|
+
{"version":3,"sources":["../src/transport/http.ts","../src/handler.ts","../src/auth.ts"],"sourcesContent":["import { createServer } from 'node:http'\nimport handler from '../handler'\n\nconst port = parseInt(process.env.PORT || '3001', 10)\n\ncreateServer(handler).listen(port, () => {\n console.log(`MCP server listening on http://localhost:${port}`)\n})\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'\nimport {\n MCP_OAUTH_ISSUER,\n MCP_PROTECTED_RESOURCE_METADATA_PATH,\n MCP_RESOURCE_AUDIENCE,\n MCP_SCOPES,\n} from '@01.software/auth-contracts'\nimport { createServer } from './server'\nimport { requestContext } from './lib/request-context'\nimport { validateBearerAuthorizationHeaderAsync } from './auth'\nimport { mcpServicePublicJwks } from './service-auth'\n\nconst MAX_REQUEST_BODY_BYTES = 1024 * 1024\nconst MCP_ALLOWED_BROWSER_ORIGIN = 'https://01.software'\nconst METHOD_NOT_ALLOWED = JSON.stringify({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Method not allowed.',\n },\n id: null,\n})\n\nfunction setCors(res: ServerResponse) {\n res.setHeader('Access-Control-Allow-Origin', MCP_ALLOWED_BROWSER_ORIGIN)\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS')\n res.setHeader(\n 'Access-Control-Allow-Headers',\n 'Authorization, Content-Type, mcp-session-id',\n )\n res.setHeader('Access-Control-Expose-Headers', 'Mcp-Session-Id, Mcp-Protocol-Version')\n}\n\nfunction getHeaderValue(\n headers: IncomingMessage['headers'],\n name: string,\n): string | undefined {\n const value = headers[name.toLowerCase()]\n if (Array.isArray(value)) return value[0]\n return value\n}\n\nfunction readBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = []\n let size = 0\n req.on('data', (chunk: Buffer) => {\n size += chunk.length\n if (size > MAX_REQUEST_BODY_BYTES) {\n req.destroy()\n reject(new Error('Request body too large'))\n return\n }\n chunks.push(chunk)\n })\n req.on('end', () => resolve(Buffer.concat(chunks).toString()))\n req.on('error', reject)\n })\n}\n\nconst HOME_PAGE = `01.software MCP Server\n======================\n\nMCP server for AI agents to interact with the 01.software API.\nConnect over HTTP with OAuth, or use the local CLI stdio transport for full\nserver-key operations.\n\n\nAuthentication\n--------------\n\nHTTP MCP uses OAuth discovery and Authorization Code + PKCE.\nClients should connect to:\n\nConnect\n-------\n\nClaude Code:\n\n claude mcp add --transport http 01software https://mcp.01.software/mcp\n\nCodex (.codex/config.toml):\n\n [mcp_servers.01software]\n url = \"https://mcp.01.software/mcp\"\n\nCursor / Claude Desktop / other JSON clients:\n\n {\n \"mcpServers\": {\n \"01software\": {\n \"type\": \"http\",\n \"url\": \"https://mcp.01.software/mcp\"\n }\n }\n }\n\nWindsurf (~/.codeium/windsurf/mcp_config.json):\n\n {\n \"mcpServers\": {\n \"01software\": {\n \"serverUrl\": \"https://mcp.01.software/mcp\"\n }\n }\n }\n\nCLI (stdio):\n\n npx @01.software/cli mcp\n\n\nHTTP OAuth Tools\n----------------\n\nSchema get-collection-schema\nContext get-tenant-context\nField Config list-configurable-fields, update-field-config\nGuidance sdk-get-recipe, sdk-search-docs, sdk-get-auth-setup, sdk-get-collection-pattern\n\nFull Tool Surface\n-----------------\n\nThe local CLI stdio transport exposes CRUD, commerce, cart, validation, product,\nschema, context, field-config, and guidance tools:\n\n npx @01.software/cli mcp\n\nPrompts (4): sdk-usage-guide, collection-query-help, order-flow-guide, feature-setup-guide\nResources (12): config, collections-schema, getting-started, guides, api, query-builder, react-query, server-api, customer-auth, browser-vs-server, file-upload, webhook\n\n\nLinks\n-----\n\nDocs https://01.software/docs/integrations/mcp\nSDK https://01.software/docs/sdk/client\nAPI Reference https://01.software/docs/api/rest-api\nConsole https://console.01.software\n`\n\nconst PROTECTED_RESOURCE_METADATA = JSON.stringify({\n resource: MCP_RESOURCE_AUDIENCE,\n authorization_servers: [MCP_OAUTH_ISSUER],\n scopes_supported: [MCP_SCOPES.read, MCP_SCOPES.write],\n})\nconst SERVICE_JWKS_PATH = '/.well-known/service-jwks.json'\n\nfunction writeOAuthError(res: ServerResponse, status: number, error: string, description: string) {\n res.writeHead(status, {\n 'Content-Type': 'application/json',\n 'WWW-Authenticate': `Bearer resource_metadata=\"${MCP_PROTECTED_RESOURCE_METADATA_PATH}\", error=\"${error}\", error_description=\"${description}\"`,\n })\n res.end(JSON.stringify({ error, error_description: description }))\n}\n\nexport default async function handler(req: IncomingMessage, res: ServerResponse) {\n setCors(res)\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204)\n res.end()\n return\n }\n\n if (req.method === 'GET') {\n const pathname = new URL(req.url ?? '/', MCP_RESOURCE_AUDIENCE).pathname\n if (pathname === MCP_PROTECTED_RESOURCE_METADATA_PATH) {\n res.setHeader('Access-Control-Allow-Origin', '*')\n res.writeHead(200, { 'Content-Type': 'application/json' })\n res.end(PROTECTED_RESOURCE_METADATA)\n return\n }\n\n if (pathname === SERVICE_JWKS_PATH) {\n res.setHeader('Access-Control-Allow-Origin', '*')\n try {\n res.writeHead(200, {\n 'Cache-Control': 'public, max-age=60',\n 'Content-Type': 'application/json',\n })\n res.end(JSON.stringify(mcpServicePublicJwks()))\n } catch {\n res.writeHead(503, {\n 'Cache-Control': 'no-store',\n 'Content-Type': 'application/json',\n })\n res.end(JSON.stringify({ keys: [] }))\n }\n return\n }\n\n res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' })\n res.end(HOME_PAGE)\n return\n }\n\n // Stateless Streamable HTTP should only accept POST requests.\n if (req.method !== 'POST') {\n res.writeHead(405, { 'Content-Type': 'application/json' })\n res.end(METHOD_NOT_ALLOWED)\n return\n }\n\n const auth = await validateBearerAuthorizationHeaderAsync(\n getHeaderValue(req.headers, 'authorization'),\n )\n if (!auth.valid) {\n writeOAuthError(res, auth.error === 'insufficient_scope' ? 403 : 401, auth.error, auth.errorDescription)\n return\n }\n\n const server = createServer({ toolSurface: 'oauth' })\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n })\n let closed = false\n\n const close = async () => {\n if (closed) return\n closed = true\n await transport.close()\n await server.close()\n }\n\n res.on('close', () => {\n void close()\n })\n\n await server.connect(transport)\n\n const headers = new Headers()\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value === 'string') headers.set(key, value)\n else if (Array.isArray(value)) headers.set(key, value.join(', '))\n }\n\n await requestContext.run({ headers, auth: auth.context }, async () => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const body = (req as any).body ?? JSON.parse(await readBody(req))\n await transport.handleRequest(req, res, body)\n } catch {\n if (!res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify({ error: 'Internal server error' }))\n }\n } finally {\n await close()\n }\n })\n}\n","import { createPublicKey, verify as verifySignature } from 'node:crypto'\nimport {\n MCP_OAUTH_ISSUER,\n MCP_RESOURCE_AUDIENCE,\n MCP_SCOPES,\n MCP_TENANT_CLAIM,\n MCP_TENANT_ROLE_CLAIM,\n} from '@01.software/auth-contracts'\n\nconst ALLOWED_ALGORITHMS = new Set(['RS256', 'ES256'])\nconst DEFAULT_CLOCK_SKEW_SECONDS = 30\nconst DEFAULT_JWKS_URI = `${MCP_OAUTH_ISSUER}/.well-known/jwks.json`\nconst MAX_ACCESS_TOKEN_LIFETIME_SECONDS = 300\ntype McpScope = (typeof MCP_SCOPES)[keyof typeof MCP_SCOPES]\ntype McpTenantRole = 'tenant-admin' | 'tenant-editor' | 'tenant-viewer'\n\ninterface JwtHeader {\n alg?: unknown\n kid?: unknown\n}\n\ninterface JwtPayload {\n aud?: unknown\n exp?: unknown\n iat?: unknown\n iss?: unknown\n nbf?: unknown\n scope?: unknown\n scp?: unknown\n sub?: unknown\n [MCP_TENANT_CLAIM]?: unknown\n [MCP_TENANT_ROLE_CLAIM]?: unknown\n}\n\nexport interface TenantAuthContext {\n tenantId: string\n principalId?: string\n scopes: McpScope[]\n tenantRole: McpTenantRole\n authMode: 'oauth'\n}\n\nexport type AuthResult =\n | {\n valid: true\n context: TenantAuthContext\n }\n | {\n valid: false\n error: 'invalid_token' | 'insufficient_scope'\n errorDescription: string\n context?: undefined\n }\n\nexport interface JsonWebKeySet {\n keys: Array<Record<string, unknown>>\n}\n\nexport interface ValidateAccessTokenOptions {\n audience?: string\n clockSkewSeconds?: number\n fetchImpl?: typeof fetch\n issuer?: string\n jwks?: JsonWebKeySet\n jwksUri?: string\n now?: Date\n requiredScopes?: McpScope[]\n}\n\nfunction invalid(errorDescription: string): AuthResult {\n return { valid: false, error: 'invalid_token', errorDescription }\n}\n\nfunction isProduction(): boolean {\n return process.env.NODE_ENV === 'production'\n}\n\nfunction insufficientScope(errorDescription: string): AuthResult {\n return { valid: false, error: 'insufficient_scope', errorDescription }\n}\n\nfunction decodeBase64UrlJson<T>(value: string): T | null {\n try {\n return JSON.parse(Buffer.from(value, 'base64url').toString('utf8')) as T\n } catch {\n return null\n }\n}\n\nfunction parseScopes(payload: JwtPayload): McpScope[] {\n const rawScopes =\n typeof payload.scope === 'string'\n ? payload.scope.split(/\\s+/).filter(Boolean)\n : Array.isArray(payload.scp)\n ? payload.scp\n : []\n\n return rawScopes.filter(\n (scope): scope is McpScope =>\n scope === MCP_SCOPES.read || scope === MCP_SCOPES.write,\n )\n}\n\nfunction audienceMatches(aud: unknown, expected: string): boolean {\n if (typeof aud === 'string') return aud === expected\n return Array.isArray(aud) && aud.includes(expected)\n}\n\nfunction verifyJwtSignature(\n alg: string,\n jwk: Record<string, unknown>,\n signingInput: string,\n signature: Buffer,\n): boolean {\n const key = createPublicKey({ key: jwk, format: 'jwk' })\n const input = Buffer.from(signingInput)\n\n if (alg === 'RS256') {\n return verifySignature('RSA-SHA256', input, key, signature)\n }\n\n if (alg === 'ES256') {\n return verifySignature('SHA256', input, { key, dsaEncoding: 'ieee-p1363' }, signature)\n }\n\n return false\n}\n\nlet cachedRemoteJwks:\n | { expiresAt: number; jwks: JsonWebKeySet; uri: string }\n | null = null\n\nfunction jwksUriFor(options: ValidateAccessTokenOptions): string | null {\n const raw = isProduction()\n ? DEFAULT_JWKS_URI\n : options.jwksUri ?? DEFAULT_JWKS_URI\n let url: URL\n try {\n url = new URL(raw)\n } catch {\n return null\n }\n if (\n url.protocol !== 'https:' ||\n url.username ||\n url.password ||\n url.search ||\n url.hash\n ) {\n return null\n }\n return url.toString()\n}\n\nasync function remoteJwks(\n options: ValidateAccessTokenOptions = {},\n forceRefresh = false,\n): Promise<JsonWebKeySet | null> {\n const uri = jwksUriFor(options)\n if (!uri) return null\n const now = Date.now()\n if (\n !forceRefresh &&\n cachedRemoteJwks &&\n cachedRemoteJwks.uri === uri &&\n cachedRemoteJwks.expiresAt > now\n ) {\n return cachedRemoteJwks.jwks\n }\n\n const fetchImpl = options.fetchImpl ?? fetch\n let response: Response\n try {\n response = await fetchImpl(uri, {\n headers: { Accept: 'application/json' },\n })\n } catch {\n return null\n }\n if (!response.ok) return null\n const parsed = (await response.json().catch(() => null)) as JsonWebKeySet | null\n if (!parsed || !Array.isArray(parsed.keys)) return null\n cachedRemoteJwks = {\n expiresAt: now + 300_000,\n jwks: parsed,\n uri,\n }\n return parsed\n}\n\nexport function validateAccessToken(\n token: string,\n options: ValidateAccessTokenOptions = {},\n): AuthResult {\n const parts = token.split('.')\n if (parts.length !== 3 || parts.some((part) => part.length === 0)) {\n return invalid('Bearer token must be a compact JWT')\n }\n\n const [encodedHeader, encodedPayload, encodedSignature] = parts\n const header = decodeBase64UrlJson<JwtHeader>(encodedHeader)\n const payload = decodeBase64UrlJson<JwtPayload>(encodedPayload)\n if (!header || !payload) return invalid('Bearer token contains invalid JSON')\n\n if (typeof header.alg !== 'string' || !ALLOWED_ALGORITHMS.has(header.alg)) {\n return invalid('Bearer token uses an unsupported signing algorithm')\n }\n\n if (typeof header.kid !== 'string' || header.kid.length === 0) {\n return invalid('Bearer token is missing kid')\n }\n\n const jwks = options.jwks\n if (!jwks) return invalid('JWKS is not configured')\n\n const jwk = jwks.keys.find((key) => key.kid === header.kid)\n if (!jwk) return invalid('Bearer token kid is unknown')\n\n const signingInput = `${encodedHeader}.${encodedPayload}`\n const signature = Buffer.from(encodedSignature, 'base64url')\n if (!verifyJwtSignature(header.alg, jwk, signingInput, signature)) {\n return invalid('Bearer token signature is invalid')\n }\n\n const issuer = options.issuer ?? MCP_OAUTH_ISSUER\n if (payload.iss !== issuer) return invalid('Bearer token issuer is invalid')\n\n const audience = options.audience ?? MCP_RESOURCE_AUDIENCE\n if (!audienceMatches(payload.aud, audience)) {\n return invalid('Bearer token audience is invalid')\n }\n\n if (typeof payload.iat !== 'number') return invalid('Bearer token is missing iat')\n if (typeof payload.exp !== 'number') return invalid('Bearer token is missing exp')\n if (payload.exp <= payload.iat) {\n return invalid('Bearer token lifetime is invalid')\n }\n if (payload.exp - payload.iat > MAX_ACCESS_TOKEN_LIFETIME_SECONDS) {\n return invalid('Bearer token lifetime exceeds 300 seconds')\n }\n\n const nowSeconds = Math.floor((options.now ?? new Date()).getTime() / 1000)\n const leeway = options.clockSkewSeconds ?? DEFAULT_CLOCK_SKEW_SECONDS\n if (payload.iat > nowSeconds + leeway) {\n return invalid('Bearer token iat is too far in the future')\n }\n if (typeof payload.nbf === 'number' && payload.nbf > nowSeconds + leeway) {\n return invalid('Bearer token is not yet valid')\n }\n if (payload.exp < nowSeconds - leeway) return invalid('Bearer token is expired')\n\n const tenantId = payload[MCP_TENANT_CLAIM]\n if (typeof tenantId !== 'string' || tenantId.length === 0) {\n return invalid('Bearer token tenant_id claim is invalid')\n }\n\n const tenantRole = payload[MCP_TENANT_ROLE_CLAIM]\n if (\n tenantRole !== 'tenant-admin' &&\n tenantRole !== 'tenant-editor' &&\n tenantRole !== 'tenant-viewer'\n ) {\n return invalid('Bearer token tenant_role claim is invalid')\n }\n\n if (typeof payload.sub !== 'string' || payload.sub.length === 0) {\n return invalid('Bearer token subject claim is invalid')\n }\n\n const scopes = parseScopes(payload)\n const requiredScopes = options.requiredScopes ?? [MCP_SCOPES.read]\n const missingScope = requiredScopes.find((scope) => !scopes.includes(scope))\n if (missingScope) return insufficientScope(`Bearer token is missing ${missingScope}`)\n\n return {\n valid: true,\n context: {\n tenantId,\n principalId: payload.sub,\n scopes,\n tenantRole,\n authMode: 'oauth',\n },\n }\n}\n\nexport function validateBearerAuthorizationHeader(\n authorization: string | null | undefined,\n options?: ValidateAccessTokenOptions,\n): AuthResult {\n if (!authorization) return invalid('Authorization Bearer token is required')\n\n const match = authorization.match(/^Bearer\\s+(.+)$/i)\n if (!match) return invalid('Authorization header must use Bearer')\n\n return validateAccessToken(match[1], options)\n}\n\nfunction shouldRefreshRemoteJwks(result: AuthResult): boolean {\n return (\n !result.valid &&\n (\n result.errorDescription === 'Bearer token kid is unknown' ||\n result.errorDescription === 'Bearer token signature is invalid'\n )\n )\n}\n\nexport async function validateBearerAuthorizationHeaderAsync(\n authorization: string | null | undefined,\n options?: ValidateAccessTokenOptions,\n): Promise<AuthResult> {\n if (!authorization) return invalid('Authorization Bearer token is required')\n\n const match = authorization.match(/^Bearer\\s+(.+)$/i)\n if (!match) return invalid('Authorization header must use Bearer')\n\n if (options?.jwks) {\n return validateAccessToken(match[1], options)\n }\n\n const jwks = await remoteJwks(options)\n const result = validateAccessToken(match[1], {\n ...options,\n jwks: jwks ?? undefined,\n })\n\n if (shouldRefreshRemoteJwks(result)) {\n const refreshedJwks = await remoteJwks(options, true)\n if (refreshedJwks) {\n return validateAccessToken(match[1], {\n ...options,\n jwks: refreshedJwks,\n })\n }\n }\n\n return result\n}\n"],"mappings":"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","names":["createServer","MCP_OAUTH_ISSUER","MCP_RESOURCE_AUDIENCE","MCP_SCOPES","MCP_RESOURCE_AUDIENCE","MCP_OAUTH_ISSUER","MCP_SCOPES","createServer"]}
|