从零搭建拾光课程表 MCP 服务器:一次 AI 工具化的实践

2026-06-14 | 拾光课程表 MCP开发日志

前言

最近在为拾光课程表构建智能问答服务时,我接触到了 MCP(Model Context Protocol) —— 一个让 AI 大模型能够调用外部工具的开放协议。经过几天的摸索,我成功搭建了一个拾光课程表专属的 MCP 知识库服务器,并部署到了云服务器上。

这篇文章记录了整个过程:从了解 MCP 是什么,到动手写代码、踩坑、部署、调试的完整经历。


一、什么是 MCP?

MCP(Model Context Protocol) 是由 Anthropic 提出的一个开放标准协议,用于在 AI 大模型与外部工具 / 数据源之间建立标准化的连接。

简单来说:

传统方式:AI 只能基于训练数据回答问题
MCP 方式:AI 可以调用外部工具(搜索、查询数据库、读文件等)

MCP 定义了三种核心能力:

  • Tools(工具):AI 可以调用的函数,比如搜索知识库、获取数据
  • Resources(资源):AI 可以读取的数据源,比如文件、API 返回
  • Prompts(提示模板):预定义的提示词模板

MCP 的传输方式

MCP 支持两种传输模式:(Rinty 已经部署好 MCP 服务,您可直接使用 详见 五、部署与验证)

模式适用场景说明
stdio本地客户端客户端直接启动服务器进程,通过标准输入输出通信
Streamable HTTP远程/Web 客户端通过 HTTP 协议通信,支持远程部署

二、为什么要做拾光课程表的 MCP 服务器?

拾光课程表作为一个课程表应用,用户经常会问:

  • "怎么导入课表?"
  • "课程提醒不响怎么办?"
  • "怎么分享课表给同学?"
  • "支持哪些学校的教务系统?"

这些问题的答案都在我们的文档里,但分散在不同的页面和 FAQ 中。如果有一个 AI 助手能够直接从知识库中检索答案,用户体验会好很多。

MCP 正好提供了这个桥梁:让 AI 大模型能够主动查询我们的知识库,而不是凭 "记忆" 回答。


三、技术实现

项目结构

shiguang-server/
├── src/
│   └── index.ts
├── knowledge/  # 知识库(Markdown 文件)
│   ├── index.md
│   ├── faq/    # 常见问题
│   └── guide/  # 使用指南
├── Dockerfile
├── docker-compose.yml
├── package.json
└── tsconfig.json

使用技术

  • Runtime: Node.js
  • Language: TypeScript (ESM)
  • SDK: @modelcontextprotocol/sdk ^1.27.1
  • Web 框架: Express(用于 HTTP 模式)
  • 部署: Docker + Docker Compose

核心设计

1. 知识库加载

知识库使用 Markdown 文件 + YAML frontmatter 组织:

---
title: 课表导入
tags:
    - 导入
    - 教务系统
---

# 课表导入

拾光课程表支持多种方式导入课表...

服务器启动时递归扫描 knowledge/ 目录,解析所有 .md 文件,构建内存知识库:

function loadKnowledgeBase(): KnowledgeItem[] {
  const items: KnowledgeItem[] = [];
  const mdFiles = getAllMdFiles(KNOWLEDGE_DIR);
  
  for (const filePath of mdFiles) {
    const rawContent = fs.readFileSync(filePath, "utf-8");
    const { metadata, body } = parseFrontmatter(rawContent);
    // ... 提取标题、标签、内容摘要
    items.push({ title, filename, tags, content, summary });
  }
  
  return items;
}

2. 实现的 6 个 MCP 工具

工具功能使用场景
search_knowledge按关键词搜索知识库用户提问时首选
list_knowledge列出所有文档AI 了解知识库范围
get_knowledge获取文档完整内容搜索后深入阅读
fetch_url抓取网页内容获取 GitHub 等在线资源
fetch_school_list获取已适配学校列表动态数据,不缓存
edit_knowledge编辑知识库文档管理员维护内容

3. 双模式传输

同一个服务器支持两种启动方式:

# stdio 模式 —— 本地客户端(VS Code、Cursor)
node build/index.js

# HTTP 模式 —— 远程客户端(Cherry Studio、picoclaw)
node build/index.js --http 3030

通过命令行参数切换,核心代码共用同一套工具处理逻辑。


四、踩坑记录

坑 1:Accept 头不完整(在使用picoclaw时遇到的问题)

问题:部分 MCP 客户端发送请求时,Accept 头只包含 application/json,但 StreamableHTTPServerTransport 要求同时接受 application/jsontext/event-stream

表现:服务器返回 400 错误,客户端无法连接。

解决:在 Express 中间件中自动补全 Accept 头:

app.all("/mcp", async (req, res) => {
  const accept = req.headers["accept"] || "";
  if (!accept.includes("text/event-stream") || !accept.includes("application/json")) {
    req.headers["accept"] = "application/json, text/event-stream";
  }
});

坑 2:Docker 镜像过大

问题:初始镜像包含完整的 node_modules 和开发依赖,镜像体积超过 800MB。

解决:使用多阶段构建(multi-stage build):

# 阶段一:编译
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npx tsc -p tsconfig.json

# 阶段二:运行
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/build ./build
COPY knowledge ./knowledge
CMD ["node", "build/index.js", "--http", "3030"]

坑 3:跨服务器部署

问题:如果将项目完整搬移到服务器,那势必要重新部署环境,因此需要本地构建的 Docker 镜像需要部署到远程云服务器。

解决:使用 docker save / docker load 方式:

# 本地导出
docker save shiguang-mcp -o shiguang-mcp-server.tar
# 传输到服务器
scp shiguang-mcp-server.tar user@server:/opt/
# 服务器导入

五、部署与验证

Docker Compose 一键部署

services:
  shiguang-mcp:
    image: shiguang-mcp:latest
    container_name: shiguang-mcp
    restart: unless-stopped
    ports:
      - "3030:3030"
    volumes:
      - /path/to/school_index.pb:/data/school_index.pb:ro
    environment:
      - SCHOOL_INDEX_PB=/data/school_index.pb

验证 MCP 协议

# 健康检查
curl https://mcp.rinty.xyz/shiguang/health
# → {"status":"ok","service":"shiguang-mcp-server","version":"1.0.0"}

# MCP 握手
curl -X POST https://mcp.rinty.xyz/shiguang/ \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "method": "initialize",
    "params": {
      "protocolVersion": "2025-11-25",
      "capabilities": {},
      "clientInfo": {"name": "test", "version": "1.0.0"}
    },
    "id": 1
  }'
# → 200 OK + session-id

接入 picoclaw

在 picoclaw 配置中添加 MCP 服务器:

!!!注意!!!

https://monitor.rinty.xyz/
MCP 服务监测上线,已停止使用 IP 端口使用 MCP 的功能,转由https://mcp.rinty.xyz/shiguang/ 提供 MCP 服务使用https://mcp.rinty.xyz/shiguang/health 作为健康监测网址
目前 MCP 处于开发中,后续可能会开发 MCP Hub 作为 MCP 共享平台。因此本 MCP 链接不保证长期有效,若无法使用请访问监测平台获取最新 mcp 服务链接,感谢您的支持!

mcp:
  servers:
    - name: shiguang
      type: http
      url: "https://mcp.rinty.xyz/shiguang/"

如果您使用 WebUI 版的 picoclaw 的话,可在

配置 ==> MCP ==>MCP 服务

添加好您的 MCP 服务,然后就可以使用询问,例如:

回复案例一回复案例二
CBA571C5-B265-4AEF-9D00-2F6CAF452C43.png20B15431-E848-4A3C-BB5F-E259BBFCF022.png

重启后日志显示连接成功:

INF mcp > Connected to MCP server protocol=2025-11-25 server=shiguang serverName=shiguang-server
INF mcp > Listed tools from MCP server server=shiguang toolCount=6
INF mcp > MCP tools registered successfully total_registrations=6 unique_tools=6

六、思考与展望

MCP 的价值

MCP 让 AI 从 "背答案" 变成了 "查资料"。对于拾光课程表这样的应用,这意味着:

  1. 知识库可以实时更新 —— 添加新文档后,AI 立即可用,无需重新训练
  2. 答案更准确 —— AI 基于官方文档回答,而不是靠猜测
  3. 可扩展 —— 未来可以添加更多工具,比如直接查询用户的课表

MCP 的发现机制

目前 MCP 还处于早期阶段,没有统一的服务发现和注册机制。每个客户端都需要手动配置 MCP 服务器地址。我在考虑构建一个 MCP 社区 / 平台:

  • MCP Gateway(网关):统一入口,客户端只需连一个地址
  • MCP Hub(注册中心):开发者注册服务器,用户搜索/浏览/一键接入
  • 工具市场:类似 Skill 商店,降低使用门槛

下一步计划

  • 优化知识库内容,覆盖更多 FAQ
  • 探索 picoclaw 的工具调用机制,让 LLM 能正确使用 MCP 工具
  • 研究 MCP Gateway 的可行性
  • 考虑支持 MCP Resources 和 Prompts

七、相关资源


本文由拾光课程表开发者撰写,记录了从零搭建 MCP 服务器的完整过程。如果你也在考虑为自己的项目接入 AI 能力,MCP 是一个值得尝试的方向。