Developers · MCP
Connect Foral to your AI assistant (MCP)
The Model Context Protocol (MCP) lets AI assistants — Claude, Cursor, ChatGPT and others — call Foral directly as tools. Foral runs two kinds of MCP server: the Foral Data MCP for Premium accounts, and one MCP server for each published company agent.
1. Foral Data MCP
The Foral Data MCP server exposes the Data API as MCP tools: public procurement (Portal BASE), municipal signals, municipalities, your alerts and the indicator catalogue. URL: https://data.foral.ai/mcp.
It is for Premium accounts. Use the same sk_live_… key as the REST API — create it on the API keys page. The key's scopes and your plan decide which tools tools/list shows; a tool your key cannot call is simply not listed.
Same limits as the REST API: every HTTP request counts against your per-minute rate limit, and each tools/call counts as one request against your monthly quota. The handshake (initialize, tools/list, ping) does not use quota. Start with the me tool — it returns your plan, domains, scopes and limits.
| Tool | Data domain |
|---|---|
me | — |
municipalities_search | indicators |
municipalities_get | indicators |
indicators_list | indicators |
indicators_series | indicators |
indicators_snapshot | indicators |
signals_list | signals |
signals_get | signals |
signal_cases_list | signals |
procurement_contracts | procurement |
procurement_aggregate | procurement |
procurement_entities | procurement |
procurement_entity_profile | procurement |
procurement_lookup_municipalities | procurement |
procurement_lookup_entities | procurement |
procurement_announcements | procurement |
procurement_amendments | procurement |
alert_rules | alerts |
alert_matches | alerts |
structuredContent (the same JSON the REST endpoint returns) and a text copy of it. A failed call (for example, monthly quota exhausted) comes back as a tool result with isError: true and the problem document of the REST API.2. Company agent MCP
Each company agent published on the MCP channel has its own server: https://agents.foral.ai/<slug>/mcp. <slug> is the deployment slug shown in the company studio.
Authenticate with an agent credential created by a company admin in the studio (Deploy step, MCP channel). The tools are the ones the agent enables — its knowledge, service area and, when enabled, procurement reads. The deployment must be live.
To publish your own company agent, see Foral Agents.
3. Install in your client
The examples use the Data MCP. For a company agent, use https://agents.foral.ai/<slug>/mcp and its credential instead. Replace sk_live_... with your key and keep it secret.
Claude Code
Then run claude mcp list — the server shows as connected.
Terminal
claude mcp add --transport http foral https://data.foral.ai/mcp \
--header "Authorization: Bearer sk_live_..."Claude Desktop (through mcp-remote)
Add this block to claude_desktop_config.json (Settings → Developer → Edit config) and restart Claude Desktop. It needs Node.js. The key goes in env so the header survives argument splitting on Windows.
claude_desktop_config.json
{
"mcpServers": {
"foral": {
"command": "npx",
"args": [
"-y", "mcp-remote", "https://data.foral.ai/mcp",
"--header", "Authorization:${AUTH_HEADER}",
"--transport", "http-only"
],
"env": { "AUTH_HEADER": "Bearer sk_live_..." }
}
}
}claude.ai, Claude Desktop connectors and ChatGPT
These clients connect with OAuth: you sign in to Foral and approve access — no key to paste.
- claude.ai and Claude Desktop: Customize → Connectors → Add → Add custom connector. Enter a name and the server URL, click Add (Claude detects OAuth and registers itself), then Connect. Foral opens: sign in and click Allow access.
- ChatGPT: Plugins → Add → Add custom MCP server. Enter a name and the server URL, keep Authentication on OAuth (ChatGPT registers itself), accept the risk notice and click Create as a plugin, then Continue. Foral opens: sign in and click Allow access.
- You can see and disconnect the apps you connected at any time in Connected apps.
Cursor and Windsurf
Cursor: add the block to ~/.cursor/mcp.json (or .cursor/mcp.json in a project). Windsurf: same block in ~/.codeium/windsurf/mcp_config.json, with serverUrl in place of url.
mcp.json
{
"mcpServers": {
"foral": {
"url": "https://data.foral.ai/mcp",
"headers": { "Authorization": "Bearer sk_live_..." }
}
}
}Python (MCP SDK 1.x)
Minimal example with the official MCP Python SDK, version 1.x (tested with 1.30). The 2.x SDK changed this client API.
client.py
# pip install "mcp>=1.30,<2" httpx
import asyncio
import httpx
from mcp import ClientSession
from mcp.client.streamable_http import streamable_http_client
URL = "https://data.foral.ai/mcp"
KEY = "sk_live_..."
async def main() -> None:
headers = {"Authorization": f"Bearer {KEY}"}
async with httpx.AsyncClient(headers=headers, timeout=30) as http:
async with streamable_http_client(URL, http_client=http) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
print([t.name for t in tools.tools])
result = await session.call_tool("municipalities_search", {"q": "Faro", "limit": 5})
print(result.structuredContent)
asyncio.run(main())Raw JSON-RPC (curl)
The servers speak Streamable HTTP with JSON responses: one JSON-RPC message per POST. There is no session to open — list the tools and call one.
List the tools
curl -s https://data.foral.ai/mcp \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Protocol-Version: 2025-06-18" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Call a tool
curl -s https://data.foral.ai/mcp \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Protocol-Version: 2025-06-18" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"municipalities_search","arguments":{"q":"Faro","limit":5}}}'4. Limits
A 429 carries Retry-After (seconds). Wait that long before you retry.
| Server | Limit |
|---|---|
| Foral Data MCP | Your plan's per-minute rate limit (every HTTP request) and monthly quota (one request per tools/call), shared with the REST API. |
| Company agent MCP | 60 requests/minute per credential, 200/minute per deployment (defaults). Connecting takes about 3 requests. |
5. Troubleshooting
| Answer | What it means |
|---|---|
401 | Missing, wrong, expired or revoked key. Send Authorization: Bearer sk_live_…. |
403 | The request came from a browser page on another site (Origin header). Call the server from a desktop client, an IDE or your backend. On the Data MCP, a 403 can also mean your plan does not include Data API access (api-access-required, upgrade to Premium) or the key is restricted to a beta cohort (beta-access-required). |
404 (company agent) | Wrong <slug>, the deployment is not live, or the credential belongs to another deployment or lacks the MCP channel. |
405 | GET is not served: these servers are POST only. MCP clients handle this on their own. |
400 | Unsupported Mcp-Protocol-Version. Use 2025-06-18 or 2025-11-25. |
429 | Rate limit. Wait Retry-After seconds. |
quota-exceeded / 402 | Monthly quota exhausted. Wait for the reset or upgrade the plan. |
| A tool is missing | Your key lacks the scope or your plan lacks the domain. Call me to see both. |
503 | Temporary. Retry with backoff. |