Metadata-Version: 2.3
Name: bub-mcp
Version: 0.3.0
Summary: Expose configured MCP servers as Bub tools
Author: Frost Ming
Author-email: Frost Ming <me@frostming.com>
Requires-Dist: fastmcp>=3.2.0,<4
Requires-Dist: pydantic-settings>=2.10.1
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# bub-mcp

Expose configured MCP servers as Bub tools.

## Installation

```bash
uv pip install bub-mcp
```

## Configuration

The plugin reads MCP server definitions from a dedicated JSON file under Bub home:

- `~/.bub/mcp.json`
- or `$BUB_HOME/mcp.json` when `BUB_HOME` is set

The file must contain a top-level `mcpServers` mapping:

```json
{
  "mcpServers": {
    "weather": {
      "url": "https://weather.example.com/mcp",
      "transport": "http"
    },
    "local": {
      "command": "python",
      "args": ["./server.py"]
    }
  }
}
```

## CLI Usage

Use the CLI to inspect and manage `mcp.json`:

```bash
bub mcp list
```

Add an HTTP server:

```bash
bub mcp add --transport http weather https://weather.example.com/mcp
```

Add an SSE server with headers:

```bash
bub mcp add \
  --transport sse \
  --header "Authorization: Bearer token" \
  events \
  https://events.example.com/mcp
```

Add a stdio server with environment variables:

```bash
bub mcp add \
  --transport stdio \
  --env API_KEY=secret \
  filesystem \
  -- npx -y @modelcontextprotocol/server-filesystem /tmp
```

Remove a server:

```bash
bub mcp remove weather
```

`bub mcp add` writes the server config into `mcp.json` and performs a connection test before exiting.

## Embedding

Other Bub plugins can reuse the MCP lifecycle and tool bridge with a read-only configuration:

```python
from bub_mcp.plugin import MCPChannel

channel = MCPChannel.from_server_configs(
    {"weather": {"url": "https://weather.example.com/mcp"}}
)

# Inside the embedding application's async lifecycle:
await channel.connect()
try:
    channel.bind_agent(agent)
    # Run the agent while its MCP connections are open.
finally:
    await channel.stop()
```

Discovered tools belong to the channel and are available through `channel.tools`; discovery does
not modify `bub.tools.REGISTRY`. Bub snapshots that registry when an `Agent` is created, so changing
it afterward would not update existing agents. The plugin's `load_state` hook waits for startup
discovery and binds tools to the turn's Agent (including an explicit `_runtime_agent`). Stopping
the channel removes its bindings and restores any tools it replaced.

An embedding application can call `channel.bind_agent(agent)` after startup discovery completes,
or `await channel.bind_runtime_tools(framework, message)` from its own `load_state` hook. Both paths
use instance tools. This requires Bub's instance-tool API introduced in upstream PR #311.

The default `MCPChannel()` behavior remains backed by Bub's `mcp.json`. Read-only channels reject
`add()` and `remove()` so an embedding plugin remains the owner of its source configuration. The
standalone `MCPChannel()` construction remains unchanged. A composite plugin that must
keep unrelated components running when all MCP servers fail can subclass the channel and set
`stop_when_all_failed = False`.
