mcpField
M
mcp-server-development

mcp-server-development

github.com/akiojin开发1037 次安装2 天前更新
代码仓库

关于 mcp-server-development

mcp-server-development 是一项指导如何构建可靠 MCP 服务的技能,重点介绍 unity-mcp-server 的架构与模式。它适用于 MCP 服务开发者,涵盖工具处理器、输入验证、传输、错误处理和测试。

主要功能

  1. 01按工具拆分处理器
  2. 02使用 Zod 验证输入
  3. 03处理 Content-Length 与 NDJSON 输入
  4. 04遵循 JSON-RPC 错误格式
  5. 05测试处理器与传输流程
  6. 06说明 Unity 命令与工作区路径

适用场景

  1. 01设计 MCP 工具处理器结构
  2. 02实现标准输入输出传输
  3. 03排查协议与错误处理问题
  4. 04为 Unity 集成编写测试

MCP 相关

该技能提供 MCP 服务构建指导,涵盖工具定义、JSON-RPC 传输及协议合规。其架构示例侧重 unity-mcp-server。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/akiojin/llmlb --skill mcp-server-development

安装后在 Claude Code 中输入/mcp-server-development确认已加载。

详细介绍

此技能提供构建稳健 MCP 服务的全面指导,重点介绍 unity-mcp-server 的架构和模式。

核心理念

MCP 服务连接 AI 助手与外部系统。它们必须:

  • 可靠:妥善处理错误,绝不意外崩溃
  • 易于发现:工具应具有清晰且自描述的架构
  • 高性能:尽量降低延迟,尤其是对 stdio 传输而言
  • 符合协议规范:严格遵循 JSON-RPC 2.0 和 MCP 规范

架构模式

基于处理器的设计

始终为每个工具使用一个处理器类。每个处理器封装:

  • 输入验证(Zod 架构)
  • 业务逻辑执行
  • 错误处理和响应格式化
// Pattern: One handler per tool
export class SystemPingToolHandler extends BaseToolHandler {
  constructor(unityConnection) {
    super({
      name: 'system_ping',
      description: 'Check Unity Editor connectivity',
      inputSchema: { type: 'object', properties: {} }
    });
    this.unityConnection = unityConnection;
  }

  async execute(params) {
    const result = await this.unityConnection.send({ command: 'ping' });
    return { status: 'ok', ...result };
  }
}
BaseToolHandler 契约

所有处理器都必须:

  1. 使用工具元数据调用 super()
  2. 实现 execute(params) 方法
  3. 返回普通对象(由框架处理 JSON-RPC 封装)
  4. 抛出包含描述性消息的错误
使用 Zod 进行输入验证

始终在处理前验证输入:

import { z } from 'zod';

const inputSchema = z.object({
  name: z.string().min(1).describe('GameObject name'),
  primitiveType: z.enum(['cube', 'sphere', 'cylinder']).optional()
});

validate(input) {
  return inputSchema.parse(input);
}

传输层

Content-Length 分帧(标准)

MCP 对 stdio 使用 LSP 风格的 Content-Length 分帧:

Content-Length: 123\r\n
\r\n
{"jsonrpc":"2.0","id":1,"method":"tools/call"...}

始终对输出使用 Content-Length。绝不在同一会话中混用分帧格式。

混合输入(兼容性)

为兼容客户端,同时接受 Content-Length 和 NDJSON 输入:

  • Claude Desktop:Content-Length
  • 部分 CLI 工具:NDJSON(以换行符分隔)

错误处理

结构化错误

使用规范中的 MCP 错误代码:

const McpError = {
  ParseError: -32700,
  InvalidRequest: -32600,
  MethodNotFound: -32601,
  InvalidParams: -32602,
  InternalError: -32603
};

throw new Error(JSON.stringify({
  code: McpError.InvalidParams,
  message: 'primitiveType must be one of: cube, sphere, cylinder'
}));
错误响应格式
{
  jsonrpc: '2.0',
  id: requestId,
  error: {
    code: -32602,
    message: 'Invalid params',
    data: { field: 'name', issue: 'required' }
  }
}

Unity 特定模式

命令协议

Unity 通信使用简单的请求/响应模式:

const result = await this.unityConnection.send({
  command: 'gameobject_create',
  params: { name: 'Cube', primitiveType: 'cube' }
});
工作区根路径解析

对于需要文件路径的命令,始终包含 workspaceRoot:

execute(params) {
  return this.unityConnection.send({
    command: 'screenshot_capture',
    workspaceRoot: config.workspaceRoot,
    ...params
  });
}

测试模式

处理器的 TDD
  1. 先编写契约测试:定义预期的输入/输出架构
  2. 模拟 Unity 连接:隔离处理器逻辑
  3. 测试错误路径:无效输入、连接失败
  4. 最后测试正常路径:验证契约后再进行
describe('CreateGameObjectHandler', () => {
  it('validates primitiveType enum', async () => {
    const handler = new CreateGameObjectHandler(mockConnection);
    await assert.rejects(
      () => handler.handle({ name: 'Cube', primitiveType: 'invalid' }),
      /primitiveType must be one of/
    );
  });
});
集成测试

测试完整的请求/响应周期,包括 JSON-RPC 分帧:

const stdin = new PassThrough();
const stdout = new PassThrough();
const transport = new HybridStdioServerTransport(stdin, stdout);

stdin.write('Content-Length: 50\r\n\r\n{"jsonrpc":"2.0","id":1,"method":"ping"}');
// Assert stdout contains Content-Length response

常见错误

混用分帧格式:

  • 绝不:收到 Content-Length 输入后输出 NDJSON
  • 始终:无论输入格式如何,都输出 Content-Length

吞掉错误:

  • 绝不:catch (e) { return null; }
  • 始终:传播错误并附带上下文

缺少验证:

  • 绝不:信任来自客户端的原始输入
  • 始终:在处理前使用 Zod 验证

阻塞 stdio:

  • 绝不:在传输过程中执行同步操作
  • 始终:全程使用 async/await

处理器注册

在中央索引中注册所有处理器:

// src/handlers/index.js
export function createHandlers(unityConnection) {
  return [
    new SystemPingToolHandler(unityConnection),
    new CreateGameObjectHandler(unityConnection),
    new ScreenshotHandler(unityConnection),
    // ...
  ];
}

请记住

  • 始终使用 Content-Length:输出分帧必须保持一致
  • 验证所有内容:绝不信任客户端输入
  • 一个处理器对应一个工具:保持处理器职责专一
  • 测试错误路径:大多数错误都隐藏在错误处理中
  • 符合协议规范:严格遵循 JSON-RPC 2.0

评论

支持 Markdown