@silkweave/mcp 2.6.1 → 3.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/README.md +4 -4
- package/build/cliProxy.d.mts.map +1 -1
- package/build/cliProxy.mjs +5 -6
- package/build/cliProxy.mjs.map +1 -1
- package/build/index.d.mts +12 -25
- package/build/index.d.mts.map +1 -1
- package/build/index.mjs +25 -236
- package/build/index.mjs.map +1 -1
- package/build/registerTools-Z6JnoM60.mjs +144 -0
- package/build/registerTools-Z6JnoM60.mjs.map +1 -0
- package/build/{result-BiFuFt5B.mjs → result-B_9Eo1ey.mjs} +1 -1
- package/build/{result-BiFuFt5B.mjs.map → result-B_9Eo1ey.mjs.map} +1 -1
- package/build/result-_Bg2cDAG.d.mts +63 -0
- package/build/result-_Bg2cDAG.d.mts.map +1 -0
- package/build/tools.d.mts +2 -0
- package/build/tools.mjs +3 -0
- package/package.json +23 -14
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ Configure in Claude Desktop or Claude Code:
|
|
|
39
39
|
|
|
40
40
|
### http
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
**Stateless** MCP transport over HTTP with per-call SSE streaming (no sessions, so any request can hit any instance behind a plain load balancer).
|
|
43
43
|
|
|
44
44
|
```typescript
|
|
45
45
|
import { silkweave } from '@silkweave/core'
|
|
@@ -51,7 +51,7 @@ await silkweave({ name: 'my-tools', description: 'My Tools', version: '1.0.0' })
|
|
|
51
51
|
.start()
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
Exposes `POST /mcp` (
|
|
54
|
+
Exposes a single stateless `POST /mcp` (each call mints a fresh transport with `sessionIdGenerator: undefined`; progress streams over SSE per call). No `Mcp-Session-Id`, no `GET`/`DELETE` reconnect. Built on Express with `StreamableHTTPServerTransport` from the MCP SDK.
|
|
55
55
|
|
|
56
56
|
| Option | Type | Description |
|
|
57
57
|
|--------|------|-------------|
|
|
@@ -62,7 +62,7 @@ Exposes `POST /mcp` (invoke tools), `GET /mcp` (SSE stream), and `DELETE /mcp` (
|
|
|
62
62
|
|
|
63
63
|
### cliProxy
|
|
64
64
|
|
|
65
|
-
MCP CLI proxy client - connects to a running HTTP MCP server and invokes tools from the command line. Imported from the dedicated `@silkweave/mcp/cli-proxy` subpath (kept out of the package root so importing the `stdio`/`http` servers does not pull the CLI client's `commander`
|
|
65
|
+
MCP CLI proxy client - connects to a running HTTP MCP server and invokes tools from the command line. Imported from the dedicated `@silkweave/mcp/cli-proxy` subpath (kept out of the package root so importing the `stdio`/`http` servers does not pull the CLI client's `commander` into the server path). `commander` is an **optional peer dependency** - install it alongside `@silkweave/mcp` when you use the CLI proxy.
|
|
66
66
|
|
|
67
67
|
```typescript
|
|
68
68
|
import { silkweave } from '@silkweave/core'
|
|
@@ -194,4 +194,4 @@ All result utilities are exported from `@silkweave/mcp`:
|
|
|
194
194
|
|
|
195
195
|
- [Silkweave README](https://github.com/silkweave/silkweave) - Full documentation
|
|
196
196
|
- [`@silkweave/core`](https://www.npmjs.com/package/@silkweave/core) - Core library
|
|
197
|
-
- [`@silkweave/
|
|
197
|
+
- [`@silkweave/edge`](https://www.npmjs.com/package/@silkweave/edge) - Stateless MCP on Web-Standard edge/serverless runtimes (Cloudflare Workers, Vercel, Bun, Deno)
|
package/build/cliProxy.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cliProxy.d.mts","names":[],"sources":["../src/adapter/cliProxy.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"cliProxy.d.mts","names":[],"sources":["../src/adapter/cliProxy.ts"],"mappings":";;;;KASY,cAAA,IAAkB,OAAA,EAAS,YAAA,EAAc,KAAA,UAAe,QAAA,EAAU,YAAA;AAAA,UAE7D,eAAA;EACf,GAAA,EAAK,GAAA;EACL,SAAA,GAAY,cAAA;AAAA;AAAA,cA4DD,QAAA,EAAU,cAAA,CAAe,eAAA"}
|
package/build/cliProxy.mjs
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { i as parseResourceMessage } from "./result-
|
|
2
|
-
import {
|
|
3
|
-
import { createCLILogger } from "@silkweave/logger";
|
|
1
|
+
import { i as parseResourceMessage } from "./result-B_9Eo1ey.mjs";
|
|
2
|
+
import { createConsoleLogger } from "@silkweave/core";
|
|
4
3
|
import { kebabCase } from "change-case";
|
|
5
4
|
import { randomUUID } from "crypto";
|
|
6
|
-
import { intro } from "@clack/prompts";
|
|
7
5
|
import { Client } from "@modelcontextprotocol/sdk/client";
|
|
8
6
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
7
|
+
import { LoggingMessageNotificationSchema, ProgressNotificationSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
9
8
|
import { Command } from "commander";
|
|
10
9
|
//#region src/adapter/cliProxy.ts
|
|
11
10
|
function coerce(value, type) {
|
|
@@ -68,9 +67,9 @@ const cliProxy = ({ url, formatter = defaultFormatter }) => {
|
|
|
68
67
|
for (const key of Object.keys(properties)) addCliOption(command, key, properties[key]);
|
|
69
68
|
command.action(async (args) => {
|
|
70
69
|
const { silent } = program.opts();
|
|
71
|
-
const logger =
|
|
70
|
+
const logger = createConsoleLogger();
|
|
72
71
|
if (!silent) {
|
|
73
|
-
|
|
72
|
+
console.info(`${options.name} - ${tool.name}`);
|
|
74
73
|
client.setNotificationHandler(LoggingMessageNotificationSchema, ({ params: { level, data } }) => {
|
|
75
74
|
logger[level](data);
|
|
76
75
|
});
|
package/build/cliProxy.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cliProxy.mjs","names":[],"sources":["../src/adapter/cliProxy.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"cliProxy.mjs","names":[],"sources":["../src/adapter/cliProxy.ts"],"sourcesContent":["import { Client } from '@modelcontextprotocol/sdk/client'\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'\nimport { ContentBlock, LoggingMessageNotificationSchema, ProgressNotificationSchema, ToolResultContent } from '@modelcontextprotocol/sdk/types.js'\nimport { AdapterFactory, createConsoleLogger } from '@silkweave/core'\nimport { kebabCase } from 'change-case'\nimport { Command } from 'commander'\nimport { randomUUID } from 'crypto'\nimport { parseResourceMessage } from '../util/result.js'\n\nexport type CLIFormatterFn = (message: ContentBlock, index: number, messages: ContentBlock[]) => string | undefined\n\nexport interface CliProxyOptions {\n url: URL\n formatter?: CLIFormatterFn\n}\n\ninterface JsonSchemaProperty {\n type?: 'string' | 'number' | 'integer' | 'boolean' | 'object' | 'array' | string\n description?: string\n default?: unknown\n enum?: unknown[]\n}\n\ninterface JsonSchemaObject {\n type?: 'object'\n properties?: Record<string, JsonSchemaProperty>\n required?: string[]\n}\n\nfunction coerce(value: unknown, type: JsonSchemaProperty['type']): unknown {\n if (value === undefined) { return undefined }\n if (type === 'number' || type === 'integer') {\n const n = Number(value)\n return Number.isNaN(n) ? value : n\n }\n return value\n}\n\nfunction addCliOption(command: Command, key: string, prop: JsonSchemaProperty) {\n const flag = kebabCase(key)\n const description = prop.description\n const defaultValue = prop.default\n const type = prop.type\n if (type === 'boolean') {\n command.option(`--${flag}`, description, defaultValue as boolean | undefined)\n command.option(`--no-${flag}`)\n return\n }\n if (type === 'number' || type === 'integer') {\n command.option(`--${flag} <number>`, description, defaultValue as string | undefined)\n return\n }\n if (type === 'string' || prop.enum) {\n command.option(`--${flag} <string>`, description, defaultValue as string | undefined)\n return\n }\n if (type === 'object' || type === 'array') {\n command.option(`--${flag} <json>`, description ?? '', JSON.parse, defaultValue as never)\n return\n }\n throw new Error(`Unsupported JSON Schema type for CLI option \"${key}\": ${type ?? 'undefined'}`)\n}\n\nconst defaultFormatter: CLIFormatterFn = (message) => {\n if (message.type === 'text' && !message.text.includes('mcp://toolResult/')) {\n return `${message.text}`\n } else if (message.type === 'resource') {\n return parseResourceMessage(message)\n } else {\n return JSON.stringify(message)\n }\n}\n\nexport const cliProxy: AdapterFactory<CliProxyOptions> = ({ url, formatter = defaultFormatter }) => {\n return (options, baseContext) => {\n const context = baseContext.fork({ adapter: 'cliProxy' })\n const program = new Command()\n .name(options.name)\n .description(options.description)\n .version(options.version)\n .option('-s, --silent', 'Silent mode, prevent log messages', false)\n\n return {\n context,\n start: async () => {\n const client = new Client({\n name: options.name,\n description: options.description,\n version: options.version\n })\n const transport = new StreamableHTTPClientTransport(url)\n await client.connect(transport)\n const { tools } = await client.listTools()\n for (const tool of tools) {\n const name = kebabCase(tool.name)\n const command = program.command(name)\n if (tool.description) { command.description(tool.description) }\n const schema = tool.inputSchema as JsonSchemaObject\n const properties = schema.properties ?? {}\n for (const key of Object.keys(properties)) {\n addCliOption(command, key, properties[key])\n }\n command.action(async (args: Record<string, unknown>) => {\n const { silent } = program.opts<{ silent: boolean }>()\n const logger = createConsoleLogger()\n if (!silent) {\n console.info(`${options.name} - ${tool.name}`)\n client.setNotificationHandler(LoggingMessageNotificationSchema, ({ params: { level, data } }) => {\n logger[level](data)\n })\n client.setNotificationHandler(ProgressNotificationSchema, ({ params: { progress, total, message } }) => {\n logger.info({ progress, total, message })\n })\n }\n const input: Record<string, unknown> = {}\n for (const key of Object.keys(properties)) {\n const value = args[key]\n if (value !== undefined) { input[key] = coerce(value, properties[key]?.type) }\n }\n const response = await client.callTool({\n name: tool.name,\n arguments: input,\n _meta: { progressToken: randomUUID(), disposition: 'json' }\n }) as ToolResultContent\n response.content.forEach((message, index, messages) => {\n const text = formatter(message, index, messages)\n process.stdout.write(`${text}\\n`)\n })\n })\n }\n await program.parseAsync()\n await transport.close()\n },\n stop: async () => { /* noop */ }\n }\n }\n}\n"],"mappings":"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"}
|
package/build/index.d.mts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { a as smartToolResult, c as requestFromExtra, i as parseResourceMessage, n as handleToolError, o as RegisterToolsOptions, r as jsonToolResult, s as registerTools, t as errorToolResult } from "./result-_Bg2cDAG.mjs";
|
|
1
2
|
import { CreateMcpExpressAppOptions } from "@modelcontextprotocol/sdk/server/express.js";
|
|
2
|
-
import { Action, AdapterFactory, SilkweaveContext,
|
|
3
|
+
import { Action, AdapterFactory, SilkweaveContext, SilkweaveOptions } from "@silkweave/core";
|
|
3
4
|
import { Express, RequestHandler } from "express";
|
|
4
5
|
import { AuthConfig, AuthInfo } from "@silkweave/auth";
|
|
5
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
7
|
import { CorsOptions, CorsOptions as CorsOptions$1 } from "cors";
|
|
7
|
-
import { CallToolResult, EmbeddedResource } from "@modelcontextprotocol/sdk/types.js";
|
|
8
8
|
import { Server } from "http";
|
|
9
9
|
|
|
10
10
|
//#region src/adapter/http.d.ts
|
|
@@ -66,8 +66,8 @@ declare function authMiddleware(auth: AuthConfig, context: SilkweaveContext): Re
|
|
|
66
66
|
declare const MCP_REQUIRED_HEADERS: string[];
|
|
67
67
|
/**
|
|
68
68
|
* CORS middleware preconfigured to expose the headers MCP clients need
|
|
69
|
-
* (`
|
|
70
|
-
*
|
|
69
|
+
* (`Last-Event-Id`, `Mcp-Protocol-Version`, `WWW-Authenticate`) on top of any
|
|
70
|
+
* user-supplied options. (Stateless transport - no `Mcp-Session-Id`.)
|
|
71
71
|
*
|
|
72
72
|
* Pass `false` to disable, omit / pass `true` for permissive defaults, or pass
|
|
73
73
|
* a `CorsOptions` object to override.
|
|
@@ -124,33 +124,20 @@ declare function sideloadResource(options?: SideloadResourceOptions): RequestHan
|
|
|
124
124
|
//#endregion
|
|
125
125
|
//#region src/handlers/transport.d.ts
|
|
126
126
|
interface McpTransportHandlers {
|
|
127
|
-
/** `POST /mcp` -
|
|
127
|
+
/** `POST /mcp` - handle a single stateless MCP request/response (SSE per call). */
|
|
128
128
|
post: RequestHandler;
|
|
129
|
-
/** `GET /mcp` and `DELETE /mcp` - long-poll session stream / session termination. Also covers `GET /mcp/resource/:id`. */
|
|
130
|
-
stream: RequestHandler;
|
|
131
129
|
}
|
|
132
130
|
/**
|
|
133
|
-
* Build the MCP Streamable HTTP transport route
|
|
131
|
+
* Build the MCP Streamable HTTP transport route handler.
|
|
134
132
|
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
133
|
+
* Stateless (per the 2026 spec direction): each `POST /mcp` mints a fresh
|
|
134
|
+
* transport + server with `sessionIdGenerator: undefined`, handles exactly that
|
|
135
|
+
* request (streaming progress over SSE when the call carries a `progressToken`),
|
|
136
|
+
* and tears down on response close. No `Mcp-Session-Id`, no session map, no
|
|
137
|
+
* `GET`/`DELETE` reconnect - any request can hit any instance.
|
|
138
138
|
*/
|
|
139
139
|
declare function mcpTransport(silkweaveOptions: SilkweaveOptions, context: SilkweaveContext, actions: Action[]): McpTransportHandlers;
|
|
140
140
|
//#endregion
|
|
141
|
-
//#region src/util/result.d.ts
|
|
142
|
-
declare function smartToolResult(data: string | object | object[]): CallToolResult;
|
|
143
|
-
declare function jsonToolResult(data: object, isError?: boolean): CallToolResult;
|
|
144
|
-
declare function errorToolResult({
|
|
145
|
-
code,
|
|
146
|
-
name,
|
|
147
|
-
message
|
|
148
|
-
}: SilkweaveError): CallToolResult;
|
|
149
|
-
declare function handleToolError(error: unknown): CallToolResult;
|
|
150
|
-
declare function parseResourceMessage({
|
|
151
|
-
resource
|
|
152
|
-
}: EmbeddedResource): string;
|
|
153
|
-
//#endregion
|
|
154
141
|
//#region src/util/sideload.d.ts
|
|
155
142
|
interface SideloadResource {
|
|
156
143
|
id: string;
|
|
@@ -163,5 +150,5 @@ declare function createSideloadResource(buffer: Buffer, {
|
|
|
163
150
|
contentType
|
|
164
151
|
}: Pick<SideloadResource, 'name' | 'contentType'>): Promise<SideloadResource>;
|
|
165
152
|
//#endregion
|
|
166
|
-
export { type CorsOptions, MCP_REQUIRED_HEADERS, McpTransportHandlers, OAuthRouteHandlers, SideloadResource, SideloadResourceOptions, StartMcpHttpOptions, authMiddleware, authStorage, buildMcpExpressApp, createSideloadResource, errorToolResult, handleToolError, http, jsonToolResult, mcpCors, mcpTransport, oauthRoutes, parseResourceMessage, protectedResourceMetadata, sideloadResource, smartToolResult, startMcpServer, stdio };
|
|
153
|
+
export { type CorsOptions, MCP_REQUIRED_HEADERS, McpTransportHandlers, OAuthRouteHandlers, RegisterToolsOptions, SideloadResource, SideloadResourceOptions, StartMcpHttpOptions, authMiddleware, authStorage, buildMcpExpressApp, createSideloadResource, errorToolResult, handleToolError, http, jsonToolResult, mcpCors, mcpTransport, oauthRoutes, parseResourceMessage, protectedResourceMetadata, registerTools, requestFromExtra, sideloadResource, smartToolResult, startMcpServer, stdio };
|
|
167
154
|
//# sourceMappingURL=index.d.mts.map
|
package/build/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapter/http.ts","../src/adapter/stdio.ts","../src/handlers/auth.ts","../src/handlers/cors.ts","../src/handlers/metadata.ts","../src/handlers/oauth.ts","../src/handlers/sideload.ts","../src/handlers/transport.ts","../src/util/
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapter/http.ts","../src/adapter/stdio.ts","../src/handlers/auth.ts","../src/handlers/cors.ts","../src/handlers/metadata.ts","../src/handlers/oauth.ts","../src/handlers/sideload.ts","../src/handlers/transport.ts","../src/util/sideload.ts"],"mappings":";;;;;;;;;;UAaiB,mBAAA,SAA4B,0BAAA;EAC3C,IAAA;EACA,IAAA;EACA,IAAA,GAAO,UAAA;EAHQ;EAKf,IAAA,GAAO,aAAA;;EAEP,iBAAA;EAFO;EAIP,WAAA;AAAA;;;;;;;;iBAUc,kBAAA,CACd,gBAAA,EAAkB,gBAAA,EAClB,OAAA,EAAS,gBAAA,EACT,OAAA,EAAS,MAAA,IACT,OAAA,EAAS,mBAAA,GACR,OAAA;;;;;AALH;;iBAmDsB,cAAA,CACpB,gBAAA,EAAkB,gBAAA,EAClB,OAAA,EAAS,MAAA,IACT,OAAA,EAAS,mBAAA,EACT,OAAA,GAAU,gBAAA,GACT,OAAA,CAAQ,MAAA;;;;;cAgBE,IAAA,EAAM,cAAA,CAAe,mBAAA;;;cCnGrB,KAAA,EAAO,cAAA;;;;;;;cCIP,WAAA,EAAW,iBAAA,CAAA,QAAA;;AFIxB;;;;;;;;;iBEQgB,cAAA,CAAe,IAAA,EAAM,UAAA,EAAY,OAAA,EAAS,gBAAA,GAAmB,cAAA;;;;cCjBhE,oBAAA;;;;;;;AHSb;;iBGCgB,OAAA,CAAQ,UAAA,GAAY,aAAA,aAA+B,cAAA;;;;;;;;iBCNnD,yBAAA,CAA0B,IAAA,EAAM,UAAA,GAAa,cAAA;;;UCa5C,kBAAA;;EAEf,mBAAA,EAAqB,cAAA;;EAErB,SAAA,EAAW,cAAA;;EAEX,QAAA,EAAU,cAAA;;EAEV,YAAA;ELhBmC;EKkBnC,KAAA,EAAO,cAAA;ELfA;EKiBP,QAAA,EAAU,cAAA;AAAA;;;;;;;;;;;iBAaI,WAAA,CAAY,IAAA,EAAM,UAAA,GAAa,kBAAA;;;UC1C9B,uBAAA;;EAEf,WAAA;AAAA;;;;;;ANOF;iBMEgB,gBAAA,CAAiB,OAAA,GAAS,uBAAA,GAA+B,cAAA;;;UCGxD,oBAAA;;EAEf,IAAA,EAAM,cAAA;AAAA;;;;;APPR;;;;;iBOmBgB,YAAA,CACd,gBAAA,EAAkB,gBAAA,EAClB,OAAA,EAAS,gBAAA,EACT,OAAA,EAAS,MAAA,KACR,oBAAA;;;UCjCc,gBAAA;EACf,EAAA;EACA,IAAA;EACA,WAAA;EACA,IAAA;AAAA;AAAA,iBAGoB,sBAAA,CAAuB,MAAA,EAAQ,MAAA;EAAU,IAAA;EAAM;AAAA,GAAe,IAAA,CAAK,gBAAA,4BAAyC,OAAA,CAAA,gBAAA"}
|
package/build/index.mjs
CHANGED
|
@@ -1,62 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as authStorage, n as requestFromExtra, r as authMiddleware, t as registerTools } from "./registerTools-Z6JnoM60.mjs";
|
|
2
|
+
import { a as smartToolResult, i as parseResourceMessage, n as handleToolError, r as jsonToolResult, t as errorToolResult } from "./result-B_9Eo1ey.mjs";
|
|
2
3
|
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
|
3
|
-
import { createContext
|
|
4
|
+
import { createContext } from "@silkweave/core";
|
|
4
5
|
import express from "express";
|
|
5
|
-
import { generateProtectedResourceMetadata
|
|
6
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
|
+
import { generateProtectedResourceMetadata } from "@silkweave/auth";
|
|
7
7
|
import cors from "cors";
|
|
8
8
|
import { readFile, writeFile } from "fs/promises";
|
|
9
|
-
import { InMemoryEventStore } from "@modelcontextprotocol/sdk/examples/shared/inMemoryEventStore.js";
|
|
10
9
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11
10
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
12
|
-
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
13
|
-
import { createLogger } from "@silkweave/logger";
|
|
14
|
-
import { capitalCase, pascalCase } from "change-case";
|
|
15
|
-
import { randomUUID } from "crypto";
|
|
16
11
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Per-request bearer-token storage used by tool handlers to read the resolved
|
|
20
|
-
* `AuthInfo` for the currently-handled MCP call.
|
|
21
|
-
*/
|
|
22
|
-
const authStorage = new AsyncLocalStorage();
|
|
23
|
-
/**
|
|
24
|
-
* Express middleware that validates the `Authorization: Bearer …` header via
|
|
25
|
-
* the supplied `AuthConfig`. On success the resolved `AuthInfo` is placed in
|
|
26
|
-
* `authStorage` for the duration of the downstream handler - `mcpTransport`'s
|
|
27
|
-
* tool callbacks pick it up to populate the silkweave context's `auth` key.
|
|
28
|
-
*
|
|
29
|
-
* The middleware should NOT be applied to OAuth-discovery / token routes -
|
|
30
|
-
* compose it only on the routes that require an authenticated caller (the MCP
|
|
31
|
-
* transport itself, sideload, etc.).
|
|
32
|
-
*/
|
|
33
|
-
function authMiddleware(auth, context) {
|
|
34
|
-
return async (req, res, next) => {
|
|
35
|
-
const result = await validateToken(req.headers.authorization, auth, context.fork({ request: req }));
|
|
36
|
-
if (result.error) {
|
|
37
|
-
for (const [key, value] of Object.entries(result.error.headers)) res.header(key, value);
|
|
38
|
-
res.status(result.error.statusCode).json(result.error.body);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
if (result.auth) authStorage.run(result.auth, () => {
|
|
42
|
-
next();
|
|
43
|
-
});
|
|
44
|
-
else next();
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
//#endregion
|
|
12
|
+
import { randomUUID } from "crypto";
|
|
48
13
|
//#region src/handlers/cors.ts
|
|
49
14
|
/** Headers required by the MCP protocol that must always be exposed when CORS is in use. */
|
|
50
15
|
const MCP_REQUIRED_HEADERS = [
|
|
51
16
|
"WWW-Authenticate",
|
|
52
|
-
"Mcp-Session-Id",
|
|
53
17
|
"Last-Event-Id",
|
|
54
18
|
"Mcp-Protocol-Version"
|
|
55
19
|
];
|
|
56
20
|
/**
|
|
57
21
|
* CORS middleware preconfigured to expose the headers MCP clients need
|
|
58
|
-
* (`
|
|
59
|
-
*
|
|
22
|
+
* (`Last-Event-Id`, `Mcp-Protocol-Version`, `WWW-Authenticate`) on top of any
|
|
23
|
+
* user-supplied options. (Stateless transport - no `Mcp-Session-Id`.)
|
|
60
24
|
*
|
|
61
25
|
* Pass `false` to disable, omit / pass `true` for permissive defaults, or pass
|
|
62
26
|
* a `CorsOptions` object to override.
|
|
@@ -81,7 +45,7 @@ function mcpCors(corsConfig = true) {
|
|
|
81
45
|
*/
|
|
82
46
|
function protectedResourceMetadata(auth) {
|
|
83
47
|
if (!auth.resourceUrl || !auth.authorizationServers?.length) throw new Error("@silkweave/mcp protectedResourceMetadata(): auth.resourceUrl and auth.authorizationServers are required");
|
|
84
|
-
const metadata = generateProtectedResourceMetadata(auth.resourceUrl, auth.authorizationServers);
|
|
48
|
+
const metadata = generateProtectedResourceMetadata(auth.resourceUrl, auth.authorizationServers, auth.requiredScopes);
|
|
85
49
|
return (_req, res) => {
|
|
86
50
|
res.json(metadata);
|
|
87
51
|
};
|
|
@@ -155,85 +119,6 @@ function sideloadResource(options = {}) {
|
|
|
155
119
|
}
|
|
156
120
|
//#endregion
|
|
157
121
|
//#region src/handlers/transport.ts
|
|
158
|
-
/**
|
|
159
|
-
* Build the generic `request` context value (the same key REST/tRPC populate)
|
|
160
|
-
* from the MCP SDK's `extra.requestInfo`, so transport-agnostic consumers - e.g.
|
|
161
|
-
* `@silkweave/nestjs` `@UseGuards` guards reading
|
|
162
|
-
* `switchToHttp().getRequest().headers` - work over MCP. There is no path
|
|
163
|
-
* `params`/`query` on an MCP call, so those are empty stand-ins for graceful
|
|
164
|
-
* degradation. Returns `undefined` when no HTTP request info is available.
|
|
165
|
-
*/
|
|
166
|
-
function requestFromExtra(requestInfo) {
|
|
167
|
-
if (!requestInfo) return;
|
|
168
|
-
return {
|
|
169
|
-
headers: requestInfo.headers ?? {},
|
|
170
|
-
url: requestInfo.url?.toString(),
|
|
171
|
-
params: {},
|
|
172
|
-
query: {}
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
function registerTools$1(server, actions, context) {
|
|
176
|
-
for (const action of actions) server.registerTool(pascalCase(action.name), {
|
|
177
|
-
title: capitalCase(action.name),
|
|
178
|
-
description: action.description,
|
|
179
|
-
inputSchema: action.input
|
|
180
|
-
}, async (input, extra) => {
|
|
181
|
-
const logger = createLogger({
|
|
182
|
-
stream: process.stderr,
|
|
183
|
-
onLog: (level, data) => {
|
|
184
|
-
extra.sendNotification({
|
|
185
|
-
method: "notifications/message",
|
|
186
|
-
params: {
|
|
187
|
-
level,
|
|
188
|
-
data
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
},
|
|
192
|
-
onProgress: ({ progress, total, message }) => {
|
|
193
|
-
if (!extra._meta?.progressToken) return;
|
|
194
|
-
extra.sendNotification({
|
|
195
|
-
method: "notifications/progress",
|
|
196
|
-
params: {
|
|
197
|
-
progress,
|
|
198
|
-
total,
|
|
199
|
-
message,
|
|
200
|
-
progressToken: extra._meta.progressToken
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
const currentAuth = authStorage.getStore();
|
|
206
|
-
const actionContext = context.fork({
|
|
207
|
-
logger,
|
|
208
|
-
extra,
|
|
209
|
-
request: requestFromExtra(extra.requestInfo),
|
|
210
|
-
...currentAuth ? { auth: currentAuth } : {}
|
|
211
|
-
});
|
|
212
|
-
const disposition = extra._meta?.disposition ?? action.disposition;
|
|
213
|
-
const progressToken = extra._meta?.progressToken;
|
|
214
|
-
try {
|
|
215
|
-
let result;
|
|
216
|
-
if (isStreamingAction(action)) result = await runStreamingAction(action, input, actionContext, progressToken ? async (chunk, index) => {
|
|
217
|
-
await extra.sendNotification({
|
|
218
|
-
method: "notifications/progress",
|
|
219
|
-
params: {
|
|
220
|
-
progressToken,
|
|
221
|
-
progress: index + 1,
|
|
222
|
-
message: JSON.stringify(chunk)
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
} : void 0);
|
|
226
|
-
else result = await action.run(input, actionContext);
|
|
227
|
-
if (action.toolResult) {
|
|
228
|
-
const response = action.toolResult(result, actionContext);
|
|
229
|
-
if (response) return response;
|
|
230
|
-
}
|
|
231
|
-
return disposition === "json" ? jsonToolResult(result) : smartToolResult(result);
|
|
232
|
-
} catch (error) {
|
|
233
|
-
return handleToolError(error);
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
122
|
function createMcpServer(options, actions, context) {
|
|
238
123
|
const server = new McpServer({
|
|
239
124
|
name: options.name,
|
|
@@ -243,56 +128,28 @@ function createMcpServer(options, actions, context) {
|
|
|
243
128
|
tools: {},
|
|
244
129
|
logging: {}
|
|
245
130
|
} });
|
|
246
|
-
registerTools
|
|
131
|
+
registerTools(server, actions, context);
|
|
247
132
|
return server;
|
|
248
133
|
}
|
|
249
|
-
function createSessionTransport(transports) {
|
|
250
|
-
const transport = new StreamableHTTPServerTransport({
|
|
251
|
-
sessionIdGenerator: () => randomUUID(),
|
|
252
|
-
enableJsonResponse: false,
|
|
253
|
-
eventStore: new InMemoryEventStore(),
|
|
254
|
-
onsessioninitialized: (sId) => {
|
|
255
|
-
transports[sId] = transport;
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
transport.onerror = (error) => {
|
|
259
|
-
console.error(error);
|
|
260
|
-
};
|
|
261
|
-
transport.onclose = () => {
|
|
262
|
-
const sid = transport.sessionId;
|
|
263
|
-
if (sid && transports[sid]) delete transports[sid];
|
|
264
|
-
};
|
|
265
|
-
return transport;
|
|
266
|
-
}
|
|
267
134
|
/**
|
|
268
|
-
* Build the MCP Streamable HTTP transport route
|
|
135
|
+
* Build the MCP Streamable HTTP transport route handler.
|
|
269
136
|
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
137
|
+
* Stateless (per the 2026 spec direction): each `POST /mcp` mints a fresh
|
|
138
|
+
* transport + server with `sessionIdGenerator: undefined`, handles exactly that
|
|
139
|
+
* request (streaming progress over SSE when the call carries a `progressToken`),
|
|
140
|
+
* and tears down on response close. No `Mcp-Session-Id`, no session map, no
|
|
141
|
+
* `GET`/`DELETE` reconnect - any request can hit any instance.
|
|
273
142
|
*/
|
|
274
143
|
function mcpTransport(silkweaveOptions, context, actions) {
|
|
275
|
-
const transports = {};
|
|
276
144
|
const post = async (req, res) => {
|
|
277
|
-
const sessionId = req.headers["mcp-session-id"];
|
|
278
145
|
try {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
error: {
|
|
287
|
-
code: -32e3,
|
|
288
|
-
message: "Session not found"
|
|
289
|
-
},
|
|
290
|
-
id: null
|
|
291
|
-
});
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
const transport = createSessionTransport(transports);
|
|
295
|
-
await createMcpServer(silkweaveOptions, actions, context).connect(transport);
|
|
146
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
147
|
+
const server = createMcpServer(silkweaveOptions, actions, context);
|
|
148
|
+
res.on("close", () => {
|
|
149
|
+
transport.close();
|
|
150
|
+
server.close();
|
|
151
|
+
});
|
|
152
|
+
await server.connect(transport);
|
|
296
153
|
await transport.handleRequest(req, res, req.body);
|
|
297
154
|
} catch (error) {
|
|
298
155
|
console.error("Error handling MCP request:", error);
|
|
@@ -306,18 +163,7 @@ function mcpTransport(silkweaveOptions, context, actions) {
|
|
|
306
163
|
});
|
|
307
164
|
}
|
|
308
165
|
};
|
|
309
|
-
|
|
310
|
-
const sessionId = req.headers["mcp-session-id"];
|
|
311
|
-
if (!sessionId || !transports[sessionId]) {
|
|
312
|
-
res.status(400).send("Invalid or missing session ID");
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
await transports[sessionId].handleRequest(req, res);
|
|
316
|
-
};
|
|
317
|
-
return {
|
|
318
|
-
post,
|
|
319
|
-
stream
|
|
320
|
-
};
|
|
166
|
+
return { post };
|
|
321
167
|
}
|
|
322
168
|
//#endregion
|
|
323
169
|
//#region src/adapter/http.ts
|
|
@@ -363,9 +209,6 @@ function buildMcpExpressApp(silkweaveOptions, context, actions, options) {
|
|
|
363
209
|
if (sideloadResources) app.get("/resource/:id", sideloadResource({ resourceDir }));
|
|
364
210
|
const transport = mcpTransport(silkweaveOptions, context, actions);
|
|
365
211
|
app.post("/mcp", express.json(), transport.post);
|
|
366
|
-
app.get("/mcp", transport.stream);
|
|
367
|
-
app.delete("/mcp", transport.stream);
|
|
368
|
-
app.get("/mcp/resource/:id", transport.stream);
|
|
369
212
|
return app;
|
|
370
213
|
}
|
|
371
214
|
/**
|
|
@@ -422,60 +265,6 @@ const http = (options) => {
|
|
|
422
265
|
};
|
|
423
266
|
//#endregion
|
|
424
267
|
//#region src/adapter/stdio.ts
|
|
425
|
-
function registerTools(server, actions, context) {
|
|
426
|
-
for (const action of actions) server.registerTool(pascalCase(action.name), {
|
|
427
|
-
title: capitalCase(action.name),
|
|
428
|
-
description: action.description,
|
|
429
|
-
inputSchema: action.input
|
|
430
|
-
}, async (input, extra) => {
|
|
431
|
-
const logger = createLogger({
|
|
432
|
-
stream: false,
|
|
433
|
-
onLog: (level, data) => {
|
|
434
|
-
extra.sendNotification({
|
|
435
|
-
method: "notifications/message",
|
|
436
|
-
params: {
|
|
437
|
-
level,
|
|
438
|
-
data
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
},
|
|
442
|
-
onProgress: ({ progress, total, message }) => {
|
|
443
|
-
if (!extra._meta?.progressToken) return;
|
|
444
|
-
extra.sendNotification({
|
|
445
|
-
method: "notifications/progress",
|
|
446
|
-
params: {
|
|
447
|
-
progress,
|
|
448
|
-
total,
|
|
449
|
-
message,
|
|
450
|
-
progressToken: extra._meta.progressToken
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
const actionContext = context.fork({
|
|
456
|
-
logger,
|
|
457
|
-
extra
|
|
458
|
-
});
|
|
459
|
-
const progressToken = extra._meta?.progressToken;
|
|
460
|
-
try {
|
|
461
|
-
let result;
|
|
462
|
-
if (isStreamingAction(action)) result = await runStreamingAction(action, input, actionContext, progressToken ? async (chunk, index) => {
|
|
463
|
-
await extra.sendNotification({
|
|
464
|
-
method: "notifications/progress",
|
|
465
|
-
params: {
|
|
466
|
-
progressToken,
|
|
467
|
-
progress: index + 1,
|
|
468
|
-
message: JSON.stringify(chunk)
|
|
469
|
-
}
|
|
470
|
-
});
|
|
471
|
-
} : void 0);
|
|
472
|
-
else result = await action.run(input, actionContext);
|
|
473
|
-
return action.toolResult?.(result, actionContext) ?? smartToolResult(result);
|
|
474
|
-
} catch (error) {
|
|
475
|
-
return handleToolError(error);
|
|
476
|
-
}
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
268
|
const stdio = () => {
|
|
480
269
|
return (options, baseContext) => {
|
|
481
270
|
const context = baseContext.fork({ adapter: "stdio" });
|
|
@@ -490,7 +279,7 @@ const stdio = () => {
|
|
|
490
279
|
return {
|
|
491
280
|
context,
|
|
492
281
|
start: async (actions) => {
|
|
493
|
-
registerTools(server, actions, context);
|
|
282
|
+
registerTools(server, actions, context, { logStream: false });
|
|
494
283
|
const transport = new StdioServerTransport();
|
|
495
284
|
await server.connect(transport);
|
|
496
285
|
},
|
|
@@ -514,6 +303,6 @@ async function createSideloadResource(buffer, { name, contentType }) {
|
|
|
514
303
|
return resource;
|
|
515
304
|
}
|
|
516
305
|
//#endregion
|
|
517
|
-
export { MCP_REQUIRED_HEADERS, authMiddleware, authStorage, buildMcpExpressApp, createSideloadResource, errorToolResult, handleToolError, http, jsonToolResult, mcpCors, mcpTransport, oauthRoutes, parseResourceMessage, protectedResourceMetadata, sideloadResource, smartToolResult, startMcpServer, stdio };
|
|
306
|
+
export { MCP_REQUIRED_HEADERS, authMiddleware, authStorage, buildMcpExpressApp, createSideloadResource, errorToolResult, handleToolError, http, jsonToolResult, mcpCors, mcpTransport, oauthRoutes, parseResourceMessage, protectedResourceMetadata, registerTools, requestFromExtra, sideloadResource, smartToolResult, startMcpServer, stdio };
|
|
518
307
|
|
|
519
308
|
//# sourceMappingURL=index.mjs.map
|
package/build/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["registerTools"],"sources":["../src/handlers/auth.ts","../src/handlers/cors.ts","../src/handlers/metadata.ts","../src/handlers/oauth.ts","../src/handlers/sideload.ts","../src/handlers/transport.ts","../src/adapter/http.ts","../src/adapter/stdio.ts","../src/util/sideload.ts"],"sourcesContent":["import { AuthConfig, AuthInfo, validateToken } from '@silkweave/auth'\nimport { SilkweaveContext } from '@silkweave/core'\nimport { type RequestHandler } from 'express'\nimport { AsyncLocalStorage } from 'node:async_hooks'\n\n/**\n * Per-request bearer-token storage used by tool handlers to read the resolved\n * `AuthInfo` for the currently-handled MCP call.\n */\nexport const authStorage = new AsyncLocalStorage<AuthInfo>()\n\n/**\n * Express middleware that validates the `Authorization: Bearer …` header via\n * the supplied `AuthConfig`. On success the resolved `AuthInfo` is placed in\n * `authStorage` for the duration of the downstream handler - `mcpTransport`'s\n * tool callbacks pick it up to populate the silkweave context's `auth` key.\n *\n * The middleware should NOT be applied to OAuth-discovery / token routes -\n * compose it only on the routes that require an authenticated caller (the MCP\n * transport itself, sideload, etc.).\n */\nexport function authMiddleware(auth: AuthConfig, context: SilkweaveContext): RequestHandler {\n return async (req, res, next) => {\n const result = await validateToken(req.headers.authorization, auth, context.fork({ request: req }))\n if (result.error) {\n for (const [key, value] of Object.entries(result.error.headers)) { res.header(key, value) }\n res.status(result.error.statusCode).json(result.error.body)\n return\n }\n if (result.auth) {\n authStorage.run(result.auth, () => { next() })\n } else {\n next()\n }\n }\n}\n","import cors, { CorsOptions } from 'cors'\nimport { type RequestHandler } from 'express'\n\n/** Headers required by the MCP protocol that must always be exposed when CORS is in use. */\nexport const MCP_REQUIRED_HEADERS = ['WWW-Authenticate', 'Mcp-Session-Id', 'Last-Event-Id', 'Mcp-Protocol-Version']\n\n/**\n * CORS middleware preconfigured to expose the headers MCP clients need\n * (`Mcp-Session-Id`, `Last-Event-Id`, `Mcp-Protocol-Version`, `WWW-Authenticate`)\n * on top of any user-supplied options.\n *\n * Pass `false` to disable, omit / pass `true` for permissive defaults, or pass\n * a `CorsOptions` object to override.\n */\nexport function mcpCors(corsConfig: CorsOptions | boolean = true): RequestHandler | null {\n if (corsConfig === false) { return null }\n const userConfig = corsConfig === true ? {} : corsConfig\n const userExposed = userConfig.exposedHeaders\n const exposedHeaders = [\n ...MCP_REQUIRED_HEADERS,\n ...(Array.isArray(userExposed) ? userExposed : userExposed ? [userExposed] : [])\n ]\n return cors({ origin: '*', ...userConfig, exposedHeaders })\n}\n","import { AuthConfig, generateProtectedResourceMetadata } from '@silkweave/auth'\nimport { type RequestHandler } from 'express'\n\n/**\n * Handler for `GET /.well-known/oauth-protected-resource` (RFC 9728). Returns\n * the resource server's metadata pointing at the configured authorization\n * servers. Requires `auth.resourceUrl` and a non-empty `auth.authorizationServers`.\n */\nexport function protectedResourceMetadata(auth: AuthConfig): RequestHandler {\n if (!auth.resourceUrl || !auth.authorizationServers?.length) {\n throw new Error('@silkweave/mcp protectedResourceMetadata(): auth.resourceUrl and auth.authorizationServers are required')\n }\n const metadata = generateProtectedResourceMetadata(auth.resourceUrl, auth.authorizationServers)\n return (_req, res) => { res.json(metadata) }\n}\n","import { AuthConfig, OAuthRequest, OAuthResponse } from '@silkweave/auth'\nimport express, { type Request, type RequestHandler, type Response } from 'express'\n\nfunction toOAuthReq(req: Request): OAuthRequest {\n return {\n method: req.method,\n url: new URL(req.url, `${req.protocol}://${req.get('host')}`),\n headers: Object.fromEntries(Object.entries(req.headers).map(([k, v]) => [k, Array.isArray(v) ? v[0] : v])),\n body: req.body as Record<string, string> | undefined\n }\n}\n\nfunction sendOAuth(res: Response, oauthRes: OAuthResponse) {\n for (const [key, value] of Object.entries(oauthRes.headers)) { res.header(key, value) }\n if (oauthRes.body) {\n res.status(oauthRes.status).send(typeof oauthRes.body === 'string' ? oauthRes.body : JSON.stringify(oauthRes.body))\n } else {\n res.status(oauthRes.status).end()\n }\n}\n\nexport interface OAuthRouteHandlers {\n /** `GET /.well-known/oauth-authorization-server` - RFC 8414 discovery. */\n wellKnownAuthServer: RequestHandler\n /** `GET /authorize` - start the OAuth flow. */\n authorize: RequestHandler\n /** `GET {callbackPath}` - provider callback. */\n callback: RequestHandler\n /** Path the provider should redirect to (defaults to `/auth/callback`). */\n callbackPath: string\n /** `POST /token` - exchange code / refresh token. Includes urlencoded body parser. */\n token: RequestHandler[]\n /** `POST /register` - dynamic client registration. Includes JSON body parser. */\n register: RequestHandler[]\n}\n\n/**\n * Build the OAuth 2.1 proxy route handlers (authorize, callback, token,\n * register, well-known) backed by the configured `auth.provider`. Returns the\n * handlers as individual `RequestHandler`s so callers can register them\n * wherever they like - under `/mcp`, at the server root, etc.\n *\n * `token` and `register` are returned as middleware arrays because the\n * appropriate body parser must run before the handler. Apply with\n * `app.post(path, ...token)`.\n */\nexport function oauthRoutes(auth: AuthConfig): OAuthRouteHandlers {\n if (!auth.provider) { throw new Error('@silkweave/mcp oauthRoutes(): auth.provider is required') }\n const provider = auth.provider\n const callbackPath = auth.callbackPath ?? '/auth/callback'\n\n return {\n callbackPath,\n wellKnownAuthServer: (_req, res) => { sendOAuth(res, provider.metadata()) },\n authorize: async (req, res) => { sendOAuth(res, await provider.authorize(toOAuthReq(req))) },\n callback: async (req, res) => { sendOAuth(res, await provider.callback(toOAuthReq(req))) },\n token: [\n express.urlencoded({ extended: false }),\n async (req, res) => { sendOAuth(res, await provider.token(toOAuthReq(req))) }\n ],\n register: [\n express.json(),\n async (req, res) => { sendOAuth(res, await provider.register(toOAuthReq(req))) }\n ]\n }\n}\n","import { type RequestHandler } from 'express'\nimport { readFile } from 'fs/promises'\nimport { type SideloadResource } from '../util/sideload.js'\n\nexport interface SideloadResourceOptions {\n /** Directory to read sideload resources from. Defaults to `resources/` (cwd-relative). */\n resourceDir?: string\n}\n\n/**\n * Handler for `GET /resource/:id` - serves a large MCP response that was\n * sideloaded to disk as a `{id}` payload with a `{id}.json` metadata sidecar.\n *\n * The route param `id` is provided by the host framework (Express, Nest, etc.).\n */\nexport function sideloadResource(options: SideloadResourceOptions = {}): RequestHandler {\n const { resourceDir = 'resources' } = options\n return async (req, res) => {\n const id = req.params['id']\n if (!id || typeof id !== 'string') { throw new Error('Invalid ID') }\n const resourceMeta: SideloadResource = JSON.parse(await readFile(`${resourceDir}/${id}.json`, 'utf-8'))\n const buffer = await readFile(`${resourceDir}/${id}`)\n res.status(200)\n res.header('Content-Type', resourceMeta.contentType)\n res.send(buffer)\n }\n}\n","import { InMemoryEventStore } from '@modelcontextprotocol/sdk/examples/shared/inMemoryEventStore.js'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'\nimport { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js'\nimport { Action, ActionRun, isStreamingAction, runStreamingAction, SilkweaveContext, SilkweaveOptions } from '@silkweave/core'\nimport { createLogger } from '@silkweave/logger'\nimport { capitalCase, pascalCase } from 'change-case'\nimport { randomUUID } from 'crypto'\nimport { type RequestHandler } from 'express'\nimport { handleToolError, jsonToolResult, smartToolResult } from '../util/result.js'\nimport { authStorage } from './auth.js'\n\n/**\n * Build the generic `request` context value (the same key REST/tRPC populate)\n * from the MCP SDK's `extra.requestInfo`, so transport-agnostic consumers - e.g.\n * `@silkweave/nestjs` `@UseGuards` guards reading\n * `switchToHttp().getRequest().headers` - work over MCP. There is no path\n * `params`/`query` on an MCP call, so those are empty stand-ins for graceful\n * degradation. Returns `undefined` when no HTTP request info is available.\n */\nfunction requestFromExtra(requestInfo: { headers?: unknown; url?: { toString(): string } } | undefined) {\n if (!requestInfo) { return undefined }\n return { headers: requestInfo.headers ?? {}, url: requestInfo.url?.toString(), params: {}, query: {} }\n}\n\nfunction registerTools(server: McpServer, actions: Action[], context: SilkweaveContext) {\n for (const action of actions) {\n server.registerTool(pascalCase(action.name), {\n title: capitalCase(action.name),\n description: action.description,\n inputSchema: action.input\n }, async (input, extra) => {\n const logger = createLogger({\n stream: process.stderr,\n onLog: (level, data) => {\n extra.sendNotification({ method: 'notifications/message', params: { level, data } })\n },\n onProgress: ({ progress, total, message }) => {\n if (!extra._meta?.progressToken) { return }\n extra.sendNotification({\n method: 'notifications/progress',\n params: { progress, total, message, progressToken: extra._meta.progressToken }\n })\n }\n })\n const currentAuth = authStorage.getStore()\n const actionContext = context.fork({\n logger,\n extra,\n request: requestFromExtra(extra.requestInfo),\n ...(currentAuth ? { auth: currentAuth } : {})\n })\n // Client-sent `_meta.disposition` wins; otherwise fall back to the\n // action's configured default (`smart` when neither is set).\n const disposition = extra._meta?.disposition ?? action.disposition\n const progressToken = extra._meta?.progressToken\n try {\n let result: object | object[]\n if (isStreamingAction(action)) {\n result = await runStreamingAction(action, input, actionContext, progressToken\n ? async (chunk, index) => {\n await extra.sendNotification({\n method: 'notifications/progress',\n params: { progressToken, progress: index + 1, message: JSON.stringify(chunk) }\n })\n }\n : undefined)\n } else {\n result = await (action.run as ActionRun<object, object>)(input, actionContext)\n }\n if (action.toolResult) {\n const response = action.toolResult(result, actionContext)\n if (response) { return response }\n }\n return disposition === 'json' ? jsonToolResult(result) : smartToolResult(result)\n } catch (error) {\n return handleToolError(error)\n }\n })\n }\n}\n\nfunction createMcpServer(options: SilkweaveOptions, actions: Action[], context: SilkweaveContext): McpServer {\n const server = new McpServer({\n name: options.name,\n description: options.description,\n version: options.version\n }, {\n capabilities: { tools: {}, logging: {} }\n })\n registerTools(server, actions, context)\n return server\n}\n\nfunction createSessionTransport(transports: Record<string, StreamableHTTPServerTransport>): StreamableHTTPServerTransport {\n const eventStore = new InMemoryEventStore()\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n enableJsonResponse: false,\n eventStore,\n onsessioninitialized: (sId) => {\n transports[sId] = transport\n }\n })\n transport.onerror = (error) => { console.error(error) }\n transport.onclose = () => {\n const sid = transport.sessionId\n if (sid && transports[sid]) { delete transports[sid] }\n }\n return transport\n}\n\nexport interface McpTransportHandlers {\n /** `POST /mcp` - initialize session or dispatch request to an existing one. */\n post: RequestHandler\n /** `GET /mcp` and `DELETE /mcp` - long-poll session stream / session termination. Also covers `GET /mcp/resource/:id`. */\n stream: RequestHandler\n}\n\n/**\n * Build the MCP Streamable HTTP transport route handlers.\n *\n * Sessions are kept in a per-call closure (one map per `mcpTransport()` call),\n * so the same handler object should be registered for all three transport\n * routes - `POST /mcp`, `GET /mcp`, `DELETE /mcp` - to share session state.\n */\nexport function mcpTransport(\n silkweaveOptions: SilkweaveOptions,\n context: SilkweaveContext,\n actions: Action[]\n): McpTransportHandlers {\n const transports: Record<string, StreamableHTTPServerTransport> = {}\n\n const post: RequestHandler = async (req, res) => {\n const sessionId = req.headers['mcp-session-id'] as string | undefined\n try {\n if (sessionId && transports[sessionId]) {\n await transports[sessionId].handleRequest(req, res, req.body)\n return\n }\n if (!isInitializeRequest(req.body)) {\n res.status(404).json({ jsonrpc: '2.0', error: { code: -32_000, message: 'Session not found' }, id: null })\n return\n }\n const transport = createSessionTransport(transports)\n await createMcpServer(silkweaveOptions, actions, context).connect(transport)\n await transport.handleRequest(req, res, req.body)\n } catch (error) {\n console.error('Error handling MCP request:', error)\n if (!res.headersSent) {\n res.status(500).json({ jsonrpc: '2.0', error: { code: -32_603, message: 'Internal server error' }, id: null })\n }\n }\n }\n\n const stream: RequestHandler = async (req, res) => {\n const sessionId = req.headers['mcp-session-id'] as string | undefined\n if (!sessionId || !transports[sessionId]) {\n res.status(400).send('Invalid or missing session ID')\n return\n }\n await transports[sessionId].handleRequest(req, res)\n }\n\n return { post, stream }\n}\n","import { createMcpExpressApp, type CreateMcpExpressAppOptions } from '@modelcontextprotocol/sdk/server/express.js'\nimport { AuthConfig } from '@silkweave/auth'\nimport { Action, AdapterFactory, createContext, SilkweaveContext, SilkweaveOptions } from '@silkweave/core'\nimport { CorsOptions } from 'cors'\nimport express, { type Express } from 'express'\nimport { Server } from 'http'\nimport { authMiddleware } from '../handlers/auth.js'\nimport { mcpCors } from '../handlers/cors.js'\nimport { protectedResourceMetadata } from '../handlers/metadata.js'\nimport { oauthRoutes } from '../handlers/oauth.js'\nimport { sideloadResource } from '../handlers/sideload.js'\nimport { mcpTransport } from '../handlers/transport.js'\n\nexport interface StartMcpHttpOptions extends CreateMcpExpressAppOptions {\n host: string\n port: number\n auth?: AuthConfig\n /** CORS configuration. `false` to disable, omitted/`true` for permissive defaults, or a `CorsOptions` object. */\n cors?: CorsOptions | boolean\n /** Mount the `/resource/:id` sideload route. Default `true`. */\n sideloadResources?: boolean\n /** Directory the sideload route reads from. Default `'resources'`. */\n resourceDir?: string\n}\n\n/**\n * Build a fully-wired Express app that exposes the MCP Streamable HTTP\n * transport (plus OAuth / sideload / well-known routes when configured).\n *\n * Pass the resulting `app` to `app.listen(port, host)` yourself, or use the\n * top-level `startMcpServer()` / `http()` adapter conveniences.\n */\nexport function buildMcpExpressApp(\n silkweaveOptions: SilkweaveOptions,\n context: SilkweaveContext,\n actions: Action[],\n options: StartMcpHttpOptions\n): Express {\n const { host, auth, cors: corsConfig, sideloadResources = true, resourceDir, ...mcpAppOptions } = options\n const app = createMcpExpressApp({ ...mcpAppOptions, host })\n\n const corsHandler = mcpCors(corsConfig ?? true)\n if (corsHandler) { app.use(corsHandler) }\n\n if (auth?.authorizationServers?.length && auth.resourceUrl) {\n app.get('/.well-known/oauth-protected-resource', protectedResourceMetadata(auth))\n }\n\n let oauthPaths = new Set<string>()\n if (auth?.provider) {\n const oauth = oauthRoutes(auth)\n app.get('/.well-known/oauth-authorization-server', oauth.wellKnownAuthServer)\n app.get('/authorize', oauth.authorize)\n app.get(oauth.callbackPath, oauth.callback)\n app.post('/token', ...oauth.token)\n app.post('/register', ...oauth.register)\n oauthPaths = new Set(['/.well-known/oauth-authorization-server', '/authorize', oauth.callbackPath, '/token', '/register'])\n }\n\n if (auth) {\n const guard = authMiddleware(auth, context)\n app.use((req, res, next) => {\n if (req.path.startsWith('/.well-known/') || oauthPaths.has(req.path)) { return next() }\n return guard(req, res, next)\n })\n }\n\n if (sideloadResources) {\n app.get('/resource/:id', sideloadResource({ resourceDir }))\n }\n\n const transport = mcpTransport(silkweaveOptions, context, actions)\n app.post('/mcp', express.json(), transport.post)\n app.get('/mcp', transport.stream)\n app.delete('/mcp', transport.stream)\n app.get('/mcp/resource/:id', transport.stream)\n\n return app\n}\n\n/**\n * Spin up a standalone MCP Streamable HTTP server on `host:port` for the\n * given `actions`. Returns the underlying `Server` so callers can close it.\n *\n * Convenience for use cases that don't go through the `silkweave()` builder.\n */\nexport async function startMcpServer(\n silkweaveOptions: SilkweaveOptions,\n actions: Action[],\n options: StartMcpHttpOptions,\n context?: SilkweaveContext\n): Promise<Server> {\n const ctx = context ?? createContext({ adapter: 'http' })\n const app = buildMcpExpressApp(silkweaveOptions, ctx, actions, options)\n return new Promise<Server>((resolve, reject) => {\n const server = app.listen(options.port, options.host, (error) => {\n if (error) { reject(error); return }\n console.log(`MCP Streamable HTTP Server listening on http://${options.host}:${options.port}/mcp`)\n resolve(server)\n })\n })\n}\n\n/**\n * Silkweave adapter that owns its own HTTP server. Composes the MCP handler\n * primitives into a fully-wired Express app and listens on `host:port`.\n */\nexport const http: AdapterFactory<StartMcpHttpOptions> = (options) => {\n return (silkweaveOptions, baseContext) => {\n const context = baseContext.fork({ adapter: 'http' })\n let httpServer: Server | undefined\n return {\n context,\n start: async (actions) => {\n const app = buildMcpExpressApp(silkweaveOptions, context, actions, options)\n httpServer = await new Promise<Server>((resolve, reject) => {\n const s = app.listen(options.port, options.host, (error) => {\n if (error) { reject(error); return }\n console.log(`MCP Streamable HTTP Server listening on http://${options.host}:${options.port}/mcp`)\n resolve(s)\n })\n })\n },\n stop: async () => {\n if (!httpServer) { return }\n await new Promise<void>((resolve, reject) => {\n httpServer!.close((err) => err ? reject(err) : resolve())\n })\n httpServer = undefined\n }\n }\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { Action, ActionRun, AdapterFactory, isStreamingAction, runStreamingAction, SilkweaveContext } from '@silkweave/core'\nimport { createLogger } from '@silkweave/logger'\nimport { capitalCase, pascalCase } from 'change-case'\nimport { handleToolError, smartToolResult } from '../util/result.js'\n\nfunction registerTools(server: McpServer, actions: Action[], context: SilkweaveContext) {\n for (const action of actions) {\n server.registerTool(pascalCase(action.name), {\n title: capitalCase(action.name),\n description: action.description,\n inputSchema: action.input\n }, async (input, extra) => {\n const logger = createLogger({\n stream: false,\n onLog: (level, data) => {\n extra.sendNotification({ method: 'notifications/message', params: { level, data } })\n },\n onProgress: ({ progress, total, message }) => {\n if (!extra._meta?.progressToken) { return }\n extra.sendNotification({\n method: 'notifications/progress',\n params: { progress, total, message, progressToken: extra._meta.progressToken }\n })\n }\n })\n const actionContext = context.fork({ logger, extra })\n const progressToken = extra._meta?.progressToken\n try {\n let result: object | object[]\n if (isStreamingAction(action)) {\n result = await runStreamingAction(action, input, actionContext, progressToken\n ? async (chunk, index) => {\n await extra.sendNotification({\n method: 'notifications/progress',\n params: { progressToken, progress: index + 1, message: JSON.stringify(chunk) }\n })\n }\n : undefined)\n } else {\n result = await (action.run as ActionRun<object, object>)(input, actionContext)\n }\n return action.toolResult?.(result, actionContext) ?? smartToolResult(result)\n } catch (error) {\n return handleToolError(error)\n }\n })\n }\n}\n\nexport const stdio: AdapterFactory = () => {\n return (options, baseContext) => {\n const context = baseContext.fork({ adapter: 'stdio' })\n const server = new McpServer({\n name: options.name,\n description: options.description,\n version: options.version\n }, {\n capabilities: { tools: {}, logging: {} }\n })\n return {\n context,\n start: async (actions) => {\n registerTools(server, actions, context)\n const transport = new StdioServerTransport()\n await server.connect(transport)\n },\n stop: async () => {\n await server?.close()\n }\n }\n }\n}\n","import { randomUUID } from 'crypto'\nimport { writeFile } from 'fs/promises'\n\nexport interface SideloadResource {\n id: string\n name: string\n contentType: string\n size: number\n}\n\nexport async function createSideloadResource(buffer: Buffer, { name, contentType }: Pick<SideloadResource, 'name' | 'contentType'>) {\n const id = randomUUID()\n const resource: SideloadResource = { id, name, contentType, size: buffer.length }\n await Promise.all([\n writeFile(`resources/${id}`, buffer),\n writeFile(`resources/${id}.json`, JSON.stringify(resource), 'utf-8')\n ])\n return resource\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/handlers/cors.ts","../src/handlers/metadata.ts","../src/handlers/oauth.ts","../src/handlers/sideload.ts","../src/handlers/transport.ts","../src/adapter/http.ts","../src/adapter/stdio.ts","../src/util/sideload.ts"],"sourcesContent":["import cors, { CorsOptions } from 'cors'\nimport { type RequestHandler } from 'express'\n\n/** Headers required by the MCP protocol that must always be exposed when CORS is in use. */\nexport const MCP_REQUIRED_HEADERS = ['WWW-Authenticate', 'Last-Event-Id', 'Mcp-Protocol-Version']\n\n/**\n * CORS middleware preconfigured to expose the headers MCP clients need\n * (`Last-Event-Id`, `Mcp-Protocol-Version`, `WWW-Authenticate`) on top of any\n * user-supplied options. (Stateless transport - no `Mcp-Session-Id`.)\n *\n * Pass `false` to disable, omit / pass `true` for permissive defaults, or pass\n * a `CorsOptions` object to override.\n */\nexport function mcpCors(corsConfig: CorsOptions | boolean = true): RequestHandler | null {\n if (corsConfig === false) { return null }\n const userConfig = corsConfig === true ? {} : corsConfig\n const userExposed = userConfig.exposedHeaders\n const exposedHeaders = [\n ...MCP_REQUIRED_HEADERS,\n ...(Array.isArray(userExposed) ? userExposed : userExposed ? [userExposed] : [])\n ]\n return cors({ origin: '*', ...userConfig, exposedHeaders })\n}\n","import { AuthConfig, generateProtectedResourceMetadata } from '@silkweave/auth'\nimport { type RequestHandler } from 'express'\n\n/**\n * Handler for `GET /.well-known/oauth-protected-resource` (RFC 9728). Returns\n * the resource server's metadata pointing at the configured authorization\n * servers. Requires `auth.resourceUrl` and a non-empty `auth.authorizationServers`.\n */\nexport function protectedResourceMetadata(auth: AuthConfig): RequestHandler {\n if (!auth.resourceUrl || !auth.authorizationServers?.length) {\n throw new Error('@silkweave/mcp protectedResourceMetadata(): auth.resourceUrl and auth.authorizationServers are required')\n }\n const metadata = generateProtectedResourceMetadata(auth.resourceUrl, auth.authorizationServers, auth.requiredScopes)\n return (_req, res) => { res.json(metadata) }\n}\n","import { AuthConfig, OAuthRequest, OAuthResponse } from '@silkweave/auth'\nimport express, { type Request, type RequestHandler, type Response } from 'express'\n\nfunction toOAuthReq(req: Request): OAuthRequest {\n return {\n method: req.method,\n url: new URL(req.url, `${req.protocol}://${req.get('host')}`),\n headers: Object.fromEntries(Object.entries(req.headers).map(([k, v]) => [k, Array.isArray(v) ? v[0] : v])),\n body: req.body as Record<string, string> | undefined\n }\n}\n\nfunction sendOAuth(res: Response, oauthRes: OAuthResponse) {\n for (const [key, value] of Object.entries(oauthRes.headers)) { res.header(key, value) }\n if (oauthRes.body) {\n res.status(oauthRes.status).send(typeof oauthRes.body === 'string' ? oauthRes.body : JSON.stringify(oauthRes.body))\n } else {\n res.status(oauthRes.status).end()\n }\n}\n\nexport interface OAuthRouteHandlers {\n /** `GET /.well-known/oauth-authorization-server` - RFC 8414 discovery. */\n wellKnownAuthServer: RequestHandler\n /** `GET /authorize` - start the OAuth flow. */\n authorize: RequestHandler\n /** `GET {callbackPath}` - provider callback. */\n callback: RequestHandler\n /** Path the provider should redirect to (defaults to `/auth/callback`). */\n callbackPath: string\n /** `POST /token` - exchange code / refresh token. Includes urlencoded body parser. */\n token: RequestHandler[]\n /** `POST /register` - dynamic client registration. Includes JSON body parser. */\n register: RequestHandler[]\n}\n\n/**\n * Build the OAuth 2.1 proxy route handlers (authorize, callback, token,\n * register, well-known) backed by the configured `auth.provider`. Returns the\n * handlers as individual `RequestHandler`s so callers can register them\n * wherever they like - under `/mcp`, at the server root, etc.\n *\n * `token` and `register` are returned as middleware arrays because the\n * appropriate body parser must run before the handler. Apply with\n * `app.post(path, ...token)`.\n */\nexport function oauthRoutes(auth: AuthConfig): OAuthRouteHandlers {\n if (!auth.provider) { throw new Error('@silkweave/mcp oauthRoutes(): auth.provider is required') }\n const provider = auth.provider\n const callbackPath = auth.callbackPath ?? '/auth/callback'\n\n return {\n callbackPath,\n wellKnownAuthServer: (_req, res) => { sendOAuth(res, provider.metadata()) },\n authorize: async (req, res) => { sendOAuth(res, await provider.authorize(toOAuthReq(req))) },\n callback: async (req, res) => { sendOAuth(res, await provider.callback(toOAuthReq(req))) },\n token: [\n express.urlencoded({ extended: false }),\n async (req, res) => { sendOAuth(res, await provider.token(toOAuthReq(req))) }\n ],\n register: [\n express.json(),\n async (req, res) => { sendOAuth(res, await provider.register(toOAuthReq(req))) }\n ]\n }\n}\n","import { type RequestHandler } from 'express'\nimport { readFile } from 'fs/promises'\nimport { type SideloadResource } from '../util/sideload.js'\n\nexport interface SideloadResourceOptions {\n /** Directory to read sideload resources from. Defaults to `resources/` (cwd-relative). */\n resourceDir?: string\n}\n\n/**\n * Handler for `GET /resource/:id` - serves a large MCP response that was\n * sideloaded to disk as a `{id}` payload with a `{id}.json` metadata sidecar.\n *\n * The route param `id` is provided by the host framework (Express, Nest, etc.).\n */\nexport function sideloadResource(options: SideloadResourceOptions = {}): RequestHandler {\n const { resourceDir = 'resources' } = options\n return async (req, res) => {\n const id = req.params['id']\n if (!id || typeof id !== 'string') { throw new Error('Invalid ID') }\n const resourceMeta: SideloadResource = JSON.parse(await readFile(`${resourceDir}/${id}.json`, 'utf-8'))\n const buffer = await readFile(`${resourceDir}/${id}`)\n res.status(200)\n res.header('Content-Type', resourceMeta.contentType)\n res.send(buffer)\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'\nimport { Action, SilkweaveContext, SilkweaveOptions } from '@silkweave/core'\nimport { type RequestHandler } from 'express'\nimport { registerTools } from './registerTools.js'\n\nfunction createMcpServer(options: SilkweaveOptions, actions: Action[], context: SilkweaveContext): McpServer {\n const server = new McpServer({\n name: options.name,\n description: options.description,\n version: options.version\n }, {\n capabilities: { tools: {}, logging: {} }\n })\n registerTools(server, actions, context)\n return server\n}\n\nexport interface McpTransportHandlers {\n /** `POST /mcp` - handle a single stateless MCP request/response (SSE per call). */\n post: RequestHandler\n}\n\n/**\n * Build the MCP Streamable HTTP transport route handler.\n *\n * Stateless (per the 2026 spec direction): each `POST /mcp` mints a fresh\n * transport + server with `sessionIdGenerator: undefined`, handles exactly that\n * request (streaming progress over SSE when the call carries a `progressToken`),\n * and tears down on response close. No `Mcp-Session-Id`, no session map, no\n * `GET`/`DELETE` reconnect - any request can hit any instance.\n */\nexport function mcpTransport(\n silkweaveOptions: SilkweaveOptions,\n context: SilkweaveContext,\n actions: Action[]\n): McpTransportHandlers {\n const post: RequestHandler = async (req, res) => {\n try {\n const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined })\n const server = createMcpServer(silkweaveOptions, actions, context)\n res.on('close', () => { void transport.close(); void server.close() })\n await server.connect(transport)\n await transport.handleRequest(req, res, req.body)\n } catch (error) {\n console.error('Error handling MCP request:', error)\n if (!res.headersSent) {\n res.status(500).json({ jsonrpc: '2.0', error: { code: -32_603, message: 'Internal server error' }, id: null })\n }\n }\n }\n\n return { post }\n}\n","import { createMcpExpressApp, type CreateMcpExpressAppOptions } from '@modelcontextprotocol/sdk/server/express.js'\nimport { AuthConfig } from '@silkweave/auth'\nimport { Action, AdapterFactory, createContext, SilkweaveContext, SilkweaveOptions } from '@silkweave/core'\nimport { CorsOptions } from 'cors'\nimport express, { type Express } from 'express'\nimport { Server } from 'http'\nimport { authMiddleware } from '../handlers/auth.js'\nimport { mcpCors } from '../handlers/cors.js'\nimport { protectedResourceMetadata } from '../handlers/metadata.js'\nimport { oauthRoutes } from '../handlers/oauth.js'\nimport { sideloadResource } from '../handlers/sideload.js'\nimport { mcpTransport } from '../handlers/transport.js'\n\nexport interface StartMcpHttpOptions extends CreateMcpExpressAppOptions {\n host: string\n port: number\n auth?: AuthConfig\n /** CORS configuration. `false` to disable, omitted/`true` for permissive defaults, or a `CorsOptions` object. */\n cors?: CorsOptions | boolean\n /** Mount the `/resource/:id` sideload route. Default `true`. */\n sideloadResources?: boolean\n /** Directory the sideload route reads from. Default `'resources'`. */\n resourceDir?: string\n}\n\n/**\n * Build a fully-wired Express app that exposes the MCP Streamable HTTP\n * transport (plus OAuth / sideload / well-known routes when configured).\n *\n * Pass the resulting `app` to `app.listen(port, host)` yourself, or use the\n * top-level `startMcpServer()` / `http()` adapter conveniences.\n */\nexport function buildMcpExpressApp(\n silkweaveOptions: SilkweaveOptions,\n context: SilkweaveContext,\n actions: Action[],\n options: StartMcpHttpOptions\n): Express {\n const { host, auth, cors: corsConfig, sideloadResources = true, resourceDir, ...mcpAppOptions } = options\n const app = createMcpExpressApp({ ...mcpAppOptions, host })\n\n const corsHandler = mcpCors(corsConfig ?? true)\n if (corsHandler) { app.use(corsHandler) }\n\n if (auth?.authorizationServers?.length && auth.resourceUrl) {\n app.get('/.well-known/oauth-protected-resource', protectedResourceMetadata(auth))\n }\n\n let oauthPaths = new Set<string>()\n if (auth?.provider) {\n const oauth = oauthRoutes(auth)\n app.get('/.well-known/oauth-authorization-server', oauth.wellKnownAuthServer)\n app.get('/authorize', oauth.authorize)\n app.get(oauth.callbackPath, oauth.callback)\n app.post('/token', ...oauth.token)\n app.post('/register', ...oauth.register)\n oauthPaths = new Set(['/.well-known/oauth-authorization-server', '/authorize', oauth.callbackPath, '/token', '/register'])\n }\n\n if (auth) {\n const guard = authMiddleware(auth, context)\n app.use((req, res, next) => {\n if (req.path.startsWith('/.well-known/') || oauthPaths.has(req.path)) { return next() }\n return guard(req, res, next)\n })\n }\n\n if (sideloadResources) {\n app.get('/resource/:id', sideloadResource({ resourceDir }))\n }\n\n const transport = mcpTransport(silkweaveOptions, context, actions)\n app.post('/mcp', express.json(), transport.post)\n\n return app\n}\n\n/**\n * Spin up a standalone MCP Streamable HTTP server on `host:port` for the\n * given `actions`. Returns the underlying `Server` so callers can close it.\n *\n * Convenience for use cases that don't go through the `silkweave()` builder.\n */\nexport async function startMcpServer(\n silkweaveOptions: SilkweaveOptions,\n actions: Action[],\n options: StartMcpHttpOptions,\n context?: SilkweaveContext\n): Promise<Server> {\n const ctx = context ?? createContext({ adapter: 'http' })\n const app = buildMcpExpressApp(silkweaveOptions, ctx, actions, options)\n return new Promise<Server>((resolve, reject) => {\n const server = app.listen(options.port, options.host, (error) => {\n if (error) { reject(error); return }\n console.log(`MCP Streamable HTTP Server listening on http://${options.host}:${options.port}/mcp`)\n resolve(server)\n })\n })\n}\n\n/**\n * Silkweave adapter that owns its own HTTP server. Composes the MCP handler\n * primitives into a fully-wired Express app and listens on `host:port`.\n */\nexport const http: AdapterFactory<StartMcpHttpOptions> = (options) => {\n return (silkweaveOptions, baseContext) => {\n const context = baseContext.fork({ adapter: 'http' })\n let httpServer: Server | undefined\n return {\n context,\n start: async (actions) => {\n const app = buildMcpExpressApp(silkweaveOptions, context, actions, options)\n httpServer = await new Promise<Server>((resolve, reject) => {\n const s = app.listen(options.port, options.host, (error) => {\n if (error) { reject(error); return }\n console.log(`MCP Streamable HTTP Server listening on http://${options.host}:${options.port}/mcp`)\n resolve(s)\n })\n })\n },\n stop: async () => {\n if (!httpServer) { return }\n await new Promise<void>((resolve, reject) => {\n httpServer!.close((err) => err ? reject(err) : resolve())\n })\n httpServer = undefined\n }\n }\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { AdapterFactory } from '@silkweave/core'\nimport { registerTools } from '../handlers/registerTools.js'\n\nexport const stdio: AdapterFactory = () => {\n return (options, baseContext) => {\n const context = baseContext.fork({ adapter: 'stdio' })\n const server = new McpServer({\n name: options.name,\n description: options.description,\n version: options.version\n }, {\n capabilities: { tools: {}, logging: {} }\n })\n return {\n context,\n start: async (actions) => {\n registerTools(server, actions, context, { logStream: false })\n const transport = new StdioServerTransport()\n await server.connect(transport)\n },\n stop: async () => {\n await server?.close()\n }\n }\n }\n}\n","import { randomUUID } from 'crypto'\nimport { writeFile } from 'fs/promises'\n\nexport interface SideloadResource {\n id: string\n name: string\n contentType: string\n size: number\n}\n\nexport async function createSideloadResource(buffer: Buffer, { name, contentType }: Pick<SideloadResource, 'name' | 'contentType'>) {\n const id = randomUUID()\n const resource: SideloadResource = { id, name, contentType, size: buffer.length }\n await Promise.all([\n writeFile(`resources/${id}`, buffer),\n writeFile(`resources/${id}.json`, JSON.stringify(resource), 'utf-8')\n ])\n return resource\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { a as smartToolResult, n as handleToolError, r as jsonToolResult } from "./result-B_9Eo1ey.mjs";
|
|
2
|
+
import { createLogger, isStreamingAction, runStreamingAction } from "@silkweave/core";
|
|
3
|
+
import { validateToken } from "@silkweave/auth";
|
|
4
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
|
+
import { capitalCase, pascalCase } from "change-case";
|
|
6
|
+
//#region src/handlers/auth.ts
|
|
7
|
+
/**
|
|
8
|
+
* Per-request bearer-token storage used by tool handlers to read the resolved
|
|
9
|
+
* `AuthInfo` for the currently-handled MCP call.
|
|
10
|
+
*/
|
|
11
|
+
const authStorage = new AsyncLocalStorage();
|
|
12
|
+
/**
|
|
13
|
+
* Express middleware that validates the `Authorization: Bearer …` header via
|
|
14
|
+
* the supplied `AuthConfig`. On success the resolved `AuthInfo` is placed in
|
|
15
|
+
* `authStorage` for the duration of the downstream handler - `mcpTransport`'s
|
|
16
|
+
* tool callbacks pick it up to populate the silkweave context's `auth` key.
|
|
17
|
+
*
|
|
18
|
+
* The middleware should NOT be applied to OAuth-discovery / token routes -
|
|
19
|
+
* compose it only on the routes that require an authenticated caller (the MCP
|
|
20
|
+
* transport itself, sideload, etc.).
|
|
21
|
+
*/
|
|
22
|
+
function authMiddleware(auth, context) {
|
|
23
|
+
return async (req, res, next) => {
|
|
24
|
+
const result = await validateToken(req.headers.authorization, auth, context.fork({ request: req }));
|
|
25
|
+
if (result.error) {
|
|
26
|
+
for (const [key, value] of Object.entries(result.error.headers)) res.header(key, value);
|
|
27
|
+
res.status(result.error.statusCode).json(result.error.body);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (result.auth) authStorage.run(result.auth, () => {
|
|
31
|
+
next();
|
|
32
|
+
});
|
|
33
|
+
else next();
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/handlers/registerTools.ts
|
|
38
|
+
/**
|
|
39
|
+
* Build the generic `request` context value (the same key REST/tRPC populate)
|
|
40
|
+
* from the MCP SDK's `extra.requestInfo`, so transport-agnostic consumers - e.g.
|
|
41
|
+
* `@silkweave/nestjs` `@UseGuards` guards reading
|
|
42
|
+
* `switchToHttp().getRequest().headers` - work over MCP. There are no path
|
|
43
|
+
* `params`/`query`/`body` on the raw MCP call, so those start as empty
|
|
44
|
+
* stand-ins; `@silkweave/nestjs` later fills them from the validated tool input
|
|
45
|
+
* per the reflected param sources (path -> `params`, `@Query` -> `query`,
|
|
46
|
+
* `@Body` -> `body`) so guards reading any of them decide as they would over
|
|
47
|
+
* REST. Returns `undefined` when no HTTP request info is available.
|
|
48
|
+
*/
|
|
49
|
+
function requestFromExtra(requestInfo) {
|
|
50
|
+
if (!requestInfo) return;
|
|
51
|
+
return {
|
|
52
|
+
headers: requestInfo.headers ?? {},
|
|
53
|
+
url: requestInfo.url?.toString(),
|
|
54
|
+
params: {},
|
|
55
|
+
query: {},
|
|
56
|
+
body: {}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Per-call logger that bridges silkweave logs/progress onto MCP notifications. */
|
|
60
|
+
function createToolLogger(extra, stream) {
|
|
61
|
+
return createLogger({
|
|
62
|
+
stream,
|
|
63
|
+
onLog: (level, data) => {
|
|
64
|
+
extra.sendNotification({
|
|
65
|
+
method: "notifications/message",
|
|
66
|
+
params: {
|
|
67
|
+
level,
|
|
68
|
+
data
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
onProgress: ({ progress, total, message }) => {
|
|
73
|
+
if (!extra._meta?.progressToken) return;
|
|
74
|
+
extra.sendNotification({
|
|
75
|
+
method: "notifications/progress",
|
|
76
|
+
params: {
|
|
77
|
+
progress,
|
|
78
|
+
total,
|
|
79
|
+
message,
|
|
80
|
+
progressToken: extra._meta.progressToken
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/** Run the action, streaming chunks as progress notifications when a token is set. */
|
|
87
|
+
async function runAction(action, input, context, extra) {
|
|
88
|
+
const progressToken = extra._meta?.progressToken;
|
|
89
|
+
if (isStreamingAction(action)) return runStreamingAction(action, input, context, progressToken ? async (chunk, index) => {
|
|
90
|
+
await extra.sendNotification({
|
|
91
|
+
method: "notifications/progress",
|
|
92
|
+
params: {
|
|
93
|
+
progressToken,
|
|
94
|
+
progress: index + 1,
|
|
95
|
+
message: JSON.stringify(chunk)
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
} : void 0);
|
|
99
|
+
return action.run(input, context);
|
|
100
|
+
}
|
|
101
|
+
/** Format via the action's `toolResult` hook, else the resolved disposition. */
|
|
102
|
+
function formatToolResult(action, result, context, disposition) {
|
|
103
|
+
if (action.toolResult) {
|
|
104
|
+
const response = action.toolResult(result, context);
|
|
105
|
+
if (response) return response;
|
|
106
|
+
}
|
|
107
|
+
return disposition === "json" ? jsonToolResult(result) : smartToolResult(result);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Register every action as an MCP tool on `server`. Shared by all MCP transports
|
|
111
|
+
* (`stdio`, `http`, `edge`). Each tool call forks `context` with a per-call
|
|
112
|
+
* `logger`, the SDK `extra`, a synthesized `request` (see `requestFromExtra`),
|
|
113
|
+
* and - when bearer auth ran for this request - the resolved `auth`. The result
|
|
114
|
+
* is formatted by the action's `toolResult` hook if present, otherwise per the
|
|
115
|
+
* resolved disposition (client `_meta.disposition` > `action.disposition` >
|
|
116
|
+
* `smart`).
|
|
117
|
+
*/
|
|
118
|
+
function registerTools(server, actions, context, options = {}) {
|
|
119
|
+
const stream = options.logStream ?? process.stderr;
|
|
120
|
+
for (const action of actions) server.registerTool(pascalCase(action.name), {
|
|
121
|
+
title: capitalCase(action.name),
|
|
122
|
+
description: action.description,
|
|
123
|
+
inputSchema: action.input
|
|
124
|
+
}, async (input, extra) => {
|
|
125
|
+
const logger = createToolLogger(extra, stream);
|
|
126
|
+
const currentAuth = authStorage.getStore();
|
|
127
|
+
const actionContext = context.fork({
|
|
128
|
+
logger,
|
|
129
|
+
extra,
|
|
130
|
+
request: requestFromExtra(extra.requestInfo),
|
|
131
|
+
...currentAuth ? { auth: currentAuth } : {}
|
|
132
|
+
});
|
|
133
|
+
const disposition = extra._meta?.disposition ?? action.disposition;
|
|
134
|
+
try {
|
|
135
|
+
return formatToolResult(action, await runAction(action, input, actionContext, extra), actionContext, disposition);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
return handleToolError(error);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
142
|
+
export { authStorage as i, requestFromExtra as n, authMiddleware as r, registerTools as t };
|
|
143
|
+
|
|
144
|
+
//# sourceMappingURL=registerTools-Z6JnoM60.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registerTools-Z6JnoM60.mjs","names":[],"sources":["../src/handlers/auth.ts","../src/handlers/registerTools.ts"],"sourcesContent":["import { AuthConfig, AuthInfo, validateToken } from '@silkweave/auth'\nimport { SilkweaveContext } from '@silkweave/core'\nimport { type RequestHandler } from 'express'\nimport { AsyncLocalStorage } from 'node:async_hooks'\n\n/**\n * Per-request bearer-token storage used by tool handlers to read the resolved\n * `AuthInfo` for the currently-handled MCP call.\n */\nexport const authStorage = new AsyncLocalStorage<AuthInfo>()\n\n/**\n * Express middleware that validates the `Authorization: Bearer …` header via\n * the supplied `AuthConfig`. On success the resolved `AuthInfo` is placed in\n * `authStorage` for the duration of the downstream handler - `mcpTransport`'s\n * tool callbacks pick it up to populate the silkweave context's `auth` key.\n *\n * The middleware should NOT be applied to OAuth-discovery / token routes -\n * compose it only on the routes that require an authenticated caller (the MCP\n * transport itself, sideload, etc.).\n */\nexport function authMiddleware(auth: AuthConfig, context: SilkweaveContext): RequestHandler {\n return async (req, res, next) => {\n const result = await validateToken(req.headers.authorization, auth, context.fork({ request: req }))\n if (result.error) {\n for (const [key, value] of Object.entries(result.error.headers)) { res.header(key, value) }\n res.status(result.error.statusCode).json(result.error.body)\n return\n }\n if (result.auth) {\n authStorage.run(result.auth, () => { next() })\n } else {\n next()\n }\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js'\nimport type { ServerNotification, ServerRequest } from '@modelcontextprotocol/sdk/types.js'\nimport { Action, ActionRun, createLogger, isStreamingAction, runStreamingAction, SilkweaveContext } from '@silkweave/core'\nimport { capitalCase, pascalCase } from 'change-case'\nimport { handleToolError, jsonToolResult, smartToolResult } from '../util/result.js'\nimport { authStorage } from './auth.js'\n\ntype LogStream = NonNullable<Parameters<typeof createLogger>[0]>['stream']\ntype ToolExtra = RequestHandlerExtra<ServerRequest, ServerNotification>\n\nexport interface RegisterToolsOptions {\n /**\n * Where the per-call logger writes its human-readable stream. The `stdio`\n * adapter MUST pass `false` (stdout is the MCP protocol channel); the HTTP\n * transports default to `process.stderr`.\n */\n logStream?: LogStream\n}\n\n/**\n * Build the generic `request` context value (the same key REST/tRPC populate)\n * from the MCP SDK's `extra.requestInfo`, so transport-agnostic consumers - e.g.\n * `@silkweave/nestjs` `@UseGuards` guards reading\n * `switchToHttp().getRequest().headers` - work over MCP. There are no path\n * `params`/`query`/`body` on the raw MCP call, so those start as empty\n * stand-ins; `@silkweave/nestjs` later fills them from the validated tool input\n * per the reflected param sources (path -> `params`, `@Query` -> `query`,\n * `@Body` -> `body`) so guards reading any of them decide as they would over\n * REST. Returns `undefined` when no HTTP request info is available.\n */\nexport function requestFromExtra(requestInfo: { headers?: unknown; url?: { toString(): string } } | undefined) {\n if (!requestInfo) { return undefined }\n return { headers: requestInfo.headers ?? {}, url: requestInfo.url?.toString(), params: {}, query: {}, body: {} }\n}\n\n/** Per-call logger that bridges silkweave logs/progress onto MCP notifications. */\nfunction createToolLogger(extra: ToolExtra, stream: LogStream) {\n return createLogger({\n stream,\n onLog: (level, data) => {\n extra.sendNotification({ method: 'notifications/message', params: { level, data } })\n },\n onProgress: ({ progress, total, message }) => {\n if (!extra._meta?.progressToken) { return }\n extra.sendNotification({\n method: 'notifications/progress',\n params: { progress, total, message, progressToken: extra._meta.progressToken }\n })\n }\n })\n}\n\n/** Run the action, streaming chunks as progress notifications when a token is set. */\nasync function runAction(action: Action, input: object, context: SilkweaveContext, extra: ToolExtra): Promise<object | object[]> {\n const progressToken = extra._meta?.progressToken\n if (isStreamingAction(action)) {\n return runStreamingAction(action, input, context, progressToken\n ? async (chunk, index) => {\n await extra.sendNotification({\n method: 'notifications/progress',\n params: { progressToken, progress: index + 1, message: JSON.stringify(chunk) }\n })\n }\n : undefined)\n }\n return (action.run as ActionRun<object, object>)(input, context)\n}\n\n/** Format via the action's `toolResult` hook, else the resolved disposition. */\nfunction formatToolResult(action: Action, result: object | object[], context: SilkweaveContext, disposition: unknown) {\n if (action.toolResult) {\n const response = action.toolResult(result, context)\n if (response) { return response }\n }\n return disposition === 'json' ? jsonToolResult(result) : smartToolResult(result)\n}\n\n/**\n * Register every action as an MCP tool on `server`. Shared by all MCP transports\n * (`stdio`, `http`, `edge`). Each tool call forks `context` with a per-call\n * `logger`, the SDK `extra`, a synthesized `request` (see `requestFromExtra`),\n * and - when bearer auth ran for this request - the resolved `auth`. The result\n * is formatted by the action's `toolResult` hook if present, otherwise per the\n * resolved disposition (client `_meta.disposition` > `action.disposition` >\n * `smart`).\n */\nexport function registerTools(\n server: McpServer,\n actions: Action[],\n context: SilkweaveContext,\n options: RegisterToolsOptions = {}\n) {\n const stream = options.logStream ?? process.stderr\n for (const action of actions) {\n server.registerTool(pascalCase(action.name), {\n title: capitalCase(action.name),\n description: action.description,\n inputSchema: action.input\n }, async (input, extra) => {\n const logger = createToolLogger(extra, stream)\n const currentAuth = authStorage.getStore()\n const actionContext = context.fork({\n logger,\n extra,\n request: requestFromExtra(extra.requestInfo),\n ...(currentAuth ? { auth: currentAuth } : {})\n })\n // Client-sent `_meta.disposition` wins; otherwise fall back to the\n // action's configured default (`smart` when neither is set).\n const disposition = extra._meta?.disposition ?? action.disposition\n try {\n const result = await runAction(action, input, actionContext, extra)\n return formatToolResult(action, result, actionContext, disposition)\n } catch (error) {\n return handleToolError(error)\n }\n })\n }\n}\n"],"mappings":";;;;;;;;;;AASA,MAAa,cAAc,IAAI,mBAA6B;;;;;;;;;;;AAY5D,SAAgB,eAAe,MAAkB,SAA2C;AAC1F,QAAO,OAAO,KAAK,KAAK,SAAS;EAC/B,MAAM,SAAS,MAAM,cAAc,IAAI,QAAQ,eAAe,MAAM,QAAQ,KAAK,EAAE,SAAS,KAAK,CAAC,CAAC;AACnG,MAAI,OAAO,OAAO;AAChB,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,MAAM,QAAQ,CAAI,KAAI,OAAO,KAAK,MAAM;AACzF,OAAI,OAAO,OAAO,MAAM,WAAW,CAAC,KAAK,OAAO,MAAM,KAAK;AAC3D;;AAEF,MAAI,OAAO,KACT,aAAY,IAAI,OAAO,YAAY;AAAE,SAAM;IAAG;MAE9C,OAAM;;;;;;;;;;;;;;;;ACDZ,SAAgB,iBAAiB,aAA8E;AAC7G,KAAI,CAAC,YAAe;AACpB,QAAO;EAAE,SAAS,YAAY,WAAW,EAAE;EAAE,KAAK,YAAY,KAAK,UAAU;EAAE,QAAQ,EAAE;EAAE,OAAO,EAAE;EAAE,MAAM,EAAE;EAAE;;;AAIlH,SAAS,iBAAiB,OAAkB,QAAmB;AAC7D,QAAO,aAAa;EAClB;EACA,QAAQ,OAAO,SAAS;AACtB,SAAM,iBAAiB;IAAE,QAAQ;IAAyB,QAAQ;KAAE;KAAO;KAAM;IAAE,CAAC;;EAEtF,aAAa,EAAE,UAAU,OAAO,cAAc;AAC5C,OAAI,CAAC,MAAM,OAAO,cAAiB;AACnC,SAAM,iBAAiB;IACrB,QAAQ;IACR,QAAQ;KAAE;KAAU;KAAO;KAAS,eAAe,MAAM,MAAM;KAAe;IAC/E,CAAC;;EAEL,CAAC;;;AAIJ,eAAe,UAAU,QAAgB,OAAe,SAA2B,OAA8C;CAC/H,MAAM,gBAAgB,MAAM,OAAO;AACnC,KAAI,kBAAkB,OAAO,CAC3B,QAAO,mBAAmB,QAAQ,OAAO,SAAS,gBAC9C,OAAO,OAAO,UAAU;AACxB,QAAM,MAAM,iBAAiB;GAC3B,QAAQ;GACR,QAAQ;IAAE;IAAe,UAAU,QAAQ;IAAG,SAAS,KAAK,UAAU,MAAM;IAAE;GAC/E,CAAC;KAEF,KAAA,EAAU;AAEhB,QAAQ,OAAO,IAAkC,OAAO,QAAQ;;;AAIlE,SAAS,iBAAiB,QAAgB,QAA2B,SAA2B,aAAsB;AACpH,KAAI,OAAO,YAAY;EACrB,MAAM,WAAW,OAAO,WAAW,QAAQ,QAAQ;AACnD,MAAI,SAAY,QAAO;;AAEzB,QAAO,gBAAgB,SAAS,eAAe,OAAO,GAAG,gBAAgB,OAAO;;;;;;;;;;;AAYlF,SAAgB,cACd,QACA,SACA,SACA,UAAgC,EAAE,EAClC;CACA,MAAM,SAAS,QAAQ,aAAa,QAAQ;AAC5C,MAAK,MAAM,UAAU,QACnB,QAAO,aAAa,WAAW,OAAO,KAAK,EAAE;EAC3C,OAAO,YAAY,OAAO,KAAK;EAC/B,aAAa,OAAO;EACpB,aAAa,OAAO;EACrB,EAAE,OAAO,OAAO,UAAU;EACzB,MAAM,SAAS,iBAAiB,OAAO,OAAO;EAC9C,MAAM,cAAc,YAAY,UAAU;EAC1C,MAAM,gBAAgB,QAAQ,KAAK;GACjC;GACA;GACA,SAAS,iBAAiB,MAAM,YAAY;GAC5C,GAAI,cAAc,EAAE,MAAM,aAAa,GAAG,EAAE;GAC7C,CAAC;EAGF,MAAM,cAAc,MAAM,OAAO,eAAe,OAAO;AACvD,MAAI;AAEF,UAAO,iBAAiB,QAAQ,MADX,UAAU,QAAQ,OAAO,eAAe,MAAM,EAC3B,eAAe,YAAY;WAC5D,OAAO;AACd,UAAO,gBAAgB,MAAM;;GAE/B"}
|
|
@@ -76,4 +76,4 @@ function parseResourceMessage({ resource }) {
|
|
|
76
76
|
//#endregion
|
|
77
77
|
export { smartToolResult as a, parseResourceMessage as i, handleToolError as n, jsonToolResult as r, errorToolResult as t };
|
|
78
78
|
|
|
79
|
-
//# sourceMappingURL=result-
|
|
79
|
+
//# sourceMappingURL=result-B_9Eo1ey.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result-
|
|
1
|
+
{"version":3,"file":"result-B_9Eo1ey.mjs","names":[],"sources":["../src/util/result.ts"],"sourcesContent":["import { EmbeddedResource, type CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport { SilkweaveError } from '@silkweave/core'\nimport { randomUUID } from 'node:crypto'\n\nexport function smartToolResult(data: string | object | object[]): CallToolResult {\n const text = typeof data === 'string' ? data : JSON.stringify(data)\n const mimeType = typeof data === 'string' ? 'text/plain' : 'application/json'\n const ext = typeof data === 'string' ? 'txt' : 'json'\n if (text.length > 4096) {\n const uri = `mcp://toolResult/${randomUUID()}.${ext}`\n const buffer = Buffer.from(text)\n const blob = buffer.toString('base64')\n return {\n content: [\n { type: 'text', text: `Received resource ${uri} with ${buffer.byteLength} bytes` },\n { type: 'resource', resource: { uri, mimeType, blob } }\n ]\n }\n } else {\n return {\n content: [{ type: 'text' as const, text }]\n }\n }\n}\n\nexport function jsonToolResult(data: object, isError = false): CallToolResult {\n const result: CallToolResult = { content: [{ type: 'text' as const, text: JSON.stringify(data) }] }\n if (isError) { result.isError = true }\n return result\n}\n\nexport function errorToolResult({ code, name, message }: SilkweaveError): CallToolResult {\n return {\n isError: true,\n content: [{\n type: 'text',\n text: JSON.stringify({ success: false, code, name, message })\n }]\n }\n}\n\nexport function handleToolError(error: unknown): CallToolResult {\n if (error instanceof SilkweaveError) {\n return jsonToolResult({ success: false, name: error.name, message: error.message, code: error.code }, true)\n } else if (error instanceof Error) {\n // Log the full error (incl. stack) to stderr only - never put the stack trace\n // on the wire, where it would leak server internals to the MCP client.\n console.error(error)\n return jsonToolResult({ success: false, name: error.name, message: error.message }, true)\n } else {\n // Likewise keep the raw thrown value server-side - only a generic message goes out.\n console.error('Unknown tool error:', error)\n return jsonToolResult({ success: false, name: 'Unknown error', message: 'An unknown error occurred' }, true)\n }\n}\n\nexport function parseResourceMessage({ resource }: EmbeddedResource) {\n const text = ('blob' in resource) ? Buffer.from(resource.blob, 'base64').toString('utf-8') : resource.text\n return text\n}\n"],"mappings":";;;AAIA,SAAgB,gBAAgB,MAAkD;CAChF,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,KAAK;CACnE,MAAM,WAAW,OAAO,SAAS,WAAW,eAAe;CAC3D,MAAM,MAAM,OAAO,SAAS,WAAW,QAAQ;AAC/C,KAAI,KAAK,SAAS,MAAM;EACtB,MAAM,MAAM,oBAAoB,YAAY,CAAC,GAAG;EAChD,MAAM,SAAS,OAAO,KAAK,KAAK;EAChC,MAAM,OAAO,OAAO,SAAS,SAAS;AACtC,SAAO,EACL,SAAS,CACP;GAAE,MAAM;GAAQ,MAAM,qBAAqB,IAAI,QAAQ,OAAO,WAAW;GAAS,EAClF;GAAE,MAAM;GAAY,UAAU;IAAE;IAAK;IAAU;IAAM;GAAE,CACxD,EACF;OAED,QAAO,EACL,SAAS,CAAC;EAAE,MAAM;EAAiB;EAAM,CAAC,EAC3C;;AAIL,SAAgB,eAAe,MAAc,UAAU,OAAuB;CAC5E,MAAM,SAAyB,EAAE,SAAS,CAAC;EAAE,MAAM;EAAiB,MAAM,KAAK,UAAU,KAAK;EAAE,CAAC,EAAE;AACnG,KAAI,QAAW,QAAO,UAAU;AAChC,QAAO;;AAGT,SAAgB,gBAAgB,EAAE,MAAM,MAAM,WAA2C;AACvF,QAAO;EACL,SAAS;EACT,SAAS,CAAC;GACR,MAAM;GACN,MAAM,KAAK,UAAU;IAAE,SAAS;IAAO;IAAM;IAAM;IAAS,CAAC;GAC9D,CAAC;EACH;;AAGH,SAAgB,gBAAgB,OAAgC;AAC9D,KAAI,iBAAiB,eACnB,QAAO,eAAe;EAAE,SAAS;EAAO,MAAM,MAAM;EAAM,SAAS,MAAM;EAAS,MAAM,MAAM;EAAM,EAAE,KAAK;UAClG,iBAAiB,OAAO;AAGjC,UAAQ,MAAM,MAAM;AACpB,SAAO,eAAe;GAAE,SAAS;GAAO,MAAM,MAAM;GAAM,SAAS,MAAM;GAAS,EAAE,KAAK;QACpF;AAEL,UAAQ,MAAM,uBAAuB,MAAM;AAC3C,SAAO,eAAe;GAAE,SAAS;GAAO,MAAM;GAAiB,SAAS;GAA6B,EAAE,KAAK;;;AAIhH,SAAgB,qBAAqB,EAAE,YAA8B;AAEnE,QADc,UAAU,WAAY,OAAO,KAAK,SAAS,MAAM,SAAS,CAAC,SAAS,QAAQ,GAAG,SAAS"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Action, SilkweaveContext, SilkweaveError, createLogger } from "@silkweave/core";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { CallToolResult, EmbeddedResource } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
|
|
5
|
+
//#region src/handlers/registerTools.d.ts
|
|
6
|
+
type LogStream = NonNullable<Parameters<typeof createLogger>[0]>['stream'];
|
|
7
|
+
interface RegisterToolsOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Where the per-call logger writes its human-readable stream. The `stdio`
|
|
10
|
+
* adapter MUST pass `false` (stdout is the MCP protocol channel); the HTTP
|
|
11
|
+
* transports default to `process.stderr`.
|
|
12
|
+
*/
|
|
13
|
+
logStream?: LogStream;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Build the generic `request` context value (the same key REST/tRPC populate)
|
|
17
|
+
* from the MCP SDK's `extra.requestInfo`, so transport-agnostic consumers - e.g.
|
|
18
|
+
* `@silkweave/nestjs` `@UseGuards` guards reading
|
|
19
|
+
* `switchToHttp().getRequest().headers` - work over MCP. There are no path
|
|
20
|
+
* `params`/`query`/`body` on the raw MCP call, so those start as empty
|
|
21
|
+
* stand-ins; `@silkweave/nestjs` later fills them from the validated tool input
|
|
22
|
+
* per the reflected param sources (path -> `params`, `@Query` -> `query`,
|
|
23
|
+
* `@Body` -> `body`) so guards reading any of them decide as they would over
|
|
24
|
+
* REST. Returns `undefined` when no HTTP request info is available.
|
|
25
|
+
*/
|
|
26
|
+
declare function requestFromExtra(requestInfo: {
|
|
27
|
+
headers?: unknown;
|
|
28
|
+
url?: {
|
|
29
|
+
toString(): string;
|
|
30
|
+
};
|
|
31
|
+
} | undefined): {
|
|
32
|
+
headers: {};
|
|
33
|
+
url: string | undefined;
|
|
34
|
+
params: {};
|
|
35
|
+
query: {};
|
|
36
|
+
body: {};
|
|
37
|
+
} | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Register every action as an MCP tool on `server`. Shared by all MCP transports
|
|
40
|
+
* (`stdio`, `http`, `edge`). Each tool call forks `context` with a per-call
|
|
41
|
+
* `logger`, the SDK `extra`, a synthesized `request` (see `requestFromExtra`),
|
|
42
|
+
* and - when bearer auth ran for this request - the resolved `auth`. The result
|
|
43
|
+
* is formatted by the action's `toolResult` hook if present, otherwise per the
|
|
44
|
+
* resolved disposition (client `_meta.disposition` > `action.disposition` >
|
|
45
|
+
* `smart`).
|
|
46
|
+
*/
|
|
47
|
+
declare function registerTools(server: McpServer, actions: Action[], context: SilkweaveContext, options?: RegisterToolsOptions): void;
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/util/result.d.ts
|
|
50
|
+
declare function smartToolResult(data: string | object | object[]): CallToolResult;
|
|
51
|
+
declare function jsonToolResult(data: object, isError?: boolean): CallToolResult;
|
|
52
|
+
declare function errorToolResult({
|
|
53
|
+
code,
|
|
54
|
+
name,
|
|
55
|
+
message
|
|
56
|
+
}: SilkweaveError): CallToolResult;
|
|
57
|
+
declare function handleToolError(error: unknown): CallToolResult;
|
|
58
|
+
declare function parseResourceMessage({
|
|
59
|
+
resource
|
|
60
|
+
}: EmbeddedResource): string;
|
|
61
|
+
//#endregion
|
|
62
|
+
export { smartToolResult as a, requestFromExtra as c, parseResourceMessage as i, handleToolError as n, RegisterToolsOptions as o, jsonToolResult as r, registerTools as s, errorToolResult as t };
|
|
63
|
+
//# sourceMappingURL=result-_Bg2cDAG.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-_Bg2cDAG.d.mts","names":[],"sources":["../src/handlers/registerTools.ts","../src/util/result.ts"],"mappings":";;;;;KAQK,SAAA,GAAY,WAAA,CAAY,UAAA,QAAkB,YAAA;AAAA,UAG9B,oBAAA;;AARyG;;;;EAcxH,SAAA,GAAY,SAAA;AAAA;;;;;;;;AANd;;;;iBAoBgB,gBAAA,CAAiB,WAAA;EAAe,OAAA;EAAmB,GAAA;IAAQ,QAAA;EAAA;AAAA;;;;;;;;;;;AAwD3E;;;;;iBAAgB,aAAA,CACd,MAAA,EAAQ,SAAA,EACR,OAAA,EAAS,MAAA,IACT,OAAA,EAAS,gBAAA,EACT,OAAA,GAAS,oBAAA;;;iBCvFK,eAAA,CAAgB,IAAA,+BAAmC,cAAA;AAAA,iBAqBnD,cAAA,CAAe,IAAA,UAAc,OAAA,aAAkB,cAAA;AAAA,iBAM/C,eAAA,CAAA;EAAkB,IAAA;EAAM,IAAA;EAAM;AAAA,GAAW,cAAA,GAAiB,cAAA;AAAA,iBAU1D,eAAA,CAAgB,KAAA,YAAiB,cAAA;AAAA,iBAejC,oBAAA,CAAA;EAAuB;AAAA,GAAY,gBAAA"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as smartToolResult, c as requestFromExtra, i as parseResourceMessage, n as handleToolError, o as RegisterToolsOptions, r as jsonToolResult, s as registerTools, t as errorToolResult } from "./result-_Bg2cDAG.mjs";
|
|
2
|
+
export { RegisterToolsOptions, errorToolResult, handleToolError, jsonToolResult, parseResourceMessage, registerTools, requestFromExtra, smartToolResult };
|
package/build/tools.mjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { n as requestFromExtra, t as registerTools } from "./registerTools-Z6JnoM60.mjs";
|
|
2
|
+
import { a as smartToolResult, i as parseResourceMessage, n as handleToolError, r as jsonToolResult, t as errorToolResult } from "./result-B_9Eo1ey.mjs";
|
|
3
|
+
export { errorToolResult, handleToolError, jsonToolResult, parseResourceMessage, registerTools, requestFromExtra, smartToolResult };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@silkweave/mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Silkweave MCP Package",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://www.silkweave.dev",
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
"types": "./build/index.d.mts",
|
|
25
25
|
"default": "./build/index.mjs"
|
|
26
26
|
},
|
|
27
|
+
"./tools": {
|
|
28
|
+
"@silkweave/source": "./src/tools.ts",
|
|
29
|
+
"types": "./build/tools.d.mts",
|
|
30
|
+
"default": "./build/tools.mjs"
|
|
31
|
+
},
|
|
27
32
|
"./cli-proxy": {
|
|
28
33
|
"@silkweave/source": "./src/cliProxy.ts",
|
|
29
34
|
"types": "./build/cliProxy.d.mts",
|
|
@@ -33,40 +38,43 @@
|
|
|
33
38
|
"dependencies": {
|
|
34
39
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
35
40
|
"change-case": "^5.4.4",
|
|
36
|
-
"cors": "^2.8.6",
|
|
37
|
-
"express": "^5.2.1",
|
|
38
41
|
"zod": "^3.25.0",
|
|
39
|
-
"@silkweave/auth": "
|
|
40
|
-
"@silkweave/core": "
|
|
41
|
-
"@silkweave/logger": "2.6.1"
|
|
42
|
+
"@silkweave/auth": "3.1.0",
|
|
43
|
+
"@silkweave/core": "3.1.0"
|
|
42
44
|
},
|
|
43
45
|
"peerDependencies": {
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
+
"commander": "^13.1.0",
|
|
47
|
+
"cors": "^2.8.5",
|
|
48
|
+
"express": "^5.0.0"
|
|
46
49
|
},
|
|
47
50
|
"peerDependenciesMeta": {
|
|
48
|
-
"
|
|
51
|
+
"commander": {
|
|
49
52
|
"optional": true
|
|
50
53
|
},
|
|
51
|
-
"
|
|
54
|
+
"cors": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
57
|
+
"express": {
|
|
52
58
|
"optional": true
|
|
53
59
|
}
|
|
54
60
|
},
|
|
55
61
|
"devDependencies": {
|
|
56
|
-
"@clack/prompts": "^1.0.1",
|
|
57
62
|
"@eslint/js": "^10.0.1",
|
|
58
63
|
"@modelcontextprotocol/inspector": "^0.21.1",
|
|
59
|
-
"commander": "^13.1.0",
|
|
60
64
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
61
65
|
"@types/cors": "^2.8.19",
|
|
62
66
|
"@types/express": "^5.0.6",
|
|
63
67
|
"@types/node": "^22.0.0",
|
|
68
|
+
"commander": "^13.1.0",
|
|
69
|
+
"cors": "^2.8.6",
|
|
64
70
|
"eslint": "^10.4.0",
|
|
71
|
+
"express": "^5.2.1",
|
|
65
72
|
"rimraf": "^6.1.3",
|
|
66
73
|
"tsdown": "^0.21.8",
|
|
67
74
|
"tsx": "^4.21.0",
|
|
68
75
|
"typescript": "^5.9.3",
|
|
69
|
-
"typescript-eslint": "^8.56.1"
|
|
76
|
+
"typescript-eslint": "^8.56.1",
|
|
77
|
+
"vitest": "^4.1.9"
|
|
70
78
|
},
|
|
71
79
|
"scripts": {
|
|
72
80
|
"clean": "rimraf build",
|
|
@@ -74,6 +82,7 @@
|
|
|
74
82
|
"watch": "tsdown --watch",
|
|
75
83
|
"typecheck": "tsc --noEmit",
|
|
76
84
|
"lint": "eslint",
|
|
77
|
-
"check": "pnpm lint && pnpm typecheck"
|
|
85
|
+
"check": "pnpm lint && pnpm typecheck",
|
|
86
|
+
"test": "vitest run"
|
|
78
87
|
}
|
|
79
88
|
}
|