Official Python SDK for Surfa Analytics - Ingest live traffic events
Project description
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 for MCP Builders
1. Install the SDK
pip install surfa-ingest
For different MCP distribution modes:
Local MCP (stdio) - npm/PyPI package
Add to your pyproject.toml:
[project]
dependencies = [
"surfa-ingest>=0.2.0",
"fastmcp>=2.0.0"
]
Or requirements.txt:
surfa-ingest>=0.2.0
fastmcp>=2.0.0
Users install your MCP locally:
pip install your-mcp-server
Remote MCP (SSE) - Hosted service
Add to your server's requirements.txt:
surfa-ingest>=0.2.0
fastmcp>=2.0.0
Deploy to cloud (Vercel, Railway, Fly.io, etc.):
# Set environment variables
SURFA_INGEST_KEY=sk_live_your_key
SURFA_API_URL=https://surfa.dev
Users connect via URL (no installation needed):
{
"mcp_servers": [{
"type": "url",
"url": "https://your-mcp.com/sse"
}]
}
2. Add to Your MCP Server
Choose the example that matches your distribution mode:
Local MCP (stdio) - Claude Desktop, Cursor
from surfa_ingest import SurfaClient
from fastmcp import FastMCP
import os
# Initialize analytics
analytics = SurfaClient(
ingest_key=os.getenv("SURFA_INGEST_KEY", "sk_live_your_key"),
api_url=os.getenv("SURFA_API_URL", "https://surfa.dev")
)
# Set runtime for local stdio mode
analytics.set_runtime(
provider="mcp",
model="your-mcp-server-name",
mode="stdio" # Local mode
)
# Initialize MCP server
mcp = FastMCP("Your MCP Server")
@mcp.tool
def search_database(query: str) -> dict:
analytics.track({
"kind": "tool",
"subtype": "call_started",
"tool_name": "search_database"
})
result = perform_search(query)
analytics.track({
"kind": "tool",
"subtype": "call_completed",
"tool_name": "search_database",
"status": "success"
})
analytics.flush()
return result
# Run in stdio mode (for Claude Desktop)
if __name__ == "__main__":
mcp.run() # Defaults to stdio
Users configure in Claude Desktop:
{
"mcpServers": {
"your-mcp": {
"command": "npx",
"args": ["-y", "your-mcp-server"]
}
}
}
Remote MCP (SSE) - Claude API, OpenAI API
from surfa_ingest import SurfaClient
from fastmcp import FastMCP
import os
# Initialize analytics
analytics = SurfaClient(
ingest_key=os.getenv("SURFA_INGEST_KEY", "sk_live_your_key"),
api_url=os.getenv("SURFA_API_URL", "https://surfa.dev")
)
# Set runtime for remote SSE mode
analytics.set_runtime(
provider="mcp",
model="your-mcp-server-name",
mode="sse" # Remote mode
)
# Initialize MCP server
mcp = FastMCP("Your MCP Server", stateless_http=True)
@mcp.tool
def search_database(query: str) -> dict:
analytics.track({
"kind": "tool",
"subtype": "call_started",
"tool_name": "search_database"
})
result = perform_search(query)
analytics.track({
"kind": "tool",
"subtype": "call_completed",
"tool_name": "search_database",
"status": "success"
})
analytics.flush()
return result
# Run in HTTP/SSE mode
if __name__ == "__main__":
mcp.run(
transport="http",
host="0.0.0.0",
port=8000,
path="/mcp"
)
Users connect via URL:
# Claude API
response = client.messages.create(
model="claude-opus-4",
mcp_servers=[{
"type": "url",
"url": "https://your-mcp.com/mcp"
}]
)
# OpenAI API
response = client.responses.create(
model="gpt-5",
tools=[{
"type": "mcp",
"server_url": "https://your-mcp.com/mcp"
}]
)
With MCP Context Auto-Detection (v0.2.0+)
NEW: Automatically extract client_id, session_id, and request_id from MCP context!
from surfa_ingest import SurfaClient
from fastmcp import FastMCP, Context
analytics = SurfaClient(ingest_key="sk_live_...")
mcp = FastMCP("My MCP Server")
@mcp.tool
def search_database(query: str, ctx: Context) -> dict:
# Just pass ctx - auto-extracts client_id, session_id, request_id!
analytics.track({
"kind": "tool",
"subtype": "call_started",
"tool_name": "search_database"
}, ctx=ctx) # ✨ Auto-extraction happens here
result = perform_search(query)
analytics.track({
"kind": "tool",
"subtype": "call_completed",
"tool_name": "search_database",
"status": "success"
}, ctx=ctx)
return result
Benefits:
- ✅ No manual field extraction
- ✅ Works with FastMCP and future MCP frameworks
- ✅ Backwards compatible (ctx is optional)
- ✅ Never breaks tracking (fails gracefully)
What gets auto-extracted:
client_id- Client identifier (if available from MCP context)request_id- Request identifier (for correlating MCP requests)
3. Get Your Analytics
Visit your Surfa dashboard to see:
- Real-time tool usage
- Performance metrics
- Task completion rates
- Client distribution
- Error tracking
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file surfa_ingest-0.2.0.tar.gz.
File metadata
- Download URL: surfa_ingest-0.2.0.tar.gz
- Upload date:
- Size: 18.2 kB
- Tags: Source
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2456569e01192495f23fa9c341bed27b77de3f01e2252a5c735b350ac1eea0ad
|
|
| MD5 |
22d9c3e3075285c6058ba6e452fd4687
|
|
| BLAKE2b-256 |
2227cf7b1a1b9744535e5c7dd46322cbf39e4938c1cb87bb17f2a76a7abaaf24
|
File details
Details for the file surfa_ingest-0.2.0-py3-none-any.whl.
File metadata
- Download URL: surfa_ingest-0.2.0-py3-none-any.whl
- Upload date:
- Size: 15.4 kB
- Tags: Python 3
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dd13cdfd5f236e56d69486db5c83df092242e798a18b34ca9125856228cf48e8
|
|
| MD5 |
c6a4286b449b2b50d98b8b77faa6a807
|
|
| BLAKE2b-256 |
32b70d4d8eee8571fbb9e4f4d09028d22efbfeb54c2258c1843971909948d589
|