MCP Server Reference

What is MCP?

The Model Context Protocol (MCP) is an open standard that lets AI assistants call external tools during a conversation. Instead of copy-pasting data into a chat window, your AI client (Claude, Cursor, Gemini, etc.) automatically calls the right tool, fetches live data from Spekta, and uses it to answer your question — all within the conversation.

Think of MCP as a typed RPC layer between LLMs and data sources. Each "tool" is a function with a JSON Schema describing its inputs and outputs. The AI decides which tool to call, fills in the parameters from context, and shows you the result in plain English.

Spekta exposes 6 MCP tools covering events, ticket sales, revenue, comparisons, attendee stats, and provider connections.


Transport options

ClientTransportURL
Claude DesktopSSE (Server-Sent Events)https://api.spekta.io/mcp/sse
ChatGPT Custom ActionsREST (HTTP POST)https://api.spekta.io/tools/<tool_name>
Cursor, Gemini, othersSSEhttps://api.spekta.io/mcp/sse
Local / self-hostedstdio or SSESee Self-hosting Guide

Authentication

API key setup

Every request (SSE or REST) must include a Spekta API key.

Generate a key

  1. Log in to Spekta.
  2. Go to Settings → API Keys.
  3. Click Generate key, give it a name, and copy the value immediately — it is shown only once.

Keys look like: tix_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Passing the key

For MCP SSE clients, set the X-Spekta-API-Key header in your MCP config:

{
  "mcpServers": {
    "spekta": {
      "command": "npx",
      "args": ["mcp-remote", "https://api.spekta.io/mcp/sse"],
      "env": {
        "X-Spekta-API-Key": "tix_live_xxxxxxxx"
      }
    }
  }
}

For direct REST calls:

curl -X POST https://api.spekta.io/tools/list_events \
  -H "X-Spekta-API-Key: tix_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"provider": "all", "page": 1, "page_size": 20}'

Coming in Phase 3: OAuth 2.0 flow for team-level access without sharing individual API keys.

Key scopes

Keys currently have full read access to all data for the owning tenant. Write access (connecting providers, triggering syncs) is reserved for dashboard UI operations and is not exposed via MCP.

Revoking a key

Go to Settings → API Keys and click Revoke next to the key. Revoked keys return 401 immediately.


Tool Reference

1. list_events

List events visible to the authenticated tenant, with optional filters.

Input schema

{
  "type": "object",
  "properties": {
    "provider": {
      "type": "string",
      "default": "all",
      "description": "Filter by provider ID (e.g. 'eventbrite', 'weezevent') or 'all'"
    },
    "date_from": {
      "type": "string",
      "format": "date",
      "description": "Start date filter, YYYY-MM-DD"
    },
    "date_to": {
      "type": "string",
      "format": "date",
      "description": "End date filter, YYYY-MM-DD"
    },
    "status": {
      "type": "string",
      "default": "all",
      "enum": ["upcoming", "live", "past", "cancelled", "all"]
    },
    "search": {
      "type": "string",
      "description": "Full-text search against event name and description"
    },
    "page": {
      "type": "integer",
      "minimum": 1,
      "default": 1
    },
    "page_size": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 20
    }
  }
}

Output schema

{
  "type": "object",
  "properties": {
    "events": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "event_id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "provider": { "type": "string" },
          "start_date": { "type": "string", "format": "date-time" },
          "end_date": { "type": "string", "format": "date-time" },
          "venue": { "type": "string" },
          "status": { "type": "string" },
          "capacity": { "type": "integer" }
        }
      }
    },
    "total": { "type": "integer" },
    "page": { "type": "integer" },
    "page_size": { "type": "integer" }
  }
}

Example

curl -X POST https://api.spekta.io/tools/list_events \
  -H "X-Spekta-API-Key: tix_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "eventbrite",
    "date_from": "2025-06-01",
    "date_to": "2025-12-31",
    "status": "upcoming",
    "page_size": 10
  }'
{
  "events": [
    {
      "event_id": "a1b2c3d4-...",
      "name": "Summer Beats Festival",
      "provider": "eventbrite",
      "start_date": "2025-08-15T18:00:00Z",
      "venue": "Parc de la Villette, Paris",
      "status": "upcoming",
      "capacity": 5000
    }
  ],
  "total": 42,
  "page": 1,
  "page_size": 10
}

2. get_ticket_sales

Return time-series ticket sales data for an event.

Input schema

{
  "type": "object",
  "required": ["event_id"],
  "properties": {
    "event_id": {
      "type": "string",
      "format": "uuid",
      "description": "Spekta event UUID (from list_events)"
    },
    "date_from": { "type": "string", "format": "date" },
    "date_to": { "type": "string", "format": "date" },
    "granularity": {
      "type": "string",
      "enum": ["hour", "day", "week", "month"],
      "default": "day"
    },
    "provider": {
      "type": "string",
      "default": "all"
    },
    "breakdown_by": {
      "type": "string",
      "enum": ["ticket_type", "provider", "none"],
      "default": "none"
    }
  }
}

Output schema

{
  "type": "object",
  "properties": {
    "event_id": { "type": "string" },
    "granularity": { "type": "string" },
    "series": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "period": { "type": "string", "description": "ISO 8601 period start" },
          "tickets_sold": { "type": "integer" },
          "gross_revenue_cents": { "type": "integer" },
          "currency": { "type": "string" }
        }
      }
    }
  }
}

Example

curl -X POST https://api.spekta.io/tools/get_ticket_sales \
  -H "X-Spekta-API-Key: tix_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "event_id": "a1b2c3d4-...",
    "granularity": "week",
    "breakdown_by": "ticket_type"
  }'

3. get_revenue

Return revenue figures, optionally grouped by event, provider, ticket type, or month.

Input schema

{
  "type": "object",
  "properties": {
    "event_ids": {
      "type": "array",
      "items": { "type": "string", "format": "uuid" },
      "description": "Limit to specific events; omit for all events"
    },
    "date_from": { "type": "string", "format": "date" },
    "date_to": { "type": "string", "format": "date" },
    "provider": { "type": "string", "default": "all" },
    "ticket_type": {
      "type": "string",
      "description": "Filter by ticket type name (case-insensitive substring match)"
    },
    "group_by": {
      "type": "string",
      "enum": ["event", "provider", "ticket_type", "month", "none"],
      "default": "none"
    },
    "currency": {
      "type": "string",
      "default": "EUR",
      "description": "Target currency for conversion (ISO 4217)"
    }
  }
}

Output schema

{
  "type": "object",
  "properties": {
    "currency": { "type": "string" },
    "total_gross": { "type": "number" },
    "total_net": { "type": "number" },
    "total_fees": { "type": "number" },
    "total_refunds": { "type": "number" },
    "tickets_sold": { "type": "integer" },
    "rows": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "group_key": { "type": "string" },
          "gross": { "type": "number" },
          "net": { "type": "number" },
          "fees": { "type": "number" },
          "refunds": { "type": "number" },
          "tickets_sold": { "type": "integer" }
        }
      }
    }
  }
}

Example

curl -X POST https://api.spekta.io/tools/get_revenue \
  -H "X-Spekta-API-Key: tix_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "date_from": "2025-01-01",
    "date_to": "2025-12-31",
    "group_by": "provider",
    "currency": "EUR"
  }'

4. compare_events

Side-by-side comparison of two or more events across a metric.

Input schema

{
  "type": "object",
  "required": ["event_ids", "metric"],
  "properties": {
    "event_ids": {
      "type": "array",
      "items": { "type": "string", "format": "uuid" },
      "minItems": 2,
      "maxItems": 10
    },
    "metric": {
      "type": "string",
      "enum": [
        "tickets_sold",
        "revenue_gross",
        "revenue_net",
        "attendance_rate",
        "early_bird_sales",
        "avg_ticket_price"
      ]
    },
    "date_window_days_before_event": {
      "type": "integer",
      "minimum": 1,
      "description": "Limit sales data to N days before each event's start date (useful for comparing sales velocity)"
    },
    "normalize": {
      "type": "boolean",
      "default": false,
      "description": "Express metric as % of capacity rather than absolute value"
    }
  }
}

Output schema

{
  "type": "object",
  "properties": {
    "metric": { "type": "string" },
    "normalized": { "type": "boolean" },
    "events": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "event_id": { "type": "string" },
          "event_name": { "type": "string" },
          "value": { "type": "number" },
          "rank": { "type": "integer" }
        }
      }
    }
  }
}

Example

curl -X POST https://api.spekta.io/tools/compare_events \
  -H "X-Spekta-API-Key: tix_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "event_ids": ["uuid-1", "uuid-2", "uuid-3"],
    "metric": "revenue_gross",
    "normalize": true
  }'

5. get_attendee_stats

Aggregated attendee statistics for an event. No PII is returned — all breakdowns are aggregate counts.

Input schema

{
  "type": "object",
  "required": ["event_id"],
  "properties": {
    "event_id": {
      "type": "string",
      "format": "uuid"
    },
    "breakdown_by": {
      "type": "string",
      "enum": ["ticket_type", "purchase_channel", "country", "none"],
      "default": "none"
    }
  }
}

Output schema

{
  "type": "object",
  "properties": {
    "event_id": { "type": "string" },
    "total_attendees": { "type": "integer" },
    "checked_in": { "type": "integer" },
    "check_in_rate": { "type": "number", "description": "0.0 – 1.0" },
    "breakdown": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "group_key": { "type": "string" },
          "count": { "type": "integer" },
          "pct": { "type": "number" }
        }
      }
    }
  }
}

Example

curl -X POST https://api.spekta.io/tools/get_attendee_stats \
  -H "X-Spekta-API-Key: tix_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "event_id": "a1b2c3d4-...",
    "breakdown_by": "ticket_type"
  }'

6. get_provider_connections

List connected providers and their health status for the authenticated tenant.

Input schema

{
  "type": "object",
  "properties": {}
}

(No parameters required.)

Output schema

{
  "type": "object",
  "properties": {
    "connections": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "provider": { "type": "string" },
          "status": {
            "type": "string",
            "enum": ["connected", "syncing", "error"]
          },
          "last_synced_at": { "type": "string", "format": "date-time" },
          "events_count": { "type": "integer" }
        }
      }
    }
  }
}

Example

curl -X POST https://api.spekta.io/tools/get_provider_connections \
  -H "X-Spekta-API-Key: tix_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "connections": [
    {
      "provider": "eventbrite",
      "status": "connected",
      "last_synced_at": "2025-08-01T10:30:00Z",
      "events_count": 42
    },
    {
      "provider": "weezevent",
      "status": "connected",
      "last_synced_at": "2025-08-01T10:31:00Z",
      "events_count": 18
    }
  ]
}

Error codes

All errors follow a consistent JSON envelope:

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "API key is invalid or has been revoked.",
    "request_id": "req_xxxxxxxx"
  }
}
HTTP statusError codeCause
400VALIDATION_ERRORInput failed JSON Schema validation. Check details field for per-field errors.
401UNAUTHORIZEDMissing or invalid X-Spekta-API-Key.
403FORBIDDENKey is valid but lacks permission for this operation.
404NOT_FOUNDThe requested event ID does not exist or belongs to a different tenant.
422UNPROCESSABLEInput was valid JSON but semantically invalid (e.g. date_from after date_to).
429RATE_LIMITEDToo many requests. See Rate limits.
500INTERNAL_ERRORUnexpected server error. The request_id can be shared with support.
503SERVICE_UNAVAILABLEDatabase or dependency health check failed.

Handling errors in TypeScript

const res = await fetch('https://api.spekta.io/tools/list_events', {
  method: 'POST',
  headers: {
    'X-Spekta-API-Key': process.env.SPEKTA_API_KEY!,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ provider: 'all' }),
});

if (!res.ok) {
  const err = await res.json();
  console.error(`[${err.error.code}] ${err.error.message}`);
  // retry only on 429 or 503
}

Rate limits

Rate limits are applied per API key.

PlanRequests / minuteRequests / day
Free201 000
Pro10010 000
Business500100 000

When you exceed the limit, the server returns HTTP 429 with a Retry-After header (seconds until the limit resets):

HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1722510000

The in-memory rate limiter resets every 60 seconds. If you are running bulk analytics, use page_size=100 and add a short pause between requests to stay within limits.

MCP clients: Claude Desktop and similar MCP hosts make calls on your behalf. If you hit rate limits inside Claude, wait a moment and retry the conversation — the AI will automatically recall the tool.