MCP 是什么?
MCP 是 Model Context Protocol 由 Anthropic 开发的开源协议,使(如 Cursor、Cline、Claude Desktop) AI 系统能够安全地连接各种数据源。它通过客户端-服务器架构为 AI 助手提供了访问外部数据、工具和提示的通用标准。
一句话总结: MCP 为 AI 代理连接到不同的服务提供了一种标准化的方式
一张你在很多地方看到过的图 这个应该原文章地址

MCP 可以干什么?
相比于 MCP 是什么我其实更想知道 MCP 能干什么?
AI 有一个缺点: AI 只知道过去发生的事情,却无法实时从外界获取最新的信息 比如今天的天气情况、今天的热点新闻等。因为训练 AI 的数据集始终是滞后的
在 MCP 之前我们可以通过 Function Call
来给 AI 增加类似网页搜索的功能让其可以获取到实时数据,但没有一个标准,同样的功能换一个 AI 可能还需要重新实现一下
就像上边的那张图片 MCP 提供一种标准的接入协议,只要是按照 MCP 开发的服务端就可以在 MCP 客户端中接入(这他么不是废话吗!)
MCP 可以定义一系列的能力比如 GitHub MCP 服务

提供了比如搜索仓库、issuer、回复、用户,创建仓库,提交代码等功能
你可以通过自然语言让 AI 来执行这些操作
然后还有 filesystem 可以让 AI 操作文件

MCP 不只是提供 Tool(工具) 还可以提供 Prompt(主要是作为 AI 提示词 模板)、Resource(客户端可以读取的数据(如 API 响应或文件内容),这里不再展开感兴趣的自行前往官网探索
然后要说的是: 使用一些 MCP 服务的时候要注意 安全 防止恶意代码被执行造成损失!
如何实现一个 MCP 服务?
这里实现两个简单的 MCP 服务一个时间时间工具 和 简单的 GitHub 搜索,把官网 MCP 服务端的两种写法都实践一些
首先做一些准备工作, 为了方便使用 monorepo 的方式组织仓库 猛击直达

实现 time MCP 服务
这里假设你已经准备好了一个与上边类似的仓库,然后进入到 time 文件夹
如果你是一个单仓库,创建一个空的文件夹直接跟着做
执行 npm init -y
初始化 package.json 文件

安装需要的依赖
bash
pnpm install @modelcontextprotocol/sdk zod dayjs
pnpm install -D @types/node typescript shx
创建 index.ts
文件
创建 tsconfig.json
写入如下内容 (仓库中是继承了根目录下的配置文件)
json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
},
"include": ["packages/**/*"],
"exclude": ["node_modules"]
}
修改 package.json
文件
json
{
"name": "time",
"version": "0.0.1",
"bin": {
"mcp-server-time": "dist/index.js"
},
"scripts": {
"build": "tsc && shx chmod +x dist/*.js",
"prepare": "npm run build",
"watch": "tsc --watch"
},
"files": [
"dist"
],
"dependencies": {
"@modelcontextprotocol/sdk": "1.8.0",
"dayjs": "^1.11.13",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/node": "^22.14.0",
"shx": "^0.3.4",
"typescript": "^5.6.2"
}
}
在 index.ts
中写入如下代码
ts
#!/usr/bin/env node
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { z } from 'zod'
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc.js'
import timezone from 'dayjs/plugin/timezone.js'
import packageJson from './package.json'
// 扩展 dayjs 以支持 UTC 和时区
dayjs.extend(utc)
dayjs.extend(timezone)
// 创建 MCP 服务器实例
const server = new McpServer({
name: 'mcp-server-time',
version: packageJson.version
})
注意
!/usr/bin/env node
(解释器指示符) 意思是需要使用 node 去执行代码
增加获取当前时间的工具
ts
// 获取当前时间的工具
server.tool(
'get_current_time', // 工具名称
'获取当前时间', // 工具描述
{
// 工具参数
timezone: z.string().optional(),
},
// 工具实现
async ({ timezone }) => {
// 获取当前时间
const tz = timezone || process.env.LOCAL_TIMEZONE || 'Asia/Shanghai';
// 格式化当前时间
const currentTime = dayjs().tz(tz).format('YYYY-MM-DD HH:mm:ss');
// 返回数据 - 下边的格式是固定的
return {
content: [{ type: "text", text: JSON.stringify({ currentTime }, null, 2) }],
};
}
)
增加时间转换工具
ts
// 时间转换工具
server.tool(
'convert_time', // 工具名称
'在时区之间转换时间', // 工具描述
{
// 工具参数
source_timezone: z.string(),
time: z.string().regex(/^([01]\d|2[0-3]):([0-5]\d)$/, 'Invalid time format, expected HH:MM'),
target_timezone: z.string(),
},
// 工具实现
async ({ source_timezone, time, target_timezone }) => {
const sourceTime = dayjs.tz(`${dayjs().format('YYYY-MM-DD')} ${time}`, source_timezone);
const convertedTime = sourceTime.clone().tz(target_timezone).format();
return {
content: [{ type: "text", text: JSON.stringify({ convertedTime }, null, 2) }],
};
}
)
启动 MCP 服务器
ts
// 启动服务器
async function runServer() {
// 这两行代码应该算是固定写法
const transport = new StdioServerTransport()
await server.connect(transport)
console.error('获取当前时间和时区转换的 MCP 服务器已在 stdio 上启动')
}
runServer().catch((error) => {
console.error('启动服务器时出错:', error)
process.exit(1)
})
实现 gitHub search MCP 服务
这里与上边的引入有一点差别使用 Server
而不是 McpServer
来实现服务
ts
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { z } from 'zod'
import packageJson from './package.json'
import {CallToolRequestSchema, ListToolsRequestSchema,} from "@modelcontextprotocol/sdk/types.js";
import { zodToJsonSchema } from 'zod-to-json-schema';
const server = new Server(
{
name: "github-search-mcp-server",
version: packageJson.version,
},
{
capabilities: {
tools: {},
},
}
);
这里会统一定义工具列表
ts
// 定义 GitHub 搜索工具的输入参数
const SearchParamsSchema = z.object({
query: z.string().describe('搜索关键词,用于匹配 GitHub 中的内容'),
page: z.number().optional().default(1).describe('当前页码,用于分页查询'),
perPage: z.number().optional().default(30).describe('每页返回的搜索结果数量'),
type: z.enum(['repositories', 'code', 'issues', 'users'])
.optional()
.describe('搜索类型,可选值为 repositories、code、issues 或 users')
});
// 定义获取 GitHub 用户信息的输入参数
const UserInfoParamsSchema = z.object({
username: z.string().describe('GitHub 的用户名')
});
// 定义工具列表及输入参数
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "github_search",
description: "在 GitHub 上搜索仓库、代码、Issues 或用户",
inputSchema: zodToJsonSchema(SearchParamsSchema),
},
{
name: "get_github_user",
description: "通过用户名查询 GitHub 用户信息",
inputSchema: zodToJsonSchema(UserInfoParamsSchema),
},
]
}
})
实现对应的工具
ts
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
if (!request.params.arguments) {
throw new Error("Arguments are required");
}
switch (request.params.name) {
// GitHub 搜索工具
case "search_github": {
const { query, page, perPage, type } = SearchParamsSchema.parse(request.params.arguments);
const url = `https://api.github.com/search/${type}?q=${encodeURIComponent(query)}&page=${page}&per_page=${perPage}`
const res = await fetch(url, {
headers: {
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`
}
})
if (!res.ok) {
throw new Error(`GitHub API 错误: ${res.status} ${res.statusText}`)
}
const data = await res.json()
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
// 获取 GitHub 用户信息工具
case "get_github_user": {
const { username } = UserInfoParamsSchema.parse(request.params.arguments);
const url = `https://api.github.com/users/${encodeURIComponent(username)}`
const res = await fetch(url, {
headers: {
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`
}
})
if (!res.ok) {
throw new Error(`获取用户信息失败: ${res.status} ${res.statusText}`)
}
const data = await res.json()
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
default:
throw new Error(`Unknown tool: ${request.params.name}`);
}
} catch (error) {
if (error instanceof z.ZodError) {
throw new Error(`Invalid input: ${JSON.stringify(error.errors)}`);
}
throw error;
}
});
这里需要注意的是我们使用了一个 process.env.GITHUB_TOKEN
(调用 GitHub 的 token 可以在 github 中生成) 环境变量
启动 MCP 服务器,这里和上边一样
ts
async function runServer() {
const transport = new StdioServerTransport()
await server.connect(transport)
console.error('GitHub 搜索 MCP 服务器已在 stdio 上启动')
}
runServer().catch((error) => {
console.error('启动服务器时出错:', error)
process.exit(1)
})
测试 MCP 服务
官方提供了一个测试工具猛击直达
在调试之前需要先执行 pnpm run build
执行打包
测试 time MCP 服务
执行 npx @modelcontextprotocol/inspector node dist/index.js

访问 http://127.0.0.1:6274


获取当前时间

根据时区转换时间

测试 gitHub search MCP 服务
执行 npx @modelcontextprotocol/inspector node dist/index.js
访问 http://127.0.0.1:6274
这里因为使用了 process.env.GITHUB_TOKEN
环境变量,启动的时候我们需要进行设置,否则 GitHub api 调用会 401

根据用户名获取 GitHub 的用户的信息

github_search 之 查询多个 GitHub 用户

按照要求返回两个符合条件的用户信息

如何使用 ?
在 vscode 中
vscode 版本更新后增加了 MCP 的相关功能 我的版本是 1.99.2

打开配置面板 (⌘,
on Mac or Ctrl+,
on Windows/Linux)
搜索 chat.agent.enabled

创建 .vscode/mcp.json
官方文档 写入如下内容
json
{
"inputs": [
{
"type": "promptString",
"id": "GITHUB_TOKEN",
"description": "GitHub token",
"password": true
}
],
"servers": {
"github-search": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@vaebe/server-github-search"],
"env": {
"GITHUB_TOKEN": "${input:GITHUB_TOKEN}"
}
},
"get_current_time": {
"type": "stdio",
"command": "node",
"args": ["xxxx/packages/time/dist/index.js"],
},
}
}
npx 是一个允许你直接运行 npm 包中命令、无需全局安装的工具
get_current_time
这个配置中 "xxxx/packages/time/dist/index.js"
是你 mcp 文件的绝对路径
细心的你可能发现了 还有一种 docker 的调用方式,这里没有写!

打开 AI 对话面板-连接 MCP

填写参数

MCP 工具列表

时间 MCP 服务调用

GitHub 搜索 MCP服务调用

在 nextjs 中使用 ai sdk 调用
具体示例可以在 ai-chat 仓库中查看
首先 ai
的版本要大于 4.2 因为 mcp clent 是 4.2 更新的内容
这个仓库是一个 AI 应用还在开发中,代码可能会发生变化
在 app/api/ai/chat/mcp/index.ts
写入 连接 MCP 服务的逻辑
ts
import { experimental_createMCPClient as createMCPClient } from 'ai'
import { Experimental_StdioMCPTransport as StdioMCPTransport } from 'ai/mcp-stdio'
/**
* 一个 MCP clent 只能连接一个 MCP server
* 所以这里我们后边可以创建多个 MCP server 函数然后导出
*/
export async function createGithubSearchMcpServer() {
// 创建 MCP client
const client = await createMCPClient({
transport: new StdioMCPTransport({
command: 'npx',
args: ['-y', '@vaebe/server-github-search'],
env: {
GITHUB_TOKEN: process.env.GITHUB_TOKEN ?? ''
}
})
})
// 获取 MCP server 的工具列表
const tools = await client.tools()
return {
client,
tools
}
}
然后在调用 AI 的地方引入使用
ts
import { createGithubSearchMcpServer } from './mcp'
// 省略其他代码
const githubSearchMcp = await createGithubSearchMcpServer()
const result = streamText({
model: openai('qwen-turbo-latest'), // 模型名称
system: '你是一个通用的智能 AI 可以根据用户的输入回答问题',
messages, // 传入用户消息历史
tools: {
...githubSearchMcp.tools
},
maxSteps: 5
})
目前看还是 MCP 服务当成 Function Call
来用了
总结
这就是我一个周的实践过程
回答了我最初的两个问题: MCP 是什么? MCP 可以干什么?
实现了两个简单的 MCP 服务, 一个用来获取当前时间、转换时区, 另一个是用于检索 GitHub 信息
做了相关测试,在 vscode 、nextjs、 ai/sdk 中使用 MCP 服务
在动手实践的过程中更容易理解 MCP 这个概念
MCP 潜力绝不止如此
参考
How to build MCP servers with TypeScript SDK
What is Model Context Protocol (MCP)? How it simplifies AI integrations compared to APIs