diff --git a/core/controller-decorator/src/model/MCPToolMeta.ts b/core/controller-decorator/src/model/MCPToolMeta.ts index c821dcf77..7e9cdeab6 100644 --- a/core/controller-decorator/src/model/MCPToolMeta.ts +++ b/core/controller-decorator/src/model/MCPToolMeta.ts @@ -1,4 +1,4 @@ -import type { MethodMeta, MiddlewareFunc } from '@eggjs/tegg-types'; +import type { MCPToolParams, MethodMeta, MiddlewareFunc } from '@eggjs/tegg-types'; import { ToolArgsSchemaDetail } from '../../src/util/MCPInfoUtil'; @@ -8,6 +8,7 @@ export class MCPToolMeta implements MethodMeta { readonly aclCode?: string; readonly mcpName?: string; readonly description?: string; + readonly meta?: MCPToolParams['meta']; readonly detail?: ToolArgsSchemaDetail; readonly middlewares: readonly MiddlewareFunc[]; readonly extra?: number; @@ -20,6 +21,7 @@ export class MCPToolMeta implements MethodMeta { needAcl?: boolean; aclCode?: string, description?: string; + meta?: MCPToolParams['meta']; mcpName?: string; detail?: ToolArgsSchemaDetail; extra?: number; @@ -27,6 +29,7 @@ export class MCPToolMeta implements MethodMeta { this.name = opt.name; this.needAcl = !!opt.needAcl; this.description = opt.description; + this.meta = opt.meta; this.mcpName = opt.mcpName; this.middlewares = opt.middlewares; this.aclCode = opt.aclCode; diff --git a/core/controller-decorator/test/MCPMeta.test.ts b/core/controller-decorator/test/MCPMeta.test.ts index 8fb7c2b08..e84b2658b 100644 --- a/core/controller-decorator/test/MCPMeta.test.ts +++ b/core/controller-decorator/test/MCPMeta.test.ts @@ -17,6 +17,12 @@ describe('test/MCPMeta.test.ts', () => { assert(fooControllerMetaData.className === 'MCPFooController'); assert(fooControllerMetaData.prompts[0].name === 'foo'); assert(fooControllerMetaData.tools[0].name === 'bar'); + assert.deepStrictEqual(fooControllerMetaData.tools[0].meta, { + ui: { + resourceUri: 'ui://test/tool', + visibility: [ 'model', 'app' ], + }, + }); assert(fooControllerMetaData.resources[0].name === 'car'); assert(fooControllerMetaData.resources[0].template instanceof ResourceTemplate); assert.strictEqual(fooControllerMetaData.tools[0].detail?.argsSchema as unknown, ToolType); diff --git a/core/controller-decorator/test/fixtures/MCPController.ts b/core/controller-decorator/test/fixtures/MCPController.ts index d81346fb8..92fbb39dc 100644 --- a/core/controller-decorator/test/fixtures/MCPController.ts +++ b/core/controller-decorator/test/fixtures/MCPController.ts @@ -36,7 +36,14 @@ export class MCPFooController { }; } - @MCPTool() + @MCPTool({ + meta: { + ui: { + resourceUri: 'ui://test/tool', + visibility: [ 'model', 'app' ], + }, + }, + }) async bar(@ToolArgsSchema(ToolType as any) args: ToolArgs, @Context() ctx: object): Promise { void ctx; return { diff --git a/core/types/controller-decorator/MCPToolParams.ts b/core/types/controller-decorator/MCPToolParams.ts index 6a42af339..ec51c9c2a 100644 --- a/core/types/controller-decorator/MCPToolParams.ts +++ b/core/types/controller-decorator/MCPToolParams.ts @@ -7,9 +7,20 @@ export type ToolExtra = Parameters['4']>['1']; export type MCPToolResponse = CallToolResult; +export type MCPToolVisibility = 'model' | 'app'; + +export interface MCPToolUIMeta { + resourceUri?: string; + visibility?: MCPToolVisibility[]; +} + +export type MCPToolRegistrationMeta = { + ui: MCPToolUIMeta; +}; + export interface MCPToolParams { name?: string; description?: string; timeout?: number; + meta?: MCPToolRegistrationMeta; } - diff --git a/plugin/controller/lib/impl/mcp/MCPServerHelper.ts b/plugin/controller/lib/impl/mcp/MCPServerHelper.ts index eaa3b7b77..5db0d7256 100644 --- a/plugin/controller/lib/impl/mcp/MCPServerHelper.ts +++ b/plugin/controller/lib/impl/mcp/MCPServerHelper.ts @@ -137,6 +137,7 @@ export class MCPServerHelper { this.server.registerTool(name, { description, inputSchema: schema, + _meta: toolMeta.meta, // TODO: outputSchema }, handler); } diff --git a/plugin/controller/test/mcp/helper.test.ts b/plugin/controller/test/mcp/helper.test.ts index c85deb5ce..252172680 100644 --- a/plugin/controller/test/mcp/helper.test.ts +++ b/plugin/controller/test/mcp/helper.test.ts @@ -45,6 +45,12 @@ describe('plugin/controller/test/mcp/mcp.test.ts', () => { name: 'testTool', needAcl: false, middlewares: [], + meta: { + ui: { + resourceUri: 'ui://test/tool', + visibility: [ 'model', 'app' ], + }, + }, contextParamIndex: 1, detail: { argsSchema: ToolType, @@ -128,10 +134,16 @@ describe('plugin/controller/test/mcp/mcp.test.ts', () => { ]); const tools = await client.listTools(); - assert.deepEqual(tools.tools.map(tool => ({ name: tool.name, description: tool.description })), [ + assert.deepEqual(tools.tools.map(tool => ({ name: tool.name, description: tool.description, _meta: tool._meta })), [ { description: undefined, name: 'testTool', + _meta: { + ui: { + resourceUri: 'ui://test/tool', + visibility: [ 'model', 'app' ], + }, + }, }, ]); diff --git a/standalone/service-worker/src/mcp/MCPServerHelper.ts b/standalone/service-worker/src/mcp/MCPServerHelper.ts index 1c795a9f3..30300dfad 100644 --- a/standalone/service-worker/src/mcp/MCPServerHelper.ts +++ b/standalone/service-worker/src/mcp/MCPServerHelper.ts @@ -125,6 +125,7 @@ export class MCPServerHelper { this.server.registerTool(name, { description, inputSchema: schema, + _meta: toolMeta.meta, }, handler); } diff --git a/standalone/service-worker/test/fixtures/mcp/MCPTestController.ts b/standalone/service-worker/test/fixtures/mcp/MCPTestController.ts index e254b7aff..03545d998 100644 --- a/standalone/service-worker/test/fixtures/mcp/MCPTestController.ts +++ b/standalone/service-worker/test/fixtures/mcp/MCPTestController.ts @@ -15,7 +15,15 @@ const AddArgs = { @Middleware(McpTestAdvice) @MCPController({ name: 'test-server', version: '1.0.0' }) export class MCPTestController { - @MCPTool({ description: 'Echo the input message' }) + @MCPTool({ + description: 'Echo the input message', + meta: { + ui: { + resourceUri: 'ui://test/echo', + visibility: [ 'model' ], + }, + }, + }) async echo(@ToolArgsSchema(EchoArgs) args: ToolArgs): Promise { return { content: [{ type: 'text', text: args.message }], diff --git a/standalone/service-worker/test/mcp/mcp.test.ts b/standalone/service-worker/test/mcp/mcp.test.ts index 036010196..5bf6e93cd 100644 --- a/standalone/service-worker/test/mcp/mcp.test.ts +++ b/standalone/service-worker/test/mcp/mcp.test.ts @@ -47,6 +47,12 @@ describe('standalone/service-worker/test/mcp/mcp.test.ts', () => { const echoTool = result.tools.find(t => t.name === 'echo'); assert.strictEqual(echoTool?.description, 'Echo the input message'); + assert.deepStrictEqual(echoTool?._meta, { + ui: { + resourceUri: 'ui://test/echo', + visibility: [ 'model' ], + }, + }); const addTool = result.tools.find(t => t.name === 'add'); assert.strictEqual(addTool?.description, 'Add two numbers');