@webpieces/mcp-server 0.4.991 → 0.4.993

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
@@ -90,6 +90,10 @@ split in two layers:
90
90
  `binding.invoke`, and error translation. Its ONLY input about the caller is the CURRENT
91
91
  `RequestContext` — the trusted USER_ID / USER_ROLES / ORG_ID and a `VerifiedMachineCaller`, exactly what
92
92
  `AuthorizationService.authorize` reads. `@WpAuthorization` on the contract is the single policy everywhere.
93
+ An AI agent with no user is a caller with roles and no user id (#1420): an `mcpOAuth` authority returns
94
+ `new AuthenticatedCaller(undefined, ['order-bot'], [], {}, new AuthenticatedMachineIdentity(...))`, a
95
+ `ROLES ['admin', 'order-bot']` tool is listed and callable for it, and `McpInvocationContext.roles`
96
+ reports its roles. `ALL_USERS` still requires a user.
93
97
  - **a transport** — whose only job is to establish that context.
94
98
 
95
99
  ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/mcp-server",
3
- "version": "0.4.991",
3
+ "version": "0.4.993",
4
4
  "description": "Secure MCP tool generation from annotated Webpieces API contracts",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -25,15 +25,15 @@
25
25
  "@modelcontextprotocol/client": "2.0.0",
26
26
  "@modelcontextprotocol/node": "2.0.0",
27
27
  "@modelcontextprotocol/server": "2.0.0",
28
- "@webpieces/core-context": "0.4.991",
29
- "@webpieces/core-util": "0.4.991",
30
- "@webpieces/http-routing": "0.4.991",
31
- "@webpieces/http-server": "0.4.991",
28
+ "@webpieces/core-context": "0.4.993",
29
+ "@webpieces/core-util": "0.4.993",
30
+ "@webpieces/http-routing": "0.4.993",
31
+ "@webpieces/http-server": "0.4.993",
32
32
  "express": "5.1.0",
33
33
  "reflect-metadata": "0.2.2",
34
34
  "tslib": "2.8.1"
35
35
  },
36
36
  "devDependencies": {
37
- "@webpieces/http-client-node": "0.4.991"
37
+ "@webpieces/http-client-node": "0.4.993"
38
38
  }
39
39
  }
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.McpDoorAuthenticator = exports.McpOAuthDoorMode = exports.McpDoorPass = exports.McpDoorRequest = exports.JwtPeek = void 0;
4
- const core_context_1 = require("@webpieces/core-context");
5
4
  const core_util_1 = require("@webpieces/core-util");
6
5
  const http_routing_1 = require("@webpieces/http-routing");
7
6
  const McpAuth_1 = require("./McpAuth");
@@ -110,6 +109,7 @@ class OidcDoorMode {
110
109
  verifiers;
111
110
  name;
112
111
  bearerFamily = true;
112
+ callerContext = new http_routing_1.AuthenticatedCallerContext();
113
113
  constructor(methods, verifiers) {
114
114
  this.methods = methods;
115
115
  this.verifiers = verifiers;
@@ -129,7 +129,7 @@ class OidcDoorMode {
129
129
  async authenticate(req) {
130
130
  const token = req.bearer();
131
131
  const identity = await this.verifiers.verifyOidc(token, this.methods);
132
- core_context_1.RequestContext.putTrusted(http_routing_1.VERIFIED_MACHINE_CALLER, new http_routing_1.VerifiedMachineCaller(identity.mechanism, identity.identity));
132
+ await this.callerContext.publishVerifiedService(identity, (verified) => this.verifiers.oidcOriginatingServiceRoles(verified));
133
133
  return new McpDoorPass(this.name, true, token, identity.identity, []);
134
134
  }
135
135
  }
@@ -139,6 +139,7 @@ class SharedSecretDoorMode {
139
139
  verifiers;
140
140
  name;
141
141
  bearerFamily = false;
142
+ callerContext = new http_routing_1.AuthenticatedCallerContext();
142
143
  constructor(secretKey, verifiers) {
143
144
  this.secretKey = secretKey;
144
145
  this.verifiers = verifiers;
@@ -150,7 +151,8 @@ class SharedSecretDoorMode {
150
151
  async authenticate(req) {
151
152
  const secret = req.sharedSecret();
152
153
  this.verifiers.verifySharedSecret(secret, this.secretKey);
153
- core_context_1.RequestContext.putTrusted(http_routing_1.VERIFIED_MACHINE_CALLER, new http_routing_1.VerifiedMachineCaller('shared-secret', this.secretKey));
154
+ // A shared secret grants no role: an originating holder is a role-less service caller.
155
+ await this.callerContext.publishVerifiedService(new http_routing_1.AuthenticatedMachineIdentity('shared-secret', this.secretKey), () => Promise.resolve([]));
154
156
  return new McpDoorPass(this.name, true, secret, `shared-secret:${this.secretKey}`, []);
155
157
  }
156
158
  }
@@ -1 +1 @@
1
- {"version":3,"file":"McpDoorAuth.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpDoorAuth.ts"],"names":[],"mappings":"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","sourcesContent":["import { HttpRequest, RequestContext } from '@webpieces/core-context';\nimport {\n ApiImplementationError,\n ApiKeyCredential,\n ApiUnauthorizedError,\n AuthMeta,\n AuthMethod,\n DtoValue,\n LogManager,\n OidcAuthMethod,\n OidcAuthMethods,\n toError,\n} from '@webpieces/core-util';\nimport {\n AuthenticatedCallerContext,\n CredentialVerifiers,\n VERIFIED_MACHINE_CALLER,\n VerifiedMachineCaller,\n} from '@webpieces/http-routing';\nimport {\n MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS,\n McpHttpAuth,\n VerifiedMcpCredential,\n DEV_OIDC_TOKEN_PREFIX,\n GOOGLE_OIDC_ISSUERS,\n McpOAuthMode,\n} from './McpAuth';\n\nconst log = LogManager.getLogger('McpDoorAuth');\nconst BEARER_SCHEME = 'Bearer ';\nconst SHARED_SECRET_SCHEME = 'Webpieces ';\n\n/**\n * JwtPeek: base64url-decode a JWT's PAYLOAD only. NEVER trusted — the result is used solely to pick\n * which ONE verifier sees the request, and that verifier fully verifies, so a token that merely\n * claims an issuer fails its verification and is a 401. It never falls through to another mode.\n */\nexport class JwtPeek {\n /** The unverified payload claims, or `undefined` when `token` is not a well-formed JWT. */\n // webpieces-disable no-function-outside-class -- pure decoder used by classification only\n static payloadOf(token: string): Record<string, DtoValue> | undefined {\n const parts = token.split('.');\n if (parts.length !== 3 || parts.some((part: string) => part === '')) return undefined;\n const header = JwtPeek.decodeRecord(parts[0]);\n return header === undefined ? undefined : JwtPeek.decodeRecord(parts[1]);\n }\n\n /** The unverified `iss` claim, or `undefined`. */\n // webpieces-disable no-function-outside-class -- pure decoder used by classification only\n static issuerOf(token: string): string | undefined {\n const iss = JwtPeek.payloadOf(token)?.['iss'];\n return typeof iss === 'string' ? iss : undefined;\n }\n\n // webpieces-disable no-function-outside-class -- pure decoder used by classification only\n private static decodeRecord(segment: string): Record<string, DtoValue> | undefined {\n const text = Buffer.from(segment, 'base64url').toString('utf8');\n if (!text.startsWith('{')) return undefined;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- a malformed segment is simply \"not a JWT\" for classification; the chosen verifier decides the rest\n try {\n const value = JSON.parse(text) as DtoValue;\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, DtoValue>)\n : undefined;\n } catch (err: unknown) {\n const error = toError(err);\n log.debug('Bearer segment is not JSON; classifying it as not a JWT', error);\n return undefined;\n }\n }\n}\n\n/** The inbound facts one door mode classifies and verifies: the transport-neutral request. */\nexport class McpDoorRequest {\n constructor(public readonly http: HttpRequest) {}\n\n header(name: string): string | undefined {\n return this.http.getHeader(name.toLowerCase());\n }\n\n /** The `Authorization: Bearer <token>` value, or `undefined`. */\n bearer(): string | undefined {\n const value = this.header('authorization');\n if (!value || !value.startsWith(BEARER_SCHEME)) return undefined;\n const token = value.substring(BEARER_SCHEME.length);\n return token === '' || /\\s/.test(token) ? undefined : token;\n }\n\n /** The `Authorization: Webpieces <secret>` value, or `undefined`. */\n sharedSecret(): string | undefined {\n const value = this.header('authorization');\n if (!value || !value.startsWith(SHARED_SECRET_SCHEME)) return undefined;\n return value.substring(SHARED_SECRET_SCHEME.length);\n }\n}\n\n/**\n * What a successful authentication established. The caller itself is already PUBLISHED into\n * `RequestContext` by the mode; these are the facts the door needs afterwards.\n */\nexport class McpDoorPass {\n constructor(\n /** Which mode authenticated, e.g. `jwt()`, `mcpOAuth(...)`. */\n public readonly mode: string,\n /** True for an internal hop (OIDC, shared secret) whose forwarded trusted context is believed. */\n public readonly callerVerified: boolean,\n /** The raw credential, for the SDK's `AuthInfo.token`. Never forwarded downstream. */\n public readonly token: string,\n /** The verified subject (user id or machine identity). */\n public readonly subject: string,\n public readonly scopes: readonly string[],\n public readonly expiresAtEpochSeconds?: number,\n ) {}\n}\n\n/** What a door mode needs to say about a request BEFORE any verification. */\ninterface McpDoorMode {\n readonly name: string;\n /** Claims some `Authorization: Bearer` credentials, so it can overlap another bearer mode. */\n readonly bearerFamily: boolean;\n /** Pure, cheap, NO crypto, NO I/O: does this request carry MY kind of credential? */\n claims(req: McpDoorRequest): boolean;\n /** Full verification; throws `ApiUnauthorizedError` on reject. Publishes the trusted caller. */\n authenticate(req: McpDoorRequest): Promise<McpDoorPass>;\n}\n\n/** `oidcSameProject()` / `oidcSvcs([...])` — ONE bearer credential, verified once against both. */\nclass OidcDoorMode implements McpDoorMode {\n readonly name: string;\n readonly bearerFamily = true;\n\n constructor(\n private readonly methods: OidcAuthMethods,\n private readonly verifiers: CredentialVerifiers,\n ) {\n this.name = methods\n .map((method: OidcAuthMethod) =>\n method.callerScope === 'same-project' ? 'oidcSameProject()' : 'oidcSvcs([...])',\n )\n .join(' + ');\n }\n\n claims(req: McpDoorRequest): boolean {\n const token = req.bearer();\n if (token === undefined) return false;\n if (token.startsWith(DEV_OIDC_TOKEN_PREFIX)) return true;\n const issuer = JwtPeek.issuerOf(token);\n return issuer !== undefined && GOOGLE_OIDC_ISSUERS.includes(issuer);\n }\n\n async authenticate(req: McpDoorRequest): Promise<McpDoorPass> {\n const token = req.bearer()!;\n const identity = await this.verifiers.verifyOidc(token, this.methods);\n RequestContext.putTrusted(\n VERIFIED_MACHINE_CALLER,\n new VerifiedMachineCaller(identity.mechanism, identity.identity),\n );\n return new McpDoorPass(this.name, true, token, identity.identity, []);\n }\n}\n\n/** `sharedSecret(key)` — the `Authorization: Webpieces <secret>` scheme, disjoint from every bearer. */\nclass SharedSecretDoorMode implements McpDoorMode {\n readonly name: string;\n readonly bearerFamily = false;\n\n constructor(\n private readonly secretKey: string,\n private readonly verifiers: CredentialVerifiers,\n ) {\n this.name = `sharedSecret('${secretKey}')`;\n }\n\n claims(req: McpDoorRequest): boolean {\n return req.sharedSecret() !== undefined;\n }\n\n async authenticate(req: McpDoorRequest): Promise<McpDoorPass> {\n const secret = req.sharedSecret()!;\n this.verifiers.verifySharedSecret(secret, this.secretKey);\n RequestContext.putTrusted(\n VERIFIED_MACHINE_CALLER,\n new VerifiedMachineCaller('shared-secret', this.secretKey),\n );\n return new McpDoorPass(this.name, true, secret, `shared-secret:${this.secretKey}`, []);\n }\n}\n\n/**\n * `apiKey(regime, credentials)` — claimed by its OWN declared headers. A bearer credential in the\n * regime also claims `Authorization: Bearer`, which is why startup refuses it beside any other bearer\n * mode.\n */\nclass ApiKeyDoorMode implements McpDoorMode {\n readonly name: string;\n readonly bearerFamily: boolean;\n private readonly headers: readonly string[];\n private readonly callerContext = new AuthenticatedCallerContext();\n\n constructor(\n private readonly regime: string,\n credentials: readonly ApiKeyCredential[],\n private readonly verifiers: CredentialVerifiers,\n ) {\n this.name = `apiKey('${regime}', ...)`;\n const headers: string[] = [];\n let bearer = false;\n for (const credential of credentials) {\n if (credential.in === 'bearer') {\n bearer = true;\n continue;\n }\n const header = (credential.key.httpHeader ?? '').toLowerCase();\n if (header === 'authorization') {\n throw new Error(\n `${this.name} declares its header credential on 'authorization', which every other ` +\n \"mode's credential rides; declare it { in: 'bearer' } or on its own header.\",\n );\n }\n headers.push(header);\n }\n this.headers = headers;\n this.bearerFamily = bearer;\n }\n\n claims(req: McpDoorRequest): boolean {\n if (this.headers.some((header: string) => req.header(header) !== undefined)) return true;\n return this.bearerFamily && req.bearer() !== undefined;\n }\n\n async authenticate(req: McpDoorRequest): Promise<McpDoorPass> {\n const caller = await this.verifiers.verifyApiKey(this.regime, req.http);\n this.callerContext.publish(caller);\n return new McpDoorPass(\n this.name,\n false,\n '',\n caller.userId ?? caller.machine?.identity ?? this.regime,\n [],\n );\n }\n}\n\n/**\n * `jwt()` — the app's own user token. It claims the REMAINDER of the bearer space: every\n * `Authorization: Bearer` credential that no other configured bearer mode claims. That makes it\n * disjoint from them by construction, with no issuer to configure.\n */\nclass JwtDoorMode implements McpDoorMode {\n readonly name = 'jwt()';\n readonly bearerFamily = true;\n private readonly callerContext = new AuthenticatedCallerContext();\n private others: readonly McpDoorMode[] = [];\n\n constructor(private readonly verifiers: CredentialVerifiers) {}\n\n excluding(others: readonly McpDoorMode[]): void {\n this.others = others.filter((mode: McpDoorMode) => mode !== this && mode.bearerFamily);\n }\n\n claims(req: McpDoorRequest): boolean {\n return (\n req.bearer() !== undefined && !this.others.some((mode: McpDoorMode) => mode.claims(req))\n );\n }\n\n async authenticate(req: McpDoorRequest): Promise<McpDoorPass> {\n const token = req.bearer()!;\n const caller = await this.verifiers.verifyJwt(token);\n this.callerContext.publish(caller);\n return new McpDoorPass(\n this.name,\n false,\n token,\n caller.userId ?? caller.machine?.identity ?? 'jwt',\n [],\n );\n }\n}\n\n/** `mcpOAuth(...)` — an audience-bound access token minted by the application's authority. */\nexport class McpOAuthDoorMode implements McpDoorMode {\n readonly name = 'mcpOAuth(...)';\n readonly bearerFamily = true;\n private readonly callerContext = new AuthenticatedCallerContext();\n\n constructor(\n public readonly mode: McpOAuthMode,\n private readonly resource: string,\n ) {}\n\n claims(req: McpDoorRequest): boolean {\n const token = req.bearer();\n if (token === undefined) return false;\n const format = this.mode.tokenFormat;\n switch (format.kind) {\n case 'every-bearer':\n return true;\n case 'opaque-prefix':\n return token.startsWith(format.prefix);\n case 'jwt-issuer': {\n const issuer = JwtPeek.issuerOf(token);\n return issuer !== undefined && this.mode.authorizationServers.includes(issuer);\n }\n }\n }\n\n async authenticate(req: McpDoorRequest): Promise<McpDoorPass> {\n const token = req.bearer()!;\n const credential = await this.mode.authority.verifyAccessToken(token, this.resource);\n this.validateCredential(credential);\n this.callerContext.publish(credential.caller);\n return new McpDoorPass(\n this.name,\n false,\n token,\n credential.subject,\n credential.scopes,\n credential.expiresAtEpochSeconds,\n );\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.resource) reject('resource mismatch');\n if (!this.mode.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.mode.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.mode.maxAccountValidationAgeSeconds\n ) {\n reject('account authorization state is not fresh enough for dispatch');\n }\n }\n}\n\n/**\n * CLASSIFY, then verify with exactly ONE verifier.\n *\n * Credentials are self-describing, so one path serves every mode. The request is classified from\n * UNVERIFIED surface facts — which header; a JWT's unverified `iss` — then handed to exactly ONE mode.\n * Classifying on unverified data is safe because the chosen verifier fully verifies: a token that\n * merely claims `iss: accounts.google.com` fails Google's verification and the request is a 401. It\n * never falls through to another mode.\n *\n * - No claimant: only an `mcpOAuth(...)` server answers with the discovery challenge; others a plain 401.\n * - Two claimants (an api-key header AND a bearer): ambiguous, refused — never \"pick the one that works\".\n * - One claimant: its rejection IS the answer.\n *\n * The set is checked at construction (bind time): `vendorAuth` / public are refused, every mode's\n * verifier must be bound, and no two modes may be able to claim one request.\n */\nexport class McpDoorAuthenticator {\n private constructor(\n private readonly modes: readonly McpDoorMode[],\n public readonly oauth: McpOAuthDoorMode | undefined,\n ) {}\n\n // webpieces-disable no-function-outside-class -- validating factory; the constructor stays private so an unchecked set cannot exist\n static create(\n auth: readonly McpHttpAuth[],\n resource: string,\n verifiers: CredentialVerifiers | undefined,\n ): McpDoorAuthenticator {\n const oauthModes = auth.filter(\n (mode: McpHttpAuth): mode is McpOAuthMode => mode instanceof McpOAuthMode,\n );\n if (oauthModes.length > 1) {\n throw new Error(\n 'WpMcpServerConfig.setAuth([...]) accepts at most one mcpOAuth(...) mode.',\n );\n }\n McpDoorAuthenticator.refuseNonCallers(auth);\n const methods = auth.filter(\n (mode: McpHttpAuth): mode is AuthMethod => !(mode instanceof McpOAuthMode),\n );\n if (methods.length > 0) {\n // The same declaration rules @WpAuth applies: no duplicate kinds (one oidc per scope), valid shapes.\n new AuthMeta([methods[0], ...methods.slice(1)], undefined);\n }\n const modes: McpDoorMode[] = [];\n const oauth =\n oauthModes[0] === undefined ? undefined : new McpOAuthDoorMode(oauthModes[0], resource);\n if (methods.length > 0) {\n if (!verifiers) {\n throw new Error(\n 'WpMcpServerConfig.setAuth([...]) holds @WpAuth modes; add setCredentialVerifiers(router.credentialVerifiers()).',\n );\n }\n McpDoorAuthenticator.refuseUnverifiable(methods, verifiers);\n modes.push(...McpDoorAuthenticator.doorModes(methods, verifiers));\n }\n if (oauth) modes.push(oauth);\n McpDoorAuthenticator.refuseOverlaps(modes, oauth);\n for (const mode of modes) if (mode instanceof JwtDoorMode) mode.excluding(modes);\n return new McpDoorAuthenticator(modes, oauth);\n }\n\n /**\n * Whether a 401 for this request carries the `mcpOAuth(...)` discovery challenge: only when the\n * server has that mode AND the request either carried no credential at all or was classified as an\n * OAuth access token. A rejected `jwt()` / OIDC / api-key credential gets a plain 401, with no hint\n * of which mode came closest.\n */\n challengeApplies(req: McpDoorRequest): boolean {\n if (!this.oauth) return false;\n const claimants = this.modes.filter((mode: McpDoorMode) => mode.claims(req));\n return claimants.length === 0 || (claimants.length === 1 && claimants[0] === this.oauth);\n }\n\n async authenticate(req: McpDoorRequest): Promise<McpDoorPass> {\n const claimants = this.modes.filter((mode: McpDoorMode) => mode.claims(req));\n if (claimants.length === 0) {\n throw new ApiUnauthorizedError('MCP request has no credential this server accepts.');\n }\n if (claimants.length > 1) {\n throw new ApiUnauthorizedError(\n 'MCP request carries more than one credential; send exactly one.',\n );\n }\n return claimants[0].authenticate(req);\n }\n\n // webpieces-disable no-function-outside-class -- bind-time validation helper of the private constructor\n private static refuseNonCallers(auth: readonly McpHttpAuth[]): void {\n // A public mode does not compile here (McpHttpAuth excludes it); one smuggled past the type is\n // refused by the AuthMeta declaration check that follows.\n for (const method of auth) {\n if (method.kind === 'vendorAuth') {\n throw new Error(\n 'WpMcpServerConfig.setAuth([...]) refuses vendorAuth(...): ' +\n 'it identifies no caller to authorize tools against. Use oidcSameProject(), oidcSvcs([...]), ' +\n 'jwt(), apiKey(...), sharedSecret(...) or mcpOAuth(...).',\n );\n }\n }\n }\n\n // webpieces-disable no-function-outside-class -- bind-time validation helper of the private constructor\n private static refuseUnverifiable(\n methods: readonly AuthMethod[],\n verifiers: CredentialVerifiers,\n ): void {\n const missing = methods\n .map((method: AuthMethod) => verifiers.missingVerifier(method))\n .filter((line: string | undefined): line is string => line !== undefined);\n if (missing.length > 0) {\n throw new Error(\n 'WpMcpServerConfig.setAuth([...]) accepts a mode this server cannot verify, so it refuses to start. Add:\\n ' +\n missing.join('\\n '),\n );\n }\n }\n\n // webpieces-disable no-function-outside-class -- bind-time construction helper of the private constructor\n private static doorModes(\n methods: readonly AuthMethod[],\n verifiers: CredentialVerifiers,\n ): McpDoorMode[] {\n const modes: McpDoorMode[] = [];\n const oidc = methods.filter(\n (method: AuthMethod): method is OidcAuthMethod => method.kind === 'oidc',\n );\n if (oidc[0] !== undefined)\n modes.push(new OidcDoorMode([oidc[0], ...oidc.slice(1)], verifiers));\n for (const method of methods) {\n switch (method.kind) {\n case 'oidc':\n break;\n case 'jwt':\n modes.push(new JwtDoorMode(verifiers));\n break;\n case 'shared-secret':\n modes.push(new SharedSecretDoorMode(method.secretKey, verifiers));\n break;\n case 'apikey':\n modes.push(new ApiKeyDoorMode(method.regime, method.credentials, verifiers));\n break;\n case 'vendorAuth':\n throw new ApiImplementationError(\n 'vendorAuth was refused before door modes are built.',\n );\n }\n }\n return modes;\n }\n\n /**\n * `claims()` sets must be DISJOINT BY CONSTRUCTION. The bearer space is the only shared one, so the\n * refusals are about it:\n * - `mcpOAuth(..., everyBearer())` claims every bearer, so it stands alone in the bearer family;\n * - a bearer `apiKey(...)` claims every bearer too, so it stands alone as well;\n * - `mcpOAuth(..., jwtIssuer())` may not list a Google issuer beside an OIDC mode.\n * `jwt()` claims the remainder, and `McpAccessTokenFormat.opaquePrefix` refuses a prefix that could\n * overlap a JWT or a dev-oidc token, so nothing else can collide.\n */\n // webpieces-disable no-function-outside-class -- bind-time validation helper of the private constructor\n private static refuseOverlaps(\n modes: readonly McpDoorMode[],\n oauth: McpOAuthDoorMode | undefined,\n ): void {\n const bearerModes = modes.filter((mode: McpDoorMode) => mode.bearerFamily);\n const greedy = bearerModes.filter(\n (mode: McpDoorMode) =>\n mode instanceof ApiKeyDoorMode ||\n (mode instanceof McpOAuthDoorMode && mode.mode.tokenFormat.kind === 'every-bearer'),\n );\n for (const mode of greedy) {\n if (bearerModes.length > 1) {\n const others = bearerModes\n .filter((other: McpDoorMode) => other !== mode)\n .map((other: McpDoorMode) => other.name);\n throw new Error(\n `WpMcpServerConfig.setAuth([...]): ${mode.name} claims EVERY bearer credential, so it overlaps ` +\n `${others.join(', ')}. Give it a disjoint shape (mcpOAuth: McpAccessTokenFormat.opaquePrefix(...) ` +\n 'or .jwtIssuer(); apiKey: a header credential) or remove the other bearer modes.',\n );\n }\n }\n const oidc = modes.some((mode: McpDoorMode) => mode instanceof OidcDoorMode);\n if (oauth && oidc && oauth.mode.tokenFormat.kind === 'jwt-issuer') {\n const google = oauth.mode.authorizationServers.filter((issuer: string) =>\n GOOGLE_OIDC_ISSUERS.includes(issuer),\n );\n if (google.length > 0) {\n throw new Error(\n `WpMcpServerConfig.setAuth([...]): mcpOAuth(...) trusts the Google issuer ${google.join(', ')}, ` +\n 'so it overlaps the OIDC mode, which claims every Google-issued token.',\n );\n }\n }\n }\n}\n"]}
1
+ {"version":3,"file":"McpDoorAuth.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpDoorAuth.ts"],"names":[],"mappings":"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","sourcesContent":["import { HttpRequest } from '@webpieces/core-context';\nimport {\n ApiImplementationError,\n ApiKeyCredential,\n ApiUnauthorizedError,\n AuthMeta,\n AuthMethod,\n DtoValue,\n LogManager,\n OidcAuthMethod,\n OidcAuthMethods,\n toError,\n} from '@webpieces/core-util';\nimport {\n AuthenticatedCallerContext,\n AuthenticatedMachineIdentity,\n CredentialVerifiers,\n} from '@webpieces/http-routing';\nimport {\n MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS,\n McpHttpAuth,\n VerifiedMcpCredential,\n DEV_OIDC_TOKEN_PREFIX,\n GOOGLE_OIDC_ISSUERS,\n McpOAuthMode,\n} from './McpAuth';\n\nconst log = LogManager.getLogger('McpDoorAuth');\nconst BEARER_SCHEME = 'Bearer ';\nconst SHARED_SECRET_SCHEME = 'Webpieces ';\n\n/**\n * JwtPeek: base64url-decode a JWT's PAYLOAD only. NEVER trusted — the result is used solely to pick\n * which ONE verifier sees the request, and that verifier fully verifies, so a token that merely\n * claims an issuer fails its verification and is a 401. It never falls through to another mode.\n */\nexport class JwtPeek {\n /** The unverified payload claims, or `undefined` when `token` is not a well-formed JWT. */\n // webpieces-disable no-function-outside-class -- pure decoder used by classification only\n static payloadOf(token: string): Record<string, DtoValue> | undefined {\n const parts = token.split('.');\n if (parts.length !== 3 || parts.some((part: string) => part === '')) return undefined;\n const header = JwtPeek.decodeRecord(parts[0]);\n return header === undefined ? undefined : JwtPeek.decodeRecord(parts[1]);\n }\n\n /** The unverified `iss` claim, or `undefined`. */\n // webpieces-disable no-function-outside-class -- pure decoder used by classification only\n static issuerOf(token: string): string | undefined {\n const iss = JwtPeek.payloadOf(token)?.['iss'];\n return typeof iss === 'string' ? iss : undefined;\n }\n\n // webpieces-disable no-function-outside-class -- pure decoder used by classification only\n private static decodeRecord(segment: string): Record<string, DtoValue> | undefined {\n const text = Buffer.from(segment, 'base64url').toString('utf8');\n if (!text.startsWith('{')) return undefined;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- a malformed segment is simply \"not a JWT\" for classification; the chosen verifier decides the rest\n try {\n const value = JSON.parse(text) as DtoValue;\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, DtoValue>)\n : undefined;\n } catch (err: unknown) {\n const error = toError(err);\n log.debug('Bearer segment is not JSON; classifying it as not a JWT', error);\n return undefined;\n }\n }\n}\n\n/** The inbound facts one door mode classifies and verifies: the transport-neutral request. */\nexport class McpDoorRequest {\n constructor(public readonly http: HttpRequest) {}\n\n header(name: string): string | undefined {\n return this.http.getHeader(name.toLowerCase());\n }\n\n /** The `Authorization: Bearer <token>` value, or `undefined`. */\n bearer(): string | undefined {\n const value = this.header('authorization');\n if (!value || !value.startsWith(BEARER_SCHEME)) return undefined;\n const token = value.substring(BEARER_SCHEME.length);\n return token === '' || /\\s/.test(token) ? undefined : token;\n }\n\n /** The `Authorization: Webpieces <secret>` value, or `undefined`. */\n sharedSecret(): string | undefined {\n const value = this.header('authorization');\n if (!value || !value.startsWith(SHARED_SECRET_SCHEME)) return undefined;\n return value.substring(SHARED_SECRET_SCHEME.length);\n }\n}\n\n/**\n * What a successful authentication established. The caller itself is already PUBLISHED into\n * `RequestContext` by the mode; these are the facts the door needs afterwards.\n */\nexport class McpDoorPass {\n constructor(\n /** Which mode authenticated, e.g. `jwt()`, `mcpOAuth(...)`. */\n public readonly mode: string,\n /** True for an internal hop (OIDC, shared secret) whose forwarded trusted context is believed. */\n public readonly callerVerified: boolean,\n /** The raw credential, for the SDK's `AuthInfo.token`. Never forwarded downstream. */\n public readonly token: string,\n /** The verified subject (user id or machine identity). */\n public readonly subject: string,\n public readonly scopes: readonly string[],\n public readonly expiresAtEpochSeconds?: number,\n ) {}\n}\n\n/** What a door mode needs to say about a request BEFORE any verification. */\ninterface McpDoorMode {\n readonly name: string;\n /** Claims some `Authorization: Bearer` credentials, so it can overlap another bearer mode. */\n readonly bearerFamily: boolean;\n /** Pure, cheap, NO crypto, NO I/O: does this request carry MY kind of credential? */\n claims(req: McpDoorRequest): boolean;\n /** Full verification; throws `ApiUnauthorizedError` on reject. Publishes the trusted caller. */\n authenticate(req: McpDoorRequest): Promise<McpDoorPass>;\n}\n\n/** `oidcSameProject()` / `oidcSvcs([...])` — ONE bearer credential, verified once against both. */\nclass OidcDoorMode implements McpDoorMode {\n readonly name: string;\n readonly bearerFamily = true;\n private readonly callerContext = new AuthenticatedCallerContext();\n\n constructor(\n private readonly methods: OidcAuthMethods,\n private readonly verifiers: CredentialVerifiers,\n ) {\n this.name = methods\n .map((method: OidcAuthMethod) =>\n method.callerScope === 'same-project' ? 'oidcSameProject()' : 'oidcSvcs([...])',\n )\n .join(' + ');\n }\n\n claims(req: McpDoorRequest): boolean {\n const token = req.bearer();\n if (token === undefined) return false;\n if (token.startsWith(DEV_OIDC_TOKEN_PREFIX)) return true;\n const issuer = JwtPeek.issuerOf(token);\n return issuer !== undefined && GOOGLE_OIDC_ISSUERS.includes(issuer);\n }\n\n async authenticate(req: McpDoorRequest): Promise<McpDoorPass> {\n const token = req.bearer()!;\n const identity = await this.verifiers.verifyOidc(token, this.methods);\n await this.callerContext.publishVerifiedService(\n identity,\n (verified: AuthenticatedMachineIdentity) => this.verifiers.oidcOriginatingServiceRoles(verified),\n );\n return new McpDoorPass(this.name, true, token, identity.identity, []);\n }\n}\n\n/** `sharedSecret(key)` — the `Authorization: Webpieces <secret>` scheme, disjoint from every bearer. */\nclass SharedSecretDoorMode implements McpDoorMode {\n readonly name: string;\n readonly bearerFamily = false;\n private readonly callerContext = new AuthenticatedCallerContext();\n\n constructor(\n private readonly secretKey: string,\n private readonly verifiers: CredentialVerifiers,\n ) {\n this.name = `sharedSecret('${secretKey}')`;\n }\n\n claims(req: McpDoorRequest): boolean {\n return req.sharedSecret() !== undefined;\n }\n\n async authenticate(req: McpDoorRequest): Promise<McpDoorPass> {\n const secret = req.sharedSecret()!;\n this.verifiers.verifySharedSecret(secret, this.secretKey);\n // A shared secret grants no role: an originating holder is a role-less service caller.\n await this.callerContext.publishVerifiedService(\n new AuthenticatedMachineIdentity('shared-secret', this.secretKey),\n () => Promise.resolve([]),\n );\n return new McpDoorPass(this.name, true, secret, `shared-secret:${this.secretKey}`, []);\n }\n}\n\n/**\n * `apiKey(regime, credentials)` — claimed by its OWN declared headers. A bearer credential in the\n * regime also claims `Authorization: Bearer`, which is why startup refuses it beside any other bearer\n * mode.\n */\nclass ApiKeyDoorMode implements McpDoorMode {\n readonly name: string;\n readonly bearerFamily: boolean;\n private readonly headers: readonly string[];\n private readonly callerContext = new AuthenticatedCallerContext();\n\n constructor(\n private readonly regime: string,\n credentials: readonly ApiKeyCredential[],\n private readonly verifiers: CredentialVerifiers,\n ) {\n this.name = `apiKey('${regime}', ...)`;\n const headers: string[] = [];\n let bearer = false;\n for (const credential of credentials) {\n if (credential.in === 'bearer') {\n bearer = true;\n continue;\n }\n const header = (credential.key.httpHeader ?? '').toLowerCase();\n if (header === 'authorization') {\n throw new Error(\n `${this.name} declares its header credential on 'authorization', which every other ` +\n \"mode's credential rides; declare it { in: 'bearer' } or on its own header.\",\n );\n }\n headers.push(header);\n }\n this.headers = headers;\n this.bearerFamily = bearer;\n }\n\n claims(req: McpDoorRequest): boolean {\n if (this.headers.some((header: string) => req.header(header) !== undefined)) return true;\n return this.bearerFamily && req.bearer() !== undefined;\n }\n\n async authenticate(req: McpDoorRequest): Promise<McpDoorPass> {\n const caller = await this.verifiers.verifyApiKey(this.regime, req.http);\n this.callerContext.publish(caller);\n return new McpDoorPass(\n this.name,\n false,\n '',\n caller.userId ?? caller.machine?.identity ?? this.regime,\n [],\n );\n }\n}\n\n/**\n * `jwt()` — the app's own user token. It claims the REMAINDER of the bearer space: every\n * `Authorization: Bearer` credential that no other configured bearer mode claims. That makes it\n * disjoint from them by construction, with no issuer to configure.\n */\nclass JwtDoorMode implements McpDoorMode {\n readonly name = 'jwt()';\n readonly bearerFamily = true;\n private readonly callerContext = new AuthenticatedCallerContext();\n private others: readonly McpDoorMode[] = [];\n\n constructor(private readonly verifiers: CredentialVerifiers) {}\n\n excluding(others: readonly McpDoorMode[]): void {\n this.others = others.filter((mode: McpDoorMode) => mode !== this && mode.bearerFamily);\n }\n\n claims(req: McpDoorRequest): boolean {\n return (\n req.bearer() !== undefined && !this.others.some((mode: McpDoorMode) => mode.claims(req))\n );\n }\n\n async authenticate(req: McpDoorRequest): Promise<McpDoorPass> {\n const token = req.bearer()!;\n const caller = await this.verifiers.verifyJwt(token);\n this.callerContext.publish(caller);\n return new McpDoorPass(\n this.name,\n false,\n token,\n caller.userId ?? caller.machine?.identity ?? 'jwt',\n [],\n );\n }\n}\n\n/** `mcpOAuth(...)` — an audience-bound access token minted by the application's authority. */\nexport class McpOAuthDoorMode implements McpDoorMode {\n readonly name = 'mcpOAuth(...)';\n readonly bearerFamily = true;\n private readonly callerContext = new AuthenticatedCallerContext();\n\n constructor(\n public readonly mode: McpOAuthMode,\n private readonly resource: string,\n ) {}\n\n claims(req: McpDoorRequest): boolean {\n const token = req.bearer();\n if (token === undefined) return false;\n const format = this.mode.tokenFormat;\n switch (format.kind) {\n case 'every-bearer':\n return true;\n case 'opaque-prefix':\n return token.startsWith(format.prefix);\n case 'jwt-issuer': {\n const issuer = JwtPeek.issuerOf(token);\n return issuer !== undefined && this.mode.authorizationServers.includes(issuer);\n }\n }\n }\n\n async authenticate(req: McpDoorRequest): Promise<McpDoorPass> {\n const token = req.bearer()!;\n const credential = await this.mode.authority.verifyAccessToken(token, this.resource);\n this.validateCredential(credential);\n this.callerContext.publish(credential.caller);\n return new McpDoorPass(\n this.name,\n false,\n token,\n credential.subject,\n credential.scopes,\n credential.expiresAtEpochSeconds,\n );\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.resource) reject('resource mismatch');\n if (!this.mode.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.mode.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.mode.maxAccountValidationAgeSeconds\n ) {\n reject('account authorization state is not fresh enough for dispatch');\n }\n }\n}\n\n/**\n * CLASSIFY, then verify with exactly ONE verifier.\n *\n * Credentials are self-describing, so one path serves every mode. The request is classified from\n * UNVERIFIED surface facts — which header; a JWT's unverified `iss` — then handed to exactly ONE mode.\n * Classifying on unverified data is safe because the chosen verifier fully verifies: a token that\n * merely claims `iss: accounts.google.com` fails Google's verification and the request is a 401. It\n * never falls through to another mode.\n *\n * - No claimant: only an `mcpOAuth(...)` server answers with the discovery challenge; others a plain 401.\n * - Two claimants (an api-key header AND a bearer): ambiguous, refused — never \"pick the one that works\".\n * - One claimant: its rejection IS the answer.\n *\n * The set is checked at construction (bind time): `vendorAuth` / public are refused, every mode's\n * verifier must be bound, and no two modes may be able to claim one request.\n */\nexport class McpDoorAuthenticator {\n private constructor(\n private readonly modes: readonly McpDoorMode[],\n public readonly oauth: McpOAuthDoorMode | undefined,\n ) {}\n\n // webpieces-disable no-function-outside-class -- validating factory; the constructor stays private so an unchecked set cannot exist\n static create(\n auth: readonly McpHttpAuth[],\n resource: string,\n verifiers: CredentialVerifiers | undefined,\n ): McpDoorAuthenticator {\n const oauthModes = auth.filter(\n (mode: McpHttpAuth): mode is McpOAuthMode => mode instanceof McpOAuthMode,\n );\n if (oauthModes.length > 1) {\n throw new Error(\n 'WpMcpServerConfig.setAuth([...]) accepts at most one mcpOAuth(...) mode.',\n );\n }\n McpDoorAuthenticator.refuseNonCallers(auth);\n const methods = auth.filter(\n (mode: McpHttpAuth): mode is AuthMethod => !(mode instanceof McpOAuthMode),\n );\n if (methods.length > 0) {\n // The same declaration rules @WpAuth applies: no duplicate kinds (one oidc per scope), valid shapes.\n new AuthMeta([methods[0], ...methods.slice(1)], undefined);\n }\n const modes: McpDoorMode[] = [];\n const oauth =\n oauthModes[0] === undefined ? undefined : new McpOAuthDoorMode(oauthModes[0], resource);\n if (methods.length > 0) {\n if (!verifiers) {\n throw new Error(\n 'WpMcpServerConfig.setAuth([...]) holds @WpAuth modes; add setCredentialVerifiers(router.credentialVerifiers()).',\n );\n }\n McpDoorAuthenticator.refuseUnverifiable(methods, verifiers);\n modes.push(...McpDoorAuthenticator.doorModes(methods, verifiers));\n }\n if (oauth) modes.push(oauth);\n McpDoorAuthenticator.refuseOverlaps(modes, oauth);\n for (const mode of modes) if (mode instanceof JwtDoorMode) mode.excluding(modes);\n return new McpDoorAuthenticator(modes, oauth);\n }\n\n /**\n * Whether a 401 for this request carries the `mcpOAuth(...)` discovery challenge: only when the\n * server has that mode AND the request either carried no credential at all or was classified as an\n * OAuth access token. A rejected `jwt()` / OIDC / api-key credential gets a plain 401, with no hint\n * of which mode came closest.\n */\n challengeApplies(req: McpDoorRequest): boolean {\n if (!this.oauth) return false;\n const claimants = this.modes.filter((mode: McpDoorMode) => mode.claims(req));\n return claimants.length === 0 || (claimants.length === 1 && claimants[0] === this.oauth);\n }\n\n async authenticate(req: McpDoorRequest): Promise<McpDoorPass> {\n const claimants = this.modes.filter((mode: McpDoorMode) => mode.claims(req));\n if (claimants.length === 0) {\n throw new ApiUnauthorizedError('MCP request has no credential this server accepts.');\n }\n if (claimants.length > 1) {\n throw new ApiUnauthorizedError(\n 'MCP request carries more than one credential; send exactly one.',\n );\n }\n return claimants[0].authenticate(req);\n }\n\n // webpieces-disable no-function-outside-class -- bind-time validation helper of the private constructor\n private static refuseNonCallers(auth: readonly McpHttpAuth[]): void {\n // A public mode does not compile here (McpHttpAuth excludes it); one smuggled past the type is\n // refused by the AuthMeta declaration check that follows.\n for (const method of auth) {\n if (method.kind === 'vendorAuth') {\n throw new Error(\n 'WpMcpServerConfig.setAuth([...]) refuses vendorAuth(...): ' +\n 'it identifies no caller to authorize tools against. Use oidcSameProject(), oidcSvcs([...]), ' +\n 'jwt(), apiKey(...), sharedSecret(...) or mcpOAuth(...).',\n );\n }\n }\n }\n\n // webpieces-disable no-function-outside-class -- bind-time validation helper of the private constructor\n private static refuseUnverifiable(\n methods: readonly AuthMethod[],\n verifiers: CredentialVerifiers,\n ): void {\n const missing = methods\n .map((method: AuthMethod) => verifiers.missingVerifier(method))\n .filter((line: string | undefined): line is string => line !== undefined);\n if (missing.length > 0) {\n throw new Error(\n 'WpMcpServerConfig.setAuth([...]) accepts a mode this server cannot verify, so it refuses to start. Add:\\n ' +\n missing.join('\\n '),\n );\n }\n }\n\n // webpieces-disable no-function-outside-class -- bind-time construction helper of the private constructor\n private static doorModes(\n methods: readonly AuthMethod[],\n verifiers: CredentialVerifiers,\n ): McpDoorMode[] {\n const modes: McpDoorMode[] = [];\n const oidc = methods.filter(\n (method: AuthMethod): method is OidcAuthMethod => method.kind === 'oidc',\n );\n if (oidc[0] !== undefined)\n modes.push(new OidcDoorMode([oidc[0], ...oidc.slice(1)], verifiers));\n for (const method of methods) {\n switch (method.kind) {\n case 'oidc':\n break;\n case 'jwt':\n modes.push(new JwtDoorMode(verifiers));\n break;\n case 'shared-secret':\n modes.push(new SharedSecretDoorMode(method.secretKey, verifiers));\n break;\n case 'apikey':\n modes.push(new ApiKeyDoorMode(method.regime, method.credentials, verifiers));\n break;\n case 'vendorAuth':\n throw new ApiImplementationError(\n 'vendorAuth was refused before door modes are built.',\n );\n }\n }\n return modes;\n }\n\n /**\n * `claims()` sets must be DISJOINT BY CONSTRUCTION. The bearer space is the only shared one, so the\n * refusals are about it:\n * - `mcpOAuth(..., everyBearer())` claims every bearer, so it stands alone in the bearer family;\n * - a bearer `apiKey(...)` claims every bearer too, so it stands alone as well;\n * - `mcpOAuth(..., jwtIssuer())` may not list a Google issuer beside an OIDC mode.\n * `jwt()` claims the remainder, and `McpAccessTokenFormat.opaquePrefix` refuses a prefix that could\n * overlap a JWT or a dev-oidc token, so nothing else can collide.\n */\n // webpieces-disable no-function-outside-class -- bind-time validation helper of the private constructor\n private static refuseOverlaps(\n modes: readonly McpDoorMode[],\n oauth: McpOAuthDoorMode | undefined,\n ): void {\n const bearerModes = modes.filter((mode: McpDoorMode) => mode.bearerFamily);\n const greedy = bearerModes.filter(\n (mode: McpDoorMode) =>\n mode instanceof ApiKeyDoorMode ||\n (mode instanceof McpOAuthDoorMode && mode.mode.tokenFormat.kind === 'every-bearer'),\n );\n for (const mode of greedy) {\n if (bearerModes.length > 1) {\n const others = bearerModes\n .filter((other: McpDoorMode) => other !== mode)\n .map((other: McpDoorMode) => other.name);\n throw new Error(\n `WpMcpServerConfig.setAuth([...]): ${mode.name} claims EVERY bearer credential, so it overlaps ` +\n `${others.join(', ')}. Give it a disjoint shape (mcpOAuth: McpAccessTokenFormat.opaquePrefix(...) ` +\n 'or .jwtIssuer(); apiKey: a header credential) or remove the other bearer modes.',\n );\n }\n }\n const oidc = modes.some((mode: McpDoorMode) => mode instanceof OidcDoorMode);\n if (oauth && oidc && oauth.mode.tokenFormat.kind === 'jwt-issuer') {\n const google = oauth.mode.authorizationServers.filter((issuer: string) =>\n GOOGLE_OIDC_ISSUERS.includes(issuer),\n );\n if (google.length > 0) {\n throw new Error(\n `WpMcpServerConfig.setAuth([...]): mcpOAuth(...) trusts the Google issuer ${google.join(', ')}, ` +\n 'so it overlaps the OIDC mode, which claims every Google-issued token.',\n );\n }\n }\n }\n}\n"]}
@@ -106,7 +106,7 @@ export declare class McpToolset {
106
106
  private logImplementationFailure;
107
107
  /** Everything a tool call does. No catch in here: failures propagate to handleCallTool. */
108
108
  private callTool;
109
- /** The trusted caller of the CURRENT RequestContext: a verified user, else a verified machine. */
109
+ /** The trusted caller of the CURRENT RequestContext: a verified user, else a verified machine (with its roles). */
110
110
  private currentCaller;
111
111
  private progressReporter;
112
112
  private progressParams;
package/src/McpToolset.js CHANGED
@@ -253,15 +253,19 @@ class McpToolset {
253
253
  structuredContent: structured,
254
254
  };
255
255
  }
256
- /** The trusted caller of the CURRENT RequestContext: a verified user, else a verified machine. */
256
+ /** The trusted caller of the CURRENT RequestContext: a verified user, else a verified machine (with its roles). */
257
257
  currentCaller() {
258
258
  const userId = core_context_1.RequestContext.getTrusted(core_util_1.WebpiecesCoreHeaders.USER_ID);
259
259
  if (typeof userId === 'string' && userId.trim() !== '') {
260
260
  return new McpCallerFacts(userId, http_routing_1.CanonicalUserRoles.read());
261
261
  }
262
262
  const machine = core_context_1.RequestContext.getTrusted(http_routing_1.VERIFIED_MACHINE_CALLER);
263
- if (machine instanceof http_routing_1.VerifiedMachineCaller)
264
- return new McpCallerFacts(machine.identity, []);
263
+ if (machine instanceof http_routing_1.VerifiedMachineCaller) {
264
+ // A machine (a service / AI agent with no user id, #1420) reports its roles: `[]` when it was
265
+ // granted none, and none at all when no USER_ROLES reached this hop.
266
+ const granted = core_context_1.RequestContext.getTrusted(core_util_1.WebpiecesCoreHeaders.USER_ROLES) !== undefined;
267
+ return new McpCallerFacts(machine.identity, granted ? http_routing_1.CanonicalUserRoles.read() : []);
268
+ }
265
269
  throw new core_util_1.ApiUnauthorizedError('MCP tool call has no trusted caller in its RequestContext.');
266
270
  }
267
271
  progressReporter(context) {
@@ -1 +1 @@
1
- {"version":3,"file":"McpToolset.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpToolset.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport {\n CallToolRequest,\n CallToolResult,\n Implementation,\n ListToolsResult,\n McpServer,\n ProgressToken,\n ProtocolError,\n ServerContext,\n ServerOptions,\n Tool,\n} from '@modelcontextprotocol/server';\nimport {\n ApiErrorBoundary,\n ApiImplementationError,\n ApiJsonSchemaValidator,\n ApiMethodInfo,\n ApiUnauthorizedError,\n DtoValue,\n LogApiCallImpl,\n LogManager,\n Mcp,\n McpInvocationContext,\n McpProgressReporter,\n McpToolProfiles,\n toError,\n WebpiecesCoreHeaders,\n} from '@webpieces/core-util';\nimport { RequestContext, RequestContextApiCallContext } from '@webpieces/core-context';\nimport {\n AuthorizationService,\n CanonicalUserRoles,\n VERIFIED_MACHINE_CALLER,\n VerifiedMachineCaller,\n} from '@webpieces/http-routing';\nimport { McpApiBinding } from './McpApiBinding';\nimport { McpApiDispatcher } from './McpApiDispatcher';\nimport { McpProtocol } from './McpProtocol';\nimport { McpToolCatalog } from './McpToolCatalog';\nimport { McpCorrelation } from './McpToolCallRendering';\nimport { McpToolDeniedError } from './McpToolPolicy';\nimport { AuthorizedMcpTools, McpToolRegistry, RegisteredMcpTool } from './McpToolRegistry';\nimport { WpMcpErrorTranslator } from './WpMcpErrorTranslator';\n\nconst log = LogManager.getLogger('McpToolset');\n\n/**\n * The request DTO the edge `LogApiCall` lines report. These boundaries run outside any API dispatch,\n * so there is no contract DTO to log; the step name is the fact worth having.\n */\nclass McpEdgeRequest {\n constructor(public readonly step: string) {}\n}\n\nclass McpSdkOperation {\n toolStage = false;\n constructor(public readonly name: string) {}\n}\n\n/**\n * `Tool.annotations` exactly as tools/list publishes it: the four hints PLUS the human-readable\n * `title` (#1180). The Claude Connectors Directory checks `annotations.title`; clients on the\n * 2025-06-18+ spec render the top-level `Tool.title`, so both carry the one title — the method's\n * `@summary` JSDoc tag, read from the generated catalog (#1296).\n */\nclass McpPublishedToolAnnotations {\n readonly title: string;\n readonly readOnlyHint: boolean;\n readonly destructiveHint: boolean;\n readonly idempotentHint: boolean;\n readonly openWorldHint: boolean;\n\n constructor(tool: RegisteredMcpTool) {\n this.title = tool.title;\n this.readOnlyHint = tool.annotations.readOnlyHint;\n this.destructiveHint = tool.annotations.destructiveHint;\n this.idempotentHint = tool.annotations.idempotentHint;\n this.openWorldHint = tool.annotations.openWorldHint;\n }\n}\n\n/**\n * What ONE MCP request may see and how it is cancelled, established by the transport before the SDK\n * handler runs: the caller's authorized projection, and the signal that fires when the caller goes away.\n */\nexport class McpToolRequest {\n constructor(\n public readonly tools: AuthorizedMcpTools,\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 request-authorized view when the transport has one, so the SDK's\n * pre-dispatch SEP-2243 `Mcp-Param-*` header validation still runs without registering SDK-side tool\n * callbacks.\n */\nclass WpSdkMcpServer extends McpServer {\n constructor(\n info: Implementation,\n options: ServerOptions,\n private readonly schemaSource: (name: string) => RegisteredMcpTool | undefined,\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.schemaSource(name)?.inputSchema as Record<string, DtoValue> | undefined;\n }\n}\n\n/**\n * McpToolset — the shared core of every MCP transport: WHICH tools are served, and everything that\n * happens to a request once its caller is established.\n *\n * ```ts\n * const toolset = new McpToolset(\n * bindings, // McpApiBinding.local / .remote\n * McpToolCatalog.fromPackages(['@myorg/my-apis'], __dirname),\n * router.authorizationService(),\n * ['user'], // #1187 profiles: WHICH tools this toolset serves\n * );\n * const client = await WpMcpInMemory.connect(toolset); // in-process agent\n * new WpMcpServer(config, toolset).bind(app, options); // HTTP\n * ```\n *\n * It owns the {@link McpToolRegistry}, the per-caller {@link AuthorizedMcpTools} projection, input and\n * output schema validation, dispatch through `binding.invoke`, and the error translation. Its ONLY input\n * about the caller is the CURRENT `RequestContext` — the trusted USER_ID / USER_ROLES / ORG_ID and a\n * `VerifiedMachineCaller`, exactly what `AuthorizationService.authorize` reads. A transport's only job\n * is to establish that context; `@WpAuthorization` on the contract is the single policy everywhere.\n *\n * Profiles are fixed per toolset: `/mcp/user` and `/mcp/admin` are two toolsets (two servers), so paths\n * split by WHICH TOOLS, never by HOW THE CALLER AUTHENTICATES.\n *\n * Before tools/list and tools/call hand an IMPLEMENTATION-kind failure to the translator, they\n * `log.error` it with its stack and the requestId ({@link logImplementationFailure}, #1055). The\n * translator reduces such a failure to \"Internal Error\" for the caller, so that line is the only place\n * its cause exists.\n */\nexport class McpToolset {\n readonly registry: McpToolRegistry;\n readonly profiles: readonly string[];\n /** A short hash of the served tool surface, appended to every server version and tool `_meta`. */\n readonly revision: string;\n private readonly dispatcher = new McpApiDispatcher();\n /** Classifies a failure exactly as the translator will render it, so the two cannot disagree. */\n private readonly boundary = new ApiErrorBoundary();\n private readonly outputSchemas = new ApiJsonSchemaValidator();\n /**\n * tools/list has NO filter chain above it — `LogApiFilter` never sees a list the SDK renders. Wrapping\n * it in `LogApiCall` is this repo's one line on logging.\n */\n private readonly logApiCall = new LogApiCallImpl(new RequestContextApiCallContext());\n /** tools/call reads `McpRegistry`; tools/list replies stay framework-owned. See McpErrorTranslator. */\n private readonly translator: WpMcpErrorTranslator = new WpMcpErrorTranslator();\n\n constructor(\n bindings: readonly McpApiBinding[],\n /**\n * The tools the build published — ONE catalog per bound contract, each the\n * `mcp-<ContractClass>-tools.json` `wp-openapi` wrote beside its library's OpenAPI documents,\n * read with `McpToolCatalog.fromPackages([...], __dirname)`. The only source of a tool's\n * documentation and schemas; `McpToolRegistry` refuses to build unless it holds exactly one\n * catalog per bound contract.\n */\n catalogs: readonly McpToolCatalog[],\n public readonly authorizationService: AuthorizationService,\n /** The union of `@WpMcpTool` profiles this toolset serves, at least one (`[Mcp.DEFAULT]` for untagged tools). */\n profiles: McpToolProfiles,\n ) {\n if (bindings.length === 0)\n throw new Error('McpToolset requires at least one API contract binding.');\n this.profiles = Mcp.profiles(profiles, 'McpToolset profiles');\n this.registry = new McpToolRegistry(bindings, catalogs, authorizationService);\n this.revision = this.calculateRevision();\n }\n\n /** The tools the caller in the CURRENT RequestContext may see, sorted by name. */\n async authorizedTools(): Promise<AuthorizedMcpTools> {\n return AuthorizedMcpTools.create(this.registry, this.authorizationService, this.profiles);\n }\n\n /**\n * A fresh SDK server whose tools/list and tools/call are this toolset's. `request` establishes, per\n * MCP request, what the caller may see; `schemaView` answers the SDK's input-schema lookup (the HTTP\n * transport passes the request's projection, the in-memory one the full registry).\n */\n createSdkServer(\n info: Implementation,\n options: ServerOptions,\n request: () => Promise<McpToolRequest>,\n schemaView: (name: string) => RegisteredMcpTool | undefined,\n ): McpServer {\n const server = new WpSdkMcpServer(info, options, schemaView);\n server.server.removeRequestHandler('tools/list');\n server.server.removeRequestHandler('tools/call');\n server.server.setRequestHandler(\n 'tools/list',\n async (_request: object): Promise<ListToolsResult> =>\n this.sdkBoundary(new McpSdkOperation('tools/list'), () =>\n this.logEdge('tools/list', async () => this.listTools((await request()).tools)),\n ) as Promise<ListToolsResult>,\n );\n server.server.setRequestHandler(\n 'tools/call',\n async (call: CallToolRequest, sdkContext: ServerContext): Promise<CallToolResult> =>\n this.handleCallTool(server, call, request, sdkContext),\n );\n return server;\n }\n\n /** Run one un-filtered edge step under `LogApiCall`, so its failure gets exactly one log line. */\n async logEdge<R>(step: string, work: () => Promise<R>): Promise<R> {\n return this.logApiCall.execute(\n new ApiMethodInfo('server', 'McpEndpoint', step, McpProtocol),\n new McpEdgeRequest(step),\n work,\n );\n }\n\n /** ONE SDK callback catch implementation; the SDK intercepts foreign throws itself. */\n private async sdkBoundary<R>(\n operation: McpSdkOperation,\n work: () => Promise<R>,\n ): Promise<R | CallToolResult> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- shared SDK response boundary, preserving protocol versus tool envelopes\n try {\n return await work();\n } catch (err: unknown) {\n const error = toError(err);\n if (error instanceof McpToolDeniedError)\n throw this.translator.unknownTool(error.toolName);\n if (!operation.toolStage && error instanceof ProtocolError) throw error;\n this.logImplementationFailure(operation.name, error);\n if (operation.toolStage) return this.translator.toToolCallResult(error);\n this.translator.toListError(error);\n }\n }\n\n private listTools(view: AuthorizedMcpTools): ListToolsResult {\n const tools: Tool[] = view.tools.map((tool: RegisteredMcpTool) =>\n this.toolDefinition(tool),\n );\n return { tools };\n }\n\n private toolDefinition(tool: RegisteredMcpTool): Tool {\n return {\n name: tool.name,\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema as Tool['inputSchema'],\n outputSchema: tool.outputSchema as Tool['outputSchema'],\n annotations: new McpPublishedToolAnnotations(tool),\n _meta: { webpiecesRegistryRevision: this.revision },\n };\n }\n\n /**\n * SDK tool callback: an unknown or hidden tool is a JSON-RPC -32602; every failure after the tool\n * is found is an `isError: true` result, identically for local and remote bindings.\n */\n private async handleCallTool(\n server: McpServer,\n call: CallToolRequest,\n request: () => Promise<McpToolRequest>,\n sdkContext: ServerContext,\n ): Promise<CallToolResult> {\n const name = call.params.name;\n const operation = new McpSdkOperation(`tools/call ${name}`);\n return this.sdkBoundary(operation, async () => {\n McpCorrelation.stamp(new McpCorrelation(sdkContext.mcpReq.id, McpProtocol, name));\n const established = await request();\n const tool = established.tools.find(name);\n if (!tool) throw this.translator.unknownTool(name);\n McpCorrelation.stamp(\n new McpCorrelation(sdkContext.mcpReq.id, tool.apiClass, name, tool.operation),\n );\n operation.toolStage = true;\n const result = await this.callTool(tool, call, established, sdkContext);\n result._meta = this.translator.resultMeta(McpCorrelation.requestId());\n return server.server.projectCallToolResult(\n result,\n tool.outputSchema as Record<string, DtoValue>,\n );\n }) as Promise<CallToolResult>;\n }\n\n /**\n * ONE `log.error` line, with the stack and the requestId, for a failure that is THIS server's bug\n * — the `implementation` kind, which the translator reduces to \"Internal Error\" and a requestId\n * for the caller (#1055).\n *\n * `LogApiCall` does not cover this. An MCP output-schema violation is raised AFTER the controller\n * returned, so the only `LogApiCall` line for that call reads `resp-SUCCESS`; before this line\n * existed, the requestId support was handed led to a log with no error in it. Where a\n * `[API-*-resp-FAIL]` line does exist it carries the error's message, not its stack. So this line\n * is not the bare duplicate `ApiErrorBoundary` stopped writing: it is the one that says WHERE.\n *\n * Every other kind (bad-request, end-user, dependency, ...) is a caller's or a downstream's\n * failure, published to the caller with its own text, and `LogApiCall` already records it.\n */\n private logImplementationFailure(entryPoint: string, error: Error): void {\n if (this.boundary.encode(error).kind !== 'implementation') return;\n log.error(\n `MCP ${entryPoint} failed with an implementation error requestId=${McpCorrelation.requestId()}`,\n error,\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 call: CallToolRequest,\n established: McpToolRequest,\n sdkContext: ServerContext,\n ): Promise<CallToolResult> {\n const caller = this.currentCaller();\n const invocation = new McpInvocationContext(\n sdkContext.mcpReq.id,\n tool.name,\n caller.subject,\n caller.roles,\n AbortSignal.any([sdkContext.mcpReq.signal, established.disconnectSignal]),\n this.progressReporter(sdkContext),\n );\n const args = (call.params.arguments ?? {}) as DtoValue;\n const value = await this.dispatcher.call(tool, args, invocation, this.authorizationService);\n const outputFailure = this.outputSchemas.validate(tool.outputSchema, value);\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 /** The trusted caller of the CURRENT RequestContext: a verified user, else a verified machine. */\n private currentCaller(): McpCallerFacts {\n const userId = RequestContext.getTrusted(WebpiecesCoreHeaders.USER_ID);\n if (typeof userId === 'string' && userId.trim() !== '') {\n return new McpCallerFacts(userId, CanonicalUserRoles.read());\n }\n const machine = RequestContext.getTrusted(VERIFIED_MACHINE_CALLER);\n if (machine instanceof VerifiedMachineCaller)\n return new McpCallerFacts(machine.identity, []);\n throw new ApiUnauthorizedError(\n 'MCP tool call has no trusted caller in its RequestContext.',\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 calculateRevision(): string {\n const surface = this.registry.tools.map((tool: RegisteredMcpTool) => [\n tool.name,\n tool.title,\n tool.description,\n tool.inputSchema,\n tool.outputSchema,\n tool.annotations,\n tool.profiles,\n ]);\n return createHash('sha256')\n .update(JSON.stringify([this.profiles, surface]))\n .digest('hex')\n .slice(0, 12);\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\n/** Who the current RequestContext says is calling, as McpInvocationContext reports it. */\nclass McpCallerFacts {\n constructor(\n public readonly subject: string,\n public readonly roles: readonly string[],\n ) {}\n}\n"]}
1
+ {"version":3,"file":"McpToolset.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpToolset.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport {\n CallToolRequest,\n CallToolResult,\n Implementation,\n ListToolsResult,\n McpServer,\n ProgressToken,\n ProtocolError,\n ServerContext,\n ServerOptions,\n Tool,\n} from '@modelcontextprotocol/server';\nimport {\n ApiErrorBoundary,\n ApiImplementationError,\n ApiJsonSchemaValidator,\n ApiMethodInfo,\n ApiUnauthorizedError,\n DtoValue,\n LogApiCallImpl,\n LogManager,\n Mcp,\n McpInvocationContext,\n McpProgressReporter,\n McpToolProfiles,\n toError,\n WebpiecesCoreHeaders,\n} from '@webpieces/core-util';\nimport { RequestContext, RequestContextApiCallContext } from '@webpieces/core-context';\nimport {\n AuthorizationService,\n CanonicalUserRoles,\n VERIFIED_MACHINE_CALLER,\n VerifiedMachineCaller,\n} from '@webpieces/http-routing';\nimport { McpApiBinding } from './McpApiBinding';\nimport { McpApiDispatcher } from './McpApiDispatcher';\nimport { McpProtocol } from './McpProtocol';\nimport { McpToolCatalog } from './McpToolCatalog';\nimport { McpCorrelation } from './McpToolCallRendering';\nimport { McpToolDeniedError } from './McpToolPolicy';\nimport { AuthorizedMcpTools, McpToolRegistry, RegisteredMcpTool } from './McpToolRegistry';\nimport { WpMcpErrorTranslator } from './WpMcpErrorTranslator';\n\nconst log = LogManager.getLogger('McpToolset');\n\n/**\n * The request DTO the edge `LogApiCall` lines report. These boundaries run outside any API dispatch,\n * so there is no contract DTO to log; the step name is the fact worth having.\n */\nclass McpEdgeRequest {\n constructor(public readonly step: string) {}\n}\n\nclass McpSdkOperation {\n toolStage = false;\n constructor(public readonly name: string) {}\n}\n\n/**\n * `Tool.annotations` exactly as tools/list publishes it: the four hints PLUS the human-readable\n * `title` (#1180). The Claude Connectors Directory checks `annotations.title`; clients on the\n * 2025-06-18+ spec render the top-level `Tool.title`, so both carry the one title — the method's\n * `@summary` JSDoc tag, read from the generated catalog (#1296).\n */\nclass McpPublishedToolAnnotations {\n readonly title: string;\n readonly readOnlyHint: boolean;\n readonly destructiveHint: boolean;\n readonly idempotentHint: boolean;\n readonly openWorldHint: boolean;\n\n constructor(tool: RegisteredMcpTool) {\n this.title = tool.title;\n this.readOnlyHint = tool.annotations.readOnlyHint;\n this.destructiveHint = tool.annotations.destructiveHint;\n this.idempotentHint = tool.annotations.idempotentHint;\n this.openWorldHint = tool.annotations.openWorldHint;\n }\n}\n\n/**\n * What ONE MCP request may see and how it is cancelled, established by the transport before the SDK\n * handler runs: the caller's authorized projection, and the signal that fires when the caller goes away.\n */\nexport class McpToolRequest {\n constructor(\n public readonly tools: AuthorizedMcpTools,\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 request-authorized view when the transport has one, so the SDK's\n * pre-dispatch SEP-2243 `Mcp-Param-*` header validation still runs without registering SDK-side tool\n * callbacks.\n */\nclass WpSdkMcpServer extends McpServer {\n constructor(\n info: Implementation,\n options: ServerOptions,\n private readonly schemaSource: (name: string) => RegisteredMcpTool | undefined,\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.schemaSource(name)?.inputSchema as Record<string, DtoValue> | undefined;\n }\n}\n\n/**\n * McpToolset — the shared core of every MCP transport: WHICH tools are served, and everything that\n * happens to a request once its caller is established.\n *\n * ```ts\n * const toolset = new McpToolset(\n * bindings, // McpApiBinding.local / .remote\n * McpToolCatalog.fromPackages(['@myorg/my-apis'], __dirname),\n * router.authorizationService(),\n * ['user'], // #1187 profiles: WHICH tools this toolset serves\n * );\n * const client = await WpMcpInMemory.connect(toolset); // in-process agent\n * new WpMcpServer(config, toolset).bind(app, options); // HTTP\n * ```\n *\n * It owns the {@link McpToolRegistry}, the per-caller {@link AuthorizedMcpTools} projection, input and\n * output schema validation, dispatch through `binding.invoke`, and the error translation. Its ONLY input\n * about the caller is the CURRENT `RequestContext` — the trusted USER_ID / USER_ROLES / ORG_ID and a\n * `VerifiedMachineCaller`, exactly what `AuthorizationService.authorize` reads. A transport's only job\n * is to establish that context; `@WpAuthorization` on the contract is the single policy everywhere.\n *\n * Profiles are fixed per toolset: `/mcp/user` and `/mcp/admin` are two toolsets (two servers), so paths\n * split by WHICH TOOLS, never by HOW THE CALLER AUTHENTICATES.\n *\n * Before tools/list and tools/call hand an IMPLEMENTATION-kind failure to the translator, they\n * `log.error` it with its stack and the requestId ({@link logImplementationFailure}, #1055). The\n * translator reduces such a failure to \"Internal Error\" for the caller, so that line is the only place\n * its cause exists.\n */\nexport class McpToolset {\n readonly registry: McpToolRegistry;\n readonly profiles: readonly string[];\n /** A short hash of the served tool surface, appended to every server version and tool `_meta`. */\n readonly revision: string;\n private readonly dispatcher = new McpApiDispatcher();\n /** Classifies a failure exactly as the translator will render it, so the two cannot disagree. */\n private readonly boundary = new ApiErrorBoundary();\n private readonly outputSchemas = new ApiJsonSchemaValidator();\n /**\n * tools/list has NO filter chain above it — `LogApiFilter` never sees a list the SDK renders. Wrapping\n * it in `LogApiCall` is this repo's one line on logging.\n */\n private readonly logApiCall = new LogApiCallImpl(new RequestContextApiCallContext());\n /** tools/call reads `McpRegistry`; tools/list replies stay framework-owned. See McpErrorTranslator. */\n private readonly translator: WpMcpErrorTranslator = new WpMcpErrorTranslator();\n\n constructor(\n bindings: readonly McpApiBinding[],\n /**\n * The tools the build published — ONE catalog per bound contract, each the\n * `mcp-<ContractClass>-tools.json` `wp-openapi` wrote beside its library's OpenAPI documents,\n * read with `McpToolCatalog.fromPackages([...], __dirname)`. The only source of a tool's\n * documentation and schemas; `McpToolRegistry` refuses to build unless it holds exactly one\n * catalog per bound contract.\n */\n catalogs: readonly McpToolCatalog[],\n public readonly authorizationService: AuthorizationService,\n /** The union of `@WpMcpTool` profiles this toolset serves, at least one (`[Mcp.DEFAULT]` for untagged tools). */\n profiles: McpToolProfiles,\n ) {\n if (bindings.length === 0)\n throw new Error('McpToolset requires at least one API contract binding.');\n this.profiles = Mcp.profiles(profiles, 'McpToolset profiles');\n this.registry = new McpToolRegistry(bindings, catalogs, authorizationService);\n this.revision = this.calculateRevision();\n }\n\n /** The tools the caller in the CURRENT RequestContext may see, sorted by name. */\n async authorizedTools(): Promise<AuthorizedMcpTools> {\n return AuthorizedMcpTools.create(this.registry, this.authorizationService, this.profiles);\n }\n\n /**\n * A fresh SDK server whose tools/list and tools/call are this toolset's. `request` establishes, per\n * MCP request, what the caller may see; `schemaView` answers the SDK's input-schema lookup (the HTTP\n * transport passes the request's projection, the in-memory one the full registry).\n */\n createSdkServer(\n info: Implementation,\n options: ServerOptions,\n request: () => Promise<McpToolRequest>,\n schemaView: (name: string) => RegisteredMcpTool | undefined,\n ): McpServer {\n const server = new WpSdkMcpServer(info, options, schemaView);\n server.server.removeRequestHandler('tools/list');\n server.server.removeRequestHandler('tools/call');\n server.server.setRequestHandler(\n 'tools/list',\n async (_request: object): Promise<ListToolsResult> =>\n this.sdkBoundary(new McpSdkOperation('tools/list'), () =>\n this.logEdge('tools/list', async () => this.listTools((await request()).tools)),\n ) as Promise<ListToolsResult>,\n );\n server.server.setRequestHandler(\n 'tools/call',\n async (call: CallToolRequest, sdkContext: ServerContext): Promise<CallToolResult> =>\n this.handleCallTool(server, call, request, sdkContext),\n );\n return server;\n }\n\n /** Run one un-filtered edge step under `LogApiCall`, so its failure gets exactly one log line. */\n async logEdge<R>(step: string, work: () => Promise<R>): Promise<R> {\n return this.logApiCall.execute(\n new ApiMethodInfo('server', 'McpEndpoint', step, McpProtocol),\n new McpEdgeRequest(step),\n work,\n );\n }\n\n /** ONE SDK callback catch implementation; the SDK intercepts foreign throws itself. */\n private async sdkBoundary<R>(\n operation: McpSdkOperation,\n work: () => Promise<R>,\n ): Promise<R | CallToolResult> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- shared SDK response boundary, preserving protocol versus tool envelopes\n try {\n return await work();\n } catch (err: unknown) {\n const error = toError(err);\n if (error instanceof McpToolDeniedError)\n throw this.translator.unknownTool(error.toolName);\n if (!operation.toolStage && error instanceof ProtocolError) throw error;\n this.logImplementationFailure(operation.name, error);\n if (operation.toolStage) return this.translator.toToolCallResult(error);\n this.translator.toListError(error);\n }\n }\n\n private listTools(view: AuthorizedMcpTools): ListToolsResult {\n const tools: Tool[] = view.tools.map((tool: RegisteredMcpTool) =>\n this.toolDefinition(tool),\n );\n return { tools };\n }\n\n private toolDefinition(tool: RegisteredMcpTool): Tool {\n return {\n name: tool.name,\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema as Tool['inputSchema'],\n outputSchema: tool.outputSchema as Tool['outputSchema'],\n annotations: new McpPublishedToolAnnotations(tool),\n _meta: { webpiecesRegistryRevision: this.revision },\n };\n }\n\n /**\n * SDK tool callback: an unknown or hidden tool is a JSON-RPC -32602; every failure after the tool\n * is found is an `isError: true` result, identically for local and remote bindings.\n */\n private async handleCallTool(\n server: McpServer,\n call: CallToolRequest,\n request: () => Promise<McpToolRequest>,\n sdkContext: ServerContext,\n ): Promise<CallToolResult> {\n const name = call.params.name;\n const operation = new McpSdkOperation(`tools/call ${name}`);\n return this.sdkBoundary(operation, async () => {\n McpCorrelation.stamp(new McpCorrelation(sdkContext.mcpReq.id, McpProtocol, name));\n const established = await request();\n const tool = established.tools.find(name);\n if (!tool) throw this.translator.unknownTool(name);\n McpCorrelation.stamp(\n new McpCorrelation(sdkContext.mcpReq.id, tool.apiClass, name, tool.operation),\n );\n operation.toolStage = true;\n const result = await this.callTool(tool, call, established, sdkContext);\n result._meta = this.translator.resultMeta(McpCorrelation.requestId());\n return server.server.projectCallToolResult(\n result,\n tool.outputSchema as Record<string, DtoValue>,\n );\n }) as Promise<CallToolResult>;\n }\n\n /**\n * ONE `log.error` line, with the stack and the requestId, for a failure that is THIS server's bug\n * — the `implementation` kind, which the translator reduces to \"Internal Error\" and a requestId\n * for the caller (#1055).\n *\n * `LogApiCall` does not cover this. An MCP output-schema violation is raised AFTER the controller\n * returned, so the only `LogApiCall` line for that call reads `resp-SUCCESS`; before this line\n * existed, the requestId support was handed led to a log with no error in it. Where a\n * `[API-*-resp-FAIL]` line does exist it carries the error's message, not its stack. So this line\n * is not the bare duplicate `ApiErrorBoundary` stopped writing: it is the one that says WHERE.\n *\n * Every other kind (bad-request, end-user, dependency, ...) is a caller's or a downstream's\n * failure, published to the caller with its own text, and `LogApiCall` already records it.\n */\n private logImplementationFailure(entryPoint: string, error: Error): void {\n if (this.boundary.encode(error).kind !== 'implementation') return;\n log.error(\n `MCP ${entryPoint} failed with an implementation error requestId=${McpCorrelation.requestId()}`,\n error,\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 call: CallToolRequest,\n established: McpToolRequest,\n sdkContext: ServerContext,\n ): Promise<CallToolResult> {\n const caller = this.currentCaller();\n const invocation = new McpInvocationContext(\n sdkContext.mcpReq.id,\n tool.name,\n caller.subject,\n caller.roles,\n AbortSignal.any([sdkContext.mcpReq.signal, established.disconnectSignal]),\n this.progressReporter(sdkContext),\n );\n const args = (call.params.arguments ?? {}) as DtoValue;\n const value = await this.dispatcher.call(tool, args, invocation, this.authorizationService);\n const outputFailure = this.outputSchemas.validate(tool.outputSchema, value);\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 /** The trusted caller of the CURRENT RequestContext: a verified user, else a verified machine (with its roles). */\n private currentCaller(): McpCallerFacts {\n const userId = RequestContext.getTrusted(WebpiecesCoreHeaders.USER_ID);\n if (typeof userId === 'string' && userId.trim() !== '') {\n return new McpCallerFacts(userId, CanonicalUserRoles.read());\n }\n const machine = RequestContext.getTrusted(VERIFIED_MACHINE_CALLER);\n if (machine instanceof VerifiedMachineCaller) {\n // A machine (a service / AI agent with no user id, #1420) reports its roles: `[]` when it was\n // granted none, and none at all when no USER_ROLES reached this hop.\n const granted = RequestContext.getTrusted(WebpiecesCoreHeaders.USER_ROLES) !== undefined;\n return new McpCallerFacts(machine.identity, granted ? CanonicalUserRoles.read() : []);\n }\n throw new ApiUnauthorizedError(\n 'MCP tool call has no trusted caller in its RequestContext.',\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 calculateRevision(): string {\n const surface = this.registry.tools.map((tool: RegisteredMcpTool) => [\n tool.name,\n tool.title,\n tool.description,\n tool.inputSchema,\n tool.outputSchema,\n tool.annotations,\n tool.profiles,\n ]);\n return createHash('sha256')\n .update(JSON.stringify([this.profiles, surface]))\n .digest('hex')\n .slice(0, 12);\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\n/** Who the current RequestContext says is calling, as McpInvocationContext reports it. */\nclass McpCallerFacts {\n constructor(\n public readonly subject: string,\n public readonly roles: readonly string[],\n ) {}\n}\n"]}
@@ -126,6 +126,7 @@ export declare class RecordingOidcVerifier extends OidcHook {
126
126
  readonly tokens: string[];
127
127
  readonly methods: OidcAuthMethod[];
128
128
  verifyOidc(token: string, methods: OidcAuthMethods): Promise<AuthenticatedMachineIdentity>;
129
+ originatingServiceRoles(_identity: AuthenticatedMachineIdentity): Promise<readonly string[]>;
129
130
  }
130
131
  export declare class BoundaryProbeFilter extends Filter<MethodMeta, WpResponse<unknown>> {
131
132
  calls: number;
@@ -406,6 +406,9 @@ class RecordingOidcVerifier extends http_routing_2.OidcHook {
406
406
  throw new Error(`unexpected OIDC token '${token}'`);
407
407
  return Promise.resolve(new http_routing_1.AuthenticatedMachineIdentity('oidc', 'test-service@example.test'));
408
408
  }
409
+ async originatingServiceRoles(_identity) {
410
+ return [];
411
+ }
409
412
  }
410
413
  exports.RecordingOidcVerifier = RecordingOidcVerifier;
411
414
  let BoundaryProbeFilter = class BoundaryProbeFilter extends core_util_3.Filter {
@@ -1 +1 @@
1
- {"version":3,"file":"McpRemoteFixtures.js","sourceRoot":"","sources":["../../../../../../packages/http/mcp-server/src/__tests__/McpRemoteFixtures.ts"],"names":[],"mappings":"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","sourcesContent":["import { Mcp } from '@webpieces/core-util';\nimport { AuthenticatedMachineIdentity } from '@webpieces/http-routing';\nimport { ClientRole, WpAuthorization, AuthorizationType, WpAuth, oidcSvcs, OidcAuthMethod, OidcAuthMethods, jwt as jwtAuth } from '@webpieces/core-util';\nimport 'reflect-metadata';\nimport { injectable } from 'inversify';\nimport { ApiBadRequestError, ApiCodedError, ApiConflictError, ApiConnectionError, ApiDependencyBackoffError, ApiDependencyError, ApiDependencyTimeoutError, ApiEndpointNotFoundError, ApiEndUserError, ApiErrorCodec, ApiErrorHttpStatus, ApiForbiddenError, ApiImplementationError, ApiNotFoundError, ApiNotImplementedError, ApiPath, ApiType, ApiPreconditionFailedError, ApiRateLimitedError, ApiRequestTimeoutError, ApiRetriesExhaustedError, ApiUnauthorizedError, ApiUnavailableError, ApiUnprocessableError, ApiUnsupportedMediaTypeError, ContextKey, Endpoint, ErrorTranslator, Filter, WebpiecesDefaultErrorTranslator, HttpResponseDto, LogLevel, Service, WebpiecesCoreHeaders, ApiJsonSchema, McpToolCatalogFile, McpToolDefinition, ObjectSchemaBuilder, WpMcpTool, WpMcpToolHints, MCP, POST, READ, RPC, SVC_TO_SVC } from '@webpieces/core-util';\nimport { RequestContext } from '@webpieces/core-context';\nimport { MethodMeta, OidcHook, WpResponse } from '@webpieces/http-routing';\nimport { McpToolCatalog } from '../McpToolCatalog';\n\n/** Contracts, controllers and test doubles for McpRemoteBinding.integration.spec.ts. */\n\nexport const REMOTE_USER = ContextKey.trusted<string>(\n 'mcpRemoteUser',\n 'verified by the MCP access-token authority before delegation',\n 'x-mcp-remote-user',\n);\nexport const REMOTE_ROLES = ContextKey.trusted<string>(\n 'mcpRemoteRoles',\n 'verified by the MCP access-token authority before delegation',\n 'x-mcp-remote-roles',\n);\nexport const REMOTE_SERVICE = 'mcp-remote-integration';\nexport const REFUSED_SERVICE = 'mcp-remote-refused';\nexport const GARBAGE_SERVICE = 'mcp-remote-garbage';\nexport const OIDC_TOKEN = 'oidc-for-mcp-remote-integration';\nexport const EXTERNAL_MCP_BEARER = 'external-mcp-access-token';\nexport const LOCAL_ENDPOINT_JWT = 'local-endpoint-jwt-that-must-not-cross-the-remote-hop';\nexport const GATEWAY_PATH = '/gateway/mcp';\n\nexport class RemoteRequest {\n /** Search text */\n query!: string;\n\n constructor(query?: string) {\n if (query !== undefined) this.query = query;\n }\n}\n\nexport class RemoteResponse {\n /** Delegated user */\n user!: string;\n\n /** Delegated roles */\n roles!: string;\n\n /** Result */\n result!: string;\n\n constructor(user?: string, roles?: string, result?: string) {\n if (user !== undefined) this.user = user;\n if (roles !== undefined) this.roles = roles;\n if (result !== undefined) this.result = result;\n }\n}\n\n/**\n * The generated tool catalogs these contracts would produce — what `wp-openapi` writes, one\n * `mcp-<ContractClass>-tools.json` per contract. Hand-built here because `@webpieces/mcp-server` does not depend on the\n * TypeScript compiler API; the runtime's job is to BE HANDED a catalog.\n */\nfunction describedString(description: string): ApiJsonSchema {\n const schema = new ApiJsonSchema('string');\n schema.description = description;\n return schema;\n}\n\nfunction searchTool(\n name: string,\n title: string,\n methodName: string,\n description: string,\n): McpToolDefinition {\n return new McpToolDefinition(\n name,\n title,\n methodName,\n description,\n new WpMcpToolHints(true, false, true, false),\n new ObjectSchemaBuilder().required('query', describedString('Search text')).build(),\n new ObjectSchemaBuilder()\n .required('user', describedString('Delegated user'))\n .required('roles', describedString('Delegated roles'))\n .required('result', describedString('Result'))\n .build(), [Mcp.DEFAULT],\n );\n}\n\n/** One contract's hand-built catalog, labelled as built in memory. */\nfunction contractCatalog(contractName: string, tool: McpToolDefinition): McpToolCatalog {\n return new McpToolCatalog(new McpToolCatalogFile(contractName, [tool]), '(built in memory by the spec)');\n}\n\nexport const REMOTE_MCP_CATALOG = contractCatalog(\n 'RemoteMcpApi',\n searchTool('remote_integration_search', 'Remote integration search', 'search', 'Calls a remote Webpieces API.'),\n);\n\n/** The catalogs of every contract the gateway binds — one per contract, exactly as a build writes them. */\nexport const GATEWAY_CATALOGS: readonly McpToolCatalog[] = [\n contractCatalog(\n 'MissingRemoteMcpApi',\n searchTool('missing_remote_integration_search', 'Missing remote integration search', 'search', 'Intentionally absent route.'),\n ),\n contractCatalog('RefusedRemoteApi', searchTool('refused_remote', 'Refused remote call', 'search', 'Nothing listens on this port.')),\n contractCatalog('GarbageRemoteApi', searchTool('garbage_remote', 'Garbage remote response', 'search', 'Answers an undecodable body.')),\n contractCatalog(\n 'OidcFailRemoteApi',\n searchTool('oidc_fail_remote', 'OIDC failure on remote', 'search', 'Its OIDC token cannot be minted.'),\n ),\n contractCatalog('LocalThrowApi', searchTool('local_throw', 'Local throw', 'fail', 'Throws the requested failure in-process.')),\n contractCatalog('RemoteThrowApi', searchTool('remote_throw', 'Remote throw', 'fail', 'Throws the requested failure remotely.')),\n];\n\n@ApiPath('/remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class RemoteMcpApi {\n /**\n * Calls a remote Webpieces API.\n *\n * @summary Remote integration search\n */\n\n @WpAuth([oidcSvcs(['mcp-gateway'])])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('remote_integration_search')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/missing-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class MissingRemoteMcpApi {\n /**\n * Intentionally absent route.\n *\n * @summary Missing remote integration search\n */\n\n @WpAuth([oidcSvcs(['mcp-gateway'])])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('missing_remote_integration_search')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/refused-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class RefusedRemoteApi {\n /**\n * Nothing listens on this port.\n *\n * @summary Refused remote call\n */\n\n @WpAuth([oidcSvcs(['mcp-gateway'])])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('refused_remote')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/garbage-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class GarbageRemoteApi {\n /**\n * Answers an undecodable body.\n *\n * @summary Garbage remote response\n */\n\n @WpAuth([oidcSvcs(['mcp-gateway'])])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('garbage_remote')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/oidc-fail-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class OidcFailRemoteApi {\n /**\n * Its OIDC token cannot be minted.\n *\n * @summary OIDC failure on remote\n */\n\n @WpAuth([oidcSvcs(['mcp-gateway'])])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('oidc_fail_remote')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/local-throw')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class LocalThrowApi {\n /**\n * Throws the requested failure in-process.\n *\n * @summary Local throw\n */\n\n @WpAuth([jwtAuth()])\n @WpAuthorization({ authType: AuthorizationType.ALL_USERS })\n @Endpoint(POST, '/throw', READ, RPC)\n @WpMcpTool('local_throw')\n fail(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/remote-throw')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class RemoteThrowApi {\n /**\n * Throws the requested failure remotely.\n *\n * @summary Remote throw\n */\n\n @WpAuth([oidcSvcs(['mcp-gateway'])])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/throw', READ, RPC)\n @WpMcpTool('remote_throw')\n fail(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n/**\n * One named throw, run identically behind the local and the remote binding.\n *\n * `expectedServerLevel` is the level of the ONE operator line the failure now produces on the server\n * that raised it — `LogApiCall`'s, at `warn` for `[API-server-resp-OTHER]` (a healthy rejection of\n * the caller's mistake) and `error` for `[API-server-resp-FAIL]`. `ApiErrorBoundary` no longer\n * writes a second line of its own (#961).\n */\nexport class ThrowCase {\n constructor(\n public readonly name: string,\n public readonly expectedKind: string,\n public readonly expectedServerLevel: LogLevel,\n public readonly raise: () => never,\n ) {}\n}\n\nexport const THROW_CASES: readonly ThrowCase[] = [\n new ThrowCase('end-user', 'end-user', 'warn', () => {\n throw new ApiEndUserError('The two passwords you entered do not match.', 'PW_MISMATCH');\n }),\n new ThrowCase('bad-request', 'bad-request', 'warn', () => {\n throw new ApiBadRequestError('internal column name', '$.query', 'query must be a word');\n }),\n new ThrowCase('unauthorized', 'unauthorized', 'warn', () => {\n throw new ApiUnauthorizedError('session internals');\n }),\n new ThrowCase('forbidden', 'forbidden', 'warn', () => {\n throw new ApiForbiddenError('acl internals');\n }),\n new ThrowCase('not-found', 'not-found', 'warn', () => {\n throw new ApiNotFoundError('row 7 missing');\n }),\n new ThrowCase('endpoint-not-found', 'endpoint-not-found', 'warn', () => {\n throw new ApiEndpointNotFoundError('route internals');\n }),\n new ThrowCase('request-timeout', 'request-timeout', 'error', () => {\n throw new ApiRequestTimeoutError('slow internals');\n }),\n new ThrowCase('rate-limited', 'rate-limited', 'error', () => {\n throw new ApiRateLimitedError('quota internals');\n }),\n new ThrowCase('conflict', 'conflict', 'warn', () => {\n throw new ApiConflictError('version internals');\n }),\n new ThrowCase('unprocessable', 'unprocessable', 'warn', () => {\n throw new ApiUnprocessableError('rule internals');\n }),\n new ThrowCase('precondition-failed', 'precondition-failed', 'warn', () => {\n throw new ApiPreconditionFailedError('etag internals');\n }),\n new ThrowCase('unsupported-media-type', 'unsupported-media-type', 'warn', () => {\n throw new ApiUnsupportedMediaTypeError('parser internals');\n }),\n new ThrowCase('not-implemented', 'not-implemented', 'error', () => {\n throw new ApiNotImplementedError('feature internals');\n }),\n new ThrowCase('coded-4xx', 'coded', 'warn', () => {\n throw new ApiCodedError('quota internals', 460, 'QUOTA');\n }),\n new ThrowCase('coded-5xx', 'coded', 'error', () => {\n throw new ApiCodedError('disk internals', 507);\n }),\n new ThrowCase('implementation', 'implementation', 'error', () => {\n throw new ApiImplementationError('bug internals');\n }),\n new ThrowCase('dependency', 'dependency', 'error', () => {\n throw new ApiDependencyError('upstream internals');\n }),\n new ThrowCase('unavailable', 'unavailable', 'error', () => {\n throw new ApiUnavailableError('maintenance internals');\n }),\n new ThrowCase('dependency-timeout', 'dependency-timeout', 'error', () => {\n throw new ApiDependencyTimeoutError('upstream slow internals');\n }),\n new ThrowCase('dependency-backoff', 'dependency-backoff', 'error', () => {\n throw new ApiDependencyBackoffError('backoff internals', 42);\n }),\n // A lower layer already retried and gave up: 502, decoded as itself on the remote hop, retry never.\n new ThrowCase('retries-exhausted', 'retries-exhausted', 'error', () => {\n throw new ApiRetriesExhaustedError(4, new ApiUnavailableError('createTask reset internals'));\n }),\n new ThrowCase('connection', 'implementation', 'error', () => {\n throw new ApiConnectionError('ECONNRESET internals');\n }),\n new ThrowCase('raw-error', 'implementation', 'error', () => {\n throw new Error('SECRET-raw-internals');\n }),\n new ThrowCase('thrown-string', 'implementation', 'error', () => {\n throw 'SECRET-string-internals';\n }),\n];\n\nexport function raiseFor(query: string): never {\n const found = THROW_CASES.find((candidate: ThrowCase) => candidate.name === query);\n if (!found) throw new Error(`unknown throw case ${query}`);\n return found.raise();\n}\n\n@injectable()\nexport class RemoteMcpController extends RemoteMcpApi {\n calls = 0;\n authorization?: string;\n\n override search(request: RemoteRequest): Promise<RemoteResponse> {\n this.calls += 1;\n this.authorization = RequestContext.getRequest()?.getHeader('authorization');\n return Promise.resolve(\n new RemoteResponse(\n RequestContext.getTrusted(REMOTE_USER) ?? 'missing-user',\n RequestContext.getTrusted(REMOTE_ROLES) ?? 'missing-roles',\n `remote:${request.query}`,\n ),\n );\n }\n}\n\n@injectable()\nexport class LocalThrowController extends LocalThrowApi {\n override async fail(request: RemoteRequest): Promise<RemoteResponse> {\n return raiseFor(request.query);\n }\n}\n\n@injectable()\nexport class RemoteThrowController extends RemoteThrowApi {\n override async fail(request: RemoteRequest): Promise<RemoteResponse> {\n return raiseFor(request.query);\n }\n}\n\nexport class RecordingOidcMinter {\n readonly audiences: string[] = [];\n\n constructor(\n private readonly failure?: Error,\n private readonly token = OIDC_TOKEN,\n ) {}\n\n mintIdToken(audience: string): Promise<string> {\n this.audiences.push(audience);\n if (this.failure) return Promise.reject(this.failure);\n return Promise.resolve(this.token);\n }\n}\n\nexport class RecordingOidcVerifier extends OidcHook {\n readonly tokens: string[] = [];\n readonly methods: OidcAuthMethod[] = [];\n\n override verifyOidc(token: string, methods: OidcAuthMethods): Promise<AuthenticatedMachineIdentity> {\n this.tokens.push(token);\n this.methods.push(...methods);\n if (token !== OIDC_TOKEN) throw new Error(`unexpected OIDC token '${token}'`);\n return Promise.resolve(new AuthenticatedMachineIdentity('oidc','test-service@example.test'));\n }\n}\n\n@injectable()\nexport class BoundaryProbeFilter extends Filter<MethodMeta, WpResponse<unknown>> {\n calls = 0;\n sawLogIdentity = false;\n sawTrustedUser?: string;\n\n override filter(\n meta: MethodMeta,\n next: Service<MethodMeta, WpResponse<unknown>>,\n ): Promise<WpResponse<unknown>> {\n this.calls += 1;\n this.sawLogIdentity =\n RequestContext.getUntrusted(WebpiecesCoreHeaders.CONTROLLER) ===\n 'RemoteMcpController' &&\n RequestContext.getUntrusted(WebpiecesCoreHeaders.METHOD) === 'search';\n this.sawTrustedUser = RequestContext.getTrusted(REMOTE_USER);\n return next.invoke(meta);\n }\n}\n\n/**\n * The documented gateway opt-out from http-client-node's \"a dependency's 4xx is OUR 500\" invariant:\n * an MCP gateway relays a Webpieces peer's typed failure so the model sees the same kind it would see\n * for a local binding. `relay = false` answers \"not mine\", which is the framework default.\n */\nexport class RelayWebpiecesPeerErrors implements ErrorTranslator {\n relay = true;\n\n private readonly fallback = new WebpiecesDefaultErrorTranslator();\n\n toWire(error: Error): HttpResponseDto {\n return this.fallback.toWire(error);\n }\n\n fromWire(response: HttpResponseDto, role: ClientRole): void {\n if (this.relay && ApiErrorCodec.isPayload(response.body)) {\n const decoded = ApiErrorCodec.decode(response.body);\n const statusCode = decoded instanceof ApiCodedError ? decoded.statusCode : undefined;\n if (\n ApiErrorHttpStatus.hasCode(decoded) &&\n ApiErrorHttpStatus.codeFor(decoded.kind, statusCode) === response.status.code\n ) {\n throw decoded;\n }\n }\n this.fallback.fromWire(response, role);\n }\n}\n"]}
1
+ {"version":3,"file":"McpRemoteFixtures.js","sourceRoot":"","sources":["../../../../../../packages/http/mcp-server/src/__tests__/McpRemoteFixtures.ts"],"names":[],"mappings":"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","sourcesContent":["import { Mcp } from '@webpieces/core-util';\nimport { AuthenticatedMachineIdentity } from '@webpieces/http-routing';\nimport { ClientRole, WpAuthorization, AuthorizationType, WpAuth, oidcSvcs, OidcAuthMethod, OidcAuthMethods, jwt as jwtAuth } from '@webpieces/core-util';\nimport 'reflect-metadata';\nimport { injectable } from 'inversify';\nimport { ApiBadRequestError, ApiCodedError, ApiConflictError, ApiConnectionError, ApiDependencyBackoffError, ApiDependencyError, ApiDependencyTimeoutError, ApiEndpointNotFoundError, ApiEndUserError, ApiErrorCodec, ApiErrorHttpStatus, ApiForbiddenError, ApiImplementationError, ApiNotFoundError, ApiNotImplementedError, ApiPath, ApiType, ApiPreconditionFailedError, ApiRateLimitedError, ApiRequestTimeoutError, ApiRetriesExhaustedError, ApiUnauthorizedError, ApiUnavailableError, ApiUnprocessableError, ApiUnsupportedMediaTypeError, ContextKey, Endpoint, ErrorTranslator, Filter, WebpiecesDefaultErrorTranslator, HttpResponseDto, LogLevel, Service, WebpiecesCoreHeaders, ApiJsonSchema, McpToolCatalogFile, McpToolDefinition, ObjectSchemaBuilder, WpMcpTool, WpMcpToolHints, MCP, POST, READ, RPC, SVC_TO_SVC } from '@webpieces/core-util';\nimport { RequestContext } from '@webpieces/core-context';\nimport { MethodMeta, OidcHook, WpResponse } from '@webpieces/http-routing';\nimport { McpToolCatalog } from '../McpToolCatalog';\n\n/** Contracts, controllers and test doubles for McpRemoteBinding.integration.spec.ts. */\n\nexport const REMOTE_USER = ContextKey.trusted<string>(\n 'mcpRemoteUser',\n 'verified by the MCP access-token authority before delegation',\n 'x-mcp-remote-user',\n);\nexport const REMOTE_ROLES = ContextKey.trusted<string>(\n 'mcpRemoteRoles',\n 'verified by the MCP access-token authority before delegation',\n 'x-mcp-remote-roles',\n);\nexport const REMOTE_SERVICE = 'mcp-remote-integration';\nexport const REFUSED_SERVICE = 'mcp-remote-refused';\nexport const GARBAGE_SERVICE = 'mcp-remote-garbage';\nexport const OIDC_TOKEN = 'oidc-for-mcp-remote-integration';\nexport const EXTERNAL_MCP_BEARER = 'external-mcp-access-token';\nexport const LOCAL_ENDPOINT_JWT = 'local-endpoint-jwt-that-must-not-cross-the-remote-hop';\nexport const GATEWAY_PATH = '/gateway/mcp';\n\nexport class RemoteRequest {\n /** Search text */\n query!: string;\n\n constructor(query?: string) {\n if (query !== undefined) this.query = query;\n }\n}\n\nexport class RemoteResponse {\n /** Delegated user */\n user!: string;\n\n /** Delegated roles */\n roles!: string;\n\n /** Result */\n result!: string;\n\n constructor(user?: string, roles?: string, result?: string) {\n if (user !== undefined) this.user = user;\n if (roles !== undefined) this.roles = roles;\n if (result !== undefined) this.result = result;\n }\n}\n\n/**\n * The generated tool catalogs these contracts would produce — what `wp-openapi` writes, one\n * `mcp-<ContractClass>-tools.json` per contract. Hand-built here because `@webpieces/mcp-server` does not depend on the\n * TypeScript compiler API; the runtime's job is to BE HANDED a catalog.\n */\nfunction describedString(description: string): ApiJsonSchema {\n const schema = new ApiJsonSchema('string');\n schema.description = description;\n return schema;\n}\n\nfunction searchTool(\n name: string,\n title: string,\n methodName: string,\n description: string,\n): McpToolDefinition {\n return new McpToolDefinition(\n name,\n title,\n methodName,\n description,\n new WpMcpToolHints(true, false, true, false),\n new ObjectSchemaBuilder().required('query', describedString('Search text')).build(),\n new ObjectSchemaBuilder()\n .required('user', describedString('Delegated user'))\n .required('roles', describedString('Delegated roles'))\n .required('result', describedString('Result'))\n .build(), [Mcp.DEFAULT],\n );\n}\n\n/** One contract's hand-built catalog, labelled as built in memory. */\nfunction contractCatalog(contractName: string, tool: McpToolDefinition): McpToolCatalog {\n return new McpToolCatalog(new McpToolCatalogFile(contractName, [tool]), '(built in memory by the spec)');\n}\n\nexport const REMOTE_MCP_CATALOG = contractCatalog(\n 'RemoteMcpApi',\n searchTool('remote_integration_search', 'Remote integration search', 'search', 'Calls a remote Webpieces API.'),\n);\n\n/** The catalogs of every contract the gateway binds — one per contract, exactly as a build writes them. */\nexport const GATEWAY_CATALOGS: readonly McpToolCatalog[] = [\n contractCatalog(\n 'MissingRemoteMcpApi',\n searchTool('missing_remote_integration_search', 'Missing remote integration search', 'search', 'Intentionally absent route.'),\n ),\n contractCatalog('RefusedRemoteApi', searchTool('refused_remote', 'Refused remote call', 'search', 'Nothing listens on this port.')),\n contractCatalog('GarbageRemoteApi', searchTool('garbage_remote', 'Garbage remote response', 'search', 'Answers an undecodable body.')),\n contractCatalog(\n 'OidcFailRemoteApi',\n searchTool('oidc_fail_remote', 'OIDC failure on remote', 'search', 'Its OIDC token cannot be minted.'),\n ),\n contractCatalog('LocalThrowApi', searchTool('local_throw', 'Local throw', 'fail', 'Throws the requested failure in-process.')),\n contractCatalog('RemoteThrowApi', searchTool('remote_throw', 'Remote throw', 'fail', 'Throws the requested failure remotely.')),\n];\n\n@ApiPath('/remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class RemoteMcpApi {\n /**\n * Calls a remote Webpieces API.\n *\n * @summary Remote integration search\n */\n\n @WpAuth([oidcSvcs(['mcp-gateway'])])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('remote_integration_search')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/missing-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class MissingRemoteMcpApi {\n /**\n * Intentionally absent route.\n *\n * @summary Missing remote integration search\n */\n\n @WpAuth([oidcSvcs(['mcp-gateway'])])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('missing_remote_integration_search')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/refused-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class RefusedRemoteApi {\n /**\n * Nothing listens on this port.\n *\n * @summary Refused remote call\n */\n\n @WpAuth([oidcSvcs(['mcp-gateway'])])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('refused_remote')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/garbage-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class GarbageRemoteApi {\n /**\n * Answers an undecodable body.\n *\n * @summary Garbage remote response\n */\n\n @WpAuth([oidcSvcs(['mcp-gateway'])])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('garbage_remote')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/oidc-fail-remote-mcp')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class OidcFailRemoteApi {\n /**\n * Its OIDC token cannot be minted.\n *\n * @summary OIDC failure on remote\n */\n\n @WpAuth([oidcSvcs(['mcp-gateway'])])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('oidc_fail_remote')\n search(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/local-throw')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class LocalThrowApi {\n /**\n * Throws the requested failure in-process.\n *\n * @summary Local throw\n */\n\n @WpAuth([jwtAuth()])\n @WpAuthorization({ authType: AuthorizationType.ALL_USERS })\n @Endpoint(POST, '/throw', READ, RPC)\n @WpMcpTool('local_throw')\n fail(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/remote-throw')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class RemoteThrowApi {\n /**\n * Throws the requested failure remotely.\n *\n * @summary Remote throw\n */\n\n @WpAuth([oidcSvcs(['mcp-gateway'])])\n @WpAuthorization({ authType: AuthorizationType.USERS_OR_SERVICES })\n @Endpoint(POST, '/throw', READ, RPC)\n @WpMcpTool('remote_throw')\n fail(_request: RemoteRequest): Promise<RemoteResponse> {\n throw new Error('contract only');\n }\n}\n\n/**\n * One named throw, run identically behind the local and the remote binding.\n *\n * `expectedServerLevel` is the level of the ONE operator line the failure now produces on the server\n * that raised it — `LogApiCall`'s, at `warn` for `[API-server-resp-OTHER]` (a healthy rejection of\n * the caller's mistake) and `error` for `[API-server-resp-FAIL]`. `ApiErrorBoundary` no longer\n * writes a second line of its own (#961).\n */\nexport class ThrowCase {\n constructor(\n public readonly name: string,\n public readonly expectedKind: string,\n public readonly expectedServerLevel: LogLevel,\n public readonly raise: () => never,\n ) {}\n}\n\nexport const THROW_CASES: readonly ThrowCase[] = [\n new ThrowCase('end-user', 'end-user', 'warn', () => {\n throw new ApiEndUserError('The two passwords you entered do not match.', 'PW_MISMATCH');\n }),\n new ThrowCase('bad-request', 'bad-request', 'warn', () => {\n throw new ApiBadRequestError('internal column name', '$.query', 'query must be a word');\n }),\n new ThrowCase('unauthorized', 'unauthorized', 'warn', () => {\n throw new ApiUnauthorizedError('session internals');\n }),\n new ThrowCase('forbidden', 'forbidden', 'warn', () => {\n throw new ApiForbiddenError('acl internals');\n }),\n new ThrowCase('not-found', 'not-found', 'warn', () => {\n throw new ApiNotFoundError('row 7 missing');\n }),\n new ThrowCase('endpoint-not-found', 'endpoint-not-found', 'warn', () => {\n throw new ApiEndpointNotFoundError('route internals');\n }),\n new ThrowCase('request-timeout', 'request-timeout', 'error', () => {\n throw new ApiRequestTimeoutError('slow internals');\n }),\n new ThrowCase('rate-limited', 'rate-limited', 'error', () => {\n throw new ApiRateLimitedError('quota internals');\n }),\n new ThrowCase('conflict', 'conflict', 'warn', () => {\n throw new ApiConflictError('version internals');\n }),\n new ThrowCase('unprocessable', 'unprocessable', 'warn', () => {\n throw new ApiUnprocessableError('rule internals');\n }),\n new ThrowCase('precondition-failed', 'precondition-failed', 'warn', () => {\n throw new ApiPreconditionFailedError('etag internals');\n }),\n new ThrowCase('unsupported-media-type', 'unsupported-media-type', 'warn', () => {\n throw new ApiUnsupportedMediaTypeError('parser internals');\n }),\n new ThrowCase('not-implemented', 'not-implemented', 'error', () => {\n throw new ApiNotImplementedError('feature internals');\n }),\n new ThrowCase('coded-4xx', 'coded', 'warn', () => {\n throw new ApiCodedError('quota internals', 460, 'QUOTA');\n }),\n new ThrowCase('coded-5xx', 'coded', 'error', () => {\n throw new ApiCodedError('disk internals', 507);\n }),\n new ThrowCase('implementation', 'implementation', 'error', () => {\n throw new ApiImplementationError('bug internals');\n }),\n new ThrowCase('dependency', 'dependency', 'error', () => {\n throw new ApiDependencyError('upstream internals');\n }),\n new ThrowCase('unavailable', 'unavailable', 'error', () => {\n throw new ApiUnavailableError('maintenance internals');\n }),\n new ThrowCase('dependency-timeout', 'dependency-timeout', 'error', () => {\n throw new ApiDependencyTimeoutError('upstream slow internals');\n }),\n new ThrowCase('dependency-backoff', 'dependency-backoff', 'error', () => {\n throw new ApiDependencyBackoffError('backoff internals', 42);\n }),\n // A lower layer already retried and gave up: 502, decoded as itself on the remote hop, retry never.\n new ThrowCase('retries-exhausted', 'retries-exhausted', 'error', () => {\n throw new ApiRetriesExhaustedError(4, new ApiUnavailableError('createTask reset internals'));\n }),\n new ThrowCase('connection', 'implementation', 'error', () => {\n throw new ApiConnectionError('ECONNRESET internals');\n }),\n new ThrowCase('raw-error', 'implementation', 'error', () => {\n throw new Error('SECRET-raw-internals');\n }),\n new ThrowCase('thrown-string', 'implementation', 'error', () => {\n throw 'SECRET-string-internals';\n }),\n];\n\nexport function raiseFor(query: string): never {\n const found = THROW_CASES.find((candidate: ThrowCase) => candidate.name === query);\n if (!found) throw new Error(`unknown throw case ${query}`);\n return found.raise();\n}\n\n@injectable()\nexport class RemoteMcpController extends RemoteMcpApi {\n calls = 0;\n authorization?: string;\n\n override search(request: RemoteRequest): Promise<RemoteResponse> {\n this.calls += 1;\n this.authorization = RequestContext.getRequest()?.getHeader('authorization');\n return Promise.resolve(\n new RemoteResponse(\n RequestContext.getTrusted(REMOTE_USER) ?? 'missing-user',\n RequestContext.getTrusted(REMOTE_ROLES) ?? 'missing-roles',\n `remote:${request.query}`,\n ),\n );\n }\n}\n\n@injectable()\nexport class LocalThrowController extends LocalThrowApi {\n override async fail(request: RemoteRequest): Promise<RemoteResponse> {\n return raiseFor(request.query);\n }\n}\n\n@injectable()\nexport class RemoteThrowController extends RemoteThrowApi {\n override async fail(request: RemoteRequest): Promise<RemoteResponse> {\n return raiseFor(request.query);\n }\n}\n\nexport class RecordingOidcMinter {\n readonly audiences: string[] = [];\n\n constructor(\n private readonly failure?: Error,\n private readonly token = OIDC_TOKEN,\n ) {}\n\n mintIdToken(audience: string): Promise<string> {\n this.audiences.push(audience);\n if (this.failure) return Promise.reject(this.failure);\n return Promise.resolve(this.token);\n }\n}\n\nexport class RecordingOidcVerifier extends OidcHook {\n readonly tokens: string[] = [];\n readonly methods: OidcAuthMethod[] = [];\n\n override verifyOidc(token: string, methods: OidcAuthMethods): Promise<AuthenticatedMachineIdentity> {\n this.tokens.push(token);\n this.methods.push(...methods);\n if (token !== OIDC_TOKEN) throw new Error(`unexpected OIDC token '${token}'`);\n return Promise.resolve(new AuthenticatedMachineIdentity('oidc','test-service@example.test'));\n }\n\n override async originatingServiceRoles(_identity: AuthenticatedMachineIdentity): Promise<readonly string[]> {\n return [];\n }\n}\n\n@injectable()\nexport class BoundaryProbeFilter extends Filter<MethodMeta, WpResponse<unknown>> {\n calls = 0;\n sawLogIdentity = false;\n sawTrustedUser?: string;\n\n override filter(\n meta: MethodMeta,\n next: Service<MethodMeta, WpResponse<unknown>>,\n ): Promise<WpResponse<unknown>> {\n this.calls += 1;\n this.sawLogIdentity =\n RequestContext.getUntrusted(WebpiecesCoreHeaders.CONTROLLER) ===\n 'RemoteMcpController' &&\n RequestContext.getUntrusted(WebpiecesCoreHeaders.METHOD) === 'search';\n this.sawTrustedUser = RequestContext.getTrusted(REMOTE_USER);\n return next.invoke(meta);\n }\n}\n\n/**\n * The documented gateway opt-out from http-client-node's \"a dependency's 4xx is OUR 500\" invariant:\n * an MCP gateway relays a Webpieces peer's typed failure so the model sees the same kind it would see\n * for a local binding. `relay = false` answers \"not mine\", which is the framework default.\n */\nexport class RelayWebpiecesPeerErrors implements ErrorTranslator {\n relay = true;\n\n private readonly fallback = new WebpiecesDefaultErrorTranslator();\n\n toWire(error: Error): HttpResponseDto {\n return this.fallback.toWire(error);\n }\n\n fromWire(response: HttpResponseDto, role: ClientRole): void {\n if (this.relay && ApiErrorCodec.isPayload(response.body)) {\n const decoded = ApiErrorCodec.decode(response.body);\n const statusCode = decoded instanceof ApiCodedError ? decoded.statusCode : undefined;\n if (\n ApiErrorHttpStatus.hasCode(decoded) &&\n ApiErrorHttpStatus.codeFor(decoded.kind, statusCode) === response.status.code\n ) {\n throw decoded;\n }\n }\n this.fallback.fromWire(response, role);\n }\n}\n"]}
@@ -79,6 +79,7 @@ export declare class DevOidcTokens {
79
79
  export declare class ProjectOidcHook extends OidcHook {
80
80
  readonly tokens: string[];
81
81
  verifyOidc(token: string, _methods: OidcAuthMethods): Promise<AuthenticatedMachineIdentity>;
82
+ originatingServiceRoles(_identity: AuthenticatedMachineIdentity): Promise<readonly string[]>;
82
83
  }
83
84
  /** The app's own user JWT: `jwt-alice` is an OPERATOR, `jwt-bob` has no role. Records every token. */
84
85
  export declare class TransportJwtHook extends JwtHook<string> {
@@ -200,6 +200,9 @@ class ProjectOidcHook extends http_routing_1.OidcHook {
200
200
  }
201
201
  return new http_routing_1.AuthenticatedMachineIdentity('oidc', claims['email']);
202
202
  }
203
+ async originatingServiceRoles(_identity) {
204
+ return [];
205
+ }
203
206
  }
204
207
  exports.ProjectOidcHook = ProjectOidcHook;
205
208
  /** The app's own user JWT: `jwt-alice` is an OPERATOR, `jwt-bob` has no role. Records every token. */
@@ -1 +1 @@
1
- {"version":3,"file":"McpTransportFixtures.js","sourceRoot":"","sources":["../../../../../../packages/http/mcp-server/src/__tests__/McpTransportFixtures.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { injectable } from 'inversify';\nimport {\n ApiPath,\n ApiType,\n ApiUnauthorizedError,\n AuthorizationType,\n ContextKey,\n Endpoint,\n getWpMcpTools,\n jwt,\n MCP,\n McpToolCatalogFile,\n McpToolDefinition,\n mcpHintsForOperation,\n OidcAuthMethods,\n POST,\n READ,\n RPC,\n SVC_TO_SVC,\n WebpiecesCoreHeaders,\n WpAuth,\n WpAuthorization,\n WpMcpTool,\n WpMcpToolMetadata,\n} from '@webpieces/core-util';\nimport { HttpRequest, RequestContext, RequestContextHeaders } from '@webpieces/core-context';\nimport {\n ApiKeyHook,\n AuthenticatedCaller,\n AuthenticatedCallerContext,\n AuthenticatedMachineIdentity,\n JwtHook,\n MintedJwt,\n OidcHook,\n} from '@webpieces/http-routing';\nimport { McpAccessTokenAuthority, VerifiedMcpCredential } from '../McpAuth';\nimport { McpToolCatalog } from '../McpToolCatalog';\nimport { SEARCH_API_CATALOG, SearchRequest, SearchResponse } from './WpMcpServerTestFixtures';\n\n/**\n * One contract served over every transport. `stores_pause` needs the OPERATOR role and `stores_purge`\n * is in the `admin` profile only, so projection by caller AND by toolset profile are both visible.\n * The endpoints declare plain `jwt()`: an MCP transport never needs an endpoint auth mode of its own.\n */\n@ApiPath('/stores')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class StoreApi {\n /**\n * List the caller's stores.\n *\n * @summary List stores\n */\n @WpAuth([jwt()])\n @WpAuthorization({ authType: AuthorizationType.ALL_USERS })\n @Endpoint(POST, '/list', READ, RPC)\n @WpMcpTool('stores_list', { profiles: ['user'] })\n list(_request: SearchRequest): Promise<SearchResponse> {\n throw new Error('contract only');\n }\n\n /**\n * Pause a store.\n *\n * @summary Pause store\n */\n @WpAuth([jwt()])\n @WpAuthorization({ authType: AuthorizationType.ROLES, roles: ['OPERATOR'] })\n @Endpoint(POST, '/pause', READ, RPC)\n @WpMcpTool('stores_pause', { profiles: ['user'] })\n pause(_request: SearchRequest): Promise<SearchResponse> {\n throw new Error('contract only');\n }\n\n /**\n * Purge a store; admin profile only.\n *\n * @summary Purge store\n */\n @WpAuth([jwt()])\n @WpAuthorization({ authType: AuthorizationType.ROLES, roles: ['OPERATOR'] })\n @Endpoint(POST, '/purge', READ, RPC)\n @WpMcpTool('stores_purge', { profiles: ['admin'] })\n purge(_request: SearchRequest): Promise<SearchResponse> {\n throw new Error('contract only');\n }\n}\n\n/** A contract that marks a SERVICE_ONLY method as an MCP tool, which `McpToolset` must refuse. */\n@ApiPath('/service-only')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class ServiceOnlyToolApi {\n /**\n * Only for a bare service hop.\n *\n * @summary Service only\n */\n @WpAuth([jwt()])\n @WpAuthorization({ authType: AuthorizationType.SERVICE_ONLY })\n @Endpoint(POST, '/run', READ, RPC)\n @WpMcpTool('service_only')\n run(_request: SearchRequest): Promise<SearchResponse> {\n throw new Error('contract only');\n }\n}\n\n@injectable()\nexport class ServiceOnlyToolController extends ServiceOnlyToolApi {\n override async run(request: SearchRequest): Promise<SearchResponse> {\n const response = new SearchResponse();\n response.userId = 'service';\n response.result = request.query;\n return response;\n }\n}\n\n/** A hold the test releases, so a call is provably still in flight while another caller runs. */\nexport class StoreGate {\n private release?: () => void;\n readonly entered: Promise<void>;\n private enter!: () => void;\n readonly opened: Promise<void>;\n\n constructor() {\n this.entered = new Promise<void>((resolve: () => void) => {\n this.enter = resolve;\n });\n this.opened = new Promise<void>((resolve: () => void) => {\n this.release = resolve;\n });\n }\n\n async pass(): Promise<void> {\n this.enter();\n await this.opened;\n }\n\n open(): void {\n this.release?.();\n }\n}\n\n@injectable()\nexport class StoreController extends StoreApi {\n static gate?: StoreGate;\n static readonly calls: string[] = [];\n\n override async list(request: SearchRequest): Promise<SearchResponse> {\n return this.answer('list', request);\n }\n\n override async pause(request: SearchRequest): Promise<SearchResponse> {\n if (request.query === 'hold') await StoreController.gate?.pass();\n return this.answer('pause', request);\n }\n\n override async purge(request: SearchRequest): Promise<SearchResponse> {\n return this.answer('purge', request);\n }\n\n private answer(method: string, request: SearchRequest): SearchResponse {\n StoreController.calls.push(method);\n const response = new SearchResponse();\n response.userId = RequestContext.getTrusted(WebpiecesCoreHeaders.USER_ID) ?? 'missing';\n response.result = request.query;\n return response;\n }\n}\n\nconst SCHEMA = SEARCH_API_CATALOG.file.tools[0];\nexport const STORE_CATALOG = new McpToolCatalog(\n new McpToolCatalogFile(\n 'StoreApi',\n getWpMcpTools(StoreApi).map(\n (tool: WpMcpToolMetadata) =>\n new McpToolDefinition(\n tool.name,\n `Store ${tool.name}`,\n tool.methodName,\n 'Store fixture',\n mcpHintsForOperation(READ, false),\n SCHEMA.inputSchema,\n SCHEMA.outputSchema,\n tool.profiles,\n ),\n ),\n ),\n '(store fixture)',\n);\n\nexport const SAME_PROJECT_SA = 'ai-chat@this-project.iam.gserviceaccount.com';\nexport const OTHER_PROJECT_SA = 'intruder@other-project.iam.gserviceaccount.com';\nexport const OAUTH_ISSUER = 'https://login.example.test';\nexport const PARTNER_KEY = ContextKey.untrusted<string>('mcpPartnerKey', 'x-partner-key');\n\n/** The unsigned off-GCP OIDC token `@webpieces/gcp-identity` mints, for `email`. */\nexport class DevOidcTokens {\n // webpieces-disable no-function-outside-class -- test token builder\n static of(email: string, audience = 'https://svc-b.example.test'): string {\n return (\n 'dev-oidc.' +\n Buffer.from(JSON.stringify({ email, aud: audience }), 'utf8').toString('base64url')\n );\n }\n\n /** A JWT that merely CLAIMS to be Google-issued; its signature is garbage. */\n // webpieces-disable no-function-outside-class -- test token builder\n static forgedGoogleJwt(): string {\n const header = Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })).toString(\n 'base64url',\n );\n const payload = Buffer.from(\n JSON.stringify({ iss: 'https://accounts.google.com', email: SAME_PROJECT_SA }),\n ).toString('base64url');\n return `${header}.${payload}.forged-signature`;\n }\n}\n\n/**\n * The OIDC verifier edge: admits a dev token whose email is a service account of `this-project`, the\n * way `oidcSameProject()` admits this GCP project's accounts. Anything else — another project, or a\n * JWT whose Google signature it cannot verify — is a 401.\n */\nexport class ProjectOidcHook extends OidcHook {\n readonly tokens: string[] = [];\n\n override async verifyOidc(\n token: string,\n _methods: OidcAuthMethods,\n ): Promise<AuthenticatedMachineIdentity> {\n this.tokens.push(token);\n if (!token.startsWith('dev-oidc.'))\n throw new ApiUnauthorizedError('OIDC signature did not verify');\n const claims = JSON.parse(\n Buffer.from(token.substring('dev-oidc.'.length), 'base64url').toString('utf8'),\n ) as Record<string, string>;\n if (!claims['email']?.endsWith('@this-project.iam.gserviceaccount.com')) {\n throw new ApiUnauthorizedError('OIDC caller is not a service account of this project');\n }\n return new AuthenticatedMachineIdentity('oidc', claims['email']);\n }\n}\n\n/** The app's own user JWT: `jwt-alice` is an OPERATOR, `jwt-bob` has no role. Records every token. */\nexport class TransportJwtHook extends JwtHook<string> {\n readonly parsed: string[] = [];\n\n override async mint(_subject: string): Promise<MintedJwt> {\n throw new Error('not used');\n }\n\n override async parseJwt(token: string): Promise<AuthenticatedCaller> {\n this.parsed.push(token);\n if (token === 'jwt-alice') return new AuthenticatedCaller('alice', ['OPERATOR']);\n if (token === 'jwt-bob') return new AuthenticatedCaller('bob', []);\n throw new ApiUnauthorizedError('invalid user token');\n }\n}\n\n/** A partner's api key on its own header. */\nexport class TransportApiKeyHook extends ApiKeyHook {\n override async verifyApiKey(\n _regime: string,\n request: HttpRequest,\n ): Promise<AuthenticatedCaller> {\n if (request.getHeader('x-partner-key') === 'key-carol') {\n return new AuthenticatedCaller('carol', ['OPERATOR']);\n }\n throw new ApiUnauthorizedError('unknown partner key');\n }\n}\n\n/** MCP OAuth access tokens minted as opaque `wpmcp_<user>` strings. */\nexport class PrefixTokenAuthority implements McpAccessTokenAuthority<string> {\n async mintAccessToken(_grant: string): Promise<never> {\n throw new Error('not used');\n }\n\n async verifyAccessToken(token: string, resource: string): Promise<VerifiedMcpCredential> {\n if (token !== 'wpmcp_dave') throw new ApiUnauthorizedError('invalid access token');\n const now = Math.floor(Date.now() / 1000);\n return new VerifiedMcpCredential(\n 'dave',\n OAUTH_ISSUER,\n resource,\n now,\n now + 60,\n ['tools'],\n now,\n new AuthenticatedCaller('dave', ['OPERATOR']),\n );\n }\n}\n\n/**\n * Runs `work` as a caller an endpoint's AuthFilter already verified: one request scope with the user\n * published and ingress completed — exactly the context an in-process agent calls its client from.\n */\nexport class VerifiedCaller {\n constructor(\n public readonly userId: string,\n public readonly roles: string[],\n ) {}\n\n async run<T>(work: () => Promise<T>): Promise<T> {\n return RequestContext.run(async () => {\n new RequestContextHeaders().fillFromRequest(\n new HttpRequest('POST', '/chat', new Map()),\n );\n const context = new AuthenticatedCallerContext();\n context.publish(new AuthenticatedCaller(this.userId, this.roles));\n context.reconcileWireTrust(false);\n return work();\n });\n }\n}\n"]}
1
+ {"version":3,"file":"McpTransportFixtures.js","sourceRoot":"","sources":["../../../../../../packages/http/mcp-server/src/__tests__/McpTransportFixtures.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { injectable } from 'inversify';\nimport {\n ApiPath,\n ApiType,\n ApiUnauthorizedError,\n AuthorizationType,\n ContextKey,\n Endpoint,\n getWpMcpTools,\n jwt,\n MCP,\n McpToolCatalogFile,\n McpToolDefinition,\n mcpHintsForOperation,\n OidcAuthMethods,\n POST,\n READ,\n RPC,\n SVC_TO_SVC,\n WebpiecesCoreHeaders,\n WpAuth,\n WpAuthorization,\n WpMcpTool,\n WpMcpToolMetadata,\n} from '@webpieces/core-util';\nimport { HttpRequest, RequestContext, RequestContextHeaders } from '@webpieces/core-context';\nimport {\n ApiKeyHook,\n AuthenticatedCaller,\n AuthenticatedCallerContext,\n AuthenticatedMachineIdentity,\n JwtHook,\n MintedJwt,\n OidcHook,\n} from '@webpieces/http-routing';\nimport { McpAccessTokenAuthority, VerifiedMcpCredential } from '../McpAuth';\nimport { McpToolCatalog } from '../McpToolCatalog';\nimport { SEARCH_API_CATALOG, SearchRequest, SearchResponse } from './WpMcpServerTestFixtures';\n\n/**\n * One contract served over every transport. `stores_pause` needs the OPERATOR role and `stores_purge`\n * is in the `admin` profile only, so projection by caller AND by toolset profile are both visible.\n * The endpoints declare plain `jwt()`: an MCP transport never needs an endpoint auth mode of its own.\n */\n@ApiPath('/stores')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class StoreApi {\n /**\n * List the caller's stores.\n *\n * @summary List stores\n */\n @WpAuth([jwt()])\n @WpAuthorization({ authType: AuthorizationType.ALL_USERS })\n @Endpoint(POST, '/list', READ, RPC)\n @WpMcpTool('stores_list', { profiles: ['user'] })\n list(_request: SearchRequest): Promise<SearchResponse> {\n throw new Error('contract only');\n }\n\n /**\n * Pause a store.\n *\n * @summary Pause store\n */\n @WpAuth([jwt()])\n @WpAuthorization({ authType: AuthorizationType.ROLES, roles: ['OPERATOR'] })\n @Endpoint(POST, '/pause', READ, RPC)\n @WpMcpTool('stores_pause', { profiles: ['user'] })\n pause(_request: SearchRequest): Promise<SearchResponse> {\n throw new Error('contract only');\n }\n\n /**\n * Purge a store; admin profile only.\n *\n * @summary Purge store\n */\n @WpAuth([jwt()])\n @WpAuthorization({ authType: AuthorizationType.ROLES, roles: ['OPERATOR'] })\n @Endpoint(POST, '/purge', READ, RPC)\n @WpMcpTool('stores_purge', { profiles: ['admin'] })\n purge(_request: SearchRequest): Promise<SearchResponse> {\n throw new Error('contract only');\n }\n}\n\n/** A contract that marks a SERVICE_ONLY method as an MCP tool, which `McpToolset` must refuse. */\n@ApiPath('/service-only')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class ServiceOnlyToolApi {\n /**\n * Only for a bare service hop.\n *\n * @summary Service only\n */\n @WpAuth([jwt()])\n @WpAuthorization({ authType: AuthorizationType.SERVICE_ONLY })\n @Endpoint(POST, '/run', READ, RPC)\n @WpMcpTool('service_only')\n run(_request: SearchRequest): Promise<SearchResponse> {\n throw new Error('contract only');\n }\n}\n\n@injectable()\nexport class ServiceOnlyToolController extends ServiceOnlyToolApi {\n override async run(request: SearchRequest): Promise<SearchResponse> {\n const response = new SearchResponse();\n response.userId = 'service';\n response.result = request.query;\n return response;\n }\n}\n\n/** A hold the test releases, so a call is provably still in flight while another caller runs. */\nexport class StoreGate {\n private release?: () => void;\n readonly entered: Promise<void>;\n private enter!: () => void;\n readonly opened: Promise<void>;\n\n constructor() {\n this.entered = new Promise<void>((resolve: () => void) => {\n this.enter = resolve;\n });\n this.opened = new Promise<void>((resolve: () => void) => {\n this.release = resolve;\n });\n }\n\n async pass(): Promise<void> {\n this.enter();\n await this.opened;\n }\n\n open(): void {\n this.release?.();\n }\n}\n\n@injectable()\nexport class StoreController extends StoreApi {\n static gate?: StoreGate;\n static readonly calls: string[] = [];\n\n override async list(request: SearchRequest): Promise<SearchResponse> {\n return this.answer('list', request);\n }\n\n override async pause(request: SearchRequest): Promise<SearchResponse> {\n if (request.query === 'hold') await StoreController.gate?.pass();\n return this.answer('pause', request);\n }\n\n override async purge(request: SearchRequest): Promise<SearchResponse> {\n return this.answer('purge', request);\n }\n\n private answer(method: string, request: SearchRequest): SearchResponse {\n StoreController.calls.push(method);\n const response = new SearchResponse();\n response.userId = RequestContext.getTrusted(WebpiecesCoreHeaders.USER_ID) ?? 'missing';\n response.result = request.query;\n return response;\n }\n}\n\nconst SCHEMA = SEARCH_API_CATALOG.file.tools[0];\nexport const STORE_CATALOG = new McpToolCatalog(\n new McpToolCatalogFile(\n 'StoreApi',\n getWpMcpTools(StoreApi).map(\n (tool: WpMcpToolMetadata) =>\n new McpToolDefinition(\n tool.name,\n `Store ${tool.name}`,\n tool.methodName,\n 'Store fixture',\n mcpHintsForOperation(READ, false),\n SCHEMA.inputSchema,\n SCHEMA.outputSchema,\n tool.profiles,\n ),\n ),\n ),\n '(store fixture)',\n);\n\nexport const SAME_PROJECT_SA = 'ai-chat@this-project.iam.gserviceaccount.com';\nexport const OTHER_PROJECT_SA = 'intruder@other-project.iam.gserviceaccount.com';\nexport const OAUTH_ISSUER = 'https://login.example.test';\nexport const PARTNER_KEY = ContextKey.untrusted<string>('mcpPartnerKey', 'x-partner-key');\n\n/** The unsigned off-GCP OIDC token `@webpieces/gcp-identity` mints, for `email`. */\nexport class DevOidcTokens {\n // webpieces-disable no-function-outside-class -- test token builder\n static of(email: string, audience = 'https://svc-b.example.test'): string {\n return (\n 'dev-oidc.' +\n Buffer.from(JSON.stringify({ email, aud: audience }), 'utf8').toString('base64url')\n );\n }\n\n /** A JWT that merely CLAIMS to be Google-issued; its signature is garbage. */\n // webpieces-disable no-function-outside-class -- test token builder\n static forgedGoogleJwt(): string {\n const header = Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })).toString(\n 'base64url',\n );\n const payload = Buffer.from(\n JSON.stringify({ iss: 'https://accounts.google.com', email: SAME_PROJECT_SA }),\n ).toString('base64url');\n return `${header}.${payload}.forged-signature`;\n }\n}\n\n/**\n * The OIDC verifier edge: admits a dev token whose email is a service account of `this-project`, the\n * way `oidcSameProject()` admits this GCP project's accounts. Anything else — another project, or a\n * JWT whose Google signature it cannot verify — is a 401.\n */\nexport class ProjectOidcHook extends OidcHook {\n readonly tokens: string[] = [];\n\n override async verifyOidc(\n token: string,\n _methods: OidcAuthMethods,\n ): Promise<AuthenticatedMachineIdentity> {\n this.tokens.push(token);\n if (!token.startsWith('dev-oidc.'))\n throw new ApiUnauthorizedError('OIDC signature did not verify');\n const claims = JSON.parse(\n Buffer.from(token.substring('dev-oidc.'.length), 'base64url').toString('utf8'),\n ) as Record<string, string>;\n if (!claims['email']?.endsWith('@this-project.iam.gserviceaccount.com')) {\n throw new ApiUnauthorizedError('OIDC caller is not a service account of this project');\n }\n return new AuthenticatedMachineIdentity('oidc', claims['email']);\n }\n\n override async originatingServiceRoles(_identity: AuthenticatedMachineIdentity): Promise<readonly string[]> {\n return [];\n }\n}\n\n/** The app's own user JWT: `jwt-alice` is an OPERATOR, `jwt-bob` has no role. Records every token. */\nexport class TransportJwtHook extends JwtHook<string> {\n readonly parsed: string[] = [];\n\n override async mint(_subject: string): Promise<MintedJwt> {\n throw new Error('not used');\n }\n\n override async parseJwt(token: string): Promise<AuthenticatedCaller> {\n this.parsed.push(token);\n if (token === 'jwt-alice') return new AuthenticatedCaller('alice', ['OPERATOR']);\n if (token === 'jwt-bob') return new AuthenticatedCaller('bob', []);\n throw new ApiUnauthorizedError('invalid user token');\n }\n}\n\n/** A partner's api key on its own header. */\nexport class TransportApiKeyHook extends ApiKeyHook {\n override async verifyApiKey(\n _regime: string,\n request: HttpRequest,\n ): Promise<AuthenticatedCaller> {\n if (request.getHeader('x-partner-key') === 'key-carol') {\n return new AuthenticatedCaller('carol', ['OPERATOR']);\n }\n throw new ApiUnauthorizedError('unknown partner key');\n }\n}\n\n/** MCP OAuth access tokens minted as opaque `wpmcp_<user>` strings. */\nexport class PrefixTokenAuthority implements McpAccessTokenAuthority<string> {\n async mintAccessToken(_grant: string): Promise<never> {\n throw new Error('not used');\n }\n\n async verifyAccessToken(token: string, resource: string): Promise<VerifiedMcpCredential> {\n if (token !== 'wpmcp_dave') throw new ApiUnauthorizedError('invalid access token');\n const now = Math.floor(Date.now() / 1000);\n return new VerifiedMcpCredential(\n 'dave',\n OAUTH_ISSUER,\n resource,\n now,\n now + 60,\n ['tools'],\n now,\n new AuthenticatedCaller('dave', ['OPERATOR']),\n );\n }\n}\n\n/**\n * Runs `work` as a caller an endpoint's AuthFilter already verified: one request scope with the user\n * published and ingress completed — exactly the context an in-process agent calls its client from.\n */\nexport class VerifiedCaller {\n constructor(\n public readonly userId: string,\n public readonly roles: string[],\n ) {}\n\n async run<T>(work: () => Promise<T>): Promise<T> {\n return RequestContext.run(async () => {\n new RequestContextHeaders().fillFromRequest(\n new HttpRequest('POST', '/chat', new Map()),\n );\n const context = new AuthenticatedCallerContext();\n context.publish(new AuthenticatedCaller(this.userId, this.roles));\n context.reconcileWireTrust(false);\n return work();\n });\n }\n}\n"]}