Skip to main content
Glama

ted-mcp: EU public tenders for AI agents

Read-only MCP server for TED (Tenders Electronic Daily), the EU's official public-procurement journal. It uses TED's free, keyless Search API v3; no account or API key is needed.

Tools

Tool

What it does

search_tenders

Search with simple filters: keywords, countries, CPV codes, date range, notice type. Returns title, buyer, country, CPV, value, link

count_tenders

Count matches only (one tiny request). Compare two date windows to see if demand is rising

search_notices

Raw TED expert query, for advanced use

check_query

Validate expert-query syntax without fetching notices

All tools are marked read-only. Notice text is returned as third-party data, and the server tells the AI to treat it as data, not instructions.

Example prompts once connected:

  • "How many EU tenders mentioning NIS2 were published since 2026-01-01, vs the same period last year?"

  • "List open Romanian IT tenders (CPV 72000000) published this month."

  • "Find active tenders in DE, AT and NL mentioning SBOM or 'Cyber Resilience Act'."

Related MCP server: LexSocket MCP Server

1. First run: check it against the real API

python3 -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
pytest -q                      # 20 tests, TED is mocked

# Real API smoke test (no key needed):
curl -s -X POST https://api.ted.europa.eu/v3/notices/search \
  -H 'Content-Type: application/json' \
  -d '{"query":"buyer-country=ROU AND publication-date>=20260901","fields":["publication-number","notice-title"],"limit":2}' | head -c 600

Query syntax follows TED's official Expert Search help: single words unquoted (FT ~ kubernetes), phrases quoted (FT ~ "cyber security"), and lists via IN (buyer-country IN (ROU DEU)). Use check_query to validate anything custom before running it.

2. Claude Desktop (stdio, local)

Settings → Developer → Edit Config, then add:

{
  "mcpServers": {
    "ted-tenders": {
      "command": "/ABSOLUTE/PATH/ted-mcp/.venv/bin/ted-mcp"
    }
  }
}

Windows path example: C:\\Users\\you\\ted-mcp\\.venv\\Scripts\\ted-mcp.exe. Restart Claude Desktop.

3. Claude Code

claude mcp add ted-tenders -- /ABSOLUTE/PATH/ted-mcp/.venv/bin/ted-mcp

4. Remote connector (claude.ai web, mobile)

claude.ai custom connectors need a public HTTPS URL. HTTP mode refuses to start without a token.

export TED_MCP_TOKEN=$(openssl rand -hex 24)     # save this in your password manager
docker build -t ted-mcp .
docker run -d --name ted-mcp --restart unless-stopped \
  -e TED_MCP_TOKEN=$TED_MCP_TOKEN \
  -e TED_MCP_ALLOWED_HOSTS=ted.example.com \
  -p 127.0.0.1:8000:8000 ted-mcp

Put TLS in front, for example with Caddy (automatic Let's Encrypt):

ted.example.com {
    reverse_proxy 127.0.0.1:8000
}

Then in claude.ai → Settings → Connectors → Add custom connector:

https://ted.example.com/mcp?token=YOUR_TOKEN

Health check (no token needed): https://ted.example.com/healthz

Security notes

  • The token is compared in constant time; requests without it get 401.

  • A token in a URL can end up in proxy logs. Keep access logs private and rotate the token if exposed. MCP clients that can send headers should use Authorization: Bearer <token> instead.

  • TED_MCP_ALLOWED_HOSTS enables DNS-rebinding protection. Set it to your domain.

  • The container runs as a non-root user and exposes only port 8000 on localhost. The server makes outbound calls to TED only.

  • Built-in politeness: at most 2 concurrent requests and 0.4 s between calls, with retries on 429/5xx.

Configuration

Variable

Default

Purpose

TED_MCP_TRANSPORT

stdio

stdio or http

TED_MCP_HOST / TED_MCP_PORT

127.0.0.1 / 8000

HTTP bind address

TED_MCP_TOKEN

none

Required for HTTP; minimum 24 characters

TED_MCP_ALLOWED_HOSTS

none

Comma-separated hostnames for DNS-rebinding protection

TED_API_URL

TED v3 search

Override for testing

Data and licence

TED notice metadata is published by the EU Publications Office for free reuse, including commercial use. Keep "Source: TED (ted.europa.eu)" attribution when you republish results. This project is independent and not affiliated with the EU. Code: MIT.

Available Tools

4 tools
check_queryB
Read-onlyIdempotent

Validate TED expert-search syntax without returning notices.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, and openWorldHint=true, covering the safety profile. The description adds the useful detail that it validates without returning notices, i.e. a validation-only result rather than data. It does not say what a failure/validity response looks like, but that is partly the output schema's job.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single compact sentence that front-loads the action and resource, with every clause earning its place (the 'without returning notices' clause carries real information about output).

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return-value explanation is not required. For a one-parameter validation tool this is close to adequate, but it omits any hint of how to interpret a validation result or what makes a query invalid. Adequate but with clear gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the schema carries no meaning for 'query'. The description compensates partially by identifying the expected format as 'TED expert-search syntax', which is genuinely useful semantics, but it gives no examples, constraints, or escaping rules for that syntax.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource: validating TED expert-search syntax, with the added scoping note that it does not return notices. This clearly distinguishes it from the search_* and count_tenders siblings, which retrieve data rather than validate a query. It stops short of naming those siblings, but the purpose is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no explicit when-to-use guidance or reference to alternatives. The implication that you run this before a search to pre-check syntax is inferable but never stated, and no conditions or exclusions are given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

count_tendersA
Read-onlyIdempotent

Count matching notices without listing them (cheap demand signal).

Same filters as search_tenders. Defaults to scope ALL so date ranges count the archive. Tip: compare the same filters across two date windows to see if demand is rising.

ParametersJSON Schema
NameRequiredDescriptionDefault
scopeNoALL
keywordsNo
countriesNo
cpv_codesNo
notice_typesNo
published_sinceNo
published_untilNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, non-destructive and openWorld, so safety is covered. The description adds real behavioral value beyond that: it returns a count rather than a list, it is cheap to call, and scope defaults to ALL so date ranges span the archive. Return shape is covered by the output schema, so nothing critical is missing.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core purpose in the first clause, then adds filter routing and a usage tip in two short sentences. The parenthetical and tip both earn their place, though the tip is slightly advisory rather than definitional.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 7-parameter count tool with an output schema and full annotation coverage, the description supplies the key non-obvious facts: no listing, cheap, scope default ALL. The main residual gap is parameter-level detail, which it offloads to a sibling rather than stating outright.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must carry the parameter burden. It explains the scope default and its effect ('ALL so date ranges count the archive') and delegates the rest to search_tenders' filter set, which is a useful pointer but not a substitute for documenting keywords, countries, cpv_codes, notice_types, and the published_since/until format.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('count matching notices') and immediately distinguishes it from the listing behavior of its sibling by saying 'without listing them'. The parenthetical '(cheap demand signal)' clarifies the intent behind the tool, so an agent understands both what it does and why it exists versus search_tenders.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes the agent to search_tenders for the filter set ('Same filters as search_tenders') and gives a concrete usage pattern (compare the same filters across two date windows). It lacks an explicit when-not clause (e.g., 'do not use when you need the actual notices'), but the context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_noticesA
Read-onlyIdempotent

Run a raw TED expert-search query (advanced).

Example: 'FT ~ kubernetes AND buyer-country IN (DEU AUT) AND publication-date>=20260801 SORT BY publication-date DESC' fields: TED field names to return (defaults to a standard set). Unknown fields are rejected by TED. Use check_query first if unsure about syntax.

ParametersJSON Schema
NameRequiredDescriptionDefault
pageNo
limitNo
queryYes
scopeNoACTIVE
fieldsNo
languageNoENG

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, open-world and non-destructive, so the safety profile is covered. The description adds genuinely useful behavioral context beyond that: fields default to a standard set and unknown fields are rejected by TED, i.e. the query is validated and can fail on bad field names.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the purpose, then supplies one worked example and two short rules. No filler; the lowercase 'fields:' block is slightly unpolished but every sentence carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need not be described, and the example query covers the hardest part of the syntax. However, the scope enum (ACTIVE/LATEST/ALL) governs which notices are searched and is left undefined, which is a material gap for a search tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must carry the load. It documents the required 'query' parameter with a concrete syntax example and explains 'fields' (default set, unknown fields rejected), but says nothing about page, limit, scope (ACTIVE/LATEST/ALL) or language, leaving four of six parameters unexplained anywhere.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource — running a raw TED expert-search query — and the parenthetical '(advanced)' signals this is the low-level escape hatch rather than a structured search. It does not explicitly name search_tenders as the simpler alternative, so sibling differentiation is only implicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"Use check_query first if unsure about syntax" gives an explicit routing rule to a named sibling for a specific condition. It stops short of stating when to use this raw query tool instead of search_tenders, which is the more consequential decision.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_tendersA
Read-onlyIdempotent

Search EU public tenders with simple filters.

keywords: full-text terms, OR-combined (e.g. ["NIS2", "cybersecurity audit"]). countries: buyer countries, ISO alpha-3 or alpha-2 (e.g. ["ROU", "DE"]). cpv_codes: 8-digit CPV codes, e.g. 72000000 (IT services), 48000000 (software packages). published_since / published_until: YYYY-MM-DD. notice_types: e.g. ["cn-standard"] (contract notices) or ["can-standard"] (award notices). scope: ACTIVE (open now), LATEST, or ALL (archive). Returns total match count plus normalised notices (title, buyer, country, CPV, value, URL).

ParametersJSON Schema
NameRequiredDescriptionDefault
pageNo
limitNo
scopeNoACTIVE
keywordsNo
languageNoENG
countriesNo
cpv_codesNo
notice_typesNo
published_sinceNo
published_untilNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, openWorldHint and destructiveHint=false, so the safety profile is covered. The description adds value with the return shape ('total match count plus normalised notices (title, buyer, country, CPV, value, URL)') and per-param semantics, but says nothing about pagination behavior, rate limits, or how scope=ALL affects volume.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One purpose sentence followed by a compact, front-loaded parameter list where every line adds new syntactic or format information. The structure is scannable; the only minor cost is that the list doubles as documentation for parameters the schema already names.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, yet the description still usefully summarizes the return payload; the filter parameters that drive matching are well covered. The gaps are the unset pagination and language parameters, which an agent may need for multi-page retrieval but which are less critical than the filters.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the full burden and mostly succeeds: it explains OR-combining for keywords, ISO alpha-3/alpha-2 for countries, 8-digit CPV format with examples, YYYY-MM-DD dates, and concrete notice_type values. It leaves page, limit, and language undocumented, which keeps it short of a 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first line states a specific verb and resource ('Search EU public tenders') plus a scope qualifier ('simple filters'), which is clear enough for an agent to act on. However, it never distinguishes itself from siblings like search_notices or count_tenders, so an agent cannot tell which search tool is correct without opening the schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'simple filters' and the scope enum explanations ('ACTIVE (open now), LATEST, or ALL (archive)') implicitly convey when this tool is appropriate, but there is no explicit when-not or alternative routing despite three closely related siblings (search_notices, count_tenders, check_query). Usage is inferable rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.1.0
    • First observedcheck_query
    • First observedcount_tenders
    • First observedsearch_notices
    • First observedsearch_tenders

TDQS

A3.8/5.0

Scored across 4 tools

Disambiguation4/5

The four tools have mostly distinct purposes: simple search, raw expert search, count-only, and syntax validation. search_tenders and search_notices overlap in domain but are differentiated by simple vs. advanced query modes, so misselection is possible but unlikely.

Naming Consistency5/5

All tool names follow a consistent snake_case verb_noun pattern: search_tenders, search_notices, count_tenders, check_query. The naming is predictable and readable.

Tool Count5/5

Four tools are well-scoped for a search-focused API: two search modes, a count utility, and a query validator. Each tool earns its place without redundancy or bloat.

Completeness4/5

The server covers discovery, advanced querying, counting, and query validation for EU tenders, which is strong for a read-only search domain. A minor gap is the absence of a direct notice-detail retrieval tool by ID, though returned URLs partially mitigate this.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    Exposes French and EU public procurement data (BOAMP + TED) as MCP tools for AI agents, enabling search for tenders, awards, and winner intelligence via typed filters.
    4
    62 PyPI
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables search and analysis of European public procurement tenders, including EU above-threshold (TED) and below-threshold from 11 national sources, with hybrid search and filtering.
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables AI agents to find, score, and monitor government contract opportunities across UK, EU, and US with AI-powered relevance scoring.
    2
    49 npm
    1
    MIT
  • F
    license
    A
    quality
    C
    maintenance
    Enables searching and analyzing Polish public procurement tenders from BZP and TED, including filtering, tender details and timelines, offers and contract values, buyer/contractor profiles, CPV code lookup, and regional statistics, with optional AI-summary endpoints.
    12
    1
    -