Skip to main content

Lightweight telemetry SDK for MCP servers. Captures MCP tool invocations, HTTP requests, business events, and UI interactions with built-in payload sanitization.

Project description

listo-mcp-observability

Lightweight telemetry SDK for Python MCP servers. Captures MCP tool invocations, HTTP requests, business events, and UI interactions with built-in payload sanitization.

Python port of @listo-labs-ltd/mcp-observability (TypeScript).

Features

  • Easy setup — 3 lines with create_mcp_observability_easy()
  • Local dashboard — Real-time metrics at /telemetry/dashboard
  • Centralized analytics — Send to Listo in production
  • MCP tool tracking — Decorator-based (@obs.track_tool()) or programmatic wrapping
  • Payload sanitization — Redacts password, token, apiKey, secret, authorization, userid, email, phone, and more
  • Sampling — Configurable rates with guaranteed error/session capture
  • Zero runtime dependencies — Core uses only Python stdlib
  • Event categories — Classify business and UI events as conversion, engagement, impression, navigation, or system
  • Batch ingestionPOST /telemetry/events for efficient multi-event ingestion
  • Platform detection — Identify which AI platform (Claude, ChatGPT, etc.) initiated each session and tool call
  • Sink compositioncombine_sinks() to route events to multiple destinations

Installation

# From PyPI
pip install listo-mcp-observability

# With uv
uv add listo-mcp-observability

# With optional MCP SDK support
pip install "listo-mcp-observability[mcp]"

# With optional Starlette dashboard support
pip install "listo-mcp-observability[starlette]"

Quick Start

from mcp.server.fastmcp import FastMCP
from mcp_observability import create_mcp_observability_easy

mcp = FastMCP("my-server")
obs = create_mcp_observability_easy(service_name="my-server")

@mcp.tool()
@obs.track_tool("search_hotels")
async def search_hotels(query: str) -> str:
    return f"Results for {query}"

Environment Variables

Variable Description Default
LISTO_API_URL Listo API endpoint (primary)
LISTO_API_KEY API key for authentication (primary)
INSIGHTS_API_URL Deprecated, use LISTO_API_URL
INSIGHTS_API_KEY Deprecated, use LISTO_API_KEY
ENVIRONMENT or PYTHON_ENV Controls dev/production defaults dev

API

create_mcp_observability_easy()

Recommended entry point with sensible defaults:

obs = create_mcp_observability_easy(
    service_name="my-server",
    service_version="1.0.0",
    environment="dev",          # "dev" | "staging" | "production"
    sample_rate=1.0,            # 0.0 to 1.0 (errors always captured)
)

Dev mode (default): Console logging (errors only), in-memory dashboard, 100% sampling.

Production mode: No console, remote sink only, 10% sampling.

@obs.track_tool(name)

Decorator for tracking MCP tool calls:

@mcp.tool()
@obs.track_tool("my_tool")
async def my_tool(arg: str) -> str:
    ...

obs.wrap_mcp_handler(request_kind, handler, context)

Programmatic handler wrapping:

wrapped = obs.wrap_mcp_handler(
    "CallTool",
    original_handler,
    context=lambda *args, **kwargs: McpTrackingContext(
        tool_name="search",
        args=kwargs,
    ),
)

obs.record_business_event(name, *, ...)

obs.record_business_event(
    "purchase",
    properties={"amount": 99.99},
    status="ok",
    category="conversion",
    session_id="sess-123",
    device_id="dev-456",
    tenant_id="tenant-789",
)

detect_platform(hints)

Detect which AI platform is connecting to your MCP server:

from mcp_observability import detect_platform, PlatformHints, McpClientInfo

platform = detect_platform(PlatformHints(
    headers={"User-Agent": "Claude-Desktop/2.0"},
))
# → "claude"

platform = detect_platform(PlatformHints(
    client_info=McpClientInfo(name="chatgpt"),
))
# → "chatgpt"

Three-tier detection (first match wins):

  1. HTTP headers — user-agent and vendor-specific headers (x-openai-*, x-anthropic-*)
  2. _meta key prefixesopenai/, anthropic/, claude/
  3. clientInfo.name — exact or prefix match

Returns "claude", "chatgpt", or "other".

obs.record_session(action, session_id, *, platform=None)

obs.record_session("open", "session-123", platform="claude")

obs.record_ui_event(name, *, ...)

obs.record_ui_event(
    "click",
    action="button_press",
    category="engagement",
    widget_id="w1",
    device_id="dev-456",
)

EventCategory

Type alias for valid event categories:

from mcp_observability import EventCategory
# "conversion" | "engagement" | "impression" | "navigation" | "system"

combine_sinks(*sinks)

Combine multiple sinks into one with error isolation:

from mcp_observability import combine_sinks, ConsoleSink, InMemorySink

combined = combine_sinks(ConsoleSink(), InMemorySink())

Dashboard Endpoints (Starlette)

from mcp_observability import create_telemetry_routes

routes = create_telemetry_routes()
# GET  /telemetry           — JSON metrics
# GET  /telemetry/dashboard — HTML dashboard
# POST /telemetry/event     — Single UI event ingestion (8KB max)
# POST /telemetry/events    — Batch UI event ingestion (max 100 events)

Remote Sink

from mcp_observability import RemoteSink, RemoteSinkOptions

sink = RemoteSink(RemoteSinkOptions(
    endpoint="https://api.listoai.co/v1/events/batch",
    api_key="your-key",
    batch_size=50,
    flush_interval=5.0,
    max_retries=3,
))

Payload Sanitization

The SDK automatically redacts sensitive fields (case-insensitive):

  • password, token, apiKey, secret, authorization
  • userId, userLocation, email, userEmail, phone, phoneNumber

Redaction is recursive (up to 6 levels deep) and limits arrays to 20 elements.

Custom redaction keys:

obs = McpObservability(ObservabilityOptions(
    service_name="my-server",
    redact_keys=["password", "token", "credit_card", "ssn"],
))

Event Types

The SDK captures five event types:

Type Description
http_request HTTP request lifecycle
mcp_request MCP tool call, resource read, prompt get
mcp_session Session open/close (always captured)
business_event Domain-specific events with optional category, session, tenant tracking
ui_event Frontend interactions with optional category

Security

  • Sampling: Errors and sessions are always captured. All other events are subject to sample_rate.
  • Redaction: Sensitive keys are recursively replaced with [redacted] up to depth 6.
  • Data minimization: No PII fields (user_id, user_location) exist in event types. User query text is truncated and hashed.

Documentation

Full documentation is available in the docs/ directory:

Page Description
Overview Architecture, quick start
Installation pip install, prerequisites
Easy Setup create_mcp_observability_easy() deep-dive
Configuration Both init methods, full config tables
Wrap MCP Handlers wrap_mcp_handler(), track_tool(), context fields
Sessions record_session(), always-capture behavior
Business Events record_business_event(), EventCategory
UI Events record_ui_event() + POST endpoint
Sinks InMemorySink, RemoteSink, ConsoleSink, combine_sinks
Endpoints Dashboard, JSON API, event ingestion
Sampling & Privacy Sampling, redaction, data minimization
Advanced Custom sinks, metrics API
Platform Detection Detect Claude, ChatGPT, etc.
Troubleshooting Common issues, migration from v0.0.x

Development

# Install dependencies
uv sync --all-extras

# Run tests
uv run pytest -v

# Lint
uv run ruff check src/ tests/

# Type check
uv run mypy src/

# Build
uv build

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

listo_mcp_observability-0.2.2.tar.gz (39.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

listo_mcp_observability-0.2.2-py3-none-any.whl (20.9 kB view details)

Uploaded Python 3

File details

Details for the file listo_mcp_observability-0.2.2.tar.gz.

File metadata

  • Download URL: listo_mcp_observability-0.2.2.tar.gz
  • Upload date:
  • Size: 39.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for listo_mcp_observability-0.2.2.tar.gz
Algorithm Hash digest
SHA256 d105bf4bea0a8a39a9e4af181c6bcb15e4c1b2b2324424d4f96f2ed7d1a06fd9
MD5 66fd925ee881e5a0be8748d1b6e8e4a8
BLAKE2b-256 444b8735ba98a97d29d1360fc5d1338d568a7d48fe6dd3f6c2cd61794efbcbc1

See more details on using hashes here.

Provenance

The following attestation bundles were made for listo_mcp_observability-0.2.2.tar.gz:

Publisher: publish.yml on Listo-Labs-Ltd/mcp-observability-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file listo_mcp_observability-0.2.2-py3-none-any.whl.

File metadata

File hashes

Hashes for listo_mcp_observability-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 a89d92531bd2850d4edfc29565d19a440ea203dc4b05bcd3c1ab8c98dfe17c6d
MD5 2ec445499ed67e0ddf62329e0bd5ef4b
BLAKE2b-256 3506bf88b60add7abd4a00ed3a975fbd99f7ca53fd260d4dafc7e060a13add20

See more details on using hashes here.

Provenance

The following attestation bundles were made for listo_mcp_observability-0.2.2-py3-none-any.whl:

Publisher: publish.yml on Listo-Labs-Ltd/mcp-observability-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page