@alcyone-labs/modelcontextprotocol-sdk

中文文档
1.16.0发布于 2025/08/01MITESM + CJS近 30 天下载 2.5万

升级到 Zod V4 用于类型脚本的模型背景协议执行

中文使用文档
文档目录33 节
  1. 原订本
  2. 这个叉子
  3. 目录
  4. 概览
  5. 安装
  6. 快速启动
  7. 什么是MCP吗?
  8. 核心概念
  9. 服务器
  10. 资源
  11. 工具
  12. 提示
  13. 完成情况
  14. 显示名称和元数据
  15. 取样
  16. 运行您的服务器
  17. 静态
  18. 可流取的 HTTP
  19. 测试和调试
  20. 实例
  21. Echo 服务器
  22. SQLite 浏览器
  23. 高级使用
  24. 动态服务器
  25. 改进网络效率,并发布通知
  26. 低级服务器
  27. Eliening 用户输入
  28. 写入 MCP 客户端
  29. 代理授权请求上游
  30. 后向兼容性
  31. 文档
  32. 捐款
  33. 许可证

MCP 类型脚本 SDK

重要说明 这是一个非官方的叉子升级到Zod V4. 您可以直接检查 github 仓库上的更改

原订本

NPM Version MIT licensed 原订本

这个叉子

NPM Version MIT licensed

目录

概览

模式上下文协议允许应用程序以标准化的方式为LLMs提供上下文,将提供上下文的关切与实际LLM交互区分开来. 这种TypeScript SDK执行完整的MCP规格,使其易于:

  • 构建可以连接到任何 MCP 服务器的 MCP 客户端
  • 创建曝光资源、提示和工具的 MCP 服务器
  • 使用 Stdio 和可流式 HTTP 等标准运输
  • 处理所有 MCP 协议信息和生命周期事件

安装

npm install @modelcontextprotocol/sdk

QQ MCP要求Node.js v18.x或更高值的工作罚款.

快速启动

让我们创建一个简单的MCP服务器,暴露一个计算工具和一些数据:

import {
  McpServer,
  ResourceTemplate,
} from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Create an MCP server
const server = new McpServer({
  name: "demo-server",
  version: "1.0.0",
});

// Add an addition tool
server.registerTool(
  "add",
  {
    title: "Addition Tool",
    description: "Add two numbers",
    inputSchema: { a: z.number(), b: z.number() },
  },
  async ({ a, b }) => ({
    content: [{ type: "text", text: String(a + b) }],
  }),
);

// Add a dynamic greeting resource
server.registerResource(
  "greeting",
  new ResourceTemplate("greeting://{name}", { list: undefined }),
  {
    title: "Greeting Resource", // Display name for UI
    description: "Dynamic greeting generator",
  },
  async (uri, { name }) => ({
    contents: [
      {
        uri: uri.href,
        text: `Hello, ${name}!`,
      },
    ],
  }),
);

// Start receiving messages on stdin and sending messages on stdout
const transport = new StdioServerTransport();
await server.connect(transport);

什么是MCP吗?

这个 示范背景协议(MCP) 允许您以安全,标准化的方式建立服务器,使数据和功能暴露于LLM应用程序. 把它想成一个网络API,但专门为LLM交互设计. MCP服务器可以:

  • 将数据通过 资源 (想想这些像Get end points;它们用来将信息装入LLM的上下文)
  • 通过 工具 (类似 POST 的终点;用于执行代码或以其他方式产生副作用)
  • 通过 提示 (LLM互动可重复使用的模板)
  • 还有更多!

核心概念

服务器

McpServer是您与MCP协议的核心接口. 它处理连接管理,协议遵守,以及消息路由:

const server = new McpServer({
  name: "my-app",
  version: "1.0.0",
});

资源

资源是您如何向LLMs披露数据。 它们类似于在 REST API 中获取端点 — 它们提供数据,但不应该进行重大的计算或产生副作用:

// Static resource
server.registerResource(
  "config",
  "config://app",
  {
    title: "Application Config",
    description: "Application configuration data",
    mimeType: "text/plain",
  },
  async (uri) => ({
    contents: [
      {
        uri: uri.href,
        text: "App configuration here",
      },
    ],
  }),
);

// Dynamic resource with parameters
server.registerResource(
  "user-profile",
  new ResourceTemplate("users://{userId}/profile", { list: undefined }),
  {
    title: "User Profile",
    description: "User profile information",
  },
  async (uri, { userId }) => ({
    contents: [
      {
        uri: uri.href,
        text: `Profile data for user ${userId}`,
      },
    ],
  }),
);

// Resource with context-aware completion
server.registerResource(
  "repository",
  new ResourceTemplate("github://repos/{owner}/{repo}", {
    list: undefined,
    complete: {
      // Provide intelligent completions based on previously resolved parameters
      repo: (value, context) => {
        if (context?.arguments?.["owner"] === "org1") {
          return ["project1", "project2", "project3"].filter((r) =>
            r.startsWith(value),
          );
        }
        return ["default-repo"].filter((r) => r.startsWith(value));
      },
    },
  }),
  {
    title: "GitHub Repository",
    description: "Repository information",
  },
  async (uri, { owner, repo }) => ({
    contents: [
      {
        uri: uri.href,
        text: `Repository: ${owner}/${repo}`,
      },
    ],
  }),
);

工具

工具让 LLMs 通过您的服务器采取行动 。 与资源不同,预计工具会进行计算并具有副作用:

// Simple tool with parameters
server.registerTool(
  "calculate-bmi",
  {
    title: "BMI Calculator",
    description: "Calculate Body Mass Index",
    inputSchema: {
      weightKg: z.number(),
      heightM: z.number(),
    },
  },
  async ({ weightKg, heightM }) => ({
    content: [
      {
        type: "text",
        text: String(weightKg / (heightM * heightM)),
      },
    ],
  }),
);

// Async tool with external API call
server.registerTool(
  "fetch-weather",
  {
    title: "Weather Fetcher",
    description: "Get weather data for a city",
    inputSchema: { city: z.string() },
  },
  async ({ city }) => {
    const response = await fetch(`https://api.weather.com/${city}`);
    const data = await response.text();
    return {
      content: [{ type: "text", text: data }],
    };
  },
);

// Tool that returns ResourceLinks
server.registerTool(
  "list-files",
  {
    title: "List Files",
    description: "List project files",
    inputSchema: { pattern: z.string() },
  },
  async ({ pattern }) => ({
    content: [
      { type: "text", text: `Found files matching "${pattern}":` },
      // ResourceLinks let tools return references without file content
      {
        type: "resource_link",
        uri: "file:///project/README.md",
        name: "README.md",
        mimeType: "text/markdown",
        description: "A README file",
      },
      {
        type: "resource_link",
        uri: "file:///project/src/index.ts",
        name: "index.ts",
        mimeType: "text/typescript",
        description: "An index file",
      },
    ],
  }),
);

工具可以返回 ResourceLink 对象为不嵌入全部内容的参考资源。 这对于处理大文件或许多资源时的性能至关重要 - 然后客户端可以有选择地只读取他们使用所提供的URI所需要的资源.

提示

提示是可重复使用的模板, 帮助 LLMS 与您的服务器有效互动 :

import { completable } from "@modelcontextprotocol/sdk/server/completable.js";

server.registerPrompt(
  "review-code",
  {
    title: "Code Review",
    description: "Review code for best practices and potential issues",
    argsSchema: { code: z.string() },
  },
  ({ code }) => ({
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: `Please review this code:\n\n${code}`,
        },
      },
    ],
  }),
);

// Prompt with context-aware completion
server.registerPrompt(
  "team-greeting",
  {
    title: "Team Greeting",
    description: "Generate a greeting for team members",
    argsSchema: {
      department: completable(z.string(), (value) => {
        // Department suggestions
        return ["engineering", "sales", "marketing", "support"].filter((d) =>
          d.startsWith(value),
        );
      }),
      name: completable(z.string(), (value, context) => {
        // Name suggestions based on selected department
        const department = context?.arguments?.["department"];
        if (department === "engineering") {
          return ["Alice", "Bob", "Charlie"].filter((n) => n.startsWith(value));
        } else if (department === "sales") {
          return ["David", "Eve", "Frank"].filter((n) => n.startsWith(value));
        } else if (department === "marketing") {
          return ["Grace", "Henry", "Iris"].filter((n) => n.startsWith(value));
        }
        return ["Guest"].filter((n) => n.startsWith(value));
      }),
    },
  },
  ({ department, name }) => ({
    messages: [
      {
        role: "assistant",
        content: {
          type: "text",
          text: `Hello ${name}, welcome to the ${department} team!`,
        },
      },
    ],
  }),
);

完成情况

MCP支持完成参数以帮助用户填写即时参数和资源模板参数. 见上文实例 资源完成情况 财务报告和审定财务报表 及时完成。 。 。 .

客户端使用

// Request completions for any argument
const result = await client.complete({
  ref: {
    type: "ref/prompt", // or "ref/resource"
    name: "example", // or uri: "template://..."
  },
  argument: {
    name: "argumentName",
    value: "partial", // What the user has typed so far
  },
  context: {
    // Optional: Include previously resolved arguments
    arguments: {
      previousArg: "value",
    },
  },
});

显示名称和元数据

所有资源、工具和提示都支持可选 title 字段,用于更好的 UI 演示。 这个 title 用作显示名称,而 name 仍然是唯一的标识符.

说明: 这个 register* 方法(单位:千美元)registerTool, (中文(简体) ), registerPrompt, (中文(简体) ), registerResource)是新守则的建议方法。 较旧的方法tool, (中文(简体) ), prompt, (中文(简体) ), resource) 仍然可供反向兼容.

工具标题优先

具体地说,对于工具,有两种方式来指定标题:

  • title 字段在工具配置中
  • annotations.title 字段( 当使用旧字段时) tool() 带有说明的方法)

优先顺序为: title → annotations.title → name

// Using registerTool (recommended)
server.registerTool(
  "my_tool",
  {
    title: "My Tool", // This title takes precedence
    annotations: {
      title: "Annotation Title", // This is ignored if title is set
    },
  },
  handler,
);

// Using tool with annotations (older API)
server.tool(
  "my_tool",
  "description",
  {
    title: "Annotation Title", // This is used as title
  },
  handler,
);

在构建客户端时, 使用所提供的工具获取适当的显示名 :

import { getDisplayName } from "@modelcontextprotocol/sdk/shared/metadataUtils.js";

// Automatically handles the precedence: title → annotations.title → name
const displayName = getDisplayName(tool);

取样

MCP服务器可以请求支持采样的连接客户端完成LLM.

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const mcpServer = new McpServer({
  name: "tools-with-sample-server",
  version: "1.0.0",
});

// Tool that uses LLM sampling to summarize any text
mcpServer.registerTool(
  "summarize",
  {
    description: "Summarize any text using an LLM",
    inputSchema: {
      text: z.string().describe("Text to summarize"),
    },
  },
  async ({ text }) => {
    // Call the LLM through MCP sampling
    const response = await mcpServer.server.createMessage({
      messages: [
        {
          role: "user",
          content: {
            type: "text",
            text: `Please summarize the following text concisely:\n\n${text}`,
          },
        },
      ],
      maxTokens: 500,
    });

    return {
      content: [
        {
          type: "text",
          text:
            response.content.type === "text"
              ? response.content.text
              : "Unable to generate summary",
        },
      ],
    };
  },
);

async function main() {
  const transport = new StdioServerTransport();
  await mcpServer.connect(transport);
  console.log("MCP server is running...");
}

main().catch((error) => {
  console.error("Server error:", error);
  process.exit(1);
});

运行您的服务器

TypeScript中的MCP服务器需要连接到一个传输上才能与客户端通信. 您如何启动服务器取决于运输的选择 :

静态

对于命令行工具和直接集成:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new McpServer({
  name: "example-server",
  version: "1.0.0",
});

// ... set up server resources, tools, and prompts ...

const transport = new StdioServerTransport();
await server.connect(transport);

可流取的 HTTP

对于远程服务器,设置可流的HTTP传输,既处理客户端请求,又处理服务器对客户端通知.

有会话管理

在某些情况下,服务器需要状态. 这是通过 会话管理。 。 。 .

import express from "express";
import { randomUUID } from "node:crypto";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";

const app = express();
app.use(express.json());

// Map to store transports by session ID
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};

// Handle POST requests for client-to-server communication
app.post("/mcp", async (req, res) => {
  // Check for existing session ID
  const sessionId = req.headers["mcp-session-id"] as string | undefined;
  let transport: StreamableHTTPServerTransport;

  if (sessionId && transports[sessionId]) {
    // Reuse existing transport
    transport = transports[sessionId];
  } else if (!sessionId && isInitializeRequest(req.body)) {
    // New initialization request
    transport = new StreamableHTTPServerTransport({
      sessionIdGenerator: () => randomUUID(),
      onsessioninitialized: (sessionId) => {
        // Store the transport by session ID
        transports[sessionId] = transport;
      },
      // DNS rebinding protection is disabled by default for backwards compatibility. If you are running this server
      // locally, make sure to set:
      // enableDnsRebindingProtection: true,
      // allowedHosts: ['127.0.0.1'],
    });

    // Clean up transport when closed
    transport.onclose = () => {
      if (transport.sessionId) {
        delete transports[transport.sessionId];
      }
    };
    const server = new McpServer({
      name: "example-server",
      version: "1.0.0",
    });

    // ... set up server resources, tools, and prompts ...

    // Connect to the MCP server
    await server.connect(transport);
  } else {
    // Invalid request
    res.status(400).json({
      jsonrpc: "2.0",
      error: {
        code: -32000,
        message: "Bad Request: No valid session ID provided",
      },
      id: null,
    });
    return;
  }

  // Handle the request
  await transport.handleRequest(req, res, req.body);
});

// Reusable handler for GET and DELETE requests
const handleSessionRequest = async (
  req: express.Request,
  res: express.Response,
) => {
  const sessionId = req.headers["mcp-session-id"] as string | undefined;
  if (!sessionId || !transports[sessionId]) {
    res.status(400).send("Invalid or missing session ID");
    return;
  }

  const transport = transports[sessionId];
  await transport.handleRequest(req, res);
};

// Handle GET requests for server-to-client notifications via SSE
app.get("/mcp", handleSessionRequest);

// Handle DELETE requests for session termination
app.delete("/mcp", handleSessionRequest);

app.listen(3000);

[TIP] 在远程环境中使用此选项时, 确保允许页眉参数 mcp-session-id 在中央统计局。 否则,可能导致: Bad Request: No valid session ID provided 错误。 下节为例.


基于浏览器的客户端的 CORS 配置

如果您希望您的服务器能够被基于浏览器的MCP客户端访问,则需要配置 CORS 头. 这个 Mcp-Session-Id 浏览器客户端必须被曝光才能访问:

import cors from "cors";

// Add CORS middleware before your MCP routes
app.use(
  cors({
    origin: "*", // Configure appropriately for production, for example:
    // origin: ['https://your-remote-domain.com', 'https://your-other-remote-domain.com'],
    exposedHeaders: ["Mcp-Session-Id"],
    allowedHeaders: ["Content-Type", "mcp-session-id"],
  }),
);

这种配置是必要的,因为:

  • MCP可流式HTTP运输使用 Mcp-Session-Id 会话管理头
  • 浏览器限制访问响应头, 除非通过 CORS 明确曝光
  • 如果没有这种配置,基于浏览器的客户端将无法从初始化回复读取会话ID

无会话管理( 无状态)

简单使用不需要会话管理的案例:

const app = express();
app.use(express.json());

app.post("/mcp", async (req: Request, res: Response) => {
  // In stateless mode, create a new instance of transport and server for each request
  // to ensure complete isolation. A single instance would cause request ID collisions
  // when multiple clients connect concurrently.

  try {
    const server = getServer();
    const transport: StreamableHTTPServerTransport =
      new StreamableHTTPServerTransport({
        sessionIdGenerator: undefined,
      });
    res.on("close", () => {
      console.log("Request closed");
      transport.close();
      server.close();
    });
    await server.connect(transport);
    await transport.handleRequest(req, res, req.body);
  } catch (error) {
    console.error("Error handling MCP request:", error);
    if (!res.headersSent) {
      res.status(500).json({
        jsonrpc: "2.0",
        error: {
          code: -32603,
          message: "Internal server error",
        },
        id: null,
      });
    }
  }
});

// SSE notifications not supported in stateless mode
app.get("/mcp", async (req: Request, res: Response) => {
  console.log("Received GET MCP request");
  res.writeHead(405).end(
    JSON.stringify({
      jsonrpc: "2.0",
      error: {
        code: -32000,
        message: "Method not allowed.",
      },
      id: null,
    }),
  );
});

// Session termination not needed in stateless mode
app.delete("/mcp", async (req: Request, res: Response) => {
  console.log("Received DELETE MCP request");
  res.writeHead(405).end(
    JSON.stringify({
      jsonrpc: "2.0",
      error: {
        code: -32000,
        message: "Method not allowed.",
      },
      id: null,
    }),
  );
});

// Start the server
const PORT = 3000;
setupServer()
  .then(() => {
    app.listen(PORT, (error) => {
      if (error) {
        console.error("Failed to start server:", error);
        process.exit(1);
      }
      console.log(
        `MCP Stateless Streamable HTTP Server listening on port ${PORT}`,
      );
    });
  })
  .catch((error) => {
    console.error("Failed to set up the server:", error);
    process.exit(1);
  });

这种无国籍做法有助于:

  • 简单的 API 包装
  • 每项请求都是独立的可重复的情景
  • 没有共享会话状态的横向部署

DNS 重新绑定的保护

流动式HTTP运输包括DNS重新绑定保护以防止安全漏洞. 默认情况下,这种保护是 已禁用 为后向相容性.

重要: 如果您正在本地运行此服务器, 请启用 DNS 重新绑定保护 :

const transport = new StreamableHTTPServerTransport({
  sessionIdGenerator: () => randomUUID(),
  enableDnsRebindingProtection: true,

  allowedHosts: ['127.0.0.1', ...],
  allowedOrigins: ['https://yourdomain.com', 'https://www.yourdomain.com']
});

测试和调试

为了测试您的服务器, 您可以使用 MCP 检查员。关于更多信息,请参看其研究.

实例

Echo 服务器

一个简单的服务器, 演示资源、 工具和提示 :

import {
  McpServer,
  ResourceTemplate,
} from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

const server = new McpServer({
  name: "echo-server",
  version: "1.0.0",
});

server.registerResource(
  "echo",
  new ResourceTemplate("echo://{message}", { list: undefined }),
  {
    title: "Echo Resource",
    description: "Echoes back messages as resources",
  },
  async (uri, { message }) => ({
    contents: [
      {
        uri: uri.href,
        text: `Resource echo: ${message}`,
      },
    ],
  }),
);

server.registerTool(
  "echo",
  {
    title: "Echo Tool",
    description: "Echoes back the provided message",
    inputSchema: { message: z.string() },
  },
  async ({ message }) => ({
    content: [{ type: "text", text: `Tool echo: ${message}` }],
  }),
);

server.registerPrompt(
  "echo",
  {
    title: "Echo Prompt",
    description: "Creates a prompt to process a message",
    argsSchema: { message: z.string() },
  },
  ({ message }) => ({
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: `Please process this message: ${message}`,
        },
      },
    ],
  }),
);

SQLite 浏览器

一个显示数据库整合的更复杂的例子:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import sqlite3 from "sqlite3";
import { promisify } from "util";
import { z } from "zod";

const server = new McpServer({
  name: "sqlite-explorer",
  version: "1.0.0",
});

// Helper to create DB connection
const getDb = () => {
  const db = new sqlite3.Database("database.db");
  return {
    all: promisify<string, any[]>(db.all.bind(db)),
    close: promisify(db.close.bind(db)),
  };
};

server.registerResource(
  "schema",
  "schema://main",
  {
    title: "Database Schema",
    description: "SQLite database schema",
    mimeType: "text/plain",
  },
  async (uri) => {
    const db = getDb();
    try {
      const tables = await db.all(
        "SELECT sql FROM sqlite_master WHERE type='table'",
      );
      return {
        contents: [
          {
            uri: uri.href,
            text: tables.map((t: { sql: string }) => t.sql).join("\n"),
          },
        ],
      };
    } finally {
      await db.close();
    }
  },
);

server.registerTool(
  "query",
  {
    title: "SQL Query",
    description: "Execute SQL queries on the database",
    inputSchema: { sql: z.string() },
  },
  async ({ sql }) => {
    const db = getDb();
    try {
      const results = await db.all(sql);
      return {
        content: [
          {
            type: "text",
            text: JSON.stringify(results, null, 2),
          },
        ],
      };
    } catch (err: unknown) {
      const error = err as Error;
      return {
        content: [
          {
            type: "text",
            text: `Error: ${error.message}`,
          },
        ],
        isError: true,
      };
    } finally {
      await db.close();
    }
  },
);

高级使用

动态服务器

如果您想要提供一组初始工具/ 即时/ 资源, 但稍后根据用户动作或外部状态变化添加更多工具/ 即时/ 资源, 您可以添加/ 更新/ 重新移动 之后 服务器已连接。 这将自动释放相应的 listChanged 通知:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

const server = new McpServer({
  name: "Dynamic Example",
  version: "1.0.0",
});

const listMessageTool = server.tool(
  "listMessages",
  { channel: z.string() },
  async ({ channel }) => ({
    content: [{ type: "text", text: await listMessages(channel) }],
  }),
);

const putMessageTool = server.tool(
  "putMessage",
  { channel: z.string(), message: z.string() },
  async ({ channel, message }) => ({
    content: [{ type: "text", text: await putMessage(channel, message) }],
  }),
);
// Until we upgrade auth, `putMessage` is disabled (won't show up in listTools)
putMessageTool.disable();

const upgradeAuthTool = server.tool(
  "upgradeAuth",
  { permission: z.enum(["write", "admin"]) },
  // Any mutations here will automatically emit `listChanged` notifications
  async ({ permission }) => {
    const { ok, err, previous } = await upgradeAuthAndStoreToken(permission);
    if (!ok) return { content: [{ type: "text", text: `Error: ${err}` }] };

    // If we previously had read-only access, 'putMessage' is now available
    if (previous === "read") {
      putMessageTool.enable();
    }

    if (permission === "write") {
      // If we've just upgraded to 'write' permissions, we can still call 'upgradeAuth'
      // but can only upgrade to 'admin'.
      upgradeAuthTool.update({
        paramSchema: { permission: z.enum(["admin"]) }, // change validation rules
      });
    } else {
      // If we're now an admin, we no longer have anywhere to upgrade to, so fully remove that tool
      upgradeAuthTool.remove();
    }
  },
);

// Connect as normal
const transport = new StdioServerTransport();
await server.connect(transport);

改进网络效率,并发布通知

当执行可触发通知的批量更新(例如,在一个回路中允许或使多个工具失效)时,SDK可以在短时间内发送大量信息. 为了提高性能并减少网络流量,您可以启用通知分解.

这个特性将多个,快速的调用相同的通知类型汇入单个消息. 例如,如果连续禁用5个工具,则只有一个 notifications/tools/list_changed 信件将改为5个.

[大 这个特性是为在其参数中不携带独特数据的"简单"通知而设计的. 为防止无声数据丢失 自动绕过 任何含有下列内容的通知: params 对象或一个 relatedRequestId这种通知将始终立即发出.

这是服务器初始化时配置的选入功能 .

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

const server = new McpServer(
  {
    name: "efficient-server",
    version: "1.0.0"
  },
  {
    // Enable notification debouncing for specific methods
    debouncedNotificationMethods: [
      'notifications/tools/list_changed',
      'notifications/resources/list_changed',
      'notifications/prompts/list_changed'
    ]
  }
);

// Now, any rapid changes to tools, resources, or prompts will result
// in a single, consolidated notification for each type.
server.registerTool("tool1", ...).disable();
server.registerTool("tool2", ...).disable();
server.registerTool("tool3", ...).disable();
// Only one 'notifications/tools/list_changed' is sent.

低级服务器

为了进行更多的控制,您可以直接使用低级服务器类:

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  ListPromptsRequestSchema,
  GetPromptRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

const server = new Server(
  {
    name: "example-server",
    version: "1.0.0",
  },
  {
    capabilities: {
      prompts: {},
    },
  },
);

server.setRequestHandler(ListPromptsRequestSchema, async () => {
  return {
    prompts: [
      {
        name: "example-prompt",
        description: "An example prompt template",
        arguments: [
          {
            name: "arg1",
            description: "Example argument",
            required: true,
          },
        ],
      },
    ],
  };
});

server.setRequestHandler(GetPromptRequestSchema, async (request) => {
  if (request.params.name !== "example-prompt") {
    throw new Error("Unknown prompt");
  }
  return {
    description: "Example prompt",
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: "Example prompt text",
        },
      },
    ],
  };
});

const transport = new StdioServerTransport();
await server.connect(transport);

Eliening 用户输入

MCP服务器可以通过引出功能要求用户提供更多信息. 用于服务器需要用户输入或确认的交互式工作流程:

// Server-side: Restaurant booking tool that asks for alternatives
server.tool(
  "book-restaurant",
  {
    restaurant: z.string(),
    date: z.string(),
    partySize: z.number(),
  },
  async ({ restaurant, date, partySize }) => {
    // Check availability
    const available = await checkAvailability(restaurant, date, partySize);

    if (!available) {
      // Ask user if they want to try alternative dates
      const result = await server.server.elicitInput({
        message: `No tables available at ${restaurant} on ${date}. Would you like to check alternative dates?`,
        requestedSchema: {
          type: "object",
          properties: {
            checkAlternatives: {
              type: "boolean",
              title: "Check alternative dates",
              description: "Would you like me to check other dates?",
            },
            flexibleDates: {
              type: "string",
              title: "Date flexibility",
              description: "How flexible are your dates?",
              enum: ["next_day", "same_week", "next_week"],
              enumNames: ["Next day", "Same week", "Next week"],
            },
          },
          required: ["checkAlternatives"],
        },
      });

      if (result.action === "accept" && result.content?.checkAlternatives) {
        const alternatives = await findAlternatives(
          restaurant,
          date,
          partySize,
          result.content.flexibleDates as string,
        );
        return {
          content: [
            {
              type: "text",
              text: `Found these alternatives: ${alternatives.join(", ")}`,
            },
          ],
        };
      }

      return {
        content: [
          {
            type: "text",
            text: "No booking made. Original date not available.",
          },
        ],
      };
    }

    // Book the table
    await makeBooking(restaurant, date, partySize);
    return {
      content: [
        {
          type: "text",
          text: `Booked table for ${partySize} at ${restaurant} on ${date}`,
        },
      ],
    };
  },
);

客户端: 处理查询请求

// This is a placeholder - implement based on your UI framework
async function getInputFromUser(
  message: string,
  schema: any,
): Promise<{
  action: "accept" | "decline" | "cancel";
  data?: Record<string, any>;
}> {
  // This should be implemented depending on the app
  throw new Error("getInputFromUser must be implemented for your platform");
}

client.setRequestHandler(ElicitRequestSchema, async (request) => {
  const userResponse = await getInputFromUser(
    request.params.message,
    request.params.requestedSchema,
  );

  return {
    action: userResponse.action,
    content: userResponse.action === "accept" ? userResponse.data : undefined,
  };
});

说明: Election需要客户端支持. 客户必须声明 elicitation 初始化期间的能力.

写入 MCP 客户端

SDK提供了一个高级客户界面:

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

const transport = new StdioClientTransport({
  command: "node",
  args: ["server.js"],
});

const client = new Client({
  name: "example-client",
  version: "1.0.0",
});

await client.connect(transport);

// List prompts
const prompts = await client.listPrompts();

// Get a prompt
const prompt = await client.getPrompt({
  name: "example-prompt",
  arguments: {
    arg1: "value",
  },
});

// List resources
const resources = await client.listResources();

// Read a resource
const resource = await client.readResource({
  uri: "file:///example.txt",
});

// Call a tool
const result = await client.callTool({
  name: "example-tool",
  arguments: {
    arg1: "value",
  },
});

代理授权请求上游

您可以代理 OAuth 请求外部授权提供者 :

import express from "express";
import { ProxyOAuthServerProvider } from "@modelcontextprotocol/sdk/server/auth/providers/proxyProvider.js";
import { mcpAuthRouter } from "@modelcontextprotocol/sdk/server/auth/router.js";

const app = express();

const proxyProvider = new ProxyOAuthServerProvider({
  endpoints: {
    authorizationUrl: "https://auth.external.com/oauth2/v1/authorize",
    tokenUrl: "https://auth.external.com/oauth2/v1/token",
    revocationUrl: "https://auth.external.com/oauth2/v1/revoke",
  },
  verifyAccessToken: async (token) => {
    return {
      token,
      clientId: "123",
      scopes: ["openid", "email", "profile"],
    };
  },
  getClient: async (client_id) => {
    return {
      client_id,
      redirect_uris: ["http://localhost:3000/callback"],
    };
  },
});

app.use(
  mcpAuthRouter({
    provider: proxyProvider,
    issuerUrl: new URL("http://auth.external.com"),
    baseUrl: new URL("http://mcp.example.com"),
    serviceDocumentationUrl: new URL("https://docs.example.com/"),
  }),
);

这个设置允许您:

  • 将 OAuth 请求转发到外部提供者
  • 添加自定义符号验证逻辑
  • 管理客户注册
  • 提供自定义文档 URL
  • 保持对 OAuth 流量的控制,同时将权限下放给外部提供者

后向兼容性

具有可流式Http运输的客户端和服务器可以维护 向后兼容 与被贬值的HTTP+SSE运输(从协议版本2024-11-05)如下:

客户端兼容性

对于需要同时使用可流式HTTP和旧的SSE服务器的客户端:

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
let client: Client | undefined = undefined;
const baseUrl = new URL(url);
try {
  client = new Client({
    name: "streamable-http-client",
    version: "1.0.0",
  });
  const transport = new StreamableHTTPClientTransport(new URL(baseUrl));
  await client.connect(transport);
  console.log("Connected using Streamable HTTP transport");
} catch (error) {
  // If that fails with a 4xx error, try the older SSE transport
  console.log(
    "Streamable HTTP connection failed, falling back to SSE transport",
  );
  client = new Client({
    name: "sse-client",
    version: "1.0.0",
  });
  const sseTransport = new SSEClientTransport(baseUrl);
  await client.connect(sseTransport);
  console.log("Connected using SSE transport");
}

服务器系统兼容性

对于需要同时支持可流式HTTP和旧客户端的服务器:

import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";

const server = new McpServer({
  name: "backwards-compatible-server",
  version: "1.0.0",
});

// ... set up server resources, tools, and prompts ...

const app = express();
app.use(express.json());

// Store transports for each session type
const transports = {
  streamable: {} as Record<string, StreamableHTTPServerTransport>,
  sse: {} as Record<string, SSEServerTransport>,
};

// Modern Streamable HTTP endpoint
app.all("/mcp", async (req, res) => {
  // Handle Streamable HTTP transport for modern clients
  // Implementation as shown in the "With Session Management" example
  // ...
});

// Legacy SSE endpoint for older clients
app.get("/sse", async (req, res) => {
  // Create SSE transport for legacy clients
  const transport = new SSEServerTransport("/messages", res);
  transports.sse[transport.sessionId] = transport;

  res.on("close", () => {
    delete transports.sse[transport.sessionId];
  });

  await server.connect(transport);
});

// Legacy message endpoint for older clients
app.post("/messages", async (req, res) => {
  const sessionId = req.query.sessionId as string;
  const transport = transports.sse[sessionId];
  if (transport) {
    await transport.handlePostMessage(req, res, req.body);
  } else {
    res.status(400).send("No transport found for sessionId");
  }
});

app.listen(3000);

说明:SSE运输现在被贬值,有利于流式HTTP. 新的实施应当使用可流式HTTP,现有的SSE实施应当计划迁移.

文档

捐款

GitHub网站欢迎提出问题和拉动请求 https://github.com/modelcontextprotocol/typescript-sdk (中文(简体) )。 。 。 .

许可证

这个项目根据麻省理工学院许可证获得许可——见 自由主义 详细文件.

版本与发布记录

@alcyone-labs/modelcontextprotocol-sdk 共发布 1 个版本,首次发布于 2025/08/01;过去 12 个月发布了 0 个版本。

版本发布日期距今
1.16.0latest2025/08/011.3 年前

关于 @alcyone-labs/modelcontextprotocol-sdk 的常见问题

@alcyone-labs/modelcontextprotocol-sdk 是什么?

升级到 Zod V4 用于类型脚本的模型背景协议执行

如何安装 @alcyone-labs/modelcontextprotocol-sdk?

在项目目录运行 npm install @alcyone-labs/modelcontextprotocol-sdk,或使用 pnpm add / yarn add / bun add。

@alcyone-labs/modelcontextprotocol-sdk 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/alcyone-labs__modelcontextprotocol-sdk 是否存在。

@alcyone-labs/modelcontextprotocol-sdk 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@alcyone-labs/modelcontextprotocol-sdk 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@alcyone-labs/modelcontextprotocol-sdk 可以商用吗?

@alcyone-labs/modelcontextprotocol-sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@alcyone-labs/modelcontextprotocol-sdk 的下载量有多少?

根据 npm 官方下载统计,@alcyone-labs/modelcontextprotocol-sdk 在 2026/10/06 – 2026/11/04 这 30 天内被下载 24,617 次,日均约 821 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@alcyone-labs/modelcontextprotocol-sdk 还在维护吗?

最新版本 1.16.0 发布于 2025/08/01(1.3 年前);至今共发布 1 个版本,其中过去 12 个月发布了 0 个;首次发布于 2025/08/01。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。