@webpieces/mcp-server 0.4.863 → 0.4.865

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
@@ -5,9 +5,10 @@ and output JSON Schema — the build extracts them from the contract source and
5
5
  `mcp-<ContractClass>-tools.json` per contract — invokes the normal Webpieces filter/controller path, and turns exceptions into safe model-visible
6
6
  MCP results.
7
7
 
8
- `@WpMcpTool` is an opt-in and every tool must also declare `@WpMcpAuthJwt`. MCP user
9
- authorization and endpoint transport authentication are deliberately separate. The MCP adapter
10
- never accepts trusted context values from tool arguments or unverified headers.
8
+ `@WpMcpTool` opts an endpoint into the protocol. Every endpoint declares canonical
9
+ `@WpAuth` credentials and common `@WpAuthorization` policy. The same policy governs tool visibility,
10
+ schema lookup, dispatch and receiving endpoint execution. The MCP adapter never accepts trusted
11
+ context from tool arguments or unverified headers.
11
12
 
12
13
  ```ts
13
14
  interface FindOrderRequest {
@@ -24,8 +25,8 @@ interface FindOrderResponse {
24
25
  @ApiType(SVC_TO_SVC, MCP)
25
26
  abstract class OrdersApi {
26
27
  /** Find one order owned by the signed-in user. */
27
- @WpMcpAuthJwt({ allRolesAllowed: true })
28
- @WpAuthJwt({ allRolesAllowed: true })
28
+ @WpAuth([jwt()])
29
+ @WpAuthorization({ authType: AuthorizationType.ALL_USERS })
29
30
  @Endpoint(POST, '/find', READ, RPC)
30
31
  @WpMcpTool('orders_find')
31
32
  find(request: FindOrderRequest): Promise<FindOrderResponse> {
@@ -101,13 +102,12 @@ settings can be swapped, and a future setting is an additive setter rather than
101
102
  change. Expect to supply the type arguments explicitly:
102
103
 
103
104
  ```ts
104
- const config = new WpMcpServerConfig<MyGrant, MyMintRequest>()
105
+ const config = new WpMcpServerConfig<MyGrant>()
105
106
  .setName('my-server')
106
107
  .setVersion('1.0.0')
107
108
  .setResource('https://api.example.com/mcp')
108
109
  .setAccessTokenAuthority(authority)
109
- .setEndpointJwtAuthority(jwtHook)
110
- .setEndpointMintRequest((credential) => new MyMintRequest(credential.subject))
110
+ .setAuthorizationService(router.authorizationService())
111
111
  .setAuthorizationServers(['https://login.example.com'])
112
112
  .setRequiredScopes(['tools'])
113
113
  .setMaxAccountValidationAgeSeconds(15 * 60);
@@ -176,13 +176,13 @@ supplied `_meta.progressToken`) a `reportProgress(...)` callback. Progress remai
176
176
  one request and causes the official handler to select request-scoped SSE; closing that response
177
177
  aborts the same signal. The context key has no HTTP header and is never propagated as caller input.
178
178
 
179
- For a local binding the API method must combine `@WpMcpAuthJwt` with `@WpAuthJwt`. Before every tool
180
- authorized, schema-valid call the bridge asks the application `JwtHook` to mint a distinct short-lived endpoint JWT and invokes
181
- `ApiFactory.createInvocationApiClient`, preserving `JwtHook`, `LogApiFilter`, and `AuthFilter`. Literal MCP-token
182
- passthrough is rejected. For a remote binding the endpoint must use `@WpAuthOidc`; the generated Node
183
- client supplies its service credential and propagates only trusted delegated context established by
184
- the MCP access-token authority. Neither an external MCP bearer nor a browser session JWT is sent to
185
- the downstream endpoint.
179
+ Local bindings require explicit tool exposure and valid common authorization. They pass a token-free
180
+ `InvocationAuthentication` through `ApiFactory.createInvocationApiClient`, preserving the ordinary
181
+ logging, authentication and authorization filters. The proof identifies the actual endpoint and
182
+ original verified ingress scope. No endpoint JWT is minted or parsed and no GUI `JWT_HOOK` is required.
183
+ Remote bindings declare `oidc(...)` or `sharedSecret(...)` network credentials. The generated Node
184
+ client chooses its service mechanism before forwarding verified delegated facts; the MCP bearer never
185
+ becomes a downstream credential.
186
186
 
187
187
  `McpDeployment.singleProcess()` uses an in-memory invalidation bus and makes that limit explicit.
188
188
  Distributed deployments must use `McpDeployment.distributed(sharedBus)`. List responses have a bounded
@@ -194,32 +194,35 @@ The authority verifies issuer, signature/token state, expiry, scopes, the exact
194
194
  current account state. Neither served era keeps a session here, so the bearer is verified once per POST
195
195
  at the `bind(...)` HTTP boundary, before the SDK is involved; tool handlers never re-verify it. A
196
196
  rejected token must be thrown as `ApiUnauthorizedError` (answered 401 + `WWW-Authenticate`); any
197
- other throw from the authority is an implementation failure (500). Endpoint JWTs are capped at one
198
- hour and MCP access tokens at 30 days.
197
+ other throw from the authority is an implementation failure (500). MCP access tokens are capped at 30 days.
199
198
 
200
199
  ## Principal, policy and request context
201
200
 
202
201
  `VerifiedMcpCredential.caller` is the application's `AuthenticatedCaller` resolved from current account
203
202
  facts. `principalRoles` is derived from that caller, a public hard cut from the old listing-role field.
204
- Use the same application mapping for this caller and the endpoint JWT mint payload. Security metadata
203
+ The framework publishes its canonical USER_ID and JSON-array USER_ROLES; custom trusted facts come from the verified caller entries. Security metadata
205
204
  (issuer, exact resource, scopes, issuance/expiry and account validation time) remains on the credential.
206
205
 
207
206
  The framework publishes the caller's trusted entries and `llm` surface immediately after verification,
208
207
  then reconciles inbound trusted headers using the same operation as ordinary HTTP. Contradictory or
209
- unvouched identity/surface headers are rejected. `JwtHook.authorizeJwt` governs the request's authorized
208
+ unvouched identity/surface headers are rejected. The common `AuthorizationService` governs the request's authorized
210
209
  tool view before SDK processing; only explicit `ApiForbiddenError` denials hide tools. Policy/datastore
211
210
  failures propagate to the safe ingress boundary. Documentation, synchronous SDK schema lookup and call
212
211
  name resolution all use that view. The dispatcher independently checks the same policy before input
213
- validation and bridge issuance. A hidden tool is indistinguishable from an unknown name and triggers no
214
- schema validation, JWT issuance or binding invocation.
212
+ validation and invocation. A hidden tool is indistinguishable from an unknown name and triggers no
213
+ schema validation or binding invocation.
215
214
 
216
215
  Each HTTP POST has one transport-owned `RequestContext.run`, including malformed-body failures. Local
217
216
  invocation opens no detached/nested scope and retains the original transport request, request/action IDs,
218
217
  custom context and downstream response-header accumulation. Its immutable `InvocationAuthentication`
219
218
  is carried on that invocation's proxy/MethodMeta, never on a mutable ambient credential slot. Normal
220
- endpoint JWT parsing and policy still run, and parsed user/roles/trusted entries must match the ingress
221
- principal. GUI proxies continue reading the transport bearer. Remote bindings still use explicit OIDC
222
- clients and propagate the verified principal's trusted entries; another HTTP process has its own scope.
219
+ receiving endpoint authorization runs against the same canonical trusted facts. A captured/restored
220
+ snapshot cannot reuse the local proof. GUI HTTP requests verify their own bearer normally. Remote
221
+ bindings authenticate their own hop before accepting delegated facts; another HTTP process has its
222
+ own scope. `SERVICE_ONLY` requires actual current-hop machine evidence, even when the surface is `llm`.
223
+
224
+ See [the authorization migration and release order](../../../docs/authorization-migration.md).
225
+
223
226
 
224
227
  ## Error boundary
225
228
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/mcp-server",
3
- "version": "0.4.863",
3
+ "version": "0.4.865",
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.863",
28
- "@webpieces/core-util": "0.4.863",
29
- "@webpieces/http-routing": "0.4.863",
30
- "@webpieces/http-server": "0.4.863",
27
+ "@webpieces/core-context": "0.4.865",
28
+ "@webpieces/core-util": "0.4.865",
29
+ "@webpieces/http-routing": "0.4.865",
30
+ "@webpieces/http-server": "0.4.865",
31
31
  "express": "5.1.0",
32
32
  "reflect-metadata": "0.2.2",
33
33
  "tslib": "2.8.1"
34
34
  },
35
35
  "devDependencies": {
36
- "@webpieces/http-client-node": "0.4.863"
36
+ "@webpieces/http-client-node": "0.4.865"
37
37
  }
38
38
  }
@@ -1,14 +1,15 @@
1
1
  import { DtoValue } from '@webpieces/core-util';
2
- import { ApiFactory, ClassType, InvocationAuthentication } from '@webpieces/http-routing';
2
+ import { ApiFactory, AuthorizationService, ClassType, InvocationAuthentication } from '@webpieces/http-routing';
3
3
  export type McpBindingTopology = 'local' | 'remote';
4
4
  /** Explicit contract-to-invoker binding; MCP never discovers implementations from a router. */
5
5
  export declare class McpApiBinding<TApi extends object = object> {
6
6
  readonly api: ClassType<TApi>;
7
7
  readonly topology: McpBindingTopology;
8
8
  private readonly provider;
9
+ private readonly receivingPolicy?;
9
10
  private constructor();
10
11
  static local<TApi extends object>(api: ClassType<TApi>, apiFactory: ApiFactory): McpApiBinding<TApi>;
11
12
  static remote<TApi extends object>(api: ClassType<TApi>, provider: () => TApi): McpApiBinding<TApi>;
12
13
  invoke(methodName: string, request: DtoValue, authentication?: InvocationAuthentication): Promise<DtoValue>;
13
- validateMethod(methodName: string): void;
14
+ validateMethod(methodName: string, policy: AuthorizationService): void;
14
15
  }
@@ -7,10 +7,12 @@ class McpApiBinding {
7
7
  api;
8
8
  topology;
9
9
  provider;
10
- constructor(api, topology, provider) {
10
+ receivingPolicy;
11
+ constructor(api, topology, provider, receivingPolicy) {
11
12
  this.api = api;
12
13
  this.topology = topology;
13
14
  this.provider = provider;
15
+ this.receivingPolicy = receivingPolicy;
14
16
  }
15
17
  // webpieces-disable no-function-outside-class -- explicit public factory mirrors the binding API
16
18
  static local(api, apiFactory) {
@@ -18,7 +20,7 @@ class McpApiBinding {
18
20
  if (!authentication)
19
21
  throw new Error('Local MCP invocation requires explicit authentication.');
20
22
  return apiFactory.createInvocationApiClient(api, authentication);
21
- });
23
+ }, apiFactory.authorizationService());
22
24
  }
23
25
  // webpieces-disable no-function-outside-class -- explicit public factory mirrors the binding API
24
26
  static remote(api, provider) {
@@ -32,13 +34,14 @@ class McpApiBinding {
32
34
  }
33
35
  return method.call(client, request);
34
36
  }
35
- validateMethod(methodName) {
36
- const auth = (0, core_util_1.getAuthMeta)(this.api, methodName)?.mode;
37
- const expected = this.topology === 'local' ? 'jwt' : 'oidc';
38
- if (auth?.kind !== expected) {
39
- throw new Error(`MCP ${this.topology} binding ${this.api.name}.${methodName} requires ` +
40
- `@${expected === 'jwt' ? 'WpAuthJwt' : 'WpAuthOidc'}, but declares ` +
41
- `${auth ? `'${auth.kind}'` : 'no HTTP auth'}.`);
37
+ validateMethod(methodName, policy) {
38
+ if (this.topology === 'local' && this.receivingPolicy !== policy) {
39
+ throw new Error('Local MCP visibility and invocation must use the receiving ApiFactory authorizationService().');
40
+ }
41
+ (0, core_util_1.assertEveryEndpointHasAuthMode)(this.api);
42
+ const methods = (0, core_util_1.getAuthMeta)(this.api, methodName)?.methods;
43
+ if (this.topology === 'remote' && !methods?.some((method) => method.kind === 'oidc' || method.kind === 'shared-secret')) {
44
+ throw new Error(`MCP remote binding ${this.api.name}.${methodName} requires oidc(...) or sharedSecret(...) network ingress.`);
42
45
  }
43
46
  }
44
47
  }
@@ -1 +1 @@
1
- {"version":3,"file":"McpApiBinding.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpApiBinding.ts"],"names":[],"mappings":";;;AAAA,oDAA6D;AAU7D,+FAA+F;AAC/F,MAAa,aAAa;IAEF;IACA;IACC;IAHrB,YACoB,GAAoB,EACpB,QAA4B,EAC3B,QAA6D;QAF9D,QAAG,GAAH,GAAG,CAAiB;QACpB,aAAQ,GAAR,QAAQ,CAAoB;QAC3B,aAAQ,GAAR,QAAQ,CAAqD;IAC/E,CAAC;IAEJ,iGAAiG;IACjG,MAAM,CAAC,KAAK,CACR,GAAoB,EACpB,UAAsB;QAEtB,OAAO,IAAI,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,cAAc,EAAE,EAAE;YACtD,IAAI,CAAC,cAAc;gBACf,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;YAC9E,OAAO,UAAU,CAAC,yBAAyB,CAAC,GAAY,EAAE,cAAc,CAAC,CAAC;QAC9E,CAAC,CAAC,CAAC;IACP,CAAC;IAED,iGAAiG;IACjG,MAAM,CAAC,MAAM,CACT,GAAoB,EACpB,QAAoB;QAEpB,OAAO,IAAI,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,CACF,UAAkB,EAClB,OAAiB,EACjB,cAAyC;QAEzC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAmB,CAAC;QAC/D,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;QAClC,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CACX,4BAA4B,IAAI,CAAC,GAAG,CAAC,IAAI,mBAAmB,UAAU,IAAI,CAC7E,CAAC;QACN,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAsB,CAAC;IAC7D,CAAC;IAED,cAAc,CAAC,UAAkB;QAC7B,MAAM,IAAI,GAAG,IAAA,uBAAW,EAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,IAAI,CAAC;QACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QAC5D,IAAI,IAAI,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACX,OAAO,IAAI,CAAC,QAAQ,YAAY,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,UAAU,YAAY;gBACnE,IAAI,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,iBAAiB;gBACpE,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,cAAc,GAAG,CACrD,CAAC;QACN,CAAC;IACL,CAAC;CACJ;AArDD,sCAqDC","sourcesContent":["import { DtoValue, getAuthMeta } from '@webpieces/core-util';\nimport {\n ApiClientProxy,\n ApiFactory,\n ClassType,\n InvocationAuthentication,\n} from '@webpieces/http-routing';\n\nexport type McpBindingTopology = 'local' | 'remote';\n\n/** Explicit contract-to-invoker binding; MCP never discovers implementations from a router. */\nexport class McpApiBinding<TApi extends object = object> {\n private constructor(\n public readonly api: ClassType<TApi>,\n public readonly topology: McpBindingTopology,\n private readonly provider: (authentication?: InvocationAuthentication) => TApi,\n ) {}\n\n // webpieces-disable no-function-outside-class -- explicit public factory mirrors the binding API\n static local<TApi extends object>(\n api: ClassType<TApi>,\n apiFactory: ApiFactory,\n ): McpApiBinding<TApi> {\n return new McpApiBinding(api, 'local', (authentication) => {\n if (!authentication)\n throw new Error('Local MCP invocation requires explicit authentication.');\n return apiFactory.createInvocationApiClient(api as never, authentication);\n });\n }\n\n // webpieces-disable no-function-outside-class -- explicit public factory mirrors the binding API\n static remote<TApi extends object>(\n api: ClassType<TApi>,\n provider: () => TApi,\n ): McpApiBinding<TApi> {\n return new McpApiBinding(api, 'remote', provider);\n }\n\n invoke(\n methodName: string,\n request: DtoValue,\n authentication?: InvocationAuthentication,\n ): Promise<DtoValue> {\n const client = this.provider(authentication) as ApiClientProxy;\n const method = client[methodName];\n if (typeof method !== 'function') {\n throw new Error(\n `MCP binding provider for ${this.api.name} has no method '${methodName}'.`,\n );\n }\n return method.call(client, request) as Promise<DtoValue>;\n }\n\n validateMethod(methodName: string): void {\n const auth = getAuthMeta(this.api, methodName)?.mode;\n const expected = this.topology === 'local' ? 'jwt' : 'oidc';\n if (auth?.kind !== expected) {\n throw new Error(\n `MCP ${this.topology} binding ${this.api.name}.${methodName} requires ` +\n `@${expected === 'jwt' ? 'WpAuthJwt' : 'WpAuthOidc'}, but declares ` +\n `${auth ? `'${auth.kind}'` : 'no HTTP auth'}.`,\n );\n }\n }\n}\n"]}
1
+ {"version":3,"file":"McpApiBinding.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpApiBinding.ts"],"names":[],"mappings":";;;AAAA,oDAAuG;AAWvG,+FAA+F;AAC/F,MAAa,aAAa;IAEF;IACA;IACC;IACA;IAJrB,YACoB,GAAoB,EACpB,QAA4B,EAC3B,QAA6D,EAC7D,eAAsC;QAHvC,QAAG,GAAH,GAAG,CAAiB;QACpB,aAAQ,GAAR,QAAQ,CAAoB;QAC3B,aAAQ,GAAR,QAAQ,CAAqD;QAC7D,oBAAe,GAAf,eAAe,CAAuB;IACxD,CAAC;IAEJ,iGAAiG;IACjG,MAAM,CAAC,KAAK,CACR,GAAoB,EACpB,UAAsB;QAEtB,OAAO,IAAI,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,cAAc,EAAE,EAAE;YACtD,IAAI,CAAC,cAAc;gBACf,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;YAC9E,OAAO,UAAU,CAAC,yBAAyB,CAAC,GAAY,EAAE,cAAc,CAAC,CAAC;QAC9E,CAAC,EAAE,UAAU,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,iGAAiG;IACjG,MAAM,CAAC,MAAM,CACT,GAAoB,EACpB,QAAoB;QAEpB,OAAO,IAAI,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,CACF,UAAkB,EAClB,OAAiB,EACjB,cAAyC;QAEzC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAmB,CAAC;QAC/D,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;QAClC,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CACX,4BAA4B,IAAI,CAAC,GAAG,CAAC,IAAI,mBAAmB,UAAU,IAAI,CAC7E,CAAC;QACN,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAsB,CAAC;IAC7D,CAAC;IAED,cAAc,CAAC,UAAkB,EAAE,MAA4B;QAC3D,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO,IAAI,IAAI,CAAC,eAAe,KAAK,MAAM,EAAE,CAAC;YAC/D,MAAM,IAAI,KAAK,CAAC,+FAA+F,CAAC,CAAC;QACrH,CAAC;QACD,IAAA,0CAA8B,EAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,IAAA,uBAAW,EAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,OAAO,CAAC;QAC3D,IAAI,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,MAAgB,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,eAAe,CAAC,EAAE,CAAC;YAChI,MAAM,IAAI,KAAK,CAAC,sBAAsB,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,UAAU,2DAA2D,CAAC,CAAC;QAClI,CAAC;IACL,CAAC;CACJ;AArDD,sCAqDC","sourcesContent":["import { AuthMode, DtoValue, getAuthMeta, assertEveryEndpointHasAuthMode } from '@webpieces/core-util';\nimport {\n ApiClientProxy,\n ApiFactory,\n AuthorizationService,\n ClassType,\n InvocationAuthentication,\n} from '@webpieces/http-routing';\n\nexport type McpBindingTopology = 'local' | 'remote';\n\n/** Explicit contract-to-invoker binding; MCP never discovers implementations from a router. */\nexport class McpApiBinding<TApi extends object = object> {\n private constructor(\n public readonly api: ClassType<TApi>,\n public readonly topology: McpBindingTopology,\n private readonly provider: (authentication?: InvocationAuthentication) => TApi,\n private readonly receivingPolicy?: AuthorizationService,\n ) {}\n\n // webpieces-disable no-function-outside-class -- explicit public factory mirrors the binding API\n static local<TApi extends object>(\n api: ClassType<TApi>,\n apiFactory: ApiFactory,\n ): McpApiBinding<TApi> {\n return new McpApiBinding(api, 'local', (authentication) => {\n if (!authentication)\n throw new Error('Local MCP invocation requires explicit authentication.');\n return apiFactory.createInvocationApiClient(api as never, authentication);\n }, apiFactory.authorizationService());\n }\n\n // webpieces-disable no-function-outside-class -- explicit public factory mirrors the binding API\n static remote<TApi extends object>(\n api: ClassType<TApi>,\n provider: () => TApi,\n ): McpApiBinding<TApi> {\n return new McpApiBinding(api, 'remote', provider);\n }\n\n invoke(\n methodName: string,\n request: DtoValue,\n authentication?: InvocationAuthentication,\n ): Promise<DtoValue> {\n const client = this.provider(authentication) as ApiClientProxy;\n const method = client[methodName];\n if (typeof method !== 'function') {\n throw new Error(\n `MCP binding provider for ${this.api.name} has no method '${methodName}'.`,\n );\n }\n return method.call(client, request) as Promise<DtoValue>;\n }\n\n validateMethod(methodName: string, policy: AuthorizationService): void {\n if (this.topology === 'local' && this.receivingPolicy !== policy) {\n throw new Error('Local MCP visibility and invocation must use the receiving ApiFactory authorizationService().');\n }\n assertEveryEndpointHasAuthMode(this.api);\n const methods = getAuthMeta(this.api, methodName)?.methods;\n if (this.topology === 'remote' && !methods?.some((method: AuthMode) => method.kind === 'oidc' || method.kind === 'shared-secret')) {\n throw new Error(`MCP remote binding ${this.api.name}.${methodName} requires oidc(...) or sharedSecret(...) network ingress.`);\n }\n }\n}\n"]}
@@ -1,10 +1,10 @@
1
1
  import { DtoValue } from '@webpieces/core-util';
2
- import { JwtHook } from '@webpieces/http-routing';
2
+ import { AuthorizationService } from '@webpieces/http-routing';
3
3
  import { RegisteredMcpTool } from './McpToolRegistry';
4
4
  import { VerifiedMcpCredential } from './McpAuth';
5
5
  import { McpInvocationContext } from './McpInvocationContext';
6
6
  /** Authorize, validate, then issue a local credential and invoke the ordinary filters. No scope or catch. */
7
7
  export declare class McpApiDispatcher {
8
8
  private readonly schemas;
9
- call<T>(tool: RegisteredMcpTool, requestDto: DtoValue, credential: VerifiedMcpCredential, invocation: McpInvocationContext, policy: JwtHook<T>, mintEndpointToken: () => Promise<string>): Promise<DtoValue>;
9
+ call(tool: RegisteredMcpTool, requestDto: DtoValue, credential: VerifiedMcpCredential, invocation: McpInvocationContext, policy: AuthorizationService): Promise<DtoValue>;
10
10
  }
@@ -9,14 +9,14 @@ const McpInvocationContext_1 = require("./McpInvocationContext");
9
9
  /** Authorize, validate, then issue a local credential and invoke the ordinary filters. No scope or catch. */
10
10
  class McpApiDispatcher {
11
11
  schemas = new core_util_1.ApiJsonSchemaValidator();
12
- async call(tool, requestDto, credential, invocation, policy, mintEndpointToken) {
13
- await new McpToolPolicy_1.McpToolPolicy(policy).require(credential.caller, tool.mcpAuth.requirement, tool.name);
12
+ async call(tool, requestDto, credential, invocation, policy) {
13
+ await new McpToolPolicy_1.McpToolPolicy(policy).require(tool.authorization, tool.name);
14
14
  const inputFailure = this.schemas.validate(tool.inputSchema, requestDto);
15
15
  if (inputFailure) {
16
16
  throw new core_util_1.ApiBadRequestError(`MCP arguments for ${tool.name} did not match its published input schema: ${inputFailure.message}`, inputFailure.field, inputFailure.message);
17
17
  }
18
18
  const authentication = tool.binding.topology === 'local'
19
- ? new http_routing_1.InvocationAuthentication(await mintEndpointToken(), credential.caller)
19
+ ? new http_routing_1.InvocationAuthentication(tool.apiClass, tool.methodName)
20
20
  : undefined;
21
21
  core_context_1.RequestContext.putTrusted(McpInvocationContext_1.MCP_INVOCATION_CONTEXT, invocation);
22
22
  return tool.binding.invoke(tool.methodName, requestDto, authentication);
@@ -1 +1 @@
1
- {"version":3,"file":"McpApiDispatcher.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpApiDispatcher.ts"],"names":[],"mappings":";;;AAAA,oDAA4F;AAC5F,0DAAyD;AACzD,0DAA4E;AAC5E,mDAAgD;AAGhD,iEAAsF;AAEtF,6GAA6G;AAC7G,MAAa,gBAAgB;IACR,OAAO,GAAG,IAAI,kCAAsB,EAAE,CAAC;IAExD,KAAK,CAAC,IAAI,CACN,IAAuB,EACvB,UAAoB,EACpB,UAAiC,EACjC,UAAgC,EAChC,MAAkB,EAClB,iBAAwC;QAExC,MAAM,IAAI,6BAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CACnC,UAAU,CAAC,MAAM,EACjB,IAAI,CAAC,OAAO,CAAC,WAAW,EACxB,IAAI,CAAC,IAAI,CACZ,CAAC;QACF,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QACzE,IAAI,YAAY,EAAE,CAAC;YACf,MAAM,IAAI,8BAAkB,CACxB,qBAAqB,IAAI,CAAC,IAAI,8CAA8C,YAAY,CAAC,OAAO,EAAE,EAClG,YAAY,CAAC,KAAK,EAClB,YAAY,CAAC,OAAO,CACvB,CAAC;QACN,CAAC;QACD,MAAM,cAAc,GAChB,IAAI,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;YAC7B,CAAC,CAAC,IAAI,uCAAwB,CAAC,MAAM,iBAAiB,EAAE,EAAE,UAAU,CAAC,MAAM,CAAC;YAC5E,CAAC,CAAC,SAAS,CAAC;QACpB,6BAAc,CAAC,UAAU,CAAC,6CAAsB,EAAE,UAAU,CAAC,CAAC;QAC9D,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,cAAc,CAAC,CAAC;IAC5E,CAAC;CACJ;AA/BD,4CA+BC","sourcesContent":["import { ApiBadRequestError, ApiJsonSchemaValidator, DtoValue } from '@webpieces/core-util';\nimport { RequestContext } from '@webpieces/core-context';\nimport { InvocationAuthentication, JwtHook } from '@webpieces/http-routing';\nimport { McpToolPolicy } from './McpToolPolicy';\nimport { RegisteredMcpTool } from './McpToolRegistry';\nimport { VerifiedMcpCredential } from './McpAuth';\nimport { MCP_INVOCATION_CONTEXT, McpInvocationContext } from './McpInvocationContext';\n\n/** Authorize, validate, then issue a local credential and invoke the ordinary filters. No scope or catch. */\nexport class McpApiDispatcher {\n private readonly schemas = new ApiJsonSchemaValidator();\n\n async call<T>(\n tool: RegisteredMcpTool,\n requestDto: DtoValue,\n credential: VerifiedMcpCredential,\n invocation: McpInvocationContext,\n policy: JwtHook<T>,\n mintEndpointToken: () => Promise<string>,\n ): Promise<DtoValue> {\n await new McpToolPolicy(policy).require(\n credential.caller,\n tool.mcpAuth.requirement,\n tool.name,\n );\n const inputFailure = this.schemas.validate(tool.inputSchema, requestDto);\n if (inputFailure) {\n throw new ApiBadRequestError(\n `MCP arguments for ${tool.name} did not match its published input schema: ${inputFailure.message}`,\n inputFailure.field,\n inputFailure.message,\n );\n }\n const authentication =\n tool.binding.topology === 'local'\n ? new InvocationAuthentication(await mintEndpointToken(), credential.caller)\n : undefined;\n RequestContext.putTrusted(MCP_INVOCATION_CONTEXT, invocation);\n return tool.binding.invoke(tool.methodName, requestDto, authentication);\n }\n}\n"]}
1
+ {"version":3,"file":"McpApiDispatcher.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpApiDispatcher.ts"],"names":[],"mappings":";;;AAAA,oDAA4F;AAC5F,0DAAyD;AACzD,0DAAyF;AACzF,mDAAgD;AAGhD,iEAAsF;AAEtF,6GAA6G;AAC7G,MAAa,gBAAgB;IACR,OAAO,GAAG,IAAI,kCAAsB,EAAE,CAAC;IAExD,KAAK,CAAC,IAAI,CACN,IAAuB,EACvB,UAAoB,EACpB,UAAiC,EACjC,UAAgC,EAChC,MAA4B;QAE5B,MAAM,IAAI,6BAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CACnC,IAAI,CAAC,aAAa,EAClB,IAAI,CAAC,IAAI,CACZ,CAAC;QACF,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QACzE,IAAI,YAAY,EAAE,CAAC;YACf,MAAM,IAAI,8BAAkB,CACxB,qBAAqB,IAAI,CAAC,IAAI,8CAA8C,YAAY,CAAC,OAAO,EAAE,EAClG,YAAY,CAAC,KAAK,EAClB,YAAY,CAAC,OAAO,CACvB,CAAC;QACN,CAAC;QACD,MAAM,cAAc,GAChB,IAAI,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;YAC7B,CAAC,CAAC,IAAI,uCAAwB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC;YAC9D,CAAC,CAAC,SAAS,CAAC;QACpB,6BAAc,CAAC,UAAU,CAAC,6CAAsB,EAAE,UAAU,CAAC,CAAC;QAC9D,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,cAAc,CAAC,CAAC;IAC5E,CAAC;CACJ;AA7BD,4CA6BC","sourcesContent":["import { ApiBadRequestError, ApiJsonSchemaValidator, DtoValue } from '@webpieces/core-util';\nimport { RequestContext } from '@webpieces/core-context';\nimport { InvocationAuthentication, AuthorizationService } from '@webpieces/http-routing';\nimport { McpToolPolicy } from './McpToolPolicy';\nimport { RegisteredMcpTool } from './McpToolRegistry';\nimport { VerifiedMcpCredential } from './McpAuth';\nimport { MCP_INVOCATION_CONTEXT, McpInvocationContext } from './McpInvocationContext';\n\n/** Authorize, validate, then issue a local credential and invoke the ordinary filters. No scope or catch. */\nexport class McpApiDispatcher {\n private readonly schemas = new ApiJsonSchemaValidator();\n\n async call(\n tool: RegisteredMcpTool,\n requestDto: DtoValue,\n credential: VerifiedMcpCredential,\n invocation: McpInvocationContext,\n policy: AuthorizationService,\n ): Promise<DtoValue> {\n await new McpToolPolicy(policy).require(\n tool.authorization,\n tool.name,\n );\n const inputFailure = this.schemas.validate(tool.inputSchema, requestDto);\n if (inputFailure) {\n throw new ApiBadRequestError(\n `MCP arguments for ${tool.name} did not match its published input schema: ${inputFailure.message}`,\n inputFailure.field,\n inputFailure.message,\n );\n }\n const authentication =\n tool.binding.topology === 'local'\n ? new InvocationAuthentication(tool.apiClass, tool.methodName)\n : undefined;\n RequestContext.putTrusted(MCP_INVOCATION_CONTEXT, invocation);\n return tool.binding.invoke(tool.methodName, requestDto, authentication);\n }\n}\n"]}
package/src/McpAuth.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { AuthenticatedCaller, JwtHook } from '@webpieces/http-routing';
1
+ import { AuthenticatedCaller, AuthorizationService } from '@webpieces/http-routing';
2
2
  export declare const MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS: number;
3
3
  export declare const MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS: number;
4
- export declare const MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS: number;
5
4
  /** Framework-normalized access-token result. The token representation remains application-owned. */
6
5
  export declare class MintedMcpAccessToken {
7
6
  readonly token: string;
@@ -24,12 +23,12 @@ export declare class VerifiedMcpCredential {
24
23
  readonly scopes: readonly string[];
25
24
  /** When current enabled/revoked state and roles were read from the authoritative source. */
26
25
  readonly accountValidatedAtEpochSeconds: number;
27
- /** Application mapping of freshly verified account facts, shared with endpoint JWT issuance. */
26
+ /** Application mapping of freshly verified account facts into canonical trusted context. */
28
27
  readonly caller: AuthenticatedCaller;
29
28
  constructor(subject: string, issuer: string, resource: string, issuedAtEpochSeconds: number, expiresAtEpochSeconds: number, scopes: readonly string[],
30
29
  /** When current enabled/revoked state and roles were read from the authoritative source. */
31
30
  accountValidatedAtEpochSeconds: number,
32
- /** Application mapping of freshly verified account facts, shared with endpoint JWT issuance. */
31
+ /** Application mapping of freshly verified account facts into canonical trusted context. */
33
32
  caller: AuthenticatedCaller);
34
33
  /** The canonical application's roles; there is no independent listing-role snapshot. */
35
34
  get principalRoles(): readonly string[];
@@ -43,15 +42,6 @@ export interface McpAccessTokenAuthority<TGrant> {
43
42
  mintAccessToken(grant: TGrant): Promise<MintedMcpAccessToken>;
44
43
  verifyAccessToken(accessToken: string, expectedResource: string): Promise<VerifiedMcpCredential>;
45
44
  }
46
- /** Minimal tool identity supplied when the app creates its own endpoint-JWT mint request. */
47
- export declare class McpEndpointDescriptor {
48
- readonly toolName: string;
49
- readonly apiClassName: string;
50
- readonly methodName: string;
51
- constructor(toolName: string, apiClassName: string, methodName: string);
52
- }
53
- /** Application mapping from verified MCP identity to its unconstrained JwtHook mint request. */
54
- export type McpEndpointMintRequestFactory<TMintRequest> = (credential: VerifiedMcpCredential, endpoint: McpEndpointDescriptor) => TMintRequest;
55
45
  /** RFC 9728-style metadata exposed by the MCP protected resource. */
56
46
  export declare class McpProtectedResourceMetadata {
57
47
  readonly resource: string;
@@ -63,9 +53,8 @@ export declare class McpProtectedResourceMetadata {
63
53
  /**
64
54
  * Node MCP server configuration, built with fluent setters: every name sits next to its own value, so
65
55
  * no two settings can be swapped, and a future setting is an additive setter rather than a breaking
66
- * signature change. Pass the same concrete application authority to both
67
- * `setAccessTokenAuthority(...)` and `setEndpointJwtAuthority(...)` when it implements both security
68
- * seams.
56
+ * signature change. The access-token authority verifies MCP credentials; the authorization service
57
+ * is shared with receiving endpoints and document projection.
69
58
  *
70
59
  * SINGLE CANONICAL ORIGIN: one server serves exactly one protected resource. {@link setResource} is
71
60
  * fixed for the lifetime of the process, is the audience of every access token, and is never derived
@@ -79,13 +68,12 @@ export declare class McpProtectedResourceMetadata {
79
68
  * so the client's correct response is to re-authenticate and fail again, forever.
80
69
  *
81
70
  * ```ts
82
- * const config = new WpMcpServerConfig<MyGrant, MyMintRequest>()
71
+ * const config = new WpMcpServerConfig<MyGrant>()
83
72
  * .setName('my-server')
84
73
  * .setVersion('1.0.0')
85
74
  * .setResource('https://api.example.com/mcp')
86
75
  * .setAccessTokenAuthority(authority)
87
- * .setEndpointJwtAuthority(jwtHook)
88
- * .setEndpointMintRequest((credential) => new MyMintRequest(credential.subject))
76
+ * .setAuthorizationService(router.authorizationService())
89
77
  * .setAuthorizationServers(['https://login.example.com'])
90
78
  * .setRequiredScopes(['tools']);
91
79
  *
@@ -93,17 +81,15 @@ export declare class McpProtectedResourceMetadata {
93
81
  * McpRegistry.setErrorTranslator(new MyMcpErrorTranslator());
94
82
  * ```
95
83
  */
96
- export declare class WpMcpServerConfig<TGrant, TMintRequest> {
84
+ export declare class WpMcpServerConfig<TGrant> {
97
85
  private nameValue?;
98
86
  private versionValue?;
99
87
  private resourceValue?;
100
88
  private accessTokenAuthorityValue?;
101
- private endpointJwtAuthorityValue?;
102
- private endpointMintRequestValue?;
89
+ private authorizationServiceValue?;
103
90
  private authorizationServersValue?;
104
91
  private requiredScopesValue?;
105
92
  private maxAccountValidationAgeSecondsValue;
106
- private maxEndpointJwtLifetimeSecondsValue;
107
93
  /** REQUIRED. The MCP server name reported by `initialize`. */
108
94
  setName(name: string): this;
109
95
  /** REQUIRED. The MCP server version; the registry revision is appended to it at bind time. */
@@ -124,10 +110,8 @@ export declare class WpMcpServerConfig<TGrant, TMintRequest> {
124
110
  setResource(resource: string): this;
125
111
  /** REQUIRED. Application-owned paired mint/verify authority for MCP access tokens. */
126
112
  setAccessTokenAuthority(authority: McpAccessTokenAuthority<TGrant>): this;
127
- /** REQUIRED. The application policy for MCP tools and short-lived endpoint JWT issuer. */
128
- setEndpointJwtAuthority(authority: JwtHook<TMintRequest>): this;
129
- /** REQUIRED. Maps a verified MCP identity plus tool identity to that JwtHook's mint request. */
130
- setEndpointMintRequest(factory: McpEndpointMintRequestFactory<TMintRequest>): this;
113
+ /** REQUIRED. The common authorization service for tool projection and invocation. */
114
+ setAuthorizationService(authority: AuthorizationService): this;
131
115
  /**
132
116
  * REQUIRED. The trusted token issuers. Every entry must be an absolute URL, because each is
133
117
  * compared against a token's `issuer` claim and published as an `authorization_servers` entry —
@@ -141,8 +125,6 @@ export declare class WpMcpServerConfig<TGrant, TMintRequest> {
141
125
  setRequiredScopes(requiredScopes: readonly string[]): this;
142
126
  /** OPTIONAL ceiling, 1 second to 1 hour. Defaults to 1 hour. */
143
127
  setMaxAccountValidationAgeSeconds(seconds: number): this;
144
- /** OPTIONAL ceiling, 1 second to 1 hour. Defaults to 1 hour. */
145
- setMaxEndpointJwtLifetimeSeconds(seconds: number): this;
146
128
  /**
147
129
  * Called by `WpMcpServer.bind(...)`. Throws naming EVERY missing required setter at once, so a
148
130
  * half-built config is repaired in one pass rather than one boot failure at a time.
@@ -152,12 +134,10 @@ export declare class WpMcpServerConfig<TGrant, TMintRequest> {
152
134
  get version(): string;
153
135
  get resource(): string;
154
136
  get accessTokenAuthority(): McpAccessTokenAuthority<TGrant>;
155
- get endpointJwtAuthority(): JwtHook<TMintRequest>;
156
- get endpointMintRequest(): McpEndpointMintRequestFactory<TMintRequest>;
137
+ get authorizationService(): AuthorizationService;
157
138
  get authorizationServers(): readonly string[];
158
139
  get requiredScopes(): readonly string[];
159
140
  get maxAccountValidationAgeSeconds(): number;
160
- get maxEndpointJwtLifetimeSeconds(): number;
161
141
  /**
162
142
  * RFC 9728 §3.1: the URL of this resource's protected-resource METADATA DOCUMENT, derived from
163
143
  * {@link resource} by inserting `/.well-known/oauth-protected-resource` between origin and path
package/src/McpAuth.js CHANGED
@@ -1,9 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WpMcpServerConfig = exports.McpProtectedResourceMetadata = exports.McpEndpointDescriptor = exports.VerifiedMcpCredential = exports.MintedMcpAccessToken = exports.MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS = exports.MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS = exports.MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS = void 0;
3
+ exports.WpMcpServerConfig = exports.McpProtectedResourceMetadata = exports.VerifiedMcpCredential = exports.MintedMcpAccessToken = exports.MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS = exports.MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS = void 0;
4
4
  exports.MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS = 30 * 24 * 60 * 60;
5
5
  exports.MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS = 60 * 60;
6
- exports.MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS = 60 * 60;
7
6
  /** Framework-normalized access-token result. The token representation remains application-owned. */
8
7
  class MintedMcpAccessToken {
9
8
  token;
@@ -47,7 +46,7 @@ class VerifiedMcpCredential {
47
46
  constructor(subject, issuer, resource, issuedAtEpochSeconds, expiresAtEpochSeconds, scopes,
48
47
  /** When current enabled/revoked state and roles were read from the authoritative source. */
49
48
  accountValidatedAtEpochSeconds,
50
- /** Application mapping of freshly verified account facts, shared with endpoint JWT issuance. */
49
+ /** Application mapping of freshly verified account facts into canonical trusted context. */
51
50
  caller) {
52
51
  this.subject = subject;
53
52
  this.issuer = issuer;
@@ -64,18 +63,6 @@ class VerifiedMcpCredential {
64
63
  }
65
64
  }
66
65
  exports.VerifiedMcpCredential = VerifiedMcpCredential;
67
- /** Minimal tool identity supplied when the app creates its own endpoint-JWT mint request. */
68
- class McpEndpointDescriptor {
69
- toolName;
70
- apiClassName;
71
- methodName;
72
- constructor(toolName, apiClassName, methodName) {
73
- this.toolName = toolName;
74
- this.apiClassName = apiClassName;
75
- this.methodName = methodName;
76
- }
77
- }
78
- exports.McpEndpointDescriptor = McpEndpointDescriptor;
79
66
  /** RFC 9728-style metadata exposed by the MCP protected resource. */
80
67
  class McpProtectedResourceMetadata {
81
68
  resource;
@@ -92,9 +79,8 @@ exports.McpProtectedResourceMetadata = McpProtectedResourceMetadata;
92
79
  /**
93
80
  * Node MCP server configuration, built with fluent setters: every name sits next to its own value, so
94
81
  * 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.
82
+ * signature change. The access-token authority verifies MCP credentials; the authorization service
83
+ * is shared with receiving endpoints and document projection.
98
84
  *
99
85
  * SINGLE CANONICAL ORIGIN: one server serves exactly one protected resource. {@link setResource} is
100
86
  * fixed for the lifetime of the process, is the audience of every access token, and is never derived
@@ -108,13 +94,12 @@ exports.McpProtectedResourceMetadata = McpProtectedResourceMetadata;
108
94
  * so the client's correct response is to re-authenticate and fail again, forever.
109
95
  *
110
96
  * ```ts
111
- * const config = new WpMcpServerConfig<MyGrant, MyMintRequest>()
97
+ * const config = new WpMcpServerConfig<MyGrant>()
112
98
  * .setName('my-server')
113
99
  * .setVersion('1.0.0')
114
100
  * .setResource('https://api.example.com/mcp')
115
101
  * .setAccessTokenAuthority(authority)
116
- * .setEndpointJwtAuthority(jwtHook)
117
- * .setEndpointMintRequest((credential) => new MyMintRequest(credential.subject))
102
+ * .setAuthorizationService(router.authorizationService())
118
103
  * .setAuthorizationServers(['https://login.example.com'])
119
104
  * .setRequiredScopes(['tools']);
120
105
  *
@@ -127,12 +112,10 @@ class WpMcpServerConfig {
127
112
  versionValue;
128
113
  resourceValue;
129
114
  accessTokenAuthorityValue;
130
- endpointJwtAuthorityValue;
131
- endpointMintRequestValue;
115
+ authorizationServiceValue;
132
116
  authorizationServersValue;
133
117
  requiredScopesValue;
134
118
  maxAccountValidationAgeSecondsValue = exports.MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS;
135
- maxEndpointJwtLifetimeSecondsValue = exports.MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS;
136
119
  /** REQUIRED. The MCP server name reported by `initialize`. */
137
120
  setName(name) {
138
121
  this.nameValue = this.requireText(name, 'setName');
@@ -165,17 +148,9 @@ class WpMcpServerConfig {
165
148
  this.accessTokenAuthorityValue = this.requirePresent(authority, 'setAccessTokenAuthority');
166
149
  return this;
167
150
  }
168
- /** REQUIRED. The application policy for MCP tools and short-lived endpoint JWT issuer. */
169
- setEndpointJwtAuthority(authority) {
170
- this.endpointJwtAuthorityValue = this.requirePresent(authority, 'setEndpointJwtAuthority');
171
- return this;
172
- }
173
- /** REQUIRED. Maps a verified MCP identity plus tool identity to that JwtHook's mint request. */
174
- setEndpointMintRequest(factory) {
175
- if (typeof factory !== 'function') {
176
- throw new Error('WpMcpServerConfig.setEndpointMintRequest(...) requires a function.');
177
- }
178
- this.endpointMintRequestValue = factory;
151
+ /** REQUIRED. The common authorization service for tool projection and invocation. */
152
+ setAuthorizationService(authority) {
153
+ this.authorizationServiceValue = this.requirePresent(authority, 'setAuthorizationService');
179
154
  return this;
180
155
  }
181
156
  /**
@@ -209,11 +184,6 @@ class WpMcpServerConfig {
209
184
  this.maxAccountValidationAgeSecondsValue = this.requireSeconds(seconds, exports.MAX_MCP_ACCOUNT_VALIDATION_AGE_SECONDS, 'setMaxAccountValidationAgeSeconds');
210
185
  return this;
211
186
  }
212
- /** OPTIONAL ceiling, 1 second to 1 hour. Defaults to 1 hour. */
213
- setMaxEndpointJwtLifetimeSeconds(seconds) {
214
- this.maxEndpointJwtLifetimeSecondsValue = this.requireSeconds(seconds, exports.MAX_MCP_ENDPOINT_JWT_LIFETIME_SECONDS, 'setMaxEndpointJwtLifetimeSeconds');
215
- return this;
216
- }
217
187
  /**
218
188
  * Called by `WpMcpServer.bind(...)`. Throws naming EVERY missing required setter at once, so a
219
189
  * half-built config is repaired in one pass rather than one boot failure at a time.
@@ -229,11 +199,8 @@ class WpMcpServerConfig {
229
199
  if (this.accessTokenAuthorityValue === undefined) {
230
200
  missing.push('setAccessTokenAuthority(...)');
231
201
  }
232
- if (this.endpointJwtAuthorityValue === undefined) {
233
- missing.push('setEndpointJwtAuthority(...)');
234
- }
235
- if (this.endpointMintRequestValue === undefined) {
236
- missing.push('setEndpointMintRequest(...)');
202
+ if (this.authorizationServiceValue === undefined) {
203
+ missing.push('setAuthorizationService(...)');
237
204
  }
238
205
  if (this.authorizationServersValue === undefined) {
239
206
  missing.push('setAuthorizationServers(...)');
@@ -256,11 +223,8 @@ class WpMcpServerConfig {
256
223
  get accessTokenAuthority() {
257
224
  return this.read(this.accessTokenAuthorityValue, 'setAccessTokenAuthority');
258
225
  }
259
- get endpointJwtAuthority() {
260
- return this.read(this.endpointJwtAuthorityValue, 'setEndpointJwtAuthority');
261
- }
262
- get endpointMintRequest() {
263
- return this.read(this.endpointMintRequestValue, 'setEndpointMintRequest');
226
+ get authorizationService() {
227
+ return this.read(this.authorizationServiceValue, 'setAuthorizationService');
264
228
  }
265
229
  get authorizationServers() {
266
230
  return this.read(this.authorizationServersValue, 'setAuthorizationServers');
@@ -271,9 +235,6 @@ class WpMcpServerConfig {
271
235
  get maxAccountValidationAgeSeconds() {
272
236
  return this.maxAccountValidationAgeSecondsValue;
273
237
  }
274
- get maxEndpointJwtLifetimeSeconds() {
275
- return this.maxEndpointJwtLifetimeSecondsValue;
276
- }
277
238
  /**
278
239
  * RFC 9728 §3.1: the URL of this resource's protected-resource METADATA DOCUMENT, derived from
279
240
  * {@link resource} by inserting `/.well-known/oauth-protected-resource` between origin and path