@warlock.js/ai-tools 5.33.0 → 5.34.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/CHANGELOG.md +15 -103
- package/cjs/index.cjs +123 -14
- package/cjs/index.cjs.map +1 -1
- package/esm/contracts/index.d.mts +2 -2
- package/esm/contracts/mcp-serve.type.d.mts +48 -3
- package/esm/contracts/mcp-serve.type.d.mts.map +1 -1
- package/esm/index.d.mts +3 -2
- package/esm/index.mjs +2 -1
- package/esm/mcp/error.d.mts +9 -0
- package/esm/mcp/error.d.mts.map +1 -0
- package/esm/mcp/error.mjs +24 -0
- package/esm/mcp/error.mjs.map +1 -0
- package/esm/mcp/index.d.mts +2 -1
- package/esm/mcp/index.d.mts.map +1 -1
- package/esm/mcp/index.mjs +2 -1
- package/esm/mcp/index.mjs.map +1 -1
- package/esm/mcp/serve-http.mjs +73 -6
- package/esm/mcp/serve-http.mjs.map +1 -1
- package/esm/mcp/serve.d.mts.map +1 -1
- package/esm/mcp/serve.mjs +25 -4
- package/esm/mcp/serve.mjs.map +1 -1
- package/esm/register.d.mts +13 -52
- package/esm/register.d.mts.map +1 -1
- package/esm/register.mjs +4 -5
- package/esm/register.mjs.map +1 -1
- package/llms-full.txt +123 -2
- package/llms.txt +1 -1
- package/package.json +2 -2
- package/skills/expose-as-mcp-server/SKILL.md +102 -2
- package/skills/use-web-and-http-tools/SKILL.md +21 -0
|
@@ -2,5 +2,5 @@ import { FetchUrlExtract, FetchUrlInput, FetchUrlOptions, FetchUrlResult, Search
|
|
|
2
2
|
import { HttpMethod, HttpRequestInput, HttpRequestOptions, HttpRequestResult } from "./http.type.mjs";
|
|
3
3
|
import { CalculatorInput, CalculatorOptions, CalculatorResult, DateTimeInput, DateTimeOp, DateTimeOptions, DateTimeResult } from "./utility.type.mjs";
|
|
4
4
|
import { JsonRpcError, JsonRpcId, JsonRpcMessage, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, JsonRpcVersion, McpClient, McpClientOptions, McpContentBlock, McpToolCallResult, McpToolDescriptor, McpTransport } from "./mcp.type.mjs";
|
|
5
|
-
import { McpBeforeCall, McpCallRefusal, McpGroupedToolSource, McpHttpAuth, McpHttpRequestInfo, McpHttpServeHandle, McpHttpServeTransport, McpPromptArgument, McpPromptMessage, McpServeCallContext, McpServeHandle, McpServeOptions, McpServePrompt, McpServeResource, McpServeSource, McpServeToolContext, McpServeTransport, McpServer, McpStdioServeTransport, McpToolSource } from "./mcp-serve.type.mjs";
|
|
6
|
-
export type { CalculatorInput, CalculatorOptions, CalculatorResult, DateTimeInput, DateTimeOp, DateTimeOptions, DateTimeResult, FetchUrlExtract, FetchUrlInput, FetchUrlOptions, FetchUrlResult, HttpMethod, HttpRequestInput, HttpRequestOptions, HttpRequestResult, JsonRpcError, JsonRpcId, JsonRpcMessage, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, JsonRpcVersion, McpBeforeCall, McpCallRefusal, McpClient, McpClientOptions, McpContentBlock, McpGroupedToolSource, McpHttpAuth, McpHttpRequestInfo, McpHttpServeHandle, McpHttpServeTransport, McpPromptArgument, McpPromptMessage, McpServeCallContext, McpServeHandle, McpServeOptions, McpServePrompt, McpServeResource, McpServeSource, McpServeToolContext, McpServeTransport, McpServer, McpStdioServeTransport, McpToolCallResult, McpToolDescriptor, McpToolSource, McpTransport, SearchProvider, WebSearchInput, WebSearchOptions, WebSearchResult, WebSearchResultItem };
|
|
5
|
+
import { McpBeforeCall, McpCallRefusal, McpGroupedToolSource, McpHttpAuth, McpHttpRequestInfo, McpHttpRoute, McpHttpRouteContext, McpHttpRouteResponse, McpHttpServeHandle, McpHttpServeTransport, McpListFilter, McpPromptArgument, McpPromptMessage, McpServeCallContext, McpServeHandle, McpServeOptions, McpServePrompt, McpServeResource, McpServeSource, McpServeToolContext, McpServeTransport, McpServer, McpStdioServeTransport, McpToolSource, McpValidateCallParams } from "./mcp-serve.type.mjs";
|
|
6
|
+
export type { CalculatorInput, CalculatorOptions, CalculatorResult, DateTimeInput, DateTimeOp, DateTimeOptions, DateTimeResult, FetchUrlExtract, FetchUrlInput, FetchUrlOptions, FetchUrlResult, HttpMethod, HttpRequestInput, HttpRequestOptions, HttpRequestResult, JsonRpcError, JsonRpcId, JsonRpcMessage, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, JsonRpcVersion, McpBeforeCall, McpCallRefusal, McpClient, McpClientOptions, McpContentBlock, McpGroupedToolSource, McpHttpAuth, McpHttpRequestInfo, McpHttpRoute, McpHttpRouteContext, McpHttpRouteResponse, McpHttpServeHandle, McpHttpServeTransport, McpListFilter, McpPromptArgument, McpPromptMessage, McpServeCallContext, McpServeHandle, McpServeOptions, McpServePrompt, McpServeResource, McpServeSource, McpServeToolContext, McpServeTransport, McpServer, McpStdioServeTransport, McpToolCallResult, McpToolDescriptor, McpToolSource, McpTransport, McpValidateCallParams, SearchProvider, WebSearchInput, WebSearchOptions, WebSearchResult, WebSearchResultItem };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JsonRpcId, McpContentBlock, McpToolCallResult } from "./mcp.type.mjs";
|
|
1
|
+
import { JsonRpcId, McpContentBlock, McpToolCallResult, McpToolDescriptor } from "./mcp.type.mjs";
|
|
2
2
|
import { JsonSchemaTarget, ToolContract, ToolRequestContext } from "@warlock.js/ai";
|
|
3
3
|
//#region ../../packages/ai-tools/src/contracts/mcp-serve.type.d.ts
|
|
4
4
|
/**
|
|
@@ -71,6 +71,34 @@ export type McpHttpAuth = {
|
|
|
71
71
|
*/
|
|
72
72
|
authenticate?: (request: McpHttpRequestInfo) => unknown | Promise<unknown>;
|
|
73
73
|
};
|
|
74
|
+
/** Authenticated context for an extra HTTP route; there is no JSON-RPC id. */
|
|
75
|
+
export type McpHttpRouteContext = McpServeCallContext & {
|
|
76
|
+
request: McpHttpRequestInfo;
|
|
77
|
+
/** Parsed request URL; use searchParams to read query parameters. */
|
|
78
|
+
url: URL;
|
|
79
|
+
/** UTF-8 request body, subject to the transport's maxBodyBytes limit. */
|
|
80
|
+
body: string;
|
|
81
|
+
signal: AbortSignal;
|
|
82
|
+
};
|
|
83
|
+
/** JSON response from an extra HTTP route. Omit body for an empty response. */
|
|
84
|
+
export type McpHttpRouteResponse = {
|
|
85
|
+
/** @default 200 */
|
|
86
|
+
status?: number;
|
|
87
|
+
headers?: Record<string, string>;
|
|
88
|
+
body?: unknown;
|
|
89
|
+
};
|
|
90
|
+
/** An exact method/path route beneath the MCP endpoint, sharing its auth. */
|
|
91
|
+
export type McpHttpRoute = {
|
|
92
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
93
|
+
/**
|
|
94
|
+
* Suffix such as /tool-policy, appended to the MCP path without its trailing
|
|
95
|
+
* slash. Nonempty segments use letters, digits, _, -, . or ~; . and .. are
|
|
96
|
+
* forbidden. No query, fragment, escapes, wildcard or trailing slash.
|
|
97
|
+
* The MCP endpoint itself is reserved for every method. Duplicates fail start().
|
|
98
|
+
*/
|
|
99
|
+
path: string;
|
|
100
|
+
handler(context: McpHttpRouteContext): McpHttpRouteResponse | Promise<McpHttpRouteResponse>;
|
|
101
|
+
};
|
|
74
102
|
/**
|
|
75
103
|
* Serve over the MCP Streamable HTTP transport.
|
|
76
104
|
*
|
|
@@ -95,13 +123,15 @@ export type McpHttpServeTransport = {
|
|
|
95
123
|
*/
|
|
96
124
|
port?: number;
|
|
97
125
|
/**
|
|
98
|
-
* The
|
|
126
|
+
* The MCP endpoint path. Other paths answer 404 unless registered in routes.
|
|
99
127
|
*
|
|
100
128
|
* @default "/mcp"
|
|
101
129
|
*/
|
|
102
130
|
path?: string;
|
|
103
131
|
/** Bearer, authorize and/or authenticate checks. Required off loopback. */
|
|
104
132
|
auth?: McpHttpAuth;
|
|
133
|
+
/** Extra HTTP routes with the same Host/Origin and authentication checks. */
|
|
134
|
+
routes?: readonly McpHttpRoute[];
|
|
105
135
|
/**
|
|
106
136
|
* Extra browser origins allowed to call the endpoint, as full origins
|
|
107
137
|
* (`"https://app.example.com"`). Loopback origins (`http://localhost:*`,
|
|
@@ -171,6 +201,13 @@ export type McpCallRefusal = McpToolCallResult & {
|
|
|
171
201
|
};
|
|
172
202
|
/** Validate policy before execute; a refusal bypasses result serialization. */
|
|
173
203
|
export type McpBeforeCall = (context: McpServeToolContext, tool: ToolContract, input: unknown) => void | McpCallRefusal | Promise<void | McpCallRefusal>;
|
|
204
|
+
/** Filter fresh wire descriptors for discovery only; hidden tools stay callable. */
|
|
205
|
+
export type McpListFilter = (context: McpServeToolContext, tools: readonly McpToolDescriptor[]) => readonly McpToolDescriptor[] | Promise<readonly McpToolDescriptor[]>;
|
|
206
|
+
/**
|
|
207
|
+
* Inspect the complete tools/call params before lookup, schema validation or
|
|
208
|
+
* beforeCall. Treat params as read-only; throw McpError for an app-defined error.
|
|
209
|
+
*/
|
|
210
|
+
export type McpValidateCallParams = (context: McpServeToolContext, params: Readonly<Record<string, unknown>>) => void | Promise<void>;
|
|
174
211
|
/**
|
|
175
212
|
* A static MCP resource served by `resources/list` + `resources/read`.
|
|
176
213
|
*/
|
|
@@ -265,9 +302,17 @@ export interface McpServeOptions {
|
|
|
265
302
|
/**
|
|
266
303
|
* Runs after schema validation and before invocation. Input is the validated
|
|
267
304
|
* value and should be treated as read-only. Return an isError:true native
|
|
268
|
-
* result to refuse
|
|
305
|
+
* result to refuse. McpError preserves its code/message/data; other throws
|
|
306
|
+
* answer with a JSON-RPC invalid-params error.
|
|
269
307
|
*/
|
|
270
308
|
beforeCall?: McpBeforeCall;
|
|
309
|
+
/** Per-request discovery filter; never changes tools/call lookup or policy. */
|
|
310
|
+
listFilter?: McpListFilter;
|
|
311
|
+
/**
|
|
312
|
+
* Optional envelope policy. Receives all params, including unknown fields,
|
|
313
|
+
* task and _meta. By default extra fields are ignored (no task capability).
|
|
314
|
+
*/
|
|
315
|
+
validateCallParams?: McpValidateCallParams;
|
|
271
316
|
/**
|
|
272
317
|
* Prefix each tool of a grouped source (`{ groups: { … } }`) with its
|
|
273
318
|
* group name. `true` exposes `<group>_<tool>`; a function builds the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-serve.type.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/contracts/mcp-serve.type.ts"],"mappings":";;;;;;;;;;;;;YAcY;EAAkB,SAAS;IAAmB;;;;;;;;;YAU9C;;EAEV,QAAQ,eAAe;;;;;;;;YASb,iBAAiB,gBAAgB;;;;;;YAOjC,qBAAqB;;EAE/B;;EAEA;;;;;;;YAQU;;;;;;;;EAQV;;;;;;EAMA,aAAa,SAAS,iCAAiC;;;;;;EAMvD,gBAAgB,SAAS,iCAAiC;;;;;;;;
|
|
1
|
+
{"version":3,"file":"mcp-serve.type.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/contracts/mcp-serve.type.ts"],"mappings":";;;;;;;;;;;;;YAcY;EAAkB,SAAS;IAAmB;;;;;;;;;YAU9C;;EAEV,QAAQ,eAAe;;;;;;;;YASb,iBAAiB,gBAAgB;;;;;;YAOjC,qBAAqB;;EAE/B;;EAEA;;;;;;;YAQU;;;;;;;;EAQV;;;;;;EAMA,aAAa,SAAS,iCAAiC;;;;;;EAMvD,gBAAgB,SAAS,iCAAiC;;;YAIhD,sBAAsB;EAChC,SAAS;;EAET,KAAK;;EAEL;EACA,QAAQ;;;YAIE;;EAEV;EACA,UAAU;EACV;;;YAIU;EACV;;;;;;;EAOA;EACA,QAAQ,SAAS,sBAAsB,uBAAuB,QAAQ;;;;;;;;YAS5D;;EAEV;;;;;;;EAOA;;;;;;;EAOA;;;;;;EAMA;;EAEA,OAAO;;EAEP,kBAAkB;;;;;;;;;EASlB;;;;;;;EAOA;;;;;;EAMA;;;;;;;EAOA;;;;;;;;YASU,oBAAoB,yBAAyB;;YAG7C;EACV;;EAEA;;EAEA,qBAAqB,SAAS,kCAAkC;;;;;;YAOtD;;EAEV,UAAU;;EAEV,YAAY;;EAEZ;;EAEA,SAAS;;;YAIC,sBAAsB;EAAwB,WAAW;;;YAGzD,iBAAiB;EAAsB;;;YAGvC,iBACV,SAAS,qBACT,MAAM,cACN,0BACU,iBAAiB,eAAe;;YAGhC,iBACV,SAAS,qBACT,gBAAgB,iCACJ,sBAAsB,iBAAiB;;;;;YAMzC,yBACV,SAAS,qBACT,QAAQ,SAAS,oCACP;;;;YAKA;;EAEV;;EAEA;;EAEA;;EAEA;;EAEA;;;;;EAKA,KAAK,SAAS,+BAA+B,aAAa,iBAAiB;;;YAIjE;;EAEV;;EAEA;;EAEA;;;YAIU;;EAEV;;EAEA,SAAS;;;;;YAMC;;EAEV;;EAEA;;EAEA;;EAEA,YAAY;;;;;;EAMZ,IACE,MAAM,wBACN,SAAS,+BACC,qBAAqB,iBAAiB;;;;;;iBAOnC;;EAEf;;;;;;EAMA;;;;;;EAMA,YAAY;;;;;;;;EAQZ,eAAe;;;;;;;;;EASf;;;;;;;EAOA,aAAa;;EAEb,aAAa;;;;;EAKb,qBAAqB;;;;;;;;EAQrB,8BAA8B,eAAe;;EAE7C,YAAY;;EAEZ,UAAU;;;;;;iBAOK;;EAEf;;;;;;EAMA,SAAS;;;iBAIM,2BAA2B;;EAE1C;;;;;iBAMe,UAAU,gBAAgB,iBAAiB;;;;;;EAM1D,SAAS,QAAQ;;EAEjB,QAAQ"}
|
package/esm/index.d.mts
CHANGED
|
@@ -2,10 +2,11 @@ import { FetchUrlExtract, FetchUrlInput, FetchUrlOptions, FetchUrlResult, Search
|
|
|
2
2
|
import { HttpMethod, HttpRequestInput, HttpRequestOptions, HttpRequestResult } from "./contracts/http.type.mjs";
|
|
3
3
|
import { CalculatorInput, CalculatorOptions, CalculatorResult, DateTimeInput, DateTimeOp, DateTimeOptions, DateTimeResult } from "./contracts/utility.type.mjs";
|
|
4
4
|
import { JsonRpcError, JsonRpcId, JsonRpcMessage, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, JsonRpcVersion, McpClient, McpClientOptions, McpContentBlock, McpToolCallResult, McpToolDescriptor, McpTransport } from "./contracts/mcp.type.mjs";
|
|
5
|
-
import { McpBeforeCall, McpCallRefusal, McpGroupedToolSource, McpHttpAuth, McpHttpRequestInfo, McpHttpServeHandle, McpHttpServeTransport, McpPromptArgument, McpPromptMessage, McpServeCallContext, McpServeHandle, McpServeOptions, McpServePrompt, McpServeResource, McpServeSource, McpServeToolContext, McpServeTransport, McpServer, McpStdioServeTransport, McpToolSource } from "./contracts/mcp-serve.type.mjs";
|
|
5
|
+
import { McpBeforeCall, McpCallRefusal, McpGroupedToolSource, McpHttpAuth, McpHttpRequestInfo, McpHttpRoute, McpHttpRouteContext, McpHttpRouteResponse, McpHttpServeHandle, McpHttpServeTransport, McpListFilter, McpPromptArgument, McpPromptMessage, McpServeCallContext, McpServeHandle, McpServeOptions, McpServePrompt, McpServeResource, McpServeSource, McpServeToolContext, McpServeTransport, McpServer, McpStdioServeTransport, McpToolSource, McpValidateCallParams } from "./contracts/mcp-serve.type.mjs";
|
|
6
6
|
import "./contracts/index.mjs";
|
|
7
7
|
import { McpServeRequestHandler, createServeHandler, serve } from "./mcp/serve.mjs";
|
|
8
8
|
import { SUPPORTED_PROTOCOL_VERSIONS } from "./mcp/protocol.mjs";
|
|
9
|
+
import { McpError } from "./mcp/error.mjs";
|
|
9
10
|
import { McpNativeResult, mcpResult } from "./mcp/result.mjs";
|
|
10
11
|
import { jsonSchemaToStandard } from "./mcp/json-schema-to-standard.mjs";
|
|
11
12
|
import { McpTransportClient } from "./mcp/transport.type.mjs";
|
|
@@ -20,6 +21,6 @@ import { calculatorTool } from "./utility/calculator.mjs";
|
|
|
20
21
|
import { dateTimeTool } from "./utility/date-time.mjs";
|
|
21
22
|
import "./utility/index.mjs";
|
|
22
23
|
import { CalculatorError, CalculatorErrorOptions, CalculatorFailure, DateTimeError, DateTimeErrorOptions, DateTimeFailure, HttpPolicyError, HttpPolicyErrorOptions, HttpPolicyFailure, McpServeError, McpServeErrorOptions, McpServeFailure, McpTransportError, McpTransportErrorOptions, McpTransportFailure, WebToolError, WebToolErrorOptions, WebToolFailure } from "./errors.mjs";
|
|
23
|
-
export { type AiToolsNamespace, CalculatorError, type CalculatorErrorOptions, type CalculatorFailure, type CalculatorInput, type CalculatorOptions, type CalculatorResult, DateTimeError, type DateTimeErrorOptions, type DateTimeFailure, type DateTimeInput, type DateTimeOp, type DateTimeOptions, type DateTimeResult, type FetchUrlExtract, type FetchUrlInput, type FetchUrlOptions, type FetchUrlResult, type HttpMethod, HttpPolicyError, type HttpPolicyErrorOptions, type HttpPolicyFailure, type HttpRequestInput, type HttpRequestOptions, type HttpRequestResult, type JsonRpcClientHandle, type JsonRpcError, type JsonRpcId, type JsonRpcMessage, type JsonRpcNotification, type JsonRpcRequest, type JsonRpcResponse, type JsonRpcVersion, type McpBeforeCall, type McpCallRefusal, type McpClient, type McpClientOptions, type McpContentBlock, type McpFactory, type McpGroupedToolSource, type McpHttpAuth, type McpHttpRequestInfo, type McpHttpServeHandle, type McpHttpServeTransport, type McpNativeResult, type McpPromptArgument, type McpPromptMessage, type McpServeCallContext, McpServeError, type McpServeErrorOptions, type McpServeFailure, type McpServeHandle, type McpServeOptions, type McpServePrompt, type McpServeRequestHandler, type McpServeResource, type McpServeSource, type McpServeToolContext, type McpServeTransport, type McpServer, type McpStdioServeTransport, type McpToolCallResult, type McpToolDescriptor, type McpToolSource, type McpTransport, type McpTransportClient, McpTransportError, type McpTransportErrorOptions, type McpTransportFailure, SUPPORTED_PROTOCOL_VERSIONS, type SearchProvider, type WebSearchInput, type WebSearchOptions, type WebSearchResult, type WebSearchResultItem, WebToolError, type WebToolErrorOptions, type WebToolFailure, calculatorTool, createJsonRpcClient, createServeHandler, createTransport, dateTimeTool, fetchUrlTool, httpRequestTool, jsonSchemaToStandard, mcp, mcpResult, serve, webSearchTool };
|
|
24
|
+
export { type AiToolsNamespace, CalculatorError, type CalculatorErrorOptions, type CalculatorFailure, type CalculatorInput, type CalculatorOptions, type CalculatorResult, DateTimeError, type DateTimeErrorOptions, type DateTimeFailure, type DateTimeInput, type DateTimeOp, type DateTimeOptions, type DateTimeResult, type FetchUrlExtract, type FetchUrlInput, type FetchUrlOptions, type FetchUrlResult, type HttpMethod, HttpPolicyError, type HttpPolicyErrorOptions, type HttpPolicyFailure, type HttpRequestInput, type HttpRequestOptions, type HttpRequestResult, type JsonRpcClientHandle, type JsonRpcError, type JsonRpcId, type JsonRpcMessage, type JsonRpcNotification, type JsonRpcRequest, type JsonRpcResponse, type JsonRpcVersion, type McpBeforeCall, type McpCallRefusal, type McpClient, type McpClientOptions, type McpContentBlock, McpError, type McpFactory, type McpGroupedToolSource, type McpHttpAuth, type McpHttpRequestInfo, type McpHttpRoute, type McpHttpRouteContext, type McpHttpRouteResponse, type McpHttpServeHandle, type McpHttpServeTransport, type McpListFilter, type McpNativeResult, type McpPromptArgument, type McpPromptMessage, type McpServeCallContext, McpServeError, type McpServeErrorOptions, type McpServeFailure, type McpServeHandle, type McpServeOptions, type McpServePrompt, type McpServeRequestHandler, type McpServeResource, type McpServeSource, type McpServeToolContext, type McpServeTransport, type McpServer, type McpStdioServeTransport, type McpToolCallResult, type McpToolDescriptor, type McpToolSource, type McpTransport, type McpTransportClient, McpTransportError, type McpTransportErrorOptions, type McpTransportFailure, type McpValidateCallParams, SUPPORTED_PROTOCOL_VERSIONS, type SearchProvider, type WebSearchInput, type WebSearchOptions, type WebSearchResult, type WebSearchResultItem, WebToolError, type WebToolErrorOptions, type WebToolFailure, calculatorTool, createJsonRpcClient, createServeHandler, createTransport, dateTimeTool, fetchUrlTool, httpRequestTool, jsonSchemaToStandard, mcp, mcpResult, serve, webSearchTool };
|
|
24
25
|
import "./mcp/index.mjs";
|
|
25
26
|
import "./register.mjs";
|
package/esm/index.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { CalculatorError, DateTimeError, HttpPolicyError, McpServeError, McpTran
|
|
|
2
2
|
import { httpRequestTool } from "./http/http-request.mjs";
|
|
3
3
|
import { jsonSchemaToStandard } from "./mcp/json-schema-to-standard.mjs";
|
|
4
4
|
import { createJsonRpcClient, createTransport } from "./mcp/transport.mjs";
|
|
5
|
+
import { McpError } from "./mcp/error.mjs";
|
|
5
6
|
import { SUPPORTED_PROTOCOL_VERSIONS } from "./mcp/protocol.mjs";
|
|
6
7
|
import { mcpResult } from "./mcp/result.mjs";
|
|
7
8
|
import { createServeHandler, serve } from "./mcp/serve.mjs";
|
|
@@ -14,4 +15,4 @@ import "./register.mjs";
|
|
|
14
15
|
import "./web/index.mjs";
|
|
15
16
|
import "./utility/index.mjs";
|
|
16
17
|
|
|
17
|
-
export { CalculatorError, DateTimeError, HttpPolicyError, McpServeError, McpTransportError, SUPPORTED_PROTOCOL_VERSIONS, WebToolError, calculatorTool, createJsonRpcClient, createServeHandler, createTransport, dateTimeTool, fetchUrlTool, httpRequestTool, jsonSchemaToStandard, mcp, mcpResult, serve, webSearchTool };
|
|
18
|
+
export { CalculatorError, DateTimeError, HttpPolicyError, McpError, McpServeError, McpTransportError, SUPPORTED_PROTOCOL_VERSIONS, WebToolError, calculatorTool, createJsonRpcClient, createServeHandler, createTransport, dateTimeTool, fetchUrlTool, httpRequestTool, jsonSchemaToStandard, mcp, mcpResult, serve, webSearchTool };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region ../../packages/ai-tools/src/mcp/error.d.ts
|
|
2
|
+
/** An application-defined JSON-RPC error thrown by MCP serving policy hooks. */
|
|
3
|
+
export declare class McpError extends Error {
|
|
4
|
+
readonly code: number;
|
|
5
|
+
readonly data: unknown;
|
|
6
|
+
constructor(code: number, message: string, data?: unknown);
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
//# sourceMappingURL=error.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/error.ts"],"mappings":";;qBACa,iBAAiB;WACZ;WAEA;EAEhB,YAAmB,cAAc,iBAAiB"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region ../../packages/ai-tools/src/mcp/error.ts
|
|
2
|
+
/** An application-defined JSON-RPC error thrown by MCP serving policy hooks. */
|
|
3
|
+
var McpError = class extends Error {
|
|
4
|
+
constructor(code, message, data) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "McpError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.data = data;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Recognize our error and the SDK's McpError without a runtime SDK dependency.
|
|
13
|
+
* SDK source (checked 2026-10-19):
|
|
14
|
+
* https://github.com/modelcontextprotocol/typescript-sdk/blob/v1.x/src/types.ts
|
|
15
|
+
* Only explicitly named errors with an integer code opt in; ordinary throws
|
|
16
|
+
* retain the existing mapping. Preserve the error's message verbatim.
|
|
17
|
+
*/
|
|
18
|
+
function isMcpError(cause) {
|
|
19
|
+
return cause instanceof Error && cause.name === "McpError" && "code" in cause && typeof cause.code === "number" && Number.isInteger(cause.code);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { McpError, isMcpError };
|
|
24
|
+
//# sourceMappingURL=error.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.mjs","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/error.ts"],"sourcesContent":["/** An application-defined JSON-RPC error thrown by MCP serving policy hooks. */\nexport class McpError extends Error {\n public readonly code: number;\n\n public readonly data: unknown;\n\n public constructor(code: number, message: string, data?: unknown) {\n super(message);\n this.name = \"McpError\";\n this.code = code;\n this.data = data;\n }\n}\n\n/**\n * Recognize our error and the SDK's McpError without a runtime SDK dependency.\n * SDK source (checked 2026-10-19):\n * https://github.com/modelcontextprotocol/typescript-sdk/blob/v1.x/src/types.ts\n * Only explicitly named errors with an integer code opt in; ordinary throws\n * retain the existing mapping. Preserve the error's message verbatim.\n */\nexport function isMcpError(cause: unknown): cause is Error & { code: number; data?: unknown } {\n return (\n cause instanceof Error &&\n cause.name === \"McpError\" &&\n \"code\" in cause &&\n typeof cause.code === \"number\" &&\n Number.isInteger(cause.code)\n );\n}\n"],"mappings":";;AACA,IAAa,WAAb,cAA8B,MAAM;CAKlC,AAAO,YAAY,MAAc,SAAiB,MAAgB;EAChE,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,OAAO;CACd;AACF;;;;;;;;AASA,SAAgB,WAAW,OAAmE;CAC5F,OACE,iBAAiB,SACjB,MAAM,SAAS,cACf,UAAU,SACV,OAAO,MAAM,SAAS,YACtB,OAAO,UAAU,MAAM,IAAI;AAE/B"}
|
package/esm/mcp/index.d.mts
CHANGED
|
@@ -3,6 +3,7 @@ import { McpHttpServeHandle, McpHttpServeTransport, McpServeOptions, McpServeSou
|
|
|
3
3
|
import "../contracts/index.mjs";
|
|
4
4
|
import { McpServeRequestHandler, createServeHandler, serve } from "./serve.mjs";
|
|
5
5
|
import { SUPPORTED_PROTOCOL_VERSIONS } from "./protocol.mjs";
|
|
6
|
+
import { McpError } from "./error.mjs";
|
|
6
7
|
import { McpNativeResult, mcpResult } from "./result.mjs";
|
|
7
8
|
import { jsonSchemaToStandard } from "./json-schema-to-standard.mjs";
|
|
8
9
|
import { McpTransportClient } from "./transport.type.mjs";
|
|
@@ -47,5 +48,5 @@ export interface McpFactory {
|
|
|
47
48
|
*/
|
|
48
49
|
export declare const mcp: McpFactory;
|
|
49
50
|
//#endregion
|
|
50
|
-
export { type McpNativeResult, type McpServeRequestHandler, type McpTransportClient, SUPPORTED_PROTOCOL_VERSIONS, createServeHandler, mcpResult, serve };
|
|
51
|
+
export { McpError, type McpNativeResult, type McpServeRequestHandler, type McpTransportClient, SUPPORTED_PROTOCOL_VERSIONS, createServeHandler, mcpResult, serve };
|
|
51
52
|
//# sourceMappingURL=index.d.mts.map
|
package/esm/mcp/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;iBA+BiB;;;;;;;;GAQd,QAAQ,cAAc,UAAU,mBAAmB;;;;;;;;;EASpD,MACE,QAAQ,gBACR,SAAS;IAAoB,WAAW;MACvC,UAAU;EACb,MAAM,QAAQ,gBAAgB,SAAS,kBAAkB;;;;;;;;;;qBAW9C,KAAK"}
|
package/esm/mcp/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsonSchemaToStandard } from "./json-schema-to-standard.mjs";
|
|
2
2
|
import { createJsonRpcClient, createTransport } from "./transport.mjs";
|
|
3
3
|
import { mcp as mcp$1 } from "./client.mjs";
|
|
4
|
+
import { McpError } from "./error.mjs";
|
|
4
5
|
import { SUPPORTED_PROTOCOL_VERSIONS } from "./protocol.mjs";
|
|
5
6
|
import { mcpResult } from "./result.mjs";
|
|
6
7
|
import { createServeHandler, serve } from "./serve.mjs";
|
|
@@ -17,5 +18,5 @@ import { createServeHandler, serve } from "./serve.mjs";
|
|
|
17
18
|
const mcp = Object.assign((server, options) => mcp$1(server, options), { serve });
|
|
18
19
|
|
|
19
20
|
//#endregion
|
|
20
|
-
export { SUPPORTED_PROTOCOL_VERSIONS, createServeHandler, mcp, mcpResult, serve };
|
|
21
|
+
export { McpError, SUPPORTED_PROTOCOL_VERSIONS, createServeHandler, mcp, mcpResult, serve };
|
|
21
22
|
//# sourceMappingURL=index.mjs.map
|
package/esm/mcp/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["mcpClient"],"sources":["../../../../../../../../packages/ai-tools/src/mcp/index.ts"],"sourcesContent":["import type {\n McpClient,\n McpClientOptions,\n McpHttpServeHandle,\n McpHttpServeTransport,\n McpServeOptions,\n McpServeSource,\n McpServer,\n McpTransport,\n} from \"../contracts\";\nimport { mcp as mcpClient } from \"./client\";\nimport { serve } from \"./serve\";\n\nexport { serve, createServeHandler } from \"./serve\";\nexport type { McpServeRequestHandler } from \"./serve\";\nexport { SUPPORTED_PROTOCOL_VERSIONS } from \"./protocol\";\nexport { mcpResult } from \"./result\";\nexport type { McpNativeResult } from \"./result\";\nexport { jsonSchemaToStandard } from \"./json-schema-to-standard\";\nexport { createJsonRpcClient, createTransport } from \"./transport\";\nexport type { JsonRpcClientHandle } from \"./transport\";\nexport type { McpTransportClient } from \"./transport.type\";\n\n/**\n * The callable `ai.mcp` surface — a factory that connects to an external\n * MCP server (Direction A) and also carries `.serve` to expose a local\n * primitive AS an MCP server (Direction B). Modeled as a function with an\n * attached `serve` property, mirroring how `ai.mcp(server).tools()` and\n * `ai.mcp.serve(source, options).start()` read in the design.\n */\nexport interface McpFactory {\n /**\n * Connect to an external MCP server and adapt its tools as agent tools.\n *\n * @param server - The transport config (`{ type: \"stdio\" }` / `{ type: \"http\" }`).\n * @param options - Prefix / filter / per-call timeout, and a `signal`\n * that closes the client (and fails its calls) when it aborts.\n */\n (server: McpTransport, options?: McpClientOptions): McpClient;\n /**\n * Expose a built agent / supervisor / orchestrator (or a literal\n * `ToolContract[]`, or named `{ groups }`) AS an MCP server. Over\n * `{ type: \"http\" }`, `start()` resolves `{ url, close }`.\n *\n * @param source - The tools to expose.\n * @param options - Server name, version, transport, schema dialect, resources, prompts.\n */\n serve(\n source: McpServeSource,\n options: McpServeOptions & { transport: McpHttpServeTransport },\n ): McpServer<McpHttpServeHandle>;\n serve(source: McpServeSource, options: McpServeOptions): McpServer;\n}\n\n/**\n * The `ai.mcp` factory value: the client factory with `.serve` attached.\n * `Object.assign` keeps `mcp` callable (Direction A) while widening it with\n * the `serve` member (Direction B) — one object, both directions. The\n * `declare module \"@warlock.js/ai\"` augmentation and the runtime\n * registration (`ai.mcp = mcp`) live in `../register`, so this barrel is a\n * pure value/type module the registrar consumes.\n */\nexport const mcp: McpFactory = Object.assign(\n (server: McpTransport, options?: McpClientOptions): McpClient => mcpClient(server, options),\n { serve },\n);\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["mcpClient"],"sources":["../../../../../../../../packages/ai-tools/src/mcp/index.ts"],"sourcesContent":["import type {\n McpClient,\n McpClientOptions,\n McpHttpServeHandle,\n McpHttpServeTransport,\n McpServeOptions,\n McpServeSource,\n McpServer,\n McpTransport,\n} from \"../contracts\";\nimport { mcp as mcpClient } from \"./client\";\nimport { serve } from \"./serve\";\n\nexport { serve, createServeHandler } from \"./serve\";\nexport type { McpServeRequestHandler } from \"./serve\";\nexport { SUPPORTED_PROTOCOL_VERSIONS } from \"./protocol\";\nexport { McpError } from \"./error\";\nexport { mcpResult } from \"./result\";\nexport type { McpNativeResult } from \"./result\";\nexport { jsonSchemaToStandard } from \"./json-schema-to-standard\";\nexport { createJsonRpcClient, createTransport } from \"./transport\";\nexport type { JsonRpcClientHandle } from \"./transport\";\nexport type { McpTransportClient } from \"./transport.type\";\n\n/**\n * The callable `ai.mcp` surface — a factory that connects to an external\n * MCP server (Direction A) and also carries `.serve` to expose a local\n * primitive AS an MCP server (Direction B). Modeled as a function with an\n * attached `serve` property, mirroring how `ai.mcp(server).tools()` and\n * `ai.mcp.serve(source, options).start()` read in the design.\n */\nexport interface McpFactory {\n /**\n * Connect to an external MCP server and adapt its tools as agent tools.\n *\n * @param server - The transport config (`{ type: \"stdio\" }` / `{ type: \"http\" }`).\n * @param options - Prefix / filter / per-call timeout, and a `signal`\n * that closes the client (and fails its calls) when it aborts.\n */\n (server: McpTransport, options?: McpClientOptions): McpClient;\n /**\n * Expose a built agent / supervisor / orchestrator (or a literal\n * `ToolContract[]`, or named `{ groups }`) AS an MCP server. Over\n * `{ type: \"http\" }`, `start()` resolves `{ url, close }`.\n *\n * @param source - The tools to expose.\n * @param options - Server name, version, transport, schema dialect, resources, prompts.\n */\n serve(\n source: McpServeSource,\n options: McpServeOptions & { transport: McpHttpServeTransport },\n ): McpServer<McpHttpServeHandle>;\n serve(source: McpServeSource, options: McpServeOptions): McpServer;\n}\n\n/**\n * The `ai.mcp` factory value: the client factory with `.serve` attached.\n * `Object.assign` keeps `mcp` callable (Direction A) while widening it with\n * the `serve` member (Direction B) — one object, both directions. The\n * `declare module \"@warlock.js/ai\"` augmentation and the runtime\n * registration (`ai.mcp = mcp`) live in `../register`, so this barrel is a\n * pure value/type module the registrar consumes.\n */\nexport const mcp: McpFactory = Object.assign(\n (server: McpTransport, options?: McpClientOptions): McpClient => mcpClient(server, options),\n { serve },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;AA+DA,MAAa,MAAkB,OAAO,QACnC,QAAsB,YAA0CA,MAAU,QAAQ,OAAO,GAC1F,EAAE,MAAM,CACV"}
|
package/esm/mcp/serve-http.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import { isIPv4, isIPv6 } from "node:net";
|
|
|
13
13
|
* specification (https://modelcontextprotocol.io/specification/2025-11-25/basic/transports,
|
|
14
14
|
* checked 2026-10-17) on `node:http`, with no SDK dependency:
|
|
15
15
|
*
|
|
16
|
-
* - one endpoint path; each POST carries exactly one JSON-RPC message
|
|
16
|
+
* - one MCP endpoint path (plus optional authenticated app routes); each MCP POST carries exactly one JSON-RPC message
|
|
17
17
|
* (batches were removed from the protocol in 2025-06-18 and get 400);
|
|
18
18
|
* - a request is answered with `Content-Type: application/json` (the spec
|
|
19
19
|
* lets a server pick JSON or SSE per request); a notification or a
|
|
@@ -71,6 +71,35 @@ function hostnameOf(hostHeader) {
|
|
|
71
71
|
function digest(value) {
|
|
72
72
|
return createHash("sha256").update(value, "utf8").digest();
|
|
73
73
|
}
|
|
74
|
+
/** Resolve exact descendant routes; no route may replace the MCP endpoint. */
|
|
75
|
+
function resolveRoutes(path, routes) {
|
|
76
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
77
|
+
const methods = /* @__PURE__ */ new Set([
|
|
78
|
+
"GET",
|
|
79
|
+
"POST",
|
|
80
|
+
"PUT",
|
|
81
|
+
"PATCH",
|
|
82
|
+
"DELETE",
|
|
83
|
+
"HEAD",
|
|
84
|
+
"OPTIONS"
|
|
85
|
+
]);
|
|
86
|
+
if (routes.length > 0) {
|
|
87
|
+
let canonical = false;
|
|
88
|
+
try {
|
|
89
|
+
canonical = !path.startsWith("//") && new URL(path, "http://localhost").pathname === path;
|
|
90
|
+
} catch {}
|
|
91
|
+
if (!canonical) throw new McpServeError("MCP http routes require a canonical endpoint pathname.", { type: "config" });
|
|
92
|
+
}
|
|
93
|
+
for (const route of routes) {
|
|
94
|
+
if (!methods.has(route.method) || !/^\/[A-Za-z0-9._~-]+(?:\/[A-Za-z0-9._~-]+)*$/.test(route.path) || route.path.split("/").some((segment) => segment === "." || segment === "..") || typeof route.handler !== "function") throw new McpServeError("Invalid MCP http route method, path or handler.", { type: "config" });
|
|
95
|
+
const fullPath = `${path.replace(/\/+$/, "")}${route.path}`;
|
|
96
|
+
const byMethod = resolved.get(fullPath) ?? /* @__PURE__ */ new Map();
|
|
97
|
+
if (fullPath === path || byMethod.has(route.method)) throw new McpServeError(`MCP http route "${route.method} ${fullPath}" conflicts.`, { type: "collision" });
|
|
98
|
+
byMethod.set(route.method, { ...route });
|
|
99
|
+
resolved.set(fullPath, byMethod);
|
|
100
|
+
}
|
|
101
|
+
return resolved;
|
|
102
|
+
}
|
|
74
103
|
/** Normalise a configured origin to `scheme://host[:port]`, or throw a config error. */
|
|
75
104
|
function normalizeOrigin(origin) {
|
|
76
105
|
try {
|
|
@@ -120,7 +149,8 @@ function resolveSettings(transport) {
|
|
|
120
149
|
authenticate: auth?.authenticate,
|
|
121
150
|
authRequired,
|
|
122
151
|
allowedOrigins: new Set((transport.allowedOrigins ?? []).map(normalizeOrigin)),
|
|
123
|
-
allowedHosts: new Set((transport.allowedHosts ?? []).map((name) => hostnameOf(name)))
|
|
152
|
+
allowedHosts: new Set((transport.allowedHosts ?? []).map((name) => hostnameOf(name))),
|
|
153
|
+
routes: resolveRoutes(path, transport.routes ?? [])
|
|
124
154
|
};
|
|
125
155
|
}
|
|
126
156
|
/** Flatten Node's header bag into lower-cased `name → value` strings. */
|
|
@@ -243,13 +273,15 @@ async function startHttpServer(transport, dispatch) {
|
|
|
243
273
|
};
|
|
244
274
|
}
|
|
245
275
|
/**
|
|
246
|
-
* Validate and answer one HTTP request:
|
|
247
|
-
* method →
|
|
276
|
+
* Validate and answer one HTTP request: Host / Origin → path → auth →
|
|
277
|
+
* method → route handler or MCP headers/body/JSON-RPC dispatch.
|
|
248
278
|
*/
|
|
249
279
|
async function handleRequest(request, response, settings, dispatch, signal, requests) {
|
|
250
280
|
const headers = flattenHeaders(request.headers);
|
|
251
281
|
const method = (request.method ?? "GET").toUpperCase();
|
|
252
|
-
const
|
|
282
|
+
const url = new URL(request.url ?? "/", "http://localhost");
|
|
283
|
+
const path = url.pathname;
|
|
284
|
+
const routes = settings.routes.get(path);
|
|
253
285
|
if (!hostAllowed(headers.host, settings)) {
|
|
254
286
|
sendRefusal(response, 403, JSON_RPC_SERVER_ERROR, "Forbidden: Host header not allowed.");
|
|
255
287
|
return;
|
|
@@ -258,7 +290,7 @@ async function handleRequest(request, response, settings, dispatch, signal, requ
|
|
|
258
290
|
sendRefusal(response, 403, JSON_RPC_SERVER_ERROR, "Forbidden: Origin not allowed.");
|
|
259
291
|
return;
|
|
260
292
|
}
|
|
261
|
-
if (path !== settings.path) {
|
|
293
|
+
if (path !== settings.path && !routes) {
|
|
262
294
|
sendRefusal(response, 404, JSON_RPC_SERVER_ERROR, "Not found.");
|
|
263
295
|
return;
|
|
264
296
|
}
|
|
@@ -274,6 +306,15 @@ async function handleRequest(request, response, settings, dispatch, signal, requ
|
|
|
274
306
|
sendRefusal(response, 401, JSON_RPC_SERVER_ERROR, "Unauthorized.", challenge);
|
|
275
307
|
return;
|
|
276
308
|
}
|
|
309
|
+
if (routes) {
|
|
310
|
+
const route = routes.get(method);
|
|
311
|
+
if (!route) {
|
|
312
|
+
sendEmpty(response, 405, { allow: [...routes.keys()].sort().join(", ") });
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
await handleRoute(request, response, route, info, url, authentication.principal, signal, settings);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
277
318
|
if (method !== "POST") {
|
|
278
319
|
sendEmpty(response, 405, { allow: "POST" });
|
|
279
320
|
return;
|
|
@@ -347,6 +388,32 @@ async function handleRequest(request, response, settings, dispatch, signal, requ
|
|
|
347
388
|
}
|
|
348
389
|
sendRefusal(response, 400, JSON_RPC_INVALID_REQUEST, "Invalid request: not a JSON-RPC message.");
|
|
349
390
|
}
|
|
391
|
+
/** Run a side route after the shared guards, tracking disconnect and shutdown. */
|
|
392
|
+
async function handleRoute(request, response, route, info, url, principal, shutdown, settings) {
|
|
393
|
+
const disconnected = new AbortController();
|
|
394
|
+
const onClose = () => disconnected.abort();
|
|
395
|
+
response.once("close", onClose);
|
|
396
|
+
try {
|
|
397
|
+
const body = await readBody(request, settings.maxBodyBytes);
|
|
398
|
+
if (body.type === "too-large") {
|
|
399
|
+
sendRefusal(response, 413, JSON_RPC_SERVER_ERROR, `Request body exceeds ${settings.maxBodyBytes} bytes.`, { connection: "close" });
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const signal = AbortSignal.any([shutdown, disconnected.signal]);
|
|
403
|
+
signal.throwIfAborted();
|
|
404
|
+
const result = await route.handler({
|
|
405
|
+
request: info,
|
|
406
|
+
url,
|
|
407
|
+
principal,
|
|
408
|
+
body: body.text,
|
|
409
|
+
signal
|
|
410
|
+
});
|
|
411
|
+
if (result.body === void 0 || info.method === "HEAD") sendEmpty(response, result.status ?? 200, result.headers);
|
|
412
|
+
else sendJson(response, result.status ?? 200, result.body, result.headers);
|
|
413
|
+
} finally {
|
|
414
|
+
response.off("close", onClose);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
350
417
|
/**
|
|
351
418
|
* Host-header check. While bound to loopback only loopback names (plus
|
|
352
419
|
* `allowedHosts`) are accepted, so a DNS-rebound name pointing at
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve-http.mjs","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/serve-http.ts"],"sourcesContent":["import { createHash, timingSafeEqual } from \"node:crypto\";\nimport {\n createServer,\n type IncomingHttpHeaders,\n type IncomingMessage,\n type Server,\n type ServerResponse,\n} from \"node:http\";\nimport { isIPv4, isIPv6 } from \"node:net\";\nimport type {\n JsonRpcRequest,\n JsonRpcResponse,\n McpHttpAuth,\n McpHttpRequestInfo,\n McpHttpServeTransport,\n McpServeCallContext,\n} from \"../contracts\";\nimport { McpServeError } from \"../errors\";\nimport { SUPPORTED_PROTOCOL_VERSIONS } from \"./protocol\";\nimport { McpServeRequests } from \"./serve-requests\";\n\n/**\n * MCP Streamable HTTP server transport for `ai.mcp.serve`.\n *\n * Implements the server side of the Streamable HTTP transport from the MCP\n * specification (https://modelcontextprotocol.io/specification/2025-11-25/basic/transports,\n * checked 2026-10-17) on `node:http`, with no SDK dependency:\n *\n * - one endpoint path; each POST carries exactly one JSON-RPC message\n * (batches were removed from the protocol in 2025-06-18 and get 400);\n * - a request is answered with `Content-Type: application/json` (the spec\n * lets a server pick JSON or SSE per request); a notification or a\n * response is acknowledged with `202 Accepted` and no body;\n * - stateless: no `MCP-Session-Id` is issued (optional in the spec), so\n * GET (server-initiated SSE stream) and DELETE (session end) answer\n * `405 Method Not Allowed`, as the spec allows;\n * - an unsupported `MCP-Protocol-Version` header gets 400;\n * - security warning items: `Origin` is validated on every request (an\n * invalid one gets 403), the default bind is `127.0.0.1`, and\n * authentication is required for any non-loopback bind.\n */\n\n/** Default bind interface — loopback only. */\nconst DEFAULT_HOST = \"127.0.0.1\";\n\n/** Default endpoint path. */\nconst DEFAULT_PATH = \"/mcp\";\n\n/** Default request body cap: 1 MiB. */\nconst DEFAULT_MAX_BODY_BYTES = 1_048_576;\n\n/** Default time `close()` waits for in-flight requests. */\nconst DEFAULT_DRAIN_TIMEOUT_MS = 5_000;\n\n/** JSON-RPC codes used for transport-level error bodies. */\nconst JSON_RPC_PARSE_ERROR = -32700;\nconst JSON_RPC_INVALID_REQUEST = -32600;\n\n/** Generic server-defined JSON-RPC code for HTTP-level refusals (auth, origin, size). */\nconst JSON_RPC_SERVER_ERROR = -32000;\n\n/** Host names that always mean \"this machine\". */\nconst LOOPBACK_NAMES = new Set([\"localhost\", \"127.0.0.1\", \"::1\", \"[::1]\"]);\n\n/** The function the transport pumps each JSON-RPC request through. */\nexport type McpHttpDispatch = (\n request: JsonRpcRequest,\n context: McpServeCallContext,\n) => Promise<JsonRpcResponse>;\n\n/** A bound HTTP endpoint. */\nexport type RunningMcpHttpServer = {\n /** Full endpoint URL. */\n url: string;\n /** Drain in-flight requests (up to the drain timeout) and free the port. Idempotent. */\n close(): Promise<void>;\n};\n\n/**\n * True when `host` names a loopback interface (`localhost`, `127.0.0.0/8`,\n * `::1`, or an IPv4-mapped `::ffff:127.x.x.x`).\n */\nexport function isLoopbackHost(host: string): boolean {\n const normalized = host.trim().toLowerCase();\n\n if (LOOPBACK_NAMES.has(normalized)) {\n return true;\n }\n\n const unbracketed = normalized.replace(/^\\[|\\]$/g, \"\");\n const mapped = unbracketed.startsWith(\"::ffff:\")\n ? unbracketed.slice(\"::ffff:\".length)\n : unbracketed;\n\n return isIPv4(mapped) && mapped.startsWith(\"127.\");\n}\n\n/** Strip the port from a `Host` header value (`[::1]:3000` → `[::1]`, `a.b:80` → `a.b`). */\nfunction hostnameOf(hostHeader: string): string {\n const value = hostHeader.trim().toLowerCase();\n\n if (value.startsWith(\"[\")) {\n const end = value.indexOf(\"]\");\n\n return end === -1 ? value : value.slice(0, end + 1);\n }\n\n const colon = value.lastIndexOf(\":\");\n\n return colon === -1 ? value : value.slice(0, colon);\n}\n\n/** SHA-256 digest — equal-length input for `timingSafeEqual`. */\nfunction digest(value: string): Buffer {\n return createHash(\"sha256\").update(value, \"utf8\").digest();\n}\n\n/**\n * Validated, defaulted transport settings plus the precomputed security\n * material (token digests, origin / host allowlists).\n */\ntype ResolvedSettings = {\n host: string;\n port: number;\n path: string;\n loopback: boolean;\n maxBodyBytes: number;\n drainTimeoutMs: number;\n tokenDigests: Buffer[];\n authorize: McpHttpAuth[\"authorize\"];\n authenticate: McpHttpAuth[\"authenticate\"];\n authRequired: boolean;\n allowedOrigins: Set<string>;\n allowedHosts: Set<string>;\n};\n\n/** Normalise a configured origin to `scheme://host[:port]`, or throw a config error. */\nfunction normalizeOrigin(origin: string): string {\n try {\n return new URL(origin).origin;\n } catch (cause) {\n throw new McpServeError(`allowedOrigins entry \"${origin}\" is not a valid origin URL.`, {\n type: \"config\",\n cause,\n });\n }\n}\n\n/**\n * Validate and default the transport options. Throws {@link McpServeError}\n * (`\"config\"`) for a non-loopback bind without auth, an empty bearer\n * token, an `auth` with neither check, a bad path / port / limit.\n */\nfunction resolveSettings(transport: McpHttpServeTransport): ResolvedSettings {\n const host = transport.host ?? DEFAULT_HOST;\n const port = transport.port ?? 0;\n const path = transport.path ?? DEFAULT_PATH;\n const loopback = isLoopbackHost(host);\n const auth = transport.auth;\n\n if (!Number.isInteger(port) || port < 0 || port > 65_535) {\n throw new McpServeError(`MCP http port must be an integer in 0–65535 (got ${port}).`, {\n type: \"config\",\n });\n }\n\n if (!path.startsWith(\"/\")) {\n throw new McpServeError(`MCP http path must start with \"/\" (got \"${path}\").`, {\n type: \"config\",\n });\n }\n\n const maxBodyBytes = transport.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;\n\n if (!Number.isInteger(maxBodyBytes) || maxBodyBytes <= 0) {\n throw new McpServeError(\"MCP http maxBodyBytes must be a positive integer.\", {\n type: \"config\",\n });\n }\n\n const drainTimeoutMs = transport.drainTimeoutMs ?? DEFAULT_DRAIN_TIMEOUT_MS;\n\n if (!Number.isFinite(drainTimeoutMs) || drainTimeoutMs < 0) {\n throw new McpServeError(\"MCP http drainTimeoutMs must be zero or a positive number.\", {\n type: \"config\",\n });\n }\n\n const tokens =\n auth?.bearer === undefined\n ? []\n : typeof auth.bearer === \"string\"\n ? [auth.bearer]\n : [...auth.bearer];\n\n if (tokens.some((token) => token.length === 0)) {\n throw new McpServeError(\"MCP http auth.bearer tokens must be non-empty strings.\", {\n type: \"config\",\n });\n }\n\n if (auth && auth.bearer !== undefined && tokens.length === 0) {\n throw new McpServeError(\"MCP http auth.bearer needs at least one token.\", { type: \"config\" });\n }\n\n if (auth && tokens.length === 0 && !auth.authorize && !auth.authenticate) {\n throw new McpServeError(\"MCP http auth needs `bearer`, `authorize` and/or `authenticate`.\", {\n type: \"config\",\n });\n }\n\n const authRequired =\n tokens.length > 0 || auth?.authorize !== undefined || auth?.authenticate !== undefined;\n\n if (!loopback && !authRequired) {\n throw new McpServeError(\n `Refusing to serve MCP over http on non-loopback host \"${host}\" without auth. ` +\n \"Set transport.auth ({ bearer }, { authorize } or { authenticate }), or bind 127.0.0.1.\",\n { type: \"config\", context: { host } },\n );\n }\n\n return {\n host,\n port,\n path,\n loopback,\n maxBodyBytes,\n drainTimeoutMs,\n tokenDigests: tokens.map(digest),\n authorize: auth?.authorize,\n authenticate: auth?.authenticate,\n authRequired,\n allowedOrigins: new Set((transport.allowedOrigins ?? []).map(normalizeOrigin)),\n allowedHosts: new Set((transport.allowedHosts ?? []).map((name) => hostnameOf(name))),\n };\n}\n\n/** Flatten Node's header bag into lower-cased `name → value` strings. */\nfunction flattenHeaders(headers: IncomingHttpHeaders): Record<string, string> {\n const flat: Record<string, string> = {};\n\n for (const [name, value] of Object.entries(headers)) {\n if (value === undefined) {\n continue;\n }\n\n flat[name.toLowerCase()] = Array.isArray(value) ? value.join(\", \") : value;\n }\n\n return flat;\n}\n\n/** Write a JSON body with a status code. */\nfunction sendJson(\n response: ServerResponse,\n status: number,\n body: unknown,\n headers: Record<string, string> = {},\n): void {\n if (response.headersSent || response.destroyed) {\n return;\n }\n\n const payload = JSON.stringify(body);\n\n response.writeHead(status, {\n ...headers,\n \"content-type\": \"application/json\",\n \"content-length\": Buffer.byteLength(payload).toString(),\n });\n response.end(payload);\n}\n\n/**\n * Write an HTTP-level refusal: the status plus a JSON-RPC error body with\n * no request id (the spec allows such a body on 4xx transport errors).\n */\nfunction sendRefusal(\n response: ServerResponse,\n status: number,\n code: number,\n message: string,\n headers: Record<string, string> = {},\n): void {\n sendJson(response, status, { jsonrpc: \"2.0\", id: null, error: { code, message } }, headers);\n}\n\n/** Write an empty-bodied status (202 / 405). */\nfunction sendEmpty(\n response: ServerResponse,\n status: number,\n headers: Record<string, string> = {},\n): void {\n if (response.headersSent || response.destroyed) {\n return;\n }\n\n response.writeHead(status, headers);\n response.end();\n}\n\n/**\n * Bind an MCP Streamable HTTP endpoint and pump each JSON-RPC request\n * through `dispatch`. Resolves once the port is bound.\n *\n * Throws {@link McpServeError}: `\"config\"` for invalid / unsafe options\n * (checked before anything binds), `\"listen\"` when binding fails.\n *\n * @param transport - The http transport options.\n * @param dispatch - Maps one JSON-RPC request (plus call context) to its response.\n */\nexport async function startHttpServer(\n transport: McpHttpServeTransport,\n dispatch: McpHttpDispatch,\n): Promise<RunningMcpHttpServer> {\n const settings = resolveSettings(transport);\n const shutdown = new AbortController();\n const requests = new McpServeRequests();\n const inFlight = new Set<Promise<void>>();\n let closing: Promise<void> | undefined;\n\n const server: Server = createServer((request, response) => {\n if (closing) {\n sendRefusal(response, 503, JSON_RPC_SERVER_ERROR, \"Server is shutting down.\", {\n connection: \"close\",\n });\n\n return;\n }\n\n const work = handleRequest(\n request,\n response,\n settings,\n dispatch,\n shutdown.signal,\n requests,\n ).catch(() => {\n sendRefusal(response, 500, JSON_RPC_SERVER_ERROR, \"Internal server error.\");\n });\n\n inFlight.add(work);\n void work.finally(() => {\n inFlight.delete(work);\n\n if (closing) {\n server.closeIdleConnections();\n }\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n const onError = (cause: Error) => {\n reject(\n new McpServeError(\n `MCP http server could not listen on ${settings.host}:${settings.port}: ${cause.message}`,\n { type: \"listen\", cause, context: { host: settings.host, port: settings.port } },\n ),\n );\n };\n\n server.once(\"error\", onError);\n server.listen(settings.port, settings.host, () => {\n server.off(\"error\", onError);\n resolve();\n });\n });\n\n const address = server.address();\n const boundPort = typeof address === \"object\" && address !== null ? address.port : settings.port;\n const urlHost = isIPv6(settings.host) ? `[${settings.host}]` : settings.host;\n const url = `http://${urlHost}:${boundPort}${settings.path}`;\n\n const close = (): Promise<void> => {\n if (closing) {\n return closing;\n }\n\n closing = (async () => {\n const closed = new Promise<void>((resolve) => {\n server.close(() => resolve());\n });\n\n server.closeIdleConnections();\n\n if (inFlight.size > 0) {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timedOut = new Promise<\"timeout\">((resolve) => {\n timer = setTimeout(() => resolve(\"timeout\"), settings.drainTimeoutMs);\n });\n\n const outcome = await Promise.race([\n Promise.allSettled([...inFlight]).then(() => \"drained\" as const),\n timedOut,\n ]);\n\n clearTimeout(timer);\n\n if (outcome === \"timeout\") {\n shutdown.abort();\n server.closeAllConnections();\n }\n }\n\n server.closeIdleConnections();\n await closed;\n })();\n\n return closing;\n };\n\n return { url, close };\n}\n\n/**\n * Validate and answer one HTTP request: path → Host / Origin → auth →\n * method → headers → body → JSON-RPC dispatch.\n */\nasync function handleRequest(\n request: IncomingMessage,\n response: ServerResponse,\n settings: ResolvedSettings,\n dispatch: McpHttpDispatch,\n signal: AbortSignal,\n requests: McpServeRequests,\n): Promise<void> {\n const headers = flattenHeaders(request.headers);\n const method = (request.method ?? \"GET\").toUpperCase();\n const path = new URL(request.url ?? \"/\", \"http://localhost\").pathname;\n\n // DNS-rebinding guards apply to every request on every path.\n if (!hostAllowed(headers.host, settings)) {\n sendRefusal(response, 403, JSON_RPC_SERVER_ERROR, \"Forbidden: Host header not allowed.\");\n\n return;\n }\n\n if (!originAllowed(headers.origin, settings)) {\n sendRefusal(response, 403, JSON_RPC_SERVER_ERROR, \"Forbidden: Origin not allowed.\");\n\n return;\n }\n\n if (path !== settings.path) {\n sendRefusal(response, 404, JSON_RPC_SERVER_ERROR, \"Not found.\");\n\n return;\n }\n\n const info: McpHttpRequestInfo = { method, path, headers };\n\n if (request.socket.remoteAddress !== undefined) {\n info.remoteAddress = request.socket.remoteAddress;\n }\n\n const authentication = await authorized(info, settings);\n\n if (!authentication.allowed) {\n const challenge: Record<string, string> =\n settings.tokenDigests.length > 0 || settings.authenticate\n ? { \"www-authenticate\": 'Bearer realm=\"mcp\"' }\n : {};\n\n sendRefusal(response, 401, JSON_RPC_SERVER_ERROR, \"Unauthorized.\", challenge);\n\n return;\n }\n\n if (method !== \"POST\") {\n // Stateless server: no server-initiated SSE stream (GET) and no\n // session to terminate (DELETE). The spec allows 405 for both.\n sendEmpty(response, 405, { allow: \"POST\" });\n\n return;\n }\n\n const protocolVersion = headers[\"mcp-protocol-version\"];\n\n if (protocolVersion !== undefined && !SUPPORTED_PROTOCOL_VERSIONS.includes(protocolVersion)) {\n sendRefusal(\n response,\n 400,\n JSON_RPC_SERVER_ERROR,\n `Unsupported MCP-Protocol-Version \"${protocolVersion}\" (supported: ${SUPPORTED_PROTOCOL_VERSIONS.join(\", \")}).`,\n );\n\n return;\n }\n\n if (!isJsonContentType(headers[\"content-type\"])) {\n sendRefusal(response, 415, JSON_RPC_SERVER_ERROR, \"Content-Type must be application/json.\");\n\n return;\n }\n\n if (!acceptsJson(headers.accept)) {\n sendRefusal(response, 406, JSON_RPC_SERVER_ERROR, \"Accept must allow application/json.\");\n\n return;\n }\n\n const body = await readBody(request, settings.maxBodyBytes);\n\n if (body.type === \"too-large\") {\n sendRefusal(\n response,\n 413,\n JSON_RPC_SERVER_ERROR,\n `Request body exceeds ${settings.maxBodyBytes} bytes.`,\n { connection: \"close\" },\n );\n\n return;\n }\n\n let message: unknown;\n\n try {\n message = JSON.parse(body.text);\n } catch {\n sendRefusal(response, 400, JSON_RPC_PARSE_ERROR, \"Parse error: body is not valid JSON.\");\n\n return;\n }\n\n if (typeof message !== \"object\" || message === null || Array.isArray(message)) {\n sendRefusal(\n response,\n 400,\n JSON_RPC_INVALID_REQUEST,\n \"Invalid request: the body must be one JSON-RPC message (batches are not supported).\",\n );\n\n return;\n }\n\n const record = message as Record<string, unknown>;\n\n if (record.jsonrpc !== \"2.0\") {\n sendRefusal(response, 400, JSON_RPC_INVALID_REQUEST, 'Invalid request: jsonrpc must be \"2.0\".');\n\n return;\n }\n\n const id = record.id;\n\n if (typeof record.method === \"string\") {\n if (typeof id !== \"string\" && typeof id !== \"number\") {\n // Only the same caller credentials can cancel an in-flight request.\n requests.cancel(record, cancellationScope(info));\n sendEmpty(response, 202);\n\n return;\n }\n\n const rpcRequest: JsonRpcRequest = { jsonrpc: \"2.0\", id, method: record.method };\n\n if (record.params !== undefined) {\n rpcRequest.params = record.params;\n }\n\n const disconnected = new AbortController();\n const onClose = () => disconnected.abort();\n response.once(\"close\", onClose);\n\n try {\n const rpcResponse = await requests.run(\n rpcRequest,\n {\n request: info,\n principal: authentication.principal,\n signal: AbortSignal.any([signal, disconnected.signal]),\n },\n (context) => dispatch(rpcRequest, context),\n cancellationScope(info),\n );\n\n if (rpcResponse === undefined) {\n // End the HTTP exchange without a JSON-RPC response for cancelled work.\n sendEmpty(response, 202);\n } else {\n sendJson(response, 200, rpcResponse);\n }\n } finally {\n response.off(\"close\", onClose);\n }\n\n return;\n }\n\n if (\n (typeof id === \"string\" || typeof id === \"number\") &&\n (\"result\" in record || \"error\" in record)\n ) {\n // A response to a server-initiated request; this server sends none, so acknowledge.\n sendEmpty(response, 202);\n\n return;\n }\n\n sendRefusal(response, 400, JSON_RPC_INVALID_REQUEST, \"Invalid request: not a JSON-RPC message.\");\n}\n\n/**\n * Host-header check. While bound to loopback only loopback names (plus\n * `allowedHosts`) are accepted, so a DNS-rebound name pointing at\n * 127.0.0.1 is refused. Off loopback the check applies only when\n * `allowedHosts` is set.\n */\nfunction hostAllowed(hostHeader: string | undefined, settings: ResolvedSettings): boolean {\n if (!settings.loopback && settings.allowedHosts.size === 0) {\n return true;\n }\n\n if (hostHeader === undefined) {\n return false;\n }\n\n const name = hostnameOf(hostHeader);\n\n if (settings.allowedHosts.has(name)) {\n return true;\n }\n\n return settings.loopback && isLoopbackHost(name);\n}\n\n/**\n * Origin check (MCP Streamable HTTP security warning). No `Origin` header —\n * a non-browser client — passes; otherwise the origin must be a loopback\n * `http(s)` origin or listed in `allowedOrigins`. `null` and unparsable\n * origins are refused.\n */\nfunction originAllowed(origin: string | undefined, settings: ResolvedSettings): boolean {\n if (origin === undefined) {\n return true;\n }\n\n let parsed: URL;\n\n try {\n parsed = new URL(origin);\n } catch {\n return false;\n }\n\n if (settings.allowedOrigins.has(parsed.origin)) {\n return true;\n }\n\n return (\n (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") && isLoopbackHost(parsed.hostname)\n );\n}\n\n/**\n * Run the configured auth. Bearer tokens are compared in constant time\n * over SHA-256 digests (equal-length buffers for `timingSafeEqual`), and\n * every configured token is compared so the match position does not leak.\n * An `authorize` hook that throws denies the request.\n */\ntype Authentication = { allowed: false } | { allowed: true; principal?: unknown };\n\nasync function authorized(\n info: McpHttpRequestInfo,\n settings: ResolvedSettings,\n): Promise<Authentication> {\n if (!settings.authRequired) {\n return { allowed: true };\n }\n\n if (settings.tokenDigests.length > 0) {\n const match = /^Bearer[ \\t]+(.+)$/i.exec(info.headers.authorization ?? \"\");\n const presented = digest(match?.[1]?.trim() ?? \"\");\n let matched = false;\n\n for (const expected of settings.tokenDigests) {\n matched = timingSafeEqual(presented, expected) || matched;\n }\n\n if (!match || !matched) {\n return { allowed: false };\n }\n }\n\n try {\n if (settings.authorize && (await settings.authorize(info)) !== true) {\n return { allowed: false };\n }\n\n if (settings.authenticate) {\n const principal = await settings.authenticate(info);\n\n return principal === undefined ? { allowed: false } : { allowed: true, principal };\n }\n\n return { allowed: true };\n } catch {\n return { allowed: false };\n }\n}\n\n/**\n * Stateless HTTP has no connection/session id. Repeat the caller's credential\n * and application headers on notifications; per-message transport headers do\n * not identify a caller. Hash the material rather than retaining secrets as keys.\n */\nfunction cancellationScope(info: McpHttpRequestInfo): string {\n const transportHeaders = new Set([\n \"host\",\n \"origin\",\n \"accept\",\n \"accept-encoding\",\n \"accept-language\",\n \"connection\",\n \"content-type\",\n \"content-length\",\n \"transfer-encoding\",\n \"mcp-protocol-version\",\n \"user-agent\",\n ]);\n const credentials = Object.entries(info.headers)\n .filter(([name]) => !transportHeaders.has(name))\n .sort(([left], [right]) => left.localeCompare(right));\n\n return digest(JSON.stringify([info.remoteAddress, credentials])).toString(\"hex\");\n}\n\n/** True for `application/json` (any parameters, e.g. `; charset=utf-8`). */\nfunction isJsonContentType(contentType: string | undefined): boolean {\n return contentType?.split(\";\")[0]?.trim().toLowerCase() === \"application/json\";\n}\n\n/**\n * True when the `Accept` header allows a JSON reply. Clients are required\n * to list both `application/json` and `text/event-stream`; this server\n * always answers JSON, so it only insists on that half. A missing header\n * is accepted.\n */\nfunction acceptsJson(accept: string | undefined): boolean {\n if (accept === undefined || accept.trim() === \"\") {\n return true;\n }\n\n return accept\n .split(\",\")\n .map((part) => part.split(\";\")[0]?.trim().toLowerCase())\n .some((type) => type === \"application/json\" || type === \"application/*\" || type === \"*/*\");\n}\n\n/** The outcome of reading a request body. */\ntype BodyResult = { type: \"ok\"; text: string } | { type: \"too-large\" };\n\n/**\n * Read the request body as UTF-8, refusing anything over `limit` bytes —\n * up front from `Content-Length`, and while streaming for chunked bodies.\n * A refused body is left unread; the 413 response closes the connection.\n */\nfunction readBody(request: IncomingMessage, limit: number): Promise<BodyResult> {\n const declared = Number(request.headers[\"content-length\"]);\n\n if (Number.isFinite(declared) && declared > limit) {\n return Promise.resolve({ type: \"too-large\" });\n }\n\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let size = 0;\n let done = false;\n\n const finish = (result: BodyResult) => {\n if (!done) {\n done = true;\n resolve(result);\n }\n };\n\n request.on(\"data\", (chunk: Buffer) => {\n if (done) {\n return;\n }\n\n size += chunk.length;\n\n if (size > limit) {\n request.pause();\n finish({ type: \"too-large\" });\n\n return;\n }\n\n chunks.push(chunk);\n });\n request.on(\"end\", () => finish({ type: \"ok\", text: Buffer.concat(chunks).toString(\"utf8\") }));\n request.on(\"error\", (error) => {\n if (!done) {\n done = true;\n reject(error);\n }\n });\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"serve-http.mjs","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/serve-http.ts"],"sourcesContent":["import { createHash, timingSafeEqual } from \"node:crypto\";\nimport {\n createServer,\n type IncomingHttpHeaders,\n type IncomingMessage,\n type Server,\n type ServerResponse,\n} from \"node:http\";\nimport { isIPv4, isIPv6 } from \"node:net\";\nimport type {\n JsonRpcRequest,\n JsonRpcResponse,\n McpHttpAuth,\n McpHttpRequestInfo,\n McpHttpRoute,\n McpHttpServeTransport,\n McpServeCallContext,\n} from \"../contracts\";\nimport { McpServeError } from \"../errors\";\nimport { SUPPORTED_PROTOCOL_VERSIONS } from \"./protocol\";\nimport { McpServeRequests } from \"./serve-requests\";\n\n/**\n * MCP Streamable HTTP server transport for `ai.mcp.serve`.\n *\n * Implements the server side of the Streamable HTTP transport from the MCP\n * specification (https://modelcontextprotocol.io/specification/2025-11-25/basic/transports,\n * checked 2026-10-17) on `node:http`, with no SDK dependency:\n *\n * - one MCP endpoint path (plus optional authenticated app routes); each MCP POST carries exactly one JSON-RPC message\n * (batches were removed from the protocol in 2025-06-18 and get 400);\n * - a request is answered with `Content-Type: application/json` (the spec\n * lets a server pick JSON or SSE per request); a notification or a\n * response is acknowledged with `202 Accepted` and no body;\n * - stateless: no `MCP-Session-Id` is issued (optional in the spec), so\n * GET (server-initiated SSE stream) and DELETE (session end) answer\n * `405 Method Not Allowed`, as the spec allows;\n * - an unsupported `MCP-Protocol-Version` header gets 400;\n * - security warning items: `Origin` is validated on every request (an\n * invalid one gets 403), the default bind is `127.0.0.1`, and\n * authentication is required for any non-loopback bind.\n */\n\n/** Default bind interface — loopback only. */\nconst DEFAULT_HOST = \"127.0.0.1\";\n\n/** Default endpoint path. */\nconst DEFAULT_PATH = \"/mcp\";\n\n/** Default request body cap: 1 MiB. */\nconst DEFAULT_MAX_BODY_BYTES = 1_048_576;\n\n/** Default time `close()` waits for in-flight requests. */\nconst DEFAULT_DRAIN_TIMEOUT_MS = 5_000;\n\n/** JSON-RPC codes used for transport-level error bodies. */\nconst JSON_RPC_PARSE_ERROR = -32700;\nconst JSON_RPC_INVALID_REQUEST = -32600;\n\n/** Generic server-defined JSON-RPC code for HTTP-level refusals (auth, origin, size). */\nconst JSON_RPC_SERVER_ERROR = -32000;\n\n/** Host names that always mean \"this machine\". */\nconst LOOPBACK_NAMES = new Set([\"localhost\", \"127.0.0.1\", \"::1\", \"[::1]\"]);\n\n/** The function the transport pumps each JSON-RPC request through. */\nexport type McpHttpDispatch = (\n request: JsonRpcRequest,\n context: McpServeCallContext,\n) => Promise<JsonRpcResponse>;\n\n/** A bound HTTP endpoint. */\nexport type RunningMcpHttpServer = {\n /** Full endpoint URL. */\n url: string;\n /** Drain in-flight requests (up to the drain timeout) and free the port. Idempotent. */\n close(): Promise<void>;\n};\n\n/**\n * True when `host` names a loopback interface (`localhost`, `127.0.0.0/8`,\n * `::1`, or an IPv4-mapped `::ffff:127.x.x.x`).\n */\nexport function isLoopbackHost(host: string): boolean {\n const normalized = host.trim().toLowerCase();\n\n if (LOOPBACK_NAMES.has(normalized)) {\n return true;\n }\n\n const unbracketed = normalized.replace(/^\\[|\\]$/g, \"\");\n const mapped = unbracketed.startsWith(\"::ffff:\")\n ? unbracketed.slice(\"::ffff:\".length)\n : unbracketed;\n\n return isIPv4(mapped) && mapped.startsWith(\"127.\");\n}\n\n/** Strip the port from a `Host` header value (`[::1]:3000` → `[::1]`, `a.b:80` → `a.b`). */\nfunction hostnameOf(hostHeader: string): string {\n const value = hostHeader.trim().toLowerCase();\n\n if (value.startsWith(\"[\")) {\n const end = value.indexOf(\"]\");\n\n return end === -1 ? value : value.slice(0, end + 1);\n }\n\n const colon = value.lastIndexOf(\":\");\n\n return colon === -1 ? value : value.slice(0, colon);\n}\n\n/** SHA-256 digest — equal-length input for `timingSafeEqual`. */\nfunction digest(value: string): Buffer {\n return createHash(\"sha256\").update(value, \"utf8\").digest();\n}\n\n/**\n * Validated, defaulted transport settings plus the precomputed security\n * material (token digests, origin / host allowlists).\n */\ntype ResolvedSettings = {\n host: string;\n port: number;\n path: string;\n loopback: boolean;\n maxBodyBytes: number;\n drainTimeoutMs: number;\n tokenDigests: Buffer[];\n authorize: McpHttpAuth[\"authorize\"];\n authenticate: McpHttpAuth[\"authenticate\"];\n authRequired: boolean;\n allowedOrigins: Set<string>;\n allowedHosts: Set<string>;\n routes: Map<string, Map<string, McpHttpRoute>>;\n};\n\n/** Resolve exact descendant routes; no route may replace the MCP endpoint. */\nfunction resolveRoutes(\n path: string,\n routes: readonly McpHttpRoute[],\n): Map<string, Map<string, McpHttpRoute>> {\n const resolved = new Map<string, Map<string, McpHttpRoute>>();\n const methods = new Set([\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"HEAD\", \"OPTIONS\"]);\n\n if (routes.length > 0) {\n let canonical = false;\n\n try {\n canonical = !path.startsWith(\"//\") && new URL(path, \"http://localhost\").pathname === path;\n } catch {\n // Invalid URLs are configuration errors too, before the server binds.\n }\n\n if (!canonical) {\n throw new McpServeError(\"MCP http routes require a canonical endpoint pathname.\", {\n type: \"config\",\n });\n }\n }\n\n for (const route of routes) {\n if (\n !methods.has(route.method) ||\n !/^\\/[A-Za-z0-9._~-]+(?:\\/[A-Za-z0-9._~-]+)*$/.test(route.path) ||\n route.path.split(\"/\").some((segment) => segment === \".\" || segment === \"..\") ||\n typeof route.handler !== \"function\"\n ) {\n throw new McpServeError(\"Invalid MCP http route method, path or handler.\", {\n type: \"config\",\n });\n }\n\n const fullPath = `${path.replace(/\\/+$/, \"\")}${route.path}`;\n const byMethod = resolved.get(fullPath) ?? new Map<string, McpHttpRoute>();\n\n if (fullPath === path || byMethod.has(route.method)) {\n throw new McpServeError(`MCP http route \"${route.method} ${fullPath}\" conflicts.`, {\n type: \"collision\",\n });\n }\n\n byMethod.set(route.method, { ...route });\n resolved.set(fullPath, byMethod);\n }\n\n return resolved;\n}\n\n/** Normalise a configured origin to `scheme://host[:port]`, or throw a config error. */\nfunction normalizeOrigin(origin: string): string {\n try {\n return new URL(origin).origin;\n } catch (cause) {\n throw new McpServeError(`allowedOrigins entry \"${origin}\" is not a valid origin URL.`, {\n type: \"config\",\n cause,\n });\n }\n}\n\n/**\n * Validate and default the transport options. Throws {@link McpServeError}\n * (`\"config\"`) for a non-loopback bind without auth, an empty bearer\n * token, an `auth` with neither check, a bad path / port / limit.\n */\nfunction resolveSettings(transport: McpHttpServeTransport): ResolvedSettings {\n const host = transport.host ?? DEFAULT_HOST;\n const port = transport.port ?? 0;\n const path = transport.path ?? DEFAULT_PATH;\n const loopback = isLoopbackHost(host);\n const auth = transport.auth;\n\n if (!Number.isInteger(port) || port < 0 || port > 65_535) {\n throw new McpServeError(`MCP http port must be an integer in 0–65535 (got ${port}).`, {\n type: \"config\",\n });\n }\n\n if (!path.startsWith(\"/\")) {\n throw new McpServeError(`MCP http path must start with \"/\" (got \"${path}\").`, {\n type: \"config\",\n });\n }\n\n const maxBodyBytes = transport.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;\n\n if (!Number.isInteger(maxBodyBytes) || maxBodyBytes <= 0) {\n throw new McpServeError(\"MCP http maxBodyBytes must be a positive integer.\", {\n type: \"config\",\n });\n }\n\n const drainTimeoutMs = transport.drainTimeoutMs ?? DEFAULT_DRAIN_TIMEOUT_MS;\n\n if (!Number.isFinite(drainTimeoutMs) || drainTimeoutMs < 0) {\n throw new McpServeError(\"MCP http drainTimeoutMs must be zero or a positive number.\", {\n type: \"config\",\n });\n }\n\n const tokens =\n auth?.bearer === undefined\n ? []\n : typeof auth.bearer === \"string\"\n ? [auth.bearer]\n : [...auth.bearer];\n\n if (tokens.some((token) => token.length === 0)) {\n throw new McpServeError(\"MCP http auth.bearer tokens must be non-empty strings.\", {\n type: \"config\",\n });\n }\n\n if (auth && auth.bearer !== undefined && tokens.length === 0) {\n throw new McpServeError(\"MCP http auth.bearer needs at least one token.\", { type: \"config\" });\n }\n\n if (auth && tokens.length === 0 && !auth.authorize && !auth.authenticate) {\n throw new McpServeError(\"MCP http auth needs `bearer`, `authorize` and/or `authenticate`.\", {\n type: \"config\",\n });\n }\n\n const authRequired =\n tokens.length > 0 || auth?.authorize !== undefined || auth?.authenticate !== undefined;\n\n if (!loopback && !authRequired) {\n throw new McpServeError(\n `Refusing to serve MCP over http on non-loopback host \"${host}\" without auth. ` +\n \"Set transport.auth ({ bearer }, { authorize } or { authenticate }), or bind 127.0.0.1.\",\n { type: \"config\", context: { host } },\n );\n }\n\n return {\n host,\n port,\n path,\n loopback,\n maxBodyBytes,\n drainTimeoutMs,\n tokenDigests: tokens.map(digest),\n authorize: auth?.authorize,\n authenticate: auth?.authenticate,\n authRequired,\n allowedOrigins: new Set((transport.allowedOrigins ?? []).map(normalizeOrigin)),\n allowedHosts: new Set((transport.allowedHosts ?? []).map((name) => hostnameOf(name))),\n routes: resolveRoutes(path, transport.routes ?? []),\n };\n}\n\n/** Flatten Node's header bag into lower-cased `name → value` strings. */\nfunction flattenHeaders(headers: IncomingHttpHeaders): Record<string, string> {\n const flat: Record<string, string> = {};\n\n for (const [name, value] of Object.entries(headers)) {\n if (value === undefined) {\n continue;\n }\n\n flat[name.toLowerCase()] = Array.isArray(value) ? value.join(\", \") : value;\n }\n\n return flat;\n}\n\n/** Write a JSON body with a status code. */\nfunction sendJson(\n response: ServerResponse,\n status: number,\n body: unknown,\n headers: Record<string, string> = {},\n): void {\n if (response.headersSent || response.destroyed) {\n return;\n }\n\n const payload = JSON.stringify(body);\n\n response.writeHead(status, {\n ...headers,\n \"content-type\": \"application/json\",\n \"content-length\": Buffer.byteLength(payload).toString(),\n });\n response.end(payload);\n}\n\n/**\n * Write an HTTP-level refusal: the status plus a JSON-RPC error body with\n * no request id (the spec allows such a body on 4xx transport errors).\n */\nfunction sendRefusal(\n response: ServerResponse,\n status: number,\n code: number,\n message: string,\n headers: Record<string, string> = {},\n): void {\n sendJson(response, status, { jsonrpc: \"2.0\", id: null, error: { code, message } }, headers);\n}\n\n/** Write an empty-bodied status (202 / 405). */\nfunction sendEmpty(\n response: ServerResponse,\n status: number,\n headers: Record<string, string> = {},\n): void {\n if (response.headersSent || response.destroyed) {\n return;\n }\n\n response.writeHead(status, headers);\n response.end();\n}\n\n/**\n * Bind an MCP Streamable HTTP endpoint and pump each JSON-RPC request\n * through `dispatch`. Resolves once the port is bound.\n *\n * Throws {@link McpServeError}: `\"config\"` for invalid / unsafe options\n * (checked before anything binds), `\"listen\"` when binding fails.\n *\n * @param transport - The http transport options.\n * @param dispatch - Maps one JSON-RPC request (plus call context) to its response.\n */\nexport async function startHttpServer(\n transport: McpHttpServeTransport,\n dispatch: McpHttpDispatch,\n): Promise<RunningMcpHttpServer> {\n const settings = resolveSettings(transport);\n const shutdown = new AbortController();\n const requests = new McpServeRequests();\n const inFlight = new Set<Promise<void>>();\n let closing: Promise<void> | undefined;\n\n const server: Server = createServer((request, response) => {\n if (closing) {\n sendRefusal(response, 503, JSON_RPC_SERVER_ERROR, \"Server is shutting down.\", {\n connection: \"close\",\n });\n\n return;\n }\n\n const work = handleRequest(\n request,\n response,\n settings,\n dispatch,\n shutdown.signal,\n requests,\n ).catch(() => {\n sendRefusal(response, 500, JSON_RPC_SERVER_ERROR, \"Internal server error.\");\n });\n\n inFlight.add(work);\n void work.finally(() => {\n inFlight.delete(work);\n\n if (closing) {\n server.closeIdleConnections();\n }\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n const onError = (cause: Error) => {\n reject(\n new McpServeError(\n `MCP http server could not listen on ${settings.host}:${settings.port}: ${cause.message}`,\n { type: \"listen\", cause, context: { host: settings.host, port: settings.port } },\n ),\n );\n };\n\n server.once(\"error\", onError);\n server.listen(settings.port, settings.host, () => {\n server.off(\"error\", onError);\n resolve();\n });\n });\n\n const address = server.address();\n const boundPort = typeof address === \"object\" && address !== null ? address.port : settings.port;\n const urlHost = isIPv6(settings.host) ? `[${settings.host}]` : settings.host;\n const url = `http://${urlHost}:${boundPort}${settings.path}`;\n\n const close = (): Promise<void> => {\n if (closing) {\n return closing;\n }\n\n closing = (async () => {\n const closed = new Promise<void>((resolve) => {\n server.close(() => resolve());\n });\n\n server.closeIdleConnections();\n\n if (inFlight.size > 0) {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timedOut = new Promise<\"timeout\">((resolve) => {\n timer = setTimeout(() => resolve(\"timeout\"), settings.drainTimeoutMs);\n });\n\n const outcome = await Promise.race([\n Promise.allSettled([...inFlight]).then(() => \"drained\" as const),\n timedOut,\n ]);\n\n clearTimeout(timer);\n\n if (outcome === \"timeout\") {\n shutdown.abort();\n server.closeAllConnections();\n }\n }\n\n server.closeIdleConnections();\n await closed;\n })();\n\n return closing;\n };\n\n return { url, close };\n}\n\n/**\n * Validate and answer one HTTP request: Host / Origin → path → auth →\n * method → route handler or MCP headers/body/JSON-RPC dispatch.\n */\nasync function handleRequest(\n request: IncomingMessage,\n response: ServerResponse,\n settings: ResolvedSettings,\n dispatch: McpHttpDispatch,\n signal: AbortSignal,\n requests: McpServeRequests,\n): Promise<void> {\n const headers = flattenHeaders(request.headers);\n const method = (request.method ?? \"GET\").toUpperCase();\n const url = new URL(request.url ?? \"/\", \"http://localhost\");\n const path = url.pathname;\n const routes = settings.routes.get(path);\n\n // DNS-rebinding guards apply to every request on every path.\n if (!hostAllowed(headers.host, settings)) {\n sendRefusal(response, 403, JSON_RPC_SERVER_ERROR, \"Forbidden: Host header not allowed.\");\n\n return;\n }\n\n if (!originAllowed(headers.origin, settings)) {\n sendRefusal(response, 403, JSON_RPC_SERVER_ERROR, \"Forbidden: Origin not allowed.\");\n\n return;\n }\n\n if (path !== settings.path && !routes) {\n sendRefusal(response, 404, JSON_RPC_SERVER_ERROR, \"Not found.\");\n\n return;\n }\n\n const info: McpHttpRequestInfo = { method, path, headers };\n\n if (request.socket.remoteAddress !== undefined) {\n info.remoteAddress = request.socket.remoteAddress;\n }\n\n const authentication = await authorized(info, settings);\n\n if (!authentication.allowed) {\n const challenge: Record<string, string> =\n settings.tokenDigests.length > 0 || settings.authenticate\n ? { \"www-authenticate\": 'Bearer realm=\"mcp\"' }\n : {};\n\n sendRefusal(response, 401, JSON_RPC_SERVER_ERROR, \"Unauthorized.\", challenge);\n\n return;\n }\n\n if (routes) {\n const route = routes.get(method);\n\n if (!route) {\n sendEmpty(response, 405, { allow: [...routes.keys()].sort().join(\", \") });\n\n return;\n }\n\n await handleRoute(\n request,\n response,\n route,\n info,\n url,\n authentication.principal,\n signal,\n settings,\n );\n\n return;\n }\n\n if (method !== \"POST\") {\n // Stateless server: no server-initiated SSE stream (GET) and no\n // session to terminate (DELETE). The spec allows 405 for both.\n sendEmpty(response, 405, { allow: \"POST\" });\n\n return;\n }\n\n const protocolVersion = headers[\"mcp-protocol-version\"];\n\n if (protocolVersion !== undefined && !SUPPORTED_PROTOCOL_VERSIONS.includes(protocolVersion)) {\n sendRefusal(\n response,\n 400,\n JSON_RPC_SERVER_ERROR,\n `Unsupported MCP-Protocol-Version \"${protocolVersion}\" (supported: ${SUPPORTED_PROTOCOL_VERSIONS.join(\", \")}).`,\n );\n\n return;\n }\n\n if (!isJsonContentType(headers[\"content-type\"])) {\n sendRefusal(response, 415, JSON_RPC_SERVER_ERROR, \"Content-Type must be application/json.\");\n\n return;\n }\n\n if (!acceptsJson(headers.accept)) {\n sendRefusal(response, 406, JSON_RPC_SERVER_ERROR, \"Accept must allow application/json.\");\n\n return;\n }\n\n const body = await readBody(request, settings.maxBodyBytes);\n\n if (body.type === \"too-large\") {\n sendRefusal(\n response,\n 413,\n JSON_RPC_SERVER_ERROR,\n `Request body exceeds ${settings.maxBodyBytes} bytes.`,\n { connection: \"close\" },\n );\n\n return;\n }\n\n let message: unknown;\n\n try {\n message = JSON.parse(body.text);\n } catch {\n sendRefusal(response, 400, JSON_RPC_PARSE_ERROR, \"Parse error: body is not valid JSON.\");\n\n return;\n }\n\n if (typeof message !== \"object\" || message === null || Array.isArray(message)) {\n sendRefusal(\n response,\n 400,\n JSON_RPC_INVALID_REQUEST,\n \"Invalid request: the body must be one JSON-RPC message (batches are not supported).\",\n );\n\n return;\n }\n\n const record = message as Record<string, unknown>;\n\n if (record.jsonrpc !== \"2.0\") {\n sendRefusal(response, 400, JSON_RPC_INVALID_REQUEST, 'Invalid request: jsonrpc must be \"2.0\".');\n\n return;\n }\n\n const id = record.id;\n\n if (typeof record.method === \"string\") {\n if (typeof id !== \"string\" && typeof id !== \"number\") {\n // Only the same caller credentials can cancel an in-flight request.\n requests.cancel(record, cancellationScope(info));\n sendEmpty(response, 202);\n\n return;\n }\n\n const rpcRequest: JsonRpcRequest = { jsonrpc: \"2.0\", id, method: record.method };\n\n if (record.params !== undefined) {\n rpcRequest.params = record.params;\n }\n\n const disconnected = new AbortController();\n const onClose = () => disconnected.abort();\n response.once(\"close\", onClose);\n\n try {\n const rpcResponse = await requests.run(\n rpcRequest,\n {\n request: info,\n principal: authentication.principal,\n signal: AbortSignal.any([signal, disconnected.signal]),\n },\n (context) => dispatch(rpcRequest, context),\n cancellationScope(info),\n );\n\n if (rpcResponse === undefined) {\n // End the HTTP exchange without a JSON-RPC response for cancelled work.\n sendEmpty(response, 202);\n } else {\n sendJson(response, 200, rpcResponse);\n }\n } finally {\n response.off(\"close\", onClose);\n }\n\n return;\n }\n\n if (\n (typeof id === \"string\" || typeof id === \"number\") &&\n (\"result\" in record || \"error\" in record)\n ) {\n // A response to a server-initiated request; this server sends none, so acknowledge.\n sendEmpty(response, 202);\n\n return;\n }\n\n sendRefusal(response, 400, JSON_RPC_INVALID_REQUEST, \"Invalid request: not a JSON-RPC message.\");\n}\n\n/** Run a side route after the shared guards, tracking disconnect and shutdown. */\nasync function handleRoute(\n request: IncomingMessage,\n response: ServerResponse,\n route: McpHttpRoute,\n info: McpHttpRequestInfo,\n url: URL,\n principal: unknown,\n shutdown: AbortSignal,\n settings: ResolvedSettings,\n): Promise<void> {\n const disconnected = new AbortController();\n const onClose = () => disconnected.abort();\n response.once(\"close\", onClose);\n\n try {\n const body = await readBody(request, settings.maxBodyBytes);\n\n if (body.type === \"too-large\") {\n sendRefusal(\n response,\n 413,\n JSON_RPC_SERVER_ERROR,\n `Request body exceeds ${settings.maxBodyBytes} bytes.`,\n {\n connection: \"close\",\n },\n );\n\n return;\n }\n\n const signal = AbortSignal.any([shutdown, disconnected.signal]);\n signal.throwIfAborted();\n const result = await route.handler({ request: info, url, principal, body: body.text, signal });\n\n if (result.body === undefined || info.method === \"HEAD\") {\n sendEmpty(response, result.status ?? 200, result.headers);\n } else {\n sendJson(response, result.status ?? 200, result.body, result.headers);\n }\n } finally {\n response.off(\"close\", onClose);\n }\n}\n\n/**\n * Host-header check. While bound to loopback only loopback names (plus\n * `allowedHosts`) are accepted, so a DNS-rebound name pointing at\n * 127.0.0.1 is refused. Off loopback the check applies only when\n * `allowedHosts` is set.\n */\nfunction hostAllowed(hostHeader: string | undefined, settings: ResolvedSettings): boolean {\n if (!settings.loopback && settings.allowedHosts.size === 0) {\n return true;\n }\n\n if (hostHeader === undefined) {\n return false;\n }\n\n const name = hostnameOf(hostHeader);\n\n if (settings.allowedHosts.has(name)) {\n return true;\n }\n\n return settings.loopback && isLoopbackHost(name);\n}\n\n/**\n * Origin check (MCP Streamable HTTP security warning). No `Origin` header —\n * a non-browser client — passes; otherwise the origin must be a loopback\n * `http(s)` origin or listed in `allowedOrigins`. `null` and unparsable\n * origins are refused.\n */\nfunction originAllowed(origin: string | undefined, settings: ResolvedSettings): boolean {\n if (origin === undefined) {\n return true;\n }\n\n let parsed: URL;\n\n try {\n parsed = new URL(origin);\n } catch {\n return false;\n }\n\n if (settings.allowedOrigins.has(parsed.origin)) {\n return true;\n }\n\n return (\n (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") && isLoopbackHost(parsed.hostname)\n );\n}\n\n/**\n * Run the configured auth. Bearer tokens are compared in constant time\n * over SHA-256 digests (equal-length buffers for `timingSafeEqual`), and\n * every configured token is compared so the match position does not leak.\n * An `authorize` hook that throws denies the request.\n */\ntype Authentication = { allowed: false } | { allowed: true; principal?: unknown };\n\nasync function authorized(\n info: McpHttpRequestInfo,\n settings: ResolvedSettings,\n): Promise<Authentication> {\n if (!settings.authRequired) {\n return { allowed: true };\n }\n\n if (settings.tokenDigests.length > 0) {\n const match = /^Bearer[ \\t]+(.+)$/i.exec(info.headers.authorization ?? \"\");\n const presented = digest(match?.[1]?.trim() ?? \"\");\n let matched = false;\n\n for (const expected of settings.tokenDigests) {\n matched = timingSafeEqual(presented, expected) || matched;\n }\n\n if (!match || !matched) {\n return { allowed: false };\n }\n }\n\n try {\n if (settings.authorize && (await settings.authorize(info)) !== true) {\n return { allowed: false };\n }\n\n if (settings.authenticate) {\n const principal = await settings.authenticate(info);\n\n return principal === undefined ? { allowed: false } : { allowed: true, principal };\n }\n\n return { allowed: true };\n } catch {\n return { allowed: false };\n }\n}\n\n/**\n * Stateless HTTP has no connection/session id. Repeat the caller's credential\n * and application headers on notifications; per-message transport headers do\n * not identify a caller. Hash the material rather than retaining secrets as keys.\n */\nfunction cancellationScope(info: McpHttpRequestInfo): string {\n const transportHeaders = new Set([\n \"host\",\n \"origin\",\n \"accept\",\n \"accept-encoding\",\n \"accept-language\",\n \"connection\",\n \"content-type\",\n \"content-length\",\n \"transfer-encoding\",\n \"mcp-protocol-version\",\n \"user-agent\",\n ]);\n const credentials = Object.entries(info.headers)\n .filter(([name]) => !transportHeaders.has(name))\n .sort(([left], [right]) => left.localeCompare(right));\n\n return digest(JSON.stringify([info.remoteAddress, credentials])).toString(\"hex\");\n}\n\n/** True for `application/json` (any parameters, e.g. `; charset=utf-8`). */\nfunction isJsonContentType(contentType: string | undefined): boolean {\n return contentType?.split(\";\")[0]?.trim().toLowerCase() === \"application/json\";\n}\n\n/**\n * True when the `Accept` header allows a JSON reply. Clients are required\n * to list both `application/json` and `text/event-stream`; this server\n * always answers JSON, so it only insists on that half. A missing header\n * is accepted.\n */\nfunction acceptsJson(accept: string | undefined): boolean {\n if (accept === undefined || accept.trim() === \"\") {\n return true;\n }\n\n return accept\n .split(\",\")\n .map((part) => part.split(\";\")[0]?.trim().toLowerCase())\n .some((type) => type === \"application/json\" || type === \"application/*\" || type === \"*/*\");\n}\n\n/** The outcome of reading a request body. */\ntype BodyResult = { type: \"ok\"; text: string } | { type: \"too-large\" };\n\n/**\n * Read the request body as UTF-8, refusing anything over `limit` bytes —\n * up front from `Content-Length`, and while streaming for chunked bodies.\n * A refused body is left unread; the 413 response closes the connection.\n */\nfunction readBody(request: IncomingMessage, limit: number): Promise<BodyResult> {\n const declared = Number(request.headers[\"content-length\"]);\n\n if (Number.isFinite(declared) && declared > limit) {\n return Promise.resolve({ type: \"too-large\" });\n }\n\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let size = 0;\n let done = false;\n\n const finish = (result: BodyResult) => {\n if (!done) {\n done = true;\n resolve(result);\n }\n };\n\n request.on(\"data\", (chunk: Buffer) => {\n if (done) {\n return;\n }\n\n size += chunk.length;\n\n if (size > limit) {\n request.pause();\n finish({ type: \"too-large\" });\n\n return;\n }\n\n chunks.push(chunk);\n });\n request.on(\"end\", () => finish({ type: \"ok\", text: Buffer.concat(chunks).toString(\"utf8\") }));\n request.on(\"error\", (error) => {\n if (!done) {\n done = true;\n reject(error);\n }\n });\n });\n}\n"],"mappings":"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"}
|
package/esm/mcp/serve.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/serve.ts"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"serve.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/serve.ts"],"mappings":";;;;;;;iBA2vBiB;;;;;;;EAOf,OAAO,SAAS,gBAAgB,UAAU,sBAAsB,QAAQ;;;;;;;;;;;;;;wBAe1D,mBACd,QAAQ,gBACR,SAAS,kBACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA6Ja,MACd,QAAQ,gBACR,SAAS;EAAoB,WAAW;IACvC,UAAU;wBACG,MAAM,QAAQ,gBAAgB,SAAS,kBAAkB"}
|