Skip to content

Commit 1c90e37

Browse files
authored
docs: add mcp toturial (#212)
1 parent 070763d commit 1c90e37

4 files changed

Lines changed: 370 additions & 0 deletions

File tree

website/src/content/docs/docs/tutorials/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Tutorials are hands-on lessons. Use this section when you want to learn a workfl
1010
## What is in this section
1111

1212
1. [Observe Bub with tapes and Jaeger](/docs/tutorials/observability/) — inspect Bub's own tape first, then export Logfire/OpenTelemetry traces to Jaeger.
13+
2. [Connect MCP Servers with bub-mcp](/docs/tutorials/mcp/) — install the MCP plugin, wire up a time server, and call it from a Bub turn.
1314

1415
## Next steps
1516

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
---
2+
title: Connect MCP Servers with bub-mcp
3+
description: Install the bub-mcp plugin, register a time MCP server, and call its tools from a Bub turn.
4+
sidebar:
5+
order: 2
6+
---
7+
8+
This tutorial wires a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server into Bub through the `bub-mcp` plugin. MCP servers expose external capabilities — APIs, local tools, data sources — that Bub can call as tools during a turn.
9+
10+
By the end you will have the official `mcp-server-time` registered with Bub and verified as connected. From there, swapping in any other stdio, HTTP, or SSE server is a one-line change.
11+
12+
## Before you begin
13+
14+
You need:
15+
16+
- Bub installed and runnable with `uv run bub --help`.
17+
- [`uv`](https://docs.astral.sh/uv/) on `PATH` so `uvx mcp-server-time` can launch the [time MCP server](https://pypi.org/project/mcp-server-time/) on demand.
18+
- A working model provider if you want to call the MCP tool from a real turn (see the final section).
19+
20+
## 1. Install bub-mcp
21+
22+
`bub-mcp` lives in [`bubbuild/bub-contrib`](https://github.com/bubbuild/bub-contrib) and is not on PyPI. Use `bub install` — it resolves bare names against bub-contrib when given an `@<ref>` suffix:
23+
24+
```bash
25+
bub install bub-mcp@main
26+
```
27+
28+
This requires Bub to be running inside a virtualenv (see [`bub install`](/docs/reference/cli/#bub-install)); activate it first if needed.
29+
30+
Verify the plugin loaded:
31+
32+
```bash
33+
uv run bub hooks
34+
```
35+
36+
You should see `mcp` listed alongside `builtin`:
37+
38+
```text
39+
load_state: builtin, mcp
40+
provide_channels: builtin, mcp
41+
register_cli_commands: builtin, mcp
42+
```
43+
44+
If `mcp` is missing, the plugin landed in a different environment than the one `uv run bub` resolves.
45+
46+
## 2. Register the time server
47+
48+
`bub-mcp` reads server definitions from `~/.bub/mcp.json` (or `$BUB_HOME/mcp.json` when `BUB_HOME` is set). Create the file with one entry that launches `mcp-server-time` over stdio:
49+
50+
```bash
51+
mkdir -p ~/.bub
52+
cat > ~/.bub/mcp.json <<'EOF'
53+
{
54+
"mcpServers": {
55+
"time": {
56+
"command": "uvx",
57+
"args": ["mcp-server-time"]
58+
}
59+
}
60+
}
61+
EOF
62+
```
63+
64+
For stdio servers, `command` is required; `args` and `env` are optional. The presence of `command` selects stdio — there is no `transport` field on stdio entries.
65+
66+
## 3. Verify the server is connected
67+
68+
```bash
69+
uv run bub mcp list
70+
```
71+
72+
Expected output:
73+
74+
```text
75+
🔌 MCP Tools
76+
- time
77+
Status: Connected
78+
Tools: mcp.time_get_current_time, mcp.time_convert_time
79+
```
80+
81+
`Status: Connected` means `bub-mcp` started the child process, completed the MCP handshake, and discovered the server's tools. Each remote tool is exposed to Bub under the prefix `mcp.<server>_<tool>`.
82+
83+
If you see `Status: Disconnected`, run the launch command directly to debug it:
84+
85+
```bash
86+
uvx mcp-server-time
87+
```
88+
89+
The process should start without exiting. Press `Ctrl-C` to stop it, fix the underlying issue, then re-run `bub mcp list`.
90+
91+
## 4. Use the MCP tool from a running turn
92+
93+
`bub mcp list` is enough to confirm the integration. Calling the tool from a real turn requires Bub's **channel runtime**, which only `bub gateway` starts:
94+
95+
- `bub run` and `bub chat` do **not** start any `Channel`. The `mcp.lifecycle` channel that owns the MCP servers never boots, so MCP tools are not exposed to the model in those commands.
96+
- `bub gateway` starts every channel returned by the `provide_channels` hook (subject to `--enable-channel` / `BUB_ENABLED_CHANNELS`). With `mcp.lifecycle` enabled, the channel boots in the background, registers each remote tool into the global tool registry as `mcp.<server>_<tool>`, and from then on the model can call them.
97+
98+
Run the gateway with both the input channel and the MCP lifecycle channel enabled:
99+
100+
```bash
101+
uv run bub gateway --enable-channel cli --enable-channel mcp.lifecycle
102+
```
103+
104+
Wait a few seconds after `channel.manager started listening` so the MCP bootstrap can complete, then ask Bub a question that needs the time server (for example, `What time is it right now in UTC?`). The model should call `mcp.time_get_current_time` and include the result in its reply.
105+
106+
If the model answers without calling the MCP tool, the bootstrap had not finished yet when the turn started — wait longer or send a warm-up message first. The bootstrap is asynchronous (`asyncio.create_task`), so it does not block channel startup, but it also does not block the first turn.
107+
108+
For long-running deployments, see [Deploy](/docs/operate/deploy/) — the same gateway invocation is what runs in the container image.
109+
110+
## Add other server types
111+
112+
Edit `~/.bub/mcp.json` to add more entries under `mcpServers`. Each transport has its own shape.
113+
114+
**HTTP**`url` plus `transport: "http"`, with optional `headers`:
115+
116+
```json
117+
{
118+
"weather": {
119+
"url": "https://weather.example.com/mcp",
120+
"transport": "http"
121+
}
122+
}
123+
```
124+
125+
**SSE**`url` plus `transport: "sse"`, with optional `headers`:
126+
127+
```json
128+
{
129+
"events": {
130+
"url": "https://events.example.com/mcp",
131+
"transport": "sse",
132+
"headers": { "Authorization": "Bearer token" }
133+
}
134+
}
135+
```
136+
137+
**Another stdio server** — for example, the Node [`@modelcontextprotocol/server-filesystem`](https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem) over `npx`. Add allowed directories as positional `args`, and pass credentials through `env`:
138+
139+
```json
140+
{
141+
"filesystem": {
142+
"command": "npx",
143+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
144+
}
145+
}
146+
```
147+
148+
After saving, run `bub mcp list` to confirm each new server connects.
149+
150+
### CLI alternatives
151+
152+
If you prefer the CLI over editing JSON, `bub mcp add` writes the same entries:
153+
154+
```bash
155+
# stdio
156+
uv run bub mcp add --transport stdio time -- uvx mcp-server-time
157+
uv run bub mcp add --transport stdio --env API_KEY=secret example -- node ./my-server.js
158+
159+
# http / sse
160+
uv run bub mcp add --transport http weather https://weather.example.com/mcp
161+
uv run bub mcp add --transport sse --header "Authorization: Bearer token" \
162+
events https://events.example.com/mcp
163+
164+
# remove
165+
uv run bub mcp remove time
166+
```
167+
168+
`--env` is only allowed with `--transport stdio`; `--header` is only allowed with `--transport http` or `--transport sse`.
169+
170+
## Troubleshooting
171+
172+
| Symptom | Check |
173+
|---|---|
174+
| `mcp` does not appear in `bub hooks` | The plugin was installed in a different environment than `uv run bub` resolves. Re-install into the active Bub venv. |
175+
| `bub mcp list` reports `Status: Disconnected` | Run the configured `command` (or open the `url`) outside Bub and confirm it starts cleanly; the error column shows the underlying cause. |
176+
| `bub mcp add` prints `CancelledError` after `Added MCP server …` | Cosmetic only — the entry is written. Use `bub mcp list` to verify, or edit `mcp.json` by hand. |
177+
| Tool never called during a turn | Confirm `bub mcp list` shows `Status: Connected` and lists the expected tool, then ask a question that clearly maps to that tool. |
178+
| Permission denied on `mcp.json` | Verify `~/.bub/` is writable, or set `BUB_HOME` to a directory you own. |
179+
180+
## Next steps
181+
182+
- [Build plugins](/docs/build/plugins/) — write your own Bub plugins.
183+
- [Configure](/docs/operate/configure/) — Bub's config layer and environment variables.
184+
- [bub-mcp source](https://github.com/bubbuild/bub-contrib/tree/main/packages/bub-mcp) — the plugin's full source and advanced options.

website/src/content/docs/zh-cn/docs/tutorials/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ sidebar:
1010
## 本节内容
1111

1212
1. [使用 tape 与 Jaeger 观察 Bub](/zh-cn/docs/tutorials/observability/) — 先检查 Bub 自身的 tape,再把 Logfire/OpenTelemetry trace 导出到 Jaeger。
13+
2. [使用 bub-mcp 连接 MCP 服务器](/zh-cn/docs/tutorials/mcp/) — 安装 MCP 插件,接入时间服务器,并在 Bub turn 中调用。
1314

1415
## 下一步
1516

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
---
2+
title: 使用 bub-mcp 连接 MCP 服务器
3+
description: 安装 bub-mcp 插件,注册一个时间 MCP 服务器,并在 Bub turn 中调用其工具。
4+
sidebar:
5+
order: 2
6+
---
7+
8+
本教程通过 `bub-mcp` 插件,将一个 [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) 服务器接入 Bub。MCP 服务器把外部能力——API、本地工具、数据源——暴露成 Bub 在 turn 中可调用的工具。
9+
10+
完成后,你将把官方的 `mcp-server-time` 注册到 Bub 并验证连接通畅。在此基础上,替换为其他 stdio、HTTP 或 SSE 服务器只需改一行配置。
11+
12+
## 前提条件
13+
14+
你需要:
15+
16+
- 已安装 Bub 并可通过 `uv run bub --help` 运行。
17+
- 系统 `PATH` 中存在 [`uv`](https://docs.astral.sh/uv/),以便 `uvx mcp-server-time` 能按需启动 [time MCP 服务器](https://pypi.org/project/mcp-server-time/)
18+
- 如果想在真实 turn 中调用 MCP 工具,需要一个可用的模型提供商(参见最后一节)。
19+
20+
## 1. 安装 bub-mcp
21+
22+
`bub-mcp` 位于 [`bubbuild/bub-contrib`](https://github.com/bubbuild/bub-contrib) 仓库,**未发布到 PyPI**。请使用 `bub install`——当传入 `<name>@<ref>` 形式的裸名时,它会自动到 bub-contrib 中解析:
23+
24+
```bash
25+
bub install bub-mcp@main
26+
```
27+
28+
这要求 Bub 自身运行在 virtualenv 中(参见 [`bub install`](/zh-cn/docs/reference/cli/#bub-install));如未激活该 venv,请先激活。
29+
30+
验证插件已加载:
31+
32+
```bash
33+
uv run bub hooks
34+
```
35+
36+
应能看到 `mcp``builtin` 并列:
37+
38+
```text
39+
load_state: builtin, mcp
40+
provide_channels: builtin, mcp
41+
register_cli_commands: builtin, mcp
42+
```
43+
44+
如果没有 `mcp`,说明插件被装到了与 `uv run bub` 解析出的环境不同的地方。
45+
46+
## 2. 注册时间服务器
47+
48+
`bub-mcp``~/.bub/mcp.json`(设置 `BUB_HOME` 时为 `$BUB_HOME/mcp.json`)读取服务器定义。创建该文件,写入一个通过 stdio 拉起 `mcp-server-time` 的条目:
49+
50+
```bash
51+
mkdir -p ~/.bub
52+
cat > ~/.bub/mcp.json <<'EOF'
53+
{
54+
"mcpServers": {
55+
"time": {
56+
"command": "uvx",
57+
"args": ["mcp-server-time"]
58+
}
59+
}
60+
}
61+
EOF
62+
```
63+
64+
stdio 服务器中,`command` 必填;`args``env` 可选。**存在 `command` 即视为 stdio**——stdio 条目无需 `transport` 字段。
65+
66+
## 3. 验证服务器已连接
67+
68+
```bash
69+
uv run bub mcp list
70+
```
71+
72+
预期输出:
73+
74+
```text
75+
🔌 MCP Tools
76+
- time
77+
Status: Connected
78+
Tools: mcp.time_get_current_time, mcp.time_convert_time
79+
```
80+
81+
`Status: Connected` 表示 `bub-mcp` 已启动子进程、完成 MCP 握手并发现了服务器工具。每个远程工具会以 `mcp.<server>_<tool>` 的前缀暴露给 Bub。
82+
83+
如果出现 `Status: Disconnected`,先在 Bub 之外直接运行启动命令排错:
84+
85+
```bash
86+
uvx mcp-server-time
87+
```
88+
89+
进程应能正常启动而不退出,按 `Ctrl-C` 终止;修复底层问题后再次执行 `bub mcp list`
90+
91+
## 4. 在运行中的 turn 里使用 MCP 工具
92+
93+
`bub mcp list` 已经足以确认接入正确。**真正在 turn 中调用工具,需要 Bub 的 channel 运行时**——而只有 `bub gateway` 才会启动它:
94+
95+
- `bub run``bub chat` **不会**启动任何 `Channel`。承载 MCP 服务器的 `mcp.lifecycle` channel 不会启动,因此这两个命令下模型看不到 MCP 工具。
96+
- `bub gateway` 会启动 `provide_channels` 钩子返回的所有 channel(受 `--enable-channel` / `BUB_ENABLED_CHANNELS` 控制)。当 `mcp.lifecycle` 启用后,channel 会在后台启动、把每个远程工具按 `mcp.<server>_<tool>` 名称注册到全局工具表中,模型从此即可调用它们。
97+
98+
同时启用输入 channel 与 MCP 生命周期 channel 来运行 gateway:
99+
100+
```bash
101+
uv run bub gateway --enable-channel cli --enable-channel mcp.lifecycle
102+
```
103+
104+
看到 `channel.manager started listening`**等几秒**让 MCP 完成 bootstrap,再向 Bub 提一个会用到时间服务器的问题(例如 `What time is it right now in UTC?`)。模型应该会调用 `mcp.time_get_current_time` 并把结果纳入回复。
105+
106+
如果模型在没调用 MCP 工具的情况下就回答了,多半是 turn 开始时 bootstrap 还没完成——多等一会,或先发一条预热消息。bootstrap 通过 `asyncio.create_task` 异步启动,既不阻塞 channel 启动,也不阻塞首个 turn。
107+
108+
长期运行的部署请参见 [Deploy](/zh-cn/docs/operate/deploy/) —— 容器镜像跑的就是同样的 gateway 命令。
109+
110+
## 添加其他类型的服务器
111+
112+
编辑 `~/.bub/mcp.json`,在 `mcpServers` 下追加更多条目。不同传输方式的字段不同。
113+
114+
**HTTP**——`url``transport: "http"`,可选 `headers`
115+
116+
```json
117+
{
118+
"weather": {
119+
"url": "https://weather.example.com/mcp",
120+
"transport": "http"
121+
}
122+
}
123+
```
124+
125+
**SSE**——`url``transport: "sse"`,可选 `headers`
126+
127+
```json
128+
{
129+
"events": {
130+
"url": "https://events.example.com/mcp",
131+
"transport": "sse",
132+
"headers": { "Authorization": "Bearer token" }
133+
}
134+
}
135+
```
136+
137+
**另一个 stdio 服务器**——例如通过 `npx` 启动的 Node 实现 [`@modelcontextprotocol/server-filesystem`](https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem)。允许访问的目录作为位置参数写入 `args`,凭证通过 `env` 传入:
138+
139+
```json
140+
{
141+
"filesystem": {
142+
"command": "npx",
143+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
144+
}
145+
}
146+
```
147+
148+
保存后,执行 `bub mcp list` 确认每个新服务器都已连接。
149+
150+
### CLI 等价命令
151+
152+
如果你不想手动编辑 JSON,可以使用 `bub mcp add`,效果完全一致:
153+
154+
```bash
155+
# stdio
156+
uv run bub mcp add --transport stdio time -- uvx mcp-server-time
157+
uv run bub mcp add --transport stdio --env API_KEY=secret example -- node ./my-server.js
158+
159+
# http / sse
160+
uv run bub mcp add --transport http weather https://weather.example.com/mcp
161+
uv run bub mcp add --transport sse --header "Authorization: Bearer token" \
162+
events https://events.example.com/mcp
163+
164+
# 移除
165+
uv run bub mcp remove time
166+
```
167+
168+
`--env` 仅在 `--transport stdio` 下可用;`--header` 仅在 `--transport http``--transport sse` 下可用。
169+
170+
## 故障排除
171+
172+
| 症状 | 检查项 |
173+
|---|---|
174+
| `bub hooks` 中没有 `mcp` | 插件被装到了与 `uv run bub` 解析出的环境不同的地方。重新装入当前 Bub venv。 |
175+
| `bub mcp list` 显示 `Status: Disconnected` | 在 Bub 之外直接运行配置的 `command`(或访问 `url`)确认能正常启动;错误信息会显示根本原因。 |
176+
| `bub mcp add``Added MCP server …` 之后打印 `CancelledError` | 仅是表面问题——条目已写入。用 `bub mcp list` 复核,或改为手动编辑 `mcp.json`|
177+
| turn 中工具未被调用 | 确认 `bub mcp list` 显示 `Status: Connected` 且列出了预期工具,然后提一个明显需要该工具的问题。 |
178+
| `mcp.json` 权限被拒绝 | 确认 `~/.bub/` 对当前用户可写,或将 `BUB_HOME` 设到你拥有的目录后重试。 |
179+
180+
## 下一步
181+
182+
- [构建插件](/zh-cn/docs/build/plugins/) — 编写自己的 Bub 插件。
183+
- [配置](/zh-cn/docs/operate/configure/) — Bub 的配置层与环境变量。
184+
- [bub-mcp 源码](https://github.com/bubbuild/bub-contrib/tree/main/packages/bub-mcp) — 插件完整源码与高级选项。

0 commit comments

Comments
 (0)