Built and run by one person.

Database Query API - Read-only SQL over Postgres & DuckDB (MCP)

TigZig API & MCP Hub
MCP Server (AI agents)SQL / Database APIOpen + Auth0 secured

Open, no-auth MCP server exposing read-only SQL over Postgres (Supabase) and DuckDB (embedded) - ~2.1M+ ball-by-ball deliveries plus match-level tables, refreshed twice daily - as agent tools, behind a multi-layer security stack. An Auth0-secured endpoint runs the same tools with login. Built for AI agents first, humans second: every page here has a Copy-as-Markdown button so you can hand it straight to your agent.

2databases
~2.1M+deliveries
Read-onlyenforced
multi-layersecurity stack
Open + Auth0two endpoints

Quick start

AI agents: this API is open (no auth) on the public endpoint, with an Auth0-secured endpoint alongside. Start with the OpenAPI spec for this API: https://db-mcp.tigzig.com/v1/openapi.json. The RFC 9727 catalog at api.tigzig.com/.well-known/api-catalog is a site-level directory of all TigZig APIs - go there only if you want a different API, not for more detail on this one.
MCP server for AI agents
https://db-mcp.tigzig.com/mcp
Streamable HTTP. Add as a custom connector in Claude.ai, ChatGPT, Cursor, n8n.
REST API base URL
https://db-mcp.tigzig.com/v1

Documentation

For people
db-mcp.tigzig.com/redoc

The full reference. Every endpoint, every field, and every enforced limit with the message you get when you cross it. Rendered from the running service, so it is never a copy that can fall behind.

For AI agents
db-mcp.tigzig.com/openapi.json

The machine contract for this API. The site-wide directory of every TigZig API is the RFC 9727 catalog at api.tigzig.com/.well-known/api-catalog.

What you can call

2 endpoints. Request bodies, every field and every enforced limit are in the full reference.

POST/v1/query/duckdbRun read-only SQL on DuckDB: T20 cricket ball-by-ball data
POST/v1/query/postgresRun read-only SQL on Postgres (Supabase): ODI cricket ball-by-ball data

Overview

This MCP server is also listed in the machine-readable MCP directory at /.well-known/mcp/servers.json. Connect a transport below and run read-only SQL as a tool call.

A security-hardened, read-only SQL query API exposing two databases as MCP tools for AI clients - an open endpoint for public use and an Auth0-secured endpoint for gated access. Connect from Claude Code, Claude Desktop, Claude.ai (web connectors), Claude in Excel, or any MCP client. Setup instructions in the README.

Prefer plain HTTP? The two query endpoints below take POST with a JSON body (primary; no URL-length limit). They also accept GET ...?sql=<url-encoded SQL> for fetch-only clients (browsers, no-code HTTP nodes) that cannot send a body.

MCP endpoints

Open MCP endpoint (no auth)

  • https://db-mcp.tigzig.com/mcp - Streamable HTTP, the recommended transport (MCP spec 2025-03-26). No API key, no login on any open endpoint - open by design, defended by the multi-layer stack below.

Secured MCP endpoint (Auth0 OAuth)

  • https://db-mcp.tigzig.com/mcp-secure - Streamable HTTP, Auth0-gated (recommended). Requires Auth0 login with an email whitelist - same databases and tools as the open endpoint, with authentication on top.

What's inside

Two databases, ~2.1M+ ball-by-ball deliveries. Each engine carries two tables joined on match_id - the deliveries, plus a match-level table. Each deliveries table holds exactly one format, and its name says which:

  • Postgres (Supabase) - odi_ball_by_ball (~1.36M ODI deliveries) + match_info.
  • DuckDB (embedded) - t20_ball_by_ball (~790K T20 deliveries) + match_info.
  • Refreshed twice daily, so the row counts above grow and recent matches are present.
  • Both read-only at database and application level.
  • JSON and TSV output (TSV uses ~70% fewer tokens).

One asymmetry worth knowing before you count anything. match_info covers all three formats (ODI, T20 and Test) in both engines, while each deliveries table holds only its own format. So a match_info row can exist with no deliveries in the database you are querying - Test matches have a match row and no deliveries anywhere - and match counts will not reconcile between the two tables unless you filter on match_type. This is deliberate: the two engines cannot be joined across, so each carries its own full copy of the match table.

Renamed, and the old names still work. The deliveries tables were odi_cricket_ball_by_ball and ball_by_ball; they are now odi_ball_by_ball and t20_ball_by_ball, so the table name tells you the format and the endpoint tells you the SQL dialect. The old names survive as views and return identical rows, so nothing you already wrote breaks - but write new queries against the new names.

Exact table and column names live in one canonical place, not copied here (so they can never drift): the query endpoint's own description in Swagger / openapi.json - the same text an MCP client hands the model as the tool description. The service root db-mcp.tigzig.com/ lists the tables too, and any query against a wrong table or column returns a 400 that hands back the real tables, all columns, and working calls.

Security hardening

The open endpoint is intentionally public - anyone with the URL can query. It is open by design, defended by a multi-layer stack rather than by obscurity:

  • Cloudflare WAF + application-level rate limiting.
  • Per-IP and global concurrency caps.
  • Multi-layer SQL validation - prefix allowlist, keyword blocklist, resource-exhaustion blocking, response size limits, comment rejection. Joins are allowed but bounded: equi-joins with an explicit ON work, while comma joins, CROSS JOIN and joins without ON are refused, and one SELECT may only carry so many joins. Every enforced limit, with its exact value and the refusal message you get, is listed on the interactive docs - generated from the running service, so it cannot go stale.
  • Schema discovery is readable - information_schema, SHOW TABLES and DESCRIBE work, so you can explore the shape without guessing. Server and role catalogs stay blocked.
  • Query timeouts with DuckDB engine interrupt.
  • Database-level read-only enforcement (Postgres + DuckDB) and container resource limits.
  • Error-message sanitization - no internal details leaked.
  • Auth0 OAuth with JWT verification on the secured endpoint.

For the broader web-app security checklist (120 items across all stacks), see tigzig.com/security.

Guides

This page is the reference - what the endpoints are and how to call them. The guides below are the long-form versions, with worked examples and the edges you only meet in real use:

Each is a plain page with a Markdown twin, so you can hand a URL straight to an agent. Come back to this page when you want parameter-level detail.

Rate limits

Published so a well-behaved client can plan around them. These are per-IP limits:

  • Per IP: 60 requests / minute.
  • At most 4 concurrent queries per IP.

You get a 429 with Retry-After and a body naming the budget you crossed.

Avoiding 429s: These are ~1M-row tables, so aggregate or LIMIT server-side in the SQL rather than paginating. One GROUP BY costs a single request where pagination costs dozens.

Every successful response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix epoch seconds), read straight from the running limiter, and Retry-After is set on 429. The current numbers are also published as machine-readable JSON at https://db-mcp.tigzig.com/, derived from live config. Read those at runtime rather than hard-coding the figures above - limits change, and these channels change with them.

Try it

No key, no signup.

Top ODI run-scorers in the 2023 season
Same shape, as a POST.
curl -X POST "https://db-mcp.tigzig.com/v1/query/postgres" -H 'Content-Type: application/json' -d '{"sql":"SELECT striker, SUM(runs_off_bat) AS runs, COUNT(*) AS balls FROM odi_ball_by_ball WHERE season = '2023' GROUP BY striker ORDER BY runs DESC LIMIT 10","format":"json"}'
Response
{"format":"json","columns":["striker","runs","balls"],
 "rows":[
        ["Asif Khan",610,678],
        ["SC Williams",600,437],
        ["V Aravind",549,787],
        ["P Nissanka",508,613],
        ["Waseem Muhammad",501,522],
        ["DJ Mitchell",493,503],
        ["SD Hope",489,529],
        ["Babar Azam",483,515],
        ["HT Tector",478,512],
        ["BA King",468,528]
 ],
 "row_count":10, "truncated":false}
Top T20 run-scorers in the 2023 season
Same shape, as a POST.
curl -X POST "https://db-mcp.tigzig.com/v1/query/duckdb" -H 'Content-Type: application/json' -d '{"sql":"SELECT striker, SUM(runs_off_bat) AS runs, COUNT(*) AS balls FROM t20_ball_by_ball WHERE season = '2023' GROUP BY striker ORDER BY runs DESC LIMIT 10","format":"json"}'
Response
{"format":"json","columns":["striker","runs","balls"],
 "rows":[
        ["S Sesazi",500,407],
        ["RG Mukasa",497,353],
        ["MS Chapman",493,327],
        ["Zeeshan Khan",478,365],
        ["VP Thamotharam",375,281],
        ["R Satheesan",334,274],
        ["L Bruce",313,300],
        ["AP Rajeevan",306,267],
        ["E Dusingizimana",300,228],
        ["V Saini",297,226]
 ],
 "row_count":10, "truncated":false}

Open-source edition

There is an open-source edition of this MCP server - an earlier release you can run as your own service or borrow components from. Treat it as a reference implementation rather than a mirror of what is running here: the hosted service has moved on since. Licensed Apache 2.0.

github.com/amararun/shared-fastapi-database-mcp

Built on FastAPI, fastapi-mcp, asyncpg, DuckDB, SlowAPI.

Auth0 OAuth is optional - the repo covers enabling the secured endpoint or running open-only.

Self-hosting a public MCP server is your responsibility for security. A public endpoint needs edge rate limiting, abuse detection and secret hygiene. Full checklist: tigzig.com/security