Skip to main content
Glama
README.md
# Basic MCP Server

一个小而完整的 MCP(Model Context Protocol)示例项目,基于 TypeScript 和官方 MCP SDK v2。

这个仓库既提供可直接运行的最终服务器,也提供从一个 Tool 开始的渐进课程:

- [学习路径](docs/learning-path.md):六个可独立运行的课程示例;
- [架构与协议边界](docs/architecture.md):组件图、原语对比和交互时序;
- [练习与验收标准](docs/exercises.md):从基础到 OAuth/Tasks 扩展;
- [贡献指南](CONTRIBUTING.md):教学示例的代码与测试约定。

第一次接触 MCP 时,建议从 `examples/01-minimal-tool.ts` 开始,不要直接阅读完整的 `src/server.ts`。

## 已实现能力

- Tools:`add`、`echo`、`current-time`、`multiply`、进度/取消示例 `process_steps`,以及持久化 Todo 工具 `create_task`、`list_tasks`、`complete_task`、`delete_task`。
- Resources:示例资源 `demo://about`、`demo://greeting/{name}`,以及任务资源 `tasks://board`、`tasks://items/{id}`。
- Subscriptions:任务变化时发布 Resource 内容更新和列表变化通知,支持 stdio 与 Streamable HTTP。
- Prompts:`summarize-text`、`review-code`。
- Completions:为 `review-code.language` 提供参数候选值。
- Transports:本地客户端常用的 stdio,以及远程/服务化使用的 Streamable HTTP。
- HTTP 安全:Bearer Token 身份认证,并按用户隔离 Tool、Resource 和订阅通知。
- Storage:Node.js 内置 SQLite;默认数据库文件为 `data/tasks.db`,可通过 `MCP_DB_PATH` 修改。所有任务查询都带 `user_id`。
- 工程质量:TypeScript 严格模式、集成测试、健康检查、优雅退出、stdio 日志隔离。

## 环境

- Node.js 24+(当前 WSL 已通过 nvm 安装 Node 24 LTS)
- npm

新开终端后如果 `node --version` 仍显示旧版本,请执行:

```bash
source ~/.nvm/nvm.sh
nvm use --lts
```

## 安装与验证

```bash
cd /absolute/path/to/mcp
npm install
npm run check
npm test
npm run build
```

运行教学客户端,一次观察 Tool、Resource、Prompt、Completion、Progress 和 `input_required`:

```bash
npm run demo
```

生成内置测试覆盖率报告:

```bash
npm run test:coverage
```

## 运行

开发模式(stdio):

```bash
npm run dev
```

首次启动会自动创建 `data/tasks.db` 和 `tasks` 表。可以指定其他数据库位置:

```bash
MCP_DB_PATH=/absolute/path/tasks.db npm run dev
```

`delete_task` 会先通过 MCP `input_required` 请求客户端向用户确认,再执行永久删除。生产环境建议配置一个至少 32 字节的签名密钥:

```bash
REQUEST_STATE_SECRET='replace-with-at-least-32-bytes-secret' npm run dev
```

这个密钥用于给确认流程中的 `requestState` 签名,防止客户端篡改“正在确认删除哪条任务”。未配置时进程会生成临时随机密钥,适合本地开发,但服务重启后尚未完成的确认会失效;多进程部署必须共享同一个密钥。

编译后运行(stdio):

```bash
npm run build
npm start
```

运行 Streamable HTTP 服务:

```bash
MCP_AUTH_TOKENS_JSON='{"alice":"replace-with-a-random-token-of-at-least-32-bytes"}' \
REQUEST_STATE_SECRET='replace-with-at-least-32-bytes-secret' \
npm run start:http
# MCP:    http://127.0.0.1:3000/mcp
# Health: http://127.0.0.1:3000/health
```

`MCP_AUTH_TOKENS_JSON` 是“用户 id → Bearer Token”的 JSON 对象,可以配置多个用户:

可以用 `openssl rand -hex 32` 生成随机 Token,然后配置:

```bash
export MCP_AUTH_TOKENS_JSON='{
  "alice":"alice-random-token-with-at-least-32-bytes",
  "bob":"bob-random-token-with-at-least-32-bytes--"
}'
```

客户端访问 `/mcp` 时必须发送对应凭据:

```http
Authorization: Bearer alice-random-token-with-at-least-32-bytes
```

`/health` 保持公开,方便本地健康检查。stdio 不经过 HTTP 认证,固定使用 `local` 用户。已有数据库升级时会自动增加 `user_id`,升级前的任务归入 `local` 用户;如果希望通过 HTTP 继续访问这些任务,请为 `local` 配置 Token。

可通过环境变量修改监听地址:

```bash
MCP_HOST=127.0.0.1 MCP_PORT=8080 npm run start:http
```

非回环监听必须同时配置允许的 Host 与 Origin,值为不带 scheme 和端口的逗号分隔 hostname:

```bash
MCP_HOST=0.0.0.0 \
MCP_ALLOWED_HOSTS=mcp.example.com \
MCP_ALLOWED_ORIGINS=app.example.com \
npm run start:http
```

默认仅监听回环地址。目前的静态 Token 方案用于学习和受控环境,它不是完整的 MCP OAuth 实现。若要暴露到公网,还应使用 TLS、真正的 OAuth/OIDC 身份提供方、密钥管理、限流、审计,并配置正确的 Host/Origin 白名单。可复制 `.env.example` 查看所有环境变量。

## 任务 Tool 与 Resource

- `create_task`、`complete_task`、`delete_task` 修改 SQLite 中的任务。
- `list_tasks` 让模型主动调用工具查询任务。
- `tasks://board` 把整个任务板作为只读 JSON 上下文提供给客户端。
- `tasks://items/{id}` 通过稳定 URI 读取单个任务,例如 `tasks://items/1`。

`delete_task` 的调用是两轮完成的:

1. 客户端第一次调用时,服务器返回 `input_required`,其中包含确认表单和已签名的 `requestState`,此时数据库还没有变化。
2. 客户端取得用户选择后,MCP SDK 自动带着 `inputResponses` 和原样的 `requestState` 重试同一个 Tool。
3. 服务器验证签名、任务 id、任务标题和确认结果;只有明确接受且 `confirm: true` 时才删除。

这不是服务器私自弹窗:显示确认界面的是支持 Elicitation 的 MCP 客户端。客户端若不支持该能力,就不能完成这项交互式删除。

Tool 和 Resource 共享同一个 `TaskStore`,所以 Tool 修改任务后,再次读取 Resource 会立即得到最新数据。

HTTP 模式会从已验证 Token 中取得 `userId`,并把它注入 MCP Server。任务的创建、读取、完成和删除都使用 `user_id` 作为 SQL 条件;每个用户还有独立的 MCP handler,因此 Resource 订阅通知也不会广播给其他用户。

客户端订阅后,服务器会发布以下通知:

- `create_task`:`tasks://board` 和新任务 URI 的内容更新,同时通知 Resource 列表发生变化。
- `complete_task`:`tasks://board` 和对应任务 URI 的内容更新。
- `delete_task`:`tasks://board` 和被删除任务 URI 的内容更新,同时通知 Resource 列表发生变化。

通知只表示某个 URI 已变化,不携带完整任务数据。客户端收到通知后,可以立即重新读取 Resource,也可以只把本地缓存标记为过期。

## 使用 MCP Inspector

先编译,再启动官方检查器:

```bash
npm run build
npm run inspect
```

运行单独课程示例:

```bash
npx @modelcontextprotocol/inspector npx tsx examples/01-minimal-tool.ts
```

## 客户端配置示例

Codex/Claude Desktop/Cursor 等支持 stdio 的 MCP Host 可使用等价配置:

```json
{
  "mcpServers": {
    "basic-mcp": {
      "command": "/absolute/path/to/node",
      "args": ["/absolute/path/to/mcp/dist/src/stdio.js"],
      "env": {
        "MCP_DB_PATH": "/absolute/path/to/mcp/data/tasks.db"
      }
    }
  }
}
```

建议先执行 `npm run build`。把占位路径替换为 `which node` 和项目目录返回的绝对路径;也可以写一个固定启动脚本,由它加载 nvm。

如果 MCP Host 运行在 Windows(而服务器代码位于 WSL),可使用:

```json
{
  "mcpServers": {
    "basic-mcp": {
      "command": "wsl.exe",
      "args": [
        "-d",
        "Ubuntu-24.04",
        "--",
        "env",
        "MCP_DB_PATH=/absolute/wsl/path/to/mcp/data/tasks.db",
        "/absolute/wsl/path/to/node",
        "/absolute/wsl/path/to/mcp/dist/src/stdio.js"
      ]
    }
  }
}
```

## 项目结构

```text
src/server.ts      # MCP 能力注册和服务器工厂
src/auth.ts        # 静态 Bearer Token 配置与验证
src/task-store.ts  # SQLite 任务存储层
src/stdio.ts       # stdio 入口
src/http.ts        # Streamable HTTP 入口
src/http-server.ts # HTTP 认证、用户路由与服务器装配
test/              # 真实 MCP Client 驱动的集成测试
examples/          # 从最小 Tool 到教学 Client 的渐进示例
docs/              # 学习路径、架构和练习
```

扩展时通常只需要在 `src/server.ts` 中增加 `registerTool`、`registerResource` 或 `registerPrompt`。