# API Reference Source: https://docs.surfa.dev/api-reference Complete API documentation for Surfa Ingest and Query APIs ## Overview Surfa provides two main APIs: 1. **Ingest API** - Track events from your MCP server 2. **MCP Query API** - Query analytics with natural language Both APIs use API key authentication with Bearer tokens. *** ## Ingest API ### POST /api/v1/ingest/events Track events from your MCP server or application. **Endpoint:** ``` POST https://surfa-web.vercel.app/api/v1/ingest/events ``` **Headers:** ```http theme={null} Authorization: Bearer sk_live_your_key_here Content-Type: application/json ``` **Request Body:** ```json theme={null} { "session_id": "string (required)", "events": [ { "kind": "string (required)", "subtype": "string (required)", // ... additional event fields } ], "execution_id": "string (optional)", "runtime": { "provider": "string (optional)", "model": "string (optional)", "mode": "string (optional)" } } ``` **Field Descriptions:** | Field | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------------------------------------------------- | | `session_id` | string | ✅ | Unique identifier for this session (e.g., `live_session_abc123`) | | `events` | array | ✅ | Array of event objects to track | | `execution_id` | string | ❌ | Execution ID (returned from first request, reuse for subsequent requests) | | `runtime` | object | ❌ | Runtime metadata (provider, model, mode) | **Event Object:** | Field | Type | Required | Description | | ----------------- | ------ | -------- | ----------------------------------------------------------- | | `kind` | string | ✅ | Event category: `tool`, `session`, `runtime`, `error`, etc. | | `subtype` | string | ✅ | Event type: `call_started`, `call_completed`, `error`, etc. | | Additional fields | any | ❌ | Any custom fields (tool\_name, status, latency\_ms, etc.) | **Response (Success):** ```json theme={null} { "ok": true, "execution_id": "exec_abc123xyz", "events_received": 1 } ``` **Response (Error):** ```json theme={null} { "ok": false, "error": "Invalid API key" } ``` **Status Codes:** | Code | Meaning | | ----- | ----------------------------------------- | | `200` | Success - events tracked | | `401` | Unauthorized - invalid or missing API key | | `403` | Forbidden - API key revoked | | `422` | Validation error - invalid payload | | `429` | Rate limit exceeded | | `500` | Server error | *** ## Example Requests ### Track a Tool Call ```bash theme={null} curl -X POST https://surfa-web.vercel.app/api/v1/ingest/events \ -H "Authorization: Bearer sk_live_your_key" \ -H "Content-Type: application/json" \ -d '{ "session_id": "live_session_abc123", "events": [ { "kind": "tool", "subtype": "call_completed", "tool_name": "search", "status": "success", "latency_ms": 245 } ] }' ``` ### Track Multiple Events ```bash theme={null} curl -X POST https://surfa-web.vercel.app/api/v1/ingest/events \ -H "Authorization: Bearer sk_live_your_key" \ -H "Content-Type: application/json" \ -d '{ "session_id": "live_session_abc123", "execution_id": "exec_xyz789", "events": [ { "kind": "tool", "subtype": "call_started", "tool_name": "search", "query": "AI agents" }, { "kind": "tool", "subtype": "call_completed", "tool_name": "search", "status": "success", "latency_ms": 245, "results_count": 10 } ] }' ``` ### Track with Runtime Info ```bash theme={null} curl -X POST https://surfa-web.vercel.app/api/v1/ingest/events \ -H "Authorization: Bearer sk_live_your_key" \ -H "Content-Type: application/json" \ -d '{ "session_id": "live_session_abc123", "runtime": { "provider": "anthropic", "model": "claude-3-5-sonnet-20241022", "mode": "mcp" }, "events": [ { "kind": "session", "subtype": "started", "client_id": "claude_desktop_v1" } ] }' ``` *** ## Event Types ### Tool Events Track MCP tool calls: ```json theme={null} { "kind": "tool", "subtype": "call_started" | "call_completed" | "call_failed", "tool_name": "string", "status": "success" | "error", "latency_ms": 123, "error_message": "string (if failed)" } ``` ### Session Events Track session lifecycle: ```json theme={null} { "kind": "session", "subtype": "started" | "ended", "client_id": "string", "total_calls": 10 } ``` ### Error Events Track errors: ```json theme={null} { "kind": "error", "subtype": "error", "error_type": "string", "error_message": "string", "stack_trace": "string (optional)" } ``` ### Custom Events Track anything: ```json theme={null} { "kind": "custom", "subtype": "your_event_type", "custom_field_1": "value", "custom_field_2": 123 } ``` *** ## MCP Query API Used by the Surfa MCP Server to query analytics. You typically don't call these directly - use the MCP server instead. ### GET /api/v1/mcp/analytics/metrics Get high-level analytics metrics. **Headers:** ```http theme={null} Authorization: Bearer sk_live_your_key ``` **Response:** ```json theme={null} { "ok": true, "data": { "totalSessions": 150, "successRate": 85, "avgExecutionTime": 245, "activeSessions": 12 } } ``` ### GET /api/v1/mcp/analytics/events Query events with filters. **Query Parameters:** | Parameter | Type | Description | | ----------- | ------ | ------------------------------------ | | `status` | string | Filter by status: `success`, `error` | | `tool_name` | string | Filter by tool name | | `limit` | number | Max results (default: 100) | **Response:** ```json theme={null} { "ok": true, "data": { "events": [...], "total": 150 } } ``` ### GET /api/v1/mcp/analytics/sessions/:sessionId Get all events for a specific session. **Response:** ```json theme={null} { "ok": true, "data": { "session_id": "live_session_abc123", "status": "completed", "events": [...], "event_count": 10 } } ``` *** ## Rate Limits ### Ingest API The Ingest API has rate limiting to prevent abuse: * **Limit:** 1,000 events per minute per API key * **Window:** 60 seconds (sliding window) * **Response:** `429 Too Many Requests` when exceeded **Rate Limit Response:** ```json theme={null} { "ok": false, "error": "Rate limit exceeded" } ``` The SDK automatically retries with exponential backoff when rate limited. ### MCP Query API The MCP Query API currently has no rate limits. Use responsibly. *** ## Error Handling ### Authentication Errors (401, 403) ```json theme={null} { "ok": false, "error": "Invalid API key" } ``` **Don't retry** - fix your API key. ### Validation Errors (422) ```json theme={null} { "ok": false, "error": "Missing required field: session_id" } ``` **Don't retry** - fix your payload. ### Rate Limit Errors (429) ```json theme={null} { "ok": false, "error": "Rate limit exceeded" } ``` **Retry with exponential backoff.** ### Server Errors (500, 502, 503, 504) ```json theme={null} { "ok": false, "error": "Internal server error" } ``` **Retry with exponential backoff** (max 3 attempts). *** ## SDK vs Direct API ### Using the SDK (Recommended) ```python theme={null} from surfa_ingest import SurfaClient analytics = SurfaClient( ingest_key="sk_live_your_key", api_url="https://surfa-web.vercel.app" ) analytics.track({ "kind": "tool", "subtype": "call_completed", "tool_name": "search", "status": "success" }) ``` **Benefits:** * ✅ Automatic retries * ✅ Buffering and batching * ✅ Session management * ✅ Error handling * ✅ Runtime metadata ### Direct API Calls Use direct API calls if: * You're not using Python * You need custom behavior * You're building your own SDK **Example (JavaScript):** ```javascript theme={null} const response = await fetch('https://surfa-web.vercel.app/api/v1/ingest/events', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ session_id: 'live_session_abc123', events: [{ kind: 'tool', subtype: 'call_completed', tool_name: 'search', status: 'success' }] }) }); const data = await response.json(); console.log(data); ``` *** ## Next Steps Get your API key and set up authentication Start tracking events in 5 minutes Explore the Python SDK Learn what data Surfa tracks # Authentication Source: https://docs.surfa.dev/authentication Get your API key and start tracking events ## Get Your API Key Authentication with Surfa is simple - you just need an API key. Create a free account at [surfa.dev](https://surfa.dev) Go to your [dashboard](https://surfa-web.vercel.app/dashboard) Navigate to **Settings → API Keys** and click **"Create New Key"** Your key will start with `sk_live_` - copy it and store it securely **Success!** You now have your API key. Keep it secure and never commit it to Git. ## Using Your API Key **Building your own integration?** See the [API Reference](/api-reference) for endpoint details, payload schemas, and request/response examples. ### For SDK (Tracking Events) Use your API key when initializing the Surfa SDK: ```python theme={null} from surfa_ingest import SurfaClient import os analytics = SurfaClient( ingest_key=os.getenv("SURFA_INGEST_KEY"), api_url=os.getenv("SURFA_API_URL", "https://surfa-web.vercel.app") ) ``` **Set as environment variable:** ```bash theme={null} export SURFA_INGEST_KEY=sk_live_your_key_here export SURFA_API_URL=https://surfa-web.vercel.app ``` Or add to your `.env` file: ```bash theme={null} SURFA_INGEST_KEY=sk_live_your_key_here SURFA_API_URL=https://surfa-web.vercel.app ``` ### For MCP Server (Querying Analytics) Add your API key to Claude Desktop config: **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json` ```json theme={null} { "mcpServers": { "surfa": { "command": "/path/to/uv", "args": ["--directory", "/path/to/surfa-mcp", "run", "surfa-mcp"], "env": { "SURFA_API_KEY": "sk_live_your_key_here", "SURFA_API_URL": "https://surfa-web.vercel.app" } } } } ``` ## API Key Types Surfa uses different key prefixes for different environments: | Prefix | Environment | Use Case | | ---------- | ----------- | --------------------------------- | | `sk_live_` | Production | Live data, production MCP servers | | `sk_test_` | Testing | Development and testing | Test keys are isolated from production data. Use them for development and CI/CD. ## Security Best Practices **Bad:** ```python theme={null} # ❌ Don't do this! analytics = SurfaClient(ingest_key="sk_live_abc123...") ``` **Good:** ```python theme={null} # ✅ Use environment variables analytics = SurfaClient(ingest_key=os.getenv("SURFA_INGEST_KEY")) ``` Add `.env` to your `.gitignore`: ``` .env .env.local ``` Store keys in environment variables, not in code: **Development:** ```bash theme={null} # .env file SURFA_INGEST_KEY=sk_live_your_key ``` **Production:** Set environment variables in your deployment platform: * Vercel: Settings → Environment Variables * Railway: Variables tab * Fly.io: `fly secrets set SURFA_INGEST_KEY=...` Rotate your API keys periodically for security: 1. Create a new key in dashboard 2. Update environment variables 3. Deploy changes 4. Delete old key Keep the old key active for 24 hours during rotation to avoid downtime. Use different keys for different environments: * **Production:** `sk_live_prod_abc123` * **Staging:** `sk_live_staging_xyz789` * **Development:** `sk_test_dev_123456` This isolates data and makes it easier to track issues. ## Workspace Isolation Each API key is tied to a specific workspace. This means: * ✅ **Data isolation** - You only see your workspace's data * ✅ **Multi-tenant safe** - No cross-workspace leakage * ✅ **Team collaboration** - Share workspace access with team members Want to track multiple projects? Create separate workspaces in your dashboard. ## Troubleshooting **Possible causes:** 1. Key is incorrect or has typos 2. Key was deleted from dashboard 3. Using test key in production (or vice versa) **Solution:** * Verify key in dashboard * Check environment variables are set correctly * Ensure no extra spaces or quotes **Possible causes:** 1. API key not included in request 2. Environment variable not set 3. Key expired or revoked **Solution:** ```python theme={null} # Verify key is loaded import os print(os.getenv("SURFA_INGEST_KEY")) # Should print your key ``` **Check:** 1. API key is correct 2. API URL is correct (`https://surfa-web.vercel.app`) 3. No firewall blocking outbound requests 4. Events are being tracked (check logs) **Debug:** ```python theme={null} import logging logging.basicConfig(level=logging.DEBUG) # You'll see API requests in logs analytics.track({...}) ``` ## Rate Limits The Ingest API is rate limited to prevent abuse: * **1,000 events per minute** per API key * **60 second sliding window** If you exceed the rate limit, requests will return `429 Too Many Requests`. The SDK automatically retries with exponential backoff. The MCP Query API (for Claude Desktop) currently has no rate limits. ## Next Steps Start tracking events in 5 minutes Complete API documentation with examples Learn what data Surfa tracks Query analytics with Claude Desktop # Claude Desktop Setup Source: https://docs.surfa.dev/claude-desktop-setup Install and configure the Surfa MCP Server for Claude Desktop Query your Surfa analytics with natural language through Claude Desktop. ## Prerequisites * Claude Desktop installed ([download here](https://claude.ai/download)) * Surfa account with API key ([sign up at surfa.dev](https://surfa.dev)) * `uv` package manager ([install guide](https://docs.astral.sh/uv/getting-started/installation/)) Get your Surfa API key from your [dashboard](https://surfa-web.vercel.app/dashboard). ## Step 1: Install the MCP Server Clone the Surfa MCP repository: ```bash theme={null} git clone https://github.com/gamladz/surfa-mcp.git cd surfa-mcp ``` Create a virtual environment and install dependencies: ```bash theme={null} uv venv uv pip install -e . ``` The `-e` flag installs in editable mode, so you can update the code later with `git pull`. ## Step 2: Find Your uv Path You'll need the full path to `uv` for the Claude Desktop config. **macOS/Linux:** ```bash theme={null} which uv ``` **Common paths:** * `/Users/yourname/.local/bin/uv` (macOS) * `/home/yourname/.local/bin/uv` (Linux) * `/opt/homebrew/bin/uv` (macOS with Homebrew) Use the **full absolute path**, not just `uv`. Claude Desktop needs the complete path. ## Step 3: Configure Claude Desktop Find your Claude Desktop config file: **macOS:** ```bash theme={null} ~/Library/Application Support/Claude/claude_desktop_config.json ``` **Windows:** ```bash theme={null} %APPDATA%\Claude\claude_desktop_config.json ``` **Linux:** ```bash theme={null} ~/.config/Claude/claude_desktop_config.json ``` Open the file and add the Surfa MCP server: ```json theme={null} { "mcpServers": { "surfa": { "command": "/Users/yourname/.local/bin/uv", "args": [ "--directory", "/Users/yourname/path/to/surfa-mcp", "run", "surfa-mcp" ], "env": { "SURFA_API_KEY": "sk_live_your_key_here", "SURFA_API_URL": "https://surfa-web.vercel.app" } } } } ``` **Replace these values:** * `/Users/yourname/.local/bin/uv` → Your actual `uv` path from Step 2 * `/Users/yourname/path/to/surfa-mcp` → Your actual clone directory * `sk_live_your_key_here` → Your Surfa API key ### If You Have Other MCP Servers If you already have MCP servers configured, just add Surfa to the existing `mcpServers` object: ```json theme={null} { "mcpServers": { "existing-server": { "command": "...", "args": [...] }, "surfa": { "command": "/Users/yourname/.local/bin/uv", "args": [ "--directory", "/Users/yourname/path/to/surfa-mcp", "run", "surfa-mcp" ], "env": { "SURFA_API_KEY": "sk_live_your_key_here", "SURFA_API_URL": "https://surfa-web.vercel.app" } } } } ``` ## Step 4: Restart Claude Desktop **Important:** You must completely quit and reopen Claude Desktop for changes to take effect. **macOS:** 1. Cmd+Q to quit Claude Desktop 2. Reopen from Applications **Windows:** 1. Right-click system tray → Quit 2. Reopen from Start menu ## Step 5: Verify Connection Look for the 🔌 icon in the bottom-right corner of Claude Desktop. Click it to see connected MCP servers. You should see **"surfa"** in the list. MCP Icon in Claude Desktop ## Step 6: Test Your First Query Ask Claude: ``` Show me my Surfa analytics overview ``` You should get a response like: ``` Your Surfa analytics: - Total Sessions: 35 - Success Rate: 85% - Average Execution Time: 245ms - Active Sessions: 2 ``` **Success!** Your Surfa MCP Server is now connected and working. ## Using Remote MCP Want to use a remote MCP instead of local? ### Option 1: Fly.io Proxy (Experimental) ```json theme={null} { "mcpServers": { "surfa-remote": { "command": "fly", "args": ["mcp", "proxy", "https://surfa-mcp.fly.dev/sse"], "env": { "SURFA_API_KEY": "sk_live_your_key" } } } } ``` The `fly mcp proxy` command is experimental and may have issues. For production use, we recommend using the Surfa web platform to test remote MCPs. ### Option 2: Direct HTTP (Coming Soon) Claude Desktop doesn't yet support direct HTTP MCP connections. Use the Surfa web platform instead. Learn how to deploy and test remote MCPs ## Example Queries Try these queries to explore your analytics: ```text Get Started theme={null} "Show me my Surfa analytics overview" ``` ```text Find Errors theme={null} "Show me all errors from the last 24 hours" ``` ```text Performance theme={null} "What were the slowest queries this week?" ``` ```text Deep Dive theme={null} "Analyze my product health and give me recommendations" ``` ## Troubleshooting **Check:** 1. Config file path is correct 2. JSON syntax is valid (use [jsonlint.com](https://jsonlint.com)) 3. No trailing commas in JSON 4. File saved after editing **Debug:** Check Claude Desktop logs: ```bash theme={null} # macOS tail -f ~/Library/Logs/Claude/mcp*.log # Windows type %APPDATA%\Claude\Logs\mcp*.log ``` **Check:** 1. `uv` path is correct (run `which uv` again) 2. Surfa MCP directory path is absolute, not relative 3. API key is correct (check dashboard) 4. Internet connection is working **Test manually:** ```bash theme={null} cd /path/to/surfa-mcp export SURFA_API_KEY=sk_live_your_key export SURFA_API_URL=https://surfa-web.vercel.app uv run surfa-mcp ``` If this works, the issue is in your Claude Desktop config. **Check:** 1. You have events in your Surfa dashboard 2. API key has access to the workspace 3. Events are from "live" source (not test data) **Verify:** Go to [your dashboard](https://surfa-web.vercel.app/dashboard) and confirm you see events. **Solution:** Reinstall dependencies: ```bash theme={null} cd /path/to/surfa-mcp uv pip install -e . ``` Then restart Claude Desktop. **Common mistakes:** * Trailing comma after last item * Missing quotes around strings * Backslashes in Windows paths (use forward slashes or double backslashes) **Fix:** Copy your config to [jsonlint.com](https://jsonlint.com) to validate. **Example of valid JSON:** ```json theme={null} { "mcpServers": { "surfa": { "command": "/path/to/uv", "args": ["--directory", "/path/to/surfa-mcp", "run", "surfa-mcp"], "env": { "SURFA_API_KEY": "sk_live_key", "SURFA_API_URL": "https://surfa-web.vercel.app" } } } } ``` ## Advanced Configuration ### Using Production API If you're using the production Surfa API (not localhost): ```json theme={null} { "env": { "SURFA_API_KEY": "sk_live_your_key_here", "SURFA_API_URL": "https://surfa.dev" } } ``` ### Custom Timeout Set a custom timeout (in seconds): ```json theme={null} { "env": { "SURFA_API_KEY": "sk_live_your_key_here", "SURFA_API_URL": "https://surfa-web.vercel.app", "SURFA_TIMEOUT": "60" } } ``` ### Multiple Workspaces Use different API keys for different workspaces: ```json theme={null} { "mcpServers": { "surfa-production": { "command": "/path/to/uv", "args": ["--directory", "/path/to/surfa-mcp", "run", "surfa-mcp"], "env": { "SURFA_API_KEY": "sk_live_prod_key", "SURFA_API_URL": "https://surfa.dev" } }, "surfa-staging": { "command": "/path/to/uv", "args": ["--directory", "/path/to/surfa-mcp", "run", "surfa-mcp"], "env": { "SURFA_API_KEY": "sk_live_staging_key", "SURFA_API_URL": "https://staging.surfa.dev" } } } } ``` ## Updating the MCP Server To update to the latest version: ```bash theme={null} cd /path/to/surfa-mcp git pull uv pip install -e . ``` Then restart Claude Desktop. ## Uninstalling To remove the Surfa MCP Server: 1. Remove the `"surfa"` entry from `claude_desktop_config.json` 2. Restart Claude Desktop 3. Optionally delete the cloned directory: ```bash theme={null} rm -rf /path/to/surfa-mcp ``` ## Next Steps Learn about all 4 analytics tools and example queries Complete API documentation with examples Start tracking events in 5 minutes View source and contribute ## Need Help? Get help from the community and Surfa team # Introduction Source: https://docs.surfa.dev/introduction Observability for AI Agents and MCP Servers ## What is Surfa? Surfa is the easiest way to understand what's happening inside your AI agents and MCP servers. **Stop debugging logs. Start asking questions.** Instead of digging through scattered logs, just ask Claude: * "What's my success rate this week?" * "Find all errors from yesterday" * "Which tools are slowest?" Get instant answers powered by your real-time analytics. ## The Problem Building AI agents? You're probably: * 🔍 **Debugging scattered logs** across multiple tools * 📊 **Manually tracking metrics** in spreadsheets * ❓ **Guessing why tools fail** without visibility * ⏰ **Wasting hours** investigating issues ## The Solution Add a few lines of code. Get instant insights. ```python theme={null} from surfa_ingest import SurfaClient analytics = SurfaClient(ingest_key="sk_live_...") analytics.track({ "kind": "tool", "subtype": "call_completed", "tool_name": "search", "status": "success" }) ``` Then ask Claude: **"Show me my analytics overview"** → Get instant answers with real data. ## How It Works Add the Surfa SDK to your MCP server (2 lines of code) Events are auto-captured in real-time to your dashboard Ask questions in natural language through Claude Desktop Get AI-powered analysis, recommendations, and alerts ## Quick Start Get Surfa running in 5 minutes with our step-by-step guide. Query your analytics with natural language through Claude. ## Two Products Track events from your MCP server. Auto-capture sessions, latency, errors, and runtime metadata. Query your analytics with natural language through Claude Desktop. No dashboards needed. ## Why Surfa? No complex logging infrastructure. No data pipelines. Just add the SDK and you're done. Built specifically for MCP patterns. Understands sessions, tools, and agent workflows out of the box. Events appear in your dashboard instantly. No waiting for batch jobs or ETL. Ask questions in natural language. Get insights and recommendations from AI that understands your data. ## Perfect For Track adoption, success rates, and user behavior without SQL. Debug faster with session replay and error tracking. Monitor performance, latency, and uptime in real-time. ## Example Queries See what you can ask Claude with the Surfa MCP Server: ```text Analytics Overview theme={null} "Show me my analytics overview" ``` ```text Error Investigation theme={null} "Show me all errors from the last 24 hours" ``` ```text Performance Analysis theme={null} "What were the slowest queries this week?" ``` ```text Session Deep-Dive theme={null} "Show me details for session abc123" ``` **Response Format:** All tools return JSON for easy parsing by AI agents: ```json theme={null} { "ok": true, "data": { "totalSessions": 150, "successRate": 85, "avgExecutionTime": 245, "activeSessions": 12 } } ``` ## Multi-Query Workflows The PM Agent can chain queries together for complex analysis: **Example workflow:** 1. **Get analytics** → sees low success rate (54%) 2. **Query events** with `status=error` → finds "Database unavailable" errors 3. **Analyze patterns** → identifies Feb 21-22 outage 4. **Provide recommendations** → Add health checks, circuit breakers **All in seconds. No dashboards. No SQL.** ## Ready to Start? Follow our quickstart guide and be up and running in 5 minutes. *** Star us on GitHub and contribute to the project. Get help, share feedback, and connect with other users. # MCP Server Overview Source: https://docs.surfa.dev/mcp-server-overview Query your Surfa analytics with natural language ## What is the Surfa MCP Server? The Surfa MCP Server lets you query your analytics data using natural language through Claude Desktop or any MCP-compatible client. **Instead of dashboards, just ask:** * "What's my success rate this week?" * "Find all errors from yesterday" * "Which tools are slowest?" Get instant answers powered by your real-time analytics data. ## How It Works Clone and install the Surfa MCP Server Add your API key to Claude Desktop config Ask questions in natural language Get AI-powered insights and recommendations ## Features Ask questions in plain English. No SQL, no dashboards. Core analytics + V2 cost optimization tools with multi-model estimates. Structured data optimized for AI agent consumption. Chain queries together for complex analysis. ## Remote vs Local MCP ### Local MCP (STDIO) * Runs on your machine * Fast, low latency * Claude Desktop only * No deployment needed ### Remote MCP (HTTP/SSE) * Deployed to cloud (Fly.io, Railway, etc.) * Accessible from anywhere * Web applications supported * Requires deployment Learn how to deploy your MCP to production with HTTP/SSE transport ## Available Tools ### Core Analytics (4 tools) #### 1. `get_analytics` Get high-level metrics about your MCP server. **Example queries:** * "Show me my analytics overview" * "What's my success rate?" * "How many sessions do I have?" **Returns:** ```json theme={null} { "ok": true, "data": { "totalSessions": 150, "successRate": 85, "avgExecutionTime": 245, "activeSessions": 12 } } ``` *** #### 2. `query_events` Filter and search through your events. **Example queries:** * "Show me all errors from the last 24 hours" * "Find tool calls with latency over 1000ms" * "Get all session events from yesterday" **Parameters:** * `tool_name` - Filter by tool name * `min_latency` / `max_latency` - Latency range in ms * `start_date` / `end_date` - ISO 8601 timestamps * `kind` - Event kind (tool, session, runtime) * `status` - Event status (success, error) * `limit` - Max results (default: 100) *** #### 3. `find_highest_latency` Find your slowest queries. **Example queries:** * "What were the slowest queries this week?" * "Show me the top 5 slowest tool calls today" **Parameters:** * `time_range` - hour, day, week, or month * `tool_name` - Optional: filter by specific tool * `limit` - Number of results (default: 10) *** #### 4. `get_session` Deep-dive into a specific session. **Example queries:** * "Show me details for session abc123" * "What happened in session xyz789?" **Parameters:** * `session_id` - The session ID to retrieve **Returns:** ```json theme={null} { "ok": true, "data": { "session_id": "abc123", "status": "completed", "started_at": "2026-03-04T12:00:00Z", "completed_at": "2026-03-04T12:05:00Z", "runtime": { "provider": "anthropic", "model": "claude-3-5-sonnet", "mode": "stdio" }, "events": [...], "event_count": 15 } } ``` *** ### V2 Analytics - Cost Optimization (4 tools) #### 5. `find_redundant_executions` Find executions with redundant tool calls that waste tokens and costs. **Example queries:** * "Which sessions have redundant tool calls?" * "Show me executions wasting the most money on duplicate calls" * "Find redundancy from the last month" **Parameters:** * `time_range` - hour, day, week, or month (default: week) * `min_redundancy` - Minimum redundancy score 0.0-1.0 (default: 0.3) * `limit` - Max results (default: 20) **Returns:** ```json theme={null} { "ok": true, "data": { "total_found": 20, "total_potential_savings": { "calls_saved": 327, "tokens_estimated": 49050, "cost_usd": { "gpt_4_turbo": 0.00049, "claude_sonnet": 0.00015 } }, "executions": [...] } } ``` *** #### 6. `find_batch_opportunities` Find tools called multiple times per session that could benefit from batch endpoints. **Example queries:** * "Which tools should I add batch endpoints for?" * "What's my biggest batching opportunity?" * "Show me tools that could be batched" **Parameters:** * `time_range` - hour, day, week, or month (default: week) * `min_call_count` - Minimum calls per execution (default: 3) * `limit` - Max results (default: 20) **Returns:** ```json theme={null} { "ok": true, "data": { "total_opportunities": 5, "opportunities": [ { "tool_name": "get_user", "execution_count": 15, "total_calls": 78, "potential_savings": { "calls_saved": 63, "tokens_estimated": 9450, "cost_usd": {...} }, "recommendation": "Add batch_get_user endpoint" } ] } } ``` *** #### 7. `get_tool_insights` Get comprehensive performance insights for a specific tool. **Example queries:** * "How is get\_user performing?" * "Analyze the search\_database tool" * "Show me insights for my slowest tool" **Parameters:** * `tool_name` - Name of the tool to analyze (required) * `time_range` - hour, day, week, or month (default: week) **Returns:** ```json theme={null} { "ok": true, "data": { "tool_name": "get_user", "usage": { "execution_count": 45, "total_calls": 120, "avg_calls_per_execution": 2.7 }, "redundancy": { "avg_redundancy_rate": 0.25, "potential_savings": {...} }, "latency": { "mean_ms": 245, "p95_ms": 890, "max_ms": 1200 }, "batch_potential": {...}, "recommendations": [ "Consider caching: 25% of calls are redundant", "Batch potential: 15 executions make multiple calls" ] } } ``` *** #### 8. `get_execution_metrics` Get detailed V2 metrics for a specific execution (enhanced version of `get_session`). **Example queries:** * "Show me everything about execution abc123" * "Debug execution xyz789 with full metrics" * "Get V2 metrics for the worst redundancy offender" **Parameters:** * `execution_id` - The execution ID to retrieve (required) **Returns:** ```json theme={null} { "ok": true, "data": { "execution_id": "abc123", "session_outcome": "complete", "metrics_v2": { "redundancy": { "score": 0.45, "redundant_calls": 12, "potential_savings": {...} }, "batch_opportunities": [...], "latency": { "mean_ms": 340, "slowest_tool": "search_database" } }, "total_optimization_potential": { "calls_saved": 15, "tokens_estimated": 2250, "cost_usd": {...} } } } ``` *** ## Multi-Query Workflows The real power comes from chaining queries together. Claude can: 1. **Get analytics** → sees low success rate 2. **Query events** → finds error patterns 3. **Analyze** → provides recommendations **Example:** ``` You: "Analyze my product health" Claude: 1. Calls get_analytics() → sees 54% success rate 2. Calls query_events(status="error") → finds "Database unavailable" 3. Analyzes patterns → identifies Feb 21-22 outage 4. Provides recommendations: - Add database health checks - Implement circuit breakers - Separate metrics by time window ``` **All in seconds. No dashboards. No SQL.** ## Use Cases **Track product metrics without SQL:** * "What's my weekly active users trend?" * "Which features are most used?" * "What's causing the drop in success rate?" Get insights in natural language, share with team instantly. **Debug faster:** * "Show me all errors in the last hour" * "Which tool is failing most often?" * "What happened in session abc123?" Skip log diving, get straight to the issue. **Monitor performance:** * "What were the slowest queries today?" * "Show me latency trends this week" * "Are there any performance regressions?" Proactive monitoring through conversation. **Autonomous analytics:** Build PM agents that automatically: * Monitor metrics * Detect anomalies * Investigate issues * Generate reports JSON responses make it easy for agents to consume. ## Why Use the MCP Server? Query analytics without leaving Claude. No switching to dashboards. Ask questions like you would a teammate. No learning query languages. Claude analyzes the data and provides recommendations automatically. JSON responses perfect for building autonomous PM agents. ## Getting Started Follow our step-by-step guide to install and configure the Surfa MCP Server. ## Example Queries ```text Analytics theme={null} "Show me my analytics overview" "What's my success rate this week?" "How many active sessions do I have?" ``` ```text Errors theme={null} "Show me all errors from yesterday" "Find errors with 'timeout' in the message" "Which tool has the most errors?" ``` ```text Performance theme={null} "What were the slowest queries today?" "Show me tool calls over 1 second" "Find performance regressions this week" ``` ```text Sessions theme={null} "Show me session abc123" "What happened in the last failed session?" "Get details for session xyz789" ``` ```text Cost Optimization theme={null} "Which sessions have redundant tool calls?" "Show me the biggest batching opportunity" "How much money could I save by optimizing?" "Compare GPT-4 vs Claude Sonnet costs" ``` ## Architecture ``` You (Natural Language) ↓ Claude Desktop ↓ Surfa MCP Server (Python) ↓ Surfa API (HTTP + Bearer Auth) ↓ Your Analytics Data ``` **Security:** * API key stored locally in Claude Desktop config * Never exposed to Claude * Workspace-scoped (you only see your data) ## Next Steps Install and configure the MCP server Complete API documentation with examples Start tracking events in 5 minutes View source code and contribute # Testing MCP Connections Source: https://docs.surfa.dev/mcp-testing Test and validate your MCP server connections ## Overview The Surfa platform includes a built-in connection tester to validate your MCP setup before creating test scenarios. ## Using the Test Connection Feature ### 1. Navigate to MCP Settings Go to **Settings → MCP** in your Surfa workspace. ### 2. Add Your MCP Click **Add New MCP** and fill in: * **Name:** Descriptive name (e.g., "Production Surfa MCP") * **Transport:** SSE, STDIO, or Streamable HTTP * **Endpoint:** Your MCP URL (e.g., `https://surfa-mcp.fly.dev/sse`) * **Headers:** Authentication headers (optional) ### 3. Click "Test Connection" The platform will: 1. Send an `initialize` request 2. Send a `tools/list` request 3. Parse the response 4. Display discovered tools ## What Happens During Testing ### For SSE Transport POST /sse with initialize request Read SSE stream for response POST /sse with tools/list request Parse tool definitions Display results in UI ### Success Response ``` ✅ Connection successful! Discovered 4 tools: - get_analytics - query_events - find_highest_latency - get_session ``` ### Retry Behavior If the server returns `202 Accepted` (machine starting): Wait 9 seconds, try again Wait 9 seconds, try again Wait 9 seconds, try again You'll see progress messages: ``` Retry attempt 1 of 3... (waiting for server to start) Retry attempt 2 of 3... (waiting for server to start) ✅ Connection successful! ``` ## Common Test Results ### ✅ Success **Message:** "Connection successful!" **Tools:** List of discovered tools displayed **Next:** Create test scenarios *** ### ⚠️ Cold Start (202) **Message:** "Retry attempt X of 3..." **Action:** Wait for retries to complete **Duration:** Up to 27 seconds *** ### ❌ Timeout **Message:** "Machine is starting up but taking longer than expected" **Cause:** Server not responding after 27 seconds **Fix:** * Check server is running * Increase timeout * Configure always-on *** ### ❌ Unauthorized (401) **Message:** "Authentication failed" **Cause:** Invalid API key or missing headers **Fix:** * Verify API key * Check Authorization header format * Ensure key has correct permissions *** ### ❌ Not Found (404) **Message:** "Endpoint not found" **Cause:** Wrong URL or path **Fix:** * Verify endpoint URL * Check transport type matches * Test with curl first *** ### ❌ No Tools Found **Message:** "Connection successful but no tools discovered" **Cause:** `tools/list` returned empty array **Fix:** * Check MCP server has tools defined * Verify tools are registered * Test `tools/list` manually ## Manual Testing with curl ### Test Initialize ```bash theme={null} curl -X POST https://your-mcp.fly.dev/sse \ -H "Authorization: Bearer sk_live_your_key" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": "test-init", "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "test-client", "version": "1.0.0"} } }' ``` ### Test Tools List ```bash theme={null} curl -X POST https://your-mcp.fly.dev/sse \ -H "Authorization: Bearer sk_live_your_key" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": "test-tools", "method": "tools/list", "params": {} }' ``` ### Expected Response ```json theme={null} { "jsonrpc": "2.0", "id": "test-tools", "result": { "tools": [ { "name": "get_analytics", "description": "Get analytics metrics", "inputSchema": { "type": "object", "properties": {}, "required": [] } } ] } } ``` ## Debugging Failed Tests ### Enable Debug Logging **In Surfa Platform:** Check browser console (F12) for detailed logs: ``` Initialize response: {...} Tools/list response status: 200 Found tools in response: [...] ``` **In Your MCP Server:** ```python theme={null} import logging logging.basicConfig(level=logging.DEBUG) ``` ### Check Server Logs **Fly.io:** ```bash theme={null} fly logs --app your-app ``` **Local:** ```bash theme={null} tail -f server.log ``` ### Verify Network **Test connectivity:** ```bash theme={null} ping your-mcp.fly.dev curl -I https://your-mcp.fly.dev ``` **Check firewall:** * Ensure port 443 (HTTPS) is open * Verify no VPN blocking requests ## Best Practices Validate your MCP works on localhost before deploying Test manually with curl to isolate issues Review server logs for detailed error messages Test without auth first, then add complexity ## Troubleshooting Guide **Possible causes:** * Server is starting (cold start) * Network issues * Firewall blocking requests **Solutions:** * Wait for retry logic to complete * Check server status: `fly status` * Test with curl to verify connectivity **Possible causes:** * MCP server not returning tools * Wrong endpoint path * Tools not registered **Solutions:** * Test `tools/list` manually with curl * Verify endpoint URL is correct * Check server logs for errors **Possible causes:** * Invalid API key * Wrong header format * Missing Authorization header **Solutions:** * Verify API key is correct * Use format: `Bearer sk_live_...` * Check headers are set in MCP config **Possible causes:** * Invalid JSON response * Server returning HTML instead of JSON * CORS issues **Solutions:** * Check server is returning valid JSON * Verify Content-Type header * Enable CORS if needed ## Next Steps Deploy your MCP to production Build test cases for your MCP Connect Claude Desktop to your MCP Complete API documentation # Privacy & PII Source: https://docs.surfa.dev/privacy-and-pii What data Surfa tracks and how to protect user privacy ## Privacy-First Design Surfa is built with privacy as a core principle. We help you track analytics **without collecting personal information**. **TL;DR:** Surfa tracks technical metrics (latency, errors, tool names) but **never** user prompts, responses, or personal data by default. ## What We Track ### ✅ We DO Track **Technical Metrics:** * Tool names (e.g., "search", "get\_weather") * Event types (tool\_call, session\_started, etc.) * Status (success, error) * Latency in milliseconds * Timestamps **Session Data:** * Session IDs (random UUIDs, not linked to users) * Request IDs (for correlation) * Client IDs (MCP client identifier) **Runtime Metadata:** * Provider (e.g., "anthropic", "openai") * Model (e.g., "claude-3-5-sonnet") * Mode (stdio, sse, http) **Example of what we track:** ```json theme={null} { "kind": "tool", "subtype": "call_completed", "tool_name": "search", "status": "success", "latency_ms": 245, "session_id": "uuid-random-123", "timestamp": "2026-03-04T12:00:00Z" } ``` ### ❌ We DON'T Track **User Content:** * ❌ User prompts or queries * ❌ Tool responses or outputs * ❌ File contents * ❌ API keys or credentials * ❌ Personal identifiable information (PII) **Sensitive Data:** * ❌ Email addresses * ❌ Names * ❌ IP addresses (beyond basic geolocation) * ❌ Device identifiers * ❌ Cookies or tracking pixels ## User Opt-Out Users can disable Surfa tracking entirely via environment variable: ```bash theme={null} export SURFA_DISABLE_TRACKING=true ``` When set, the SDK will: * ✅ Not send any events to Surfa * ✅ Not make any network requests * ✅ Fail silently (no errors) * ✅ Continue to work normally otherwise Respect user privacy by documenting this opt-out option in your MCP server's README. ## What You Control (MCP Builders) As an MCP builder, **you decide what data to track**. Here's how to do it responsibly: ### ✅ Good Practices **Track technical metrics only:** ```python theme={null} from surfa_ingest import SurfaClient analytics = SurfaClient(ingest_key="sk_live_...") # ✅ Good - no PII analytics.track({ "kind": "tool", "subtype": "call_completed", "tool_name": "search", "status": "success", "latency_ms": 245 }) ``` **Track aggregated data:** ```python theme={null} # ✅ Good - counts, not content analytics.track({ "kind": "tool", "subtype": "call_completed", "tool_name": "search", "results_count": 10, # How many results, not what they are "status": "success" }) ``` **Track error types, not messages:** ```python theme={null} # ✅ Good - error type only analytics.track({ "kind": "tool", "subtype": "call_failed", "tool_name": "database_query", "error_type": "DatabaseUnavailable", # Type, not message "status": "error" }) ``` ### ❌ Bad Practices **Don't track user input:** ```python theme={null} # ❌ Bad - contains user's query analytics.track({ "kind": "tool", "tool_name": "search", "query": "user's private search query", # Don't do this! "status": "success" }) ``` **Don't track tool outputs:** ```python theme={null} # ❌ Bad - contains response data analytics.track({ "kind": "tool", "tool_name": "get_user_info", "response": {"name": "John Doe", "email": "..."}, # Don't do this! "status": "success" }) ``` **Don't track error messages with PII:** ```python theme={null} # ❌ Bad - error message might contain PII analytics.track({ "kind": "tool", "tool_name": "send_email", "error_message": "Failed to send to john@example.com", # Don't do this! "status": "error" }) ``` ## GDPR Compliance Surfa is designed to be GDPR-compliant out of the box: We only collect the minimum data needed for analytics: * Technical metrics (latency, errors) * Session correlation (random UUIDs) * No personal data by default Users can opt-out at any time: ```bash theme={null} export SURFA_DISABLE_TRACKING=true ``` Document this in your MCP server's README. Users can request data deletion: 1. Contact [support@surfa.dev](mailto:support@surfa.dev) 2. Provide workspace ID 3. Data deleted within 30 days Or delete via dashboard: Settings → Delete Workspace Each workspace is isolated: * No cross-workspace data sharing * Workspace-scoped API keys * No data aggregation across workspaces Data retention by tier: * Free: 7 days * Pro: 30 days * Team: 90 days * Enterprise: Custom (up to 1 year) After retention period, data is automatically deleted. ## Best Practices for MCP Builders ### 1. Document What You Track Add a privacy section to your MCP server's README: ````markdown theme={null} ## Privacy This MCP server uses Surfa for analytics. We track: - Tool usage (which tools are called) - Success/error rates - Performance metrics (latency) We DO NOT track: - Your prompts or queries - Tool responses - Any personal information To disable tracking: ```bash export SURFA_DISABLE_TRACKING=true ```` ```` ### 2. Sanitize Error Messages Remove PII from error messages before tracking: ```python def sanitize_error(error_msg: str) -> str: """Remove potential PII from error messages.""" # Remove email addresses error_msg = re.sub(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', '[EMAIL]', error_msg) # Remove URLs with potential tokens error_msg = re.sub(r'https?://[^\s]+', '[URL]', error_msg) return error_msg # Use it try: result = call_api() except Exception as e: analytics.track({ "kind": "tool", "subtype": "call_failed", "tool_name": "api_call", "error_type": type(e).__name__, "error_message": sanitize_error(str(e)), # Sanitized "status": "error" }) ```` ### 3. Use Custom Fields Wisely Only track what you need: ```python theme={null} # ✅ Good - useful for debugging analytics.track({ "kind": "tool", "tool_name": "database_query", "query_type": "SELECT", # Type, not actual query "table_name": "users", # Table, not data "row_count": 42, # Count, not content "status": "success" }) # ❌ Bad - too much detail analytics.track({ "kind": "tool", "tool_name": "database_query", "sql": "SELECT * FROM users WHERE email='...'", # Don't do this! "results": [{...}], # Don't do this! "status": "success" }) ``` ### 4. Respect User Preferences Check for opt-out before tracking: ```python theme={null} import os def should_track() -> bool: """Check if tracking is enabled.""" return os.getenv("SURFA_DISABLE_TRACKING", "").lower() != "true" if should_track(): analytics.track({...}) ``` The Surfa SDK already handles this automatically. This is just for reference. ## Data Security ### In Transit * ✅ All data encrypted with TLS 1.3 * ✅ HTTPS only (no HTTP) * ✅ Certificate pinning ### At Rest * ✅ Encrypted database storage * ✅ Workspace-level isolation * ✅ Role-based access control (RBAC) ### Access Control * ✅ API key authentication * ✅ Workspace-scoped permissions * ✅ No cross-workspace access ## Compliance Certifications Surfa is working towards: * [ ] SOC 2 Type II * [ ] GDPR compliance certification * [ ] ISO 27001 Enterprise customers can request compliance documentation at [support@surfa.dev](mailto:support@surfa.dev) ## Transparency We believe in transparency: * ✅ **Open source MCP server** - [View on GitHub](https://github.com/gamladz/surfa-mcp) * ✅ **Public SDK** - [View on GitHub](https://github.com/gamladz/surfa-ingest) * ✅ **Clear documentation** - You're reading it! * ✅ **No hidden tracking** - Only what you explicitly send ## Questions? Email us with privacy questions See exactly what we track Learn about API key security More tips for responsible tracking ## Summary **Surfa is privacy-first:** * No PII collected by default * Users can opt-out anytime * You control what data is tracked * GDPR-compliant design * Transparent and open source **Remember:** Track technical metrics, not user content. When in doubt, don't track it! # Quickstart Source: https://docs.surfa.dev/quickstart Get Surfa running in 5 minutes Get Surfa up and running in 5 minutes. This guide will walk you through installing the SDK, tracking your first events, and querying them with Claude Desktop. ## Prerequisites * Python 3.10 or higher * A Surfa account (sign up at [surfa.dev](https://surfa.dev)) * Your Surfa API key ## Step 1: Install the SDK Install the Surfa SDK in your MCP server project: ```bash theme={null} pip install surfa-ingest ``` Or with `uv`: ```bash theme={null} uv pip install surfa-ingest ``` ## Step 2: Track Your First Event Add Surfa to your MCP server code: ```python theme={null} from surfa_ingest import SurfaClient import os # Initialize analytics client analytics = SurfaClient( ingest_key=os.getenv("SURFA_INGEST_KEY"), api_url=os.getenv("SURFA_API_URL", "https://surfa-web.vercel.app") ) # Set runtime info once at startup analytics.set_runtime( provider="anthropic", model="claude-3-5-sonnet", mode="stdio" ) # Track events throughout your code analytics.track({ "kind": "tool", "subtype": "call_completed", "tool_name": "search", "status": "success", "latency_ms": 245 }) ``` The SDK auto-captures `client_id` and `request_id` from MCP context when you pass `ctx` parameter. You don't need to extract them manually! ## Step 3: Configure Your API Key Set your Surfa API key as an environment variable: ```bash theme={null} export SURFA_INGEST_KEY=sk_live_your_key_here export SURFA_API_URL=https://surfa-web.vercel.app ``` Or add to your `.env` file: ```bash theme={null} SURFA_INGEST_KEY=sk_live_your_key_here SURFA_API_URL=https://surfa-web.vercel.app ``` Get your API key from your [Surfa dashboard](https://surfa-web.vercel.app/dashboard). ## Step 4: Run Your MCP Server Start your MCP server and trigger some tool calls: ```bash theme={null} # Example with FastMCP uv run fastmcp run server.py ``` Events will be automatically tracked and sent to Surfa in real-time. ## Step 5: View Your Analytics Go to your [Surfa dashboard](https://surfa-web.vercel.app/dashboard) to see: * Total sessions * Success rate * Average execution time * Event timeline * Error tracking Surfa Dashboard ## Step 6: Test Your MCP Connection (Optional) Want to validate your MCP is working correctly? Go to **Settings → MCP** in Surfa Click **Add New MCP** Enter your MCP details (endpoint, transport, headers) Click **Test Connection** See discovered tools ✅ Learn how to test and debug MCP connections ## Next: Query with Natural Language (Optional) Want to query your analytics by asking Claude questions in natural language? Install the Surfa MCP Server to query analytics through Claude Desktop **What you can do:** * Ask "What's my success rate?" → Get instant answers * "Find all errors from yesterday" → Filtered results * "Analyze my product health" → AI-powered recommendations **No dashboards. No SQL. Just conversation.** ## Example: Complete MCP Server Here's a complete example of an MCP server with Surfa tracking: ```python theme={null} from fastmcp import FastMCP, Context from surfa_ingest import SurfaClient import os # Initialize analytics analytics = SurfaClient( ingest_key=os.getenv("SURFA_INGEST_KEY"), api_url=os.getenv("SURFA_API_URL", "https://surfa-web.vercel.app") ) # Set runtime info analytics.set_runtime( provider="mcp", model="my-mcp-server", mode="stdio" ) # Initialize MCP server mcp = FastMCP("my-mcp-server") @mcp.tool() def search(query: str, ctx: Context) -> str: """Search for information.""" # Track tool call start (ctx auto-extracts client_id, request_id) analytics.track({ "kind": "tool", "subtype": "call_started", "tool_name": "search" }, ctx=ctx) try: # Your search logic here results = perform_search(query) # Track success analytics.track({ "kind": "tool", "subtype": "call_completed", "tool_name": "search", "status": "success" }, ctx=ctx) analytics.flush() return results except Exception as e: # Track error analytics.track({ "kind": "tool", "subtype": "call_failed", "tool_name": "search", "status": "error" }, ctx=ctx) analytics.flush() raise if __name__ == "__main__": mcp.run() ``` ## What's Tracked Automatically? When you pass the `ctx` parameter, the Surfa SDK automatically extracts: * ✅ **Client ID** - Identifies the MCP client (e.g., Claude Desktop) from MCP context * ✅ **Request ID** - Tracks individual requests from MCP context * ✅ **Timestamps** - When events occurred * ✅ **Sequence Numbers** - Event ordering within a session * ✅ **Runtime Metadata** - Provider, model, mode (via `set_runtime()`) You need to provide: * **kind** - Event kind (e.g., "tool", "session", "runtime") * **subtype** - Event subtype (e.g., "call\_started", "call\_completed") * **tool\_name** - Name of the tool being called * **Custom fields** - Status, latency, error messages, etc. ## Next Steps Learn how to track different types of events Full guide to setting up Claude Desktop integration Explore all MCP server tools for querying analytics Tips for getting the most out of Surfa ## Troubleshooting **Check:** * API key is correct (`SURFA_INGEST_KEY`) * API URL is set (`SURFA_API_URL`) * Your MCP server is actually running * No firewall blocking outbound requests **Debug:** ```python theme={null} import logging logging.basicConfig(level=logging.DEBUG) ``` **Check:** * Config file path is correct * JSON syntax is valid (no trailing commas) * `uv` path is correct (run `which uv` to find it) * Surfa MCP directory path is absolute, not relative **Debug:** Check Claude Desktop logs: ```bash theme={null} tail -f ~/Library/Logs/Claude/mcp*.log ``` **Solution:** Make sure you installed the SDK in the correct environment: ```bash theme={null} pip install surfa-ingest # or uv pip install surfa-ingest ``` Verify installation: ```bash theme={null} pip list | grep surfa ``` ## Need Help? Browse the full documentation Get help from the community Report bugs or request features Contact our support team # Remote MCP Deployment Source: https://docs.surfa.dev/remote-mcp-deployment Deploy your MCP server to production with HTTP/SSE transport ## Overview Deploy your MCP server to the cloud for remote access via HTTP/SSE transport. This enables: * Access from anywhere (not just localhost) * Integration with web applications * Multi-user support * Always-available analytics ## Deployment Options ### Fly.io (Recommended) **Why Fly.io:** * Built-in MCP support (`fly mcp wrap`) * Automatic HTTPS * Global edge network * Free tier available **Steps:** 1. **Install flyctl** ```bash theme={null} curl -L https://fly.io/install.sh | sh ``` 2. **Wrap your MCP server** ```bash theme={null} fly mcp wrap --port 8080 ``` 3. **Deploy** ```bash theme={null} fly deploy ``` 4. **Get your endpoint** ```bash theme={null} fly status # Your MCP is now at: https://your-app.fly.dev/sse ``` Fly.io machines auto-stop after 5 minutes of inactivity. The Surfa platform handles cold starts automatically with retry logic (3 attempts × 9 seconds). ### Other Platforms **Railway, Render, Heroku:** * Use HTTP/SSE transport * Expose port 8080 * Set environment variables * Enable HTTPS ## Transport: SSE vs STDIO ### STDIO (Local Only) **Pros:** * ✅ Fast, low latency * ✅ Simple setup **Cons:** * ❌ Only works on localhost * ❌ Can't be accessed remotely ### SSE (Remote-Ready) **Pros:** * ✅ Works over HTTP/HTTPS * ✅ Accessible from anywhere * ✅ Web-compatible **Cons:** * ⚠️ Slightly higher latency * ⚠️ May have cold starts **When to use SSE:** * Deploying to cloud platforms * Building web applications * Multi-user scenarios * Remote access needed ## Authentication ### API Key via Headers ```bash theme={null} curl -X POST https://your-mcp.fly.dev/sse \ -H "Authorization: Bearer sk_live_your_key" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":"1","method":"tools/list","params":{}}' ``` ### In Surfa Platform The platform automatically handles authentication when you add your MCP with headers: ```json theme={null} { "Authorization": "Bearer sk_live_your_key" } ``` ## Cold Start Handling Remote MCPs may "sleep" after inactivity. The Surfa platform handles this automatically: Returns 202 (machine starting) Wait 9 seconds, try again Wait 9 seconds, try again Wait 9 seconds, try again Tools discovered and displayed **Total wait time:** Up to 27 seconds for cold starts. **To avoid cold starts:** * Keep machines always-on (costs more) * Use health check pings * Accept the retry delay ## Testing Your Deployment ### 1. Test with curl ```bash theme={null} curl -X POST https://your-mcp.fly.dev/sse \ -H "Authorization: Bearer sk_live_your_key" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":"1","method":"tools/list","params":{}}' ``` ### 2. Test in Surfa Platform Go to **Settings → MCP** Click **Add New MCP** * Name: "My Remote MCP" * Transport: SSE * Endpoint: `https://your-mcp.fly.dev/sse` * Headers: `{"Authorization": "Bearer sk_live_your_key"}` Click **Test Connection** Wait for retries (if cold start) See discovered tools ✅ ## CORS Configuration If accessing from web apps, enable CORS: ```python theme={null} from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["https://surfa.dev"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) ``` ## Security Best Practices Never expose API keys over HTTP. Use HTTPS in production. Check API keys on every request. Reject invalid keys immediately. Prevent abuse by limiting requests per IP or API key. Track all API requests for auditing and debugging. Regularly rotate API keys and revoke old ones. ## Troubleshooting **Cause:** Machine is starting (cold start) **Solution:** * Wait for retry logic to complete * Check Fly.io logs: `fly logs` * Verify machine is running: `fly status` **Cause:** Invalid or missing API key **Solution:** * Check API key is correct * Verify Authorization header format: `Bearer sk_live_...` * Ensure key has correct permissions **Cause:** Server not responding after 27 seconds **Solution:** * Increase timeout in Surfa platform * Check machine is running: `fly status` * Configure always-on if needed **Cause:** MCP server not returning tools **Solution:** * Verify `/sse` endpoint is correct * Check `initialize` request works * Test `tools/list` manually with curl * Review server logs for errors ## Next Steps Learn how to test your MCP connection Connect Claude Desktop to your remote MCP Complete API documentation View source and contribute