@silkweave/edge 3.2.0 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -92,6 +92,9 @@ const { adapter, handler } = edge({
92
92
  | `enableJsonResponse` | `boolean` | `false` | Return JSON responses instead of SSE streams |
93
93
  | `auth` | `AuthConfig` | - | Bearer-token validation + OAuth routes (see [Auth](#auth)) |
94
94
  | `path` | `string` | `/mcp` | The MCP transport path |
95
+ | `allowedHosts` | `string[]` | - | Opt-in DNS-rebinding protection: when set, the transport validates the inbound `Host` header against this list (browser pages on other hosts are rejected) |
96
+ | `allowedOrigins` | `string[]` | - | Opt-in DNS-rebinding protection: validate the inbound `Origin` header against this list |
97
+ | `corsOrigin` | `string` | `*` | The `Access-Control-Allow-Origin` value. Set to a specific origin (pair with `allowedOrigins`) instead of reflecting every origin |
95
98
  | `filterActions` | `FilterActions` | - | Per-request tool filter, applied before tools are registered on every POST (the stateless transport recomputes the tool list per request, so permission changes apply on the next `tools/list`). Receives the actions (with their `tags`) and a request stand-in `{ headers, url, method, toolName }` - `method` is the JSON-RPC method (skip DB lookups on `initialize`/`ping`), `toolName` is set for `tools/call`. May be async. A throw surfaces as its `SilkweaveError.statusCode` (e.g. 401) with a JSON-RPC error body, or 500 for other errors - never an empty tool list |
96
99
  | `onToolCall` | `OnToolCall` | - | Telemetry hook invoked once per tool call, fire-and-forget (never awaited on the result path; errors logged and swallowed). Events carry `{ action, tool, transport: 'mcp', durationMs, ok, errorCode?, errorMessage?, resultBytes?, sideloaded?, context }` |
97
100
 
package/build/index.d.mts CHANGED
@@ -7,6 +7,19 @@ interface EdgeAdapterOptions {
7
7
  enableJsonResponse?: boolean;
8
8
  auth?: AuthConfig;
9
9
  path?: string;
10
+ /**
11
+ * DNS-rebinding protection. When `allowedHosts`/`allowedOrigins` are set the
12
+ * transport validates the inbound `Host`/`Origin` against them (browser pages
13
+ * on other origins are then rejected). Off by default to preserve the
14
+ * any-origin behavior; set these when the server is reachable from a browser.
15
+ */
16
+ allowedHosts?: string[];
17
+ allowedOrigins?: string[];
18
+ /**
19
+ * `Access-Control-Allow-Origin` value. Defaults to `'*'`. Set to a specific
20
+ * origin to stop reflecting every origin (pair with `allowedOrigins`).
21
+ */
22
+ corsOrigin?: string;
10
23
  /**
11
24
  * Per-request tool filter, applied before `registerTools()` on every POST
12
25
  * (the stateless transport recomputes the tool list per request). See
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapter/edge.ts"],"mappings":";;;;;UAMiB,kBAAA;EACf,kBAAA;EACA,IAAA,GAAO,UAAA;EACP,IAAA;;;;;;;;EAQA,aAAA,GAAgB,aAAA;EATT;EAWP,UAAA,GAAa,UAAA;AAAA;AAAA,UAGE,WAAA;EACf,OAAA,EAAS,gBAAA;EACT,OAAA,GAAU,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;EACvC,GAAA,GAAM,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;EACnC,IAAA,GAAO,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;EACpC,MAAA,GAAS,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;AAAA;AAAA,iBA0DxB,IAAA,CAAK,OAAA,GAAS,kBAAA,GAA0B,WAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapter/edge.ts"],"mappings":";;;;;UAMiB,kBAAA;EACf,kBAAA;EACA,IAAA,GAAO,UAAA;EACP,IAAA;;;;;;;EAOA,YAAA;EACA,cAAA;EATO;;;;EAcP,UAAA;EAQA;;;;;;AAKF;EALE,aAAA,GAAgB,aAAA;;EAEhB,UAAA,GAAa,UAAA;AAAA;AAAA,UAGE,WAAA;EACf,OAAA,EAAS,gBAAA;EACT,OAAA,GAAU,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;EACvC,GAAA,GAAM,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;EACnC,IAAA,GAAO,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;EACpC,MAAA,GAAS,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;AAAA;AAAA,iBAiGxB,IAAA,CAAK,OAAA,GAAS,kBAAA,GAA0B,WAAA"}
package/build/index.mjs CHANGED
@@ -4,18 +4,69 @@ import { generateProtectedResourceMetadata, validateToken } from "@silkweave/aut
4
4
  import { validateActionDisposition } from "@silkweave/core";
5
5
  import { filterErrorResponse, registerTools, rpcInfo } from "@silkweave/mcp/tools";
6
6
  //#region src/adapter/edge.ts
7
- const CORS_HEADERS = {
8
- "Access-Control-Allow-Origin": "*",
9
- "Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
10
- "Access-Control-Allow-Headers": "*",
11
- "Access-Control-Max-Age": "86400"
7
+ function buildCorsHeaders(origin) {
8
+ return {
9
+ "Access-Control-Allow-Origin": origin,
10
+ "Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
11
+ "Access-Control-Allow-Headers": "*",
12
+ "Access-Control-Max-Age": "86400"
13
+ };
14
+ }
15
+ /** A malformed OAuth request (e.g. non-JSON body) - surfaced as a 400, not a 500. */
16
+ var OAuthRequestError = class extends Error {
17
+ constructor(code, message) {
18
+ super(message);
19
+ this.code = code;
20
+ this.name = "OAuthRequestError";
21
+ }
12
22
  };
23
+ /** Transport DNS-rebinding config, enabled only when host/origin allow-lists are set. */
24
+ function dnsRebindingOptions(options) {
25
+ if (!options.allowedHosts && !options.allowedOrigins) return {};
26
+ return {
27
+ enableDnsRebindingProtection: true,
28
+ ...options.allowedHosts ? { allowedHosts: options.allowedHosts } : {},
29
+ ...options.allowedOrigins ? { allowedOrigins: options.allowedOrigins } : {}
30
+ };
31
+ }
32
+ /** Map a thrown handler error to a JSON error Response (400 for a bad OAuth body, else 500). */
33
+ function edgeErrorResponse(error, corsHeaders) {
34
+ if (error instanceof OAuthRequestError) return new Response(JSON.stringify({
35
+ error: error.code,
36
+ error_description: error.message
37
+ }), {
38
+ status: 400,
39
+ headers: {
40
+ ...corsHeaders,
41
+ "Content-Type": "application/json"
42
+ }
43
+ });
44
+ console.error("edge handler error:", error);
45
+ return new Response(JSON.stringify({
46
+ jsonrpc: "2.0",
47
+ error: {
48
+ code: -32603,
49
+ message: "Internal server error"
50
+ },
51
+ id: null
52
+ }), {
53
+ status: 500,
54
+ headers: {
55
+ ...corsHeaders,
56
+ "Content-Type": "application/json"
57
+ }
58
+ });
59
+ }
13
60
  async function parseOAuthRequest(url, request) {
14
61
  let body;
15
62
  if (request.method === "POST") {
16
63
  const contentType = request.headers.get("content-type") ?? "";
17
64
  const text = await request.text();
18
- if (contentType.includes("json")) body = JSON.parse(text);
65
+ if (contentType.includes("json")) try {
66
+ body = JSON.parse(text);
67
+ } catch {
68
+ throw new OAuthRequestError("invalid_request", "Request body is not valid JSON");
69
+ }
19
70
  else body = Object.fromEntries(new URLSearchParams(text));
20
71
  }
21
72
  return {
@@ -45,12 +96,15 @@ async function routeOAuth(url, request, provider, callbackPath) {
45
96
  function edge(options = {}) {
46
97
  const mcpPath = options.path ?? "/mcp";
47
98
  const callbackPath = options.auth?.callbackPath ?? "/auth/callback";
99
+ const CORS_HEADERS = buildCorsHeaders(options.corsOrigin ?? "*");
48
100
  let _actions = [];
49
101
  let _options = null;
50
102
  let _context = null;
51
103
  let _readyResolve;
52
- const _ready = new Promise((resolve) => {
104
+ let _readyReject;
105
+ const _ready = new Promise((resolve, reject) => {
53
106
  _readyResolve = resolve;
107
+ _readyReject = reject;
54
108
  });
55
109
  const validPaths = new Set([mcpPath]);
56
110
  if (options.auth?.authorizationServers?.length && options.auth.resourceUrl) validPaths.add("/.well-known/oauth-protected-resource");
@@ -68,7 +122,7 @@ function edge(options = {}) {
68
122
  "/token": ["POST"],
69
123
  "/register": ["POST"]
70
124
  } : null;
71
- const handleRequest = async (request) => {
125
+ const handleRequestInner = async (request) => {
72
126
  const url = new URL(request.url);
73
127
  if (!validPaths.has(url.pathname)) return new Response("Not Found", { status: 404 });
74
128
  if (request.method === "OPTIONS") return new Response(null, {
@@ -98,6 +152,21 @@ function edge(options = {}) {
98
152
  }
99
153
  });
100
154
  await _ready;
155
+ const rawBody = await request.clone().json().catch(() => void 0);
156
+ if (Array.isArray(rawBody)) return new Response(JSON.stringify({
157
+ jsonrpc: "2.0",
158
+ error: {
159
+ code: -32600,
160
+ message: "JSON-RPC batch requests are not supported"
161
+ },
162
+ id: null
163
+ }), {
164
+ status: 400,
165
+ headers: {
166
+ ...CORS_HEADERS,
167
+ "Content-Type": "application/json"
168
+ }
169
+ });
101
170
  let requestContext = _context;
102
171
  if (options.auth) {
103
172
  const result = await validateToken(request.headers.get("authorization"), options.auth, _context.fork({ request }));
@@ -108,28 +177,26 @@ function edge(options = {}) {
108
177
  if (result.auth) requestContext = _context.fork({ auth: result.auth });
109
178
  }
110
179
  let activeActions = _actions;
111
- if (options.filterActions) {
112
- const body = await request.clone().json().catch(() => void 0);
113
- try {
114
- activeActions = await options.filterActions(_actions, {
115
- headers: Object.fromEntries(request.headers.entries()),
116
- url: request.url,
117
- ...rpcInfo(body)
118
- });
119
- } catch (error) {
120
- const mapped = filterErrorResponse(error, body);
121
- return new Response(JSON.stringify(mapped.body), {
122
- status: mapped.status,
123
- headers: {
124
- ...CORS_HEADERS,
125
- "Content-Type": "application/json"
126
- }
127
- });
128
- }
180
+ if (options.filterActions) try {
181
+ activeActions = await options.filterActions(_actions, {
182
+ headers: Object.fromEntries(request.headers.entries()),
183
+ url: request.url,
184
+ ...rpcInfo(rawBody)
185
+ });
186
+ } catch (error) {
187
+ const mapped = filterErrorResponse(error, rawBody);
188
+ return new Response(JSON.stringify(mapped.body), {
189
+ status: mapped.status,
190
+ headers: {
191
+ ...CORS_HEADERS,
192
+ "Content-Type": "application/json"
193
+ }
194
+ });
129
195
  }
130
196
  const transport = new WebStandardStreamableHTTPServerTransport({
131
197
  sessionIdGenerator: void 0,
132
- enableJsonResponse: options.enableJsonResponse
198
+ enableJsonResponse: options.enableJsonResponse,
199
+ ...dnsRebindingOptions(options)
133
200
  });
134
201
  const server = new McpServer({
135
202
  name: _options.name,
@@ -143,15 +210,27 @@ function edge(options = {}) {
143
210
  await server.connect(transport);
144
211
  return transport.handleRequest(request);
145
212
  };
213
+ const handleRequest = async (request) => {
214
+ try {
215
+ return await handleRequestInner(request);
216
+ } catch (error) {
217
+ return edgeErrorResponse(error, CORS_HEADERS);
218
+ }
219
+ };
146
220
  const adapter = (silkweaveOptions, baseContext) => {
147
221
  _options = silkweaveOptions;
148
222
  _context = baseContext.fork({ adapter: "edge" });
149
223
  return {
150
224
  context: _context,
151
225
  start: async (actions) => {
152
- actions.forEach(validateActionDisposition);
153
- _actions = actions;
154
- _readyResolve();
226
+ try {
227
+ actions.forEach(validateActionDisposition);
228
+ _actions = actions;
229
+ _readyResolve();
230
+ } catch (error) {
231
+ _readyReject(error);
232
+ throw error;
233
+ }
155
234
  },
156
235
  stop: async () => {
157
236
  _actions = [];
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/adapter/edge.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js'\nimport { AuthConfig, generateProtectedResourceMetadata, OAuthRequest, OAuthResponse, validateToken } from '@silkweave/auth'\nimport { Action, AdapterGenerator, OnToolCall, SilkweaveContext, SilkweaveOptions, validateActionDisposition } from '@silkweave/core'\nimport { filterErrorResponse, registerTools, rpcInfo, type FilterActions } from '@silkweave/mcp/tools'\n\nexport interface EdgeAdapterOptions {\n enableJsonResponse?: boolean\n auth?: AuthConfig\n path?: string\n /**\n * Per-request tool filter, applied before `registerTools()` on every POST\n * (the stateless transport recomputes the tool list per request). See\n * `FilterActions` for the request stand-in (`headers`/`url`/`method`/\n * `toolName`) and error semantics (a throw surfaces as its\n * `SilkweaveError.statusCode` or 500 - never an empty tool list).\n */\n filterActions?: FilterActions\n /** Telemetry hook invoked once per tool call (fire-and-forget). */\n onToolCall?: OnToolCall\n}\n\nexport interface EdgeAdapter {\n adapter: AdapterGenerator\n handler: (request: Request) => Promise<Response>\n GET: (request: Request) => Promise<Response>\n POST: (request: Request) => Promise<Response>\n DELETE: (request: Request) => Promise<Response>\n}\n\nconst CORS_HEADERS: Record<string, string> = {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': '*',\n 'Access-Control-Max-Age': '86400'\n}\n\nasync function parseOAuthRequest(url: URL, request: Request): Promise<OAuthRequest> {\n let body: Record<string, string> | undefined\n if (request.method === 'POST') {\n const contentType = request.headers.get('content-type') ?? ''\n const text = await request.text()\n if (contentType.includes('json')) {\n body = JSON.parse(text)\n } else {\n body = Object.fromEntries(new URLSearchParams(text))\n }\n }\n return {\n method: request.method,\n url,\n headers: Object.fromEntries(request.headers.entries()),\n body\n }\n}\n\nfunction oauthResponseToResponse(oauthRes: OAuthResponse): Response {\n const responseBody = oauthRes.body\n ? (typeof oauthRes.body === 'string' ? oauthRes.body : JSON.stringify(oauthRes.body))\n : null\n return new Response(responseBody, { status: oauthRes.status, headers: oauthRes.headers })\n}\n\nasync function routeOAuth(\n url: URL,\n request: Request,\n provider: NonNullable<AuthConfig['provider']>,\n callbackPath: string\n): Promise<Response> {\n const oauthReq = await parseOAuthRequest(url, request)\n let oauthRes\n if (url.pathname === '/.well-known/oauth-authorization-server') {\n oauthRes = provider.metadata()\n } else if (url.pathname === '/authorize') {\n oauthRes = await provider.authorize(oauthReq)\n } else if (url.pathname === callbackPath) {\n oauthRes = await provider.callback(oauthReq)\n } else if (url.pathname === '/token') {\n oauthRes = await provider.token(oauthReq)\n } else {\n oauthRes = await provider.register(oauthReq)\n }\n return oauthResponseToResponse(oauthRes)\n}\n\nexport function edge(options: EdgeAdapterOptions = {}): EdgeAdapter {\n const mcpPath = options.path ?? '/mcp'\n const callbackPath = options.auth?.callbackPath ?? '/auth/callback'\n\n let _actions: Action[] = []\n let _options: SilkweaveOptions | null = null\n let _context: SilkweaveContext | null = null\n let _readyResolve: () => void\n const _ready = new Promise<void>((resolve) => {\n _readyResolve = resolve\n })\n\n // Pre-compute valid paths for fast rejection of bogus requests\n const validPaths = new Set<string>([mcpPath])\n if (options.auth?.authorizationServers?.length && options.auth.resourceUrl) {\n validPaths.add('/.well-known/oauth-protected-resource')\n }\n if (options.auth?.provider) {\n validPaths.add('/.well-known/oauth-authorization-server')\n validPaths.add('/authorize')\n validPaths.add(callbackPath)\n validPaths.add('/token')\n validPaths.add('/register')\n }\n\n // OAuth path → allowed methods (built once, not per-request)\n const oauthPaths: Record<string, string[]> | null = options.auth?.provider\n ? {\n '/.well-known/oauth-authorization-server': ['GET'],\n '/authorize': ['GET'],\n [callbackPath]: ['GET'],\n '/token': ['POST'],\n '/register': ['POST']\n }\n : null\n\n const handleRequest = async (request: Request): Promise<Response> => {\n const url = new URL(request.url)\n\n // Fast rejection - no async work, no allocations for unknown paths\n if (!validPaths.has(url.pathname)) {\n return new Response('Not Found', { status: 404 })\n }\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, { status: 200, headers: CORS_HEADERS })\n }\n\n // Protected resource metadata (RFC 9728)\n if (url.pathname === '/.well-known/oauth-protected-resource') {\n const metadata = generateProtectedResourceMetadata(options.auth!.resourceUrl!, options.auth!.authorizationServers!, options.auth!.requiredScopes)\n return new Response(JSON.stringify(metadata), {\n headers: { ...CORS_HEADERS, 'Content-Type': 'application/json', 'Cache-Control': 'max-age=3600' }\n })\n }\n\n // OAuth provider routes\n if (oauthPaths) {\n const methods = oauthPaths[url.pathname]\n if (methods) {\n if (!methods.includes(request.method)) {\n return new Response('Method not allowed', { status: 405 })\n }\n return routeOAuth(url, request, options.auth!.provider!, callbackPath)\n }\n }\n\n // MCP transport (stateless): only POST carries JSON-RPC. A standing-stream\n // GET or a session-teardown DELETE has no session to act on - and the SDK's\n // GET handler would open an SSE stream that never closes, hanging the request\n // on serverless runtimes (e.g. Cloudflare Workers). Answer them with 405, which\n // the Streamable HTTP spec explicitly permits for servers without a GET stream.\n if (request.method !== 'POST') {\n return new Response('Method Not Allowed', {\n status: 405,\n headers: { ...CORS_HEADERS, Allow: 'POST' }\n })\n }\n\n // wait for silkweave().start() to complete\n await _ready\n\n let requestContext = _context!\n if (options.auth) {\n const result = await validateToken(request.headers.get('authorization'), options.auth, _context!.fork({ request }))\n if (result.error) {\n return new Response(JSON.stringify(result.error.body), {\n status: result.error.statusCode,\n headers: result.error.headers\n })\n }\n if (result.auth) {\n requestContext = _context!.fork({ auth: result.auth })\n }\n }\n\n let activeActions = _actions\n if (options.filterActions) {\n // Clone so the transport can still consume the original body stream.\n const body: unknown = await request.clone().json().catch(() => undefined)\n try {\n activeActions = await options.filterActions(_actions, {\n headers: Object.fromEntries(request.headers.entries()),\n url: request.url,\n ...rpcInfo(body)\n })\n } catch (error) {\n const mapped = filterErrorResponse(error, body)\n return new Response(JSON.stringify(mapped.body), {\n status: mapped.status,\n headers: { ...CORS_HEADERS, 'Content-Type': 'application/json' }\n })\n }\n }\n\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: options.enableJsonResponse\n })\n\n const server = new McpServer({\n name: _options!.name,\n description: _options!.description,\n version: _options!.version\n }, {\n capabilities: { tools: {}, logging: {} }\n })\n\n registerTools(server, activeActions, requestContext, { onToolCall: options.onToolCall })\n\n await server.connect(transport)\n return transport.handleRequest(request)\n }\n\n const adapter: AdapterGenerator = (silkweaveOptions: SilkweaveOptions, baseContext: SilkweaveContext) => {\n _options = silkweaveOptions\n _context = baseContext.fork({ adapter: 'edge' })\n return {\n context: _context,\n start: async (actions) => {\n actions.forEach(validateActionDisposition)\n _actions = actions\n _readyResolve()\n },\n stop: async () => {\n _actions = []\n }\n }\n }\n\n return {\n adapter,\n handler: handleRequest,\n GET: handleRequest,\n POST: handleRequest,\n DELETE: handleRequest\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/adapter/edge.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js'\nimport { AuthConfig, generateProtectedResourceMetadata, OAuthRequest, OAuthResponse, validateToken } from '@silkweave/auth'\nimport { Action, AdapterGenerator, OnToolCall, SilkweaveContext, SilkweaveOptions, validateActionDisposition } from '@silkweave/core'\nimport { filterErrorResponse, registerTools, rpcInfo, type FilterActions } from '@silkweave/mcp/tools'\n\nexport interface EdgeAdapterOptions {\n enableJsonResponse?: boolean\n auth?: AuthConfig\n path?: string\n /**\n * DNS-rebinding protection. When `allowedHosts`/`allowedOrigins` are set the\n * transport validates the inbound `Host`/`Origin` against them (browser pages\n * on other origins are then rejected). Off by default to preserve the\n * any-origin behavior; set these when the server is reachable from a browser.\n */\n allowedHosts?: string[]\n allowedOrigins?: string[]\n /**\n * `Access-Control-Allow-Origin` value. Defaults to `'*'`. Set to a specific\n * origin to stop reflecting every origin (pair with `allowedOrigins`).\n */\n corsOrigin?: string\n /**\n * Per-request tool filter, applied before `registerTools()` on every POST\n * (the stateless transport recomputes the tool list per request). See\n * `FilterActions` for the request stand-in (`headers`/`url`/`method`/\n * `toolName`) and error semantics (a throw surfaces as its\n * `SilkweaveError.statusCode` or 500 - never an empty tool list).\n */\n filterActions?: FilterActions\n /** Telemetry hook invoked once per tool call (fire-and-forget). */\n onToolCall?: OnToolCall\n}\n\nexport interface EdgeAdapter {\n adapter: AdapterGenerator\n handler: (request: Request) => Promise<Response>\n GET: (request: Request) => Promise<Response>\n POST: (request: Request) => Promise<Response>\n DELETE: (request: Request) => Promise<Response>\n}\n\nfunction buildCorsHeaders(origin: string): Record<string, string> {\n return {\n 'Access-Control-Allow-Origin': origin,\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': '*',\n 'Access-Control-Max-Age': '86400'\n }\n}\n\n/** A malformed OAuth request (e.g. non-JSON body) - surfaced as a 400, not a 500. */\nclass OAuthRequestError extends Error {\n constructor(public readonly code: string, message: string) {\n super(message)\n this.name = 'OAuthRequestError'\n }\n}\n\n/** Transport DNS-rebinding config, enabled only when host/origin allow-lists are set. */\nfunction dnsRebindingOptions(options: EdgeAdapterOptions): Record<string, unknown> {\n if (!options.allowedHosts && !options.allowedOrigins) { return {} }\n return {\n enableDnsRebindingProtection: true,\n ...(options.allowedHosts ? { allowedHosts: options.allowedHosts } : {}),\n ...(options.allowedOrigins ? { allowedOrigins: options.allowedOrigins } : {})\n }\n}\n\n/** Map a thrown handler error to a JSON error Response (400 for a bad OAuth body, else 500). */\nfunction edgeErrorResponse(error: unknown, corsHeaders: Record<string, string>): Response {\n if (error instanceof OAuthRequestError) {\n return new Response(JSON.stringify({ error: error.code, error_description: error.message }), {\n status: 400,\n headers: { ...corsHeaders, 'Content-Type': 'application/json' }\n })\n }\n console.error('edge handler error:', error)\n return new Response(JSON.stringify({ jsonrpc: '2.0', error: { code: -32_603, message: 'Internal server error' }, id: null }), {\n status: 500,\n headers: { ...corsHeaders, 'Content-Type': 'application/json' }\n })\n}\n\nasync function parseOAuthRequest(url: URL, request: Request): Promise<OAuthRequest> {\n let body: Record<string, string> | undefined\n if (request.method === 'POST') {\n const contentType = request.headers.get('content-type') ?? ''\n const text = await request.text()\n if (contentType.includes('json')) {\n try {\n body = JSON.parse(text)\n } catch {\n throw new OAuthRequestError('invalid_request', 'Request body is not valid JSON')\n }\n } else {\n body = Object.fromEntries(new URLSearchParams(text))\n }\n }\n return {\n method: request.method,\n url,\n headers: Object.fromEntries(request.headers.entries()),\n body\n }\n}\n\nfunction oauthResponseToResponse(oauthRes: OAuthResponse): Response {\n const responseBody = oauthRes.body\n ? (typeof oauthRes.body === 'string' ? oauthRes.body : JSON.stringify(oauthRes.body))\n : null\n return new Response(responseBody, { status: oauthRes.status, headers: oauthRes.headers })\n}\n\nasync function routeOAuth(\n url: URL,\n request: Request,\n provider: NonNullable<AuthConfig['provider']>,\n callbackPath: string\n): Promise<Response> {\n const oauthReq = await parseOAuthRequest(url, request)\n let oauthRes\n if (url.pathname === '/.well-known/oauth-authorization-server') {\n oauthRes = provider.metadata()\n } else if (url.pathname === '/authorize') {\n oauthRes = await provider.authorize(oauthReq)\n } else if (url.pathname === callbackPath) {\n oauthRes = await provider.callback(oauthReq)\n } else if (url.pathname === '/token') {\n oauthRes = await provider.token(oauthReq)\n } else {\n oauthRes = await provider.register(oauthReq)\n }\n return oauthResponseToResponse(oauthRes)\n}\n\nexport function edge(options: EdgeAdapterOptions = {}): EdgeAdapter {\n const mcpPath = options.path ?? '/mcp'\n const callbackPath = options.auth?.callbackPath ?? '/auth/callback'\n const CORS_HEADERS = buildCorsHeaders(options.corsOrigin ?? '*')\n\n let _actions: Action[] = []\n let _options: SilkweaveOptions | null = null\n let _context: SilkweaveContext | null = null\n let _readyResolve: () => void\n let _readyReject: (error: unknown) => void\n const _ready = new Promise<void>((resolve, reject) => {\n _readyResolve = resolve\n _readyReject = reject\n })\n\n // Pre-compute valid paths for fast rejection of bogus requests\n const validPaths = new Set<string>([mcpPath])\n if (options.auth?.authorizationServers?.length && options.auth.resourceUrl) {\n validPaths.add('/.well-known/oauth-protected-resource')\n }\n if (options.auth?.provider) {\n validPaths.add('/.well-known/oauth-authorization-server')\n validPaths.add('/authorize')\n validPaths.add(callbackPath)\n validPaths.add('/token')\n validPaths.add('/register')\n }\n\n // OAuth path → allowed methods (built once, not per-request)\n const oauthPaths: Record<string, string[]> | null = options.auth?.provider\n ? {\n '/.well-known/oauth-authorization-server': ['GET'],\n '/authorize': ['GET'],\n [callbackPath]: ['GET'],\n '/token': ['POST'],\n '/register': ['POST']\n }\n : null\n\n const handleRequestInner = async (request: Request): Promise<Response> => {\n const url = new URL(request.url)\n\n // Fast rejection - no async work, no allocations for unknown paths\n if (!validPaths.has(url.pathname)) {\n return new Response('Not Found', { status: 404 })\n }\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, { status: 200, headers: CORS_HEADERS })\n }\n\n // Protected resource metadata (RFC 9728)\n if (url.pathname === '/.well-known/oauth-protected-resource') {\n const metadata = generateProtectedResourceMetadata(options.auth!.resourceUrl!, options.auth!.authorizationServers!, options.auth!.requiredScopes)\n return new Response(JSON.stringify(metadata), {\n headers: { ...CORS_HEADERS, 'Content-Type': 'application/json', 'Cache-Control': 'max-age=3600' }\n })\n }\n\n // OAuth provider routes\n if (oauthPaths) {\n const methods = oauthPaths[url.pathname]\n if (methods) {\n if (!methods.includes(request.method)) {\n return new Response('Method not allowed', { status: 405 })\n }\n return routeOAuth(url, request, options.auth!.provider!, callbackPath)\n }\n }\n\n // MCP transport (stateless): only POST carries JSON-RPC. A standing-stream\n // GET or a session-teardown DELETE has no session to act on - and the SDK's\n // GET handler would open an SSE stream that never closes, hanging the request\n // on serverless runtimes (e.g. Cloudflare Workers). Answer them with 405, which\n // the Streamable HTTP spec explicitly permits for servers without a GET stream.\n if (request.method !== 'POST') {\n return new Response('Method Not Allowed', {\n status: 405,\n headers: { ...CORS_HEADERS, Allow: 'POST' }\n })\n }\n\n // wait for silkweave().start() to complete\n await _ready\n\n // JSON-RPC batching was removed from the MCP spec (2025-06-18). A batch also\n // defeats per-request filterActions (rpcInfo reflects only the first message\n // while the transport would execute every entry), so reject batches before\n // dispatch. Parsed once here and reused by the filter below.\n const rawBody: unknown = await request.clone().json().catch(() => undefined)\n if (Array.isArray(rawBody)) {\n return new Response(\n JSON.stringify({ jsonrpc: '2.0', error: { code: -32_600, message: 'JSON-RPC batch requests are not supported' }, id: null }),\n { status: 400, headers: { ...CORS_HEADERS, 'Content-Type': 'application/json' } }\n )\n }\n\n let requestContext = _context!\n if (options.auth) {\n const result = await validateToken(request.headers.get('authorization'), options.auth, _context!.fork({ request }))\n if (result.error) {\n return new Response(JSON.stringify(result.error.body), {\n status: result.error.statusCode,\n headers: result.error.headers\n })\n }\n if (result.auth) {\n requestContext = _context!.fork({ auth: result.auth })\n }\n }\n\n let activeActions = _actions\n if (options.filterActions) {\n try {\n activeActions = await options.filterActions(_actions, {\n headers: Object.fromEntries(request.headers.entries()),\n url: request.url,\n ...rpcInfo(rawBody)\n })\n } catch (error) {\n const mapped = filterErrorResponse(error, rawBody)\n return new Response(JSON.stringify(mapped.body), {\n status: mapped.status,\n headers: { ...CORS_HEADERS, 'Content-Type': 'application/json' }\n })\n }\n }\n\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: options.enableJsonResponse,\n ...dnsRebindingOptions(options)\n })\n\n const server = new McpServer({\n name: _options!.name,\n description: _options!.description,\n version: _options!.version\n }, {\n capabilities: { tools: {}, logging: {} }\n })\n\n registerTools(server, activeActions, requestContext, { onToolCall: options.onToolCall })\n\n await server.connect(transport)\n return transport.handleRequest(request)\n }\n\n // Top-level guard: any throw (malformed OAuth body, a boot-failure rejection\n // from `await _ready`, an unexpected error) becomes a JSON error Response\n // rather than an opaque host 500 / unhandled rejection on the serverless runtime.\n const handleRequest = async (request: Request): Promise<Response> => {\n try {\n return await handleRequestInner(request)\n } catch (error) {\n return edgeErrorResponse(error, CORS_HEADERS)\n }\n }\n\n const adapter: AdapterGenerator = (silkweaveOptions: SilkweaveOptions, baseContext: SilkweaveContext) => {\n _options = silkweaveOptions\n _context = baseContext.fork({ adapter: 'edge' })\n return {\n context: _context,\n start: async (actions) => {\n try {\n actions.forEach(validateActionDisposition)\n _actions = actions\n _readyResolve()\n } catch (error) {\n // Reject `_ready` so awaiting handlers get a 500 instead of hanging\n // forever, then rethrow so the builder's start() rejects too.\n _readyReject(error)\n throw error\n }\n },\n stop: async () => {\n _actions = []\n }\n }\n }\n\n return {\n adapter,\n handler: handleRequest,\n GET: handleRequest,\n POST: handleRequest,\n DELETE: handleRequest\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silkweave/edge",
3
- "version": "3.2.0",
3
+ "version": "3.2.1",
4
4
  "description": "Silkweave Web-Standard edge/serverless adapter (Cloudflare Workers, Vercel, Bun, Deno, Hono)",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.silkweave.dev",
@@ -28,9 +28,9 @@
28
28
  "dependencies": {
29
29
  "@modelcontextprotocol/sdk": "^1.29.0",
30
30
  "zod": "^3.25.0",
31
- "@silkweave/auth": "3.2.0",
32
- "@silkweave/core": "3.2.0",
33
- "@silkweave/mcp": "3.2.0"
31
+ "@silkweave/auth": "3.2.1",
32
+ "@silkweave/core": "3.2.1",
33
+ "@silkweave/mcp": "3.2.1"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@eslint/js": "^10.0.1",