商业工具 核心MCP工具
这个软件包以MCP(模式上下文协议)工具的形式暴露出商业工具资源,可以跨多个MCP项目使用.
这些文件描述了详细的设计决定、将暴露工具纳入现有项目的步骤以及取决于项目设计选择的各种使用实例.
设计文档
-- -- 待补 -- --
设计决定
1. 分层建筑
该套件采用分层结构模式,明确区分了所关切的问题:
- 基层( E)
BaseResourceHandler(中文(简体) ):通用,API-不可知处理器,提供工具路由,验证,并命名常规. - 平台层( E)
CommercetoolsResourceHandler(中文(简体) ):利用工厂的客户端来建造 平台 API 根( 通过createApiBuilderFromCtpClient从@commercetools/platform-sdk) (中文(简体) ). 用于客户,项目,订单等. - 取出图层( E)
CheckoutResourceHandler(中文(简体) ): 使用同一工厂客户端来构建 检查出 API 根( 通过createApiBuilderFromCtpClient从@commercetools/checkout-sdk带有取出基准 URL ). 用于支付意图,交易. - 资源层:特定资源的具体实施(CustomersHandler,PayingIntentsHandler等).
理由: 此分离允许基接器被再用到任何API平台(Stripe, Shopify等). 商务工具层被分拆,这样资源就可以根据需要使用平台API或Checkout API;两者都使用来自工厂的同为低等客户端.
2. 搬运工登记:装饰或直接证明
工具处理器类可以两种方式中的一种方式注册:
- 基于装饰的: 应用
@ToolHandler()以扩展一个基础处理器的类(例如:.CustomerHandler) (中文(简体) ). 当该类被定义时,它会在全球登记册上注册. 获取所有注册的处理器getDecoratorRegisteredHandlers()把它们交给你的MCP服务器. - 直接即时: 自己创建处理器实例(例如:.
new CustomerHandler(), (中文(简体) ),new PaymentIntentsHandler(factory)),并将这一系列实例传递到您的 MCP 服务器。 不涉及装饰或登记.
可以同时混合:为一些处理器使用装饰器,并明确即时处理其他的,然后在构建服务器时将数组组合.
理由: 装饰器允许零配置的发现; 直接即时使构造器参数完全控制( 如自定义) IApiClientFactory),并避免全球状态.
3. 客户端和API根分离
低层 客户端 (从) @commercetools/ts-client由工厂创建并归还; API 根数 (平面或取出)由处理器内部所建. 工厂曝光 getClient(configuration, authToken); 处理器调用然后使用 createApiBuilderFromCtpClient 从合适的SDK(平面-sdk或取出-sdk)来构建它们所需要的根.
理由: 一个客户端实例既可以背靠平台,也可以背靠取出API;处理者拥有他们使用的API.
4. 依赖性注射
API客户工厂可注射,允许:
- 对模拟工厂进行简单测试
- 自定义 API 客户端执行
- 不同的认证战略
理由:提高可检验性和灵活性,同时保持清洁的API.
5. 基于Schema的验证
所有工具参数都使用处理器元数据中定义的Zod schemas验证.
理由: Type-safed 参数验证,带有清晰的错误消息和自动TypeScript类型推论.
6. 基于职能和班级的支助
该架构支持两个方面:
- 基于类:有全能处理器(如.
CustomerHandler, (中文(简体) ),PaymentIntentsHandler) (中文(简体) ). 通过 装饰器 (单位:千美元)@ToolHandler()+ 键getDecoratorRegisteredHandlers())或通过 直接即时处理 (例如:.new CustomerHandler())并将实例传递给您的服务器. - 基于函数:用于快速工具创建的简单函数包(例如.
createCustomerHandler()返回处理器实例.
理由:为不同的使用案例提供灵活性 - 复杂工具受益于类,简单工具可以使用功能;类可以通过装饰者注册或直接即时注册.
软件包组件
packages/core/
├── src/
│ ├── handlers/
│ │ ├── base.handler.ts # Generic base handler (API-agnostic)
│ │ ├── commercetools.handler.ts # Platform API handler (builds root from client)
│ │ └── checkout.handler.ts # Checkout API handler (payment-intents, transactions)
│ ├── decorators/
│ │ └── tool.decorator.ts # @ToolHandler() decorator
│ ├── resources/
│ │ ├── customer/ # Customer resource handler
│ │ ├── project/ # Project resource handler
│ │ ├── payment-intents/ # Checkout API resource
│ │ ├── transactions/ # Checkout API resource
│ │ └── ... # Other resources
│ └── shared/
│ ├── types/ # TypeScript interfaces (e.g. IApiClientFactory)
│ ├── client/
│ │ └── client.base.ts # CommercetoolsAPIClient + ApiClientFactory
│ └── errors/ # Error handling
└── index.ts # Public API exports
构成部分关系
┌────────────────────────────────────────────────────────┐
│ ToolExecutionContext │
│ (authToken, configuration, toolName, parameters) │
└────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ BaseResourceHandler<TConfig> │
│ • Tool routing & validation │
│ • Schema validation │
│ • Tool name generation │
│ • Abstract CRUD methods │
└────────────────────┬────────────────────────────────────┘
│
│ extends
┌───────────┴───────────┐
▼ ▼
┌─────────────────────┐ ┌─────────────────────────────┐
│ CommercetoolsResource│ │ CheckoutResourceHandler │
│ Handler │ │ • getClient() from factory │
│ • getClient() from │ │ • getApiRoot() = checkout- │
│ factory │ │ sdk createApiBuilder… │
│ • getApiRoot() = │ │ • payment-intents, │
│ platform-sdk │ │ transactions │
│ createApiBuilder… │ └──────────────┬──────────────┘
│ • customers, │ │
│ project, etc. │ │ extends
└──────────┬───────────┘ │
│ extends │
▼ ▼
┌─────────────────────┐ ┌─────────────────────────────┐
│ CustomerHandler / │ │ PaymentIntentsHandler / │
│ ProjectHandler /… │ │ TransactionsHandler │
└──────────┬───────────┘ └──────────────┬──────────────┘
│ │
└─────────────┬───────────────┘
│ registered via
▼
┌─────────────────────────────┐
│ Decorator Registry │
│ getDecoratorRegistered…() │
└──────────────┬──────────────┘
│ used by
▼
┌─────────────────────────────┐
│ MCP Server │
└─────────────────────────────┘
用法实例
1. 使用基控工具的自定义工具(基于类)
为任何 API 平台创建自定义工具 :
import { z } from 'zod';
import {
BaseResourceHandler,
type ResourceMetadata,
type ToolExecutionContext,
} from '@commercetools/tools-core';
// Define your custom configuration type
interface CustomApiConfig {
apiKey: string;
baseUrl: string;
}
// Create a custom handler
class ProductHandler extends BaseResourceHandler<CustomApiConfig> {
protected readonly metadata: ResourceMetadata = {
namespace: 'product',
description: 'Manage products in custom API',
toolNamePrefix: 'mcp_custom_',
schemas: {
read: z.object({
id: z.string().describe('Product ID'),
}),
create: z.object({
name: z.string(),
price: z.number(),
}),
update: z.object({
id: z.string(),
name: z.string().optional(),
price: z.number().optional(),
}),
delete: z.object({
id: z.string(),
}),
},
};
async read(context: ToolExecutionContext<CustomApiConfig>): Promise<unknown> {
const { id } = context.parameters as { id: string };
const { apiKey, baseUrl } = context.configuration;
// Make API call using your custom API client
const response = await fetch(`${baseUrl}/products/${id}`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
return response.json();
}
async create(
context: ToolExecutionContext<CustomApiConfig>
): Promise<unknown> {
const params = context.parameters as { name: string; price: number };
const { apiKey, baseUrl } = context.configuration;
const response = await fetch(`${baseUrl}/products`, {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(params),
});
return response.json();
}
async update(
context: ToolExecutionContext<CustomApiConfig>
): Promise<unknown> {
// Implementation similar to create
return {};
}
async delete(
context: ToolExecutionContext<CustomApiConfig>
): Promise<unknown> {
// Implementation
return {};
}
// other custom functions can go here
}
// Use in MCP server
import { createMCPServer } from './mcp.server';
const server = createMCPServer(
'auth-token',
{ apiKey: 'key', baseUrl: 'https://api.example.com' },
[new ProductHandler()]
);
2. 使用基准处理器的自定义工具(基于功能)
对于更简单的工具,使用基于函数的方法:
import { z } from 'zod';
import {
BaseResourceHandler,
type ToolExecutionContext,
type ResourceMetadata,
} from '@commercetools/tools-core';
interface CustomConfig {
apiKey: string;
}
// Create handler instance with function-based CRUD operations
function createProductHandler(): BaseResourceHandler<CustomConfig> {
return new (class extends BaseResourceHandler<CustomConfig> {
protected readonly metadata: ResourceMetadata = {
namespace: 'product',
description: 'Manage products',
toolNamePrefix: 'mcp_custom_',
schemas: {
read: z.object({ id: z.string() }),
create: z.object({ name: z.string(), price: z.number() }),
update: z.object({ id: z.string(), name: z.string().optional() }),
delete: z.object({ id: z.string() }),
},
};
async read(context: ToolExecutionContext<CustomConfig>) {
const { id } = context.parameters as { id: string };
// Your API call logic
return { id, name: 'Product' };
}
async create(context: ToolExecutionContext<CustomConfig>) {
return { created: true };
}
async update(context: ToolExecutionContext<CustomConfig>) {
return { updated: true };
}
async delete(context: ToolExecutionContext<CustomConfig>) {
return { deleted: true };
}
})();
}
// Use in MCP server
const server = createMCPServer('auth-token', { apiKey: 'key' }, [
createProductHandler(),
]);
3. 商业工具 核心工具(以装饰品为基础)
扩展商务工具处理器并使用装饰器 :
import {
CustomerHandler as BaseCustomerHandler,
ToolHandler,
getDecoratorRegisteredHandlers,
} from '@commercetools/tools-core';
// Simply extend and decorate - automatically registered!
@ToolHandler()
export class CustomerHandler extends BaseCustomerHandler {
// Optionally override methods for custom behavior
override getToolDefinition(
operation: 'read' | 'create' | 'update' | 'delete'
) {
const base = super.getToolDefinition(operation);
return {
...base,
description: `Custom: ${base.description}`,
};
}
}
// In your MCP server setup
import { createMCPServer } from './mcp.server';
import { getDecoratorRegisteredHandlers } from '@commercetools/tools-core';
// Import handlers to trigger decorator registration
import './handlers/customer.handler';
import './handlers/project.handler';
// Get all registered handlers
const handlers = getDecoratorRegisteredHandlers();
const server = createMCPServer(authToken, configuration, handlers);
4. 核心工具(基于功能)
使用函数创建商务工具处理器 :
import {
CustomerHandler,
ProjectHandler,
BaseResourceHandler,
type Configuration,
type IApiClientFactory,
type ToolExecutionContext,
} from '@commercetools/tools-core';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
// Create handler instance with custom factory
function createCustomerHandler(
apiClientFactory?: IApiClientFactory<Configuration>
) {
return new CustomerHandler(apiClientFactory);
}
// Create MCP server with function-based handlers
export function createMCPServer(
authToken: string,
configuration: Configuration,
handlers: BaseResourceHandler<Configuration>[] = []
): McpServer {
const server = new McpServer({
name: 'commercetools-mcp-server',
version: '1.0.0',
});
// Use function-created handlers
const customerHandler = createCustomerHandler();
const projectHandler = new ProjectHandler();
const allHandlers =
handlers.length > 0 ? handlers : [customerHandler, projectHandler];
// Register each handler's tools
for (const handler of allHandlers) {
const toolDefinitions = handler.getAllToolDefinitions();
for (const toolDef of toolDefinitions) {
server.registerTool(
toolDef.name,
{
title: toolDef.title,
description: toolDef.description,
inputSchema: toolDef.inputSchema as any,
annotations: toolDef.annotations,
},
async (args: any): Promise<any> => {
const context: ToolExecutionContext<Configuration> = {
authToken,
configuration,
toolName: toolDef.name,
parameters: args,
};
try {
const result = await handler.execute(context);
return {
content: [
{
type: 'text' as const,
text: JSON.stringify(result),
},
],
};
} catch (e) {
console.error(e);
throw e;
}
}
);
}
}
return server;
}
// Usage
const server = createMCPServer(process.env.AUTH_TOKEN!, {
projectKey: 'my-project',
correlationId: 'my-correlation-id',
tools: ['customer', 'project'],
});
5. 完成 MCP 服务器示例(合并两种方法)
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import {
BaseResourceHandler,
CustomerHandler,
ProjectHandler,
getDecoratorRegisteredHandlers,
type ToolExecutionContext,
type Configuration,
} from '@commercetools/tools-core';
// Function to create MCP server with both decorator and manual handlers
export function createMCPServer(
authToken: string,
configuration: Configuration
): McpServer {
const server = new McpServer({
name: 'commercetools-mcp-server',
version: '1.0.0',
});
// Get decorator-registered handlers (class-based with @ToolHandler())
const decoratorHandlers = getDecoratorRegisteredHandlers();
// Create function-based handlers
const functionHandlers: BaseResourceHandler<Configuration>[] = [
new CustomerHandler(),
new ProjectHandler(),
];
// Combine all handlers
const allHandlers = [...decoratorHandlers, ...functionHandlers];
// Register tools from all handlers
for (const handler of allHandlers) {
const toolDefinitions = handler.getAllToolDefinitions();
for (const toolDef of toolDefinitions) {
server.registerTool(
toolDef.name,
{
title: toolDef.title,
description: toolDef.description,
inputSchema: toolDef.inputSchema as any,
annotations: toolDef.annotations,
},
async (args: any) => {
const context: ToolExecutionContext<Configuration> = {
authToken,
configuration,
toolName: toolDef.name,
parameters: args,
};
try {
const result = await handler.execute(context);
return {
content: [
{
type: 'text' as const,
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: 'text' as const,
text: `Error: ${
error instanceof Error ? error.message : String(error)
}`,
},
],
isError: true,
};
}
}
);
}
}
return server;
}
工具标题和 MCP 说明
每个工具定义都带有人可读性 title 和一个完整的MCP
annotations 对象(title, (中文(简体) ), readOnlyHint, (中文(简体) ), destructiveHint, (中文(简体) ),
openWorldHint与 name, (中文(简体) ), description 财务报告和审定财务报表 inputSchema,所以一个MCP
客户端知道 read_orders 从 update_orders 当决定运行时
不询问用户.
两者都是 来源于工具名 而不是每个工具存储,通过
src/utils/tool-annotations.ts。一个动词表将工具名的动词映射到
对环境的影响——读取、添加或破坏性——和标题
并随附说明。 稍后添加的工具会自动附加说明,
没有第二个位置更新.
接受两个名称形状,所以同一个表格为服务器服务,以重命名
此软件包定义的工具 : verb_resource (单位:千美元)read_orders)),这是如何
这里的工具被命名,以及 resource.verb (单位:千美元)orders.read) (中文(简体) ). 名称的任意一端
中,无论哪个段都与已知动词相匹配.
import {
deriveToolAnnotations,
deriveToolAnnotationsOrConservative,
} from '@commercetools/tools-core';
deriveToolAnnotations('read_orders');
// { title: 'Read Orders', readOnlyHint: true, destructiveHint: false, openWorldHint: false }
deriveToolAnnotations('update_orders');
// { title: 'Update Orders', readOnlyHint: false, destructiveHint: true, openWorldHint: false }
// The `resource.verb` shape resolves to the same effect.
deriveToolAnnotations('orders.read');
// { title: 'Orders Read', readOnlyHint: true, destructiveHint: false, openWorldHint: false }
两个入站点,按呼叫站点划分:
deriveToolAnnotations掷出 在一个无法识别的动词上。 用在楼上 路径——计划发射器,代码生成器——所以 CI 失败而不是发布 一个猜测的安全提示.deriveToolAnnotationsOrConservative倒计时readOnlyHint: false编号 :destructiveHint: true然后通过可选的回调报告倒计时 所以可以登录 在服务路径上使用, 其中必须有一个未知的工具 不带tools/list给整个房客.
关于分类的说明:
- 全体
update_*工具为 破坏性。删除商务工具更新动作 线性项目和覆盖字段,而不只是添加. create_*财务报告和审定财务报表replicate_carts为: 添加剂 他们带来了新的资源 并留下剩下的东西.openWorldHint实值false到处都是 每个工具都使用一个单一工具 商业工具项目,通过一个范围化的符号进行,这是一个封闭的域名 感知到MCP的特性.idempotentHint省略。 写作用乐观货币保管 版本,所以重复的呼叫被拒绝而不是被吸收; spec's 默认false是诚实的答案.writeHint实值 没有 在 MCP 谱中,并且永远不会被排放。 光谱 表示写作readOnlyHint: false。 。 。 .
如果你不同意分类, VERB_EFFECTS 内
src/utils/tool-annotations.ts 是改变它的唯一地方.
密钥设计模式
-
模板方法模式数字 :
BaseResourceHandler.execute()定义算法结构,而子类则执行特定的 CRUD 操作. -
战略模式数字 :
IApiClientFactory曝光情况getClient(configuration, authToken)并允许注入不同的客户端创建策略(例如默认)ApiClientFactory用于CommercetoolsAPIClient) (中文(简体) ). -
客户端/ API 根分离: 工厂只返回低层 客户端处理器获得该客户端并构建适当的 API 根 (格式或退出)使用
createApiBuilderFromCtpClient从相应的SDK. -
装饰图案数字 :
@ToolHandler()装饰者将注册行为添加到处理类. 处理器也可以通过直接即时(例如:.new CustomerHandler())而不使用装饰或注册. -
工厂模式数字 :
ApiClientFactory通过CommercetoolsAPIClient;处理器然后从该客户端建立平台或检查出 API 根. -
依赖性注射: 处理器接受可选
IApiClientFactory在其构建器中,允许测试性和定制客户端执行.