Appearance
MCP Server
Tendios exposes an MCP (Model Context Protocol) server so AI assistants and agents can query public procurement data and your own pipeline directly, without you writing any HTTP integration code.
MCP is an open standard for connecting language models to external tools. Once the Tendios server is connected, your assistant can search tenders, pull a tender's full details, resolve CPV codes, look up contracting authorities, read your alerts and opportunity pipeline, and — with your confirmation — create alerts, move opportunities between stages, and add tasks, all as a natural part of the conversation.
The server wraps the same v1 use cases as the REST API, returns the same versioned response contracts, and is authenticated with the same API keys.
Endpoint
https://mcp.tendios.com/api/mcpThe server speaks the Streamable HTTP transport. It runs in stateless mode — a fresh server instance is built per request, so there are no sessions to manage and no session affinity required.
| Property | Value |
|---|---|
| Server name | tendios-mcp |
| Server version | 1.0.0 |
| Transport | Streamable HTTP |
| Tools exposed | 20 |
Requirements
MCP access requires an API key with the MCP capability enabled. This is a per-key flag, separate from the key itself: an existing REST API key will not work against the MCP server until the flag is turned on.
- Have an API key. If your plan includes API access you can generate one yourself — see Authentication. API access is available on Enterprise plans.
- Ask us to enable MCP on that key by contacting support@tendios.com. The flag is set by the Tendios team and can't be turned on from the app.
A key without the flag is rejected with 403 Forbidden:
json
{
"statusCode": 403,
"message": "API key is not enabled for MCP access"
}Authentication
Authenticate exactly as you do for the REST API: send your key in the api-key header on every request. There is no OAuth flow and no token exchange.
api-key: tend_your_api_key_hereAccount-scoped tools (context, alerts, pipelines, kanban, opportunities, and tasks) resolve your account from the API key itself. You never pass an account id — a key can only ever reach the data belonging to its own account. Call whoami to see which account, organization, and plan a key resolves to.
Connecting a client
Most MCP clients — Claude Code, Claude Desktop, and others — use the same mcpServers configuration shape. Add the Tendios server with your API key as a header:
json
{
"mcpServers": {
"tendios": {
"type": "http",
"url": "https://mcp.tendios.com/api/mcp",
"headers": {
"api-key": "tend_your_api_key_here"
}
}
}
}In Claude Code this goes in your project's .mcp.json (or add it with claude mcp add). In Claude Desktop it goes in your claude_desktop_config.json. Restart the client after editing the file, then confirm the tendios server is listed as connected.
Treat the config file as a secret
Your API key sits in plain text in these config files. Don't commit them to a public repository. If a key leaks, you can disable or delete it yourself and generate a new one, or contact support@tendios.com to have it rotated.
Verifying the connection
To check the endpoint outside a client, post a JSON-RPC request directly. MCP clients perform the initialize handshake for you; because the server is stateless you can also call methods without carrying a session id:
bash
curl -X POST https://mcp.tendios.com/api/mcp \
-H "api-key: tend_your_api_key_here" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }'A healthy server responds with the list of available tools and their input schemas.
Available tools
Twenty tools are exposed: six read public procurement data, ten read data scoped to your own account, and four write to it.
| Tool | Scope | Purpose |
|---|---|---|
search_tenders | Public | Full-text and faceted search over tenders |
get_tender | Public | Full details of one tender by id |
get_tender_sheets | Public | Documents attached to a tender |
get_cpv | Public | Details of a CPV code |
get_organization | Public | Organization profile by id or slug |
get_organization_award_stats | Public | Award statistics by year for an organization |
whoami | Account | Which account, organization, and plan the key resolves to |
list_alerts | Account | Every alert configured for the account |
get_alert | Account | Full configuration of one alert |
get_alert_results | Account | Tenders matching one of your saved alerts |
list_pipelines | Account | Pipelines and their stages, with the IDs other tools need |
get_kanban_status_totals | Account | Opportunity totals per pipeline status |
get_kanban_opportunities_by_status | Account | Paginated opportunities in one pipeline status |
search_kanban_opportunities | Account | Search a pipeline, grouped by status |
get_opportunity_workflow_actions | Account | Workflow action records for an opportunity |
list_opportunity_tasks | Account | Tasks attached to an opportunity |
create_alert | Account write | Create a new alert |
update_alert | Account write | Change fields on an existing alert |
change_opportunity_status | Account write | Move an opportunity to another pipeline stage |
create_opportunity_task | Account write | Add a task to an opportunity |
See MCP Tools for the full parameter reference for each tool.
Start with list_pipelines and whoami
The kanban and opportunity tools need pipeline and stage identifiers. Rather than making you look them up, list_pipelines returns every pipeline with its stages, and the kanban tools accept either a UUID or the exact pipeline or stage name it returned. whoami confirms which account a key is acting on. Together they let an assistant orient itself in two calls before touching anything.
Response format
Every tool returns its payload as JSON inside a single text content block:
json
{
"content": [{ "type": "text", "text": "{\"id\":\"507f1f77bcf86cd799439011\", ... }" }]
}The JSON inside text follows the same v1 response contracts as the REST API, so a field means the same thing whether you reached it through MCP or through GET /v1/tenders/{id}. Paginated tools return the familiar { data, pagination } envelope described in Pagination.
References
Most tools also return a structuredContent.references object alongside the text. Each reference is a { type, id, label } triple naming an entity the assistant can feed straight into a follow-up tool call, so it never has to parse IDs back out of prose:
json
{
"references": {
"alerts": [
{ "type": "alert", "id": "507f1f77bcf86cd799439011", "label": "IT services in Madrid" }
]
}
}Reference types are tender, alert, organization, pipeline, pipeline-status, opportunity, and task.
Tools that take an entity as input are correspondingly forgiving: the alert and opportunity parameters accept a reference ID or a Tendios URL pasted from the browser, and pipeline and stage parameters accept a UUID or the exact name from list_pipelines.
Rate limits
The MCP server is rate-limited per API key, not per IP. Each key uses its own configured limit; keys without an explicit limit fall back to 30 requests per minute.
http
HTTP/1.1 429 Too Many RequestsThis limit is lower than the REST API's
The REST API allows 300 requests per minute. The MCP default is 30 per minute, because a single assistant turn typically makes only a handful of tool calls. If your agent workload needs more, contact support@tendios.com to raise the limit on your key.
Exceeding the limit blocks further requests for the remainder of the 60-second window.
Errors
Authentication and authorization failures use standard HTTP status codes; tool-level failures come back as MCP error results.
| Code | Meaning |
|---|---|
401 | Missing api-key header, invalid key, expired or disabled key |
403 | Valid key, but MCP access is not enabled on it |
429 | Rate limit exceeded for this key |
See Errors for the shared error body shape.
Writing data
The MCP server is no longer read-only. Four tools write to your account:
| Tool | Writes | Guard |
|---|---|---|
create_alert | Creates an alert | Requires confirmed: true |
update_alert | Changes fields on an existing alert | Idempotent; omitted fields stay as they are |
change_opportunity_status | Moves an opportunity to another stage | Requires confirmTerminal: true for terminal stages |
create_opportunity_task | Adds a task to an opportunity | Requires confirmed: true |
Nothing deletes. There is no tool that removes an alert, opportunity, or task — the most destructive action available is moving an opportunity into a terminal stage, which closes it.
Two-step confirmation
create_alert and create_opportunity_task refuse to act on the first call. Invoked without confirmed: true, they return an error result instructing the assistant to summarize what it is about to create and ask you to approve it; only a second call carrying confirmed: true performs the write. change_opportunity_status works the same way for terminal stages via confirmTerminal, and short-circuits harmlessly when the opportunity is already in the requested stage.
This puts the approval step inside the protocol rather than relying on the client to ask. Clients that surface MCP tool annotations will also show these four as non-read-only, with change_opportunity_status flagged destructive.
A key with MCP enabled can modify your account
Because writes share the same credential as reads, any key with the MCP flag can create alerts and tasks and move opportunities. If you want an assistant that can only look, don't hand it a key with MCP enabled — use the REST API read endpoints instead.
Not available as tools
- Tender sheet file download — binary streams don't map onto MCP tool results. Use
get_tender_sheets, which returns short-lived Tendios download links, and fetch the file over HTTP. - API key lifecycle metadata —
whoamideliberately exposes only safe caller context (principal, account, organization, plan). Key prefixes, expiry, and rate limits are not reachable from MCP; useGET /v1/api-keyfor those.