@webpieces/mcp-server 0.4.879 → 0.4.881

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
@@ -238,14 +238,16 @@ MCP spec fixes each. The shared SDK catch selects protocol versus tool rendering
238
238
 
239
239
  | Boundary | Method | Reply |
240
240
  |---|---|---|
241
- | before the SDK: bearer, `Origin`, malformed body | `toBearerBoundaryResponse(error): HttpResponseDto<McpHttpErrorBody>` | HTTP 401 + `WWW-Authenticate` / 403 / 400, anything else 500 |
241
+ | before the SDK: bearer, `Origin`, malformed body | `toBearerBoundaryResponse(error): HttpResponseDto<McpHttpErrorBody> \| HttpResponseDto<OAuthErrorBody>` | HTTP 401 + `WWW-Authenticate` / 403 / 400, anything else 500; an `OAuthProtocolError` gets the RFC 6749/6750 body at its own status |
242
242
  | `tools/list` | `toListError(error): never` | throws JSON-RPC `-32602` (bad request) or `-32603` `Internal Error` |
243
243
  | `tools/call`, tool found: bad arguments, endpoint policy denial, JWT/OIDC mint failure, any local or remote failure, output-schema or serialization failure | `toToolCallResult(error): CallToolResult` (delegates to `McpRegistry.getErrorTranslator().toWire`) | `isError: true` result |
244
244
  | `tools/call`, unknown or hidden tool name (including a dispatcher policy recheck denial) | `unknownTool(name)` | JSON-RPC `-32602` `Unknown tool: <name>` |
245
245
 
246
246
  The pre-SDK boundary produces a VALUE like every other API in the framework and `WpMcpServer` writes
247
247
  it through the same `ExpressResponseWriter` the ordinary HTTP path uses — it does not hand-roll
248
- `res.status(...).json(...)`. The body stays JSON-RPC shaped because an MCP client expects that;
248
+ `res.status(...).json(...)`. The body stays JSON-RPC shaped because an MCP client expects that — except for an `OAuthProtocolError`
249
+ (an authorization service refusing the bearer with `invalid_token` / `insufficient_scope`), which is
250
+ answered in the RFC shape by `OAuthErrorResponse`, keeping this server's challenge on a 401;
249
251
  `HttpResponseDto` is generic and its header LIST carries `WWW-Authenticate`.
250
252
 
251
253
  `toListError` is typed `never` — it THROWS rather than returning a value the caller must remember to
@@ -352,6 +354,35 @@ rotation metadata. Opaque tokens remain valid implementations of the same author
352
354
  `protectedResourceMetadata()` returns the resource metadata an HTTP adapter can publish at the
353
355
  well-known OAuth protected-resource endpoint. OAuth token issuance remains pluggable.
354
356
 
357
+ ### Refusing in the RFC shape from your own OAuth routes
358
+
359
+ Token issuance, client registration and the authorization endpoint are the app's own express routes.
360
+ Refuse there with `OAuthProtocolError` (from `@webpieces/core-util`) and mount
361
+ `OAuthExpressErrorHandler` (from `@webpieces/http-server`) as the error handler, so the OAuth client
362
+ gets `{ "error": "invalid_grant", "error_description": "..." }` at the RFC status with the right
363
+ `WWW-Authenticate`, and the refusal is logged at INFO rather than as a server ERROR:
364
+
365
+ ```typescript
366
+ const oauthErrors = new OAuthExpressErrorHandler();
367
+ app.post('/oauth/token', urlencoded({ extended: false }), async (req, res) => {
368
+ if (req.body.grant_type !== 'authorization_code') {
369
+ throw new OAuthProtocolError(OAuthErrorCode.UNSUPPORTED_GRANT_TYPE, 'only authorization_code');
370
+ }
371
+ // ...
372
+ }, oauthErrors.middleware);
373
+
374
+ // authorization endpoint: errors go back as a redirect, never a status (RFC 6749 §4.1.2.1)
375
+ res.redirect(302, new OAuthAuthorizationErrorRedirect(
376
+ validatedRedirectUri, OAuthErrorCode.ACCESS_DENIED,
377
+ typeof req.query.state === 'string' ? req.query.state : undefined, 'The user declined').toUrl());
378
+ ```
379
+
380
+ An authorization service that throws an `OAuthProtocolError` (`invalid_token`, `insufficient_scope`)
381
+ while this server verifies a bearer is rendered the same way at the MCP boundary, and a 401 without
382
+ its own challenge keeps this server's `resource_metadata` challenge. The status is derived from the
383
+ code; only `invalid_client` may choose 400 or 401, and the constructor's overloads make any other
384
+ status a compile error. See `docs/portable-ipc-and-errors.md` for the full vocabulary.
385
+
355
386
  That endpoint's URL is `WpMcpServerConfig.resourceMetadataUrl` — RFC 9728 §3.1 path insertion, so
356
387
  `https://host/mcp` publishes at `https://host/.well-known/oauth-protected-resource/mcp` — and it is the
357
388
  value of `resource_metadata` in the 401 `WWW-Authenticate` challenge. RFC 9728 §5.1 defines that
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/mcp-server",
3
- "version": "0.4.879",
3
+ "version": "0.4.881",
4
4
  "description": "Secure MCP tool generation from annotated Webpieces API contracts",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -24,15 +24,15 @@
24
24
  "dependencies": {
25
25
  "@modelcontextprotocol/node": "2.0.0",
26
26
  "@modelcontextprotocol/server": "2.0.0",
27
- "@webpieces/core-context": "0.4.879",
28
- "@webpieces/core-util": "0.4.879",
29
- "@webpieces/http-routing": "0.4.879",
30
- "@webpieces/http-server": "0.4.879",
27
+ "@webpieces/core-context": "0.4.881",
28
+ "@webpieces/core-util": "0.4.881",
29
+ "@webpieces/http-routing": "0.4.881",
30
+ "@webpieces/http-server": "0.4.881",
31
31
  "express": "5.1.0",
32
32
  "reflect-metadata": "0.2.2",
33
33
  "tslib": "2.8.1"
34
34
  },
35
35
  "devDependencies": {
36
- "@webpieces/http-client-node": "0.4.879"
36
+ "@webpieces/http-client-node": "0.4.881"
37
37
  }
38
38
  }
@@ -1,5 +1,5 @@
1
1
  import { CallToolResult, ProtocolError } from '@modelcontextprotocol/server';
2
- import { HttpResponseDto } from '@webpieces/core-util';
2
+ import { HttpResponseDto, OAuthErrorBody } from '@webpieces/core-util';
3
3
  import { McpHttpErrorBody } from './McpToolCallRendering';
4
4
  /**
5
5
  * The ONE place every MCP failure becomes a reply, mirroring `WebpiecesDefaultErrorTranslator` for HTTP: an error
@@ -9,6 +9,7 @@ import { McpHttpErrorBody } from './McpToolCallRendering';
9
9
  * | Boundary | Who writes HTTP | Failure shape |
10
10
  * |--------------------------------|-----------------|-------------------------------------|
11
11
  * | pre-SDK bearer / Origin / body | **webpieces** | `HttpResponseDto<McpHttpErrorBody>` |
12
+ * | ... an `OAuthProtocolError` | **webpieces** | `HttpResponseDto<OAuthErrorBody>` |
12
13
  * | `tools/list` | the SDK | `throw ProtocolError` |
13
14
  * | `tools/call` | the SDK | `CallToolResult` with `isError` |
14
15
  *
@@ -33,9 +34,9 @@ export declare class WpMcpErrorTranslator {
33
34
  * verification, `Origin`, a malformed body. This is 401/403/400/500 today and any status
34
35
  * tomorrow, so it produces a VALUE like every other API in the framework and `WpMcpServer` hands
35
36
  * it to the shared express writer. The BODY stays JSON-RPC shaped because an MCP client expects
36
- * that; `HttpResponseDto` is generic, and its header LIST carries `WWW-Authenticate`.
37
+ * that — except for an {@link OAuthProtocolError}, which is answered in the RFC 6749/6750 shape; `HttpResponseDto` is generic, and its header LIST carries `WWW-Authenticate`.
37
38
  */
38
- toBearerBoundaryResponse(error: Error): HttpResponseDto<McpHttpErrorBody>;
39
+ toBearerBoundaryResponse(error: Error): HttpResponseDto<McpHttpErrorBody> | HttpResponseDto<OAuthErrorBody>;
39
40
  /**
40
41
  * Boundary #2: `tools/list` has no tool-result channel, so a failure IS a JSON-RPC error. It
41
42
  * THROWS rather than returning a value the caller must remember to throw, and is typed `never`
@@ -14,6 +14,7 @@ const log = core_util_1.LogManager.getLogger('WpMcpErrorTranslator');
14
14
  * | Boundary | Who writes HTTP | Failure shape |
15
15
  * |--------------------------------|-----------------|-------------------------------------|
16
16
  * | pre-SDK bearer / Origin / body | **webpieces** | `HttpResponseDto<McpHttpErrorBody>` |
17
+ * | ... an `OAuthProtocolError` | **webpieces** | `HttpResponseDto<OAuthErrorBody>` |
17
18
  * | `tools/list` | the SDK | `throw ProtocolError` |
18
19
  * | `tools/call` | the SDK | `CallToolResult` with `isError` |
19
20
  *
@@ -40,9 +41,15 @@ class WpMcpErrorTranslator {
40
41
  * verification, `Origin`, a malformed body. This is 401/403/400/500 today and any status
41
42
  * tomorrow, so it produces a VALUE like every other API in the framework and `WpMcpServer` hands
42
43
  * it to the shared express writer. The BODY stays JSON-RPC shaped because an MCP client expects
43
- * that; `HttpResponseDto` is generic, and its header LIST carries `WWW-Authenticate`.
44
+ * that — except for an {@link OAuthProtocolError}, which is answered in the RFC 6749/6750 shape; `HttpResponseDto` is generic, and its header LIST carries `WWW-Authenticate`.
44
45
  */
45
46
  toBearerBoundaryResponse(error) {
47
+ // An authorization service that refuses the bearer with an RFC 6750 code (invalid_token,
48
+ // insufficient_scope) is answered in the RFC shape, and a 401 without its own challenge
49
+ // gets this server's, so the client still finds `resource_metadata` (#1176).
50
+ if (error instanceof core_util_1.OAuthProtocolError) {
51
+ return core_util_1.OAuthErrorResponse.toResponse(error, this.challenge());
52
+ }
46
53
  const payload = this.boundary.encode(error);
47
54
  const generic = payload.message;
48
55
  switch (payload.kind) {
@@ -1 +1 @@
1
- {"version":3,"file":"WpMcpErrorTranslator.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/WpMcpErrorTranslator.ts"],"names":[],"mappings":"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","sourcesContent":["import { CallToolResult, ProtocolError, ProtocolErrorCode } from '@modelcontextprotocol/server';\nimport {\n ApiErrorBoundary,\n HttpHeader,\n HttpResponseDto,\n HttpResponseStatus,\n LogManager,\n toError,\n} from '@webpieces/core-util';\nimport { McpRegistry } from './McpRegistry';\nimport {\n McpCorrelation,\n McpDefaultToolCallRenderer,\n McpErrorData,\n McpErrorTranslator,\n McpHttpErrorBody,\n McpHttpErrorDetail,\n MCP_REQUEST_ID_META_KEY,\n} from './McpToolCallRendering';\n\nconst log = LogManager.getLogger('WpMcpErrorTranslator');\n\n/**\n * The ONE place every MCP failure becomes a reply, mirroring `WebpiecesDefaultErrorTranslator` for HTTP: an error\n * in, the exact wire shape out. There is one method per BOUNDARY, and the MCP spec is what makes\n * their renderings distinct at the two interception seams (HTTP ingress and shared SDK callbacks):\n *\n * | Boundary | Who writes HTTP | Failure shape |\n * |--------------------------------|-----------------|-------------------------------------|\n * | pre-SDK bearer / Origin / body | **webpieces** | `HttpResponseDto<McpHttpErrorBody>` |\n * | `tools/list` | the SDK | `throw ProtocolError` |\n * | `tools/call` | the SDK | `CallToolResult` with `isError` |\n *\n * Classification and published text are the shared {@link ApiErrorBoundary} rules. Operator logging\n * is NOT here: `LogApiCall` wraps each of those boundaries in `WpMcpServer`, so every failure gets\n * exactly one `[API-server-resp-*]` line with the request and the identity on it — and\n * `WpMcpServer` adds one `log.error` with the stack and requestId for an IMPLEMENTATION-kind failure\n * at tools/list and tools/call before handing it here, because this class renders that kind as\n * \"Internal Error\" and nothing else records WHERE it broke (#1055).\n */\nexport class WpMcpErrorTranslator {\n private readonly boundary = new ApiErrorBoundary();\n private readonly defaultToolCall = new McpDefaultToolCallRenderer();\n\n /**\n * @param challenge - the `WWW-Authenticate` value for a 401, read lazily because\n * `WpMcpServerConfig` is only fully validated at `bind(...)` time.\n */\n constructor(private readonly challenge: () => string) {}\n\n /**\n * Boundary #1: everything webpieces itself rejects before the SDK is involved — bearer\n * verification, `Origin`, a malformed body. This is 401/403/400/500 today and any status\n * tomorrow, so it produces a VALUE like every other API in the framework and `WpMcpServer` hands\n * it to the shared express writer. The BODY stays JSON-RPC shaped because an MCP client expects\n * that; `HttpResponseDto` is generic, and its header LIST carries `WWW-Authenticate`.\n */\n toBearerBoundaryResponse(error: Error): HttpResponseDto<McpHttpErrorBody> {\n const payload = this.boundary.encode(error);\n const generic = payload.message;\n switch (payload.kind) {\n case 'unauthorized':\n return this.httpError(401, -32_000, generic, [\n new HttpHeader('WWW-Authenticate', this.challenge()),\n ]);\n case 'forbidden':\n return this.httpError(403, -32_000, generic, []);\n case 'bad-request':\n return this.httpError(400, ProtocolErrorCode.ParseError, generic, []);\n default:\n return this.httpError(500, ProtocolErrorCode.InternalError, 'Internal Error', []);\n }\n }\n\n /**\n * Boundary #2: `tools/list` has no tool-result channel, so a failure IS a JSON-RPC error. It\n * THROWS rather than returning a value the caller must remember to throw, and is typed `never`\n * so the compiler knows the call is terminal.\n *\n * This is LOAD-BEARING for disclosure, not defence in depth. Measured on\n * `@modelcontextprotocol/server` 2.0.0 and pinned by `McpSdkForeignThrow.spec.ts`: the SDK wraps\n * a foreign (non-`ProtocolError`) throw in `-32603` but copies its `message` onto the wire\n * VERBATIM. So a `tools/list` handler that lets any error escape un-translated publishes that\n * error's operator text to the caller.\n */\n toListError(error: Error): never {\n const payload = this.boundary.encode(error);\n const requestId = McpCorrelation.requestId();\n if (payload.kind === 'bad-request') {\n throw new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n payload.callerMessage ?? payload.message,\n new McpErrorData(requestId),\n );\n }\n const message = payload.kind === 'end-user' ? payload.message : 'Internal Error';\n throw new ProtocolError(\n ProtocolErrorCode.InternalError,\n message,\n new McpErrorData(requestId),\n );\n }\n\n /** tools/call with a name no binding registered: a JSON-RPC protocol error in either MCP era. */\n unknownTool(toolName: string): ProtocolError {\n const requestId = McpCorrelation.requestId();\n log.info(`Unknown MCP tool: ${toolName} requestId=${requestId}`);\n return new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n `Unknown tool: ${toolName}`,\n new McpErrorData(requestId),\n );\n }\n\n /**\n * Boundary #3: every tools/call failure after the tool is found — an `isError: true` result the\n * model sees.\n *\n * ONE unconditional line: {@link McpRegistry.getErrorTranslator} is never undefined, so there is\n * no \"was one registered\" branch. A registered {@link McpErrorTranslator} REPLACES the default and\n * declines by delegating to {@link McpDefaultToolCallRenderer}, so \"no translator registered\" and\n * \"a translator that does not claim this error\" produce byte-identical results.\n */\n toToolCallResult(failure: Error): CallToolResult {\n return this.withDefaultMeta(this.appOrDefault(McpRegistry.getErrorTranslator(), failure));\n }\n\n /** The `_meta` every tools/call result carries so a user can quote its requestId. */\n resultMeta(requestId: string): Record<string, string> {\n return { [MCP_REQUEST_ID_META_KEY]: requestId };\n }\n\n /**\n * An app translator that THROWS must not replace the failure being reported: its own bug is\n * logged here and the ORIGINAL error still renders through the webpieces default, so the model\n * still gets a reply carrying the requestId.\n *\n * This is BUG CONTAINMENT, not a \"did anyone register one\" branch — the same shape as\n * `ClientErrorTranslator.throwIfFailure` on the HTTP side.\n */\n private appOrDefault(renderer: McpErrorTranslator, failure: Error): CallToolResult {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- an app translator's own bug must not replace the failure it was asked to render\n try {\n return renderer.toWire(failure);\n } catch (err: unknown) {\n const error = toError(err);\n log.error(\n 'Application McpErrorTranslator.toWire threw; rendering the webpieces default.',\n error,\n );\n return this.defaultToolCall.toWire(failure);\n }\n }\n\n /**\n * webpieces fills `_meta` only when the app left it off, keeping the README's \"every reply\n * carries the requestId\" promise a DEFAULT rather than a restriction: an app that set `_meta`\n * keeps it byte-for-byte.\n */\n private withDefaultMeta(result: CallToolResult): CallToolResult {\n if (result._meta !== undefined) return result;\n return { ...result, _meta: this.resultMeta(McpCorrelation.requestId()) };\n }\n\n private httpError(\n status: number,\n code: number,\n message: string,\n headers: HttpHeader[],\n ): HttpResponseDto<McpHttpErrorBody> {\n const correlation = McpCorrelation.current();\n const requestId = McpCorrelation.requestId();\n return new HttpResponseDto<McpHttpErrorBody>(\n new HttpResponseStatus(status, message),\n headers,\n new McpHttpErrorBody(\n new McpHttpErrorDetail(code, message, new McpErrorData(requestId)),\n correlation.jsonRpcId,\n ),\n );\n }\n}\n"]}
1
+ {"version":3,"file":"WpMcpErrorTranslator.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/WpMcpErrorTranslator.ts"],"names":[],"mappings":"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","sourcesContent":["import { CallToolResult, ProtocolError, ProtocolErrorCode } from '@modelcontextprotocol/server';\nimport {\n ApiErrorBoundary,\n HttpHeader,\n HttpResponseDto,\n HttpResponseStatus,\n LogManager,\n OAuthErrorBody,\n OAuthErrorResponse,\n OAuthProtocolError,\n toError,\n} from '@webpieces/core-util';\nimport { McpRegistry } from './McpRegistry';\nimport {\n McpCorrelation,\n McpDefaultToolCallRenderer,\n McpErrorData,\n McpErrorTranslator,\n McpHttpErrorBody,\n McpHttpErrorDetail,\n MCP_REQUEST_ID_META_KEY,\n} from './McpToolCallRendering';\n\nconst log = LogManager.getLogger('WpMcpErrorTranslator');\n\n/**\n * The ONE place every MCP failure becomes a reply, mirroring `WebpiecesDefaultErrorTranslator` for HTTP: an error\n * in, the exact wire shape out. There is one method per BOUNDARY, and the MCP spec is what makes\n * their renderings distinct at the two interception seams (HTTP ingress and shared SDK callbacks):\n *\n * | Boundary | Who writes HTTP | Failure shape |\n * |--------------------------------|-----------------|-------------------------------------|\n * | pre-SDK bearer / Origin / body | **webpieces** | `HttpResponseDto<McpHttpErrorBody>` |\n * | ... an `OAuthProtocolError` | **webpieces** | `HttpResponseDto<OAuthErrorBody>` |\n * | `tools/list` | the SDK | `throw ProtocolError` |\n * | `tools/call` | the SDK | `CallToolResult` with `isError` |\n *\n * Classification and published text are the shared {@link ApiErrorBoundary} rules. Operator logging\n * is NOT here: `LogApiCall` wraps each of those boundaries in `WpMcpServer`, so every failure gets\n * exactly one `[API-server-resp-*]` line with the request and the identity on it — and\n * `WpMcpServer` adds one `log.error` with the stack and requestId for an IMPLEMENTATION-kind failure\n * at tools/list and tools/call before handing it here, because this class renders that kind as\n * \"Internal Error\" and nothing else records WHERE it broke (#1055).\n */\nexport class WpMcpErrorTranslator {\n private readonly boundary = new ApiErrorBoundary();\n private readonly defaultToolCall = new McpDefaultToolCallRenderer();\n\n /**\n * @param challenge - the `WWW-Authenticate` value for a 401, read lazily because\n * `WpMcpServerConfig` is only fully validated at `bind(...)` time.\n */\n constructor(private readonly challenge: () => string) {}\n\n /**\n * Boundary #1: everything webpieces itself rejects before the SDK is involved — bearer\n * verification, `Origin`, a malformed body. This is 401/403/400/500 today and any status\n * tomorrow, so it produces a VALUE like every other API in the framework and `WpMcpServer` hands\n * it to the shared express writer. The BODY stays JSON-RPC shaped because an MCP client expects\n * that — except for an {@link OAuthProtocolError}, which is answered in the RFC 6749/6750 shape; `HttpResponseDto` is generic, and its header LIST carries `WWW-Authenticate`.\n */\n toBearerBoundaryResponse(\n error: Error,\n ): HttpResponseDto<McpHttpErrorBody> | HttpResponseDto<OAuthErrorBody> {\n // An authorization service that refuses the bearer with an RFC 6750 code (invalid_token,\n // insufficient_scope) is answered in the RFC shape, and a 401 without its own challenge\n // gets this server's, so the client still finds `resource_metadata` (#1176).\n if (error instanceof OAuthProtocolError) {\n return OAuthErrorResponse.toResponse(error, this.challenge());\n }\n const payload = this.boundary.encode(error);\n const generic = payload.message;\n switch (payload.kind) {\n case 'unauthorized':\n return this.httpError(401, -32_000, generic, [\n new HttpHeader('WWW-Authenticate', this.challenge()),\n ]);\n case 'forbidden':\n return this.httpError(403, -32_000, generic, []);\n case 'bad-request':\n return this.httpError(400, ProtocolErrorCode.ParseError, generic, []);\n default:\n return this.httpError(500, ProtocolErrorCode.InternalError, 'Internal Error', []);\n }\n }\n\n /**\n * Boundary #2: `tools/list` has no tool-result channel, so a failure IS a JSON-RPC error. It\n * THROWS rather than returning a value the caller must remember to throw, and is typed `never`\n * so the compiler knows the call is terminal.\n *\n * This is LOAD-BEARING for disclosure, not defence in depth. Measured on\n * `@modelcontextprotocol/server` 2.0.0 and pinned by `McpSdkForeignThrow.spec.ts`: the SDK wraps\n * a foreign (non-`ProtocolError`) throw in `-32603` but copies its `message` onto the wire\n * VERBATIM. So a `tools/list` handler that lets any error escape un-translated publishes that\n * error's operator text to the caller.\n */\n toListError(error: Error): never {\n const payload = this.boundary.encode(error);\n const requestId = McpCorrelation.requestId();\n if (payload.kind === 'bad-request') {\n throw new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n payload.callerMessage ?? payload.message,\n new McpErrorData(requestId),\n );\n }\n const message = payload.kind === 'end-user' ? payload.message : 'Internal Error';\n throw new ProtocolError(\n ProtocolErrorCode.InternalError,\n message,\n new McpErrorData(requestId),\n );\n }\n\n /** tools/call with a name no binding registered: a JSON-RPC protocol error in either MCP era. */\n unknownTool(toolName: string): ProtocolError {\n const requestId = McpCorrelation.requestId();\n log.info(`Unknown MCP tool: ${toolName} requestId=${requestId}`);\n return new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n `Unknown tool: ${toolName}`,\n new McpErrorData(requestId),\n );\n }\n\n /**\n * Boundary #3: every tools/call failure after the tool is found — an `isError: true` result the\n * model sees.\n *\n * ONE unconditional line: {@link McpRegistry.getErrorTranslator} is never undefined, so there is\n * no \"was one registered\" branch. A registered {@link McpErrorTranslator} REPLACES the default and\n * declines by delegating to {@link McpDefaultToolCallRenderer}, so \"no translator registered\" and\n * \"a translator that does not claim this error\" produce byte-identical results.\n */\n toToolCallResult(failure: Error): CallToolResult {\n return this.withDefaultMeta(this.appOrDefault(McpRegistry.getErrorTranslator(), failure));\n }\n\n /** The `_meta` every tools/call result carries so a user can quote its requestId. */\n resultMeta(requestId: string): Record<string, string> {\n return { [MCP_REQUEST_ID_META_KEY]: requestId };\n }\n\n /**\n * An app translator that THROWS must not replace the failure being reported: its own bug is\n * logged here and the ORIGINAL error still renders through the webpieces default, so the model\n * still gets a reply carrying the requestId.\n *\n * This is BUG CONTAINMENT, not a \"did anyone register one\" branch — the same shape as\n * `ClientErrorTranslator.throwIfFailure` on the HTTP side.\n */\n private appOrDefault(renderer: McpErrorTranslator, failure: Error): CallToolResult {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- an app translator's own bug must not replace the failure it was asked to render\n try {\n return renderer.toWire(failure);\n } catch (err: unknown) {\n const error = toError(err);\n log.error(\n 'Application McpErrorTranslator.toWire threw; rendering the webpieces default.',\n error,\n );\n return this.defaultToolCall.toWire(failure);\n }\n }\n\n /**\n * webpieces fills `_meta` only when the app left it off, keeping the README's \"every reply\n * carries the requestId\" promise a DEFAULT rather than a restriction: an app that set `_meta`\n * keeps it byte-for-byte.\n */\n private withDefaultMeta(result: CallToolResult): CallToolResult {\n if (result._meta !== undefined) return result;\n return { ...result, _meta: this.resultMeta(McpCorrelation.requestId()) };\n }\n\n private httpError(\n status: number,\n code: number,\n message: string,\n headers: HttpHeader[],\n ): HttpResponseDto<McpHttpErrorBody> {\n const correlation = McpCorrelation.current();\n const requestId = McpCorrelation.requestId();\n return new HttpResponseDto<McpHttpErrorBody>(\n new HttpResponseStatus(status, message),\n headers,\n new McpHttpErrorBody(\n new McpHttpErrorDetail(code, message, new McpErrorData(requestId)),\n correlation.jsonRpcId,\n ),\n );\n }\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { AuthenticatedMachineIdentity } from '@webpieces/http-routing';
2
+ import { ClientRole } from '@webpieces/core-util';
2
3
  import 'reflect-metadata';
3
4
  import { ContextKey, ErrorTranslator, Filter, HttpResponseDto, LogLevel, Service } from '@webpieces/core-util';
4
5
  import { MethodMeta, OidcHook, WpResponse } from '@webpieces/http-routing';
@@ -113,5 +114,5 @@ export declare class RelayWebpiecesPeerErrors implements ErrorTranslator {
113
114
  relay: boolean;
114
115
  private readonly fallback;
115
116
  toWire(error: Error): HttpResponseDto;
116
- fromWire(response: HttpResponseDto): void;
117
+ fromWire(response: HttpResponseDto, role: ClientRole): void;
117
118
  }
@@ -404,7 +404,7 @@ class RelayWebpiecesPeerErrors {
404
404
  toWire(error) {
405
405
  return this.fallback.toWire(error);
406
406
  }
407
- fromWire(response) {
407
+ fromWire(response, role) {
408
408
  if (this.relay && core_util_2.ApiErrorCodec.isPayload(response.body)) {
409
409
  const decoded = core_util_2.ApiErrorCodec.decode(response.body);
410
410
  const statusCode = decoded instanceof core_util_2.ApiCodedError ? decoded.statusCode : undefined;
@@ -413,7 +413,7 @@ class RelayWebpiecesPeerErrors {
413
413
  throw decoded;
414
414
  }
415
415
  }
416
- this.fallback.fromWire(response);
416
+ this.fallback.fromWire(response, role);
417
417
  }
418
418
  }
419
419
  exports.RelayWebpiecesPeerErrors = RelayWebpiecesPeerErrors;
@@ -1 +1 @@
1
- {"version":3,"file":"McpRemoteFixtures.js","sourceRoot":"","sources":["../../../../../../packages/http/mcp-server/src/__tests__/McpRemoteFixtures.ts"],"names":[],"mappings":"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","sourcesContent":["import { AuthenticatedMachineIdentity } from '@webpieces/http-routing';\nimport { WpAuthorization, AuthorizationType, WpAuth, oidc as oidcAuth, jwt as jwtAuth } from '@webpieces/core-util';\nimport 'reflect-metadata';\nimport { injectable } from 'inversify';\nimport { ApiBadRequestError, ApiCodedError, ApiConflictError, ApiConnectionError, ApiDependencyBackoffError, ApiDependencyError, ApiDependencyTimeoutError, ApiEndpointNotFoundError, ApiEndUserError, ApiErrorCodec, ApiErrorHttpStatus, ApiForbiddenError, ApiImplementationError, ApiNotFoundError, ApiNotImplementedError, ApiPath, ApiType, ApiPreconditionFailedError, ApiRateLimitedError, ApiRequestTimeoutError, ApiUnauthorizedError, ApiUnavailableError, ApiUnprocessableError, ApiUnsupportedMediaTypeError, ContextKey, Endpoint, ErrorTranslator, Filter, WebpiecesDefaultErrorTranslator, HttpResponseDto, LogLevel, Service, WebpiecesCoreHeaders, ApiJsonSchema, McpToolCatalogFile, McpToolDefinition, ObjectSchemaBuilder, WpMcpTool, WpMcpToolHints, MCP, POST, READ, RPC, SVC_TO_SVC } from '@webpieces/core-util';\nimport { RequestContext } from '@webpieces/core-context';\nimport { MethodMeta, OidcHook, WpResponse } from '@webpieces/http-routing';\nimport { McpToolCatalog } from '../McpToolCatalog';\n\n/** Contracts, controllers and test doubles for McpRemoteBinding.integration.spec.ts. */\n\nexport const REMOTE_USER = ContextKey.trusted<string>(\n 'mcpRemoteUser',\n 'verified by the MCP access-token authority before delegation',\n 'x-mcp-remote-user',\n);\nexport const REMOTE_ROLES = ContextKey.trusted<string>(\n 'mcpRemoteRoles',\n 'verified by the MCP access-token authority before delegation',\n 'x-mcp-remote-roles',\n);\nexport const REMOTE_SERVICE = 'mcp-remote-integration';\nexport const REFUSED_SERVICE = 'mcp-remote-refused';\nexport const GARBAGE_SERVICE = 'mcp-remote-garbage';\nexport const OIDC_TOKEN = 'oidc-for-mcp-remote-integration';\nexport const EXTERNAL_MCP_BEARER = 'external-mcp-access-token';\nexport const LOCAL_ENDPOINT_JWT = 'local-endpoint-jwt-that-must-not-cross-the-remote-hop';\nexport const GATEWAY_PATH = '/gateway/mcp';\n\nexport class RemoteRequest {\n /** Search text */\n query!: string;\n\n constructor(query?: string) {\n if (query !== undefined) this.query = query;\n }\n}\n\nexport class RemoteResponse {\n /** Delegated user */\n user!: string;\n\n /** Delegated roles */\n roles!: string;\n\n /** Result */\n result!: string;\n\n constructor(user?: string, roles?: string, result?: string) {\n if (user !== undefined) this.user = user;\n if (roles !== undefined) this.roles = roles;\n if (result !== undefined) this.result = result;\n }\n}\n\n/**\n * The generated tool catalogs these contracts would produce — what `wp-openapi` writes, one\n * `mcp-<ContractClass>-tools.json` per contract. Hand-built here because `@webpieces/mcp-server` does not depend on the\n * TypeScript compiler API; the runtime's job is to BE HANDED a catalog.\n */\nfunction describedString(description: string): ApiJsonSchema {\n const schema = new ApiJsonSchema('string');\n schema.description = description;\n return schema;\n}\n\nfunction searchTool(name: string, methodName: string, description: string): McpToolDefinition {\n return new McpToolDefinition(\n name,\n methodName,\n description,\n new WpMcpToolHints(true, false, true, false),\n new ObjectSchemaBuilder().required('query', describedString('Search text')).build(),\n new ObjectSchemaBuilder()\n .required('user', describedString('Delegated user'))\n .required('roles', describedString('Delegated roles'))\n .required('result', describedString('Result'))\n .build(),\n );\n}\n\n/** One contract's hand-built catalog, labelled as built in memory. */\nfunction contractCatalog(contractName: string, tool: McpToolDefinition): McpToolCatalog {\n return new McpToolCatalog(new McpToolCatalogFile(contractName, [tool]), '(built in memory by the spec)');\n}\n\nexport const REMOTE_MCP_CATALOG = contractCatalog(\n 'RemoteMcpApi',\n searchTool('remote_integration_search', 'search', 'Calls a remote Webpieces API.'),\n);\n\n/** The catalogs of every contract the gateway binds — one per contract, exactly as a build writes them. */\nexport const GATEWAY_CATALOGS: readonly McpToolCatalog[] = [\n contractCatalog(\n 'MissingRemoteMcpApi',\n searchTool('missing_remote_integration_search', 'search', 'Intentionally absent route.'),\n ),\n contractCatalog('RefusedRemoteApi', searchTool('refused_remote', 'search', 'Nothing listens on this port.')),\n contractCatalog('GarbageRemoteApi', searchTool('garbage_remote', 'search', 'Answers an undecodable body.')),\n contractCatalog(\n 'OidcFailRemoteApi',\n searchTool('oidc_fail_remote', 'search', 'Its OIDC token cannot be minted.'),\n ),\n contractCatalog('LocalThrowApi', searchTool('local_throw', 'fail', 'Throws the requested failure in-process.')),\n contractCatalog('RemoteThrowApi', searchTool('remote_throw', 'fail', 'Throws the requested failure remotely.')),\n];\n\n@ApiPath('/remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class RemoteMcpApi {\n /** Calls a remote Webpieces API. */\n\n @WpAuth([oidcAuth('mcp-gateway')])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('remote_integration_search')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/missing-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class MissingRemoteMcpApi {\n /** Intentionally absent route. */\n\n @WpAuth([oidcAuth('mcp-gateway')])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('missing_remote_integration_search')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/refused-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class RefusedRemoteApi {\n /** Nothing listens on this port. */\n\n @WpAuth([oidcAuth('mcp-gateway')])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('refused_remote')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/garbage-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class GarbageRemoteApi {\n /** Answers an undecodable body. */\n\n @WpAuth([oidcAuth('mcp-gateway')])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('garbage_remote')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/oidc-fail-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class OidcFailRemoteApi {\n /** Its OIDC token cannot be minted. */\n\n @WpAuth([oidcAuth('mcp-gateway')])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('oidc_fail_remote')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/local-throw')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class LocalThrowApi {\n /** Throws the requested failure in-process. */\n\n @WpAuth([jwtAuth()])\n @WpAuthorization({ authType: AuthorizationType.ALL_USERS })\n @Endpoint(POST, '/throw', READ, RPC)\n @WpMcpTool('local_throw')\n fail(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/remote-throw')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class RemoteThrowApi {\n /** Throws the requested failure remotely. */\n\n @WpAuth([oidcAuth('mcp-gateway')])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/throw', READ, RPC)\n @WpMcpTool('remote_throw')\n fail(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n/**\n * One named throw, run identically behind the local and the remote binding.\n *\n * `expectedServerLevel` is the level of the ONE operator line the failure now produces on the server\n * that raised it — `LogApiCall`'s, at `warn` for `[API-server-resp-OTHER]` (a healthy rejection of\n * the caller's mistake) and `error` for `[API-server-resp-FAIL]`. `ApiErrorBoundary` no longer\n * writes a second line of its own (#961).\n */\nexport class ThrowCase {\n constructor(\n public readonly name: string,\n public readonly expectedKind: string,\n public readonly expectedServerLevel: LogLevel,\n public readonly raise: () => never,\n ) {}\n}\n\nexport const THROW_CASES: readonly ThrowCase[] = [\n new ThrowCase('end-user', 'end-user', 'warn', () => {\n throw new ApiEndUserError('The two passwords you entered do not match.', 'PW_MISMATCH');\n }),\n new ThrowCase('bad-request', 'bad-request', 'warn', () => {\n throw new ApiBadRequestError('internal column name', '$.query', 'query must be a word');\n }),\n new ThrowCase('unauthorized', 'unauthorized', 'warn', () => {\n throw new ApiUnauthorizedError('session internals');\n }),\n new ThrowCase('forbidden', 'forbidden', 'warn', () => {\n throw new ApiForbiddenError('acl internals');\n }),\n new ThrowCase('not-found', 'not-found', 'warn', () => {\n throw new ApiNotFoundError('row 7 missing');\n }),\n new ThrowCase('endpoint-not-found', 'endpoint-not-found', 'warn', () => {\n throw new ApiEndpointNotFoundError('route internals');\n }),\n new ThrowCase('request-timeout', 'request-timeout', 'error', () => {\n throw new ApiRequestTimeoutError('slow internals');\n }),\n new ThrowCase('rate-limited', 'rate-limited', 'error', () => {\n throw new ApiRateLimitedError('quota internals');\n }),\n new ThrowCase('conflict', 'conflict', 'warn', () => {\n throw new ApiConflictError('version internals');\n }),\n new ThrowCase('unprocessable', 'unprocessable', 'warn', () => {\n throw new ApiUnprocessableError('rule internals');\n }),\n new ThrowCase('precondition-failed', 'precondition-failed', 'warn', () => {\n throw new ApiPreconditionFailedError('etag internals');\n }),\n new ThrowCase('unsupported-media-type', 'unsupported-media-type', 'warn', () => {\n throw new ApiUnsupportedMediaTypeError('parser internals');\n }),\n new ThrowCase('not-implemented', 'not-implemented', 'error', () => {\n throw new ApiNotImplementedError('feature internals');\n }),\n new ThrowCase('coded-4xx', 'coded', 'warn', () => {\n throw new ApiCodedError('quota internals', 460, 'QUOTA');\n }),\n new ThrowCase('coded-5xx', 'coded', 'error', () => {\n throw new ApiCodedError('disk internals', 507);\n }),\n new ThrowCase('implementation', 'implementation', 'error', () => {\n throw new ApiImplementationError('bug internals');\n }),\n new ThrowCase('dependency', 'dependency', 'error', () => {\n throw new ApiDependencyError('upstream internals');\n }),\n new ThrowCase('unavailable', 'unavailable', 'error', () => {\n throw new ApiUnavailableError('maintenance internals');\n }),\n new ThrowCase('dependency-timeout', 'dependency-timeout', 'error', () => {\n throw new ApiDependencyTimeoutError('upstream slow internals');\n }),\n new ThrowCase('dependency-backoff', 'dependency-backoff', 'error', () => {\n throw new ApiDependencyBackoffError('backoff internals', 42);\n }),\n new ThrowCase('connection', 'implementation', 'error', () => {\n throw new ApiConnectionError('ECONNRESET internals');\n }),\n new ThrowCase('raw-error', 'implementation', 'error', () => {\n throw new Error('SECRET-raw-internals');\n }),\n new ThrowCase('thrown-string', 'implementation', 'error', () => {\n throw 'SECRET-string-internals';\n }),\n];\n\nexport function raiseFor(query: string): never {\n const found = THROW_CASES.find((candidate: ThrowCase) => candidate.name === query);\n if (!found) throw new Error(`unknown throw case ${query}`);\n return found.raise();\n}\n\n@injectable()\nexport class RemoteMcpController extends RemoteMcpApi {\n calls = 0;\n authorization?: string;\n\n override search(request: RemoteRequest): Promise<RemoteResponse> {\n this.calls += 1;\n this.authorization = RequestContext.getRequest()?.getHeader('authorization');\n return Promise.resolve(\n new RemoteResponse(\n RequestContext.getTrusted(REMOTE_USER) ?? 'missing-user',\n RequestContext.getTrusted(REMOTE_ROLES) ?? 'missing-roles',\n `remote:${request.query}`,\n ),\n );\n }\n}\n\n@injectable()\nexport class LocalThrowController extends LocalThrowApi {\n override async fail(request: RemoteRequest): Promise<RemoteResponse> {\n return raiseFor(request.query);\n }\n}\n\n@injectable()\nexport class RemoteThrowController extends RemoteThrowApi {\n override async fail(request: RemoteRequest): Promise<RemoteResponse> {\n return raiseFor(request.query);\n }\n}\n\nexport class RecordingOidcMinter {\n readonly audiences: string[] = [];\n\n constructor(\n private readonly failure?: Error,\n private readonly token = OIDC_TOKEN,\n ) {}\n\n mintIdToken(audience: string): Promise<string> {\n this.audiences.push(audience);\n if (this.failure) return Promise.reject(this.failure);\n return Promise.resolve(this.token);\n }\n}\n\nexport class RecordingOidcVerifier extends OidcHook {\n readonly tokens: string[] = [];\n readonly callerLists: string[][] = [];\n\n override verifyOidc(token: string, callers: string[]): Promise<AuthenticatedMachineIdentity> {\n this.tokens.push(token);\n this.callerLists.push(callers);\n if (token !== OIDC_TOKEN) throw new Error(`unexpected OIDC token '${token}'`);\n return Promise.resolve(new AuthenticatedMachineIdentity('oidc','test-service@example.test'));\n }\n}\n\n@injectable()\nexport class BoundaryProbeFilter extends Filter<MethodMeta, WpResponse<unknown>> {\n calls = 0;\n sawLogIdentity = false;\n sawTrustedUser?: string;\n\n override filter(\n meta: MethodMeta,\n next: Service<MethodMeta, WpResponse<unknown>>,\n ): Promise<WpResponse<unknown>> {\n this.calls += 1;\n this.sawLogIdentity =\n RequestContext.getUntrusted(WebpiecesCoreHeaders.CONTROLLER) ===\n 'RemoteMcpController' &&\n RequestContext.getUntrusted(WebpiecesCoreHeaders.METHOD) === 'search';\n this.sawTrustedUser = RequestContext.getTrusted(REMOTE_USER);\n return next.invoke(meta);\n }\n}\n\n/**\n * The documented gateway opt-out from http-client-node's \"a dependency's 4xx is OUR 500\" invariant:\n * an MCP gateway relays a Webpieces peer's typed failure so the model sees the same kind it would see\n * for a local binding. `relay = false` answers \"not mine\", which is the framework default.\n */\nexport class RelayWebpiecesPeerErrors implements ErrorTranslator {\n relay = true;\n\n private readonly fallback = new WebpiecesDefaultErrorTranslator();\n\n toWire(error: Error): HttpResponseDto {\n return this.fallback.toWire(error);\n }\n\n fromWire(response: HttpResponseDto): void {\n if (this.relay && ApiErrorCodec.isPayload(response.body)) {\n const decoded = ApiErrorCodec.decode(response.body);\n const statusCode = decoded instanceof ApiCodedError ? decoded.statusCode : undefined;\n if (\n ApiErrorHttpStatus.hasCode(decoded) &&\n ApiErrorHttpStatus.codeFor(decoded.kind, statusCode) === response.status.code\n ) {\n throw decoded;\n }\n }\n this.fallback.fromWire(response);\n }\n}\n"]}
1
+ {"version":3,"file":"McpRemoteFixtures.js","sourceRoot":"","sources":["../../../../../../packages/http/mcp-server/src/__tests__/McpRemoteFixtures.ts"],"names":[],"mappings":"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","sourcesContent":["import { AuthenticatedMachineIdentity } from '@webpieces/http-routing';\nimport { ClientRole, WpAuthorization, AuthorizationType, WpAuth, oidc as oidcAuth, jwt as jwtAuth } from '@webpieces/core-util';\nimport 'reflect-metadata';\nimport { injectable } from 'inversify';\nimport { ApiBadRequestError, ApiCodedError, ApiConflictError, ApiConnectionError, ApiDependencyBackoffError, ApiDependencyError, ApiDependencyTimeoutError, ApiEndpointNotFoundError, ApiEndUserError, ApiErrorCodec, ApiErrorHttpStatus, ApiForbiddenError, ApiImplementationError, ApiNotFoundError, ApiNotImplementedError, ApiPath, ApiType, ApiPreconditionFailedError, ApiRateLimitedError, ApiRequestTimeoutError, ApiUnauthorizedError, ApiUnavailableError, ApiUnprocessableError, ApiUnsupportedMediaTypeError, ContextKey, Endpoint, ErrorTranslator, Filter, WebpiecesDefaultErrorTranslator, HttpResponseDto, LogLevel, Service, WebpiecesCoreHeaders, ApiJsonSchema, McpToolCatalogFile, McpToolDefinition, ObjectSchemaBuilder, WpMcpTool, WpMcpToolHints, MCP, POST, READ, RPC, SVC_TO_SVC } from '@webpieces/core-util';\nimport { RequestContext } from '@webpieces/core-context';\nimport { MethodMeta, OidcHook, WpResponse } from '@webpieces/http-routing';\nimport { McpToolCatalog } from '../McpToolCatalog';\n\n/** Contracts, controllers and test doubles for McpRemoteBinding.integration.spec.ts. */\n\nexport const REMOTE_USER = ContextKey.trusted<string>(\n 'mcpRemoteUser',\n 'verified by the MCP access-token authority before delegation',\n 'x-mcp-remote-user',\n);\nexport const REMOTE_ROLES = ContextKey.trusted<string>(\n 'mcpRemoteRoles',\n 'verified by the MCP access-token authority before delegation',\n 'x-mcp-remote-roles',\n);\nexport const REMOTE_SERVICE = 'mcp-remote-integration';\nexport const REFUSED_SERVICE = 'mcp-remote-refused';\nexport const GARBAGE_SERVICE = 'mcp-remote-garbage';\nexport const OIDC_TOKEN = 'oidc-for-mcp-remote-integration';\nexport const EXTERNAL_MCP_BEARER = 'external-mcp-access-token';\nexport const LOCAL_ENDPOINT_JWT = 'local-endpoint-jwt-that-must-not-cross-the-remote-hop';\nexport const GATEWAY_PATH = '/gateway/mcp';\n\nexport class RemoteRequest {\n /** Search text */\n query!: string;\n\n constructor(query?: string) {\n if (query !== undefined) this.query = query;\n }\n}\n\nexport class RemoteResponse {\n /** Delegated user */\n user!: string;\n\n /** Delegated roles */\n roles!: string;\n\n /** Result */\n result!: string;\n\n constructor(user?: string, roles?: string, result?: string) {\n if (user !== undefined) this.user = user;\n if (roles !== undefined) this.roles = roles;\n if (result !== undefined) this.result = result;\n }\n}\n\n/**\n * The generated tool catalogs these contracts would produce — what `wp-openapi` writes, one\n * `mcp-<ContractClass>-tools.json` per contract. Hand-built here because `@webpieces/mcp-server` does not depend on the\n * TypeScript compiler API; the runtime's job is to BE HANDED a catalog.\n */\nfunction describedString(description: string): ApiJsonSchema {\n const schema = new ApiJsonSchema('string');\n schema.description = description;\n return schema;\n}\n\nfunction searchTool(name: string, methodName: string, description: string): McpToolDefinition {\n return new McpToolDefinition(\n name,\n methodName,\n description,\n new WpMcpToolHints(true, false, true, false),\n new ObjectSchemaBuilder().required('query', describedString('Search text')).build(),\n new ObjectSchemaBuilder()\n .required('user', describedString('Delegated user'))\n .required('roles', describedString('Delegated roles'))\n .required('result', describedString('Result'))\n .build(),\n );\n}\n\n/** One contract's hand-built catalog, labelled as built in memory. */\nfunction contractCatalog(contractName: string, tool: McpToolDefinition): McpToolCatalog {\n return new McpToolCatalog(new McpToolCatalogFile(contractName, [tool]), '(built in memory by the spec)');\n}\n\nexport const REMOTE_MCP_CATALOG = contractCatalog(\n 'RemoteMcpApi',\n searchTool('remote_integration_search', 'search', 'Calls a remote Webpieces API.'),\n);\n\n/** The catalogs of every contract the gateway binds — one per contract, exactly as a build writes them. */\nexport const GATEWAY_CATALOGS: readonly McpToolCatalog[] = [\n contractCatalog(\n 'MissingRemoteMcpApi',\n searchTool('missing_remote_integration_search', 'search', 'Intentionally absent route.'),\n ),\n contractCatalog('RefusedRemoteApi', searchTool('refused_remote', 'search', 'Nothing listens on this port.')),\n contractCatalog('GarbageRemoteApi', searchTool('garbage_remote', 'search', 'Answers an undecodable body.')),\n contractCatalog(\n 'OidcFailRemoteApi',\n searchTool('oidc_fail_remote', 'search', 'Its OIDC token cannot be minted.'),\n ),\n contractCatalog('LocalThrowApi', searchTool('local_throw', 'fail', 'Throws the requested failure in-process.')),\n contractCatalog('RemoteThrowApi', searchTool('remote_throw', 'fail', 'Throws the requested failure remotely.')),\n];\n\n@ApiPath('/remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class RemoteMcpApi {\n /** Calls a remote Webpieces API. */\n\n @WpAuth([oidcAuth('mcp-gateway')])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('remote_integration_search')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/missing-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class MissingRemoteMcpApi {\n /** Intentionally absent route. */\n\n @WpAuth([oidcAuth('mcp-gateway')])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('missing_remote_integration_search')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/refused-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class RefusedRemoteApi {\n /** Nothing listens on this port. */\n\n @WpAuth([oidcAuth('mcp-gateway')])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('refused_remote')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/garbage-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class GarbageRemoteApi {\n /** Answers an undecodable body. */\n\n @WpAuth([oidcAuth('mcp-gateway')])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('garbage_remote')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/oidc-fail-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class OidcFailRemoteApi {\n /** Its OIDC token cannot be minted. */\n\n @WpAuth([oidcAuth('mcp-gateway')])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('oidc_fail_remote')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/local-throw')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class LocalThrowApi {\n /** Throws the requested failure in-process. */\n\n @WpAuth([jwtAuth()])\n @WpAuthorization({ authType: AuthorizationType.ALL_USERS })\n @Endpoint(POST, '/throw', READ, RPC)\n @WpMcpTool('local_throw')\n fail(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/remote-throw')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class RemoteThrowApi {\n /** Throws the requested failure remotely. */\n\n @WpAuth([oidcAuth('mcp-gateway')])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/throw', READ, RPC)\n @WpMcpTool('remote_throw')\n fail(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n/**\n * One named throw, run identically behind the local and the remote binding.\n *\n * `expectedServerLevel` is the level of the ONE operator line the failure now produces on the server\n * that raised it — `LogApiCall`'s, at `warn` for `[API-server-resp-OTHER]` (a healthy rejection of\n * the caller's mistake) and `error` for `[API-server-resp-FAIL]`. `ApiErrorBoundary` no longer\n * writes a second line of its own (#961).\n */\nexport class ThrowCase {\n constructor(\n public readonly name: string,\n public readonly expectedKind: string,\n public readonly expectedServerLevel: LogLevel,\n public readonly raise: () => never,\n ) {}\n}\n\nexport const THROW_CASES: readonly ThrowCase[] = [\n new ThrowCase('end-user', 'end-user', 'warn', () => {\n throw new ApiEndUserError('The two passwords you entered do not match.', 'PW_MISMATCH');\n }),\n new ThrowCase('bad-request', 'bad-request', 'warn', () => {\n throw new ApiBadRequestError('internal column name', '$.query', 'query must be a word');\n }),\n new ThrowCase('unauthorized', 'unauthorized', 'warn', () => {\n throw new ApiUnauthorizedError('session internals');\n }),\n new ThrowCase('forbidden', 'forbidden', 'warn', () => {\n throw new ApiForbiddenError('acl internals');\n }),\n new ThrowCase('not-found', 'not-found', 'warn', () => {\n throw new ApiNotFoundError('row 7 missing');\n }),\n new ThrowCase('endpoint-not-found', 'endpoint-not-found', 'warn', () => {\n throw new ApiEndpointNotFoundError('route internals');\n }),\n new ThrowCase('request-timeout', 'request-timeout', 'error', () => {\n throw new ApiRequestTimeoutError('slow internals');\n }),\n new ThrowCase('rate-limited', 'rate-limited', 'error', () => {\n throw new ApiRateLimitedError('quota internals');\n }),\n new ThrowCase('conflict', 'conflict', 'warn', () => {\n throw new ApiConflictError('version internals');\n }),\n new ThrowCase('unprocessable', 'unprocessable', 'warn', () => {\n throw new ApiUnprocessableError('rule internals');\n }),\n new ThrowCase('precondition-failed', 'precondition-failed', 'warn', () => {\n throw new ApiPreconditionFailedError('etag internals');\n }),\n new ThrowCase('unsupported-media-type', 'unsupported-media-type', 'warn', () => {\n throw new ApiUnsupportedMediaTypeError('parser internals');\n }),\n new ThrowCase('not-implemented', 'not-implemented', 'error', () => {\n throw new ApiNotImplementedError('feature internals');\n }),\n new ThrowCase('coded-4xx', 'coded', 'warn', () => {\n throw new ApiCodedError('quota internals', 460, 'QUOTA');\n }),\n new ThrowCase('coded-5xx', 'coded', 'error', () => {\n throw new ApiCodedError('disk internals', 507);\n }),\n new ThrowCase('implementation', 'implementation', 'error', () => {\n throw new ApiImplementationError('bug internals');\n }),\n new ThrowCase('dependency', 'dependency', 'error', () => {\n throw new ApiDependencyError('upstream internals');\n }),\n new ThrowCase('unavailable', 'unavailable', 'error', () => {\n throw new ApiUnavailableError('maintenance internals');\n }),\n new ThrowCase('dependency-timeout', 'dependency-timeout', 'error', () => {\n throw new ApiDependencyTimeoutError('upstream slow internals');\n }),\n new ThrowCase('dependency-backoff', 'dependency-backoff', 'error', () => {\n throw new ApiDependencyBackoffError('backoff internals', 42);\n }),\n new ThrowCase('connection', 'implementation', 'error', () => {\n throw new ApiConnectionError('ECONNRESET internals');\n }),\n new ThrowCase('raw-error', 'implementation', 'error', () => {\n throw new Error('SECRET-raw-internals');\n }),\n new ThrowCase('thrown-string', 'implementation', 'error', () => {\n throw 'SECRET-string-internals';\n }),\n];\n\nexport function raiseFor(query: string): never {\n const found = THROW_CASES.find((candidate: ThrowCase) => candidate.name === query);\n if (!found) throw new Error(`unknown throw case ${query}`);\n return found.raise();\n}\n\n@injectable()\nexport class RemoteMcpController extends RemoteMcpApi {\n calls = 0;\n authorization?: string;\n\n override search(request: RemoteRequest): Promise<RemoteResponse> {\n this.calls += 1;\n this.authorization = RequestContext.getRequest()?.getHeader('authorization');\n return Promise.resolve(\n new RemoteResponse(\n RequestContext.getTrusted(REMOTE_USER) ?? 'missing-user',\n RequestContext.getTrusted(REMOTE_ROLES) ?? 'missing-roles',\n `remote:${request.query}`,\n ),\n );\n }\n}\n\n@injectable()\nexport class LocalThrowController extends LocalThrowApi {\n override async fail(request: RemoteRequest): Promise<RemoteResponse> {\n return raiseFor(request.query);\n }\n}\n\n@injectable()\nexport class RemoteThrowController extends RemoteThrowApi {\n override async fail(request: RemoteRequest): Promise<RemoteResponse> {\n return raiseFor(request.query);\n }\n}\n\nexport class RecordingOidcMinter {\n readonly audiences: string[] = [];\n\n constructor(\n private readonly failure?: Error,\n private readonly token = OIDC_TOKEN,\n ) {}\n\n mintIdToken(audience: string): Promise<string> {\n this.audiences.push(audience);\n if (this.failure) return Promise.reject(this.failure);\n return Promise.resolve(this.token);\n }\n}\n\nexport class RecordingOidcVerifier extends OidcHook {\n readonly tokens: string[] = [];\n readonly callerLists: string[][] = [];\n\n override verifyOidc(token: string, callers: string[]): Promise<AuthenticatedMachineIdentity> {\n this.tokens.push(token);\n this.callerLists.push(callers);\n if (token !== OIDC_TOKEN) throw new Error(`unexpected OIDC token '${token}'`);\n return Promise.resolve(new AuthenticatedMachineIdentity('oidc','test-service@example.test'));\n }\n}\n\n@injectable()\nexport class BoundaryProbeFilter extends Filter<MethodMeta, WpResponse<unknown>> {\n calls = 0;\n sawLogIdentity = false;\n sawTrustedUser?: string;\n\n override filter(\n meta: MethodMeta,\n next: Service<MethodMeta, WpResponse<unknown>>,\n ): Promise<WpResponse<unknown>> {\n this.calls += 1;\n this.sawLogIdentity =\n RequestContext.getUntrusted(WebpiecesCoreHeaders.CONTROLLER) ===\n 'RemoteMcpController' &&\n RequestContext.getUntrusted(WebpiecesCoreHeaders.METHOD) === 'search';\n this.sawTrustedUser = RequestContext.getTrusted(REMOTE_USER);\n return next.invoke(meta);\n }\n}\n\n/**\n * The documented gateway opt-out from http-client-node's \"a dependency's 4xx is OUR 500\" invariant:\n * an MCP gateway relays a Webpieces peer's typed failure so the model sees the same kind it would see\n * for a local binding. `relay = false` answers \"not mine\", which is the framework default.\n */\nexport class RelayWebpiecesPeerErrors implements ErrorTranslator {\n relay = true;\n\n private readonly fallback = new WebpiecesDefaultErrorTranslator();\n\n toWire(error: Error): HttpResponseDto {\n return this.fallback.toWire(error);\n }\n\n fromWire(response: HttpResponseDto, role: ClientRole): void {\n if (this.relay && ApiErrorCodec.isPayload(response.body)) {\n const decoded = ApiErrorCodec.decode(response.body);\n const statusCode = decoded instanceof ApiCodedError ? decoded.statusCode : undefined;\n if (\n ApiErrorHttpStatus.hasCode(decoded) &&\n ApiErrorHttpStatus.codeFor(decoded.kind, statusCode) === response.status.code\n ) {\n throw decoded;\n }\n }\n this.fallback.fromWire(response, role);\n }\n}\n"]}