Metadata-Version: 2.5
Name: logrocket-mcp-replay
Version: 0.1.0
Summary: Record MCP server requests as LogRocket sessions
Project-URL: Homepage, https://logrocket.com
Project-URL: Documentation, https://docs.logrocket.com
Project-URL: Changelog, https://github.com/LogRocket/logrocket/blob/master/python-mcp-replay/CHANGELOG.md
Author-email: LogRocket <support@logrocket.com>
License-Expression: MIT
Keywords: logrocket,mcp,model context protocol,observability,session replay
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Monitoring
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: mcp<3,>=1.9
Description-Content-Type: text/markdown

# LogRocket MCP Replay

Records the requests your MCP server handles as LogRocket sessions so you can see how users' agents interact with your product: which tools they call and with what arguments, which resources and prompts they fetch, what they got back, and where errors happen.

Works with MCP servers built on the official [`mcp`](https://pypi.org/project/mcp/) SDK, both 2.x (`MCPServer`, low-level `Server`) and 1.x (`FastMCP`, low-level `Server`), and on the standalone [`fastmcp`](https://pypi.org/project/fastmcp/) package (2.x, 3.x, 4.x), over any transport (stdio, SSE, Streamable HTTP).

## Installation

```bash
uv add logrocket-mcp-replay
# or: pip install logrocket-mcp-replay
```

## Usage

```python
import os

from logrocket_mcp_replay import instrument

server = build_my_mcp_server()

instrument(
    server,
    api_key=os.environ["LOGROCKET_INGEST_KEY"],
    get_user=lambda ctx: {"id": ctx.request.headers["x-user-id"]} if ctx.request else None,
)
```

`instrument` records every request the server handles - tool calls, resource reads, prompt gets, listings, and any handler registered afterwards - and uploads batched request events to LogRocket from a background thread. Errors raised by handlers will be surfaced as LogRocket issues.

### API key

Create an API key for your app in the LogRocket dashboard and pass it as `api_key`. The key both authenticates uploads and determines which LogRocket app sessions are recorded to. There is no separate appID to configure.

### Short-lived processes

Events are buffered and flushed when the buffer fills, on a timer, and at interpreter exit. On platforms that freeze the process as soon as a response is sent (e.g., AWS Lambda), pass a `recorder` and flush it before returning:

```python
from logrocket_mcp_replay import InstrumentOptions, RecorderOptions, create_recorder, instrument

recorder = create_recorder(RecorderOptions(api_key=os.environ["LOGROCKET_INGEST_KEY"]))
instrument(server, InstrumentOptions(api_key=os.environ["LOGROCKET_INGEST_KEY"], recorder=recorder))


def handler(event, context):
    try:
        return run_server(event)
    finally:
        recorder.shutdown()
```

## Sessions and identity

LogRocket assigns requests to sessions server-side, emulating web visits: one person using one MCP client is one session, ended by 30 minutes of inactivity. Requests are grouped per client app (name and version) by the best available identity signal:

1. The identified user, when `get_user` is provided.
2. A SHA-256 hash of the bearer token, for OAuth-protected servers. The token itself is never sent.
3. The client IP and user agent, for unauthenticated servers.
4. A random per-process ID, for transports with no HTTP request (e.g., stdio), where one server process serves one user.

Provide `get_user` for the best results. It receives the SDK's per-request context (`mcp.server.context.ServerRequestContext` on mcp 2.x, `mcp.shared.context.RequestContext` on 1.x). On both, `ctx.request` is the Starlette `Request` on HTTP transports (`None` on stdio), so the user can be resolved from headers, the validated OAuth token (`ctx.request.scope["user"].access_token`), or whatever your auth middleware attached.

W3C trace context (`traceparent`/`baggage` in `_meta`) is captured on every request and shown with the request's headers in the replay, so requests can be correlated with your tracing backend. It is not used for session grouping.

## Options

| Option               | Description                                                         |
| -------------------- | ------------------------------------------------------------------- |
| `api_key` (required) | LogRocket API key; identifies the app recorded to (see above)       |
| `server_url`         | Ingest server origin override                                       |
| `release`            | Your server's release (e.g., version, git SHA) for release tracking |
| `get_user`           | Resolve the LogRocket user from the request context                 |
| `capture_params`     | Capture request params (default `True`)                             |
| `capture_results`    | Capture request results (default `True`)                            |
| `capture_headers`    | Capture HTTP request headers (default `True`)                       |
| `redact_headers`     | Additional header names to redact                                   |
| `max_value_length`   | Max JSON-serialized length of captured values (default `100000`)    |
| `sanitize_event`     | Transform or drop (return `None`) events before upload              |
| `recorder`           | Share one recorder across `instrument()` calls                      |
| `max_batch_size`     | Flush when the buffer reaches this many events (default `25`)       |
| `flush_interval_ms`  | Timer-based flush interval (default `5000`)                         |
| `upload_timeout_ms`  | Abort an ingest upload after this long (default `10000`)            |
| `on_error`           | Callback for upload/serialization errors                            |

Options can be passed as keyword arguments to `instrument()` or as an `InstrumentOptions` instance.

## Sanitization

HTTP request headers are captured so they appear alongside each request in the Network view. Credential-bearing headers (`Authorization`, `Cookie`, `X-Api-Key`, and similar) are always replaced with `[REDACTED]`. This can't be disabled. Use `redact_headers` to redact additional headers, or `capture_headers=False` to skip header capture entirely.

Params and results are truncated to `max_value_length` by default. To handle requests containing sensitive data, use `sanitize_event` to redact fields or drop events entirely:

```python
def sanitize(event):
    if event["method"] == "tools/call" and event.get("target") == "create_payment":
        return {**event, "params": None}
    return event


instrument(server, api_key=os.environ["LOGROCKET_INGEST_KEY"], sanitize_event=sanitize)
```
