@devlab.group/agent-commerce 1.6.1 → 2.1.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/LICENSE +187 -2
- package/README.md +105 -54
- package/dist/ap2.d.ts +4 -4
- package/dist/ap2.js +95 -28
- package/dist/ap2.js.map +1 -1
- package/dist/{authorization-BpbWGGrf.d.ts → authorization-CMkIuAkO.d.ts} +1 -1
- package/dist/chunk-2CJNA7MS.js +8 -0
- package/dist/chunk-2CJNA7MS.js.map +1 -0
- package/dist/chunk-5ZYXBCDX.js +113 -0
- package/dist/chunk-5ZYXBCDX.js.map +1 -0
- package/dist/{chunk-V2V2E7HX.js → chunk-6Z2H53CG.js} +7 -6
- package/dist/chunk-6Z2H53CG.js.map +1 -0
- package/dist/chunk-ES3V6ECS.js +125 -0
- package/dist/chunk-ES3V6ECS.js.map +1 -0
- package/dist/chunk-IFGRCXME.js +581 -0
- package/dist/chunk-IFGRCXME.js.map +1 -0
- package/dist/chunk-KA2AX5FH.js +44 -0
- package/dist/chunk-KA2AX5FH.js.map +1 -0
- package/dist/{chunk-VKDXMXHL.js → chunk-LZS5BL3W.js} +3 -3
- package/dist/{chunk-VKDXMXHL.js.map → chunk-LZS5BL3W.js.map} +1 -1
- package/dist/{chunk-N5KRK7YA.js → chunk-SY6GUYSQ.js} +6 -10
- package/dist/chunk-SY6GUYSQ.js.map +1 -0
- package/dist/chunk-UHMYRV3J.js +1396 -0
- package/dist/chunk-UHMYRV3J.js.map +1 -0
- package/dist/cli/index.js +2980 -1964
- package/dist/cli/index.js.map +1 -1
- package/dist/{constants-Bkq04FRZ.d.ts → constants-nackLItD.d.ts} +4 -3
- package/dist/{guardrails-71XwtXlT.d.ts → guardrails-CX_jYXvZ.d.ts} +9 -1
- package/dist/index.d.ts +88 -49
- package/dist/index.js +3432 -2161
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +3 -3
- package/dist/mcp.js +252 -94
- package/dist/mcp.js.map +1 -1
- package/dist/mpp.d.ts +15 -4
- package/dist/mpp.js +115 -74
- package/dist/mpp.js.map +1 -1
- package/dist/near-D6C42VPE.js +58 -0
- package/dist/near-D6C42VPE.js.map +1 -0
- package/dist/networks-olfflCI_.d.ts +38 -0
- package/dist/{protocol-adapter-DTkrXTev.d.ts → protocol-adapter-CfxU8Z2T.d.ts} +4 -2
- package/dist/{runtime-CiYbgGpi.d.ts → runtime-7vLNYvmU.d.ts} +25 -3
- package/dist/svm-QEUAUNPQ.js +33 -0
- package/dist/svm-QEUAUNPQ.js.map +1 -0
- package/dist/{types-O6lg3Q5V.d.ts → types-CVw6Sf46.d.ts} +11 -0
- package/dist/x402.d.ts +78 -63
- package/dist/x402.js +13 -4
- package/dist/x402.js.map +1 -1
- package/package.json +36 -13
- package/dist/chunk-AFI2CEZS.js +0 -10
- package/dist/chunk-AFI2CEZS.js.map +0 -1
- package/dist/chunk-CBJIT7QP.js +0 -258
- package/dist/chunk-CBJIT7QP.js.map +0 -1
- package/dist/chunk-JKEKKXRJ.js +0 -680
- package/dist/chunk-JKEKKXRJ.js.map +0 -1
- package/dist/chunk-N5KRK7YA.js.map +0 -1
- package/dist/chunk-V2V2E7HX.js.map +0 -1
package/dist/mcp.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { H as HttpProtocolAdapter } from './protocol-adapter-
|
|
1
|
+
import { H as HttpProtocolAdapter } from './protocol-adapter-CfxU8Z2T.js';
|
|
2
2
|
import 'node:http';
|
|
3
|
-
import './runtime-
|
|
4
|
-
import './authorization-
|
|
3
|
+
import './runtime-7vLNYvmU.js';
|
|
4
|
+
import './authorization-CMkIuAkO.js';
|
|
5
5
|
|
|
6
6
|
interface McpAdapterOptions {
|
|
7
7
|
/** Path prefix the gateway mounts this adapter at. Default '/mcp' */
|
package/dist/mcp.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { MCP_TOOL_NAME_PATTERN } from './chunk-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { MCP_TOOL_NAME_PATTERN, readCappedBody, MCP_MODERN_PROTOCOL_REVISION, x402ErrorCode, settlementFailure, settlementResponseFromDetails, x402McpPaymentSubmission, X402_MCP_PAYMENT_META_KEY, X402_MCP_PAYMENT_RESPONSE_META_KEY, settlementResponse, X402_MCP_ERROR_META_KEY } from './chunk-ES3V6ECS.js';
|
|
2
|
+
import { mcpPaymentRequired, MPP_MCP_CREDENTIAL_META_KEY, receiptObject, MPP_MCP_RECEIPT_META_KEY, MPP_MCP_PAYMENT_REQUIRED_META_KEY } from './chunk-5ZYXBCDX.js';
|
|
3
|
+
import { PACKAGE_VERSION, toLogInfo, toCommerceError, CommerceError, extractReservedInputFields, PAYMENT_INPUT_FIELD, AUTHORIZATION_INPUT_FIELD, toErrorEnvelope, DELIVERY_SUMMARY_META_KEY, toDeliverySummary, toPaymentRequiredEnvelope } from './chunk-SY6GUYSQ.js';
|
|
4
|
+
import { isRecord } from './chunk-2CJNA7MS.js';
|
|
5
|
+
import { createMcpHandler, Server, ProtocolError, ProtocolErrorCode, LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/server';
|
|
6
6
|
|
|
7
|
-
var MCP_SUPPORTED_SPEC = LATEST_PROTOCOL_VERSION
|
|
7
|
+
var MCP_SUPPORTED_SPEC = `${MCP_MODERN_PROTOCOL_REVISION}, ${LATEST_PROTOCOL_VERSION}`;
|
|
8
8
|
var MCP_CAPABILITIES = ["tools/list", "tools/call"];
|
|
9
9
|
var MCP_UNSUPPORTED = [
|
|
10
10
|
"resources",
|
|
@@ -16,7 +16,15 @@ var MCP_UNSUPPORTED = [
|
|
|
16
16
|
"logging",
|
|
17
17
|
"notifications/tools/list_changed",
|
|
18
18
|
"tasks",
|
|
19
|
-
"dns-rebinding-protection"
|
|
19
|
+
"dns-rebinding-protection",
|
|
20
|
+
"authorization",
|
|
21
|
+
"sessions",
|
|
22
|
+
"subscriptions",
|
|
23
|
+
"pagination",
|
|
24
|
+
"HTTP+SSE transport",
|
|
25
|
+
// mppx reads challenges and refusals from tool-result `_meta`.
|
|
26
|
+
"MPP payment-required as JSON-RPC error -32042",
|
|
27
|
+
"MPP verification failure as JSON-RPC error -32043"
|
|
20
28
|
];
|
|
21
29
|
function buildDescriptor(implementationVersion) {
|
|
22
30
|
return {
|
|
@@ -30,41 +38,6 @@ function buildDescriptor(implementationVersion) {
|
|
|
30
38
|
};
|
|
31
39
|
}
|
|
32
40
|
|
|
33
|
-
// src/protocols/mcp/result-mapping.ts
|
|
34
|
-
function toRecord(value) {
|
|
35
|
-
return value;
|
|
36
|
-
}
|
|
37
|
-
function deliveredResult(outcome) {
|
|
38
|
-
const body = outcome.body;
|
|
39
|
-
const text = typeof body === "string" ? body : JSON.stringify(body ?? null);
|
|
40
|
-
return {
|
|
41
|
-
content: [{ type: "text", text }],
|
|
42
|
-
...isRecord(body) ? { structuredContent: body } : {},
|
|
43
|
-
_meta: { [DELIVERY_SUMMARY_META_KEY]: toRecord(toDeliverySummary(outcome)) }
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
function paymentRequiredResult(outcome) {
|
|
47
|
-
const envelope = toPaymentRequiredEnvelope(outcome);
|
|
48
|
-
const p = envelope.payment;
|
|
49
|
-
const text = `Payment required: ${p.amount} ${p.currency} to ${p.destination} for resource "${outcome.resourceId}". Retry the call with a ${p.provider} payment proof in the "${PAYMENT_INPUT_FIELD}" input field.`;
|
|
50
|
-
return {
|
|
51
|
-
isError: true,
|
|
52
|
-
content: [{ type: "text", text }],
|
|
53
|
-
structuredContent: toRecord(envelope)
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
function errorResult(error) {
|
|
57
|
-
const envelope = toErrorEnvelope(error);
|
|
58
|
-
return {
|
|
59
|
-
isError: true,
|
|
60
|
-
content: [{ type: "text", text: `${envelope.code}: ${envelope.message}` }],
|
|
61
|
-
structuredContent: toRecord(envelope)
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
function mapOutcome(outcome) {
|
|
65
|
-
return outcome.kind === "delivered" ? deliveredResult(outcome) : paymentRequiredResult(outcome);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
41
|
// src/protocols/mcp/tool-mapping.ts
|
|
69
42
|
function isValidToolName(id) {
|
|
70
43
|
return MCP_TOOL_NAME_PATTERN.test(id);
|
|
@@ -75,18 +48,25 @@ function isPaidResource(resource) {
|
|
|
75
48
|
function primaryPaymentMethod(resource) {
|
|
76
49
|
return resource.paymentMethods[0];
|
|
77
50
|
}
|
|
51
|
+
function proofCarriers(method) {
|
|
52
|
+
const field = `the "${PAYMENT_INPUT_FIELD}" argument`;
|
|
53
|
+
if (method === "x402") return `_meta["${X402_MCP_PAYMENT_META_KEY}"] or ${field}`;
|
|
54
|
+
if (method === "mpp") return `_meta["${MPP_MCP_CREDENTIAL_META_KEY}"] or ${field}`;
|
|
55
|
+
return field;
|
|
56
|
+
}
|
|
78
57
|
function paymentProofNote(resource) {
|
|
79
58
|
const method = primaryPaymentMethod(resource);
|
|
80
|
-
const rail = method !== void 0 ? `
|
|
81
|
-
return `Requires ${rail} payment proof in
|
|
59
|
+
const rail = method !== void 0 ? `an ${method}` : "a";
|
|
60
|
+
return `Requires ${rail} payment proof in ${proofCarriers(method)}, built from the payment-required response.`;
|
|
82
61
|
}
|
|
62
|
+
var OMIT_NOTE = "Omit it when the client sends the proof in _meta.";
|
|
83
63
|
function paymentInputFieldDescription(resource) {
|
|
84
64
|
const method = primaryPaymentMethod(resource);
|
|
85
65
|
if (method === "x402") {
|
|
86
|
-
return
|
|
66
|
+
return `x402 payment proof (base64 PAYMENT-SIGNATURE value) for a previous payment-required response. ${OMIT_NOTE}`;
|
|
87
67
|
}
|
|
88
68
|
if (method === "mpp") {
|
|
89
|
-
return
|
|
69
|
+
return `MPP credential (the full "Authorization: Payment ..." value) for the challenge in a previous payment-required response. ${OMIT_NOTE}`;
|
|
90
70
|
}
|
|
91
71
|
if (method !== void 0) {
|
|
92
72
|
return `${method} payment proof (base64-encoded) returned from a previous payment-required response.`;
|
|
@@ -152,11 +132,96 @@ function buildInputSchema(resource) {
|
|
|
152
132
|
};
|
|
153
133
|
}
|
|
154
134
|
|
|
135
|
+
// src/protocols/mcp/result-mapping.ts
|
|
136
|
+
function toRecord(value) {
|
|
137
|
+
return value;
|
|
138
|
+
}
|
|
139
|
+
function deliveredResult(outcome) {
|
|
140
|
+
const body = outcome.body;
|
|
141
|
+
const text = typeof body === "string" ? body : JSON.stringify(body ?? null);
|
|
142
|
+
const payment = outcome.payment;
|
|
143
|
+
const receipt = payment?.provider === "mpp" ? receiptObject(payment.metadata?.["receipt"]) : void 0;
|
|
144
|
+
return {
|
|
145
|
+
content: [{ type: "text", text }],
|
|
146
|
+
...isRecord(body) ? { structuredContent: body } : {},
|
|
147
|
+
_meta: {
|
|
148
|
+
[DELIVERY_SUMMARY_META_KEY]: toRecord(toDeliverySummary(outcome)),
|
|
149
|
+
...payment?.provider === "x402" ? { [X402_MCP_PAYMENT_RESPONSE_META_KEY]: settlementResponse(payment) } : {},
|
|
150
|
+
...receipt !== void 0 ? { [MPP_MCP_RECEIPT_META_KEY]: receipt } : {}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function structuredError(structured, sentence, meta) {
|
|
155
|
+
return {
|
|
156
|
+
isError: true,
|
|
157
|
+
content: [
|
|
158
|
+
{ type: "text", text: JSON.stringify(structured) },
|
|
159
|
+
{ type: "text", text: sentence }
|
|
160
|
+
],
|
|
161
|
+
structuredContent: structured,
|
|
162
|
+
...meta !== void 0 ? { _meta: meta } : {}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function withX402Challenge(envelope, challenge) {
|
|
166
|
+
return { ...challenge, ...envelope };
|
|
167
|
+
}
|
|
168
|
+
function paymentRequiredResult(outcome) {
|
|
169
|
+
const envelope = toPaymentRequiredEnvelope(outcome);
|
|
170
|
+
const p = envelope.payment;
|
|
171
|
+
const x402Required = p.provider === "x402" ? p.envelope : void 0;
|
|
172
|
+
const structured = x402Required !== void 0 ? withX402Challenge(toRecord(envelope), x402Required) : toRecord(envelope);
|
|
173
|
+
const mppRequired = p.provider === "mpp" ? mcpPaymentRequired(p.envelope) : void 0;
|
|
174
|
+
let meta;
|
|
175
|
+
if (x402Required !== void 0) meta = { [X402_MCP_ERROR_META_KEY]: x402Required };
|
|
176
|
+
else if (mppRequired !== void 0) meta = { [MPP_MCP_PAYMENT_REQUIRED_META_KEY]: mppRequired };
|
|
177
|
+
return structuredError(
|
|
178
|
+
structured,
|
|
179
|
+
// A challenge with several offers names no single destination
|
|
180
|
+
`Payment required: ${p.amount} ${p.currency}${p.destination !== void 0 ? ` to ${p.destination}` : ""} for resource "${outcome.resourceId}". Retry with an ${p.provider} payment proof in ${proofCarriers(p.provider)}.`,
|
|
181
|
+
meta
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
function errorResult(error, rail) {
|
|
185
|
+
const envelope = toErrorEnvelope(error);
|
|
186
|
+
const details = error.details;
|
|
187
|
+
const challenge = details?.["challenge"];
|
|
188
|
+
const refused = error.httpStatus === 402 && isRecord(challenge);
|
|
189
|
+
const x402Required = rail === "x402" && refused ? {
|
|
190
|
+
...challenge,
|
|
191
|
+
error: x402ErrorCode(error.code, details?.["reason"], details?.["network"])
|
|
192
|
+
} : void 0;
|
|
193
|
+
const structured = x402Required !== void 0 ? withX402Challenge(toRecord(envelope), x402Required) : toRecord(envelope);
|
|
194
|
+
let meta;
|
|
195
|
+
if (rail === "x402") {
|
|
196
|
+
const settlement = error.code === "PAYMENT_SETTLEMENT_FAILED" ? settlementFailure(details) : settlementResponseFromDetails(details);
|
|
197
|
+
if (settlement !== void 0 || x402Required !== void 0) {
|
|
198
|
+
meta = {
|
|
199
|
+
...x402Required !== void 0 ? { [X402_MCP_ERROR_META_KEY]: x402Required } : {},
|
|
200
|
+
...settlement !== void 0 ? { [X402_MCP_PAYMENT_RESPONSE_META_KEY]: settlement } : {}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
} else if (rail === "mpp" && refused) {
|
|
204
|
+
const required = mcpPaymentRequired(challenge, {
|
|
205
|
+
code: error.code,
|
|
206
|
+
reason: details?.["reason"],
|
|
207
|
+
detail: error.message
|
|
208
|
+
});
|
|
209
|
+
if (required !== void 0) meta = { [MPP_MCP_PAYMENT_REQUIRED_META_KEY]: required };
|
|
210
|
+
}
|
|
211
|
+
return structuredError(structured, `${envelope.code}: ${envelope.message}`, meta);
|
|
212
|
+
}
|
|
213
|
+
function mapOutcome(outcome) {
|
|
214
|
+
return outcome.kind === "delivered" ? deliveredResult(outcome) : paymentRequiredResult(outcome);
|
|
215
|
+
}
|
|
216
|
+
|
|
155
217
|
// src/protocols/mcp/adapter.ts
|
|
156
218
|
var DEFAULT_MOUNT_PATH = "/mcp";
|
|
157
219
|
var DEFAULT_SERVER_NAME = "agent-commerce";
|
|
158
220
|
var MAX_CONCURRENT_TOOL_CALLS = 8;
|
|
221
|
+
var MAX_REQUEST_BODY_BYTES = 256 * 1024;
|
|
159
222
|
var MAX_QUEUED_TOOL_CALLS = 64;
|
|
223
|
+
var MCP_BATCH_PROTOCOL_REVISION = "2025-03-26";
|
|
224
|
+
var TOOLS_LIST_CACHE_HINT = { ttlMs: 6e4, cacheScope: "public" };
|
|
160
225
|
var McpProtocolAdapter = class {
|
|
161
226
|
name = "mcp";
|
|
162
227
|
mountPath;
|
|
@@ -168,7 +233,7 @@ var McpProtocolAdapter = class {
|
|
|
168
233
|
tools = [];
|
|
169
234
|
toolsByResourceId = /* @__PURE__ */ new Map();
|
|
170
235
|
skipped = [];
|
|
171
|
-
|
|
236
|
+
handler;
|
|
172
237
|
// Counting semaphore on `pipeline.execute()`. It lives on the adapter, not
|
|
173
238
|
// the request, so the cap holds across concurrent `/mcp` requests as well
|
|
174
239
|
// as within one batch.
|
|
@@ -232,6 +297,14 @@ var McpProtocolAdapter = class {
|
|
|
232
297
|
this.tools = tools;
|
|
233
298
|
this.toolsByResourceId = toolsByResourceId;
|
|
234
299
|
this.skipped = skipped;
|
|
300
|
+
this.handler = createMcpHandler(
|
|
301
|
+
(ctx) => this.buildServer(ctx.requestInfo?.signal ?? new AbortController().signal),
|
|
302
|
+
{
|
|
303
|
+
legacy: "stateless",
|
|
304
|
+
// Reporting only: a request the SDK rejects is already answered
|
|
305
|
+
onerror: (err) => context.logger.debug({ adapter: "mcp", err: toLogInfo(err) }, "mcp adapter: SDK error")
|
|
306
|
+
}
|
|
307
|
+
);
|
|
235
308
|
this.started = true;
|
|
236
309
|
context.logger.info(
|
|
237
310
|
{
|
|
@@ -245,7 +318,7 @@ var McpProtocolAdapter = class {
|
|
|
245
318
|
}
|
|
246
319
|
async handleHttp(req, res) {
|
|
247
320
|
try {
|
|
248
|
-
if (!this.started || !this.context) {
|
|
321
|
+
if (!this.started || !this.context || !this.handler) {
|
|
249
322
|
this.writeJsonRpcError(res, 503, "MCP adapter is not running.");
|
|
250
323
|
return;
|
|
251
324
|
}
|
|
@@ -254,20 +327,39 @@ var McpProtocolAdapter = class {
|
|
|
254
327
|
return;
|
|
255
328
|
}
|
|
256
329
|
const abortController = new AbortController();
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
330
|
+
res.once("close", () => abortController.abort());
|
|
331
|
+
const read = await readCappedBody(req, MAX_REQUEST_BODY_BYTES);
|
|
332
|
+
if (read.kind === "too-large") {
|
|
333
|
+
this.writeJsonRpcError(
|
|
334
|
+
res,
|
|
335
|
+
413,
|
|
336
|
+
`Request body exceeds the ${MAX_REQUEST_BODY_BYTES}-byte limit.`
|
|
337
|
+
);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
if (read.kind !== "ok") {
|
|
341
|
+
this.writeJsonRpcError(res, 400, "Could not read the request body.");
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const body = parseJson(read.text);
|
|
345
|
+
const version = req.headers["mcp-protocol-version"];
|
|
346
|
+
if (Array.isArray(body) && version !== void 0 && version !== MCP_BATCH_PROTOCOL_REVISION) {
|
|
347
|
+
this.writeJsonRpcError(
|
|
348
|
+
res,
|
|
349
|
+
400,
|
|
350
|
+
`Invalid Request: JSON-RPC batches are supported only in MCP ${MCP_BATCH_PROTOCOL_REVISION}.`
|
|
351
|
+
);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const malformed = isModernRequest(req) ? void 0 : malformedToolCall(body);
|
|
355
|
+
if (malformed !== void 0) {
|
|
356
|
+
this.writeJsonRpcError(res, 200, malformed.message, -32602, malformed.id);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const response = await this.handler.fetch(
|
|
360
|
+
toWebRequest(req, read.text, abortController.signal)
|
|
361
|
+
);
|
|
362
|
+
await writeWebResponse(response, res);
|
|
271
363
|
} catch (err) {
|
|
272
364
|
this.context?.logger.error(
|
|
273
365
|
{ adapter: "mcp", err: toLogInfo(err) },
|
|
@@ -290,55 +382,60 @@ var McpProtocolAdapter = class {
|
|
|
290
382
|
}
|
|
291
383
|
async stop() {
|
|
292
384
|
this.started = false;
|
|
293
|
-
const
|
|
294
|
-
this.
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
} catch {
|
|
300
|
-
}
|
|
301
|
-
})
|
|
302
|
-
);
|
|
385
|
+
const handler = this.handler;
|
|
386
|
+
this.handler = void 0;
|
|
387
|
+
try {
|
|
388
|
+
await handler?.close();
|
|
389
|
+
} catch {
|
|
390
|
+
}
|
|
303
391
|
this.context = void 0;
|
|
304
392
|
}
|
|
305
|
-
|
|
393
|
+
// Use general JSON-RPC codes for transport errors
|
|
394
|
+
writeJsonRpcError(res, status, message, code = status >= 500 ? -32603 : -32600, id = null) {
|
|
306
395
|
if (res.headersSent) return;
|
|
307
|
-
res.writeHead(status, {
|
|
308
|
-
|
|
396
|
+
res.writeHead(status, {
|
|
397
|
+
"content-type": "application/json",
|
|
398
|
+
// RFC 9110 requires Allow on a 405, and this endpoint takes only POST
|
|
399
|
+
...status === 405 ? { allow: "POST" } : {}
|
|
400
|
+
});
|
|
401
|
+
res.end(JSON.stringify({ jsonrpc: "2.0", error: { code, message }, id }));
|
|
309
402
|
}
|
|
310
403
|
buildServer(signal) {
|
|
311
404
|
const server = new Server(
|
|
312
405
|
{ name: this.serverName, version: this.serverVersion },
|
|
313
|
-
{ capabilities: { tools: {} } }
|
|
406
|
+
{ capabilities: { tools: {} }, cacheHints: { "tools/list": TOOLS_LIST_CACHE_HINT } }
|
|
314
407
|
);
|
|
315
|
-
server.setRequestHandler(
|
|
316
|
-
|
|
317
|
-
|
|
408
|
+
server.setRequestHandler("tools/list", async (request) => {
|
|
409
|
+
if (request.params?.cursor !== void 0) {
|
|
410
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, "Invalid cursor.");
|
|
411
|
+
}
|
|
412
|
+
return { tools: this.tools.map((t) => t.tool) };
|
|
413
|
+
});
|
|
318
414
|
server.setRequestHandler(
|
|
319
|
-
|
|
320
|
-
async (request) => this.handleToolCall(
|
|
415
|
+
"tools/call",
|
|
416
|
+
async (request) => this.handleToolCall(
|
|
417
|
+
request.params.name,
|
|
418
|
+
request.params.arguments ?? {},
|
|
419
|
+
request.params._meta,
|
|
420
|
+
signal
|
|
421
|
+
)
|
|
321
422
|
);
|
|
322
423
|
return server;
|
|
323
424
|
}
|
|
324
|
-
async handleToolCall(resourceId, rawArgs, signal) {
|
|
425
|
+
async handleToolCall(resourceId, rawArgs, meta, signal) {
|
|
325
426
|
const context = this.context;
|
|
326
427
|
if (!context) {
|
|
327
428
|
return errorResult(new CommerceError("INTERNAL_ERROR", "MCP adapter is not running."));
|
|
328
429
|
}
|
|
430
|
+
const resource = this.toolsByResourceId.get(resourceId);
|
|
431
|
+
if (!resource) {
|
|
432
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Unknown tool "${resourceId}".`);
|
|
433
|
+
}
|
|
329
434
|
try {
|
|
330
|
-
const resource = this.toolsByResourceId.get(resourceId);
|
|
331
|
-
if (!resource) {
|
|
332
|
-
return errorResult(
|
|
333
|
-
new CommerceError("RESOURCE_NOT_FOUND", `Unknown tool "${resourceId}".`, { resourceId })
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
435
|
const requestId = context.ids.next("mcp");
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
requestId
|
|
341
|
-
);
|
|
436
|
+
const fields = extractReservedInputFields(rawArgs, resource, requestId);
|
|
437
|
+
const { input, authorization } = fields;
|
|
438
|
+
const payment = paymentFromMeta(meta, resource) ?? fields.payment;
|
|
342
439
|
const request = {
|
|
343
440
|
requestId,
|
|
344
441
|
resourceId,
|
|
@@ -369,7 +466,7 @@ var McpProtocolAdapter = class {
|
|
|
369
466
|
{ adapter: "mcp", resourceId, err: toLogInfo(error) },
|
|
370
467
|
"mcp adapter: execution failed"
|
|
371
468
|
);
|
|
372
|
-
return errorResult(error);
|
|
469
|
+
return errorResult(error, resource.paymentMethods[0]);
|
|
373
470
|
}
|
|
374
471
|
}
|
|
375
472
|
/**
|
|
@@ -400,6 +497,67 @@ var McpProtocolAdapter = class {
|
|
|
400
497
|
else this.inFlightToolCalls--;
|
|
401
498
|
}
|
|
402
499
|
};
|
|
500
|
+
function toWebRequest(req, body, signal) {
|
|
501
|
+
const headers = new Headers();
|
|
502
|
+
for (const [name, value] of Object.entries(req.headers)) {
|
|
503
|
+
if (value !== void 0) headers.set(name, Array.isArray(value) ? value.join(", ") : value);
|
|
504
|
+
}
|
|
505
|
+
return new Request(new URL(req.url ?? "/", "http://localhost"), {
|
|
506
|
+
method: req.method ?? "POST",
|
|
507
|
+
headers,
|
|
508
|
+
body,
|
|
509
|
+
signal
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
function isModernRequest(req) {
|
|
513
|
+
if (req.headers["mcp-method"] !== void 0) return true;
|
|
514
|
+
const version = req.headers["mcp-protocol-version"];
|
|
515
|
+
return typeof version === "string" && version >= MCP_MODERN_PROTOCOL_REVISION;
|
|
516
|
+
}
|
|
517
|
+
function parseJson(body) {
|
|
518
|
+
try {
|
|
519
|
+
return JSON.parse(body);
|
|
520
|
+
} catch {
|
|
521
|
+
return void 0;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
function malformedToolCall(message) {
|
|
525
|
+
if (!isRecord(message) || message["method"] !== "tools/call") return void 0;
|
|
526
|
+
const id = message["id"];
|
|
527
|
+
if (typeof id !== "string" && typeof id !== "number") return void 0;
|
|
528
|
+
const params = message["params"];
|
|
529
|
+
let problem;
|
|
530
|
+
if (!isRecord(params)) problem = "params must be an object";
|
|
531
|
+
else if (typeof params["name"] !== "string") problem = '"name" must be a string';
|
|
532
|
+
else if (params["arguments"] !== void 0 && !isRecord(params["arguments"])) {
|
|
533
|
+
problem = '"arguments" must be an object';
|
|
534
|
+
} else if (params["_meta"] !== void 0 && !isRecord(params["_meta"])) {
|
|
535
|
+
problem = '"_meta" must be an object';
|
|
536
|
+
}
|
|
537
|
+
return problem === void 0 ? void 0 : { id, message: `Invalid tools/call params: ${problem}.` };
|
|
538
|
+
}
|
|
539
|
+
async function writeWebResponse(response, res) {
|
|
540
|
+
res.writeHead(response.status, Object.fromEntries(response.headers));
|
|
541
|
+
if (response.body !== null) {
|
|
542
|
+
for await (const chunk of response.body) {
|
|
543
|
+
if (res.destroyed) break;
|
|
544
|
+
res.write(chunk);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
res.end();
|
|
548
|
+
}
|
|
549
|
+
function paymentFromMeta(meta, resource) {
|
|
550
|
+
const method = resource.paymentMethods[0];
|
|
551
|
+
if (method === "x402") {
|
|
552
|
+
const payload = x402McpPaymentSubmission(meta?.[X402_MCP_PAYMENT_META_KEY]);
|
|
553
|
+
if (payload !== void 0) return { method, payload };
|
|
554
|
+
}
|
|
555
|
+
const credential = meta?.[MPP_MCP_CREDENTIAL_META_KEY];
|
|
556
|
+
if (method === "mpp" && isRecord(credential)) {
|
|
557
|
+
return { method, payload: JSON.stringify(credential) };
|
|
558
|
+
}
|
|
559
|
+
return void 0;
|
|
560
|
+
}
|
|
403
561
|
function createMcpAdapter(options) {
|
|
404
562
|
return new McpProtocolAdapter(options);
|
|
405
563
|
}
|