tbcontracts-mcp
# tbcontracts-mcp
An MCP (Model Context Protocol) server that exposes
[`token-budget-contracts`](https://pypi.org/project/token-budget-contracts/)
as tools, so any MCP-aware client (Claude Code, Claude Desktop, Cursor,
etc.) can manage token budgets across a multi-agent LLM system.
## What this actually does
Multi-agent orchestrators (a planner spawning a researcher, a writer, a
critic, ...) burn tokens unevenly. A high-priority agent can starve
mid-task while a low-priority agent sits on unused budget. This server's
one job is answering, in real time: **given these agents' priorities and
current spend, how should the remaining budget move right now?**
It does this by wrapping `token-budget-contracts`'
priority-weighted `Reallocator`: spare budget flows from idle or
lower-priority agents to whichever agent is actually starved, never
below a donor's protected minimum reserve, and never "uphill" from a
more important agent to a less important one.
This is **not** a cost-tracking dashboard or a network gateway. See
[Honest scope](#honest-scope) below.
## Install
```bash
pip install tbcontracts-mcp
```
This pulls in `token-budget-contracts>=0.3.0` and `opentelemetry-api` as
dependencies. Requires Python 3.10+ (see [Why not Python 3.9](#why-not-python-39)).
## Tools
| Tool | What it does |
|---|---|
| `register_agent` | Register an agent with a priority weight and initial token budget. |
| `record_spend` | Record raw input/output tokens an agent consumed for a task. Automatically triggers priority-weighted reallocation if the agent goes over budget. |
| `get_remaining_budget` | Look up one agent's current remaining budget, priority, and reserve. |
| `request_reallocation` | **The core tool.** Given an agent that needs more tokens right now, runs the real priority-weighted borrowing logic and returns a concrete plan: which agents gave up how much, which agent received it, and why each donor was eligible. |
| `get_budget_snapshot` | Full current state of every registered agent, as structured JSON. |
Every tool takes a strict, typed JSON input schema and returns structured
JSON (`{"success": true/false, ...}`) - never free text - so a calling
agent or orchestrator can parse the result programmatically.
### Error handling
Unknown agent IDs, invalid input, and budget-exceeded conditions all come
back as a structured error, never a stack trace:
```json
{
"success": false,
"error": {
"type": "unknown_agent",
"message": "Agent 'ghost' was never registered. Call register_agent first.",
"agent_id": "ghost",
"known_agents": ["critic", "researcher"]
}
}
```
Error `type` is one of `unknown_agent`, `invalid_input`, `budget_exceeded`,
`tbcontracts_error`, or `internal_error`. A bad tool call never crashes
the server process - the MCP client keeps working.
## Quick start (as a library, for testing)
```python
from tbcontracts_mcp import server
server.register_agent(agent_id="researcher", priority=3, max_tokens=4000)
server.register_agent(agent_id="critic", priority=1, max_tokens=2000)
server.record_spend(agent_id="researcher", input_tokens=3800, output_tokens=100)
# -> over budget by 400 tokens; automatically borrows from critic
plan = server.request_reallocation(agent_id="researcher", tokens_needed=1000)
print(plan)
```
Normally you won't call these functions directly - an MCP client calls
them as tools over stdio. See the client configs below.
## Using it from an MCP client
The server runs over stdio and needs no network setup - just point your
client at the `tbcontracts-mcp` command.
### Claude Code
```bash
claude mcp add tbcontracts -- tbcontracts-mcp
```
Or add it directly to `.mcp.json`:
```json
{
"mcpServers": {
"tbcontracts": {
"command": "tbcontracts-mcp"
}
}
}
```
### Claude Desktop
Add to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"tbcontracts": {
"command": "tbcontracts-mcp"
}
}
}
```
### Cursor
Add to `.cursor/mcp.json`:
```json
{
"mcpServers": {
"tbcontracts": {
"command": "tbcontracts-mcp"
}
}
}
```
Any of these can equally run it via `python -m tbcontracts_mcp` instead of
the console script, e.g. if you've installed it into a specific venv:
```json
{
"mcpServers": {
"tbcontracts": {
"command": "/path/to/venv/bin/python",
"args": ["-m", "tbcontracts_mcp"]
}
}
}
```
## Observability
Spend recorded through `record_spend` is emitted two ways, both additive
to your existing observability stack rather than replacing it:
1. **The underlying library's own OTel spans.** `token-budget-contracts`
already instruments every governance decision (registration, spend,
reallocation) as an OpenTelemetry span when telemetry is enabled. This
server wires `record_spend` straight through that existing hook rather
than building a parallel tracer - set `TBCONTRACTS_MCP_OTEL=1` in the
server's environment to turn it on (uses the global OTel tracer
provider; configure your exporter the usual OTel way).
2. **A `gen_ai.client.token.usage` counter**, following the emerging
OpenTelemetry `gen_ai.*` semantic conventions, emitted via
`opentelemetry-api` for every `record_spend` call. This tracks **raw
input/output token counts per agent**, not pre-computed dollar cost -
pricing tables change constantly and a token counter shouldn't be
coupled to one. Attach whatever OTel `MeterProvider`/exporter you like
in the process that launches this server; if none is configured, this
is a no-op.
## Honest scope
`tbcontracts-mcp` is the allocation-decision layer for one thing:
priority-weighted budget reallocation between agents you've already told
it about. It is meant to be composed with other tools, not to replace
them. Specifically, it does **not**:
- **do cross-provider cost tracking.** It emits raw token counters, not
dollar costs, and has no notion of a pricing table for OpenAI,
Anthropic, or anyone else.
- **do network-level rate limiting or gateway routing.** It doesn't sit
in the request path between your app and an LLM provider, and it can't
throttle or route calls. Tools like Bifrost, MuleSoft, or Solo.io's
gateways already do that well - use one of those alongside this.
- **replace an observability platform.** It emits spans/counters you can
send to Grafana, Datadog, Honeycomb, etc., but it isn't a dashboard,
storage backend, or alerting system itself.
What it does do: given the agents you've registered and their current
spend, decide - and actually execute - how unused budget should move
between them right now, based on priority.
## Why not Python 3.9?
`token-budget-contracts` itself supports Python 3.9+, but the official
`mcp` Python SDK this server depends on has never supported Python 3.9
(it requires 3.10+ on every released version). This package therefore
requires Python 3.10+, even though the library it wraps does not.
## Development
```bash
git clone https://github.com/swaranshu-borgaonkar/tbcontracts-mcp
cd tbcontracts-mcp
pip install -e ".[dev]"
pytest -v
```
## License
MIT for the code in this package. `token-budget-contracts`, which this
server wraps, implements the governance model described in a pending
U.S. provisional patent application (see its own README for details). If
you plan to use this commercially at scale, consult your own counsel.
TDQS
Scored across 5 tools
Each tool maps to a distinct action: registration, spend recording, single-agent read, system-wide read, and reallocation. Although record_spend can trigger reallocation as a side effect, the descriptions clearly separate that automatic behavior from the explicit request_reallocation decision tool.
All tool names follow a consistent snake_case verb_noun pattern: register_agent, record_spend, get_remaining_budget, get_budget_snapshot, request_reallocation. The verb and noun choices are predictable and match each tool's purpose.
Five tools is well-scoped for a narrow budget-management domain. Each tool earns its place, covering registration, spending, inspection, and reallocation without redundancy or bloat.
The core lifecycle is covered: register agents, record spend, inspect individual and global state, and manually reallocate budget. Minor gaps include no explicit unregister/agent removal or direct priority adjustment without re-registering/resetting, but these are workable limitations rather than blocking dead ends.