@webpieces/mcp-server 0.4.781 → 0.4.783

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
@@ -75,6 +75,36 @@ A map is still a closed schema: no key can carry an unspecified value. Check clo
75
75
  `new DtoSchemaBuilder().isClosedSchema(schema)`, not `schema.additionalProperties === false`, which wrongly
76
76
  rejects typed maps.
77
77
 
78
+ ## Configuration
79
+
80
+ `WpMcpServerConfig` is built with fluent setters — every name sits next to its own value, so no two
81
+ settings can be swapped, and a future setting is an additive setter rather than a breaking signature
82
+ change. Expect to supply the type arguments explicitly:
83
+
84
+ ```ts
85
+ const config = new WpMcpServerConfig<MyGrant, MyMintRequest>()
86
+ .setName('my-server')
87
+ .setVersion('1.0.0')
88
+ .setResource('https://api.example.com/mcp')
89
+ .setAccessTokenAuthority(authority)
90
+ .setEndpointJwtAuthority(jwtHook)
91
+ .setEndpointMintRequest((credential) => new MyMintRequest(credential.subject))
92
+ .setAuthorizationServers(['https://login.example.com'])
93
+ .setRequiredScopes(['tools'])
94
+ .setErrorTranslator(new MyMcpErrorTranslators())
95
+ .setMaxAccountValidationAgeSeconds(15 * 60);
96
+ ```
97
+
98
+ Each setter validates its own value immediately and names itself in the failure: `setResource(...)`
99
+ and every entry of `setAuthorizationServers(...)` must be an absolute URL, because they are compared
100
+ against a token's audience and issuer. `setMaxAccountValidationAgeSeconds` and
101
+ `setMaxEndpointJwtLifetimeSeconds` default to one hour and are capped there.
102
+ `setErrorTranslator(...)` is the only optional non-ceiling setting; everything else is required and
103
+ `WpMcpServer.bind(...)` fails at startup listing **every** missing setter
104
+ (`WpMcpServerConfig is missing setResource(...)`). That is deliberate: a misconfigured audience or
105
+ issuer otherwise surfaces as a first-request `401 + WWW-Authenticate`, which is the OAuth discovery
106
+ signal, so a client answers it by re-authenticating and failing again — forever.
107
+
78
108
  Applications supply the endpoint path, explicit bindings, and deployment topology. There is no
79
109
  framework-owned `/mcp` path:
80
110
 
@@ -150,6 +180,40 @@ Every reply carries the requestId so a user can quote it: `_meta["webpieces/requ
150
180
  `tools/call` result (success or `isError`), `requestId` in the `isError` payload, and
151
181
  `error.data.requestId` on JSON-RPC and HTTP-boundary errors.
152
182
 
183
+ ### The application's own `tools/call` translation
184
+
185
+ An app owns its error taxonomy and owns how those errors should be explained to a model, so it gets
186
+ FIRST REFUSAL on every `tools/call` failure — the same convention `ExpressWrapper.handleError`
187
+ applies on the HTTP path through `ClientRegistry.tryTranslateToWire`. Register one with
188
+ `WpMcpServerConfig.setErrorTranslator(...)`; it is a setter and not a global, because `WpMcpServer` is
189
+ constructed by app code and two servers may run in one process.
190
+
191
+ ```ts
192
+ class LangMcpErrorTranslators implements McpErrorTranslators {
193
+ toToolResult(error: Error, scope: McpFailureScope): CallToolResult | undefined {
194
+ if (!(error instanceof LangPassageLockedError)) return undefined; // not mine
195
+ return {
196
+ content: [{ type: 'text', text: `Passage ${error.passageId} is locked.` }],
197
+ structuredContent: { passageId: error.passageId, action: 'ask_the_user_to_unlock' },
198
+ isError: true,
199
+ };
200
+ }
201
+ }
202
+ ```
203
+
204
+ - `error` is the RAW thrown value, NOT normalized, so `instanceof` on the app's own classes works.
205
+ - Returning `undefined` means "not mine" and the webpieces default renders, unchanged.
206
+ - A claimed error owns the ENTIRE `CallToolResult` — content, `structuredContent`, `isError`.
207
+ - webpieces default-fills `_meta["webpieces/requestId"]` only when the returned result has no
208
+ `_meta`; an app that sets `_meta` keeps it untouched.
209
+ - Operator-detail logging has already run when the translator is called, so claiming an error can
210
+ never silently kill observability. A translator that THROWS is itself reported through the same
211
+ boundary and the original error still renders the reply.
212
+ - The scope is `tools/call` ONLY. `tools/list` and the pre-SDK HTTP boundary stay framework-owned:
213
+ that boundary emits the `401 + WWW-Authenticate: Bearer resource_metadata=...` MCP clients depend
214
+ on for OAuth discovery, and an app rewriting it breaks connector onboarding in a way that is
215
+ extremely hard to debug.
216
+
153
217
  A remote binding's generated Node client turns a dependency's 4xx into the gateway's own
154
218
  `ApiImplementationError` (see `NodeProxyClient.adaptDownstreamFailure`). A gateway that wants the model
155
219
  to see the peer's typed 4xx instead registers an `ErrorTranslators` via
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/mcp-server",
3
- "version": "0.4.781",
3
+ "version": "0.4.783",
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.781",
28
- "@webpieces/core-util": "0.4.781",
29
- "@webpieces/http-routing": "0.4.781",
27
+ "@webpieces/core-context": "0.4.783",
28
+ "@webpieces/core-util": "0.4.783",
29
+ "@webpieces/http-routing": "0.4.783",
30
30
  "express": "5.1.0",
31
31
  "reflect-metadata": "0.2.2",
32
32
  "tslib": "2.8.1"
33
33
  },
34
34
  "devDependencies": {
35
- "@webpieces/http-client-node": "0.4.781",
36
- "@webpieces/http-server": "0.4.781"
35
+ "@webpieces/http-client-node": "0.4.783",
36
+ "@webpieces/http-server": "0.4.783"
37
37
  }
38
38
  }
package/src/McpAuth.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { JwtHook } from '@webpieces/http-routing';
2
2
  import { ContextTuple } from '@webpieces/core-util';
3
+ import { McpErrorTranslators } from './WpMcpErrorTranslator';
3
4
  export declare const MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS: number;
4
5
  export declare const MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS: number;
5
6
  export declare const MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS: number;
@@ -64,20 +65,102 @@ export declare class McpProtectedResourceMetadata {
64
65
  constructor(resource: string, authorizationServers: readonly string[], scopesSupported?: readonly string[]);
65
66
  }
66
67
  /**
67
- * Node MCP server configuration. Pass the same concrete application authority as both
68
- * accessTokenAuthority and endpointJwtAuthority when it implements both security seams.
68
+ * Node MCP server configuration, built with fluent setters: every name sits next to its own value, so
69
+ * no two settings can be swapped, and a future setting is an additive setter rather than a breaking
70
+ * signature change. Pass the same concrete application authority to both
71
+ * `setAccessTokenAuthority(...)` and `setEndpointJwtAuthority(...)` when it implements both security
72
+ * seams.
73
+ *
74
+ * Each setter validates its own value immediately and names itself in the failure. Setters cannot
75
+ * make a field impossible to forget the way a positional constructor could, so
76
+ * {@link WpMcpServerConfig.validate} runs from `WpMcpServer.bind(...)` and fails at startup listing
77
+ * EVERY missing required setter at once. A misconfiguration is therefore a boot failure naming the
78
+ * setter, instead of a first-request 401 + `WWW-Authenticate` — which is the OAuth discovery signal,
79
+ * so the client's correct response is to re-authenticate and fail again, forever.
80
+ *
81
+ * ```ts
82
+ * const config = new WpMcpServerConfig<MyGrant, MyMintRequest>()
83
+ * .setName('my-server')
84
+ * .setVersion('1.0.0')
85
+ * .setResource('https://api.example.com/mcp')
86
+ * .setAccessTokenAuthority(authority)
87
+ * .setEndpointJwtAuthority(jwtHook)
88
+ * .setEndpointMintRequest((credential) => new MyMintRequest(credential.subject))
89
+ * .setAuthorizationServers(['https://login.example.com'])
90
+ * .setRequiredScopes(['tools'])
91
+ * .setErrorTranslator(new MyMcpErrorTranslators());
92
+ * ```
69
93
  */
70
94
  export declare class WpMcpServerConfig<TGrant, TMintRequest> {
71
- readonly name: string;
72
- readonly version: string;
73
- readonly resource: string;
74
- readonly accessTokenAuthority: McpAccessTokenAuthority<TGrant>;
75
- readonly endpointJwtAuthority: JwtHook<TMintRequest>;
76
- readonly endpointMintRequest: McpEndpointMintRequestFactory<TMintRequest>;
77
- readonly authorizationServers: readonly string[];
78
- readonly requiredScopes: readonly string[];
79
- readonly maxAccountValidationAgeSeconds: number;
80
- readonly maxEndpointJwtLifetimeSeconds: number;
81
- constructor(name: string, version: string, resource: string, accessTokenAuthority: McpAccessTokenAuthority<TGrant>, endpointJwtAuthority: JwtHook<TMintRequest>, endpointMintRequest: McpEndpointMintRequestFactory<TMintRequest>, authorizationServers: readonly string[], requiredScopes: readonly string[], maxAccountValidationAgeSeconds?: number, maxEndpointJwtLifetimeSeconds?: number);
95
+ private nameValue?;
96
+ private versionValue?;
97
+ private resourceValue?;
98
+ private accessTokenAuthorityValue?;
99
+ private endpointJwtAuthorityValue?;
100
+ private endpointMintRequestValue?;
101
+ private authorizationServersValue?;
102
+ private requiredScopesValue?;
103
+ private errorTranslatorValue?;
104
+ private maxAccountValidationAgeSecondsValue;
105
+ private maxEndpointJwtLifetimeSecondsValue;
106
+ /** REQUIRED. The MCP server name reported by `initialize`. */
107
+ setName(name: string): this;
108
+ /** REQUIRED. The MCP server version; the registry revision is appended to it at bind time. */
109
+ setVersion(version: string): this;
110
+ /**
111
+ * REQUIRED. The canonical protected-resource URI, which must be an absolute URL: it is the exact
112
+ * audience every access token is checked against and the `resource_metadata` of the 401
113
+ * challenge, so a non-URL here is always a mistake — including a name or version landed on it.
114
+ */
115
+ setResource(resource: string): this;
116
+ /** REQUIRED. Application-owned paired mint/verify authority for MCP access tokens. */
117
+ setAccessTokenAuthority(authority: McpAccessTokenAuthority<TGrant>): this;
118
+ /** REQUIRED. The `JwtHook` minting the short-lived per-endpoint JWT for local bindings. */
119
+ setEndpointJwtAuthority(authority: JwtHook<TMintRequest>): this;
120
+ /** REQUIRED. Maps a verified MCP identity plus tool identity to that JwtHook's mint request. */
121
+ setEndpointMintRequest(factory: McpEndpointMintRequestFactory<TMintRequest>): this;
122
+ /**
123
+ * REQUIRED. The trusted token issuers. Every entry must be an absolute URL, because each is
124
+ * compared against a token's `issuer` claim and published as an `authorization_servers` entry —
125
+ * which is what makes a scope list landed here fail at boot rather than on the first request.
126
+ */
127
+ setAuthorizationServers(authorizationServers: readonly string[]): this;
128
+ /**
129
+ * REQUIRED, and empty only by saying so explicitly. Scopes every access token must carry before
130
+ * any tool is dispatched.
131
+ */
132
+ setRequiredScopes(requiredScopes: readonly string[]): this;
133
+ /**
134
+ * OPTIONAL. The application's `tools/call` error translators — first refusal on every tool
135
+ * failure, owning the ENTIRE result it claims. See {@link McpErrorTranslators}.
136
+ */
137
+ setErrorTranslator(translators: McpErrorTranslators): this;
138
+ /** OPTIONAL ceiling, 1 second to 1 hour. Defaults to 1 hour. */
139
+ setMaxAccountValidationAgeSeconds(seconds: number): this;
140
+ /** OPTIONAL ceiling, 1 second to 1 hour. Defaults to 1 hour. */
141
+ setMaxEndpointJwtLifetimeSeconds(seconds: number): this;
142
+ /**
143
+ * Called by `WpMcpServer.bind(...)`. Throws naming EVERY missing required setter at once, so a
144
+ * half-built config is repaired in one pass rather than one boot failure at a time.
145
+ */
146
+ validate(): void;
147
+ get name(): string;
148
+ get version(): string;
149
+ get resource(): string;
150
+ get accessTokenAuthority(): McpAccessTokenAuthority<TGrant>;
151
+ get endpointJwtAuthority(): JwtHook<TMintRequest>;
152
+ get endpointMintRequest(): McpEndpointMintRequestFactory<TMintRequest>;
153
+ get authorizationServers(): readonly string[];
154
+ get requiredScopes(): readonly string[];
155
+ /** `undefined` when the app registered none: webpieces then renders every tool failure. */
156
+ get errorTranslator(): McpErrorTranslators | undefined;
157
+ get maxAccountValidationAgeSeconds(): number;
158
+ get maxEndpointJwtLifetimeSeconds(): number;
82
159
  protectedResourceMetadata(): McpProtectedResourceMetadata;
160
+ private requireText;
161
+ private requireUrl;
162
+ private requireArray;
163
+ private requireSeconds;
164
+ private requirePresent;
165
+ private read;
83
166
  }
package/src/McpAuth.js CHANGED
@@ -90,46 +90,233 @@ class McpProtectedResourceMetadata {
90
90
  }
91
91
  exports.McpProtectedResourceMetadata = McpProtectedResourceMetadata;
92
92
  /**
93
- * Node MCP server configuration. Pass the same concrete application authority as both
94
- * accessTokenAuthority and endpointJwtAuthority when it implements both security seams.
93
+ * Node MCP server configuration, built with fluent setters: every name sits next to its own value, so
94
+ * no two settings can be swapped, and a future setting is an additive setter rather than a breaking
95
+ * signature change. Pass the same concrete application authority to both
96
+ * `setAccessTokenAuthority(...)` and `setEndpointJwtAuthority(...)` when it implements both security
97
+ * seams.
98
+ *
99
+ * Each setter validates its own value immediately and names itself in the failure. Setters cannot
100
+ * make a field impossible to forget the way a positional constructor could, so
101
+ * {@link WpMcpServerConfig.validate} runs from `WpMcpServer.bind(...)` and fails at startup listing
102
+ * EVERY missing required setter at once. A misconfiguration is therefore a boot failure naming the
103
+ * setter, instead of a first-request 401 + `WWW-Authenticate` — which is the OAuth discovery signal,
104
+ * so the client's correct response is to re-authenticate and fail again, forever.
105
+ *
106
+ * ```ts
107
+ * const config = new WpMcpServerConfig<MyGrant, MyMintRequest>()
108
+ * .setName('my-server')
109
+ * .setVersion('1.0.0')
110
+ * .setResource('https://api.example.com/mcp')
111
+ * .setAccessTokenAuthority(authority)
112
+ * .setEndpointJwtAuthority(jwtHook)
113
+ * .setEndpointMintRequest((credential) => new MyMintRequest(credential.subject))
114
+ * .setAuthorizationServers(['https://login.example.com'])
115
+ * .setRequiredScopes(['tools'])
116
+ * .setErrorTranslator(new MyMcpErrorTranslators());
117
+ * ```
95
118
  */
96
119
  class WpMcpServerConfig {
97
- name;
98
- version;
99
- resource;
100
- accessTokenAuthority;
101
- endpointJwtAuthority;
102
- endpointMintRequest;
103
- authorizationServers;
104
- requiredScopes;
105
- maxAccountValidationAgeSeconds;
106
- maxEndpointJwtLifetimeSeconds;
107
- constructor(name, version, resource, accessTokenAuthority, endpointJwtAuthority, endpointMintRequest, authorizationServers, requiredScopes, maxAccountValidationAgeSeconds = exports.MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS, maxEndpointJwtLifetimeSeconds = exports.MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS) {
108
- this.name = name;
109
- this.version = version;
110
- this.resource = resource;
111
- this.accessTokenAuthority = accessTokenAuthority;
112
- this.endpointJwtAuthority = endpointJwtAuthority;
113
- this.endpointMintRequest = endpointMintRequest;
114
- this.authorizationServers = authorizationServers;
115
- this.requiredScopes = requiredScopes;
116
- this.maxAccountValidationAgeSeconds = maxAccountValidationAgeSeconds;
117
- this.maxEndpointJwtLifetimeSeconds = maxEndpointJwtLifetimeSeconds;
118
- if (name.trim() === '' || version.trim() === '' || resource.trim() === '') {
119
- throw new Error('MCP name, version, and resource must be non-empty.');
120
+ nameValue;
121
+ versionValue;
122
+ resourceValue;
123
+ accessTokenAuthorityValue;
124
+ endpointJwtAuthorityValue;
125
+ endpointMintRequestValue;
126
+ authorizationServersValue;
127
+ requiredScopesValue;
128
+ errorTranslatorValue;
129
+ maxAccountValidationAgeSecondsValue = exports.MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS;
130
+ maxEndpointJwtLifetimeSecondsValue = exports.MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS;
131
+ /** REQUIRED. The MCP server name reported by `initialize`. */
132
+ setName(name) {
133
+ this.nameValue = this.requireText(name, 'setName');
134
+ return this;
135
+ }
136
+ /** REQUIRED. The MCP server version; the registry revision is appended to it at bind time. */
137
+ setVersion(version) {
138
+ this.versionValue = this.requireText(version, 'setVersion');
139
+ return this;
140
+ }
141
+ /**
142
+ * REQUIRED. The canonical protected-resource URI, which must be an absolute URL: it is the exact
143
+ * audience every access token is checked against and the `resource_metadata` of the 401
144
+ * challenge, so a non-URL here is always a mistake — including a name or version landed on it.
145
+ */
146
+ setResource(resource) {
147
+ this.resourceValue = this.requireUrl(resource, 'setResource');
148
+ return this;
149
+ }
150
+ /** REQUIRED. Application-owned paired mint/verify authority for MCP access tokens. */
151
+ setAccessTokenAuthority(authority) {
152
+ this.accessTokenAuthorityValue = this.requirePresent(authority, 'setAccessTokenAuthority');
153
+ return this;
154
+ }
155
+ /** REQUIRED. The `JwtHook` minting the short-lived per-endpoint JWT for local bindings. */
156
+ setEndpointJwtAuthority(authority) {
157
+ this.endpointJwtAuthorityValue = this.requirePresent(authority, 'setEndpointJwtAuthority');
158
+ return this;
159
+ }
160
+ /** REQUIRED. Maps a verified MCP identity plus tool identity to that JwtHook's mint request. */
161
+ setEndpointMintRequest(factory) {
162
+ if (typeof factory !== 'function') {
163
+ throw new Error('WpMcpServerConfig.setEndpointMintRequest(...) requires a function.');
120
164
  }
121
- if (maxAccountValidationAgeSeconds <= 0 ||
122
- maxAccountValidationAgeSeconds > exports.MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS) {
123
- throw new Error('MCP account validation cache ceiling must be between 1 second and 1 hour.');
165
+ this.endpointMintRequestValue = factory;
166
+ return this;
167
+ }
168
+ /**
169
+ * REQUIRED. The trusted token issuers. Every entry must be an absolute URL, because each is
170
+ * compared against a token's `issuer` claim and published as an `authorization_servers` entry —
171
+ * which is what makes a scope list landed here fail at boot rather than on the first request.
172
+ */
173
+ setAuthorizationServers(authorizationServers) {
174
+ const servers = this.requireArray(authorizationServers, 'setAuthorizationServers');
175
+ if (servers.length === 0) {
176
+ throw new Error('WpMcpServerConfig.setAuthorizationServers(...) requires at least one issuer URL.');
124
177
  }
125
- if (maxEndpointJwtLifetimeSeconds <= 0 ||
126
- maxEndpointJwtLifetimeSeconds > exports.MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS) {
127
- throw new Error('MCP endpoint JWT lifetime must be between 1 second and 1 hour.');
178
+ for (const server of servers)
179
+ this.requireUrl(server, 'setAuthorizationServers');
180
+ this.authorizationServersValue = servers;
181
+ return this;
182
+ }
183
+ /**
184
+ * REQUIRED, and empty only by saying so explicitly. Scopes every access token must carry before
185
+ * any tool is dispatched.
186
+ */
187
+ setRequiredScopes(requiredScopes) {
188
+ const scopes = this.requireArray(requiredScopes, 'setRequiredScopes');
189
+ for (const scope of scopes)
190
+ this.requireText(scope, 'setRequiredScopes');
191
+ this.requiredScopesValue = scopes;
192
+ return this;
193
+ }
194
+ /**
195
+ * OPTIONAL. The application's `tools/call` error translators — first refusal on every tool
196
+ * failure, owning the ENTIRE result it claims. See {@link McpErrorTranslators}.
197
+ */
198
+ setErrorTranslator(translators) {
199
+ const value = this.requirePresent(translators, 'setErrorTranslator');
200
+ if (typeof value.toToolResult !== 'function') {
201
+ throw new Error('WpMcpServerConfig.setErrorTranslator(...) requires a toToolResult(error, scope) method.');
128
202
  }
203
+ this.errorTranslatorValue = value;
204
+ return this;
205
+ }
206
+ /** OPTIONAL ceiling, 1 second to 1 hour. Defaults to 1 hour. */
207
+ setMaxAccountValidationAgeSeconds(seconds) {
208
+ this.maxAccountValidationAgeSecondsValue = this.requireSeconds(seconds, exports.MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS, 'setMaxAccountValidationAgeSeconds');
209
+ return this;
210
+ }
211
+ /** OPTIONAL ceiling, 1 second to 1 hour. Defaults to 1 hour. */
212
+ setMaxEndpointJwtLifetimeSeconds(seconds) {
213
+ this.maxEndpointJwtLifetimeSecondsValue = this.requireSeconds(seconds, exports.MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS, 'setMaxEndpointJwtLifetimeSeconds');
214
+ return this;
215
+ }
216
+ /**
217
+ * Called by `WpMcpServer.bind(...)`. Throws naming EVERY missing required setter at once, so a
218
+ * half-built config is repaired in one pass rather than one boot failure at a time.
219
+ */
220
+ validate() {
221
+ const missing = [];
222
+ if (this.nameValue === undefined)
223
+ missing.push('setName(...)');
224
+ if (this.versionValue === undefined)
225
+ missing.push('setVersion(...)');
226
+ if (this.resourceValue === undefined)
227
+ missing.push('setResource(...)');
228
+ if (this.accessTokenAuthorityValue === undefined) {
229
+ missing.push('setAccessTokenAuthority(...)');
230
+ }
231
+ if (this.endpointJwtAuthorityValue === undefined) {
232
+ missing.push('setEndpointJwtAuthority(...)');
233
+ }
234
+ if (this.endpointMintRequestValue === undefined) {
235
+ missing.push('setEndpointMintRequest(...)');
236
+ }
237
+ if (this.authorizationServersValue === undefined) {
238
+ missing.push('setAuthorizationServers(...)');
239
+ }
240
+ if (this.requiredScopesValue === undefined)
241
+ missing.push('setRequiredScopes(...)');
242
+ if (missing.length > 0) {
243
+ throw new Error(`WpMcpServerConfig is missing ${missing.join(', ')}`);
244
+ }
245
+ }
246
+ get name() {
247
+ return this.read(this.nameValue, 'setName');
248
+ }
249
+ get version() {
250
+ return this.read(this.versionValue, 'setVersion');
251
+ }
252
+ get resource() {
253
+ return this.read(this.resourceValue, 'setResource');
254
+ }
255
+ get accessTokenAuthority() {
256
+ return this.read(this.accessTokenAuthorityValue, 'setAccessTokenAuthority');
257
+ }
258
+ get endpointJwtAuthority() {
259
+ return this.read(this.endpointJwtAuthorityValue, 'setEndpointJwtAuthority');
260
+ }
261
+ get endpointMintRequest() {
262
+ return this.read(this.endpointMintRequestValue, 'setEndpointMintRequest');
263
+ }
264
+ get authorizationServers() {
265
+ return this.read(this.authorizationServersValue, 'setAuthorizationServers');
266
+ }
267
+ get requiredScopes() {
268
+ return this.read(this.requiredScopesValue, 'setRequiredScopes');
269
+ }
270
+ /** `undefined` when the app registered none: webpieces then renders every tool failure. */
271
+ get errorTranslator() {
272
+ return this.errorTranslatorValue;
273
+ }
274
+ get maxAccountValidationAgeSeconds() {
275
+ return this.maxAccountValidationAgeSecondsValue;
276
+ }
277
+ get maxEndpointJwtLifetimeSeconds() {
278
+ return this.maxEndpointJwtLifetimeSecondsValue;
129
279
  }
130
280
  protectedResourceMetadata() {
131
281
  return new McpProtectedResourceMetadata(this.resource, this.authorizationServers, this.requiredScopes);
132
282
  }
283
+ requireText(value, setter) {
284
+ if (typeof value !== 'string' || value.trim() === '') {
285
+ throw new Error(`WpMcpServerConfig.${setter}(...) requires a non-empty string.`);
286
+ }
287
+ return value;
288
+ }
289
+ requireUrl(value, setter) {
290
+ this.requireText(value, setter);
291
+ if (!URL.canParse(value)) {
292
+ throw new Error(`WpMcpServerConfig.${setter}(...) requires an absolute URL, got '${value}'.`);
293
+ }
294
+ return value;
295
+ }
296
+ requireArray(value, setter) {
297
+ if (!Array.isArray(value)) {
298
+ throw new Error(`WpMcpServerConfig.${setter}(...) requires an array of strings.`);
299
+ }
300
+ return value;
301
+ }
302
+ requireSeconds(value, ceiling, setter) {
303
+ if (!Number.isFinite(value) || value <= 0 || value > ceiling) {
304
+ throw new Error(`WpMcpServerConfig.${setter}(...) requires 1..${ceiling} seconds, got ${String(value)}.`);
305
+ }
306
+ return value;
307
+ }
308
+ requirePresent(value, setter) {
309
+ if (value === undefined || value === null) {
310
+ throw new Error(`WpMcpServerConfig.${setter}(...) requires a value.`);
311
+ }
312
+ return value;
313
+ }
314
+ read(value, setter) {
315
+ if (value === undefined) {
316
+ throw new Error(`WpMcpServerConfig is missing ${setter}(...)`);
317
+ }
318
+ return value;
319
+ }
133
320
  }
134
321
  exports.WpMcpServerConfig = WpMcpServerConfig;
135
322
  //# sourceMappingURL=McpAuth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"McpAuth.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpAuth.ts"],"names":[],"mappings":"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","sourcesContent":["import { JwtHook } from '@webpieces/http-routing';\nimport { ContextTuple } from '@webpieces/core-util';\n\nexport const MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS = 30 * 24 * 60 * 60;\nexport const MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS = 60 * 60;\nexport const MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS = 60 * 60;\n\n/** Framework-normalized access-token result. The token representation remains application-owned. */\nexport class MintedMcpAccessToken {\n constructor(\n public readonly token: string,\n public readonly resource: string,\n public readonly issuedAtEpochSeconds: number,\n public readonly expiresAtEpochSeconds: number,\n ) {\n if (token.trim() === '' || resource.trim() === '') {\n throw new Error('Minted MCP access token and resource must be non-empty.');\n }\n if (!Number.isFinite(issuedAtEpochSeconds) || !Number.isFinite(expiresAtEpochSeconds)) {\n throw new Error(\n 'Minted MCP access token timestamps must be finite epoch-second values.',\n );\n }\n if (expiresAtEpochSeconds <= issuedAtEpochSeconds) {\n throw new Error('MCP access token expiry must be after issuance.');\n }\n if (expiresAtEpochSeconds - issuedAtEpochSeconds > MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS) {\n throw new Error('MCP access token lifetime must not exceed 30 days.');\n }\n }\n}\n\n/**\n * Fresh, authoritative security facts resolved at the MCP resource boundary. Implementations must\n * verify issuer, exact resource/audience, expiry, token type/version, key/algorithm policy, and\n * current account state. Opaque access tokens are fully supported.\n */\nexport class VerifiedMcpCredential {\n constructor(\n public readonly subject: string,\n public readonly issuer: string,\n public readonly resource: string,\n public readonly issuedAtEpochSeconds: number,\n public readonly expiresAtEpochSeconds: number,\n public readonly scopes: readonly string[],\n /** When current enabled/revoked state and roles were read from the authoritative source. */\n public readonly accountValidatedAtEpochSeconds: number,\n /** Advisory tools/list filtering only. Endpoint authorization always runs again. */\n public readonly listingRoles: readonly string[] = [],\n /** Trusted delegated identity derived only by the access-token authority. */\n public readonly trustedContext: readonly ContextTuple[] = [],\n ) {}\n}\n\n/**\n * Application-owned paired mint/verify authority for resource-bound MCP access tokens.\n * `verifyAccessToken` must throw `ApiUnauthorizedError` for a token it rejects (the bind boundary\n * answers 401 + `WWW-Authenticate`); any other throw is treated as an implementation failure (500).\n */\nexport interface McpAccessTokenAuthority<TGrant> {\n mintAccessToken(grant: TGrant): Promise<MintedMcpAccessToken>;\n verifyAccessToken(\n accessToken: string,\n expectedResource: string,\n ): Promise<VerifiedMcpCredential>;\n}\n\n/** Minimal tool identity supplied when the app creates its own endpoint-JWT mint request. */\nexport class McpEndpointDescriptor {\n constructor(\n public readonly toolName: string,\n public readonly apiClassName: string,\n public readonly methodName: string,\n ) {}\n}\n\n/** Application mapping from verified MCP identity to its unconstrained JwtHook mint request. */\nexport type McpEndpointMintRequestFactory<TMintRequest> = (\n credential: VerifiedMcpCredential,\n endpoint: McpEndpointDescriptor,\n) => TMintRequest;\n\n/** RFC 9728-style metadata exposed by the MCP protected resource. */\nexport class McpProtectedResourceMetadata {\n readonly resource: string;\n readonly authorization_servers: readonly string[];\n readonly bearer_methods_supported = ['header'];\n readonly scopes_supported?: readonly string[];\n\n constructor(\n resource: string,\n authorizationServers: readonly string[],\n scopesSupported?: readonly string[],\n ) {\n this.resource = resource;\n this.authorization_servers = authorizationServers;\n this.scopes_supported = scopesSupported;\n }\n}\n\n/**\n * Node MCP server configuration. Pass the same concrete application authority as both\n * accessTokenAuthority and endpointJwtAuthority when it implements both security seams.\n */\nexport class WpMcpServerConfig<TGrant, TMintRequest> {\n constructor(\n public readonly name: string,\n public readonly version: string,\n public readonly resource: string,\n public readonly accessTokenAuthority: McpAccessTokenAuthority<TGrant>,\n public readonly endpointJwtAuthority: JwtHook<TMintRequest>,\n public readonly endpointMintRequest: McpEndpointMintRequestFactory<TMintRequest>,\n public readonly authorizationServers: readonly string[],\n public readonly requiredScopes: readonly string[],\n public readonly maxAccountValidationAgeSeconds = MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS,\n public readonly maxEndpointJwtLifetimeSeconds = MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS,\n ) {\n if (name.trim() === '' || version.trim() === '' || resource.trim() === '') {\n throw new Error('MCP name, version, and resource must be non-empty.');\n }\n if (\n maxAccountValidationAgeSeconds <= 0 ||\n maxAccountValidationAgeSeconds > MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS\n ) {\n throw new Error(\n 'MCP account validation cache ceiling must be between 1 second and 1 hour.',\n );\n }\n if (\n maxEndpointJwtLifetimeSeconds <= 0 ||\n maxEndpointJwtLifetimeSeconds > MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS\n ) {\n throw new Error('MCP endpoint JWT lifetime must be between 1 second and 1 hour.');\n }\n }\n\n protectedResourceMetadata(): McpProtectedResourceMetadata {\n return new McpProtectedResourceMetadata(\n this.resource,\n this.authorizationServers,\n this.requiredScopes,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"McpAuth.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpAuth.ts"],"names":[],"mappings":"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","sourcesContent":["import { JwtHook } from '@webpieces/http-routing';\nimport { ContextTuple } from '@webpieces/core-util';\nimport { McpErrorTranslators } from './WpMcpErrorTranslator';\n\nexport const MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS = 30 * 24 * 60 * 60;\nexport const MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS = 60 * 60;\nexport const MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS = 60 * 60;\n\n/** Framework-normalized access-token result. The token representation remains application-owned. */\nexport class MintedMcpAccessToken {\n constructor(\n public readonly token: string,\n public readonly resource: string,\n public readonly issuedAtEpochSeconds: number,\n public readonly expiresAtEpochSeconds: number,\n ) {\n if (token.trim() === '' || resource.trim() === '') {\n throw new Error('Minted MCP access token and resource must be non-empty.');\n }\n if (!Number.isFinite(issuedAtEpochSeconds) || !Number.isFinite(expiresAtEpochSeconds)) {\n throw new Error(\n 'Minted MCP access token timestamps must be finite epoch-second values.',\n );\n }\n if (expiresAtEpochSeconds <= issuedAtEpochSeconds) {\n throw new Error('MCP access token expiry must be after issuance.');\n }\n if (expiresAtEpochSeconds - issuedAtEpochSeconds > MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS) {\n throw new Error('MCP access token lifetime must not exceed 30 days.');\n }\n }\n}\n\n/**\n * Fresh, authoritative security facts resolved at the MCP resource boundary. Implementations must\n * verify issuer, exact resource/audience, expiry, token type/version, key/algorithm policy, and\n * current account state. Opaque access tokens are fully supported.\n */\nexport class VerifiedMcpCredential {\n constructor(\n public readonly subject: string,\n public readonly issuer: string,\n public readonly resource: string,\n public readonly issuedAtEpochSeconds: number,\n public readonly expiresAtEpochSeconds: number,\n public readonly scopes: readonly string[],\n /** When current enabled/revoked state and roles were read from the authoritative source. */\n public readonly accountValidatedAtEpochSeconds: number,\n /** Advisory tools/list filtering only. Endpoint authorization always runs again. */\n public readonly listingRoles: readonly string[] = [],\n /** Trusted delegated identity derived only by the access-token authority. */\n public readonly trustedContext: readonly ContextTuple[] = [],\n ) {}\n}\n\n/**\n * Application-owned paired mint/verify authority for resource-bound MCP access tokens.\n * `verifyAccessToken` must throw `ApiUnauthorizedError` for a token it rejects (the bind boundary\n * answers 401 + `WWW-Authenticate`); any other throw is treated as an implementation failure (500).\n */\nexport interface McpAccessTokenAuthority<TGrant> {\n mintAccessToken(grant: TGrant): Promise<MintedMcpAccessToken>;\n verifyAccessToken(\n accessToken: string,\n expectedResource: string,\n ): Promise<VerifiedMcpCredential>;\n}\n\n/** Minimal tool identity supplied when the app creates its own endpoint-JWT mint request. */\nexport class McpEndpointDescriptor {\n constructor(\n public readonly toolName: string,\n public readonly apiClassName: string,\n public readonly methodName: string,\n ) {}\n}\n\n/** Application mapping from verified MCP identity to its unconstrained JwtHook mint request. */\nexport type McpEndpointMintRequestFactory<TMintRequest> = (\n credential: VerifiedMcpCredential,\n endpoint: McpEndpointDescriptor,\n) => TMintRequest;\n\n/** RFC 9728-style metadata exposed by the MCP protected resource. */\nexport class McpProtectedResourceMetadata {\n readonly resource: string;\n readonly authorization_servers: readonly string[];\n readonly bearer_methods_supported = ['header'];\n readonly scopes_supported?: readonly string[];\n\n constructor(\n resource: string,\n authorizationServers: readonly string[],\n scopesSupported?: readonly string[],\n ) {\n this.resource = resource;\n this.authorization_servers = authorizationServers;\n this.scopes_supported = scopesSupported;\n }\n}\n\n/**\n * Node MCP server configuration, built with fluent setters: every name sits next to its own value, so\n * no two settings can be swapped, and a future setting is an additive setter rather than a breaking\n * signature change. Pass the same concrete application authority to both\n * `setAccessTokenAuthority(...)` and `setEndpointJwtAuthority(...)` when it implements both security\n * seams.\n *\n * Each setter validates its own value immediately and names itself in the failure. Setters cannot\n * make a field impossible to forget the way a positional constructor could, so\n * {@link WpMcpServerConfig.validate} runs from `WpMcpServer.bind(...)` and fails at startup listing\n * EVERY missing required setter at once. A misconfiguration is therefore a boot failure naming the\n * setter, instead of a first-request 401 + `WWW-Authenticate` — which is the OAuth discovery signal,\n * so the client's correct response is to re-authenticate and fail again, forever.\n *\n * ```ts\n * const config = new WpMcpServerConfig<MyGrant, MyMintRequest>()\n * .setName('my-server')\n * .setVersion('1.0.0')\n * .setResource('https://api.example.com/mcp')\n * .setAccessTokenAuthority(authority)\n * .setEndpointJwtAuthority(jwtHook)\n * .setEndpointMintRequest((credential) => new MyMintRequest(credential.subject))\n * .setAuthorizationServers(['https://login.example.com'])\n * .setRequiredScopes(['tools'])\n * .setErrorTranslator(new MyMcpErrorTranslators());\n * ```\n */\nexport class WpMcpServerConfig<TGrant, TMintRequest> {\n private nameValue?: string;\n private versionValue?: string;\n private resourceValue?: string;\n private accessTokenAuthorityValue?: McpAccessTokenAuthority<TGrant>;\n private endpointJwtAuthorityValue?: JwtHook<TMintRequest>;\n private endpointMintRequestValue?: McpEndpointMintRequestFactory<TMintRequest>;\n private authorizationServersValue?: readonly string[];\n private requiredScopesValue?: readonly string[];\n private errorTranslatorValue?: McpErrorTranslators;\n private maxAccountValidationAgeSecondsValue = MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS;\n private maxEndpointJwtLifetimeSecondsValue = MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS;\n\n /** REQUIRED. The MCP server name reported by `initialize`. */\n setName(name: string): this {\n this.nameValue = this.requireText(name, 'setName');\n return this;\n }\n\n /** REQUIRED. The MCP server version; the registry revision is appended to it at bind time. */\n setVersion(version: string): this {\n this.versionValue = this.requireText(version, 'setVersion');\n return this;\n }\n\n /**\n * REQUIRED. The canonical protected-resource URI, which must be an absolute URL: it is the exact\n * audience every access token is checked against and the `resource_metadata` of the 401\n * challenge, so a non-URL here is always a mistake — including a name or version landed on it.\n */\n setResource(resource: string): this {\n this.resourceValue = this.requireUrl(resource, 'setResource');\n return this;\n }\n\n /** REQUIRED. Application-owned paired mint/verify authority for MCP access tokens. */\n setAccessTokenAuthority(authority: McpAccessTokenAuthority<TGrant>): this {\n this.accessTokenAuthorityValue = this.requirePresent(authority, 'setAccessTokenAuthority');\n return this;\n }\n\n /** REQUIRED. The `JwtHook` minting the short-lived per-endpoint JWT for local bindings. */\n setEndpointJwtAuthority(authority: JwtHook<TMintRequest>): this {\n this.endpointJwtAuthorityValue = this.requirePresent(authority, 'setEndpointJwtAuthority');\n return this;\n }\n\n /** REQUIRED. Maps a verified MCP identity plus tool identity to that JwtHook's mint request. */\n setEndpointMintRequest(factory: McpEndpointMintRequestFactory<TMintRequest>): this {\n if (typeof factory !== 'function') {\n throw new Error('WpMcpServerConfig.setEndpointMintRequest(...) requires a function.');\n }\n this.endpointMintRequestValue = factory;\n return this;\n }\n\n /**\n * REQUIRED. The trusted token issuers. Every entry must be an absolute URL, because each is\n * compared against a token's `issuer` claim and published as an `authorization_servers` entry —\n * which is what makes a scope list landed here fail at boot rather than on the first request.\n */\n setAuthorizationServers(authorizationServers: readonly string[]): this {\n const servers = this.requireArray(authorizationServers, 'setAuthorizationServers');\n if (servers.length === 0) {\n throw new Error(\n 'WpMcpServerConfig.setAuthorizationServers(...) requires at least one issuer URL.',\n );\n }\n for (const server of servers) this.requireUrl(server, 'setAuthorizationServers');\n this.authorizationServersValue = servers;\n return this;\n }\n\n /**\n * REQUIRED, and empty only by saying so explicitly. Scopes every access token must carry before\n * any tool is dispatched.\n */\n setRequiredScopes(requiredScopes: readonly string[]): this {\n const scopes = this.requireArray(requiredScopes, 'setRequiredScopes');\n for (const scope of scopes) this.requireText(scope, 'setRequiredScopes');\n this.requiredScopesValue = scopes;\n return this;\n }\n\n /**\n * OPTIONAL. The application's `tools/call` error translators — first refusal on every tool\n * failure, owning the ENTIRE result it claims. See {@link McpErrorTranslators}.\n */\n setErrorTranslator(translators: McpErrorTranslators): this {\n const value = this.requirePresent(translators, 'setErrorTranslator');\n if (typeof value.toToolResult !== 'function') {\n throw new Error(\n 'WpMcpServerConfig.setErrorTranslator(...) requires a toToolResult(error, scope) method.',\n );\n }\n this.errorTranslatorValue = value;\n return this;\n }\n\n /** OPTIONAL ceiling, 1 second to 1 hour. Defaults to 1 hour. */\n setMaxAccountValidationAgeSeconds(seconds: number): this {\n this.maxAccountValidationAgeSecondsValue = this.requireSeconds(\n seconds,\n MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS,\n 'setMaxAccountValidationAgeSeconds',\n );\n return this;\n }\n\n /** OPTIONAL ceiling, 1 second to 1 hour. Defaults to 1 hour. */\n setMaxEndpointJwtLifetimeSeconds(seconds: number): this {\n this.maxEndpointJwtLifetimeSecondsValue = this.requireSeconds(\n seconds,\n MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS,\n 'setMaxEndpointJwtLifetimeSeconds',\n );\n return this;\n }\n\n /**\n * Called by `WpMcpServer.bind(...)`. Throws naming EVERY missing required setter at once, so a\n * half-built config is repaired in one pass rather than one boot failure at a time.\n */\n validate(): void {\n const missing: string[] = [];\n if (this.nameValue === undefined) missing.push('setName(...)');\n if (this.versionValue === undefined) missing.push('setVersion(...)');\n if (this.resourceValue === undefined) missing.push('setResource(...)');\n if (this.accessTokenAuthorityValue === undefined) {\n missing.push('setAccessTokenAuthority(...)');\n }\n if (this.endpointJwtAuthorityValue === undefined) {\n missing.push('setEndpointJwtAuthority(...)');\n }\n if (this.endpointMintRequestValue === undefined) {\n missing.push('setEndpointMintRequest(...)');\n }\n if (this.authorizationServersValue === undefined) {\n missing.push('setAuthorizationServers(...)');\n }\n if (this.requiredScopesValue === undefined) missing.push('setRequiredScopes(...)');\n if (missing.length > 0) {\n throw new Error(`WpMcpServerConfig is missing ${missing.join(', ')}`);\n }\n }\n\n get name(): string {\n return this.read(this.nameValue, 'setName');\n }\n\n get version(): string {\n return this.read(this.versionValue, 'setVersion');\n }\n\n get resource(): string {\n return this.read(this.resourceValue, 'setResource');\n }\n\n get accessTokenAuthority(): McpAccessTokenAuthority<TGrant> {\n return this.read(this.accessTokenAuthorityValue, 'setAccessTokenAuthority');\n }\n\n get endpointJwtAuthority(): JwtHook<TMintRequest> {\n return this.read(this.endpointJwtAuthorityValue, 'setEndpointJwtAuthority');\n }\n\n get endpointMintRequest(): McpEndpointMintRequestFactory<TMintRequest> {\n return this.read(this.endpointMintRequestValue, 'setEndpointMintRequest');\n }\n\n get authorizationServers(): readonly string[] {\n return this.read(this.authorizationServersValue, 'setAuthorizationServers');\n }\n\n get requiredScopes(): readonly string[] {\n return this.read(this.requiredScopesValue, 'setRequiredScopes');\n }\n\n /** `undefined` when the app registered none: webpieces then renders every tool failure. */\n get errorTranslator(): McpErrorTranslators | undefined {\n return this.errorTranslatorValue;\n }\n\n get maxAccountValidationAgeSeconds(): number {\n return this.maxAccountValidationAgeSecondsValue;\n }\n\n get maxEndpointJwtLifetimeSeconds(): number {\n return this.maxEndpointJwtLifetimeSecondsValue;\n }\n\n protectedResourceMetadata(): McpProtectedResourceMetadata {\n return new McpProtectedResourceMetadata(\n this.resource,\n this.authorizationServers,\n this.requiredScopes,\n );\n }\n\n private requireText(value: string, setter: string): string {\n if (typeof value !== 'string' || value.trim() === '') {\n throw new Error(`WpMcpServerConfig.${setter}(...) requires a non-empty string.`);\n }\n return value;\n }\n\n private requireUrl(value: string, setter: string): string {\n this.requireText(value, setter);\n if (!URL.canParse(value)) {\n throw new Error(\n `WpMcpServerConfig.${setter}(...) requires an absolute URL, got '${value}'.`,\n );\n }\n return value;\n }\n\n private requireArray(value: readonly string[], setter: string): readonly string[] {\n if (!Array.isArray(value)) {\n throw new Error(`WpMcpServerConfig.${setter}(...) requires an array of strings.`);\n }\n return value;\n }\n\n private requireSeconds(value: number, ceiling: number, setter: string): number {\n if (!Number.isFinite(value) || value <= 0 || value > ceiling) {\n throw new Error(\n `WpMcpServerConfig.${setter}(...) requires 1..${ceiling} seconds, got ${String(value)}.`,\n );\n }\n return value;\n }\n\n private requirePresent<T>(value: T, setter: string): T {\n if (value === undefined || value === null) {\n throw new Error(`WpMcpServerConfig.${setter}(...) requires a value.`);\n }\n return value;\n }\n\n private read<T>(value: T | undefined, setter: string): T {\n if (value === undefined) {\n throw new Error(`WpMcpServerConfig is missing ${setter}(...)`);\n }\n return value;\n }\n}\n"]}
@@ -16,6 +16,31 @@ export declare class McpFailureScope {
16
16
  constructor(requestId: string, jsonRpcId: string | number | null, method: string, toolName?: string | undefined);
17
17
  describe(): string;
18
18
  }
19
+ /**
20
+ * The application's own `tools/call` error translation — the MCP twin of the `ErrorTranslators` that
21
+ * `ExpressWrapper.handleError` consults through `ClientRegistry.tryTranslateToWire` on the HTTP path.
22
+ * Register one with `WpMcpServerConfig.setErrorTranslator(...)`; it is NOT a global, because
23
+ * `WpMcpServer` is constructed by app code and two servers may run in one process.
24
+ *
25
+ * An app owns its error taxonomy and owns how those errors should be explained to a model, so a
26
+ * claimed error yields the ENTIRE `CallToolResult` — content, `structuredContent`, `isError`, the lot.
27
+ *
28
+ * Contract:
29
+ * - `error` is the RAW thrown value, NOT normalized: an app must be able to `instanceof` its own
30
+ * error classes, which `ApiErrorBoundary.normalize` would have collapsed to
31
+ * `ApiImplementationError`.
32
+ * - return `undefined` for "not mine" — webpieces then renders its own default, unchanged.
33
+ * - webpieces default-fills `_meta['webpieces/requestId']` only when the returned result has NO
34
+ * `_meta`. An app that sets `_meta` owns it untouched.
35
+ * - operator-detail logging has ALREADY happened when this is called, so claiming an error can never
36
+ * silently kill observability.
37
+ * - scope is `tools/call` ONLY. The pre-SDK HTTP boundary and `tools/list` stay framework-owned:
38
+ * that boundary emits the `401 + WWW-Authenticate: Bearer resource_metadata=...` MCP clients
39
+ * depend on for OAuth discovery, and an app rewriting it breaks connector onboarding.
40
+ */
41
+ export interface McpErrorTranslators {
42
+ toToolResult(error: Error, scope: McpFailureScope): CallToolResult | undefined;
43
+ }
19
44
  /** The only error shape rendered into model-visible MCP tool content. */
20
45
  export declare class ModelVisibleToolError {
21
46
  readonly kind: string;
@@ -49,7 +74,10 @@ export declare class McpHttpErrorDetail {
49
74
  * error's own message on the wire unless it is an `ApiEndUserError`.
50
75
  */
51
76
  export declare class WpMcpErrorTranslator {
77
+ private readonly appTranslators?;
52
78
  private readonly boundary;
79
+ /** `appTranslators` is the app's `tools/call` seam; `undefined` means webpieces renders all. */
80
+ constructor(appTranslators?: McpErrorTranslators | undefined);
53
81
  /**
54
82
  * Pre-SDK failures: 401 + WWW-Authenticate, 403, 400 (-32700), anything else 500 (-32603).
55
83
  * A failure after response headers were sent can only end the stream.
@@ -61,10 +89,30 @@ export declare class WpMcpErrorTranslator {
61
89
  toProtocolError(thrown: Error, scope: McpFailureScope): ProtocolError;
62
90
  /** tools/call with a name no binding registered: a JSON-RPC protocol error per MCP 2026-07-28. */
63
91
  unknownTool(toolName: string, scope: McpFailureScope): ProtocolError;
64
- /** Every tools/call failure after the tool is found: an `isError: true` result the model sees. */
92
+ /**
93
+ * Every tools/call failure after the tool is found: an `isError: true` result the model sees.
94
+ *
95
+ * The app's {@link McpErrorTranslators} gets FIRST REFUSAL — the same convention
96
+ * `ExpressWrapper.handleError` applies on the HTTP path — and owns the entire result it claims.
97
+ * Operator-detail logging runs BEFORE it, so exactly one operator line is written per failure
98
+ * whoever renders the reply.
99
+ */
65
100
  toToolResult(thrown: Error, scope: McpFailureScope): CallToolResult;
66
101
  /** The `_meta` every tools/call result carries so a user can quote its requestId. */
67
102
  resultMeta(requestId: string): Record<string, string>;
103
+ /**
104
+ * The app's first refusal. A translator that THROWS must not replace the failure being reported:
105
+ * the throw is reported through the same boundary as its own implementation failure and the
106
+ * ORIGINAL error still renders through the webpieces default, so the model still gets a reply
107
+ * carrying the requestId.
108
+ */
109
+ private appToolResult;
110
+ /**
111
+ * webpieces fills `_meta` only when the app left it off, keeping the README's "every reply
112
+ * carries the requestId" promise a DEFAULT rather than a restriction: an app that set `_meta`
113
+ * keeps it byte-for-byte.
114
+ */
115
+ private withDefaultMeta;
68
116
  private modelVisible;
69
117
  private writeHttp;
70
118
  }
@@ -84,7 +84,12 @@ exports.McpHttpErrorDetail = McpHttpErrorDetail;
84
84
  * error's own message on the wire unless it is an `ApiEndUserError`.
85
85
  */
86
86
  class WpMcpErrorTranslator {
87
+ appTranslators;
87
88
  boundary = new core_util_1.ApiErrorBoundary(log);
89
+ /** `appTranslators` is the app's `tools/call` seam; `undefined` means webpieces renders all. */
90
+ constructor(appTranslators) {
91
+ this.appTranslators = appTranslators;
92
+ }
88
93
  /**
89
94
  * Pre-SDK failures: 401 + WWW-Authenticate, 403, 400 (-32700), anything else 500 (-32603).
90
95
  * A failure after response headers were sent can only end the stream.
@@ -132,10 +137,20 @@ class WpMcpErrorTranslator {
132
137
  log.info(`Unknown MCP tool: ${scope.describe()}`);
133
138
  return new server_1.ProtocolError(server_1.ProtocolErrorCode.InvalidParams, `Unknown tool: ${toolName}`, new McpErrorData(scope.requestId));
134
139
  }
135
- /** Every tools/call failure after the tool is found: an `isError: true` result the model sees. */
140
+ /**
141
+ * Every tools/call failure after the tool is found: an `isError: true` result the model sees.
142
+ *
143
+ * The app's {@link McpErrorTranslators} gets FIRST REFUSAL — the same convention
144
+ * `ExpressWrapper.handleError` applies on the HTTP path — and owns the entire result it claims.
145
+ * Operator-detail logging runs BEFORE it, so exactly one operator line is written per failure
146
+ * whoever renders the reply.
147
+ */
136
148
  toToolResult(thrown, scope) {
137
149
  const error = this.boundary.normalize(thrown);
138
150
  this.boundary.logOperatorDetail(error, scope.describe());
151
+ const claimed = this.appToolResult(thrown, scope);
152
+ if (claimed)
153
+ return this.withDefaultMeta(claimed, scope.requestId);
139
154
  const visible = this.modelVisible(core_util_1.ApiErrorCodec.encode(error), scope);
140
155
  const text = JSON.stringify(visible);
141
156
  return {
@@ -148,6 +163,36 @@ class WpMcpErrorTranslator {
148
163
  resultMeta(requestId) {
149
164
  return { [exports.MCP_REQUEST_ID_META_KEY]: requestId };
150
165
  }
166
+ /**
167
+ * The app's first refusal. A translator that THROWS must not replace the failure being reported:
168
+ * the throw is reported through the same boundary as its own implementation failure and the
169
+ * ORIGINAL error still renders through the webpieces default, so the model still gets a reply
170
+ * carrying the requestId.
171
+ */
172
+ appToolResult(thrown, scope) {
173
+ if (!this.appTranslators)
174
+ return undefined;
175
+ // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- an app translator's own bug must not replace the failure it was asked to render
176
+ try {
177
+ return this.appTranslators.toToolResult(thrown, scope);
178
+ }
179
+ catch (err) {
180
+ const error = (0, core_util_1.toError)(err);
181
+ const failure = new core_util_1.ApiImplementationError('Application McpErrorTranslators.toToolResult threw; rendering the webpieces default.', error);
182
+ this.boundary.logOperatorDetail(failure, scope.describe());
183
+ return undefined;
184
+ }
185
+ }
186
+ /**
187
+ * webpieces fills `_meta` only when the app left it off, keeping the README's "every reply
188
+ * carries the requestId" promise a DEFAULT rather than a restriction: an app that set `_meta`
189
+ * keeps it byte-for-byte.
190
+ */
191
+ withDefaultMeta(result, requestId) {
192
+ if (result._meta !== undefined)
193
+ return result;
194
+ return { ...result, _meta: this.resultMeta(requestId) };
195
+ }
151
196
  modelVisible(payload, scope) {
152
197
  switch (payload.kind) {
153
198
  case 'end-user':
@@ -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 { Response } from 'express';\nimport {\n ApiBadRequestError,\n ApiErrorBoundary,\n ApiErrorCodec,\n ApiErrorPayload,\n DtoValue,\n LogManager,\n} from '@webpieces/core-util';\n\nconst log = LogManager.getLogger('WpMcpErrorTranslator');\n\n/** `_meta` key carrying the Webpieces requestId on every tools/call result, success or failure. */\nexport const MCP_REQUEST_ID_META_KEY = 'webpieces/requestId';\n\n/** JSON-RPC error `data` so a user can quote the requestId of any protocol-level failure. */\nexport class McpErrorData {\n constructor(public readonly requestId: string) {}\n}\n\n/** Correlation facts for one failure; rendered into the single operator log line. */\nexport class McpFailureScope {\n constructor(\n public readonly requestId: string,\n public readonly jsonRpcId: string | number | null,\n public readonly method: string,\n public readonly toolName?: string,\n ) {}\n\n describe(): string {\n const tool = this.toolName ? ` tool=${this.toolName}` : '';\n return `requestId=${this.requestId} jsonRpcId=${String(this.jsonRpcId)} method=${this.method}${tool}`;\n }\n}\n\n/** The only error shape rendered into model-visible MCP tool content. */\nexport class ModelVisibleToolError {\n constructor(\n public readonly kind: string,\n public readonly message: string,\n public readonly requestId: string,\n public readonly field?: string,\n public readonly callerMessage?: string,\n public readonly errorCode?: string,\n public readonly retryAfterSeconds?: number,\n public readonly statusCode?: number,\n ) {}\n}\n\n/** JSON-RPC error body written by the HTTP boundary before the SDK is involved. */\nexport class McpHttpErrorBody {\n readonly jsonrpc = '2.0';\n constructor(\n public readonly error: McpHttpErrorDetail,\n public readonly id: string | number | null,\n ) {}\n}\n\nexport class McpHttpErrorDetail {\n constructor(\n public readonly code: number,\n public readonly message: string,\n public readonly data: McpErrorData,\n ) {}\n}\n\n/**\n * The ONE place every MCP failure is mapped to a reply, mirroring `ApiErrorHttpMapper` for HTTP.\n * Each MCP entry point (the bind HTTP handler, tools/list, tools/call) has exactly one catch that\n * only delegates here. Normalization and per-kind log levels are the shared `ApiErrorBoundary`\n * rules, and published text comes only from `ApiErrorCodec.encode`: this class never puts an\n * error's own message on the wire unless it is an `ApiEndUserError`.\n */\nexport class WpMcpErrorTranslator {\n private readonly boundary = new ApiErrorBoundary(log);\n\n /**\n * Pre-SDK failures: 401 + WWW-Authenticate, 403, 400 (-32700), anything else 500 (-32603).\n * A failure after response headers were sent can only end the stream.\n */\n toHttp(thrown: Error, res: Response, scope: McpFailureScope, wwwAuthenticate: string): void {\n const error = this.boundary.normalize(thrown);\n this.boundary.logOperatorDetail(error, scope.describe());\n if (res.headersSent) {\n res.end();\n return;\n }\n const generic = ApiErrorCodec.encode(error).message;\n switch (error.kind) {\n case 'unauthorized':\n res.setHeader('WWW-Authenticate', wwwAuthenticate);\n this.writeHttp(res, 401, -32_000, generic, scope);\n return;\n case 'forbidden':\n this.writeHttp(res, 403, -32_000, generic, scope);\n return;\n case 'bad-request':\n this.writeHttp(res, 400, ProtocolErrorCode.ParseError, generic, scope);\n return;\n default:\n this.writeHttp(res, 500, ProtocolErrorCode.InternalError, 'Internal Error', scope);\n }\n }\n\n /** Express body-parser failures (malformed JSON, body too large) are caller input errors. */\n toHttpBodyFailure(cause: Error, res: Response, scope: McpFailureScope, wwwAuth: string): void {\n this.toHttp(\n new ApiBadRequestError(\n `MCP request body rejected: ${cause.message}`,\n undefined,\n undefined,\n cause,\n ),\n res,\n scope,\n wwwAuth,\n );\n }\n\n /** tools/list and other result-less methods: bad-request → -32602, anything else → -32603. */\n toProtocolError(thrown: Error, scope: McpFailureScope): ProtocolError {\n const error = this.boundary.normalize(thrown);\n this.boundary.logOperatorDetail(error, scope.describe());\n const payload = ApiErrorCodec.encode(error);\n if (error.kind === 'bad-request') {\n return new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n payload.callerMessage ?? payload.message,\n new McpErrorData(scope.requestId),\n );\n }\n const message = error.kind === 'end-user' ? payload.message : 'Internal Error';\n return new ProtocolError(\n ProtocolErrorCode.InternalError,\n message,\n new McpErrorData(scope.requestId),\n );\n }\n\n /** tools/call with a name no binding registered: a JSON-RPC protocol error per MCP 2026-07-28. */\n unknownTool(toolName: string, scope: McpFailureScope): ProtocolError {\n log.info(`Unknown MCP tool: ${scope.describe()}`);\n return new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n `Unknown tool: ${toolName}`,\n new McpErrorData(scope.requestId),\n );\n }\n\n /** Every tools/call failure after the tool is found: an `isError: true` result the model sees. */\n toToolResult(thrown: Error, scope: McpFailureScope): CallToolResult {\n const error = this.boundary.normalize(thrown);\n this.boundary.logOperatorDetail(error, scope.describe());\n const visible = this.modelVisible(ApiErrorCodec.encode(error), scope);\n const text = JSON.stringify(visible as DtoValue);\n return {\n content: [{ type: 'text', text }],\n isError: true,\n _meta: this.resultMeta(scope.requestId),\n };\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 private modelVisible(payload: ApiErrorPayload, scope: McpFailureScope): ModelVisibleToolError {\n switch (payload.kind) {\n case 'end-user':\n return new ModelVisibleToolError(\n payload.kind,\n payload.message,\n scope.requestId,\n undefined,\n undefined,\n payload.errorCode,\n );\n case 'bad-request':\n return new ModelVisibleToolError(\n payload.kind,\n payload.callerMessage ?? payload.message,\n scope.requestId,\n payload.field,\n payload.callerMessage,\n );\n case 'coded':\n return new ModelVisibleToolError(\n payload.kind,\n payload.message,\n scope.requestId,\n undefined,\n undefined,\n payload.errorCode,\n undefined,\n payload.statusCode,\n );\n case 'implementation':\n return new ModelVisibleToolError(\n payload.kind,\n `Internal error in tool ${scope.toolName ?? 'unknown'} (requestId ${scope.requestId}). ` +\n 'This is a bug in the tool, not in your arguments; retrying with different ' +\n 'arguments will not help.',\n scope.requestId,\n );\n default:\n return new ModelVisibleToolError(\n payload.kind,\n payload.message,\n scope.requestId,\n undefined,\n undefined,\n undefined,\n payload.retryAfterSeconds,\n );\n }\n }\n\n private writeHttp(\n res: Response,\n status: number,\n code: number,\n message: string,\n scope: McpFailureScope,\n ): void {\n res.status(status).json(\n new McpHttpErrorBody(\n new McpHttpErrorDetail(code, message, new McpErrorData(scope.requestId)),\n scope.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 { Response } from 'express';\nimport {\n ApiBadRequestError,\n ApiErrorBoundary,\n ApiErrorCodec,\n ApiErrorPayload,\n ApiImplementationError,\n DtoValue,\n LogManager,\n toError,\n} from '@webpieces/core-util';\n\nconst log = LogManager.getLogger('WpMcpErrorTranslator');\n\n/** `_meta` key carrying the Webpieces requestId on every tools/call result, success or failure. */\nexport const MCP_REQUEST_ID_META_KEY = 'webpieces/requestId';\n\n/** JSON-RPC error `data` so a user can quote the requestId of any protocol-level failure. */\nexport class McpErrorData {\n constructor(public readonly requestId: string) {}\n}\n\n/** Correlation facts for one failure; rendered into the single operator log line. */\nexport class McpFailureScope {\n constructor(\n public readonly requestId: string,\n public readonly jsonRpcId: string | number | null,\n public readonly method: string,\n public readonly toolName?: string,\n ) {}\n\n describe(): string {\n const tool = this.toolName ? ` tool=${this.toolName}` : '';\n return `requestId=${this.requestId} jsonRpcId=${String(this.jsonRpcId)} method=${this.method}${tool}`;\n }\n}\n\n/**\n * The application's own `tools/call` error translation — the MCP twin of the `ErrorTranslators` that\n * `ExpressWrapper.handleError` consults through `ClientRegistry.tryTranslateToWire` on the HTTP path.\n * Register one with `WpMcpServerConfig.setErrorTranslator(...)`; it is NOT a global, because\n * `WpMcpServer` is constructed by app code and two servers may run in one process.\n *\n * An app owns its error taxonomy and owns how those errors should be explained to a model, so a\n * claimed error yields the ENTIRE `CallToolResult` — content, `structuredContent`, `isError`, the lot.\n *\n * Contract:\n * - `error` is the RAW thrown value, NOT normalized: an app must be able to `instanceof` its own\n * error classes, which `ApiErrorBoundary.normalize` would have collapsed to\n * `ApiImplementationError`.\n * - return `undefined` for \"not mine\" — webpieces then renders its own default, unchanged.\n * - webpieces default-fills `_meta['webpieces/requestId']` only when the returned result has NO\n * `_meta`. An app that sets `_meta` owns it untouched.\n * - operator-detail logging has ALREADY happened when this is called, so claiming an error can never\n * silently kill observability.\n * - scope is `tools/call` ONLY. The pre-SDK HTTP boundary and `tools/list` stay framework-owned:\n * that boundary emits the `401 + WWW-Authenticate: Bearer resource_metadata=...` MCP clients\n * depend on for OAuth discovery, and an app rewriting it breaks connector onboarding.\n */\nexport interface McpErrorTranslators {\n toToolResult(error: Error, scope: McpFailureScope): CallToolResult | undefined;\n}\n\n/** The only error shape rendered into model-visible MCP tool content. */\nexport class ModelVisibleToolError {\n constructor(\n public readonly kind: string,\n public readonly message: string,\n public readonly requestId: string,\n public readonly field?: string,\n public readonly callerMessage?: string,\n public readonly errorCode?: string,\n public readonly retryAfterSeconds?: number,\n public readonly statusCode?: number,\n ) {}\n}\n\n/** JSON-RPC error body written by the HTTP boundary before the SDK is involved. */\nexport class McpHttpErrorBody {\n readonly jsonrpc = '2.0';\n constructor(\n public readonly error: McpHttpErrorDetail,\n public readonly id: string | number | null,\n ) {}\n}\n\nexport class McpHttpErrorDetail {\n constructor(\n public readonly code: number,\n public readonly message: string,\n public readonly data: McpErrorData,\n ) {}\n}\n\n/**\n * The ONE place every MCP failure is mapped to a reply, mirroring `ApiErrorHttpMapper` for HTTP.\n * Each MCP entry point (the bind HTTP handler, tools/list, tools/call) has exactly one catch that\n * only delegates here. Normalization and per-kind log levels are the shared `ApiErrorBoundary`\n * rules, and published text comes only from `ApiErrorCodec.encode`: this class never puts an\n * error's own message on the wire unless it is an `ApiEndUserError`.\n */\nexport class WpMcpErrorTranslator {\n private readonly boundary = new ApiErrorBoundary(log);\n\n /** `appTranslators` is the app's `tools/call` seam; `undefined` means webpieces renders all. */\n constructor(private readonly appTranslators?: McpErrorTranslators) {}\n\n /**\n * Pre-SDK failures: 401 + WWW-Authenticate, 403, 400 (-32700), anything else 500 (-32603).\n * A failure after response headers were sent can only end the stream.\n */\n toHttp(thrown: Error, res: Response, scope: McpFailureScope, wwwAuthenticate: string): void {\n const error = this.boundary.normalize(thrown);\n this.boundary.logOperatorDetail(error, scope.describe());\n if (res.headersSent) {\n res.end();\n return;\n }\n const generic = ApiErrorCodec.encode(error).message;\n switch (error.kind) {\n case 'unauthorized':\n res.setHeader('WWW-Authenticate', wwwAuthenticate);\n this.writeHttp(res, 401, -32_000, generic, scope);\n return;\n case 'forbidden':\n this.writeHttp(res, 403, -32_000, generic, scope);\n return;\n case 'bad-request':\n this.writeHttp(res, 400, ProtocolErrorCode.ParseError, generic, scope);\n return;\n default:\n this.writeHttp(res, 500, ProtocolErrorCode.InternalError, 'Internal Error', scope);\n }\n }\n\n /** Express body-parser failures (malformed JSON, body too large) are caller input errors. */\n toHttpBodyFailure(cause: Error, res: Response, scope: McpFailureScope, wwwAuth: string): void {\n this.toHttp(\n new ApiBadRequestError(\n `MCP request body rejected: ${cause.message}`,\n undefined,\n undefined,\n cause,\n ),\n res,\n scope,\n wwwAuth,\n );\n }\n\n /** tools/list and other result-less methods: bad-request → -32602, anything else → -32603. */\n toProtocolError(thrown: Error, scope: McpFailureScope): ProtocolError {\n const error = this.boundary.normalize(thrown);\n this.boundary.logOperatorDetail(error, scope.describe());\n const payload = ApiErrorCodec.encode(error);\n if (error.kind === 'bad-request') {\n return new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n payload.callerMessage ?? payload.message,\n new McpErrorData(scope.requestId),\n );\n }\n const message = error.kind === 'end-user' ? payload.message : 'Internal Error';\n return new ProtocolError(\n ProtocolErrorCode.InternalError,\n message,\n new McpErrorData(scope.requestId),\n );\n }\n\n /** tools/call with a name no binding registered: a JSON-RPC protocol error per MCP 2026-07-28. */\n unknownTool(toolName: string, scope: McpFailureScope): ProtocolError {\n log.info(`Unknown MCP tool: ${scope.describe()}`);\n return new ProtocolError(\n ProtocolErrorCode.InvalidParams,\n `Unknown tool: ${toolName}`,\n new McpErrorData(scope.requestId),\n );\n }\n\n /**\n * Every tools/call failure after the tool is found: an `isError: true` result the model sees.\n *\n * The app's {@link McpErrorTranslators} gets FIRST REFUSAL — the same convention\n * `ExpressWrapper.handleError` applies on the HTTP path — and owns the entire result it claims.\n * Operator-detail logging runs BEFORE it, so exactly one operator line is written per failure\n * whoever renders the reply.\n */\n toToolResult(thrown: Error, scope: McpFailureScope): CallToolResult {\n const error = this.boundary.normalize(thrown);\n this.boundary.logOperatorDetail(error, scope.describe());\n const claimed = this.appToolResult(thrown, scope);\n if (claimed) return this.withDefaultMeta(claimed, scope.requestId);\n const visible = this.modelVisible(ApiErrorCodec.encode(error), scope);\n const text = JSON.stringify(visible as DtoValue);\n return {\n content: [{ type: 'text', text }],\n isError: true,\n _meta: this.resultMeta(scope.requestId),\n };\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 * The app's first refusal. A translator that THROWS must not replace the failure being reported:\n * the throw is reported through the same boundary as its own implementation failure and the\n * ORIGINAL error still renders through the webpieces default, so the model still gets a reply\n * carrying the requestId.\n */\n private appToolResult(thrown: Error, scope: McpFailureScope): CallToolResult | undefined {\n if (!this.appTranslators) return undefined;\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 this.appTranslators.toToolResult(thrown, scope);\n } catch (err: unknown) {\n const error = toError(err);\n const failure = new ApiImplementationError(\n 'Application McpErrorTranslators.toToolResult threw; rendering the webpieces default.',\n error,\n );\n this.boundary.logOperatorDetail(failure, scope.describe());\n return undefined;\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, requestId: string): CallToolResult {\n if (result._meta !== undefined) return result;\n return { ...result, _meta: this.resultMeta(requestId) };\n }\n\n private modelVisible(payload: ApiErrorPayload, scope: McpFailureScope): ModelVisibleToolError {\n switch (payload.kind) {\n case 'end-user':\n return new ModelVisibleToolError(\n payload.kind,\n payload.message,\n scope.requestId,\n undefined,\n undefined,\n payload.errorCode,\n );\n case 'bad-request':\n return new ModelVisibleToolError(\n payload.kind,\n payload.callerMessage ?? payload.message,\n scope.requestId,\n payload.field,\n payload.callerMessage,\n );\n case 'coded':\n return new ModelVisibleToolError(\n payload.kind,\n payload.message,\n scope.requestId,\n undefined,\n undefined,\n payload.errorCode,\n undefined,\n payload.statusCode,\n );\n case 'implementation':\n return new ModelVisibleToolError(\n payload.kind,\n `Internal error in tool ${scope.toolName ?? 'unknown'} (requestId ${scope.requestId}). ` +\n 'This is a bug in the tool, not in your arguments; retrying with different ' +\n 'arguments will not help.',\n scope.requestId,\n );\n default:\n return new ModelVisibleToolError(\n payload.kind,\n payload.message,\n scope.requestId,\n undefined,\n undefined,\n undefined,\n payload.retryAfterSeconds,\n );\n }\n }\n\n private writeHttp(\n res: Response,\n status: number,\n code: number,\n message: string,\n scope: McpFailureScope,\n ): void {\n res.status(status).json(\n new McpHttpErrorBody(\n new McpHttpErrorDetail(code, message, new McpErrorData(scope.requestId)),\n scope.jsonRpcId,\n ),\n );\n }\n}\n"]}
@@ -13,6 +13,7 @@ import { McpBindOptions } from './McpBindOptions';
13
13
  export declare class WpMcpServer<TGrant, TMintRequest> {
14
14
  private readonly config;
15
15
  private readonly dispatcher;
16
+ /** tools/call gives the app's translators first refusal; every other reply stays framework-owned. */
16
17
  private readonly translator;
17
18
  private registry?;
18
19
  private handler?;
@@ -52,7 +52,8 @@ class WpSdkMcpServer extends server_1.McpServer {
52
52
  class WpMcpServer {
53
53
  config;
54
54
  dispatcher = new McpApiDispatcher_1.McpApiDispatcher();
55
- translator = new WpMcpErrorTranslator_1.WpMcpErrorTranslator();
55
+ /** tools/call gives the app's translators first refusal; every other reply stays framework-owned. */
56
+ translator;
56
57
  registry;
57
58
  handler;
58
59
  streamingHandler;
@@ -61,10 +62,14 @@ class WpMcpServer {
61
62
  revision;
62
63
  constructor(config) {
63
64
  this.config = config;
65
+ this.translator = new WpMcpErrorTranslator_1.WpMcpErrorTranslator(config.errorTranslator);
64
66
  }
65
67
  bind(app, options) {
66
68
  if (this.handler)
67
69
  throw new Error('WpMcpServer.bind(...) may be called only once.');
70
+ // Fail at boot naming the forgotten setter, never on the first request with a 401 the client
71
+ // answers by re-authenticating (see WpMcpServerConfig).
72
+ this.config.validate();
68
73
  this.registry = new McpToolRegistry_1.McpToolRegistry(options.bindings);
69
74
  this.revision = this.calculateRegistryRevision(this.registry);
70
75
  this.handler = this.createHandler(options, 'auto');
@@ -1 +1 @@
1
- {"version":3,"file":"WpMcpServer.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/WpMcpServer.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport {\n AuthInfo,\n CallToolRequest,\n CallToolResult,\n createMcpHandler,\n ListToolsResult,\n McpHttpHandler,\n McpRequestContext,\n McpServer,\n ProgressToken,\n ServerContext,\n ServerOptions,\n Tool,\n Implementation,\n} from '@modelcontextprotocol/server';\nimport { NodeMcpRequestHandler, toNodeHandler } from '@modelcontextprotocol/node';\nimport { Express, json, NextFunction, Request, Response } from 'express';\nimport {\n ApiForbiddenError,\n ApiImplementationError,\n ApiUnauthorizedError,\n DtoValue,\n LogManager,\n toError,\n WebpiecesCoreHeaders,\n} from '@webpieces/core-util';\nimport { HttpRequest, RequestContext, RequestContextHeaders } from '@webpieces/core-context';\nimport { MintedJwt } from '@webpieces/http-routing';\nimport {\n MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS,\n McpEndpointDescriptor,\n McpProtectedResourceMetadata,\n VerifiedMcpCredential,\n WpMcpServerConfig,\n} from './McpAuth';\nimport { McpApiDispatcher } from './McpApiDispatcher';\nimport { McpBindOptions } from './McpBindOptions';\nimport { McpInvocationContext, McpProgressReporter } from './McpInvocationContext';\nimport { McpToolRegistry, RegisteredMcpTool } from './McpToolRegistry';\nimport { McpFailureScope, WpMcpErrorTranslator } from './WpMcpErrorTranslator';\n\nconst log = LogManager.getLogger('WpMcpServer');\n\ntype AuthenticatedExpressRequest = Request & { auth?: AuthInfo };\n\n/** Facts established once per POST at the bind boundary and handed to the SDK handlers. */\nclass McpPostAuthentication {\n constructor(\n public readonly accessToken: string,\n public readonly credential: VerifiedMcpCredential,\n public readonly disconnectSignal: AbortSignal,\n ) {}\n}\n\n/**\n * The official SDK server with Webpieces-owned tools/list and tools/call handlers. It answers\n * `toolInputSchemaJson` from the Webpieces registry so the SDK's pre-dispatch SEP-2243\n * `Mcp-Param-*` header validation still runs without registering SDK-side tool callbacks.\n */\nclass WpSdkMcpServer extends McpServer {\n constructor(\n info: Implementation,\n options: ServerOptions,\n private readonly registry: McpToolRegistry,\n ) {\n super(info, options);\n }\n\n // webpieces-disable no-any-unknown -- signature is fixed by the SDK's JSON Schema record type\n override toolInputSchemaJson(name: string): Record<string, unknown> | undefined {\n return this.registry.find(name)?.inputSchema as Record<string, DtoValue> | undefined;\n }\n}\n\n/**\n * Modern-only MCP 2026-07-28 adapter around the official SDK's request-scoped HTTP handler.\n *\n * Error boundary: every failure is mapped by the one `WpMcpErrorTranslator`, and each entry point\n * has exactly one catch that only delegates to it: the bind HTTP handler (`toHttp`), tools/list\n * (`toProtocolError`) and tools/call (`toToolResult`). `subscriptions/listen` is served entirely by\n * the SDK's listen router, so Webpieces has no handler (and no catch) there. The external bearer is\n * verified exactly once per POST, at the HTTP boundary, before the SDK is involved.\n */\nexport class WpMcpServer<TGrant, TMintRequest> {\n private readonly dispatcher = new McpApiDispatcher();\n private readonly translator = new WpMcpErrorTranslator();\n private registry?: McpToolRegistry;\n private handler?: McpHttpHandler;\n private streamingHandler?: McpHttpHandler;\n private nodeHandler?: NodeMcpRequestHandler;\n private streamingNodeHandler?: NodeMcpRequestHandler;\n private revision?: string;\n\n constructor(private readonly config: WpMcpServerConfig<TGrant, TMintRequest>) {}\n\n bind(app: Express, options: McpBindOptions): void {\n if (this.handler) throw new Error('WpMcpServer.bind(...) may be called only once.');\n this.registry = new McpToolRegistry(options.bindings);\n this.revision = this.calculateRegistryRevision(this.registry);\n this.handler = this.createHandler(options, 'auto');\n this.streamingHandler = this.createHandler(options, 'sse');\n this.nodeHandler = toNodeHandler(this.handler, {\n onerror: (error: Error) => log.error('MCP Node transport failed', error),\n });\n this.streamingNodeHandler = toNodeHandler(this.streamingHandler, {\n onerror: (error: Error) => log.error('MCP streaming Node transport failed', error),\n });\n app.post(\n options.endpointPath,\n json(),\n async (req: Request, res: Response): Promise<void> =>\n this.handleHttp(req, res, options),\n // Express routes body-parser failures (bad JSON, body too large) to an error handler.\n (err: Error, req: Request, res: Response, _next: NextFunction): void =>\n RequestContext.run(() => {\n new RequestContextHeaders().fillFromRequest(this.toHttpRequest(req));\n this.translator.toHttpBodyFailure(\n err,\n res,\n this.httpScope(req),\n this.challenge(),\n );\n }),\n );\n }\n\n protectedResourceMetadata(): McpProtectedResourceMetadata {\n return this.config.protectedResourceMetadata();\n }\n\n /** Gracefully closes active listen/request streams; clients must reconnect and refresh lists. */\n async close(): Promise<void> {\n await Promise.all([this.handler?.close(), this.streamingHandler?.close()]);\n }\n\n /** Level-triggered invalidation; the authoritative list is always fetched again by clients. */\n toolsChanged(): void {\n this.handler?.notify.toolsChanged();\n }\n\n get registryRevision(): string | undefined {\n return this.revision;\n }\n\n private createHandler(options: McpBindOptions, responseMode: 'auto' | 'sse'): McpHttpHandler {\n return createMcpHandler(\n (context: McpRequestContext) => this.buildSdkServer(context, options),\n {\n legacy: 'reject',\n responseMode,\n bus: options.deployment.bus,\n maxSubscriptions: options.maxSubscriptions,\n keepAliveMs: options.keepAliveMs,\n onerror: (error: Error) => log.warn('MCP protocol request rejected', error),\n },\n );\n }\n\n /** Entry point #1: the one catch for everything before and around the SDK exchange. */\n private async handleHttp(req: Request, res: Response, options: McpBindOptions): Promise<void> {\n res.setHeader('X-Accel-Buffering', 'no');\n await RequestContext.run(async () => {\n new RequestContextHeaders().fillFromRequest(this.toHttpRequest(req));\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- MCP HTTP entry point; delegates only to WpMcpErrorTranslator\n try {\n await this.serveExpress(req, res, options);\n } catch (err: unknown) {\n const error = toError(err);\n this.translator.toHttp(error, res, this.httpScope(req), this.challenge());\n }\n });\n }\n\n private async serveExpress(\n req: Request,\n res: Response,\n options: McpBindOptions,\n ): Promise<void> {\n const origin = req.header('origin');\n if (origin && !options.allowedOrigins.includes(origin)) {\n throw new ApiForbiddenError(`MCP request Origin '${origin}' is not allowed.`);\n }\n const authentication = await this.authenticate(req);\n const request = req as AuthenticatedExpressRequest;\n request.auth = this.authInfo(authentication);\n const serve = this.hasProgressToken(req.body)\n ? this.streamingNodeHandler\n : this.nodeHandler;\n if (!serve) throw new ApiImplementationError('MCP Node handlers are not initialized.');\n await serve(request, res, req.body);\n }\n\n /** Verifies the external bearer once for this POST. Handlers never re-verify it. */\n private async authenticate(req: Request): Promise<McpPostAuthentication> {\n const match = req.header('authorization')?.match(/^Bearer ([^\\s]+)$/i);\n const token = match?.[1];\n if (!token) throw new ApiUnauthorizedError('MCP request has no bearer access token.');\n const credential = await this.config.accessTokenAuthority.verifyAccessToken(\n token,\n this.config.resource,\n );\n this.validateCredential(credential);\n return new McpPostAuthentication(token, credential, this.disconnectSignal(req));\n }\n\n private disconnectSignal(req: Request): AbortSignal {\n const disconnected = new AbortController();\n const res = req.res;\n const cancel = (): void => disconnected.abort();\n req.once('aborted', cancel);\n req.socket.once('close', cancel);\n res?.once('close', cancel);\n res?.once('finish', (): void => {\n req.off('aborted', cancel);\n req.socket.off('close', cancel);\n res.off('close', cancel);\n });\n return disconnected.signal;\n }\n\n private buildSdkServer(context: McpRequestContext, options: McpBindOptions): McpServer {\n const authentication = context.authInfo?.extra?.['webpiecesAuthentication'];\n if (context.era !== 'modern' || !(authentication instanceof McpPostAuthentication)) {\n throw new ApiImplementationError(\n 'WpMcpServer serves only MCP 2026-07-28 requests authenticated by its bind boundary.',\n );\n }\n const registry = this.requireRegistry();\n const server = new WpSdkMcpServer(\n { name: this.config.name, version: `${this.config.version}+${this.revision}` },\n {\n capabilities: { tools: { listChanged: true } },\n cacheHints: {\n 'tools/list': { ttlMs: options.deployment.ttlMs, cacheScope: 'private' },\n 'server/discover': { ttlMs: options.deployment.ttlMs, cacheScope: 'private' },\n },\n },\n registry,\n );\n server.server.removeRequestHandler('tools/list');\n server.server.removeRequestHandler('tools/call');\n server.server.setRequestHandler(\n 'tools/list',\n async (_request: object, sdkContext: ServerContext): Promise<ListToolsResult> =>\n this.handleListTools(authentication, sdkContext),\n );\n server.server.setRequestHandler(\n 'tools/call',\n async (request: CallToolRequest, sdkContext: ServerContext): Promise<CallToolResult> =>\n this.handleCallTool(server, request, authentication, sdkContext),\n );\n return server;\n }\n\n /** Entry point #2: tools/list has no tool-result channel, so failures are JSON-RPC errors. */\n private async handleListTools(\n authentication: McpPostAuthentication,\n sdkContext: ServerContext,\n ): Promise<ListToolsResult> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- MCP tools/list entry point; delegates only to WpMcpErrorTranslator\n try {\n return this.listTools(authentication.credential);\n } catch (err: unknown) {\n const error = toError(err);\n throw this.translator.toProtocolError(error, this.sdkScope(sdkContext, 'tools/list'));\n }\n }\n\n private listTools(credential: VerifiedMcpCredential): ListToolsResult {\n const tools: Tool[] = [];\n for (const tool of this.requireRegistry().tools) {\n if (tool.isVisibleTo(credential.listingRoles)) tools.push(this.toolDefinition(tool));\n }\n return { tools };\n }\n\n private toolDefinition(tool: RegisteredMcpTool): Tool {\n return {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema as Tool['inputSchema'],\n outputSchema: tool.outputSchema as Tool['outputSchema'],\n annotations: tool.annotations,\n _meta: { webpiecesRegistryRevision: this.revision },\n };\n }\n\n /**\n * Entry point #3: an unknown tool is a JSON-RPC -32602; every failure after the tool is found\n * is an `isError: true` result, identically for local and remote bindings.\n */\n private async handleCallTool(\n server: WpSdkMcpServer,\n request: CallToolRequest,\n authentication: McpPostAuthentication,\n sdkContext: ServerContext,\n ): Promise<CallToolResult> {\n const name = request.params.name;\n const scope = this.sdkScope(sdkContext, 'tools/call', name);\n const tool = this.requireRegistry().find(name);\n if (!tool) throw this.translator.unknownTool(name, scope);\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- MCP tools/call entry point; delegates only to WpMcpErrorTranslator\n try {\n const result = await this.callTool(tool, request, authentication, sdkContext);\n result._meta = this.translator.resultMeta(scope.requestId);\n return server.server.projectCallToolResult(\n result,\n tool.outputSchema as Record<string, DtoValue>,\n );\n } catch (err: unknown) {\n const error = toError(err);\n return this.translator.toToolResult(error, scope);\n }\n }\n\n /** Everything a tool call does. No catch in here: failures propagate to handleCallTool. */\n private async callTool(\n tool: RegisteredMcpTool,\n request: CallToolRequest,\n authentication: McpPostAuthentication,\n sdkContext: ServerContext,\n ): Promise<CallToolResult> {\n const credential = authentication.credential;\n const endpointJwt =\n tool.binding.topology === 'local'\n ? await this.mintEndpointJwt(tool, authentication)\n : undefined;\n const invocation = new McpInvocationContext(\n sdkContext.mcpReq.id,\n tool.name,\n credential.subject,\n credential.listingRoles,\n AbortSignal.any([sdkContext.mcpReq.signal, authentication.disconnectSignal]),\n this.progressReporter(sdkContext),\n );\n const args = (request.params.arguments ?? {}) as DtoValue;\n const value = await this.dispatcher.call(\n tool,\n args,\n credential,\n invocation,\n endpointJwt?.token,\n );\n const outputFailure = this.requireRegistry().schemaBuilder.validate(\n tool.responseClass,\n value,\n );\n if (outputFailure) {\n throw new ApiImplementationError(\n `MCP output schema violation for ${tool.apiClass.name}.${tool.methodName}: ${outputFailure.message}`,\n );\n }\n const structured = this.record(value);\n return {\n content: [{ type: 'text', text: JSON.stringify(structured) }],\n structuredContent: structured,\n };\n }\n\n private async mintEndpointJwt(\n tool: RegisteredMcpTool,\n authentication: McpPostAuthentication,\n ): Promise<MintedJwt> {\n const descriptor = new McpEndpointDescriptor(\n tool.name,\n tool.apiClass.name,\n tool.methodName,\n );\n const minted = await this.config.endpointJwtAuthority.mint(\n this.config.endpointMintRequest(authentication.credential, descriptor),\n );\n const now = Math.floor(Date.now() / 1000);\n if (minted.token === authentication.accessToken) {\n throw new ApiImplementationError(\n `MCP access-token passthrough is forbidden (endpoint JWT for ${tool.name}).`,\n );\n }\n if (\n minted.expiresAtEpochSeconds <= now ||\n minted.expiresAtEpochSeconds - now > this.config.maxEndpointJwtLifetimeSeconds\n ) {\n throw new ApiImplementationError(\n `MCP endpoint JWT lifetime is invalid for ${tool.name}.`,\n );\n }\n return minted;\n }\n\n private validateCredential(credential: VerifiedMcpCredential): void {\n const now = Math.floor(Date.now() / 1000);\n const reject = (reason: string): never => {\n throw new ApiUnauthorizedError(`MCP access token rejected: ${reason}`);\n };\n if (credential.subject.trim() === '') reject('no subject');\n const stamps = [\n credential.issuedAtEpochSeconds,\n credential.expiresAtEpochSeconds,\n credential.accountValidatedAtEpochSeconds,\n ];\n if (!stamps.every(Number.isFinite)) reject('security timestamps must be finite');\n if (credential.resource !== this.config.resource) reject('resource mismatch');\n if (!this.config.authorizationServers.includes(credential.issuer)) {\n reject('issuer is not trusted');\n }\n if (credential.issuedAtEpochSeconds > now || credential.expiresAtEpochSeconds <= now) {\n reject('outside its valid time window');\n }\n const lifetime = credential.expiresAtEpochSeconds - credential.issuedAtEpochSeconds;\n if (lifetime > MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS) reject('lifetime exceeds 30 days');\n for (const scope of this.config.requiredScopes) {\n if (!credential.scopes.includes(scope)) reject(`missing required scope '${scope}'`);\n }\n const validatedAge = now - credential.accountValidatedAtEpochSeconds;\n if (\n credential.accountValidatedAtEpochSeconds > now ||\n validatedAge > this.config.maxAccountValidationAgeSeconds\n ) {\n reject('account authorization state is not fresh enough for dispatch');\n }\n }\n\n private authInfo(authentication: McpPostAuthentication): AuthInfo {\n const credential = authentication.credential;\n return {\n token: authentication.accessToken,\n clientId: credential.subject,\n scopes: [...credential.scopes],\n expiresAt: credential.expiresAtEpochSeconds,\n resource: new URL(credential.resource),\n extra: { webpiecesAuthentication: authentication },\n };\n }\n\n private progressReporter(context: ServerContext): McpProgressReporter | undefined {\n const token = context.mcpReq._meta?.progressToken;\n if (token === undefined) return undefined;\n return async (progress: number, total?: number, message?: string): Promise<void> => {\n await context.mcpReq.notify({\n method: 'notifications/progress',\n params: this.progressParams(token, progress, total, message),\n });\n };\n }\n\n private progressParams(\n progressToken: ProgressToken,\n progress: number,\n total?: number,\n message?: string,\n ): Record<string, DtoValue> {\n const params: Record<string, DtoValue> = { progressToken, progress };\n if (total !== undefined) params['total'] = total;\n if (message !== undefined) params['message'] = message;\n return params;\n }\n\n private requestId(): string {\n return RequestContext.getUntrusted(WebpiecesCoreHeaders.REQUEST_ID) ?? 'missing-request-id';\n }\n\n private sdkScope(context: ServerContext, method: string, toolName?: string): McpFailureScope {\n return new McpFailureScope(this.requestId(), context.mcpReq.id, method, toolName);\n }\n\n private httpScope(req: Request): McpFailureScope {\n const body = this.bodyObject(req.body);\n const id = body?.['id'];\n const method = body?.['method'];\n const params = this.bodyObject(body?.['params']);\n const name = params?.['name'];\n return new McpFailureScope(\n this.requestId(),\n typeof id === 'string' || typeof id === 'number' ? id : null,\n typeof method === 'string' ? method : 'unknown',\n typeof name === 'string' ? name : undefined,\n );\n }\n\n private challenge(): string {\n return `Bearer resource_metadata=\"${this.config.resource}\"`;\n }\n\n private bodyObject(value: DtoValue | undefined): Record<string, DtoValue> | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;\n return value as Record<string, DtoValue>;\n }\n\n private hasProgressToken(body: DtoValue): boolean {\n const params = this.bodyObject(this.bodyObject(body)?.['params']);\n return this.bodyObject(params?.['_meta'])?.['progressToken'] !== undefined;\n }\n\n private toHttpRequest(req: Request): HttpRequest {\n const headers = new Map<string, string[]>();\n for (const [name, value] of Object.entries(req.headers)) {\n if (typeof value === 'string') headers.set(name, [value]);\n else if (Array.isArray(value)) headers.set(name, value);\n }\n return new HttpRequest(req.method, req.originalUrl || req.path, headers);\n }\n\n private calculateRegistryRevision(registry: McpToolRegistry): string {\n const surface = registry.tools.map((tool: RegisteredMcpTool) => [\n tool.name,\n tool.description,\n tool.inputSchema,\n tool.outputSchema,\n tool.annotations,\n ]);\n return createHash('sha256').update(JSON.stringify(surface)).digest('hex').slice(0, 12);\n }\n\n private requireRegistry(): McpToolRegistry {\n if (!this.registry) throw new Error('Call WpMcpServer.bind(...) before serving requests.');\n return this.registry;\n }\n\n private record(value: DtoValue): Record<string, DtoValue> {\n const jsonValue = JSON.stringify(value);\n if (jsonValue === undefined) {\n throw new ApiImplementationError('MCP structured content is not JSON serializable.');\n }\n const parsed = JSON.parse(jsonValue) as DtoValue;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return { value: parsed };\n }\n return parsed as Record<string, DtoValue>;\n }\n}\n"]}
1
+ {"version":3,"file":"WpMcpServer.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/WpMcpServer.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport {\n AuthInfo,\n CallToolRequest,\n CallToolResult,\n createMcpHandler,\n ListToolsResult,\n McpHttpHandler,\n McpRequestContext,\n McpServer,\n ProgressToken,\n ServerContext,\n ServerOptions,\n Tool,\n Implementation,\n} from '@modelcontextprotocol/server';\nimport { NodeMcpRequestHandler, toNodeHandler } from '@modelcontextprotocol/node';\nimport { Express, json, NextFunction, Request, Response } from 'express';\nimport {\n ApiForbiddenError,\n ApiImplementationError,\n ApiUnauthorizedError,\n DtoValue,\n LogManager,\n toError,\n WebpiecesCoreHeaders,\n} from '@webpieces/core-util';\nimport { HttpRequest, RequestContext, RequestContextHeaders } from '@webpieces/core-context';\nimport { MintedJwt } from '@webpieces/http-routing';\nimport {\n MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS,\n McpEndpointDescriptor,\n McpProtectedResourceMetadata,\n VerifiedMcpCredential,\n WpMcpServerConfig,\n} from './McpAuth';\nimport { McpApiDispatcher } from './McpApiDispatcher';\nimport { McpBindOptions } from './McpBindOptions';\nimport { McpInvocationContext, McpProgressReporter } from './McpInvocationContext';\nimport { McpToolRegistry, RegisteredMcpTool } from './McpToolRegistry';\nimport { McpFailureScope, WpMcpErrorTranslator } from './WpMcpErrorTranslator';\n\nconst log = LogManager.getLogger('WpMcpServer');\n\ntype AuthenticatedExpressRequest = Request & { auth?: AuthInfo };\n\n/** Facts established once per POST at the bind boundary and handed to the SDK handlers. */\nclass McpPostAuthentication {\n constructor(\n public readonly accessToken: string,\n public readonly credential: VerifiedMcpCredential,\n public readonly disconnectSignal: AbortSignal,\n ) {}\n}\n\n/**\n * The official SDK server with Webpieces-owned tools/list and tools/call handlers. It answers\n * `toolInputSchemaJson` from the Webpieces registry so the SDK's pre-dispatch SEP-2243\n * `Mcp-Param-*` header validation still runs without registering SDK-side tool callbacks.\n */\nclass WpSdkMcpServer extends McpServer {\n constructor(\n info: Implementation,\n options: ServerOptions,\n private readonly registry: McpToolRegistry,\n ) {\n super(info, options);\n }\n\n // webpieces-disable no-any-unknown -- signature is fixed by the SDK's JSON Schema record type\n override toolInputSchemaJson(name: string): Record<string, unknown> | undefined {\n return this.registry.find(name)?.inputSchema as Record<string, DtoValue> | undefined;\n }\n}\n\n/**\n * Modern-only MCP 2026-07-28 adapter around the official SDK's request-scoped HTTP handler.\n *\n * Error boundary: every failure is mapped by the one `WpMcpErrorTranslator`, and each entry point\n * has exactly one catch that only delegates to it: the bind HTTP handler (`toHttp`), tools/list\n * (`toProtocolError`) and tools/call (`toToolResult`). `subscriptions/listen` is served entirely by\n * the SDK's listen router, so Webpieces has no handler (and no catch) there. The external bearer is\n * verified exactly once per POST, at the HTTP boundary, before the SDK is involved.\n */\nexport class WpMcpServer<TGrant, TMintRequest> {\n private readonly dispatcher = new McpApiDispatcher();\n /** tools/call gives the app's translators first refusal; every other reply stays framework-owned. */\n private readonly translator: WpMcpErrorTranslator;\n private registry?: McpToolRegistry;\n private handler?: McpHttpHandler;\n private streamingHandler?: McpHttpHandler;\n private nodeHandler?: NodeMcpRequestHandler;\n private streamingNodeHandler?: NodeMcpRequestHandler;\n private revision?: string;\n\n constructor(private readonly config: WpMcpServerConfig<TGrant, TMintRequest>) {\n this.translator = new WpMcpErrorTranslator(config.errorTranslator);\n }\n\n bind(app: Express, options: McpBindOptions): void {\n if (this.handler) throw new Error('WpMcpServer.bind(...) may be called only once.');\n // Fail at boot naming the forgotten setter, never on the first request with a 401 the client\n // answers by re-authenticating (see WpMcpServerConfig).\n this.config.validate();\n this.registry = new McpToolRegistry(options.bindings);\n this.revision = this.calculateRegistryRevision(this.registry);\n this.handler = this.createHandler(options, 'auto');\n this.streamingHandler = this.createHandler(options, 'sse');\n this.nodeHandler = toNodeHandler(this.handler, {\n onerror: (error: Error) => log.error('MCP Node transport failed', error),\n });\n this.streamingNodeHandler = toNodeHandler(this.streamingHandler, {\n onerror: (error: Error) => log.error('MCP streaming Node transport failed', error),\n });\n app.post(\n options.endpointPath,\n json(),\n async (req: Request, res: Response): Promise<void> =>\n this.handleHttp(req, res, options),\n // Express routes body-parser failures (bad JSON, body too large) to an error handler.\n (err: Error, req: Request, res: Response, _next: NextFunction): void =>\n RequestContext.run(() => {\n new RequestContextHeaders().fillFromRequest(this.toHttpRequest(req));\n this.translator.toHttpBodyFailure(\n err,\n res,\n this.httpScope(req),\n this.challenge(),\n );\n }),\n );\n }\n\n protectedResourceMetadata(): McpProtectedResourceMetadata {\n return this.config.protectedResourceMetadata();\n }\n\n /** Gracefully closes active listen/request streams; clients must reconnect and refresh lists. */\n async close(): Promise<void> {\n await Promise.all([this.handler?.close(), this.streamingHandler?.close()]);\n }\n\n /** Level-triggered invalidation; the authoritative list is always fetched again by clients. */\n toolsChanged(): void {\n this.handler?.notify.toolsChanged();\n }\n\n get registryRevision(): string | undefined {\n return this.revision;\n }\n\n private createHandler(options: McpBindOptions, responseMode: 'auto' | 'sse'): McpHttpHandler {\n return createMcpHandler(\n (context: McpRequestContext) => this.buildSdkServer(context, options),\n {\n legacy: 'reject',\n responseMode,\n bus: options.deployment.bus,\n maxSubscriptions: options.maxSubscriptions,\n keepAliveMs: options.keepAliveMs,\n onerror: (error: Error) => log.warn('MCP protocol request rejected', error),\n },\n );\n }\n\n /** Entry point #1: the one catch for everything before and around the SDK exchange. */\n private async handleHttp(req: Request, res: Response, options: McpBindOptions): Promise<void> {\n res.setHeader('X-Accel-Buffering', 'no');\n await RequestContext.run(async () => {\n new RequestContextHeaders().fillFromRequest(this.toHttpRequest(req));\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- MCP HTTP entry point; delegates only to WpMcpErrorTranslator\n try {\n await this.serveExpress(req, res, options);\n } catch (err: unknown) {\n const error = toError(err);\n this.translator.toHttp(error, res, this.httpScope(req), this.challenge());\n }\n });\n }\n\n private async serveExpress(\n req: Request,\n res: Response,\n options: McpBindOptions,\n ): Promise<void> {\n const origin = req.header('origin');\n if (origin && !options.allowedOrigins.includes(origin)) {\n throw new ApiForbiddenError(`MCP request Origin '${origin}' is not allowed.`);\n }\n const authentication = await this.authenticate(req);\n const request = req as AuthenticatedExpressRequest;\n request.auth = this.authInfo(authentication);\n const serve = this.hasProgressToken(req.body)\n ? this.streamingNodeHandler\n : this.nodeHandler;\n if (!serve) throw new ApiImplementationError('MCP Node handlers are not initialized.');\n await serve(request, res, req.body);\n }\n\n /** Verifies the external bearer once for this POST. Handlers never re-verify it. */\n private async authenticate(req: Request): Promise<McpPostAuthentication> {\n const match = req.header('authorization')?.match(/^Bearer ([^\\s]+)$/i);\n const token = match?.[1];\n if (!token) throw new ApiUnauthorizedError('MCP request has no bearer access token.');\n const credential = await this.config.accessTokenAuthority.verifyAccessToken(\n token,\n this.config.resource,\n );\n this.validateCredential(credential);\n return new McpPostAuthentication(token, credential, this.disconnectSignal(req));\n }\n\n private disconnectSignal(req: Request): AbortSignal {\n const disconnected = new AbortController();\n const res = req.res;\n const cancel = (): void => disconnected.abort();\n req.once('aborted', cancel);\n req.socket.once('close', cancel);\n res?.once('close', cancel);\n res?.once('finish', (): void => {\n req.off('aborted', cancel);\n req.socket.off('close', cancel);\n res.off('close', cancel);\n });\n return disconnected.signal;\n }\n\n private buildSdkServer(context: McpRequestContext, options: McpBindOptions): McpServer {\n const authentication = context.authInfo?.extra?.['webpiecesAuthentication'];\n if (context.era !== 'modern' || !(authentication instanceof McpPostAuthentication)) {\n throw new ApiImplementationError(\n 'WpMcpServer serves only MCP 2026-07-28 requests authenticated by its bind boundary.',\n );\n }\n const registry = this.requireRegistry();\n const server = new WpSdkMcpServer(\n { name: this.config.name, version: `${this.config.version}+${this.revision}` },\n {\n capabilities: { tools: { listChanged: true } },\n cacheHints: {\n 'tools/list': { ttlMs: options.deployment.ttlMs, cacheScope: 'private' },\n 'server/discover': { ttlMs: options.deployment.ttlMs, cacheScope: 'private' },\n },\n },\n registry,\n );\n server.server.removeRequestHandler('tools/list');\n server.server.removeRequestHandler('tools/call');\n server.server.setRequestHandler(\n 'tools/list',\n async (_request: object, sdkContext: ServerContext): Promise<ListToolsResult> =>\n this.handleListTools(authentication, sdkContext),\n );\n server.server.setRequestHandler(\n 'tools/call',\n async (request: CallToolRequest, sdkContext: ServerContext): Promise<CallToolResult> =>\n this.handleCallTool(server, request, authentication, sdkContext),\n );\n return server;\n }\n\n /** Entry point #2: tools/list has no tool-result channel, so failures are JSON-RPC errors. */\n private async handleListTools(\n authentication: McpPostAuthentication,\n sdkContext: ServerContext,\n ): Promise<ListToolsResult> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- MCP tools/list entry point; delegates only to WpMcpErrorTranslator\n try {\n return this.listTools(authentication.credential);\n } catch (err: unknown) {\n const error = toError(err);\n throw this.translator.toProtocolError(error, this.sdkScope(sdkContext, 'tools/list'));\n }\n }\n\n private listTools(credential: VerifiedMcpCredential): ListToolsResult {\n const tools: Tool[] = [];\n for (const tool of this.requireRegistry().tools) {\n if (tool.isVisibleTo(credential.listingRoles)) tools.push(this.toolDefinition(tool));\n }\n return { tools };\n }\n\n private toolDefinition(tool: RegisteredMcpTool): Tool {\n return {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema as Tool['inputSchema'],\n outputSchema: tool.outputSchema as Tool['outputSchema'],\n annotations: tool.annotations,\n _meta: { webpiecesRegistryRevision: this.revision },\n };\n }\n\n /**\n * Entry point #3: an unknown tool is a JSON-RPC -32602; every failure after the tool is found\n * is an `isError: true` result, identically for local and remote bindings.\n */\n private async handleCallTool(\n server: WpSdkMcpServer,\n request: CallToolRequest,\n authentication: McpPostAuthentication,\n sdkContext: ServerContext,\n ): Promise<CallToolResult> {\n const name = request.params.name;\n const scope = this.sdkScope(sdkContext, 'tools/call', name);\n const tool = this.requireRegistry().find(name);\n if (!tool) throw this.translator.unknownTool(name, scope);\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- MCP tools/call entry point; delegates only to WpMcpErrorTranslator\n try {\n const result = await this.callTool(tool, request, authentication, sdkContext);\n result._meta = this.translator.resultMeta(scope.requestId);\n return server.server.projectCallToolResult(\n result,\n tool.outputSchema as Record<string, DtoValue>,\n );\n } catch (err: unknown) {\n const error = toError(err);\n return this.translator.toToolResult(error, scope);\n }\n }\n\n /** Everything a tool call does. No catch in here: failures propagate to handleCallTool. */\n private async callTool(\n tool: RegisteredMcpTool,\n request: CallToolRequest,\n authentication: McpPostAuthentication,\n sdkContext: ServerContext,\n ): Promise<CallToolResult> {\n const credential = authentication.credential;\n const endpointJwt =\n tool.binding.topology === 'local'\n ? await this.mintEndpointJwt(tool, authentication)\n : undefined;\n const invocation = new McpInvocationContext(\n sdkContext.mcpReq.id,\n tool.name,\n credential.subject,\n credential.listingRoles,\n AbortSignal.any([sdkContext.mcpReq.signal, authentication.disconnectSignal]),\n this.progressReporter(sdkContext),\n );\n const args = (request.params.arguments ?? {}) as DtoValue;\n const value = await this.dispatcher.call(\n tool,\n args,\n credential,\n invocation,\n endpointJwt?.token,\n );\n const outputFailure = this.requireRegistry().schemaBuilder.validate(\n tool.responseClass,\n value,\n );\n if (outputFailure) {\n throw new ApiImplementationError(\n `MCP output schema violation for ${tool.apiClass.name}.${tool.methodName}: ${outputFailure.message}`,\n );\n }\n const structured = this.record(value);\n return {\n content: [{ type: 'text', text: JSON.stringify(structured) }],\n structuredContent: structured,\n };\n }\n\n private async mintEndpointJwt(\n tool: RegisteredMcpTool,\n authentication: McpPostAuthentication,\n ): Promise<MintedJwt> {\n const descriptor = new McpEndpointDescriptor(\n tool.name,\n tool.apiClass.name,\n tool.methodName,\n );\n const minted = await this.config.endpointJwtAuthority.mint(\n this.config.endpointMintRequest(authentication.credential, descriptor),\n );\n const now = Math.floor(Date.now() / 1000);\n if (minted.token === authentication.accessToken) {\n throw new ApiImplementationError(\n `MCP access-token passthrough is forbidden (endpoint JWT for ${tool.name}).`,\n );\n }\n if (\n minted.expiresAtEpochSeconds <= now ||\n minted.expiresAtEpochSeconds - now > this.config.maxEndpointJwtLifetimeSeconds\n ) {\n throw new ApiImplementationError(\n `MCP endpoint JWT lifetime is invalid for ${tool.name}.`,\n );\n }\n return minted;\n }\n\n private validateCredential(credential: VerifiedMcpCredential): void {\n const now = Math.floor(Date.now() / 1000);\n const reject = (reason: string): never => {\n throw new ApiUnauthorizedError(`MCP access token rejected: ${reason}`);\n };\n if (credential.subject.trim() === '') reject('no subject');\n const stamps = [\n credential.issuedAtEpochSeconds,\n credential.expiresAtEpochSeconds,\n credential.accountValidatedAtEpochSeconds,\n ];\n if (!stamps.every(Number.isFinite)) reject('security timestamps must be finite');\n if (credential.resource !== this.config.resource) reject('resource mismatch');\n if (!this.config.authorizationServers.includes(credential.issuer)) {\n reject('issuer is not trusted');\n }\n if (credential.issuedAtEpochSeconds > now || credential.expiresAtEpochSeconds <= now) {\n reject('outside its valid time window');\n }\n const lifetime = credential.expiresAtEpochSeconds - credential.issuedAtEpochSeconds;\n if (lifetime > MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS) reject('lifetime exceeds 30 days');\n for (const scope of this.config.requiredScopes) {\n if (!credential.scopes.includes(scope)) reject(`missing required scope '${scope}'`);\n }\n const validatedAge = now - credential.accountValidatedAtEpochSeconds;\n if (\n credential.accountValidatedAtEpochSeconds > now ||\n validatedAge > this.config.maxAccountValidationAgeSeconds\n ) {\n reject('account authorization state is not fresh enough for dispatch');\n }\n }\n\n private authInfo(authentication: McpPostAuthentication): AuthInfo {\n const credential = authentication.credential;\n return {\n token: authentication.accessToken,\n clientId: credential.subject,\n scopes: [...credential.scopes],\n expiresAt: credential.expiresAtEpochSeconds,\n resource: new URL(credential.resource),\n extra: { webpiecesAuthentication: authentication },\n };\n }\n\n private progressReporter(context: ServerContext): McpProgressReporter | undefined {\n const token = context.mcpReq._meta?.progressToken;\n if (token === undefined) return undefined;\n return async (progress: number, total?: number, message?: string): Promise<void> => {\n await context.mcpReq.notify({\n method: 'notifications/progress',\n params: this.progressParams(token, progress, total, message),\n });\n };\n }\n\n private progressParams(\n progressToken: ProgressToken,\n progress: number,\n total?: number,\n message?: string,\n ): Record<string, DtoValue> {\n const params: Record<string, DtoValue> = { progressToken, progress };\n if (total !== undefined) params['total'] = total;\n if (message !== undefined) params['message'] = message;\n return params;\n }\n\n private requestId(): string {\n return RequestContext.getUntrusted(WebpiecesCoreHeaders.REQUEST_ID) ?? 'missing-request-id';\n }\n\n private sdkScope(context: ServerContext, method: string, toolName?: string): McpFailureScope {\n return new McpFailureScope(this.requestId(), context.mcpReq.id, method, toolName);\n }\n\n private httpScope(req: Request): McpFailureScope {\n const body = this.bodyObject(req.body);\n const id = body?.['id'];\n const method = body?.['method'];\n const params = this.bodyObject(body?.['params']);\n const name = params?.['name'];\n return new McpFailureScope(\n this.requestId(),\n typeof id === 'string' || typeof id === 'number' ? id : null,\n typeof method === 'string' ? method : 'unknown',\n typeof name === 'string' ? name : undefined,\n );\n }\n\n private challenge(): string {\n return `Bearer resource_metadata=\"${this.config.resource}\"`;\n }\n\n private bodyObject(value: DtoValue | undefined): Record<string, DtoValue> | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;\n return value as Record<string, DtoValue>;\n }\n\n private hasProgressToken(body: DtoValue): boolean {\n const params = this.bodyObject(this.bodyObject(body)?.['params']);\n return this.bodyObject(params?.['_meta'])?.['progressToken'] !== undefined;\n }\n\n private toHttpRequest(req: Request): HttpRequest {\n const headers = new Map<string, string[]>();\n for (const [name, value] of Object.entries(req.headers)) {\n if (typeof value === 'string') headers.set(name, [value]);\n else if (Array.isArray(value)) headers.set(name, value);\n }\n return new HttpRequest(req.method, req.originalUrl || req.path, headers);\n }\n\n private calculateRegistryRevision(registry: McpToolRegistry): string {\n const surface = registry.tools.map((tool: RegisteredMcpTool) => [\n tool.name,\n tool.description,\n tool.inputSchema,\n tool.outputSchema,\n tool.annotations,\n ]);\n return createHash('sha256').update(JSON.stringify(surface)).digest('hex').slice(0, 12);\n }\n\n private requireRegistry(): McpToolRegistry {\n if (!this.registry) throw new Error('Call WpMcpServer.bind(...) before serving requests.');\n return this.registry;\n }\n\n private record(value: DtoValue): Record<string, DtoValue> {\n const jsonValue = JSON.stringify(value);\n if (jsonValue === undefined) {\n throw new ApiImplementationError('MCP structured content is not JSON serializable.');\n }\n const parsed = JSON.parse(jsonValue) as DtoValue;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return { value: parsed };\n }\n return parsed as Record<string, DtoValue>;\n }\n}\n"]}
package/src/index.d.ts CHANGED
@@ -11,3 +11,4 @@ export type { McpDeploymentMode } from './McpDeployment';
11
11
  export { McpApiDispatcher } from './McpApiDispatcher';
12
12
  export { WpMcpServer } from './WpMcpServer';
13
13
  export { MCP_REQUEST_ID_META_KEY, McpErrorData, McpFailureScope, McpHttpErrorBody, McpHttpErrorDetail, ModelVisibleToolError, WpMcpErrorTranslator, } from './WpMcpErrorTranslator';
14
+ export type { McpErrorTranslators } from './WpMcpErrorTranslator';
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/index.ts"],"names":[],"mappings":";;;AAAA,qCASmB;AARf,gIAAA,qCAAqC,OAAA;AACrC,iIAAA,sCAAsC,OAAA;AACtC,gIAAA,qCAAqC,OAAA;AACrC,gHAAA,qBAAqB,OAAA;AACrB,+GAAA,oBAAoB,OAAA;AACpB,uHAAA,4BAA4B,OAAA;AAC5B,gHAAA,qBAAqB,OAAA;AACrB,4GAAA,iBAAiB,OAAA;AAGrB,qDAAuE;AAA9D,kHAAA,eAAe,OAAA;AAAE,oHAAA,iBAAiB,OAAA;AAC3C,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AAEtB,+DAAsF;AAA7E,8HAAA,sBAAsB,OAAA;AAAE,4HAAA,oBAAoB,OAAA;AAErD,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AAEtB,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AACzB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,+DAQgC;AAP5B,+HAAA,uBAAuB,OAAA;AACvB,oHAAA,YAAY,OAAA;AACZ,uHAAA,eAAe,OAAA;AACf,wHAAA,gBAAgB,OAAA;AAChB,0HAAA,kBAAkB,OAAA;AAClB,6HAAA,qBAAqB,OAAA;AACrB,4HAAA,oBAAoB,OAAA","sourcesContent":["export {\n MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS,\n MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS,\n MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS,\n McpEndpointDescriptor,\n MintedMcpAccessToken,\n McpProtectedResourceMetadata,\n VerifiedMcpCredential,\n WpMcpServerConfig,\n} from './McpAuth';\nexport type { McpAccessTokenAuthority, McpEndpointMintRequestFactory } from './McpAuth';\nexport { McpToolRegistry, RegisteredMcpTool } from './McpToolRegistry';\nexport { McpApiBinding } from './McpApiBinding';\nexport type { McpBindingTopology } from './McpApiBinding';\nexport { MCP_INVOCATION_CONTEXT, McpInvocationContext } from './McpInvocationContext';\nexport type { McpProgressReporter } from './McpInvocationContext';\nexport { McpBindOptions } from './McpBindOptions';\nexport { McpDeployment } from './McpDeployment';\nexport type { McpDeploymentMode } from './McpDeployment';\nexport { McpApiDispatcher } from './McpApiDispatcher';\nexport { WpMcpServer } from './WpMcpServer';\nexport {\n MCP_REQUEST_ID_META_KEY,\n McpErrorData,\n McpFailureScope,\n McpHttpErrorBody,\n McpHttpErrorDetail,\n ModelVisibleToolError,\n WpMcpErrorTranslator,\n} from './WpMcpErrorTranslator';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/index.ts"],"names":[],"mappings":";;;AAAA,qCASmB;AARf,gIAAA,qCAAqC,OAAA;AACrC,iIAAA,sCAAsC,OAAA;AACtC,gIAAA,qCAAqC,OAAA;AACrC,gHAAA,qBAAqB,OAAA;AACrB,+GAAA,oBAAoB,OAAA;AACpB,uHAAA,4BAA4B,OAAA;AAC5B,gHAAA,qBAAqB,OAAA;AACrB,4GAAA,iBAAiB,OAAA;AAGrB,qDAAuE;AAA9D,kHAAA,eAAe,OAAA;AAAE,oHAAA,iBAAiB,OAAA;AAC3C,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AAEtB,+DAAsF;AAA7E,8HAAA,sBAAsB,OAAA;AAAE,4HAAA,oBAAoB,OAAA;AAErD,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AAEtB,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AACzB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,+DAQgC;AAP5B,+HAAA,uBAAuB,OAAA;AACvB,oHAAA,YAAY,OAAA;AACZ,uHAAA,eAAe,OAAA;AACf,wHAAA,gBAAgB,OAAA;AAChB,0HAAA,kBAAkB,OAAA;AAClB,6HAAA,qBAAqB,OAAA;AACrB,4HAAA,oBAAoB,OAAA","sourcesContent":["export {\n MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS,\n MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS,\n MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS,\n McpEndpointDescriptor,\n MintedMcpAccessToken,\n McpProtectedResourceMetadata,\n VerifiedMcpCredential,\n WpMcpServerConfig,\n} from './McpAuth';\nexport type { McpAccessTokenAuthority, McpEndpointMintRequestFactory } from './McpAuth';\nexport { McpToolRegistry, RegisteredMcpTool } from './McpToolRegistry';\nexport { McpApiBinding } from './McpApiBinding';\nexport type { McpBindingTopology } from './McpApiBinding';\nexport { MCP_INVOCATION_CONTEXT, McpInvocationContext } from './McpInvocationContext';\nexport type { McpProgressReporter } from './McpInvocationContext';\nexport { McpBindOptions } from './McpBindOptions';\nexport { McpDeployment } from './McpDeployment';\nexport type { McpDeploymentMode } from './McpDeployment';\nexport { McpApiDispatcher } from './McpApiDispatcher';\nexport { WpMcpServer } from './WpMcpServer';\nexport {\n MCP_REQUEST_ID_META_KEY,\n McpErrorData,\n McpFailureScope,\n McpHttpErrorBody,\n McpHttpErrorDetail,\n ModelVisibleToolError,\n WpMcpErrorTranslator,\n} from './WpMcpErrorTranslator';\nexport type { McpErrorTranslators } from './WpMcpErrorTranslator';\n"]}