返回 AiToEarn
mcp-resources.handler.ts
根目录 / project / aitoearn-backend / libs / nest-mcp / src / services / handlers / mcp-resources.handler.ts
1 import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2 import {
3 ErrorCode,
4 ListResourcesRequestSchema,
5 ListResourceTemplatesRequestSchema,
6 McpError,
7 ReadResourceRequestSchema,
8 } from '@modelcontextprotocol/sdk/types.js'
9 import { Inject, Injectable, InjectionToken, Scope } from '@nestjs/common'
10 import { ContextIdFactory, ModuleRef } from '@nestjs/core'
11 import { getErrorMessage } from '@yikart/common'
12 import { Context } from '../../interfaces'
13 import { HttpRequest } from '../../interfaces/http-adapter.interface'
14 import { McpRegistryService } from '../mcp-registry.service'
15 import { McpHandlerBase } from './mcp-handler.base'
16
17 @Injectable({ scope: Scope.REQUEST })
18 export class McpResourcesHandler extends McpHandlerBase {
19 constructor(
20 moduleRef: ModuleRef,
21 registry: McpRegistryService,
22 @Inject('MCP_MODULE_ID') private readonly mcpModuleId: string,
23 ) {
24 super(moduleRef, registry, McpResourcesHandler.name)
25 }
26
27 registerHandlers(mcpServer: McpServer, httpRequest: HttpRequest) {
28 const resources = this.registry.getResources(this.mcpModuleId)
29 const resourceTemplates = this.registry.getResourceTemplates(
30 this.mcpModuleId,
31 )
32 if (resources.length === 0 && resourceTemplates.length === 0) {
33 this.logger.debug(
34 'No resources or resource templates registered, skipping resource handlers',
35 )
36 return
37 }
38
39 mcpServer.server.setRequestHandler(ListResourcesRequestSchema, () => {
40 this.logger.debug('ListResourcesRequestSchema is being called')
41 return {
42 resources: this.registry
43 .getResources(this.mcpModuleId)
44 .map(resources => resources.metadata),
45 }
46 })
47
48 mcpServer.server.setRequestHandler(
49 ListResourceTemplatesRequestSchema,
50 () => {
51 this.logger.debug('ListResourceTemplatesRequestSchema is being called')
52 return {
53 resourceTemplates: this.registry
54 .getResourceTemplates(this.mcpModuleId)
55 .map(resourceTemplate => resourceTemplate.metadata),
56 }
57 },
58 )
59
60 mcpServer.server.setRequestHandler(
61 ReadResourceRequestSchema,
62 async (request) => {
63 this.logger.debug('ReadResourceRequestSchema is being called')
64
65 const uri = request.params.uri
66 const resourceInfo = this.registry.findResourceByUri(
67 this.mcpModuleId,
68 uri,
69 )
70 const resourceTemplateInfo = this.registry.findResourceTemplateByUri(
71 this.mcpModuleId,
72 uri,
73 )
74
75 try {
76 let providerClass: InjectionToken
77 let params: Record<string, unknown> = {}
78 let methodName: string
79 if (resourceTemplateInfo) {
80 providerClass = resourceTemplateInfo.resourceTemplate.providerClass
81 params = {
82 ...resourceTemplateInfo.params,
83 ...request.params,
84 }
85 methodName = resourceTemplateInfo.resourceTemplate.methodName
86 }
87 else if (resourceInfo) {
88 providerClass = resourceInfo.resource.providerClass
89
90 params = {
91 ...resourceInfo.params,
92 ...request.params,
93 }
94 methodName = resourceInfo.resource.methodName
95 }
96 else {
97 throw new McpError(
98 ErrorCode.MethodNotFound,
99 `Unknown resource: ${uri}`,
100 )
101 }
102 return await this.handleRequest(
103 httpRequest,
104 providerClass,
105 uri,
106 this.createContext(mcpServer, request),
107 params,
108 methodName,
109 )
110 }
111 catch (error) {
112 this.logger.error(error)
113 const errorMessage = getErrorMessage(error)
114 return {
115 contents: [{ uri, mimeType: 'text/plain', text: errorMessage }],
116 isError: true,
117 }
118 }
119 },
120 )
121 }
122
123 private async handleRequest(
124 httpRequest: HttpRequest,
125 providerClass: InjectionToken,
126 uri: string,
127 context: Context,
128 requestParams: Record<string, unknown>,
129 methodName: string,
130 ) {
131 const contextId = ContextIdFactory.getByRequest(httpRequest)
132 this.moduleRef.registerRequestByContextId(httpRequest, contextId)
133
134 const resourceInstance = await this.moduleRef.resolve(
135 providerClass,
136 contextId,
137 { strict: false },
138 )
139
140 if (!resourceInstance) {
141 throw new McpError(
142 ErrorCode.MethodNotFound,
143 `Unknown resource template: ${uri}`,
144 )
145 }
146 const result = await resourceInstance[methodName](
147 requestParams,
148 context,
149 httpRequest,
150 )
151
152 this.logger.debug(result, 'ReadResourceRequestSchema result')
153
154 return result
155 }
156 }
157
157 lines TYPESCRIPT