@webpieces/mcp-server 0.4.806 → 0.4.808
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 +56 -48
- package/package.json +6 -6
- package/src/McpApiDispatcher.d.ts +1 -1
- package/src/McpApiDispatcher.js +3 -3
- package/src/McpApiDispatcher.js.map +1 -1
- package/src/McpBindOptions.d.ts +14 -1
- package/src/McpBindOptions.js +9 -1
- package/src/McpBindOptions.js.map +1 -1
- package/src/McpToolRegistry.d.ts +22 -7
- package/src/McpToolRegistry.js +39 -12
- package/src/McpToolRegistry.js.map +1 -1
- package/src/WpMcpServer.d.ts +1 -0
- package/src/WpMcpServer.js +3 -2
- package/src/WpMcpServer.js.map +1 -1
- package/src/__tests__/McpRemoteFixtures.d.ts +2 -1
- package/src/__tests__/McpRemoteFixtures.js +38 -71
- package/src/__tests__/McpRemoteFixtures.js.map +1 -1
- package/src/__tests__/WpMcpServerTestFixtures.d.ts +2 -1
- package/src/__tests__/WpMcpServerTestFixtures.js +37 -41
- package/src/__tests__/WpMcpServerTestFixtures.js.map +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var SearchController_1;
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.RecordingLoggerFactory = exports.RecordedLogLine = exports.TestTokenAuthority = exports.TestJwtHook = exports.RemoteSearchClient = exports.SearchController = exports.RemoteSearchApi = exports.SearchApi = exports.SearchResponse = exports.SearchRequest = exports.UNKNOWN_VERSION = exports.OLDER_LEGACY_VERSION = exports.LEGACY_VERSION = exports.MODERN_VERSION = exports.ENDPOINT_PATH = exports.USER_ID = void 0;
|
|
4
|
+
exports.RecordingLoggerFactory = exports.RecordedLogLine = exports.TestTokenAuthority = exports.TestJwtHook = exports.RemoteSearchClient = exports.SearchController = exports.RemoteSearchApi = exports.SearchApi = exports.SPEC_TOOL_CATALOG = exports.SearchResponse = exports.SearchRequest = exports.UNKNOWN_VERSION = exports.OLDER_LEGACY_VERSION = exports.LEGACY_VERSION = exports.MODERN_VERSION = exports.ENDPOINT_PATH = exports.USER_ID = void 0;
|
|
5
5
|
const tslib_1 = require("tslib");
|
|
6
6
|
require("reflect-metadata");
|
|
7
7
|
const inversify_1 = require("inversify");
|
|
@@ -19,39 +19,50 @@ exports.LEGACY_VERSION = '2025-11-25';
|
|
|
19
19
|
exports.OLDER_LEGACY_VERSION = '2025-03-26';
|
|
20
20
|
/** A revision NO era knows: the one case a server is still right to refuse. */
|
|
21
21
|
exports.UNKNOWN_VERSION = '1999-01-01';
|
|
22
|
-
|
|
22
|
+
class SearchRequest {
|
|
23
23
|
/**
|
|
24
24
|
* Search text
|
|
25
25
|
* @mcpHeader query
|
|
26
26
|
*/
|
|
27
27
|
query;
|
|
28
|
-
}
|
|
28
|
+
}
|
|
29
29
|
exports.SearchRequest = SearchRequest;
|
|
30
|
-
|
|
31
|
-
(0, core_util_1.WpDtoField)(new core_util_1.WpDtoFieldOptions('Search text', true, undefined, false, undefined, undefined, undefined, new core_util_1.WpMcpHeader('query'))),
|
|
32
|
-
tslib_1.__metadata("design:type", String)
|
|
33
|
-
], SearchRequest.prototype, "query", void 0);
|
|
34
|
-
exports.SearchRequest = SearchRequest = tslib_1.__decorate([
|
|
35
|
-
(0, core_util_1.WpDto)()
|
|
36
|
-
], SearchRequest);
|
|
37
|
-
let SearchResponse = class SearchResponse {
|
|
30
|
+
class SearchResponse {
|
|
38
31
|
/** Authenticated user */
|
|
39
32
|
userId;
|
|
40
33
|
/** Search result */
|
|
41
34
|
result;
|
|
42
|
-
}
|
|
35
|
+
}
|
|
43
36
|
exports.SearchResponse = SearchResponse;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
37
|
+
/**
|
|
38
|
+
* The tool catalog these specs boot `WpMcpServer` with — the same shape `wp-openapi` writes to
|
|
39
|
+
* `mcp-tools.json` from the contracts above.
|
|
40
|
+
*
|
|
41
|
+
* It is built by hand here rather than extracted, because `@webpieces/mcp-server` deliberately does
|
|
42
|
+
* not depend on the TypeScript compiler API: the runtime's job is to BE HANDED a catalog, and these
|
|
43
|
+
* specs exercise exactly that. The extractor's own suite proves the catalog matches these contracts.
|
|
44
|
+
*/
|
|
45
|
+
function searchInputSchema() {
|
|
46
|
+
const query = new core_util_1.ApiJsonSchema('string');
|
|
47
|
+
query.description = 'Search text';
|
|
48
|
+
query['x-mcp-header'] = 'query';
|
|
49
|
+
return new core_util_1.ObjectSchemaBuilder().required('query', query).build();
|
|
50
|
+
}
|
|
51
|
+
function searchOutputSchema() {
|
|
52
|
+
const userId = new core_util_1.ApiJsonSchema('string');
|
|
53
|
+
userId.description = 'Authenticated user';
|
|
54
|
+
const result = new core_util_1.ApiJsonSchema('string');
|
|
55
|
+
result.description = 'Search result';
|
|
56
|
+
return new core_util_1.ObjectSchemaBuilder().required('userId', userId).required('result', result).build();
|
|
57
|
+
}
|
|
58
|
+
function searchTool(name, methodName, description, hints) {
|
|
59
|
+
return new core_util_1.McpToolDefinition(name, methodName, description, hints, searchInputSchema(), searchOutputSchema());
|
|
60
|
+
}
|
|
61
|
+
exports.SPEC_TOOL_CATALOG = new core_util_1.McpToolCatalog([
|
|
62
|
+
searchTool('account_search', 'search', 'Search records owned by the authenticated user.', new core_util_1.WpMcpToolHints(true, false, true, false)),
|
|
63
|
+
searchTool('admin_search', 'admin', 'Administrative search.', new core_util_1.WpMcpToolHints(false, true, false, false)),
|
|
64
|
+
searchTool('remote_search', 'search', 'Search a remote binding.', new core_util_1.WpMcpToolHints(false, true, false, false)),
|
|
65
|
+
]);
|
|
55
66
|
let SearchApi = class SearchApi {
|
|
56
67
|
/** Search records owned by the authenticated user. */
|
|
57
68
|
search(_request) {
|
|
@@ -67,12 +78,7 @@ tslib_1.__decorate([
|
|
|
67
78
|
(0, core_util_1.WpMcpAuthJwt)({ allRolesAllowed: true }),
|
|
68
79
|
(0, core_util_1.WpAuthJwt)({ allRolesAllowed: true }),
|
|
69
80
|
(0, core_util_1.Endpoint)(core_util_1.POST, '/search', core_util_1.READ, core_util_1.RPC),
|
|
70
|
-
(0, core_util_1.
|
|
71
|
-
(0, core_util_1.WpMcpTool)({
|
|
72
|
-
name: 'account_search',
|
|
73
|
-
description: 'Search records owned by the authenticated user.',
|
|
74
|
-
openWorldHint: false,
|
|
75
|
-
}),
|
|
81
|
+
(0, core_util_1.WpMcpTool)('account_search'),
|
|
76
82
|
tslib_1.__metadata("design:type", Function),
|
|
77
83
|
tslib_1.__metadata("design:paramtypes", [SearchRequest]),
|
|
78
84
|
tslib_1.__metadata("design:returntype", Promise)
|
|
@@ -81,12 +87,7 @@ tslib_1.__decorate([
|
|
|
81
87
|
(0, core_util_1.WpMcpAuthJwt)({ roles: ['admin'] }),
|
|
82
88
|
(0, core_util_1.WpAuthJwt)({ roles: ['admin'] }),
|
|
83
89
|
(0, core_util_1.Endpoint)(core_util_1.POST, '/admin', core_util_1.WRITE, core_util_1.RPC),
|
|
84
|
-
(0, core_util_1.
|
|
85
|
-
(0, core_util_1.WpMcpTool)({
|
|
86
|
-
name: 'admin_search',
|
|
87
|
-
description: 'Administrative search.',
|
|
88
|
-
openWorldHint: false,
|
|
89
|
-
}),
|
|
90
|
+
(0, core_util_1.WpMcpTool)('admin_search'),
|
|
90
91
|
tslib_1.__metadata("design:type", Function),
|
|
91
92
|
tslib_1.__metadata("design:paramtypes", [SearchRequest]),
|
|
92
93
|
tslib_1.__metadata("design:returntype", Promise)
|
|
@@ -106,12 +107,7 @@ tslib_1.__decorate([
|
|
|
106
107
|
(0, core_util_1.WpMcpAuthJwt)({ allRolesAllowed: true }),
|
|
107
108
|
(0, core_util_1.WpAuthOidc)(),
|
|
108
109
|
(0, core_util_1.Endpoint)(core_util_1.POST, '/search', core_util_1.WRITE, core_util_1.RPC),
|
|
109
|
-
(0, core_util_1.
|
|
110
|
-
(0, core_util_1.WpMcpTool)({
|
|
111
|
-
name: 'remote_search',
|
|
112
|
-
description: 'Search a remote binding.',
|
|
113
|
-
openWorldHint: false,
|
|
114
|
-
}),
|
|
110
|
+
(0, core_util_1.WpMcpTool)('remote_search'),
|
|
115
111
|
tslib_1.__metadata("design:type", Function),
|
|
116
112
|
tslib_1.__metadata("design:paramtypes", [SearchRequest]),
|
|
117
113
|
tslib_1.__metadata("design:returntype", Promise)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WpMcpServerTestFixtures.js","sourceRoot":"","sources":["../../../../../../packages/http/mcp-server/src/__tests__/WpMcpServerTestFixtures.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { injectable } from 'inversify';\nimport {\n ApiBadRequestError,\n ApiBadGatewayError,\n ApiConnectionError,\n ApiDependencyBackoffError,\n ApiDependencyError,\n ApiDependencyTimeoutError,\n ApiEndUserError,\n ApiPath,\n ApiType,\n ApiUnauthorizedError,\n ApiUnavailableError,\n Logger,\n LoggerFactory,\n LogLevel,\n ContextKey,\n ContextTuple,\n Endpoint,\n WpAuthJwt,\n WpAuthOidc,\n WpDto,\n WpDtoField,\n WpDtoFieldOptions,\n WpMcpAuthJwt,\n WpMcpHeader,\n WpMcpTool,\n WpResponseDto,\n MCP,\n POST,\n READ,\n RPC,\n SVC_TO_SVC,\n WRITE,\n} from '@webpieces/core-util';\nimport { RequestContext } from '@webpieces/core-context';\nimport { AuthenticatedCaller, JwtHook, MintedJwt } from '@webpieces/http-routing';\nimport { McpAccessTokenAuthority, VerifiedMcpCredential } from '../McpAuth';\nimport { MCP_INVOCATION_CONTEXT } from '../McpInvocationContext';\n\nexport const USER_ID = ContextKey.trusted<string>(\n 'mcpSpecUserId',\n 'proven by a credential verifier',\n);\nexport const ENDPOINT_PATH = '/app-owned/mcp';\nexport const MODERN_VERSION = '2026-07-28';\n/** What every shipping MCP client opens with today — the top of the 2025 negotiation table. */\nexport const LEGACY_VERSION = '2025-11-25';\n/** An older 2025-era revision the negotiation table still carries. */\nexport const OLDER_LEGACY_VERSION = '2025-03-26';\n/** A revision NO era knows: the one case a server is still right to refuse. */\nexport const UNKNOWN_VERSION = '1999-01-01';\n\n@WpDto()\nexport class SearchRequest {\n /**\n * Search text\n * @mcpHeader query\n */\n @WpDtoField(\n new WpDtoFieldOptions(\n 'Search text',\n true,\n undefined,\n false,\n undefined,\n undefined,\n undefined,\n new WpMcpHeader('query'),\n ),\n )\n query!: string;\n}\n\n@WpDto()\nexport class SearchResponse {\n /** Authenticated user */\n @WpDtoField(new WpDtoFieldOptions('Authenticated user', true))\n userId!: string;\n\n /** Search result */\n @WpDtoField(new WpDtoFieldOptions('Search result', true))\n result!: string;\n}\n\n@ApiPath('/mcp-spec')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class SearchApi {\n /** Search records owned by the authenticated user. */\n @WpMcpAuthJwt({ allRolesAllowed: true })\n @WpAuthJwt({ allRolesAllowed: true })\n @Endpoint(POST, '/search', READ, RPC)\n @WpResponseDto(() => SearchResponse)\n @WpMcpTool({\n name: 'account_search',\n description: 'Search records owned by the authenticated user.',\n openWorldHint: false,\n })\n search(_request: SearchRequest): Promise<SearchResponse> {\n throw new Error('contract only');\n }\n\n /** Administrative search. */\n @WpMcpAuthJwt({ roles: ['admin'] })\n @WpAuthJwt({ roles: ['admin'] })\n @Endpoint(POST, '/admin', WRITE, RPC)\n @WpResponseDto(() => SearchResponse)\n @WpMcpTool({\n name: 'admin_search',\n description: 'Administrative search.',\n openWorldHint: false,\n })\n admin(_request: SearchRequest): Promise<SearchResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/remote-spec')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class RemoteSearchApi {\n /** Search a remote binding. */\n @WpMcpAuthJwt({ allRolesAllowed: true })\n @WpAuthOidc()\n @Endpoint(POST, '/search', WRITE, RPC)\n @WpResponseDto(() => SearchResponse)\n @WpMcpTool({\n name: 'remote_search',\n description: 'Search a remote binding.',\n openWorldHint: false,\n })\n search(_request: SearchRequest): Promise<SearchResponse> {\n throw new Error('contract only');\n }\n}\n\n@injectable()\nexport class SearchController extends SearchApi {\n static waitStartedHook?: () => void;\n static waitAbortedHook?: () => void;\n adminInvocations = 0;\n\n override async search(request: SearchRequest): Promise<SearchResponse> {\n if (request.query === 'internal') throw new Error('database password appeared here');\n // A DOWNSTREAM call of ours failed. From the model's seat that is OUR bug, never a kind of\n // its own on the wire — see #959 and ApiErrorBoundary.encode.\n if (request.query === 'downstream') {\n throw new ApiConnectionError('ECONNREFUSED private-host:8443');\n }\n if (request.query === 'human') throw new ApiEndUserError('Safe human message', 'SAFE');\n if (request.query === 'malformed') {\n throw new ApiBadRequestError(\n 'operator-only: column tenant_7 rejected',\n '$.query',\n 'query is malformed',\n );\n }\n if (request.query === 'dependency') throw new ApiDependencyError('private upstream');\n if (request.query === 'bad-gateway') throw new ApiBadGatewayError('private proxy');\n if (request.query === 'timeout') throw new ApiDependencyTimeoutError('private timeout');\n if (request.query === 'backoff') {\n throw new ApiDependencyBackoffError('private throttling detail', 37);\n }\n if (request.query === 'unavailable') throw new ApiUnavailableError('private deployment');\n const invocation = RequestContext.getTrusted(MCP_INVOCATION_CONTEXT);\n if (request.query === 'progress') {\n await invocation?.reportProgress?.(1, 2, 'halfway');\n }\n if (request.query === 'wait' && invocation) {\n await invocation.reportProgress?.(0, 1, 'waiting');\n SearchController.waitStartedHook?.();\n await new Promise<void>((resolve: () => void) => {\n const finish = (): void => {\n SearchController.waitAbortedHook?.();\n resolve();\n };\n if (invocation.signal.aborted) finish();\n else invocation.signal.addEventListener('abort', finish, { once: true });\n });\n }\n return { userId: RequestContext.getTrusted(USER_ID) ?? 'missing', result: request.query };\n }\n\n override async admin(request: SearchRequest): Promise<SearchResponse> {\n this.adminInvocations += 1;\n return this.search(request);\n }\n}\n\nexport class RemoteSearchClient extends RemoteSearchApi {\n seenAuthorization?: string;\n\n override async search(request: SearchRequest): Promise<SearchResponse> {\n this.seenAuthorization = RequestContext.getRequest()?.getHeader('authorization');\n return { userId: RequestContext.getTrusted(USER_ID) ?? 'missing', result: request.query };\n }\n}\n\nexport class TestJwtHook extends JwtHook<string> {\n mintedTokens: string[] = [];\n lifetimeSeconds = 60;\n failWith?: Error;\n\n override async mint(subject: string): Promise<MintedJwt> {\n if (this.failWith) throw this.failWith;\n const token =\n subject === 'passthrough'\n ? 'mcp-passthrough'\n : `${subject}-endpoint-${this.mintedTokens.length + 1}`;\n this.mintedTokens.push(token);\n return new MintedJwt(token, Math.floor(Date.now() / 1000) + this.lifetimeSeconds);\n }\n\n override async parseJwt(token: string): Promise<AuthenticatedCaller> {\n if (token.startsWith('admin-endpoint-')) {\n return new AuthenticatedCaller(\n 'admin-7',\n ['admin'],\n [new ContextTuple(USER_ID, 'admin-7')],\n );\n }\n if (!token.startsWith('user-endpoint-')) throw new Error('invalid endpoint token');\n return new AuthenticatedCaller('user-7', [], [new ContextTuple(USER_ID, 'user-7')]);\n }\n}\n\nexport class TestTokenAuthority implements McpAccessTokenAuthority<string> {\n verificationCount = 0;\n roles: readonly string[] = [];\n\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 this.verificationCount += 1;\n if (token === 'invalid') throw new ApiUnauthorizedError('invalid token');\n if (token === 'authority-bug') throw new Error('SECRET-internal-detail');\n const now = Math.floor(Date.now() / 1000);\n const actualResource =\n token === 'wrong-resource' ? 'https://attacker.example/mcp' : resource;\n const subject = token.startsWith('mcp-admin')\n ? 'admin'\n : token === 'mcp-passthrough'\n ? 'passthrough'\n : 'user';\n const issuer =\n token === 'wrong-issuer' ? 'https://attacker.example' : 'https://login.example.test';\n const expiresAt = token === 'expired' ? now - 1 : now + 60;\n const scopes = token === 'missing-scope' ? [] : ['tools'];\n const validatedAt = token === 'stale-account' ? now - 3_601 : now;\n const credential = new VerifiedMcpCredential(\n subject,\n issuer,\n actualResource,\n token === 'expired' ? now - 61 : now,\n expiresAt,\n scopes,\n validatedAt,\n this.roles,\n [new ContextTuple(USER_ID, `${subject}-delegated`)],\n );\n if (token === 'list-bug') {\n Object.defineProperty(credential, 'listingRoles', {\n get: (): readonly string[] => {\n throw new Error('SECRET-internal-detail');\n },\n });\n }\n return credential;\n }\n}\n\nexport class RecordedLogLine {\n constructor(\n public readonly logger: string,\n public readonly level: LogLevel,\n public readonly message: string,\n public readonly error?: Error,\n ) {}\n\n /** The rendered line an operator would see, including an attached error's message and stack. */\n rendered(): string {\n return `${this.message} ${this.error?.message ?? ''} ${this.error?.stack ?? ''}`;\n }\n}\n\nclass RecordingLogger implements Logger {\n constructor(\n private readonly name: string,\n private readonly lines: RecordedLogLine[],\n ) {}\n\n trace(message: string, err?: Error): void {\n this.lines.push(new RecordedLogLine(this.name, 'trace', message, err));\n }\n\n debug(message: string, err?: Error): void {\n this.lines.push(new RecordedLogLine(this.name, 'debug', message, err));\n }\n\n info(message: string, err?: Error): void {\n this.lines.push(new RecordedLogLine(this.name, 'info', message, err));\n }\n\n warn(message: string, err?: Error): void {\n this.lines.push(new RecordedLogLine(this.name, 'warn', message, err));\n }\n\n error(message: string, err?: Error): void {\n this.lines.push(new RecordedLogLine(this.name, 'error', message, err));\n }\n}\n\n/** Captures every log line in-process so tests can count and level-check operator output. */\nexport class RecordingLoggerFactory implements LoggerFactory {\n readonly lines: RecordedLogLine[] = [];\n\n getLogger(name: string): Logger {\n return new RecordingLogger(name, this.lines);\n }\n\n containing(text: string): RecordedLogLine[] {\n return this.lines.filter((line: RecordedLogLine) => line.rendered().includes(text));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"WpMcpServerTestFixtures.js","sourceRoot":"","sources":["../../../../../../packages/http/mcp-server/src/__tests__/WpMcpServerTestFixtures.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { injectable } from 'inversify';\nimport {\n ApiBadRequestError,\n ApiBadGatewayError,\n ApiConnectionError,\n ApiDependencyBackoffError,\n ApiDependencyError,\n ApiDependencyTimeoutError,\n ApiEndUserError,\n ApiPath,\n ApiType,\n ApiUnauthorizedError,\n ApiUnavailableError,\n Logger,\n LoggerFactory,\n LogLevel,\n ContextKey,\n ContextTuple,\n Endpoint,\n WpAuthJwt,\n WpAuthOidc,\n ApiJsonSchema,\n McpToolCatalog,\n McpToolDefinition,\n ObjectSchemaBuilder,\n WpMcpAuthJwt,\n WpMcpTool,\n WpMcpToolHints,\n MCP,\n POST,\n READ,\n RPC,\n SVC_TO_SVC,\n WRITE,\n} from '@webpieces/core-util';\nimport { RequestContext } from '@webpieces/core-context';\nimport { AuthenticatedCaller, JwtHook, MintedJwt } from '@webpieces/http-routing';\nimport { McpAccessTokenAuthority, VerifiedMcpCredential } from '../McpAuth';\nimport { MCP_INVOCATION_CONTEXT } from '../McpInvocationContext';\n\nexport const USER_ID = ContextKey.trusted<string>(\n 'mcpSpecUserId',\n 'proven by a credential verifier',\n);\nexport const ENDPOINT_PATH = '/app-owned/mcp';\nexport const MODERN_VERSION = '2026-07-28';\n/** What every shipping MCP client opens with today — the top of the 2025 negotiation table. */\nexport const LEGACY_VERSION = '2025-11-25';\n/** An older 2025-era revision the negotiation table still carries. */\nexport const OLDER_LEGACY_VERSION = '2025-03-26';\n/** A revision NO era knows: the one case a server is still right to refuse. */\nexport const UNKNOWN_VERSION = '1999-01-01';\n\nexport class SearchRequest {\n /**\n * Search text\n * @mcpHeader query\n */\n query!: string;\n}\n\nexport class SearchResponse {\n /** Authenticated user */\n userId!: string;\n\n /** Search result */\n result!: string;\n}\n\n/**\n * The tool catalog these specs boot `WpMcpServer` with — the same shape `wp-openapi` writes to\n * `mcp-tools.json` from the contracts above.\n *\n * It is built by hand here rather than extracted, because `@webpieces/mcp-server` deliberately does\n * not depend on the TypeScript compiler API: the runtime's job is to BE HANDED a catalog, and these\n * specs exercise exactly that. The extractor's own suite proves the catalog matches these contracts.\n */\nfunction searchInputSchema(): ApiJsonSchema {\n const query = new ApiJsonSchema('string');\n query.description = 'Search text';\n query['x-mcp-header'] = 'query';\n return new ObjectSchemaBuilder().required('query', query).build();\n}\n\nfunction searchOutputSchema(): ApiJsonSchema {\n const userId = new ApiJsonSchema('string');\n userId.description = 'Authenticated user';\n const result = new ApiJsonSchema('string');\n result.description = 'Search result';\n return new ObjectSchemaBuilder().required('userId', userId).required('result', result).build();\n}\n\nfunction searchTool(\n name: string,\n methodName: string,\n description: string,\n hints: WpMcpToolHints,\n): McpToolDefinition {\n return new McpToolDefinition(\n name,\n methodName,\n description,\n hints,\n searchInputSchema(),\n searchOutputSchema(),\n );\n}\n\nexport const SPEC_TOOL_CATALOG = new McpToolCatalog([\n searchTool(\n 'account_search',\n 'search',\n 'Search records owned by the authenticated user.',\n new WpMcpToolHints(true, false, true, false),\n ),\n searchTool(\n 'admin_search',\n 'admin',\n 'Administrative search.',\n new WpMcpToolHints(false, true, false, false),\n ),\n searchTool(\n 'remote_search',\n 'search',\n 'Search a remote binding.',\n new WpMcpToolHints(false, true, false, false),\n ),\n]);\n\n@ApiPath('/mcp-spec')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class SearchApi {\n /** Search records owned by the authenticated user. */\n @WpMcpAuthJwt({ allRolesAllowed: true })\n @WpAuthJwt({ allRolesAllowed: true })\n @Endpoint(POST, '/search', READ, RPC)\n @WpMcpTool('account_search')\n search(_request: SearchRequest): Promise<SearchResponse> {\n throw new Error('contract only');\n }\n\n /** Administrative search. */\n @WpMcpAuthJwt({ roles: ['admin'] })\n @WpAuthJwt({ roles: ['admin'] })\n @Endpoint(POST, '/admin', WRITE, RPC)\n @WpMcpTool('admin_search')\n admin(_request: SearchRequest): Promise<SearchResponse> {\n throw new Error('contract only');\n }\n}\n\n@ApiPath('/remote-spec')\n@ApiType(SVC_TO_SVC, MCP)\nexport abstract class RemoteSearchApi {\n /** Search a remote binding. */\n @WpMcpAuthJwt({ allRolesAllowed: true })\n @WpAuthOidc()\n @Endpoint(POST, '/search', WRITE, RPC)\n @WpMcpTool('remote_search')\n search(_request: SearchRequest): Promise<SearchResponse> {\n throw new Error('contract only');\n }\n}\n\n@injectable()\nexport class SearchController extends SearchApi {\n static waitStartedHook?: () => void;\n static waitAbortedHook?: () => void;\n adminInvocations = 0;\n\n override async search(request: SearchRequest): Promise<SearchResponse> {\n if (request.query === 'internal') throw new Error('database password appeared here');\n // A DOWNSTREAM call of ours failed. From the model's seat that is OUR bug, never a kind of\n // its own on the wire — see #959 and ApiErrorBoundary.encode.\n if (request.query === 'downstream') {\n throw new ApiConnectionError('ECONNREFUSED private-host:8443');\n }\n if (request.query === 'human') throw new ApiEndUserError('Safe human message', 'SAFE');\n if (request.query === 'malformed') {\n throw new ApiBadRequestError(\n 'operator-only: column tenant_7 rejected',\n '$.query',\n 'query is malformed',\n );\n }\n if (request.query === 'dependency') throw new ApiDependencyError('private upstream');\n if (request.query === 'bad-gateway') throw new ApiBadGatewayError('private proxy');\n if (request.query === 'timeout') throw new ApiDependencyTimeoutError('private timeout');\n if (request.query === 'backoff') {\n throw new ApiDependencyBackoffError('private throttling detail', 37);\n }\n if (request.query === 'unavailable') throw new ApiUnavailableError('private deployment');\n const invocation = RequestContext.getTrusted(MCP_INVOCATION_CONTEXT);\n if (request.query === 'progress') {\n await invocation?.reportProgress?.(1, 2, 'halfway');\n }\n if (request.query === 'wait' && invocation) {\n await invocation.reportProgress?.(0, 1, 'waiting');\n SearchController.waitStartedHook?.();\n await new Promise<void>((resolve: () => void) => {\n const finish = (): void => {\n SearchController.waitAbortedHook?.();\n resolve();\n };\n if (invocation.signal.aborted) finish();\n else invocation.signal.addEventListener('abort', finish, { once: true });\n });\n }\n return { userId: RequestContext.getTrusted(USER_ID) ?? 'missing', result: request.query };\n }\n\n override async admin(request: SearchRequest): Promise<SearchResponse> {\n this.adminInvocations += 1;\n return this.search(request);\n }\n}\n\nexport class RemoteSearchClient extends RemoteSearchApi {\n seenAuthorization?: string;\n\n override async search(request: SearchRequest): Promise<SearchResponse> {\n this.seenAuthorization = RequestContext.getRequest()?.getHeader('authorization');\n return { userId: RequestContext.getTrusted(USER_ID) ?? 'missing', result: request.query };\n }\n}\n\nexport class TestJwtHook extends JwtHook<string> {\n mintedTokens: string[] = [];\n lifetimeSeconds = 60;\n failWith?: Error;\n\n override async mint(subject: string): Promise<MintedJwt> {\n if (this.failWith) throw this.failWith;\n const token =\n subject === 'passthrough'\n ? 'mcp-passthrough'\n : `${subject}-endpoint-${this.mintedTokens.length + 1}`;\n this.mintedTokens.push(token);\n return new MintedJwt(token, Math.floor(Date.now() / 1000) + this.lifetimeSeconds);\n }\n\n override async parseJwt(token: string): Promise<AuthenticatedCaller> {\n if (token.startsWith('admin-endpoint-')) {\n return new AuthenticatedCaller(\n 'admin-7',\n ['admin'],\n [new ContextTuple(USER_ID, 'admin-7')],\n );\n }\n if (!token.startsWith('user-endpoint-')) throw new Error('invalid endpoint token');\n return new AuthenticatedCaller('user-7', [], [new ContextTuple(USER_ID, 'user-7')]);\n }\n}\n\nexport class TestTokenAuthority implements McpAccessTokenAuthority<string> {\n verificationCount = 0;\n roles: readonly string[] = [];\n\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 this.verificationCount += 1;\n if (token === 'invalid') throw new ApiUnauthorizedError('invalid token');\n if (token === 'authority-bug') throw new Error('SECRET-internal-detail');\n const now = Math.floor(Date.now() / 1000);\n const actualResource =\n token === 'wrong-resource' ? 'https://attacker.example/mcp' : resource;\n const subject = token.startsWith('mcp-admin')\n ? 'admin'\n : token === 'mcp-passthrough'\n ? 'passthrough'\n : 'user';\n const issuer =\n token === 'wrong-issuer' ? 'https://attacker.example' : 'https://login.example.test';\n const expiresAt = token === 'expired' ? now - 1 : now + 60;\n const scopes = token === 'missing-scope' ? [] : ['tools'];\n const validatedAt = token === 'stale-account' ? now - 3_601 : now;\n const credential = new VerifiedMcpCredential(\n subject,\n issuer,\n actualResource,\n token === 'expired' ? now - 61 : now,\n expiresAt,\n scopes,\n validatedAt,\n this.roles,\n [new ContextTuple(USER_ID, `${subject}-delegated`)],\n );\n if (token === 'list-bug') {\n Object.defineProperty(credential, 'listingRoles', {\n get: (): readonly string[] => {\n throw new Error('SECRET-internal-detail');\n },\n });\n }\n return credential;\n }\n}\n\nexport class RecordedLogLine {\n constructor(\n public readonly logger: string,\n public readonly level: LogLevel,\n public readonly message: string,\n public readonly error?: Error,\n ) {}\n\n /** The rendered line an operator would see, including an attached error's message and stack. */\n rendered(): string {\n return `${this.message} ${this.error?.message ?? ''} ${this.error?.stack ?? ''}`;\n }\n}\n\nclass RecordingLogger implements Logger {\n constructor(\n private readonly name: string,\n private readonly lines: RecordedLogLine[],\n ) {}\n\n trace(message: string, err?: Error): void {\n this.lines.push(new RecordedLogLine(this.name, 'trace', message, err));\n }\n\n debug(message: string, err?: Error): void {\n this.lines.push(new RecordedLogLine(this.name, 'debug', message, err));\n }\n\n info(message: string, err?: Error): void {\n this.lines.push(new RecordedLogLine(this.name, 'info', message, err));\n }\n\n warn(message: string, err?: Error): void {\n this.lines.push(new RecordedLogLine(this.name, 'warn', message, err));\n }\n\n error(message: string, err?: Error): void {\n this.lines.push(new RecordedLogLine(this.name, 'error', message, err));\n }\n}\n\n/** Captures every log line in-process so tests can count and level-check operator output. */\nexport class RecordingLoggerFactory implements LoggerFactory {\n readonly lines: RecordedLogLine[] = [];\n\n getLogger(name: string): Logger {\n return new RecordingLogger(name, this.lines);\n }\n\n containing(text: string): RecordedLogLine[] {\n return this.lines.filter((line: RecordedLogLine) => line.rendered().includes(text));\n }\n}\n"]}
|