@webpieces/mcp-server 0.4.767 → 0.4.769
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/package.json +4 -4
- package/src/WpMcpServer.js +3 -1
- package/src/WpMcpServer.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/mcp-server",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.769",
|
|
4
4
|
"description": "Secure MCP tool generation from annotated Webpieces API contracts",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
26
|
-
"@webpieces/core-context": "0.4.
|
|
27
|
-
"@webpieces/core-util": "0.4.
|
|
28
|
-
"@webpieces/http-routing": "0.4.
|
|
26
|
+
"@webpieces/core-context": "0.4.769",
|
|
27
|
+
"@webpieces/core-util": "0.4.769",
|
|
28
|
+
"@webpieces/http-routing": "0.4.769",
|
|
29
29
|
"reflect-metadata": "0.2.2",
|
|
30
30
|
"tslib": "2.8.1"
|
|
31
31
|
}
|
package/src/WpMcpServer.js
CHANGED
|
@@ -194,7 +194,9 @@ class WpMcpServer {
|
|
|
194
194
|
const structured = this.record(error);
|
|
195
195
|
return {
|
|
196
196
|
content: [{ type: 'text', text: JSON.stringify(structured) }],
|
|
197
|
-
|
|
197
|
+
// `outputSchema` describes the endpoint's success DTO. MCP clients validate
|
|
198
|
+
// structuredContent against it even when isError is true, so error details must stay
|
|
199
|
+
// in text content until the protocol supports a separate error schema.
|
|
198
200
|
isError: true,
|
|
199
201
|
};
|
|
200
202
|
}
|
package/src/WpMcpServer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WpMcpServer.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/WpMcpServer.ts"],"names":[],"mappings":"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","sourcesContent":["import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n CallToolRequest,\n CallToolRequestSchema,\n CallToolResult,\n ErrorCode,\n ListToolsRequestSchema,\n McpError,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n ApiErrorPayload,\n ApiUnauthorizedError,\n DtoValue,\n LogManager,\n toError,\n} from '@webpieces/core-util';\nimport { ApiFactory, ClassType, MintedJwt } from '@webpieces/http-routing';\nimport {\n MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS,\n McpEndpointDescriptor,\n McpProtectedResourceMetadata,\n VerifiedMcpCredential,\n WpMcpServerConfig,\n} from './McpAuth';\nimport { McpApiDispatcher } from './McpApiDispatcher';\nimport { McpToolRegistry, RegisteredMcpTool } from './McpToolRegistry';\n\nconst log = LogManager.getLogger('WpMcpServer');\n\n/** The only error shape rendered into model-visible MCP content. */\nexport class ModelVisibleToolError {\n constructor(\n public readonly kind: string,\n public readonly message: string,\n public readonly requestId?: string,\n public readonly field?: string,\n public readonly callerMessage?: string,\n public readonly errorCode?: string,\n public readonly retryAfterSeconds?: number,\n ) {}\n}\n\n/**\n * Node-only MCP adapter. Build one Server per authenticated transport/session; every tool call still\n * re-enters the endpoint's ordinary AuthFilter, so tools/list visibility never grants access.\n */\nexport class WpMcpServer<TGrant, TMintRequest> {\n private readonly registry: McpToolRegistry;\n private readonly dispatcher: McpApiDispatcher;\n\n constructor(\n private readonly config: WpMcpServerConfig<TGrant, TMintRequest>,\n apiFactory: ApiFactory,\n apiClasses: readonly ClassType[],\n ) {\n this.registry = new McpToolRegistry(apiClasses);\n this.dispatcher = new McpApiDispatcher(apiFactory);\n }\n\n async build(accessToken: string): Promise<Server> {\n await this.verifyCredential(accessToken);\n return this.buildForAccessToken(accessToken);\n }\n\n protectedResourceMetadata(): McpProtectedResourceMetadata {\n return this.config.protectedResourceMetadata();\n }\n\n private buildForAccessToken(accessToken: string): Server {\n const server = new Server(\n // webpieces-disable no-anonymous-object-literals -- external MCP SDK request structure\n { name: this.config.name, version: this.config.version },\n // webpieces-disable no-anonymous-object-literals -- external MCP SDK capability structure\n { capabilities: { tools: {} } },\n );\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n const credential = await this.verifyCredential(accessToken);\n return {\n tools: this.registry.tools\n .filter((tool: RegisteredMcpTool) => tool.isVisibleTo(credential.listingRoles))\n .map((tool: RegisteredMcpTool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n outputSchema: tool.outputSchema,\n annotations: tool.annotations,\n })),\n };\n });\n server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {\n const tool = this.registry.find(request.params.name);\n if (!tool) {\n throw new McpError(ErrorCode.InvalidParams, `Unknown tool: ${request.params.name}`);\n }\n return this.call(tool, request.params.arguments ?? {}, accessToken);\n });\n return server;\n }\n\n private async verifyCredential(accessToken: string): Promise<VerifiedMcpCredential> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- security boundary logs owner detail and exposes only a generic authentication failure\n try {\n const credential = await this.config.accessTokenAuthority.verifyAccessToken(\n accessToken,\n this.config.resource,\n );\n this.validateCredential(credential);\n return credential;\n } catch (err: unknown) {\n const error = toError(err);\n log.warn('MCP access-token verification rejected a request', error);\n throw new ApiUnauthorizedError('MCP access token rejected.', undefined, error);\n }\n }\n\n private validateCredential(credential: VerifiedMcpCredential): void {\n const now = Math.floor(Date.now() / 1000);\n if (credential.subject.trim() === '') {\n throw new Error('MCP access token has no subject.');\n }\n if (\n !Number.isFinite(credential.issuedAtEpochSeconds) ||\n !Number.isFinite(credential.expiresAtEpochSeconds) ||\n !Number.isFinite(credential.accountValidatedAtEpochSeconds)\n ) {\n throw new Error('MCP access token security timestamps must be finite.');\n }\n if (credential.resource !== this.config.resource) {\n throw new Error('MCP access token was not issued for this protected resource.');\n }\n if (!this.config.authorizationServers.includes(credential.issuer)) {\n throw new Error('MCP access token issuer is not trusted by this protected resource.');\n }\n if (credential.issuedAtEpochSeconds > now) {\n throw new Error('MCP access token was issued in the future.');\n }\n if (credential.expiresAtEpochSeconds <= now) {\n throw new Error('MCP access token has expired.');\n }\n if (\n credential.expiresAtEpochSeconds - credential.issuedAtEpochSeconds >\n MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS\n ) {\n throw new Error('MCP access token lifetime exceeds 30 days.');\n }\n for (const scope of this.config.requiredScopes) {\n if (!credential.scopes.includes(scope)) {\n throw new Error(`MCP access token is missing required scope '${scope}'.`);\n }\n }\n if (\n credential.accountValidatedAtEpochSeconds > now ||\n now - credential.accountValidatedAtEpochSeconds > this.config.maxAccountValidationAgeSeconds\n ) {\n throw new Error('MCP account authorization state is not fresh enough for dispatch.');\n }\n }\n\n private async call(\n tool: RegisteredMcpTool,\n args: DtoValue,\n accessToken: string,\n ): Promise<CallToolResult> {\n let credential: VerifiedMcpCredential;\n // webpieces-disable no-unmanaged-exceptions -- MCP transport boundary normalizes authentication failures before they reach the model\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- MCP transport boundary\n try {\n credential = await this.verifyCredential(accessToken);\n } catch (err: unknown) {\n //const error = toError(err);\n throw new McpError(ErrorCode.InvalidRequest, 'Unauthorized');\n }\n let endpointJwt: MintedJwt;\n // webpieces-disable no-unmanaged-exceptions -- MCP transport boundary logs mint failures and exposes only a generic protocol error\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- MCP transport boundary\n try {\n const descriptor = new McpEndpointDescriptor(\n tool.name,\n tool.apiClass.name,\n tool.methodName,\n );\n const mintRequest = this.config.endpointMintRequest(credential, descriptor);\n endpointJwt = await this.config.endpointJwtAuthority.mint(mintRequest);\n this.validateEndpointJwt(endpointJwt, accessToken);\n } catch (err: unknown) {\n const error = toError(err);\n log.error(\n `MCP endpoint credential mint failed for ${tool.apiClass.name}.${tool.methodName}`,\n error,\n );\n throw new McpError(ErrorCode.InternalError, 'Internal Error');\n }\n const result = await this.dispatcher.call(tool, args ?? {}, endpointJwt.token);\n if (!result.success) return this.apiError(result.error, result.requestId);\n\n const outputFailure = this.registry.schemaBuilder.validate(tool.responseClass, result.value);\n if (outputFailure) {\n log.error(\n `MCP output schema violation for ${tool.apiClass.name}.${tool.methodName}: ${outputFailure.message}`,\n );\n return this.errorResult(\n new ModelVisibleToolError('implementation', 'Internal Error', result.requestId),\n );\n }\n let structured: Record<string, DtoValue>;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- transport boundary sanitizes serialization failures\n try {\n structured = this.record(result.value);\n } catch (err: unknown) {\n const error = toError(err);\n log.error(\n `MCP response serialization failed for ${tool.apiClass.name}.${tool.methodName}`,\n error,\n );\n return this.errorResult(\n new ModelVisibleToolError('implementation', 'Internal Error', result.requestId),\n );\n }\n return {\n content: [{ type: 'text', text: JSON.stringify(structured) }],\n structuredContent: structured,\n };\n }\n\n private validateEndpointJwt(endpointJwt: MintedJwt, accessToken: string): void {\n const now = Math.floor(Date.now() / 1000);\n if (endpointJwt.token === accessToken) {\n throw new Error('MCP access-token passthrough to endpoint dispatch is forbidden.');\n }\n if (endpointJwt.expiresAtEpochSeconds <= now) {\n throw new Error('MCP endpoint JWT has already expired.');\n }\n if (\n endpointJwt.expiresAtEpochSeconds - now >\n this.config.maxEndpointJwtLifetimeSeconds\n ) {\n throw new Error('MCP endpoint JWT lifetime exceeds the configured one-hour ceiling.');\n }\n }\n\n private apiError(payload: ApiErrorPayload, requestId: string): CallToolResult {\n return this.errorResult(\n new ModelVisibleToolError(\n payload.kind,\n payload.message,\n requestId,\n payload.field,\n payload.callerMessage,\n payload.errorCode,\n payload.retryAfterSeconds,\n ),\n );\n }\n\n private errorResult(error: ModelVisibleToolError): CallToolResult {\n const structured = this.record(error);\n return {\n content: [{ type: 'text', text: JSON.stringify(structured) }],\n structuredContent: structured,\n isError: true,\n };\n }\n\n private record(value: DtoValue): Record<string, DtoValue> {\n const json = JSON.stringify(value);\n if (json === undefined) throw new Error('MCP structured content is not JSON serializable.');\n const parsed = JSON.parse(json) as DtoValue;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n // webpieces-disable no-anonymous-object-literals -- external MCP structured-content wrapper\n return { value: parsed };\n }\n return parsed as Record<string, DtoValue>;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"WpMcpServer.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/WpMcpServer.ts"],"names":[],"mappings":"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","sourcesContent":["import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n CallToolRequest,\n CallToolRequestSchema,\n CallToolResult,\n ErrorCode,\n ListToolsRequestSchema,\n McpError,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n ApiErrorPayload,\n ApiUnauthorizedError,\n DtoValue,\n LogManager,\n toError,\n} from '@webpieces/core-util';\nimport { ApiFactory, ClassType, MintedJwt } from '@webpieces/http-routing';\nimport {\n MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS,\n McpEndpointDescriptor,\n McpProtectedResourceMetadata,\n VerifiedMcpCredential,\n WpMcpServerConfig,\n} from './McpAuth';\nimport { McpApiDispatcher } from './McpApiDispatcher';\nimport { McpToolRegistry, RegisteredMcpTool } from './McpToolRegistry';\n\nconst log = LogManager.getLogger('WpMcpServer');\n\n/** The only error shape rendered into model-visible MCP content. */\nexport class ModelVisibleToolError {\n constructor(\n public readonly kind: string,\n public readonly message: string,\n public readonly requestId?: string,\n public readonly field?: string,\n public readonly callerMessage?: string,\n public readonly errorCode?: string,\n public readonly retryAfterSeconds?: number,\n ) {}\n}\n\n/**\n * Node-only MCP adapter. Build one Server per authenticated transport/session; every tool call still\n * re-enters the endpoint's ordinary AuthFilter, so tools/list visibility never grants access.\n */\nexport class WpMcpServer<TGrant, TMintRequest> {\n private readonly registry: McpToolRegistry;\n private readonly dispatcher: McpApiDispatcher;\n\n constructor(\n private readonly config: WpMcpServerConfig<TGrant, TMintRequest>,\n apiFactory: ApiFactory,\n apiClasses: readonly ClassType[],\n ) {\n this.registry = new McpToolRegistry(apiClasses);\n this.dispatcher = new McpApiDispatcher(apiFactory);\n }\n\n async build(accessToken: string): Promise<Server> {\n await this.verifyCredential(accessToken);\n return this.buildForAccessToken(accessToken);\n }\n\n protectedResourceMetadata(): McpProtectedResourceMetadata {\n return this.config.protectedResourceMetadata();\n }\n\n private buildForAccessToken(accessToken: string): Server {\n const server = new Server(\n // webpieces-disable no-anonymous-object-literals -- external MCP SDK request structure\n { name: this.config.name, version: this.config.version },\n // webpieces-disable no-anonymous-object-literals -- external MCP SDK capability structure\n { capabilities: { tools: {} } },\n );\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n const credential = await this.verifyCredential(accessToken);\n return {\n tools: this.registry.tools\n .filter((tool: RegisteredMcpTool) => tool.isVisibleTo(credential.listingRoles))\n .map((tool: RegisteredMcpTool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n outputSchema: tool.outputSchema,\n annotations: tool.annotations,\n })),\n };\n });\n server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {\n const tool = this.registry.find(request.params.name);\n if (!tool) {\n throw new McpError(ErrorCode.InvalidParams, `Unknown tool: ${request.params.name}`);\n }\n return this.call(tool, request.params.arguments ?? {}, accessToken);\n });\n return server;\n }\n\n private async verifyCredential(accessToken: string): Promise<VerifiedMcpCredential> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- security boundary logs owner detail and exposes only a generic authentication failure\n try {\n const credential = await this.config.accessTokenAuthority.verifyAccessToken(\n accessToken,\n this.config.resource,\n );\n this.validateCredential(credential);\n return credential;\n } catch (err: unknown) {\n const error = toError(err);\n log.warn('MCP access-token verification rejected a request', error);\n throw new ApiUnauthorizedError('MCP access token rejected.', undefined, error);\n }\n }\n\n private validateCredential(credential: VerifiedMcpCredential): void {\n const now = Math.floor(Date.now() / 1000);\n if (credential.subject.trim() === '') {\n throw new Error('MCP access token has no subject.');\n }\n if (\n !Number.isFinite(credential.issuedAtEpochSeconds) ||\n !Number.isFinite(credential.expiresAtEpochSeconds) ||\n !Number.isFinite(credential.accountValidatedAtEpochSeconds)\n ) {\n throw new Error('MCP access token security timestamps must be finite.');\n }\n if (credential.resource !== this.config.resource) {\n throw new Error('MCP access token was not issued for this protected resource.');\n }\n if (!this.config.authorizationServers.includes(credential.issuer)) {\n throw new Error('MCP access token issuer is not trusted by this protected resource.');\n }\n if (credential.issuedAtEpochSeconds > now) {\n throw new Error('MCP access token was issued in the future.');\n }\n if (credential.expiresAtEpochSeconds <= now) {\n throw new Error('MCP access token has expired.');\n }\n if (\n credential.expiresAtEpochSeconds - credential.issuedAtEpochSeconds >\n MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS\n ) {\n throw new Error('MCP access token lifetime exceeds 30 days.');\n }\n for (const scope of this.config.requiredScopes) {\n if (!credential.scopes.includes(scope)) {\n throw new Error(`MCP access token is missing required scope '${scope}'.`);\n }\n }\n if (\n credential.accountValidatedAtEpochSeconds > now ||\n now - credential.accountValidatedAtEpochSeconds > this.config.maxAccountValidationAgeSeconds\n ) {\n throw new Error('MCP account authorization state is not fresh enough for dispatch.');\n }\n }\n\n private async call(\n tool: RegisteredMcpTool,\n args: DtoValue,\n accessToken: string,\n ): Promise<CallToolResult> {\n let credential: VerifiedMcpCredential;\n // webpieces-disable no-unmanaged-exceptions -- MCP transport boundary normalizes authentication failures before they reach the model\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- MCP transport boundary\n try {\n credential = await this.verifyCredential(accessToken);\n } catch (err: unknown) {\n //const error = toError(err);\n throw new McpError(ErrorCode.InvalidRequest, 'Unauthorized');\n }\n let endpointJwt: MintedJwt;\n // webpieces-disable no-unmanaged-exceptions -- MCP transport boundary logs mint failures and exposes only a generic protocol error\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- MCP transport boundary\n try {\n const descriptor = new McpEndpointDescriptor(\n tool.name,\n tool.apiClass.name,\n tool.methodName,\n );\n const mintRequest = this.config.endpointMintRequest(credential, descriptor);\n endpointJwt = await this.config.endpointJwtAuthority.mint(mintRequest);\n this.validateEndpointJwt(endpointJwt, accessToken);\n } catch (err: unknown) {\n const error = toError(err);\n log.error(\n `MCP endpoint credential mint failed for ${tool.apiClass.name}.${tool.methodName}`,\n error,\n );\n throw new McpError(ErrorCode.InternalError, 'Internal Error');\n }\n const result = await this.dispatcher.call(tool, args ?? {}, endpointJwt.token);\n if (!result.success) return this.apiError(result.error, result.requestId);\n\n const outputFailure = this.registry.schemaBuilder.validate(tool.responseClass, result.value);\n if (outputFailure) {\n log.error(\n `MCP output schema violation for ${tool.apiClass.name}.${tool.methodName}: ${outputFailure.message}`,\n );\n return this.errorResult(\n new ModelVisibleToolError('implementation', 'Internal Error', result.requestId),\n );\n }\n let structured: Record<string, DtoValue>;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- transport boundary sanitizes serialization failures\n try {\n structured = this.record(result.value);\n } catch (err: unknown) {\n const error = toError(err);\n log.error(\n `MCP response serialization failed for ${tool.apiClass.name}.${tool.methodName}`,\n error,\n );\n return this.errorResult(\n new ModelVisibleToolError('implementation', 'Internal Error', result.requestId),\n );\n }\n return {\n content: [{ type: 'text', text: JSON.stringify(structured) }],\n structuredContent: structured,\n };\n }\n\n private validateEndpointJwt(endpointJwt: MintedJwt, accessToken: string): void {\n const now = Math.floor(Date.now() / 1000);\n if (endpointJwt.token === accessToken) {\n throw new Error('MCP access-token passthrough to endpoint dispatch is forbidden.');\n }\n if (endpointJwt.expiresAtEpochSeconds <= now) {\n throw new Error('MCP endpoint JWT has already expired.');\n }\n if (\n endpointJwt.expiresAtEpochSeconds - now >\n this.config.maxEndpointJwtLifetimeSeconds\n ) {\n throw new Error('MCP endpoint JWT lifetime exceeds the configured one-hour ceiling.');\n }\n }\n\n private apiError(payload: ApiErrorPayload, requestId: string): CallToolResult {\n return this.errorResult(\n new ModelVisibleToolError(\n payload.kind,\n payload.message,\n requestId,\n payload.field,\n payload.callerMessage,\n payload.errorCode,\n payload.retryAfterSeconds,\n ),\n );\n }\n\n private errorResult(error: ModelVisibleToolError): CallToolResult {\n const structured = this.record(error);\n return {\n content: [{ type: 'text', text: JSON.stringify(structured) }],\n // `outputSchema` describes the endpoint's success DTO. MCP clients validate\n // structuredContent against it even when isError is true, so error details must stay\n // in text content until the protocol supports a separate error schema.\n isError: true,\n };\n }\n\n private record(value: DtoValue): Record<string, DtoValue> {\n const json = JSON.stringify(value);\n if (json === undefined) throw new Error('MCP structured content is not JSON serializable.');\n const parsed = JSON.parse(json) as DtoValue;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n // webpieces-disable no-anonymous-object-literals -- external MCP structured-content wrapper\n return { value: parsed };\n }\n return parsed as Record<string, DtoValue>;\n }\n}\n"]}
|