参考
https://zhuanlan.zhihu.com/p/1915029704936760261
https://www.5ee.net/archives/tmXJAgWz
https://github.com/modelcontextprotocol/python-sdk
https://github.com/modelcontextprotocol/typescript-sdk
https://modelcontextprotocol.io/quickstart/server
https://modelcontextprotocol.io/quickstart/server#java
什么是 MCP(Model Context Protocol)
https://modelcontextprotocol.io/introduction

LLM 使用 MCP 协议调用我们自己写的工具。
使用 Python 写一个 MCP
stdio
前置条件:
- Python3
- UV
MCP 推荐使用 UV 创建 MCP Server 项目。
https://docs.astral.sh/uv/getting-started/installation/
安装 UV:
shell
curl -LsSf https://astral.sh/uv/install.sh | sh
shell
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
安装完成后,使用如下命令显示 UV 的版本:
shell
uv --version
创建一个 MCP 项目的目录 mcp_server,命令行进入这个 mcp_server 目录,使用如下命令初始化这个项目,并指定这个项目使用的 Python 版本:
shell
uv init . -p 3.13
这里是由 uv 自己决定项目的虚拟环境,你也可以通过 uv venv you_env_name --python 3.11.5 这种方式指定创建的虚拟环境的环境名
执行后,就给你初始化好一个项目了,项目的结构如下:

.git 文件夹
:这是一个 Git 版本控制系统 的隐藏文件夹。当你在一个目录中运行 git init 或 git clone 命令时,Git 会在这里存储项目的所有版本历史、配置、分支信息等。
它的存在表明你的项目已经初始化为一个 Git 仓库,可以进行版本控制(例如,跟踪文件修改、创建分支、合并代码等)。
.gitignore
: 这是一个 Git 配置文件,用于指定哪些文件或目录应该被 Git 忽略,不被纳入版本控制。
.python-version
:这个文件通常与 **pyenv**
或其他 Python 版本管理工具相关。
main.py
:程序入口。
pyproject.toml
:这是一个在现代 Python 项目中越来越常见的配置文件,遵循 PEP 518 和 PEP 621 规范。它用于定义项目的元数据、构建系统以及依赖关系。uv
和其他现代 Python 工具(如 Rye
, Poetry
, PDM
, Hatch
)会读取这个文件来管理项目。它取代了传统的 setup.py
和 requirements.txt
在项目配置和依赖管理方面的一些功能。
安装 mcp 的 SDK:
shell
uv add "mcp[cli]"
如果后面写代码找不到依赖,需要在 Pycharm 中设置项目的解释器为项目的 .venv 目录
编写代码
在项目的根目录下创建 main.py 文件:
python
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Demo")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
@mcp.tool()
def get_today_weather() -> str:
"""get today weather"""
return "晴天"
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
"""Get a personalized greeting"""
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run(transport='stdio')
使用如下命令运行 mcp,确保不会出现报错:
shell
uv run main.py
使用 MCP
打开 cursor,配置文件中配置:
json
{
"mcpServers": {
"ts_mcp_server": {
"name": "MCP 服务器",
"type": "stdio",
"description": "",
"isActive": true,
"registryUrl": "",
"command": "uv",
"args": [
"--directory",
"E:/ai-projects/python_mcp_server/",
"run",
"main.py"
]
}
}
}
Cursor 识别成功我们的 MCP 后,我们提问,它会自发的察觉到可能需要掉用 MCP,我们点击对话中的 Run tool,它就调用我们开发的 MCP 了:


sse
编写代码
类型从 stdio 改成 sse(Server-Sent Events),这个 mcp 就使用 SSE 协议了,它的 URL 就是 server 的地址,后面加上 /sse。
还支持 streamable-http 协议,如果是 streamable-http,它的 URL 就是 server 地址加上 /mcp。
python
if __name__ == "__main__":
mcp.run(transport='sse')
使用 mcp
cursor 中添加 mcp servers:
json
{
"mcpServers": {
"server-name": {
"url": "http://localhost:3000/sse",
"headers": {
"API_KEY": "value"
}
}
}
}
json
{
"mcpServers": {
"server-name": {
"url": "http://localhost:3000/mcp",
"headers": {
"API_KEY": "value"
}
}
}
}
用 ts 编写一个 MCP
stdio
编写代码
前置条件:
- node
- ts
- npm
初始化一个项目:
shell
# 创建项目目录
mkdir ts_mcp_server
cd ts_mcp_server
# 初始化npm项目
npm init -y
# 安装依赖
npm install @modelcontextprotocol/sdk zod
npm install --save-dev typescript @types/node
# 安装 express,streamable-http 需要使用的 http 框架
npm install express
npm install --save-dev @types/express
创建 tsconfig.json 文件:
json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"outDir": "dist",
"rootDir": "src",
"sourceMap": true,
"declaration": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
创建 src/index.ts 文件:
typescript
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// 创建 MCP 服务器
const server = new McpServer({
name: "ts_mcp_server",
version: "1.0.0"
});
// 添加一个简单的打招呼工具
server.tool(
"get_today_weather",
{ name: z.string().describe("get today weather") },
async (params: { name: string }) => ({
content: [{ type: "text", text: `晴天` }]
})
);
// 添加一个加法工具
server.tool(
"add",
{
a: z.number().describe("第一个数字"),
b: z.number().describe("第二个数字")
},
async (params: { a: number, b: number }) => ({
content: [{ type: "text", text: `${params.a} + ${params.b} = ${params.a + params.b}` }]
})
);
// 启动服务器
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP 服务器已启动");
}
main().catch((error) => {
console.error("服务器启动失败:", error);
process.exit(1);
});
更新 package.json:
json
{
"name": "ts_mcp_server",
"version": "1.0.0",
"description": "ts mcp server",
"main": "dist/index.js",
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"start:http": "node dist/streamable_http.js",
"dev:http": "ts-node --esm src/streamable_http.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.13.0",
"express": "^5.1.0",
"zod": "^3.25.67"
},
"devDependencies": {
"@types/express": "^5.0.3",
"@types/node": "^24.0.3",
"typescript": "^5.8.3"
}
}
编译项目:
shell
npm run build
启动 mcp server:
shell
npm run start
使用 mcp
cursor 中添加这个 mcp:
json
{
"mcpServers": {
"ts_mcp_server": {
"name": "MCP 服务器",
"type": "stdio",
"description": "",
"isActive": true,
"registryUrl": "",
"command": "node",
"args": [
"E:/ai-projects/ts_mcp_server/build/index.js"
]
}
}
}
streamable-http
编写代码
项目中的其他代码和上面的 stdio 的例子一样,只是我们把 streamable-http 这种方式的 mcp 写在一个新文件 streamabl_http.ts 文件中:
typescript
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";
import {z} from "zod";
const app = express();
app.use(express.json());
// 用于按会话 ID 存储传输实例的映射
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
const servers: { [sessionId: string]: McpServer } = {};
// 处理客户端到服务器通信的 POST 请求
app.post('/mcp', async (req, res) => {
try {
// 检查现有会话 ID
const sessionId = req.headers['mcp-session-id'] as string | undefined;
let transport: StreamableHTTPServerTransport;
console.log('收到请求:', {
method: req.body.method,
isInitialize: isInitializeRequest(req.body),
sessionId,
headers: req.headers,
body: req.body
});
if (sessionId && transports[sessionId]) {
// 复用现有传输实例
transport = transports[sessionId];
console.log('使用现有会话:', sessionId);
} else if (req.body.method === 'initialize') {
// 新的初始化请求
const newSessionId = randomUUID();
console.log('创建新会话:', newSessionId);
// 创建新的服务器实例
const server = new McpServer({
name: "ts-streamable-server",
version: "1.0.0"
});
// 添加工具
server.tool(
"get_today_weather",
{ name: z.string().describe("获取今天的天气") },
async (params: { name: string }) => ({
content: [{ type: "text", text: `晴天` }]
})
);
server.tool(
"add",
{
a: z.number().describe("第一个数字"),
b: z.number().describe("第二个数字")
},
async (params: { a: number, b: number }) => ({
content: [{ type: "text", text: `${params.a} + ${params.b} = ${params.a + params.b}` }]
})
);
// 创建新的传输实例
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => newSessionId
});
// 存储实例
transports[newSessionId] = transport;
servers[newSessionId] = server;
// 当连接关闭时清理传输实例
transport.onclose = () => {
console.log('会话已关闭:', newSessionId);
delete transports[newSessionId];
delete servers[newSessionId];
};
// 连接到服务器
await server.connect(transport);
console.log('服务器已连接到传输层');
// 设置响应头
res.setHeader('mcp-session-id', newSessionId);
} else {
// 无效请求
console.log('无效请求:需要初始化请求或有效的会话 ID');
res.status(400).json({
jsonrpc: '2.0',
error: {
code: -32000,
message: '错误请求:需要初始化请求或有效的会话 ID',
},
id: null,
});
return;
}
// 处理请求
await transport.handleRequest(req, res, req.body);
} catch (error) {
console.error('处理请求时出错:', error);
res.status(500).json({
jsonrpc: '2.0',
error: {
code: -32000,
message: '服务器内部错误',
},
id: null,
});
}
});
// 处理 GET 和 DELETE 请求的可复用处理函数
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('无效或缺失的会话 ID');
return;
}
const transport = transports[sessionId];
await transport.handleRequest(req, res);
};
// 处理会话删除的函数
const handleDeleteSession = 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('无效或缺失的会话 ID');
return;
}
try {
// 获取传输实例
const transport = transports[sessionId];
// 关闭传输连接
if (transport.close) {
await transport.close();
}
// 清理资源
console.log('正在删除会话:', sessionId);
delete transports[sessionId];
delete servers[sessionId];
res.status(200).send('会话已成功删除');
} catch (error) {
console.error('删除会话时出错:', error);
res.status(500).send('删除会话时出错');
}
};
// 通过 SSE 处理服务器到客户端通知的 GET 请求
app.get('/mcp', handleSessionRequest);
// 处理会话终止的 DELETE 请求
app.delete('/mcp', handleDeleteSession);
app.listen(3000, () => {
console.log('MCP 服务器成功启动在端口 3000');
});
编译并启动:
shell
npm run build
npm run start:http
使用 mcp
json
{
"mcpServers": {
"ts_mcp_server_name": {
"url": "http://localhost:3000/mcp"
}
}
}
添加成功后:

使用 Java 编写一个 MCP
stdio
编写代码
前置条件:
- java17 及以上
- SpringBoot3.3.X 及以上
- Maven
从 https://start.spring.io/ 这个 spring initializr 网站或 IDEA 中创建一个 SpringBoot 项目。
添加如下依赖:
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
创建一个服务 bean:
java
package com.example.mcpserver;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
@Service
@SuppressWarnings("unused")
public class WeatherService {
@Tool(description = "get today weather")
public String getTodayWeather() {
return "晴天";
}
@Tool(description = "add two number")
public Double addTwoNumber(
@ToolParam(description = "first number") Double firstNumber,
@ToolParam(description = "Two-letter US state code (e.g. CA, NY)") Double secondNumber) {
return firstNumber + secondNumber;
}
}
在启动类中注入 bean:
java
package com.example.mcpserver;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class McpserverApplication {
public static void main(String[] args) {
SpringApplication.run(McpserverApplication.class, args);
}
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
}
}
在 idea 中打包项目或执行 maven 的打包命令:
shell
mvn package
确保项目的根目录下生成打包后的 jar 文件。
使用 mcp
json
{
"mcpServers": {
"java_mcp_server": {
"command": "C:/Users/Administrator/.jdks/corretto-17.0.13/bin/java.exe",
"args": [
"-Dspring.ai.mcp.server.stdio=true",
"-jar",
"E:/ai-projects/java_mcp_server/target/mcpserver-0.0.1-SNAPSHOT.jar"
]
}
}
}
这里的 command 配置的不是 java,因为电脑安装的 java 版本是 1.8,不能启动这个 jar 包,我改成了我的电脑上的 jdk17 的位置来执行这个 jar 包

sse
编写代码
在 stdio 代码的基础上,依赖中添加:
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
<version>1.0.0</version>
</dependency>
代码和 stdio 一致,需要将 application.yml 中修改为如下内容:
yaml
spring:
ai:
mcp:
server:
stdio: false
使用 mcp
确保项目启动。
json
{
"mcpServers": {
"java_mcp_server_name": {
"url": "http://localhost:8080/sse"
}
}
}
cursor 添加 mcp 成功后:
