从一个 TypeScript 函数开始理解 MCP 协议

之前学 Function Calling 时,我弄明白了一件事:模型能提出"调用这个函数,参数是这些",真正执行函数的是程序。再往前走一步,如果我写了一个工具,怎么让不同的 AI 应用都能找到它、读懂它需要哪些参数,并按同一种方式调用它?

MCP(Model Context Protocol,模型上下文协议)约定了 AI 应用与外部程序交换信息的方式。拿后端开发里一个很小的需求做例子:日志里写着接口耗时 3250 ms,我想让助手调用一个本地工具,得到 3.250 s。换算只有一行逻辑,却足够看清 MCP 帮我们做了哪些事。

一次调用里有三个角色

角色 在耗时换算这个例子里做什么
Host(宿主应用) 用户输入问题的地方,例如支持 MCP 的桌面助手。它管理模型、工具权限和对话,决定是否允许执行一次调用。
Client(客户端) 宿主内部负责 MCP 通信的部分。它向某个服务端查询工具清单,发送调用请求,再把结果交回宿主。
Server(服务端) 提供能力的程序。下面由 Node.js 运行的 TypeScript 程序就可以充当服务端,在本机执行换算并返回结果。

一个宿主可以连接多个服务端。假设助手同时接入日志工具和文档查询工具,它可以为两个服务端分别维护客户端。模型看到的是宿主提供的工具说明,开发者编写的函数则在对应服务端运行。MCP 官方架构说明

服务端可以提供什么

**Tools(工具)**适合把一项操作交给应用调用。每个工具有名字、用途说明和参数格式。比如 format_duration 接收整数毫秒,返回格式化后的秒数。客户端用 tools/list 发现工具,用 tools/call 发起调用。

**Resources(资源)**适合提供可读取的数据。比如服务端可以用 docs://logging/fields 标识一份日志字段说明,客户端通过 resources/read 读取,宿主再决定是否把它加入模型上下文。

**Prompts(提示模板)**适合提供可复用的任务说明。比如服务端提供一个"分析慢请求"模板,用户选中后,客户端用 prompts/get 取回消息内容,宿主再用它组织对话。这三类能力可以按需实现,下面的小工具只用到 Tools。MCP 官方服务端开发指南

客户端怎样找到并调用函数

MCP 的数据层 规定消息怎么写,使用 JSON-RPC 2.0。请求用 method 表示要做什么,params 装参数,id 用来匹配响应。传输层 负责把消息送到对方。本地工具常用 stdio,宿主启动服务端子进程,通过标准输入和标准输出交换消息。远程服务则可以通过 Streamable HTTP 暴露 MCP 端点。协议架构与传输

以一次换算为例,完整过程可以顺着这几步读:

  1. 发现服务能力。 在 2026-07-28 版协议中,客户端可以先调用 server/discover 查看服务端支持的版本和能力,也可以直接发带版本信息的请求。较早的教程中,2025-11-25 等版本使用 initialize 握手,协商完成后再发送 notifications/initialized。官方版本说明
  2. 读取工具清单。 客户端调用 tools/list。服务端返回 format_duration 的名字、说明和 inputSchema,其中写明 milliseconds 是必填整数。有了这份说明,宿主就能把工具提供给模型。
  3. 发送一次调用。 用户问"把 3250 毫秒换算成秒"。模型提出使用该工具,宿主按自身策略允许后,客户端发送 tools/call。业务参数包含工具名 format_duration 和 arguments: {"milliseconds": 3250}。
  4. 执行并返回。 服务端检查参数,执行 TypeScript 函数,把 3.250 s 放进工具结果。客户端按请求 ID 收到它,宿主可以直接展示结果,也可以交给模型组织回答。

对写后端的人来说,这里面有熟悉的 RPC 调用。MCP 额外约定了"去哪里拿工具清单"和"怎样描述参数",所以宿主可以在运行时认识新工具。

用 TypeScript 写一个本地 MCP 工具

本例使用 Node.js 24.15 和官方 TypeScript SDK 2.4.0。在一个空目录打开 PowerShell,初始化项目并安装依赖。

powershell 复制代码
npm init -y
npm install --save-exact @modelcontextprotocol/server@2.4.0 zod@4.6.7

把示例源码保存到刚才安装依赖的目录,文件名为 server.mts。.mts 表示这是使用 ES 模块的 TypeScript 文件。Node.js 24.15 可以直接运行下面这段代码。完整源码

typescript 复制代码
import { McpServer } from "@modelcontextprotocol/server";
import { serveStdio } from "@modelcontextprotocol/server/stdio";
import { z } from "zod";

function formatDuration(milliseconds: number): string {
  const remainder = milliseconds % 1000;
  const seconds = (milliseconds - remainder) / 1000;
  return `${seconds}.${String(remainder).padStart(3, "0")} s`;
}

function createServer() {
  const server = new McpServer({ name: "log-tools", version: "1.0.0" });

  server.registerTool(
    "format_duration",
    {
      description: "把非负的整数毫秒换算成秒,保留三位小数。",
      inputSchema: z.object({ milliseconds: z.int().nonnegative() }),
    },
    ({ milliseconds }) => ({
      content: [{ type: "text", text: formatDuration(milliseconds) }],
    }),
  );
  return server;
}

serveStdio(createServer, { onerror: (error) => console.error(error) });

formatDuration 只负责计算。传入 3250 时,整秒部分是 3,余数是 250。传入 5 时,补零后得到 0.005 s。server.registerTool 把函数接到 MCP 上。客户端通过 tools/list 看到的工具名是 format_duration,调用时 SDK 会按照 inputSchema 检查参数。TypeScript 类型帮助编写代码,Zod 则在程序运行时检查来自客户端的数据。

最后的 serveStdio 负责通过标准输入输出收发协议消息。调试日志用 console.error 输出到标准错误,避免日志混入返回给客户端的消息。官方 TypeScript 开发示例

先试工具,再接入助手

在项目目录运行下面两条命令。第一条查询工具清单,第二条用 Inspector 发起一次真实调用。MCP Inspector

powershell 复制代码
npx -y @modelcontextprotocol/inspector --cli node ./server.mts --method tools/list
npx -y @modelcontextprotocol/inspector --cli node ./server.mts --method tools/call --tool-name format_duration --tool-arg milliseconds=3250

清单里应当出现 format_duration,第二条命令的文本结果应为 3.250 s。把参数改为 0,预期为 0.000 s。再传 -1,可以观察输入校验返回的错误。

接入支持本地 MCP 的助手时,在宿主配置里选择标准输入输出传输。command 填 node.exe 的绝对路径,可用 (Get-Command node.exe).Source 查看。args 填只含 server.mts 绝对路径的数组。这样即使宿主从其他目录启动,也能找到脚本。加载配置后,要求助手"调用 format_duration 换算 3250 毫秒",在调用记录中核对工具名、参数和结果。

相关推荐
鱼听禅1 小时前
Ubuntu安装YOLOv8进行菌落识别训练
linux·python·yolo·ubuntu
测试界的小码农2 小时前
【Linux 命令实战(04):文件内容查看,掌握 cat、less、head、tail】
linux·服务器·less
CodeYp20193 小时前
第13讲:HTTPS 协议原理
linux·https
一条破秋裤3 小时前
【1112】Linux中断【整理】
linux·运维·服务器
CodeYp20195 小时前
第14讲:传输层协议 UDP
linux·udp
北京迅为5 小时前
案例分享 | RK182X算力卡:多路视频VLM监控,四路画面AI同时看懂,一句人话说清现场
linux·人工智能·嵌入式·rk3588·算力卡·rk182x
lzx_00217 小时前
Linux 进程(三) 从进程状态到僵尸进程、孤儿进程
linux·服务器
ZhengEnCi17 小时前
Hono 4 多库多模型服务的企业级 Monorepo 目录结构:npm workspaces 与 Turborepo 该怎么分层
typescript
FlightYe18 小时前
音视频修炼之容器与协议(四):RTSP详解
android·linux·ffmpeg·音视频·dash