Surfa Ingest SDK
Analytics for MCP Servers and AI Agents - Track usage, performance, and task completion whether your server runs locally or remotely.
Why Surfa Ingest?
For MCP Server Builders
Get visibility into how your MCP server is being used - whether it's installed locally on users' machines or hosted remotely:
📊 Gain Visibility You Wouldn't Have Otherwise
- Local MCP servers: Track usage even when installed on users' machines (Claude Desktop, Cursor, etc.)
- Remote MCP servers: Get structured analytics beyond basic server logs
🎯 Understand Your Users
- Which tools are most popular
- Which AI clients are using your server (Claude, ChatGPT, Cursor)
- Real-time usage patterns and trends
- Geographic distribution and platform breakdown
🚀 Optimize Based on Real Data
- Performance metrics (latency, error rates)
- Task completion tracking (did the user actually accomplish their goal?)
- Identify bottlenecks and failure points
- A/B test improvements
💰 Enable Usage-Based Pricing
- Track API calls and tool usage
- Monitor quota consumption
- Build monetization models
🔍 Debug Issues Faster
- See exactly what's failing and where
- Trace user sessions end-to-end
- Identify retry patterns and error sequences
Privacy-First Design
- ✅ No PII collected by default
- ✅ Users can opt-out via environment variable
- ✅ You control what data is sent
- ✅ GDPR and privacy-compliant
Quick Start: one line
pip install surfa-ingest
from fastmcp import FastMCP
from surfa_ingest import with_surfa
mcp = with_surfa(FastMCP("my-server"))
Set SURFA_API_KEY (create one at app.surfa.dev → Settings → API Keys) and every tool call shows up in your dashboard: tool name, arguments (secrets masked), latency, errors, and which client called it. No per-tool decorators; tools added later are captured too.
Works with:
| Server | Versions tested |
|---|---|
fastmcp FastMCP |
2.8, 2.9, 2.14, 3.0, 3.4, 4.1 |
Official SDK mcp.server.fastmcp.FastMCP (mcp 1.x) |
1.9, 1.10, 1.26, 1.30 |
Official SDK mcp.server.mcpserver.MCPServer (mcp 2.x) |
2.3 |
Low-level mcp.server.Server (1.x and 2.x) |
as above |
It never hurts your server: events are queued in memory (bounded) and uploaded on a background thread; if Surfa is slow, down or rejects the key, tool calls behave exactly as before. Messages go to stderr only, never stdout (the stdio channel).
| Option | Env var | Default |
|---|---|---|
api_key |
SURFA_API_KEY |
required (without it, one warning and nothing is sent) |
api_url |
SURFA_API_URL |
https://app.surfa.dev |
capture_args |
True |
|
redact_keys |
extra key names to mask, on top of password/secret/token/api_key/authorization/cookie/... | |
session_idle_timeout_s |
900 |
|
flush_delay_s |
0.25 |
|
debug |
False (log uploads to stderr) |
Serverless or short-lived processes: call flush_surfa() before returning. A server with its own SIGTERM handler should call shutdown_surfa() in it.
Full guide: docs.surfa.dev/python-setup. Example: examples/fastmcp_server.py.
Manual events (advanced)
SurfaClient sends events you build yourself, for code that isn't an MCP server:
import os
from surfa_ingest import SurfaClient
with SurfaClient(ingest_key=os.environ["SURFA_API_KEY"]) as client:
client.tool_started("search", args={"query": "mcp"})
client.tool_completed("search", latency_ms=120)
For MCP servers prefer with_surfa(): it records the fields Surfa's metrics read (input, correlation_id, sessions per client connection), which manual events often miss.
What You Get
Real-Time Dashboards
- 📈 Tool usage trends
- ⚡ Performance metrics (P50, P95, P99 latency)
- ✅ Task completion rates
- 🔴 Error rates and types
- 👥 Active users and sessions
Deterministic Agent Metrics
- Task Completion: Did the user actually accomplish their goal?
- Tool Calls: How many tools were invoked?
- Retries: Repeated calls with same parameters
- Recovery: Did the agent recover from errors?
- Latency: P95 and total latency tracking
Client Intelligence
- Which AI clients are using your MCP (Claude, ChatGPT, Cursor)
- Platform distribution (macOS, Linux, Windows)
- Client versions and configurations
Key Features
- 🚀 Event Buffering - Automatic batching with configurable buffer size
- 🔄 Auto-Retry - Built-in retry logic with exponential backoff
- 📦 Context Manager - Automatic session lifecycle management
- 🏷️ Runtime Metadata - Track AI provider, model, and configuration
- ✨ MCP Context Auto-Detection (v0.2.0+) - Automatically extract client_id, session_id from MCP context
- 📊 Deterministic Agent Metrics - Track task completion, retries, recovery, and performance metrics that traditional analytics platforms don't provide
- ✅ Event Validation - Client-side validation before sending
- 🔍 Correlation IDs - Link related events together
- 🛡️ Type Safety - Full type hints and IDE autocomplete
- 🔒 Privacy-First - No PII by default, user opt-out support
What makes Surfa different: We calculate deterministic metrics like task completion, retry detection, and recovery rates automatically from your event stream - metrics that generic analytics platforms can't provide because they don't understand AI agent behavior.
Learn More
📚 Documentation
- Full API Reference - Complete method documentation
- Deterministic Metrics - How we calculate task completion, retries, and recovery
- Advanced Usage - Context managers, error handling, logging
- Examples - More code examples for different use cases
🔗 Links
- 📦 PyPI: https://pypi.org/project/surfa-ingest/
- 📝 Changelog: CHANGELOG.md
- 🐛 Issues: https://github.com/gamladz/surfa/issues
- 💬 Discussions: https://github.com/gamladz/surfa/discussions
Quick Reference
Deterministic Metrics (Auto-Calculated)
When you send events using this SDK, the Surfa platform automatically calculates these metrics for each execution:
Automatically Calculated Metrics
| Metric | Definition | How It's Calculated |
|---|---|---|
| Task Completion | Whether the task was actually completed | Uses task_completed field if present, otherwise infers from event sequence |
| Tool Calls | Total number of tool invocations | Count of tool_call_started events |
| Retries | Repeated calls with same tool+args | Detected from tool_name + payload.input matching |
| Recovery | Agent recovered from errors | First success after any failure |
| Latency | P50, P95, P99 latencies | Calculated from latency_ms distribution |
See full metrics documentation →
Development Status
Current Version: 0.2.0 (Alpha)
This SDK is in active development. The API may change in future versions.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT
Made with ❤️ for MCP builders
Metadata
Release files for surfa-ingest 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| surfa_ingest-0.3.0.tar.gz | 33.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| surfa_ingest-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 61.7 kB
Release files / surfa_ingest-0.3.0.tar.gz
| Download URL | surfa_ingest-0.3.0.tar.gz |
|---|---|
| Size | 33.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
663e21dab8d2f6dfe7bb065f366a27abb2639b9fac7f8bdcfdbb1b2411d50d6a
|
|
BLAKE2b-256 checksum How to use checksums |
2ce2f659465cddaeb9fa31eb8f79fe171d90fe760fbe0e7fd4a52f09b8fa42b5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.15 {"installer":{"name":"uv","version":"0.9.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / surfa_ingest-0.3.0-py3-none-any.whl
| Download URL | surfa_ingest-0.3.0-py3-none-any.whl |
|---|---|
| Size | 28.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2599b5c2cf79eebc15d53f871e79ab96031eca42519e8460dd2b5e3a4b3f9bde
|
|
BLAKE2b-256 checksum How to use checksums |
ea69f274543f78fd23b27cb043a062989e9da4b6a6a7694e0705d668bc2961fe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.15 {"installer":{"name":"uv","version":"0.9.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|