mcp-server-skills

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Links

链接

Folder Structure (Next.js App Router)

文件夹结构(Next.js App Router)

app/
  api/[transport]/route.ts   # One handler for all transports (e.g., /api/mcp)
  actions/mcp-actions.ts     # Server actions reusing the same logic/schemas
lib/
  dice.ts | tools.ts         # Zod schemas, tool definitions, pure logic
components/                  # UI that calls server actions for web testing
Goal: Keep
route.ts
minimal. Put logic + Zod schemas in
lib/*
so both the MCP handler and server actions share a single source of truth.
app/
  api/[transport]/route.ts   # 处理所有传输方式的统一处理器(例如:/api/mcp)
  actions/mcp-actions.ts     # 复用相同逻辑/模式的服务器操作
lib/
  dice.ts | tools.ts         # Zod模式、工具定义、纯逻辑
components/                  # 调用服务器操作进行Web测试的UI组件
目标: 保持
route.ts
尽可能简洁。将逻辑和Zod模式放在
lib/*
目录下,让MCP处理器和服务器操作共享单一数据源。

Shared Zod Schema + Tool Definition

共享Zod模式与工具定义

ts
// lib/dice.ts
import { z } from "zod";

export const diceSchema = z.number().int().min(2);

export function rollDice(sides: number) {
  const validated = diceSchema.parse(sides);
  const value = 1 + Math.floor(Math.random() * validated);
  return { type: "text" as const, text: `🎲 You rolled a ${value}!` };
}

export const rollDiceTool = {
  name: "roll_dice",
  description: "Rolls an N-sided die",
  schema: { sides: diceSchema },
} as const;
ts
// lib/dice.ts
import { z } from "zod";

export const diceSchema = z.number().int().min(2);

export function rollDice(sides: number) {
  const validated = diceSchema.parse(sides);
  const value = 1 + Math.floor(Math.random() * validated);
  return { type: "text" as const, text: `🎲 You rolled a ${value}!` };
}

export const rollDiceTool = {
  name: "roll_dice",
  description: "Rolls an N-sided die",
  schema: { sides: diceSchema },
} as const;

Reusable Server Actions (Web UI + Tests)

可复用服务器操作(Web UI与测试)

ts
// app/actions/mcp-actions.ts
"use server";
import { rollDice as rollDiceCore, rollDiceTool } from "@/lib/dice";

export async function rollDice(sides: number) {
  try {
    const result = rollDiceCore(sides);
    return { success: true, result: { content: [result] } };
  } catch {
    return {
      success: false,
      error: { code: -32602, message: "Invalid parameters: sides must be >= 2" },
    };
  }
}

export async function listTools() {
  return {
    success: true,
    result: {
      tools: [
        {
          name: rollDiceTool.name,
          description: rollDiceTool.description,
          inputSchema: {
            type: "object",
            properties: { sides: { type: "number", minimum: 2 } },
            required: ["sides"],
          },
        },
      ],
    },
  };
}
Server actions call the same logic as the MCP handler and power the web UI, keeping responses aligned.
ts
// app/actions/mcp-actions.ts
"use server";
import { rollDice as rollDiceCore, rollDiceTool } from "@/lib/dice";

export async function rollDice(sides: number) {
  try {
    const result = rollDiceCore(sides);
    return { success: true, result: { content: [result] } };
  } catch {
    return {
      success: false,
      error: { code: -32602, message: "Invalid parameters: sides must be >= 2" },
    };
  }
}

export async function listTools() {
  return {
    success: true,
    result: {
      tools: [
        {
          name: rollDiceTool.name,
          description: rollDiceTool.description,
          inputSchema: {
            type: "object",
            properties: { sides: { type: "number", minimum: 2 } },
            required: ["sides"],
          },
        },
      ],
    },
  };
}
服务器操作调用与MCP处理器相同的逻辑,为Web UI提供支持,确保响应一致。

Lightweight MCP Route

轻量级MCP路由

ts
// app/api/[transport]/route.ts
import { createMcpHandler } from "mcp-handler";
import { rollDice, rollDiceTool } from "@/lib/dice";

const handler = createMcpHandler(
  (server) => {
    server.tool(
      rollDiceTool.name,
      rollDiceTool.description,
      rollDiceTool.schema,
      async ({ sides }) => ({ content: [rollDice(sides)] }),
    );
  },
  {}, // server options
  {
    basePath: "/api",     // must match folder path
    maxDuration: 60,
    verboseLogs: true,
  },
);

export { handler as GET, handler as POST };
Pattern highlights
  • Route only wires
    createMcpHandler
    ; no business logic inline.
  • server.tool
    consumes the shared tool schema/description and calls shared logic.
  • basePath
    should align with the folder (e.g.,
    /api/[transport]
    ).
  • Works for SSE/HTTP transports; stdio can be added separately if needed.
ts
// app/api/[transport]/route.ts
import { createMcpHandler } from "mcp-handler";
import { rollDice, rollDiceTool } from "@/lib/dice";

const handler = createMcpHandler(
  (server) => {
    server.tool(
      rollDiceTool.name,
      rollDiceTool.description,
      rollDiceTool.schema,
      async ({ sides }) => ({ content: [rollDice(sides)] }),
    );
  },
  {}, // 服务器选项
  {
    basePath: "/api",     // 必须与文件夹路径匹配
    maxDuration: 60,
    verboseLogs: true,
  },
);

export { handler as GET, handler as POST };
方案亮点
  • 路由仅负责连接
    createMcpHandler
    ;无内联业务逻辑。
  • server.tool
    使用共享的工具模式/描述,并调用共享逻辑。
  • basePath
    应与文件夹路径对齐(例如:
    /api/[transport]
    )。
  • 支持SSE/HTTP传输方式;若需要可单独添加stdio入口。

Claude Desktop Config (mcp-remote)

Claude Desktop配置(mcp-remote)

jsonc
{
  "mcpServers": {
    "rolldice": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://localhost:3000/api/mcp"]
    }
  }
}
jsonc
{
  "mcpServers": {
    "rolldice": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://localhost:3000/api/mcp"]
    }
  }
}

Best Practices

最佳实践

  1. Single source of truth — schemas + logic in
    lib/*
    ; both MCP tools and server actions import them.
  2. Validation first — use Zod for inputs and reuse the same schema for UI + MCP.
  3. Keep route.ts light — only handler wiring, logging, and transport config.
  4. Shared responses — standardize
    { success, result | error }
    shapes for tools and UI.
  5. Vercel-friendly — avoid stateful globals; configure
    maxDuration
    and
    runtime
    if needed.
  6. Multiple transports — expose
    /api/[transport]
    for HTTP/SSE; add stdio entrypoint when required.
  7. Local testing — hit server actions from the web UI to ensure MCP responses stay in sync.
  1. 单一数据源 — 模式与逻辑放在
    lib/*
    中;MCP工具和服务器操作均从这里导入。
  2. 优先验证 — 使用Zod处理输入,并为UI和MCP复用相同的模式。
  3. 保持route.ts轻量化 — 仅包含处理器连接、日志记录和传输配置。
  4. 共享响应格式 — 为工具和UI标准化
    { success, result | error }
    的结构。
  5. 适配Vercel — 避免有状态的全局变量;必要时配置
    maxDuration
    runtime
  6. 多传输方式 — 暴露
    /api/[transport]
    以支持HTTP/SSE;需要时添加stdio入口点。
  7. 本地测试 — 从Web UI调用服务器操作,确保MCP响应保持同步。