# Azure Service Bus MCP Server — Project Context

## What This Is

An MCP (Model Context Protocol) server that exposes Azure Service Bus operations as tools to any MCP-compatible client (Claude Code, Claude Desktop, Cursor, etc.). It fills a gap in the built-in Azure MCP server, which supports read-only queue metadata but cannot send messages or purge queues.

Auth uses `DefaultAzureCredential` — no secrets are ever passed as tool arguments. It picks up an active `az login` session automatically.

## What We Built

### Tools (7 total)

| Tool | Description |
|------|-------------|
| `servicebus_send_message` | Send a single message to a queue or topic |
| `servicebus_send_batch` | Send multiple messages atomically |
| `servicebus_peek_messages` | Non-destructively peek at queue messages (returns bodies in context) |
| `servicebus_peek_messages_to_file` | Same but saves bodies to a file, returns only metadata (avoids context bloat) |
| `servicebus_purge_queue` | Delete all messages from a queue (with safety cap) |
| `servicebus_peek_subscription_messages` | Peek at topic subscription messages |
| `servicebus_peek_subscription_messages_to_file` | Same but saves bodies to a file |
| `servicebus_purge_subscription` | Delete all messages from a topic subscription (with safety cap) |

Sending to topics works via `servicebus_send_message` and `servicebus_send_batch` — the Azure SDK's `get_queue_or_topic_sender` handles both transparently.

### Key Design Decisions

- **FastMCP** — uses `mcp.server.fastmcp.FastMCP` (the high-level SDK API). The low-level async stdio wiring is handled by the SDK. Tools are registered with `@app.tool()` decorators; docstrings become the tool descriptions Claude reads.
- **`client.py`** — module-level `ServiceBusClient` cache keyed by namespace. Clients are long-lived and reused across calls. `DefaultAzureCredential` is created once and refreshes tokens internally.
- **Namespace normalisation** — short names like `shdapps-dev1-eus2-sbn` are accepted; `.servicebus.windows.net` is appended automatically.
- **Error handling** — expected failures (auth errors, size exceeded, connection issues) return descriptive strings. Unexpected exceptions propagate and are formatted by the MCP SDK.
- **Production warning** — purge tools append a warning when the namespace contains `dco1`.
- **`peek_*_to_file` pattern** — large message bodies can exhaust the context window. The `_to_file` variants write bodies to disk and return only metadata in context.

### Project Structure

```
servicebus-mcp/
├── .claude/
│   └── CLAUDE.md               # This file
├── .gitignore
├── README.md
├── pyproject.toml               # uv project config, entry point, dependencies
├── uv.lock
└── src/
    └── servicebus_mcp/
        ├── __init__.py
        ├── server.py            # FastMCP app, all @app.tool() registrations
        ├── client.py            # ServiceBusClient factory with namespace cache
        └── tools/
            ├── __init__.py
            ├── send_message.py
            ├── send_batch.py
            ├── peek_messages.py
            ├── peek_subscription_messages.py
            ├── purge_queue.py
            └── purge_subscription.py
```

### Tech Stack

- **Runtime**: Python 3.10+
- **Package manager / build**: `uv`
- **MCP SDK**: `mcp` (Anthropic's official Python SDK, using FastMCP)
- **Azure SDK**: `azure-servicebus`, `azure-identity`
- **Build backend**: `hatchling`
- **Entry point**: `servicebus-mcp` → `servicebus_mcp.server:main`

## Current State

The server is **fully implemented and working**. It is registered in `~/.claude.json` as a user-scoped MCP server and was verified connected in a live Claude Code session.

The current MCP config entry in `~/.claude.json`:

```json
"azure-service-bus": {
  "type": "stdio",
  "command": "uv",
  "args": ["run", "--directory", "/Users/mzxt2g/Documents/NavelPlace/servicebus-mcp", "servicebus-mcp"],
  "env": {}
}
```

**Note:** The `--directory` path in `~/.claude.json` may still point to `Documents/Development/`. Update it to `Documents/NavelPlace/` if so.

## What Is Left To Do

### Immediate

- [ ] Verify `~/.claude.json` path points to `NavelPlace/` (not `Development/`)
- [ ] `git init` and push to `https://github.com/BrianDeacon/servicebus-mcp`
- [ ] Test a live send + peek against a dev1 Service Bus queue (was blocked on PIM — needs `Azure Service Bus Data Receiver` or `Data Owner` role activated on `shdapps-dev1-eus2-sbn`)

### Publishing to PyPI (future)

The goal is to make setup completely frictionless so users only need:

```json
{
  "mcpServers": {
    "azure-service-bus": {
      "command": "uvx",
      "args": ["servicebus-mcp"]
    }
  }
}
```

No cloning, no paths, no `--directory`. `uvx` downloads, installs, and runs from PyPI on demand.

Steps to publish:
1. Create a PyPI account at https://pypi.org
2. Generate an API token
3. `uv build` — produces `dist/` with wheel and sdist
4. `uv publish` — uploads to PyPI (will prompt for token)
5. Update README to show the `uvx` config as the primary installation method
6. Tag a release on GitHub

### Nice-to-haves

- GitHub Actions workflow to publish to PyPI on tag push
- Test against session-enabled queues (the `session_id` parameter paths are untested)
- Consider whether `purge_*` tools should require an explicit confirmation parameter rather than relying on `max_messages` alone
