> ## Documentation Index
> Fetch the complete documentation index at: https://www.botgauge.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Trace MCP with BotGauge

> Trace MCP clients and servers with BotGauge in code or zero-code, including an LLM calling a tool on an in-process MCP server.

Records the MCP client and server.

| | |
| - | - |
| Extra | `[mcp]` |
| Verified framework versions | mcp 1.24.0 and 2.3.0 |
| Instrumentor installed by the extra | `openinference-instrumentation-mcp 2.0.13` |
| Zero-code run | Traced |

Also verified: mcp 1.24.0 with instrumentor 2.0.10. The extra no longer installs that older instrumentor. See [supported frameworks](https://www.botgauge.com/docs/integrations/overview.md) for how verification works.

## In code

Call `tracing.init()`, attach the framework instrumentor to the provider it returns, and wrap the work in `tracing.turn()` and `tracing.agent()`.

> [!NOTE]
>
> In this sample an LLM, through the OpenAI SDK, calls a tool on an MCP server running in the same process. The `[mcp]` extra traces the MCP client and server, and `[openai]` traces the LLM calls.

**1. Install**

```bash terminal theme={null}
pip install "botgauge[mcp,openai]" mcp==2.3.0 openai==3.35.1
```

**2. Add the code**

```python app.py theme={null}
import json
import os

import anyio
from botgauge import tracing
from mcp import Client
from mcp.server.mcpserver import MCPServer
from openai import AsyncOpenAI
from openinference.instrumentation.mcp import MCPInstrumentor
from openinference.instrumentation.openai import OpenAIInstrumentor

provider = tracing.init()
MCPInstrumentor().instrument(tracer_provider=provider)
OpenAIInstrumentor().instrument(tracer_provider=provider)

server = MCPServer("accounts")
llm = AsyncOpenAI()

@server.tool()
def lookup_account(account_id: str) -> str:
    """Look up an account by its id."""
    with tracing.get_tracer().start_as_current_span("lookup_account"):
        return f"account {account_id} is active"

async def ask(client: Client, question: str) -> str:
    listed = await client.list_tools()
    tools = [{"type": "function", "function": {"name": t.name, "description": t.description,
                                               "parameters": t.input_schema}} for t in listed.tools]
    messages = [{"role": "user", "content": question}]
    first = await llm.chat.completions.create(model=os.environ["OPENAI_MODEL"], messages=messages, tools=tools)
    reply = first.choices[0].message
    messages.append(reply)
    for call in reply.tool_calls or []:
        result = await client.call_tool(call.function.name, json.loads(call.function.arguments))
        messages.append({"role": "tool", "tool_call_id": call.id, "content": result.content[0].text})
    final = await llm.chat.completions.create(model=os.environ["OPENAI_MODEL"], messages=messages)
    return final.choices[0].message.content

async def main():
    async with Client(server) as client:
        return await ask(client, "Is account ACC001 active? Check with the tool.")

with tracing.turn(session_id="ses_mcp"), tracing.agent("mcp-agent"):
    answer = anyio.run(main)
print(answer)

tracing.shutdown()
```

**3. Run**

```bash terminal theme={null}
python app.py
```

Needs `BOTGAUGE_API_KEY`, `OPENAI_API_KEY`, `OPENAI_MODEL`. Set `OPENAI_BASE_URL` too if your LLM isn't api.openai.com.

## Zero-code

Leave your app unchanged and start it with `botgauge instrument`. The sample still wraps the work in `tracing.turn()` and `tracing.agent()` so spans carry session and agent ids.

> [!NOTE]
>
> In this sample an LLM, through the OpenAI SDK, calls a tool on an MCP server running in the same process. The `[mcp]` extra traces the MCP client and server, and `[openai]` traces the LLM calls.

**1. Install**

```bash terminal theme={null}
pip install "botgauge[mcp,openai]" mcp==2.3.0 openai==3.35.1
```

**2. Add the code**

```python app.py theme={null}
import json
import os

import anyio
from botgauge import tracing
from mcp import Client
from mcp.server.mcpserver import MCPServer
from openai import AsyncOpenAI

server = MCPServer("accounts")
llm = AsyncOpenAI()

@server.tool()
def lookup_account(account_id: str) -> str:
    """Look up an account by its id."""
    with tracing.get_tracer().start_as_current_span("lookup_account"):
        return f"account {account_id} is active"

async def ask(client: Client, question: str) -> str:
    listed = await client.list_tools()
    tools = [{"type": "function", "function": {"name": t.name, "description": t.description,
                                               "parameters": t.input_schema}} for t in listed.tools]
    messages = [{"role": "user", "content": question}]
    first = await llm.chat.completions.create(model=os.environ["OPENAI_MODEL"], messages=messages, tools=tools)
    reply = first.choices[0].message
    messages.append(reply)
    for call in reply.tool_calls or []:
        result = await client.call_tool(call.function.name, json.loads(call.function.arguments))
        messages.append({"role": "tool", "tool_call_id": call.id, "content": result.content[0].text})
    final = await llm.chat.completions.create(model=os.environ["OPENAI_MODEL"], messages=messages)
    return final.choices[0].message.content

async def main():
    async with Client(server) as client:
        return await ask(client, "Is account ACC001 active? Check with the tool.")

with tracing.turn(session_id="ses_mcp"), tracing.agent("mcp-agent"):
    answer = anyio.run(main)
print(answer)
```

**3. Run**

```bash terminal theme={null}
botgauge instrument python app.py
```

Needs `BOTGAUGE_API_KEY`, `OPENAI_API_KEY`, `OPENAI_MODEL`. Set `OPENAI_BASE_URL` too if your LLM isn't api.openai.com.

View MCP spans liveWatch your MCP client, server, and LLM tool calls appear in [BotGauge](https://cloud.botgauge.com).

[Try BotGauge →](https://cloud.botgauge.com)

## Related

See [versions not verified](https://www.botgauge.com/docs/integrations/versions-not-verified.md) if you run a different version, [capture and redaction](https://www.botgauge.com/docs/tracing/capture-and-redaction.md) to control exported content, and [troubleshooting](https://www.botgauge.com/docs/tracing/troubleshooting.md) if spans are missing.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.