@shipfox/api-agent-access 20.2.0

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.
Files changed (76) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +21 -0
  3. package/dist/constants.d.ts +9 -0
  4. package/dist/constants.d.ts.map +1 -0
  5. package/dist/constants.js +17 -0
  6. package/dist/constants.js.map +1 -0
  7. package/dist/core/envelope.d.ts +9 -0
  8. package/dist/core/envelope.d.ts.map +1 -0
  9. package/dist/core/envelope.js +33 -0
  10. package/dist/core/envelope.js.map +1 -0
  11. package/dist/core/rate-limiter.d.ts +18 -0
  12. package/dist/core/rate-limiter.d.ts.map +1 -0
  13. package/dist/core/rate-limiter.js +67 -0
  14. package/dist/core/rate-limiter.js.map +1 -0
  15. package/dist/core/tools.d.ts +21 -0
  16. package/dist/core/tools.d.ts.map +1 -0
  17. package/dist/core/tools.js +60 -0
  18. package/dist/core/tools.js.map +1 -0
  19. package/dist/index.d.ts +11 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +12 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/metrics/index.d.ts +2 -0
  24. package/dist/metrics/index.d.ts.map +1 -0
  25. package/dist/metrics/index.js +3 -0
  26. package/dist/metrics/index.js.map +1 -0
  27. package/dist/metrics/instance.d.ts +8 -0
  28. package/dist/metrics/instance.d.ts.map +1 -0
  29. package/dist/metrics/instance.js +26 -0
  30. package/dist/metrics/instance.js.map +1 -0
  31. package/dist/module.d.ts +7 -0
  32. package/dist/module.d.ts.map +1 -0
  33. package/dist/module.js +12 -0
  34. package/dist/module.js.map +1 -0
  35. package/dist/presentation/audit.d.ts +15 -0
  36. package/dist/presentation/audit.d.ts.map +1 -0
  37. package/dist/presentation/audit.js +28 -0
  38. package/dist/presentation/audit.js.map +1 -0
  39. package/dist/presentation/mcp-server.d.ts +13 -0
  40. package/dist/presentation/mcp-server.d.ts.map +1 -0
  41. package/dist/presentation/mcp-server.js +171 -0
  42. package/dist/presentation/mcp-server.js.map +1 -0
  43. package/dist/presentation/routes.d.ts +14 -0
  44. package/dist/presentation/routes.d.ts.map +1 -0
  45. package/dist/presentation/routes.js +149 -0
  46. package/dist/presentation/routes.js.map +1 -0
  47. package/dist/tsconfig.test.tsbuildinfo +1 -0
  48. package/dist/version.d.ts +2 -0
  49. package/dist/version.d.ts.map +1 -0
  50. package/dist/version.js +5 -0
  51. package/dist/version.js.map +1 -0
  52. package/package.json +50 -0
  53. package/src/constants.ts +22 -0
  54. package/src/core/envelope.test.ts +9 -0
  55. package/src/core/envelope.ts +36 -0
  56. package/src/core/rate-limiter.test.ts +62 -0
  57. package/src/core/rate-limiter.ts +95 -0
  58. package/src/core/tools.test.ts +38 -0
  59. package/src/core/tools.ts +67 -0
  60. package/src/index.ts +54 -0
  61. package/src/metrics/index.ts +1 -0
  62. package/src/metrics/instance.test.ts +67 -0
  63. package/src/metrics/instance.ts +48 -0
  64. package/src/module.ts +19 -0
  65. package/src/presentation/audit.test.ts +67 -0
  66. package/src/presentation/audit.ts +45 -0
  67. package/src/presentation/mcp-server.test.ts +168 -0
  68. package/src/presentation/mcp-server.ts +212 -0
  69. package/src/presentation/routes.test.ts +193 -0
  70. package/src/presentation/routes.ts +171 -0
  71. package/src/version.ts +5 -0
  72. package/tsconfig.build.json +9 -0
  73. package/tsconfig.build.tsbuildinfo +1 -0
  74. package/tsconfig.json +3 -0
  75. package/tsconfig.test.json +8 -0
  76. package/vitest.config.ts +10 -0
@@ -0,0 +1,171 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
3
+ import { agentAccessEnvelopeSchema } from '@shipfox/api-agent-access-dto';
4
+ import { reportError } from '@shipfox/node-error-monitoring';
5
+ import { logger } from '@shipfox/node-opentelemetry';
6
+ import { AGENT_ACCESS_MCP_INSTRUCTIONS, AGENT_ACCESS_MCP_SERVER_NAME } from '#constants.js';
7
+ import { agentAccessError, serializeAgentAccessEnvelope } from '#core/envelope.js';
8
+ import { createAgentAccessRateLimiter } from '#core/rate-limiter.js';
9
+ import { createAgentAccessFixtureTool, createAgentAccessToolMap } from '#core/tools.js';
10
+ import { AGENT_ACCESS_PACKAGE_VERSION } from '#version.js';
11
+ import { createAgentAccessToolCallRecorder } from './audit.js';
12
+ const defaultTools = ()=>[
13
+ createAgentAccessFixtureTool()
14
+ ];
15
+ export function buildAgentAccessMcpServer(params) {
16
+ const tools = createAgentAccessToolMap(params.tools ?? defaultTools());
17
+ const rateLimiter = params.rateLimiter ?? createAgentAccessRateLimiter();
18
+ const recordCall = params.recordCall ?? createAgentAccessToolCallRecorder();
19
+ const server = new Server({
20
+ name: AGENT_ACCESS_MCP_SERVER_NAME,
21
+ version: AGENT_ACCESS_PACKAGE_VERSION
22
+ }, {
23
+ capabilities: {
24
+ tools: {}
25
+ },
26
+ instructions: AGENT_ACCESS_MCP_INSTRUCTIONS
27
+ });
28
+ server.setRequestHandler(ListToolsRequestSchema, ()=>({
29
+ tools: [
30
+ ...tools.values()
31
+ ].map((tool)=>({
32
+ name: tool.name,
33
+ description: tool.description,
34
+ inputSchema: tool.inputSchema,
35
+ outputSchema: tool.outputSchema,
36
+ annotations: {
37
+ readOnlyHint: true
38
+ }
39
+ }))
40
+ }));
41
+ server.setRequestHandler(CallToolRequestSchema, (request)=>handleAgentAccessToolCall({
42
+ name: request.params.name,
43
+ arguments: request.params.arguments,
44
+ context: params.context,
45
+ tools,
46
+ rateLimiter,
47
+ recordCall
48
+ }));
49
+ return server;
50
+ }
51
+ async function handleAgentAccessToolCall(params) {
52
+ const tool = params.tools.get(params.name);
53
+ const rateLimit = params.rateLimiter.consume(params.context.credential);
54
+ if (!rateLimit.allowed) {
55
+ recordToolCall(params.recordCall, {
56
+ tool: tool?.name ?? 'unknown',
57
+ outcome: 'rate-limited',
58
+ errorCode: 'rate-limited',
59
+ context: params.context
60
+ });
61
+ return toolResult(agentAccessError('rate-limited', rateLimit.retry_after_seconds === undefined ? {} : {
62
+ retryAfterSeconds: rateLimit.retry_after_seconds
63
+ }), true);
64
+ }
65
+ if (tool === undefined) return unknownToolResult(params);
66
+ const input = params.arguments ?? {};
67
+ if (!isRecord(input)) return invalidArgumentsResult(params, tool.name);
68
+ return await executeAgentAccessTool({
69
+ tool,
70
+ input,
71
+ context: params.context,
72
+ recordCall: params.recordCall
73
+ });
74
+ }
75
+ async function executeAgentAccessTool(params) {
76
+ try {
77
+ const response = await params.tool.execute({
78
+ context: params.context,
79
+ arguments: params.input
80
+ });
81
+ const envelope = agentAccessEnvelopeSchema.safeParse(response);
82
+ if (!envelope.success) {
83
+ recordToolCall(params.recordCall, {
84
+ tool: params.tool.name,
85
+ outcome: 'exception',
86
+ errorCode: 'invalid-tool-response',
87
+ context: params.context
88
+ });
89
+ return toolResult(agentAccessError('invalid-tool-response'), true);
90
+ }
91
+ const outcome = envelope.data.ok ? 'success' : 'tool-error';
92
+ const result = toolResult(envelope.data, !envelope.data.ok);
93
+ recordToolCall(params.recordCall, {
94
+ tool: params.tool.name,
95
+ outcome,
96
+ errorCode: envelope.data.ok ? 'none' : envelope.data.error?.code ?? 'unknown',
97
+ context: params.context
98
+ });
99
+ return result;
100
+ } catch (error) {
101
+ recordToolCall(params.recordCall, {
102
+ tool: params.tool.name,
103
+ outcome: 'exception',
104
+ errorCode: 'unknown',
105
+ context: params.context
106
+ });
107
+ logger().error({
108
+ err: error,
109
+ tool: params.tool.name
110
+ }, 'Agent-access tool execution failed');
111
+ reportError(error, {
112
+ boundary: 'agent-access.mcp',
113
+ operation: 'tool-call'
114
+ });
115
+ return toolResult(agentAccessError('tool-failed'), true);
116
+ }
117
+ }
118
+ function unknownToolResult(params) {
119
+ recordToolCall(params.recordCall, {
120
+ tool: 'unknown',
121
+ outcome: 'invalid-request',
122
+ errorCode: 'unknown-tool',
123
+ context: params.context
124
+ });
125
+ return toolResult(agentAccessError('unknown-tool', {
126
+ message: 'Tool is not available'
127
+ }), true);
128
+ }
129
+ function invalidArgumentsResult(params, toolName) {
130
+ recordToolCall(params.recordCall, {
131
+ tool: toolName,
132
+ outcome: 'invalid-request',
133
+ errorCode: 'invalid-request',
134
+ context: params.context
135
+ });
136
+ return toolResult(agentAccessError('invalid-request', {
137
+ message: 'Tool arguments must be an object'
138
+ }), true);
139
+ }
140
+ function toolResult(envelope, isError) {
141
+ return {
142
+ ...isError ? {
143
+ isError: true
144
+ } : {},
145
+ content: [
146
+ {
147
+ type: 'text',
148
+ text: serializeAgentAccessEnvelope(envelope)
149
+ }
150
+ ],
151
+ structuredContent: envelope
152
+ };
153
+ }
154
+ function recordToolCall(recordCall, record) {
155
+ try {
156
+ recordCall(record);
157
+ } catch (error) {
158
+ logger().error({
159
+ err: error
160
+ }, 'Failed to record agent-access tool audit event');
161
+ reportError(error, {
162
+ boundary: 'agent-access.mcp',
163
+ operation: 'audit'
164
+ });
165
+ }
166
+ }
167
+ function isRecord(value) {
168
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
169
+ }
170
+
171
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/presentation/mcp-server.ts"],"sourcesContent":["import {Server} from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n CallToolRequestSchema,\n type CallToolResult,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {agentAccessEnvelopeSchema} from '@shipfox/api-agent-access-dto';\nimport type {AgentAccessContext} from '@shipfox/api-auth-context';\nimport {reportError} from '@shipfox/node-error-monitoring';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {AGENT_ACCESS_MCP_INSTRUCTIONS, AGENT_ACCESS_MCP_SERVER_NAME} from '#constants.js';\nimport {agentAccessError, serializeAgentAccessEnvelope} from '#core/envelope.js';\nimport {type AgentAccessRateLimiter, createAgentAccessRateLimiter} from '#core/rate-limiter.js';\nimport {\n type AgentAccessTool,\n type AgentAccessToolMap,\n createAgentAccessFixtureTool,\n createAgentAccessToolMap,\n} from '#core/tools.js';\nimport type {AgentAccessToolCallOutcome} from '#metrics/index.js';\nimport {AGENT_ACCESS_PACKAGE_VERSION} from '#version.js';\nimport {type AgentAccessToolCallRecorder, createAgentAccessToolCallRecorder} from './audit.js';\n\nexport interface BuildAgentAccessMcpServerParams {\n context: AgentAccessContext;\n tools?: readonly AgentAccessTool[] | undefined;\n rateLimiter?: AgentAccessRateLimiter | undefined;\n recordCall?: AgentAccessToolCallRecorder | undefined;\n}\n\nconst defaultTools = (): readonly AgentAccessTool[] => [createAgentAccessFixtureTool()];\n\nexport function buildAgentAccessMcpServer(params: BuildAgentAccessMcpServerParams): Server {\n const tools = createAgentAccessToolMap(params.tools ?? defaultTools());\n const rateLimiter = params.rateLimiter ?? createAgentAccessRateLimiter();\n const recordCall = params.recordCall ?? createAgentAccessToolCallRecorder();\n const server = new Server(\n {name: AGENT_ACCESS_MCP_SERVER_NAME, version: AGENT_ACCESS_PACKAGE_VERSION},\n {\n capabilities: {tools: {}},\n instructions: AGENT_ACCESS_MCP_INSTRUCTIONS,\n },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, () => ({\n tools: [...tools.values()].map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema as {\n type: 'object';\n properties?: Record<string, object> | undefined;\n required?: string[] | undefined;\n },\n outputSchema: tool.outputSchema as {\n type: 'object';\n properties?: Record<string, object> | undefined;\n required?: string[] | undefined;\n },\n annotations: {readOnlyHint: true},\n })),\n }));\n\n server.setRequestHandler(CallToolRequestSchema, (request) =>\n handleAgentAccessToolCall({\n name: request.params.name,\n arguments: request.params.arguments,\n context: params.context,\n tools,\n rateLimiter,\n recordCall,\n }),\n );\n\n return server;\n}\n\ninterface HandleAgentAccessToolCallParams {\n name: string;\n arguments?: Record<string, unknown> | undefined;\n context: AgentAccessContext;\n tools: AgentAccessToolMap;\n rateLimiter: AgentAccessRateLimiter;\n recordCall: AgentAccessToolCallRecorder;\n}\n\nasync function handleAgentAccessToolCall(\n params: HandleAgentAccessToolCallParams,\n): Promise<CallToolResult> {\n const tool = params.tools.get(params.name);\n const rateLimit = params.rateLimiter.consume(params.context.credential);\n if (!rateLimit.allowed) {\n recordToolCall(params.recordCall, {\n tool: tool?.name ?? 'unknown',\n outcome: 'rate-limited',\n errorCode: 'rate-limited',\n context: params.context,\n });\n return toolResult(\n agentAccessError(\n 'rate-limited',\n rateLimit.retry_after_seconds === undefined\n ? {}\n : {retryAfterSeconds: rateLimit.retry_after_seconds},\n ),\n true,\n );\n }\n if (tool === undefined) return unknownToolResult(params);\n\n const input = params.arguments ?? {};\n if (!isRecord(input)) return invalidArgumentsResult(params, tool.name);\n return await executeAgentAccessTool({\n tool,\n input,\n context: params.context,\n recordCall: params.recordCall,\n });\n}\n\nasync function executeAgentAccessTool(params: {\n tool: AgentAccessTool;\n input: Record<string, unknown>;\n context: AgentAccessContext;\n recordCall: AgentAccessToolCallRecorder;\n}): Promise<CallToolResult> {\n try {\n const response = await params.tool.execute({context: params.context, arguments: params.input});\n const envelope = agentAccessEnvelopeSchema.safeParse(response);\n if (!envelope.success) {\n recordToolCall(params.recordCall, {\n tool: params.tool.name,\n outcome: 'exception',\n errorCode: 'invalid-tool-response',\n context: params.context,\n });\n return toolResult(agentAccessError('invalid-tool-response'), true);\n }\n\n const outcome: AgentAccessToolCallOutcome = envelope.data.ok ? 'success' : 'tool-error';\n const result = toolResult(envelope.data, !envelope.data.ok);\n recordToolCall(params.recordCall, {\n tool: params.tool.name,\n outcome,\n errorCode: envelope.data.ok ? 'none' : (envelope.data.error?.code ?? 'unknown'),\n context: params.context,\n });\n return result;\n } catch (error) {\n recordToolCall(params.recordCall, {\n tool: params.tool.name,\n outcome: 'exception',\n errorCode: 'unknown',\n context: params.context,\n });\n logger().error({err: error, tool: params.tool.name}, 'Agent-access tool execution failed');\n reportError(error, {boundary: 'agent-access.mcp', operation: 'tool-call'});\n return toolResult(agentAccessError('tool-failed'), true);\n }\n}\n\nfunction unknownToolResult(params: HandleAgentAccessToolCallParams): CallToolResult {\n recordToolCall(params.recordCall, {\n tool: 'unknown',\n outcome: 'invalid-request',\n errorCode: 'unknown-tool',\n context: params.context,\n });\n return toolResult(agentAccessError('unknown-tool', {message: 'Tool is not available'}), true);\n}\n\nfunction invalidArgumentsResult(\n params: HandleAgentAccessToolCallParams,\n toolName: string,\n): CallToolResult {\n recordToolCall(params.recordCall, {\n tool: toolName,\n outcome: 'invalid-request',\n errorCode: 'invalid-request',\n context: params.context,\n });\n return toolResult(\n agentAccessError('invalid-request', {message: 'Tool arguments must be an object'}),\n true,\n );\n}\n\nfunction toolResult(\n envelope: ReturnType<typeof agentAccessError>,\n isError: boolean,\n): CallToolResult {\n return {\n ...(isError ? {isError: true} : {}),\n content: [{type: 'text', text: serializeAgentAccessEnvelope(envelope)}],\n structuredContent: envelope as Record<string, unknown>,\n };\n}\n\nfunction recordToolCall(\n recordCall: AgentAccessToolCallRecorder,\n record: Parameters<AgentAccessToolCallRecorder>[0],\n): void {\n try {\n recordCall(record);\n } catch (error) {\n logger().error({err: error}, 'Failed to record agent-access tool audit event');\n reportError(error, {boundary: 'agent-access.mcp', operation: 'audit'});\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"],"names":["Server","CallToolRequestSchema","ListToolsRequestSchema","agentAccessEnvelopeSchema","reportError","logger","AGENT_ACCESS_MCP_INSTRUCTIONS","AGENT_ACCESS_MCP_SERVER_NAME","agentAccessError","serializeAgentAccessEnvelope","createAgentAccessRateLimiter","createAgentAccessFixtureTool","createAgentAccessToolMap","AGENT_ACCESS_PACKAGE_VERSION","createAgentAccessToolCallRecorder","defaultTools","buildAgentAccessMcpServer","params","tools","rateLimiter","recordCall","server","name","version","capabilities","instructions","setRequestHandler","values","map","tool","description","inputSchema","outputSchema","annotations","readOnlyHint","request","handleAgentAccessToolCall","arguments","context","get","rateLimit","consume","credential","allowed","recordToolCall","outcome","errorCode","toolResult","retry_after_seconds","undefined","retryAfterSeconds","unknownToolResult","input","isRecord","invalidArgumentsResult","executeAgentAccessTool","response","execute","envelope","safeParse","success","data","ok","result","error","code","err","boundary","operation","message","toolName","isError","content","type","text","structuredContent","record","value","Array","isArray"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type RouteGroup } from '@shipfox/node-fastify';
2
+ import { type AgentAccessRateLimiter } from '#core/rate-limiter.js';
3
+ import { type AgentAccessTool } from '#core/tools.js';
4
+ import { type AgentAccessToolCallRecorder } from './audit.js';
5
+ export interface CreateAgentAccessRoutesOptions {
6
+ apiPublicUrl?: string | undefined;
7
+ protectedResourceMetadataUrl?: string | undefined;
8
+ tools?: readonly AgentAccessTool[] | undefined;
9
+ rateLimiter?: AgentAccessRateLimiter | undefined;
10
+ recordCall?: AgentAccessToolCallRecorder | undefined;
11
+ isOriginAllowed?: ((origin: string | undefined) => boolean) | undefined;
12
+ }
13
+ export declare function createAgentAccessRoutes(options?: CreateAgentAccessRoutesOptions): RouteGroup;
14
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/presentation/routes.ts"],"names":[],"mappings":"AAIA,OAAO,EAOL,KAAK,UAAU,EAEhB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAC,KAAK,sBAAsB,EAA+B,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAC,KAAK,eAAe,EAA+B,MAAM,gBAAgB,CAAC;AAElF,OAAO,EAAC,KAAK,2BAA2B,EAAoC,MAAM,YAAY,CAAC;AAK/F,MAAM,WAAW,8BAA8B;IAC7C,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,4BAA4B,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClD,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,GAAG,SAAS,CAAC;IAC/C,WAAW,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;IACjD,UAAU,CAAC,EAAE,2BAA2B,GAAG,SAAS,CAAC;IACrD,eAAe,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC,GAAG,SAAS,CAAC;CACzE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,8BAAmC,GAAG,UAAU,CA0EhG"}
@@ -0,0 +1,149 @@
1
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
2
+ import { AUTH_AGENT_ACCESS, requireAgentAccessContext } from '@shipfox/api-auth-context';
3
+ import { reportError } from '@shipfox/node-error-monitoring';
4
+ import { ClientError, createAllowedOriginMatcher, errorHandler as defaultErrorHandler, defineRoute } from '@shipfox/node-fastify';
5
+ import { logger } from '@shipfox/node-opentelemetry';
6
+ import { AGENT_ACCESS_MCP_PATH, AGENT_ACCESS_PROTECTED_RESOURCE_METADATA_PATH } from '#constants.js';
7
+ import { createAgentAccessRateLimiter } from '#core/rate-limiter.js';
8
+ import { createAgentAccessFixtureTool } from '#core/tools.js';
9
+ import { recordAgentAccessAuthFailure } from '#metrics/index.js';
10
+ import { createAgentAccessToolCallRecorder } from './audit.js';
11
+ import { buildAgentAccessMcpServer } from './mcp-server.js';
12
+ const TRAILING_SLASHES_RE = /\/+$/u;
13
+ export function createAgentAccessRoutes(options = {}) {
14
+ const tools = options.tools ?? [
15
+ createAgentAccessFixtureTool()
16
+ ];
17
+ const rateLimiter = options.rateLimiter ?? createAgentAccessRateLimiter();
18
+ const recordCall = options.recordCall ?? createAgentAccessToolCallRecorder();
19
+ const originMatcher = options.isOriginAllowed ?? createAllowedOriginMatcher();
20
+ const errorHandler = createAgentAccessErrorHandler(resourceMetadataUrl(options));
21
+ return {
22
+ prefix: '',
23
+ routes: [
24
+ defineRoute({
25
+ method: 'GET',
26
+ path: AGENT_ACCESS_MCP_PATH,
27
+ description: 'MCP endpoint does not provide an SSE GET stream.',
28
+ preAuth: createOriginGuard(originMatcher),
29
+ handler: methodNotAllowed
30
+ }),
31
+ defineRoute({
32
+ method: 'DELETE',
33
+ path: AGENT_ACCESS_MCP_PATH,
34
+ description: 'MCP endpoint does not provide DELETE session operations.',
35
+ preAuth: createOriginGuard(originMatcher),
36
+ handler: methodNotAllowed
37
+ }),
38
+ defineRoute({
39
+ method: 'POST',
40
+ path: AGENT_ACCESS_MCP_PATH,
41
+ description: 'Stateless Streamable HTTP MCP endpoint for agent-access tools.',
42
+ auth: AUTH_AGENT_ACCESS,
43
+ preAuth: createOriginGuard(originMatcher),
44
+ errorHandler,
45
+ handler: async (request, reply)=>{
46
+ const context = requireAgentAccessContext(request);
47
+ const server = buildAgentAccessMcpServer({
48
+ context,
49
+ tools,
50
+ rateLimiter,
51
+ recordCall
52
+ });
53
+ // No sessionIdGenerator selects the SDK's stateless transport mode.
54
+ const transport = new StreamableHTTPServerTransport();
55
+ let connected = false;
56
+ let cleanedUp = false;
57
+ const cleanup = ()=>{
58
+ if (cleanedUp) return;
59
+ cleanedUp = true;
60
+ void transport.close().catch((error)=>{
61
+ logger().error({
62
+ err: error
63
+ }, 'Failed to close agent-access transport');
64
+ reportError(error, {
65
+ boundary: 'agent-access.mcp',
66
+ operation: 'close-transport'
67
+ });
68
+ });
69
+ if (connected) {
70
+ void server.close().catch((error)=>{
71
+ logger().error({
72
+ err: error
73
+ }, 'Failed to close agent-access server');
74
+ reportError(error, {
75
+ boundary: 'agent-access.mcp',
76
+ operation: 'close-server'
77
+ });
78
+ });
79
+ }
80
+ };
81
+ reply.raw.once('close', cleanup);
82
+ try {
83
+ await server.connect(transport);
84
+ connected = true;
85
+ reply.hijack();
86
+ await transport.handleRequest(request.raw, reply.raw, request.body);
87
+ } catch (error) {
88
+ cleanup();
89
+ throw error;
90
+ }
91
+ }
92
+ })
93
+ ]
94
+ };
95
+ }
96
+ function methodNotAllowed(_request, reply) {
97
+ return reply.code(405).header('allow', 'POST').send({
98
+ code: 'method-not-allowed'
99
+ });
100
+ }
101
+ function createOriginGuard(isOriginAllowed) {
102
+ return async (request, reply)=>{
103
+ if (isOriginAllowed(request.headers.origin)) return;
104
+ recordAgentAccessAuthFailure('origin-not-allowed');
105
+ await reply.code(403).send({
106
+ code: 'origin-not-allowed'
107
+ });
108
+ };
109
+ }
110
+ function createAgentAccessErrorHandler(resourceMetadataUrl) {
111
+ const challenge = `Bearer scope="read", resource_metadata="${escapeHeaderValue(resourceMetadataUrl)}"`;
112
+ return (error, request, reply)=>{
113
+ const status = errorStatus(error);
114
+ const reason = authFailureReason(error, status, request);
115
+ if (reason !== undefined) recordAgentAccessAuthFailure(reason);
116
+ if (status === 401) reply.header('www-authenticate', challenge);
117
+ return defaultErrorHandler(error, request, reply);
118
+ };
119
+ }
120
+ function resourceMetadataUrl(options) {
121
+ if (options.protectedResourceMetadataUrl !== undefined) {
122
+ return options.protectedResourceMetadataUrl;
123
+ }
124
+ if (options.apiPublicUrl === undefined) return AGENT_ACCESS_PROTECTED_RESOURCE_METADATA_PATH;
125
+ return `${options.apiPublicUrl.replace(TRAILING_SLASHES_RE, '')}${AGENT_ACCESS_PROTECTED_RESOURCE_METADATA_PATH}`;
126
+ }
127
+ function escapeHeaderValue(value) {
128
+ return value.replace(/[\\"\r\n]/gu, (character)=>{
129
+ if (character === '\r' || character === '\n') return '';
130
+ return `\\${character}`;
131
+ });
132
+ }
133
+ function errorStatus(error) {
134
+ if (error instanceof ClientError) return error.status ?? 400;
135
+ if (isRecord(error) && typeof error.statusCode === 'number') return error.statusCode;
136
+ return undefined;
137
+ }
138
+ function authFailureReason(error, status, request) {
139
+ if (isRecord(error) && error.code === 'auth-dependency-unavailable') {
140
+ return 'dependency-unavailable';
141
+ }
142
+ if (status !== 401) return undefined;
143
+ return request.headers.authorization === undefined ? 'missing' : 'invalid';
144
+ }
145
+ function isRecord(value) {
146
+ return typeof value === 'object' && value !== null;
147
+ }
148
+
149
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/presentation/routes.ts"],"sourcesContent":["import {StreamableHTTPServerTransport} from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport type {Transport} from '@modelcontextprotocol/sdk/shared/transport.js';\nimport {AUTH_AGENT_ACCESS, requireAgentAccessContext} from '@shipfox/api-auth-context';\nimport {reportError} from '@shipfox/node-error-monitoring';\nimport {\n ClientError,\n createAllowedOriginMatcher,\n errorHandler as defaultErrorHandler,\n defineRoute,\n type FastifyReply,\n type FastifyRequest,\n type RouteGroup,\n type RoutePreHandler,\n} from '@shipfox/node-fastify';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {AGENT_ACCESS_MCP_PATH, AGENT_ACCESS_PROTECTED_RESOURCE_METADATA_PATH} from '#constants.js';\nimport {type AgentAccessRateLimiter, createAgentAccessRateLimiter} from '#core/rate-limiter.js';\nimport {type AgentAccessTool, createAgentAccessFixtureTool} from '#core/tools.js';\nimport {recordAgentAccessAuthFailure} from '#metrics/index.js';\nimport {type AgentAccessToolCallRecorder, createAgentAccessToolCallRecorder} from './audit.js';\nimport {buildAgentAccessMcpServer} from './mcp-server.js';\n\nconst TRAILING_SLASHES_RE = /\\/+$/u;\n\nexport interface CreateAgentAccessRoutesOptions {\n apiPublicUrl?: string | undefined;\n protectedResourceMetadataUrl?: string | undefined;\n tools?: readonly AgentAccessTool[] | undefined;\n rateLimiter?: AgentAccessRateLimiter | undefined;\n recordCall?: AgentAccessToolCallRecorder | undefined;\n isOriginAllowed?: ((origin: string | undefined) => boolean) | undefined;\n}\n\nexport function createAgentAccessRoutes(options: CreateAgentAccessRoutesOptions = {}): RouteGroup {\n const tools = options.tools ?? [createAgentAccessFixtureTool()];\n const rateLimiter = options.rateLimiter ?? createAgentAccessRateLimiter();\n const recordCall = options.recordCall ?? createAgentAccessToolCallRecorder();\n const originMatcher = options.isOriginAllowed ?? createAllowedOriginMatcher();\n const errorHandler = createAgentAccessErrorHandler(resourceMetadataUrl(options));\n\n return {\n prefix: '',\n routes: [\n defineRoute({\n method: 'GET',\n path: AGENT_ACCESS_MCP_PATH,\n description: 'MCP endpoint does not provide an SSE GET stream.',\n preAuth: createOriginGuard(originMatcher),\n handler: methodNotAllowed,\n }),\n defineRoute({\n method: 'DELETE',\n path: AGENT_ACCESS_MCP_PATH,\n description: 'MCP endpoint does not provide DELETE session operations.',\n preAuth: createOriginGuard(originMatcher),\n handler: methodNotAllowed,\n }),\n defineRoute({\n method: 'POST',\n path: AGENT_ACCESS_MCP_PATH,\n description: 'Stateless Streamable HTTP MCP endpoint for agent-access tools.',\n auth: AUTH_AGENT_ACCESS,\n preAuth: createOriginGuard(originMatcher),\n errorHandler,\n handler: async (request, reply) => {\n const context = requireAgentAccessContext(request);\n const server = buildAgentAccessMcpServer({context, tools, rateLimiter, recordCall});\n // No sessionIdGenerator selects the SDK's stateless transport mode.\n const transport = new StreamableHTTPServerTransport();\n let connected = false;\n let cleanedUp = false;\n\n const cleanup = () => {\n if (cleanedUp) return;\n cleanedUp = true;\n void transport.close().catch((error) => {\n logger().error({err: error}, 'Failed to close agent-access transport');\n reportError(error, {\n boundary: 'agent-access.mcp',\n operation: 'close-transport',\n });\n });\n if (connected) {\n void server.close().catch((error) => {\n logger().error({err: error}, 'Failed to close agent-access server');\n reportError(error, {\n boundary: 'agent-access.mcp',\n operation: 'close-server',\n });\n });\n }\n };\n\n reply.raw.once('close', cleanup);\n try {\n await server.connect(transport as unknown as Transport);\n connected = true;\n reply.hijack();\n await transport.handleRequest(request.raw, reply.raw, request.body);\n } catch (error) {\n cleanup();\n throw error;\n }\n },\n }),\n ],\n };\n}\n\nfunction methodNotAllowed(_request: FastifyRequest, reply: FastifyReply) {\n return reply.code(405).header('allow', 'POST').send({code: 'method-not-allowed'});\n}\n\nfunction createOriginGuard(\n isOriginAllowed: (origin: string | undefined) => boolean,\n): RoutePreHandler {\n return async (request, reply) => {\n if (isOriginAllowed(request.headers.origin)) return;\n recordAgentAccessAuthFailure('origin-not-allowed');\n await reply.code(403).send({code: 'origin-not-allowed'});\n };\n}\n\nfunction createAgentAccessErrorHandler(resourceMetadataUrl: string) {\n const challenge = `Bearer scope=\"read\", resource_metadata=\"${escapeHeaderValue(resourceMetadataUrl)}\"`;\n\n return (error: unknown, request: FastifyRequest, reply: FastifyReply) => {\n const status = errorStatus(error);\n const reason = authFailureReason(error, status, request);\n if (reason !== undefined) recordAgentAccessAuthFailure(reason);\n if (status === 401) reply.header('www-authenticate', challenge);\n return defaultErrorHandler(error, request, reply);\n };\n}\n\nfunction resourceMetadataUrl(options: CreateAgentAccessRoutesOptions): string {\n if (options.protectedResourceMetadataUrl !== undefined) {\n return options.protectedResourceMetadataUrl;\n }\n if (options.apiPublicUrl === undefined) return AGENT_ACCESS_PROTECTED_RESOURCE_METADATA_PATH;\n return `${options.apiPublicUrl.replace(TRAILING_SLASHES_RE, '')}${AGENT_ACCESS_PROTECTED_RESOURCE_METADATA_PATH}`;\n}\n\nfunction escapeHeaderValue(value: string): string {\n return value.replace(/[\\\\\"\\r\\n]/gu, (character) => {\n if (character === '\\r' || character === '\\n') return '';\n return `\\\\${character}`;\n });\n}\n\nfunction errorStatus(error: unknown): number | undefined {\n if (error instanceof ClientError) return error.status ?? 400;\n if (isRecord(error) && typeof error.statusCode === 'number') return error.statusCode;\n return undefined;\n}\n\nfunction authFailureReason(\n error: unknown,\n status: number | undefined,\n request: FastifyRequest,\n): 'missing' | 'invalid' | 'dependency-unavailable' | undefined {\n if (isRecord(error) && error.code === 'auth-dependency-unavailable') {\n return 'dependency-unavailable';\n }\n if (status !== 401) return undefined;\n return request.headers.authorization === undefined ? 'missing' : 'invalid';\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n"],"names":["StreamableHTTPServerTransport","AUTH_AGENT_ACCESS","requireAgentAccessContext","reportError","ClientError","createAllowedOriginMatcher","errorHandler","defaultErrorHandler","defineRoute","logger","AGENT_ACCESS_MCP_PATH","AGENT_ACCESS_PROTECTED_RESOURCE_METADATA_PATH","createAgentAccessRateLimiter","createAgentAccessFixtureTool","recordAgentAccessAuthFailure","createAgentAccessToolCallRecorder","buildAgentAccessMcpServer","TRAILING_SLASHES_RE","createAgentAccessRoutes","options","tools","rateLimiter","recordCall","originMatcher","isOriginAllowed","createAgentAccessErrorHandler","resourceMetadataUrl","prefix","routes","method","path","description","preAuth","createOriginGuard","handler","methodNotAllowed","auth","request","reply","context","server","transport","connected","cleanedUp","cleanup","close","catch","error","err","boundary","operation","raw","once","connect","hijack","handleRequest","body","_request","code","header","send","headers","origin","challenge","escapeHeaderValue","status","errorStatus","reason","authFailureReason","undefined","protectedResourceMetadataUrl","apiPublicUrl","replace","value","character","isRecord","statusCode","authorization"],"mappings":"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"}