使用插件扩展 MCP 服务端(MCP server)
页面摘要: Strapi 插件可以通过
strapi.ai.mcp服务注册额外的 MCP 工具。注册必须在 MCP 服务端空闲时(即在插件的register()生命周期阶段)、服务端启动之前进行。
Strapi 包含一个内置的 模型上下文协议(MCP)服务端,它向 AI 客户端暴露内容管理工具。除了从你的 schema 生成的工具之外,插件还可以注册自己的 MCP 能力,以便 AI 客户端能够触发插件特定的操作。插件可以通过 strapi.ai.mcp 服务注册 3 种能力类型:tools(工具)、resources(资源)和 prompts(提示)。
注册必须在 MCP 服务端空闲时、服务端启动之前进行。在 Strapi 的加载生命周期中,请在插件的 register() 阶段注册工具。
注册自定义工具
使用 strapi.ai.mcp.registerTool() 向 AI 客户端暴露一个自定义工具:
JavaScript
const { z } = require('@strapi/utils');
module.exports = {
register({ strapi }) {
strapi.ai.mcp.registerTool({
name: 'my_custom_tool',
title: 'My Custom Tool',
description: 'A short description shown to the AI client.',
auth: {
// The session gate passes when the token satisfies ANY policy in the array.
policies: [{ action: 'plugin::my-plugin.my-action' }],
},
// resolveInputSchema and resolveOutputSchema are called per request,
// so they can narrow schemas based on the token's permissions.
resolveInputSchema: (context) =>
z.object({
message: z.string().describe('The message to echo.'),
}),
resolveOutputSchema: (context) =>
z.object({
result: z.string(),
}),
createHandler: (strapi, context) => async ({ args }) => ({
content: [{ type: 'text', text: args.message }],
structuredContent: { result: args.message },
}),
});
},
};
TypeScript
import { z } from '@strapi/utils';
export default {
register({ strapi }) {
strapi.ai.mcp.registerTool({
name: 'my_custom_tool',
title: 'My Custom Tool',
description: 'A short description shown to the AI client.',
auth: {
// The session gate passes when the token satisfies ANY policy in the array.
policies: [{ action: 'plugin::my-plugin.my-action' }],
},
// resolveInputSchema and resolveOutputSchema are called per request,
// so they can narrow schemas based on the token's permissions.
resolveInputSchema: (context) =>
z.object({
message: z.string().describe('The message to echo.'),
}),
resolveOutputSchema: (context) =>
z.object({
result: z.string(),
}),
createHandler: (strapi, context) => async ({ args }) => ({
content: [{ type: 'text', text: args.message }],
structuredContent: { result: args.message },
}),
});
},
};
工具定义选项
| Option | Type | Required | Description |
|---|---|---|---|
name | String | Yes | 唯一的工具名称。必须在所有已注册的 MCP 工具中保持唯一。 |
title | String | Yes | 向 AI 客户端显示的可读标题。 |
description | String | Yes | 关于该工具功能的简短描述。 |
auth | Object | Yes(或 devModeOnly) | 身份验证要求。当令牌满足 policies 数组中的任意策略时,会话门(session gate)通过。每个策略为 { action, subject? }。 |
devModeOnly | Boolean | Yes(或 auth) | 设为 true 以将该工具限制为仅开发模式(等同于内置的 log 工具)。 |
resolveInputSchema | Function | No | 返回该工具输入参数的 Zod schema。每次请求都会调用,以便可以动态应用 RBAC 约束。对于没有输入的工具有省略。 |
resolveOutputSchema | Function | Yes | 返回该工具结构化输出的 Zod schema。每次请求都会调用。 |
createHandler | Function | Yes | 返回异步工具处理函数的工厂函数。接收 Strapi 实例和每次请求的上下文(包括 userAbility 和 user)。 |
resolveInputSchema 和 resolveOutputSchema 每次传入的 MCP 请求都会调用一次,因此你可以根据令牌的权限(通过 context.userAbility)动态收窄 schema。
使用构建器辅助函数定义能力
构建器辅助函数是对 TypeScript 用户的可选便利。注册能力的标准、推荐方式是将能力定义内联传递给 registerTool(),如 上一节 所示。你永远不需要构建器辅助函数来注册工具、资源或提示:除非你特别需要它所提供的额外 TypeScript 类型推断,否则可以跳过本节。
将工具定义内联传递给 registerTool() 是标准做法,在多数情况下都很好用。对于将能力定义保存在各自模块中的较大插件,Strapi 选择性地导出一组构建器辅助函数,用于在与 register 调用分离声明定义时改善 TypeScript 推断。
这些辅助函数通过 @strapi/strapi 上的 ai.mcp 命名空间导出:ai.mcp.defineTool、ai.mcp.defineResource 和 ai.mcp.definePrompt。每个函数在运行时都会原样返回其定义:它是一个纯粹的类型推断辅助函数,而不是注册能力的另一种方式。它们推断能力的 name、schema 和处理函数类型,并收窄访问变体(devModeOnly 或 auth),使结果可直接赋值给匹配的 register 方法。这类似于内容管理器 API 所用的 factories 辅助函数。
无论你是否使用构建器,注册方式仍然相同:在插件的 register() 阶段将定义传递给 registerTool()(或 registerResource() / registerPrompt())。每个定义要么取 devModeOnly: true,要么取一个 auth 策略集,绝不能两者兼有。
定义工具
以下示例为了简洁使用 devModeOnly。一个 auth 策略集,如上方 工具定义选项 所示,工作方式相同:
import { ai } from '@strapi/strapi';
import { z } from '@strapi/utils';
export const greet = ai.mcp.defineTool({
name: 'greet',
title: 'Greet',
description: 'Greets a user by name',
devModeOnly: true,
resolveInputSchema: () => z.object({ name: z.string() }),
resolveOutputSchema: () => z.object({ message: z.string() }),
createHandler: (strapi) => async ({ args }) => {
const message = `Hello, ${args.name}!`;
return { content: [{ type: 'text', text: message }], structuredContent: { message } };
},
});
从插件的服务端入口文件注册该工具:
import { greet } from './mcp/greet';
export default {
register({ strapi }) {
strapi.ai.mcp.registerTool(greet);
},
};
定义资源
资源通过 URI 向 AI 客户端暴露只读数据。使用 ai.mcp.defineResource 定义它,然后使用 strapi.ai.mcp.registerResource() 注册它:
import { ai } from '@strapi/strapi';
export const appInfo = ai.mcp.defineResource({
name: 'app-info',
uri: 'strapi://app/info',
metadata: { description: 'Metadata about the app', mimeType: 'application/json' },
devModeOnly: true,
createHandler: (strapi) => async (uri) => ({
contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify({ ok: true }) }],
}),
});
定义提示
提示向 AI 客户端暴露一个可复用的提示模板。使用 ai.mcp.definePrompt 定义它,然后使用 strapi.ai.mcp.registerPrompt() 注册它:
import { ai } from '@strapi/strapi';
export const appContext = ai.mcp.definePrompt({
name: 'app-context',
title: 'App Context',
description: 'Provides context about the app',
devModeOnly: true,
createHandler: (strapi) => async () => ({
messages: [{ role: 'user', content: { type: 'text', text: 'You are connected to Strapi.' } }],
}),
});
这些构建器是恒等函数:它们在运行时不会改变定义。定义一个能力并不会注册它。请在 register() 期间、且 MCP 服务端仍处于空闲状态时,将结果传递给 strapi.ai.mcp.registerTool()、registerResource() 或 registerPrompt()。