MCP server
mcp.igregulator.io exposes iGregulator as a Model Context Protocol
server. Compatible clients (Claude Desktop, Cursor, Windsurf, Cline) can
discover and call iGregulator tools without your users writing any
HTTP code.
Two ways in, one plan: sign in with your iGregulator account (claude.ai,
ChatGPT and Claude Code connectors — no key to copy), or send the same API
key that works against https://api.igregulator.io. Either way tool calls
are counted against your account’s normal quota (shared by all your keys and
connected apps) — there’s no separate MCP plan.
Why MCP vs. direct API
Section titled “Why MCP vs. direct API”| Direct API | MCP | |
|---|---|---|
| Where it runs | Your application code | The agent runtime (Claude Desktop, Cursor, etc.) |
| Auth | Your code stores + sends Bearer header | Configured once, agent reuses across sessions |
| Tool discovery | Read OpenAPI, write wrappers | Agent auto-introspects |
| Best for | Server-to-server, batch jobs | End-user agent flows, KYB / compliance assistants |
Use direct API when you build the integration. Use MCP when an agent on the user’s machine needs to talk to iGregulator.
Available tools
Section titled “Available tools”Mapped to the public API surface. Tool output is lean — a compact
verdict, not the full REST payload — so it stays cheap in your context;
fetch get_operator when you need the full dossier.
| Tool | Backs onto | Use case |
|---|---|---|
check_domain | GET /v1/check | ”Is bet365.com licensed?” (supports as_of) |
check_domain_batch | POST /v1/check/batch | A KYB sweep — up to 100 domains in one call |
search_operators | GET /v1/operators/search | Brand → registered legal entity |
get_operator | GET /v1/operators/:slug | Full record incl. licenses + domains (supports as_of). Long domain lists are capped — 50 by default, listed links first; domains_total says how many there are and domains_limit (up to 1,000) asks for more |
get_operator_regulatory_actions | GET /v1/operators/:slug/regulatory-actions | Enforcement history (fines, suspensions). An empty list means none is linked to that operator — not a clean record |
check_coverage | GET /v1/health/coverage | Data freshness per jurisdiction |
list_jurisdictions | GET /v1/jurisdictions | Coverage overview |
get_jurisdiction | GET /v1/jurisdictions/:code | One regulator’s metadata |
get_license | GET /v1/licenses/:license_id | Specific license detail |
get_license_history | GET /v1/licenses/:license_id/history | Status-change audit trail |
check_domain and each check_domain_batch row lead with the API’s verdict
and verdict_detail (see Reading the answer). On a
no-match, they return match_absence_reason + checked_jurisdictions — so an
agent says “not found in the jurisdictions we cover”, never an unqualified
“unlicensed”.
On a match, check_domain also passes through verification_url (the
regulator’s own per-domain verification page — Curaçao certificate /
Tobique seal — the citation to surface next to the verdict) and, for
dual-licensed domains, a compact jurisdictions[] array so the answer
never flattens a multi-register brand to one jurisdiction.
Reading the answer
Section titled “Reading the answer”Branch on verdict; quote verdict_detail. Only licensed and
licensed_provisional mean licensed now — licensed_provisional is in force
provisionally (a Curaçao licence under the CGA’s final assessment), so say so and
suggest a re-check. Everything else — licence_not_active, domain_not_listed,
related_host_listed, name_match_only, not_found, generic_term, or a value
added later — is “not confirmed as licensed by the registers we cover”, never
“unlicensed”. The rules behind each value are in
confidence scoring.
verdict_detail already names the status in its own words. When you need to
say more about a status, the values are not interchangeable, and the server’s
own instructions tell the model so on connect:
status | What it means | What an agent should say |
|---|---|---|
revoked, suspended | The regulator published an enforcement decision and we read it | That, citing the source |
surrendered | The operator gave the licence up | Not licensed now — not enforcement |
expired | It ran out | Not licensed now |
not_in_register | The register stopped listing it; no reason was published | ”No longer listed” — never “revoked” |
unknown | Listed, with wording we could not classify | Escalate |
check_domain, check_domain_batch and get_operator return a lean result
(they land in the model’s context window). A check carries the verdict, status,
status_qualifier, domain_status and — as the API sends them —
regulator_name, status_source_url (the page that published this status,
often not the register itself), status_observed_at, license_id, and
related_hosts[] when the host isn’t listed but others on its registrable domain
are (never that host’s licence — see
which host answers).
Next to a verification_url (a check, or a get_operator domain) comes
verification_page_status: what the regulator’s own page printed about the
licence at our latest read, verbatim, with verification_page_read_at. If that
word does not say “in force”, the page is not proof (get_operator’s summary
names such domains); don’t cite the link as confirmation.
get_operator gives each licence its license_id, status, status_qualifier
and license_reference_is_ours (true for Kahnawake, Tobique and the Isle of
Man: license_number is then our reference, not the regulator’s). Pass a
license_id to get_license for the full REST record — status_source_url,
status_observed_at, not_listed_since, last_listed_at — and to
get_license_history for every event with its note and the evidence hash
(snapshot_sha256). An event with corrected_at set was later withdrawn:
never quote it as fact. A correction_note without corrected_at is a reworded
note on an event that stands, not a withdrawal.
get_operator_regulatory_actions says what its list means in the result itself
(note, sources_read): which regulator publications we read, and that not every
published action is matched to an operator — an empty list is not a clean record.
Webhook + watchlist management (creating endpoints, rotating secrets, etc.) is intentionally not exposed via MCP — those belong in the dashboard at app.igregulator.io. MCP is read-only by design.
Installation
Section titled “Installation”Connect from claude.ai, ChatGPT or Claude Code (sign in — no key)
Section titled “Connect from claude.ai, ChatGPT or Claude Code (sign in — no key)”Paste this URL as a custom connector, then sign in with your iGregulator account when asked:
https://mcp.igregulator.io/mcp/accountNo account yet? The sign-in page has a sign up link — free, no card — and you come straight back to the connection afterwards.
-
claude.ai (and Claude Desktop, mobile): Settings → Connectors → Add custom connector, paste the URL, Add, then Connect. Leave the advanced OAuth client fields empty: Claude registers itself.
-
ChatGPT: turn on developer mode (Settings → Security and login → Developer mode; availability depends on your plan and workspace), add a new app/connector with the URL above, and choose OAuth if asked.
-
Claude Code:
Terminal window claude mcp add --transport http igregulator https://mcp.igregulator.io/mcp/accountthen run
/mcpin a session and pick igregulator → Authenticate; a browser opens for the sign-in.
You will see a consent screen on app.igregulator.io naming the app, the site it
returns you to, and what it gets: read-only licence lookups — all ten tools —
on your plan, counted against your monthly quota, until you revoke it. It never
sees your password or API keys and cannot change your account. Every app you
connect is listed under API keys → Connected apps;
Revoke cuts it off at once (its tokens stop working on the next request).
How it works, for the curious: mcp.igregulator.io/mcp/account is an OAuth 2.1
protected resource (RFC 9728 metadata);
app.igregulator.io is its authorization server
(RFC 8414 metadata) —
dynamic client registration, PKCE (S256), one scope (mcp:read), one-hour access
tokens and rotating refresh tokens, all per the
MCP authorization spec.
The same endpoint also accepts an API key (Authorization: Bearer igk_…).
Try it without a key
Section titled “Try it without a key”No account needed to see what the server does. With no Authorization header, check_domain, search_operators (top 3), list_jurisdictions and check_coverage work, 10 requests per hour per IP; the other six tools answer with a pointer to a free key.
claude mcp add --transport http igregulator https://mcp.igregulator.io/mcpWith a key (all ten tools)
Section titled “With a key (all ten tools)”Create a free account and get an API key at app.igregulator.io/api-keys. Founding members get the full Starter plan free — all tools, no card.
Claude Code
Section titled “Claude Code”Native HTTP MCP support. One command:
claude mcp add --transport http igregulator https://mcp.igregulator.io/mcp \ --header "Authorization: Bearer igk_..."Replace igk_... with your real key. Verify with claude mcp list — igregulator should appear with status connected. Restart any active Claude Code session and the tools become available immediately.
Claude Desktop (recommended path: mcp-remote shim)
Section titled “Claude Desktop (recommended path: mcp-remote shim)”Claude Desktop’s stdio-based MCP support is universal; HTTP support is rolling out. The most reliable config today uses mcp-remote to bridge stdio → streamable HTTP. Open ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows) and add:
{ "mcpServers": { "igregulator": { "command": "npx", "args": [ "-y", "mcp-remote", "https://mcp.igregulator.io/mcp", "--header", "Authorization: Bearer igk_..." ] } }}Replace igk_... with your real key. Restart Claude Desktop. Verify with: “What iGregulator tools do you have?”
Claude Desktop (native HTTP — newer builds only)
Section titled “Claude Desktop (native HTTP — newer builds only)”If your Claude Desktop build supports remote MCP servers natively, you can drop the mcp-remote shim:
{ "mcpServers": { "igregulator": { "url": "https://mcp.igregulator.io/mcp", "transport": "streamable-http", "headers": { "Authorization": "Bearer igk_..." } } }}If this gives you an “unknown transport” error, your build is too old — fall back to the mcp-remote shim above.
Cursor
Section titled “Cursor”Cursor → Settings → Features → Model Context Protocol → Add new MCP server. Use the same mcp-remote shim:
npx -y mcp-remote https://mcp.igregulator.io/mcp --header "Authorization: Bearer igk_..."Windsurf
Section titled “Windsurf”Windsurf → Settings → Cascade → Manage MCP Servers. Same mcp-remote shim pattern as Cursor; Windsurf documents per-version specifics in their MCP guide.
Cline / other clients
Section titled “Cline / other clients”Anything that speaks MCP can connect. Refer to your client’s docs for HTTP/SSE transport configuration; the connect URL is https://mcp.igregulator.io/mcp and auth is a bearer header named Authorization.
Example prompts
Section titled “Example prompts”Check whether bet365.com is licensed.→ Agent calls check_domain with domain="bet365.com", returns UKGC license details.
Find every operator named "Flutter" and show me their licenses.→ search_operators(q="flutter") followed by get_operator(slug=...) for each result.
What's the regulatory action history for license <UUID>?→ get_license_history(license_id=...) returns the full status-change timeline.
Compare UKGC and MGA — what license types do they each issue?→ get_jurisdiction(code="UKGC") + get_jurisdiction(code="MGA").
Here are 40 merchant domains — which are licensed?→ one check_domain_batch(domains=[...]) call, not 40 separate checks.
Was virginbet.com licensed on 2026-03-01?→ check_domain(domain="virginbet.com", as_of="2026-03-01") — reads as_of.knowledge (won’t assert a status from before tracking began).
Any enforcement actions against Flutter?→ get_operator_regulatory_actions(slug="flutter-uk-limited").
Authentication
Section titled “Authentication”Two credentials work:
- Signing in (OAuth 2.1, at
https://mcp.igregulator.io/mcp/account): what claude.ai, ChatGPT and Claude Code connectors do. The MCP server checks each access token withapp.igregulator.ioon every request — it must be active, issued for this endpoint, and carrymcp:read— then calls the API for you with its own internal credential; your token is never passed on. A revoked connection (Connected apps → Revoke) or a canceled plan stops working on the next request. Without a token the endpoint answers401withWWW-Authenticate: Bearer resource_metadata="…", which is what starts the sign-in. - An API key, on either endpoint:
API keys are bearer tokens. The same key works against the direct API and the MCP server. Rotate or revoke at app.igregulator.io/api-keys; changes take effect within seconds across both surfaces.
The MCP gateway does not store your key. It checks that the header is shaped Bearer <key> and forwards the key on each tool call to the API, which runs the same requireApiKey middleware that direct-HTTP callers go through — an unknown or revoked key is refused there, on the first tool call.
A key is optional. Without one, the gateway calls only what the REST API serves without a key (/v1/check, /v1/operators/search, /v1/jurisdictions, /v1/health/coverage) and the API applies its public limit to your IP, not the gateway’s. A header that is present but malformed is still rejected with a 401.
Rate limits
Section titled “Rate limits”Without a key: 10 requests per hour per IP — the REST API’s own counters, kept per route, not a separate MCP allowance. A keyless tool call counts against the route it calls, from your IP: check_domain spends the same /v1/check counter as a direct request (ten keyless check_domain calls from one IP in an hour use up that IP’s /v1/check allowance for direct requests too), search_operators spends /v1/operators/search’s, list_jurisdictions /v1/jurisdictions’s and check_coverage /v1/health/coverage’s. With a key, tool calls count against your account’s existing quota, one call per tool call. HTTP headers don’t cross the MCP boundary: when a limit trips, the tool result is an error whose text is { "http_status": 429, …the API's error body… } — code, details.reason, and details.reset_at where the API sets it — plus, for a keyless caller, a how_to_fix hint.
Founding (Starter) keys reach every tool. Only legacy trial-tier keys are scoped — to the four tools that also work without a key (check_domain, search_operators, list_jurisdictions, check_coverage; 1,000 calls/day) — and return 402 payment_required with details.reason=endpoint_requires_paid_plan on the other tools.
Discovery
Section titled “Discovery”This server publishes a manifest at three URLs (community convention; not a canonical MCP spec field — canonical discovery is the user typing the URL into their client):
https://mcp.igregulator.io/.well-known/mcp.jsonhttps://api.igregulator.io/.well-known/mcp.jsonhttps://igregulator.io/.well-known/mcp.json
All three return the same JSON: server URL, transport, auth flow (key, or OAuth at /mcp/account), key-request URL, docs URL.
OAuth discovery for the signed-in endpoint follows the MCP spec: Protected Resource
Metadata at /.well-known/oauth-protected-resource/mcp/account
(also at the root /.well-known/oauth-protected-resource), naming the authorization
server https://app.igregulator.io/api/auth, whose metadata is at
/.well-known/oauth-authorization-server/api/auth.
Troubleshooting
Section titled “Troubleshooting”“Tool not found” — usually the agent never connected. Check that your client logs show iGregulator listed under MCP servers. If it’s missing, the bearer header is malformed or the URL is unreachable.
“Connection failed” or DNS errors — curl -I https://mcp.igregulator.io/health should return 200 OK. If not, the issue is upstream of MCP.
Connector says it can’t connect, or asks to sign in again — the sign-in was revoked under Connected apps, or went unused for 30 days (refresh tokens expire after 30 days without use). Connect again; you won’t be asked to approve an app you already approved unless you revoked it.
“402 / plan_inactive” from a signed-in connector — the account has no active plan; signed-in connections are refused exactly as its keys would be.
“401 / api_key_missing / api_key_invalid” — your key isn’t being passed. With mcp-remote, double-check the --header arg includes Bearer (with the space). Quoting matters in some shells; if your config has issues, restart the client.
“402 / endpoint_requires_paid_plan” — a legacy trial-tier key hit a paid endpoint. Free founding (Starter) accounts don’t see this; create one at app.igregulator.io/signup, or ask the agent to stay on the four keyless tools.
“429 / rate limited” — same quota as direct API. Rate-limit headers are not passed through; the API’s error body is (http_status, code, details.reason, and details.reset_at where set). Branch on details.reason as in the errors reference: per_second_limit_exceeded clears in a second, monthly_request_limit_reached at details.reset_at, and the keyless quota_exceeded at the top of the next UTC hour.
Feedback
Section titled “Feedback”If a tool description or argument schema needs work, write to founder@igregulator.io — agent-voice quality is something we iterate on.