Skip to main content
Glama
README.md
# nanomcp

这是一个不用 MCP Python SDK 手写的最小 MCP demo。它包含完整链路:

1. `nanomcp.server` 作为 MCP server,通过 stdio 收发 JSON-RPC。
2. `nanomcp.cli` 作为 MCP client/host,启动 server,做 `initialize`、`tools/list`、`tools/call`。
3. `chat` 命令调用 OpenAI Chat Completions。模型返回 function/tool call 后,CLI 把它转成 MCP `tools/call`,再把工具结果发回模型生成最终回答。

## MCP 和 function call 的关系

一句话:function call 是“模型告诉你的应用它想调用什么函数”的模型 API 能力;MCP 是“你的应用如何用统一协议发现和调用外部工具/上下文服务”的连接协议。

更具体地说:

- Function/tool calling 发生在 `LLM API <-> 你的应用` 之间。模型不会真的执行函数,它只返回类似 `{"name":"get_weather","arguments":{...}}` 的调用意图。
- MCP 发生在 `你的应用 <-> MCP server` 之间。MCP server 暴露工具清单和执行入口,比如 `tools/list` 和 `tools/call`。
- Host/client 是中间人。它先从 MCP server 拿工具 schema,把这些 schema 转成模型 API 的 tools;模型选择工具后,host/client 再调用 MCP server。

本项目里的链路是:

```text
用户问题
  -> nanomcp.cli
  -> OpenAI Chat Completions tools=function schemas
  <- 模型返回 tool_calls
  -> nanomcp.cli 把 tool_call 映射为 MCP tools/call
  -> nanomcp.server 执行 get_weather 或 find_files
  <- MCP tool result
  -> nanomcp.cli 把结果发回模型
  <- 模型最终回答
```

所以它们不是同一个层级:

```text
Function call: 模型 API 的工具选择/参数生成机制
MCP: 应用连接工具服务器的标准协议
```

## 文件结构

```text
nanomcp/
  nanomcp/
    cli.py       # MCP client + model caller
    server.py    # hand-written MCP server over stdio
  tests/
    test_protocol.py
  pyproject.toml
  README.md
```

## 直接跑 MCP,不调用模型

在项目目录运行:

```bash
cd ~/Desktop/nanomcp
python3 -m nanomcp.cli list-tools
```

直接调用天气工具:

```bash
python3 -m nanomcp.cli call get_weather '{"location":"Shanghai","unit":"celsius"}'
```

直接调用当前日期时间工具:

```bash
python3 -m nanomcp.cli call get_current_datetime '{"timezone":"Asia/Shanghai"}'
```

直接调用文件查找工具:

```bash
python3 -m nanomcp.cli call find_files '{"query":"*.pdf","max_results":5}'
```

默认只搜索 `~/Desktop`。可以临时扩大或缩小搜索根目录:

```bash
NANOMCP_FILE_ROOT=~/Desktop/nanomcp python3 -m nanomcp.cli call find_files '{"query":"*.py"}'
```

## 跑完整模型 + MCP 链路

需要 OpenAI API key。这里没有使用 OpenAI Python SDK,而是用标准库 `urllib` 直接发 HTTP。

推荐把本地配置写进 `.env`:

```bash
cd ~/Desktop/nanomcp
cp .envtemplate .env
```

然后编辑 `.env`:

```bash
OPENAI_API_KEY=你的 key
OPENAI_BASE_URL=https://api.openai.com/v1
NANOMCP_MODEL=gpt-4.1-mini
NANOMCP_TIMEZONE=Asia/Shanghai
```

`.env` 会被 CLI 自动读取,并且已经被 `.gitignore` 忽略。

```bash
cd ~/Desktop/nanomcp
python3 -m nanomcp.cli chat "上海今天天气怎么样?顺便帮我找桌面上的 PDF 文件"
```

默认模型是 `gpt-4.1-mini`。你可以改:

```bash
NANOMCP_MODEL=gpt-5-mini python3 -m nanomcp.cli chat "找一下这个项目里的 py 文件"
```

如果你使用 OpenAI-compatible gateway:

```bash
OPENAI_BASE_URL=http://localhost:8000/v1 python3 -m nanomcp.cli chat "上海天气怎么样?"
```

轻量检查本地配置和 MCP server:

```bash
python3 -m nanomcp.cli doctor
```

## Troubleshooting

如果 `chat` 输出 `OpenAI API quota is exhausted (429 insufficient_quota)`,意思是模型 API 拒绝了请求:当前 `OPENAI_API_KEY` 所属项目没有可用额度或 billing 没开通。这不是 MCP server 失败,因为请求在模型返回 tool call 之前就被拒绝了。

排查顺序:

```bash
python3 -m nanomcp.cli doctor
echo "$OPENAI_API_KEY"
cat .env
python3 -m nanomcp.cli call get_weather '{"location":"Shanghai"}'
OPENAI_BASE_URL=http://localhost:8000/v1 python3 -m nanomcp.cli chat "上海天气怎么样?"
```

- 第一个命令脱敏显示有效配置、shell 是否覆盖 `.env`、MCP server 是否能列出工具。
- 第二个命令确认 shell 里是否已经设置了 key。
- 第三个命令确认 `.env` 里的本地配置。
- 第四个命令验证本地 MCP 链路是否正常,不依赖模型 API。
- 第五个命令演示如何切到 OpenAI-compatible gateway。
- 如果仍然使用 OpenAI 官方 API,需要更换有额度的 key/project,或检查 billing 和模型权限。

## 可选真实天气

默认天气是 deterministic demo data,方便无网络、无第三方 key 时学习协议链路。要尝试真实查询:

```bash
NANOMCP_LIVE_WEATHER=1 python3 -m nanomcp.cli call get_weather '{"location":"Shanghai"}'
```

真实天气使用 `https://wttr.in`,失败时会自动回退到 demo data。

## 测试

```bash
cd ~/Desktop/nanomcp
python3 -m unittest discover -s tests
```

测试覆盖:

- MCP `initialize`
- MCP `tools/list`
- MCP `tools/call get_weather`
- MCP `tools/call find_files`
- MCP `tools/call get_current_datetime`

## 关键观察

看 `nanomcp/cli.py` 的 `openai_tools_from_mcp()`:它把 MCP tool schema 转成 OpenAI function tool schema。

看 `run_chat()`:它收到模型 `tool_calls` 后调用 `mcp.call_tool()`。这就是 MCP 和 function call 的衔接点。

看 `nanomcp/server.py` 的 `main()`:它只读 stdin、写 stdout,每一行都是 JSON-RPC。server 不知道 OpenAI,也不直接接触模型。

TDQS

A3.7/5.0

Scored across 3 tools

Disambiguation5/5

Each tool has a clear, distinct purpose: file search, datetime, and weather. No overlap or ambiguity.

Naming Consistency5/5

All tool names follow the verb_noun snake_case pattern consistently: find_files, get_current_datetime, get_weather.

Tool Count4/5

Three tools is small but appropriate for a 'nano' server intended as a minimal utility collection. Not too few given its scope.

Completeness3/5

The tools cover only three disparate areas with no clear domain. As a general utility set, common operations like calculations or text processing are missing, but it may be intentionally limited.

Maintenance

ActivityInactive
ResponsivenessNo issues