Claude Desktop 和 GitHub Copilot调用MCP Server 示例

前言

Model Context Protocol(MCP)是 Anthropic 在 2024 年末推出的开放协议,旨在标准化 AI 助手(如 Claude、GitHub Copilot)与外部工具/数据源之间的通信方式。简单来说,MCP 让你的 AI 助手不再只是"聊天",而是可以真正调用你的业务系统

本文将带你从零开始,用 TypeScript 实现一个完整的 MCP Server,功能包括:

  • 搭建一个本地 Mock REST API(模拟图书馆管理系统)
  • 用 TypeScript 实现 MCP Server,注册多个业务工具(Tools)
  • 支持 stdio 模式连接 Claude Desktop
  • 支持 https 模式连接 Claude Desktop
  • 支持 HTTP+SSE 模式连接 GitHub Copilot(VS Code)
  • 通过自然语言对话完成借书、还书、查询等操作

一、什么是 MCP?

MCP(Model Context Protocol)协议定义了以下核心概念:

概念 说明
Server 提供工具/资源的服务端,即我们要实现的部分
Client AI 助手(Claude、Copilot),发起工具调用
Tool 一个可被 AI 调用的函数,有名称、描述和参数 Schema
Transport 通信方式:stdio(本地子进程)或 HTTP+SSE(远程)

工作流程如下:

复制代码
用户自然语言输入
       ↓
   AI 助手(Client)
       ↓  ListTools(发现可用工具)
   MCP Server
       ↓  CallTool(执行工具)
   业务逻辑 / REST API
       ↓  返回结果
   AI 助手(生成回答)
       ↓
  用户看到结果

二、项目结构

复制代码
mcp-demo/
├── src/
│   ├── server.ts      # MCP Server 核心:注册工具 + 处理调用
│   ├── apiClient.ts   # 封装对 Mock REST API 的 HTTP 请求
│   └── mock-api.ts    # Mock REST API Server(Express)
├── .env.example
├── .vscode/
│   └── mcp.json       # GitHub Copilot MCP 配置
├── package.json
└── tsconfig.json

三、搭建 Mock REST API

我们用 Express 搭建一个图书馆管理系统,提供以下端点:

复制代码
GET  /api/books              获取所有书籍
GET  /api/books/search?q=    搜索书籍
GET  /api/books/:id          书籍详情
POST /api/books              新增书籍
GET  /api/users              获取用户列表
GET  /api/users/:id/borrows  用户借阅记录
POST /api/borrows            借阅书籍
PUT  /api/borrows/:id/return 归还书籍
GET  /api/stats              统计信息

Mock 数据包含 7 本书和 4 个用户,完整代码见 src/mock-api.ts

关键代码片段(借阅接口):

typescript 复制代码
app.post('/api/borrows', (req: Request, res: Response) => {
    const { userId, bookId } = req.body;
    const book = books.find(b => b.id === bookId);

    if (book!.availableCopies <= 0) {
        res.status(400).json({ success: false, error: '该书籍当前无可借副本' });
        return;
    }

    book!.availableCopies -= 1;
    // 创建借阅记录,设置30天归还期限
    const due = new Date();
    due.setDate(due.getDate() + 30);
    const record: BorrowRecord = { ... };
    res.status(201).json({ success: true, data: record, message: `借阅成功,请于 ${record.dueDate} 前归还` });
});

四、实现 MCP Server

4.1 安装依赖

bash 复制代码
npm init -y
npm install @modelcontextprotocol/sdk axios express dotenv
npm install -D typescript ts-node @types/node @types/express cross-env

@modelcontextprotocol/sdk 是官方 SDK,提供 ServerStdioServerTransportSSEServerTransport 等核心类。

4.2 tsconfig.json

json 复制代码
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "moduleResolution": "node",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}

4.3 创建 MCP Server 实例

核心是 createMcpServer() 函数,创建 Server 实例并注册两个 Handler:

typescript 复制代码
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';

function createMcpServer(): Server {
    const srv = new Server(
        { name: '图书馆MCP助手', version: '1.0.0' },
        { capabilities: { tools: {} } },
    );

    // Handler 1:返回工具列表
    srv.setRequestHandler(ListToolsRequestSchema, async () => ({
        tools: [
            {
                name: 'list_books',
                description: '获取图书馆所有书籍列表',
                inputSchema: { type: 'object', properties: {}, required: [] },
            },
            {
                name: 'search_books',
                description: '按书名或作者关键词搜索书籍',
                inputSchema: {
                    type: 'object',
                    properties: {
                        query: { type: 'string', description: '搜索关键词' },
                        category: { type: 'string', description: '(可选)分类过滤' },
                    },
                    required: ['query'],
                },
            },
            // ... 更多工具
        ],
    }));

    // Handler 2:执行工具调用
    srv.setRequestHandler(CallToolRequestSchema, async (req) => {
        const { name, arguments: args = {} } = req.params;

        try {
            let result: object;
            switch (name) {
                case 'list_books':
                    result = await listBooks();
                    break;
                case 'search_books':
                    result = await searchBooks(args['query'] as string, args['category'] as string);
                    break;
                // ... 其他 case
            }
            return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };

        } catch (err) {
            return { content: [{ type: 'text', text: `调用失败: ${err}` }], isError: true };
        }
    });

    return srv;
}

关键设计 :将 createMcpServer() 提取为独立函数,使 stdio 和每个 SSE session 都能复用同一套 Handler 逻辑------这与 tamale-mcp-ts 的设计思路一致。

4.4 双 Transport 模式

typescript 复制代码
const TRANSPORT = process.env['TRANSPORT'] || 'stdio';

if (TRANSPORT === 'http') {
    // HTTP + SSE 模式
    const app = express();
    const transports: Record<string, SSEServerTransport> = {};

    // 每个 SSE 连接创建独立的 Server 实例
    app.get('/sse', async (req, res) => {
        const transport = new SSEServerTransport('/messages', res);
        transports[transport.sessionId] = transport;

        const srv = createMcpServer();
        await srv.connect(transport);
    });

    app.post('/messages', async (req, res) => {
        const transport = transports[req.query['sessionId'] as string];
        await transport.handlePostMessage(req, res);
    });

    app.listen(PORT);

} else {
    // stdio 模式
    const srv = createMcpServer();
    const transport = new StdioServerTransport();
    await srv.connect(transport);
}

五、连接 Claude Desktop

5.1 构建项目

bash 复制代码
npm run build
# 生成 dist/server.js

5.2 配置 Claude Desktop

找到配置文件:

  • macOS : ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows : %APPDATA%\Claude\claude_desktop_config.json

添加以下配置:

json 复制代码
{
  "mcpServers": {
    "library-assistant": {
      "command": "node",
      "args": ["E:/Alex/dev/git/mcp-demo/dist/server.js"],
      "env": {
        "TRANSPORT": "stdio",
        "MOCK_API_BASE_URL": "http://localhost:4000"
      }
    }
  }
}

5.3 启动并测试

  1. 先启动 Mock API:npm run mock-api
  2. 重启 Claude Desktop

示例对话:

复制代码
用户:图书馆现在有多少书?有没有TypeScript相关的?

Claude:
图书馆共有7种书目,23本书。
关于TypeScript,有《TypeScript编程》by Boris Cherny(2021年),
目前还有1本可借,书号 b004。需要帮你借阅吗?

六、连接 GitHub Copilot(VS Code)

6.1 启动 HTTP 模式

bash 复制代码
npm run dev:http
# [MCP Server] 图书馆MCP助手已启动 (HTTP+SSE 模式)
# [MCP Server] SSE 端点:  http://localhost:3100/sse

6.2 配置 .vscode/mcp.json

在项目根目录创建 .vscode/mcp.json

json 复制代码
{
  "servers": {
    "library-assistant": {
      "type": "http",
      "url": "http://localhost:3099/mcp"
    }
  }
}

6.3 在 GitHub Copilot 中使用

  1. 打开 VS Code,进入 Copilot ChatCtrl+Shift+I
  2. 切换到 Agent 模式(点击对话框左下角的模式选择器)
  3. 可以看到 library-assistant 的工具列表

示例对话:

复制代码
用户:@library-assistant 用户u001有哪些借阅记录?

Copilot:
用户张三(u001)的借阅记录:

当前借阅:
- 《深入理解计算机系统》(br001) --- 借于 2026-03-10,应还 2026-04-10

历史记录:
- 《JavaScript高级程序设计》(br002) --- 已于 2026-02-25 归还

七、完整演示

以下是一段完整的自然语言对话示例:

复制代码
用户:图书馆有哪些前端开发相关的书?

AI:(调用 search_books,category="前端开发")
找到2本前端开发书籍:
1. 《JavaScript高级程序设计》- Nicholas C. Zakas(2020)可借2本
2. 《TypeScript编程》- Boris Cherny(2021)可借1本

用户:帮用户u003借TypeScript编程这本书

AI:(调用 borrow_book,user_id="u003", book_id="b004")
借阅成功!《TypeScript编程》已为王五(u003)借出。
借阅日期:2026-04-12,请于 2026-05-12 前归还。
借阅记录号:br006

用户:现在图书馆的统计数据怎么样?

AI:(调用 get_stats)
图书馆概况:
- 书目总数:7种,23本
- 当前可借:16本
- 借出中:7本
- 注册用户:4人
- 活跃借阅:6笔

八、测试效果验证 ------ 参考 Prompt

服务启动后,可以把下面这组 Prompt 依次发给 AI 助手,覆盖所有 9 个 MCP 工具,快速验证整条链路是否正常。

8.1 单工具测试 Prompt

# 目标工具 测试 Prompt
1 list_books 图书馆现在有哪些书?帮我列出全部书籍。
2 search_books(关键词) 帮我找找有没有关于 Vue 或 React 的书。
3 search_books(分类过滤) 我想看人工智能方向的书,有什么推荐?
4 get_book 帮我查一下书籍 b003 的详细信息,包括简介和 ISBN。
5 add_book 请帮图书馆新增一本书:《深入理解 TypeScript》,作者是张三,分类前端开发,出版年份 2024,共 2 本。
6 list_users 图书馆目前有哪些注册用户?帮我列出来。
7 get_user_borrows 用户 u002 借了哪些书?有没有还没还的?
8 borrow_book 我是用户 u001,想借书籍 b005,帮我办理借阅。
9 return_book 帮我归还借阅记录 br001 对应的书籍。
10 get_stats 图书馆现在总共有多少本书、多少用户、借出去了几本?给我看看整体统计。

8.2 完整流程串联 Prompt

先查一下图书馆统计,然后搜索"软件工程"相关的书,找到后帮用户 u001 借阅其中一本,最后再看看他的借阅记录。

这条 Prompt 会触发 AI 依次调用 get_statssearch_booksborrow_bookget_user_borrows,完整走通一次借书流程,适合演示和冒烟测试。


九、连接 Claude Desktop(HTTPS 隧道)

Claude Desktop 的「Add custom connector」要求 URL 必须以 https:// 开头,但我们本地的 MCP Server 是普通 HTTP。

解决思路:用 SSH 反向隧道,让 localhost.run 服务器负责 HTTPS 加密,流量经隧道转发到本地 HTTP 服务:

复制代码
Claude.ai
   │  HTTPS(由 localhost.run 提供证书和加密)
   ▼
localhost.run 服务器
   │  SSH 反向隧道(加密 SSH 通道内转发明文 HTTP)
   ▼
localhost:3099   ← 本地 MCP Server,只需普通 HTTP

关键理解 :本地服务只需跑 HTTP,不需要配置任何 SSL 证书。HTTPS 由 localhost.run 在公网侧提供,你的代码无需任何改动。

9.1 三步启动(需三个终端同时运行)

终端 1 --- 启动 Mock REST API(图书馆数据,监听 :4000)

bash 复制代码
npm run mock-api
# Mock Library API 已启动: http://localhost:4000

终端 2 --- 启动 MCP Server(监听 :3099,处理 Claude 的工具调用)

bash 复制代码
npm run dev:http
# [MCP Server] Streamable HTTP : http://localhost:3099/mcp   ← Claude.ai Web
# [MCP Server] SSE (旧式)      : http://localhost:3099/sse   ← GitHub Copilot

终端 3 --- 建立 SSH 隧道(将 localhost:3099 暴露为公网 HTTPS)

powershell 复制代码
# 基础用法(免费,每次 URL 随机)
ssh -o ServerAliveInterval=60 -R 80:localhost:3099 localhost.run

# 如已生成专属 Key(可获得固定域名)
ssh -o ServerAliveInterval=60 -i "$env:USERPROFILE\.ssh\id_localhost_run" -R 80:localhost:3099 localhost.run

隧道建立后终端输出:

复制代码
Connect to https://74a517ac28596c.lhr.life

9.2 在 Claude Desktop 中添加 Connector

  1. 打开 [claude desktop] → 左上角 → CustomizeConnectors

  2. 点击 Add custom connector

  3. URL 填入(注意末尾是 /mcp,使用 Streamable HTTP 协议):

    复制代码
    https://74a517ac28596c.lhr.life/mcp
  4. 点击保存,对话框中即可看到图书馆工具

注意:免费版 localhost.run 的 URL 每次重启 SSH 会变化,需重新在 Claude Desktop 中更新地址。

9.3 备选方案:ngrok

如不方便使用 localhost.run,也可以用 ngrok(需安装):

bash 复制代码
scoop install ngrok   # Windows
brew install ngrok    # macOS

ngrok http 3099
# Forwarding  https://a1b2-123-45-67-89.ngrok-free.app -> http://localhost:3099
对比 localhost.run ngrok
需要安装 ❌ 不需要(用系统 SSH) ✅ 需要安装
免费版限制 隧道不定期断开 有连接数/速率限制
固定域名 付费版支持 付费版支持
协议支持 HTTP/HTTPS HTTP/HTTPS/TCP

十、关键设计要点

1. Tool 的 description 要写清楚

AI 依赖 description 字段决定何时调用哪个工具,写得越清楚,AI 的判断越准确:

typescript 复制代码
{
    name: 'search_books',
    // ✅ 好的描述:说明用途、参数含义、返回内容
    description: '按书名或作者关键词搜索书籍,可选择按分类过滤。返回匹配书籍列表。',
    // ❌ 差的描述
    // description: '搜索',
}

2. 错误处理要返回 isError: true

typescript 复制代码
} catch (err) {
    return {
        content: [{ type: 'text', text: `调用失败: ${err}` }],
        isError: true,  // 告诉 AI 这是错误,需要处理
    };
}

3. stdio vs HTTP+SSE 选择

场景 推荐 Transport
Claude Desktop 本地使用 stdio
GitHub Copilot / VS Code http(SSE)
多用户 / 远程部署 http(加 API Key 鉴权)

十一、总结

本文实现了一个完整的 MCP Server Demo,核心步骤:

  1. Mock REST API:用 Express 模拟业务接口
  2. API Client:封装 HTTP 调用
  3. MCP Server :注册工具列表(ListToolsRequestSchema)+ 处理调用(CallToolRequestSchema
  4. 双 Transportstdio for Claude Desktop,HTTP+SSE for GitHub Copilot
  5. 配置文件claude_desktop_config.json + .vscode/mcp.json

MCP 的核心价值在于:让 AI 从"聊天机器人"变成真正能操作你系统的自动化助手,而你只需要实现标准的工具接口即可。

十二、程序运行截图

Claude Desktop

Github Copilot:

十三、源码

MCP_Demo_Code 已上传


参考资料

相关推荐
一条19962 小时前
如何设计一个可维护的 PHP 后台系统?分层架构实践
github
洛卡卡了2 小时前
Hermes Agent 火了,我也把它从安装到飞书聊天跑了一遍
人工智能·aigc·ai编程
探物 AI3 小时前
虾破苍穹(一):RTX 3060 养一只本地“呆呆”龙虾 [特殊字符]
人工智能·ai编程
陈苏同学3 小时前
匿名Github (Anonymous Github - 应对双盲评审 - 制作匿名链接)
github
财经资讯数据_灵砚智能3 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年4月12日
大数据·人工智能·信息可视化·自然语言处理·ai编程
darkb1rd3 小时前
gemma-tuner-multimodal:实战
开源·github·好物分享
拖孩3 小时前
我用 AI 搓了一个"比谁更持久"的微信小游戏,AI实现只用了一天,微信审核却用了一个月!!!
微信小程序·ai编程·游戏开发
Ts-Drunk3 小时前
[特殊字符]深度解剖!Hermes-Agent 源码全解析(架构+核心流程+二次开发指南)
人工智能·架构·ai编程·hermes
恋猫de小郭3 小时前
AI 的公开测评得分都在作弊,就像泡面的封面,一切以实物为准
前端·人工智能·ai编程