Skip to content
Mathieu Mafille
Go back

Building an MCP Server for Switzerland's Public Procurement Platform

At Digilac, we help organizations get more out of their tools — including AI assistants. One challenge we kept running into: when you ask Claude about Swiss public tenders, it has no idea what you’re talking about. The data is out there, on SIMAP.ch, but AI assistants can’t access it. So I built a bridge: simap-mcp.

Manual tender search vs AI-powered insight with SIMAP-MCP AI-generated image via Google Nano Banana 2

Table of contents

Open Table of contents

What is MCP?

MCP stands for Model Context Protocol, an open standard introduced by Anthropic that lets AI assistants connect to external tools and data sources. Think of it as a plugin system for LLMs.

Without MCP, an AI assistant only knows what’s in its training data and whatever you paste into the conversation. With MCP, it can call tools — search a database, fetch an API, read a file — and use the results to answer your questions.

An MCP server exposes a set of tools (functions the AI can call) over a standardized protocol. You configure your AI client (Claude Desktop, Claude Code, etc.) to load the server, and from that point on, the AI can use those tools automatically when relevant.

The key insight: you don’t need to change how you talk to the AI. You just ask your question, and the AI decides which tools to call, in what order, to give you a good answer.

What is SIMAP.ch?

SIMAP.ch is Switzerland’s official public procurement platform. It’s where federal, cantonal, and municipal institutions publish tenders — construction projects, IT services, supply contracts, and more. If you’re a company looking for public contracts in Switzerland, SIMAP is where you look.

The platform has a public API, but it’s not the most approachable thing to work with. You need to know CPV codes (Common Procurement Vocabulary), BKP codes (for construction), canton codes, process types, publication types… there’s a lot of vocabulary to get right before you can find anything useful.

That’s exactly where an AI assistant can help — if it has access to the data.

What simap-mcp does

simap-mcp is a TypeScript MCP server that wraps the SIMAP public API and exposes it as a set of tools Claude can call. Once configured, you can have conversations like:

Show me new IT tenders published this week in canton Vaud

Find construction contracts in Geneva related to road infrastructure

What’s the publication history of this tender?

Behind the scenes, Claude figures out which tools to call and what parameters to pass. You don’t need to know the API — you just ask in plain language.

The server exposes 14 tools:

ToolWhat it does
search_tendersSearch tenders by keyword, date, type, canton, CPV code…
get_tender_detailsGet full details of a specific tender
search_cpv_codesSearch CPV codes by keyword
browse_cpv_treeNavigate the CPV hierarchy
list_cantonsList all Swiss cantons with their codes
list_institutionsList public institutions that publish tenders
get_publication_historyGet the publication history of a project
search_proc_officesSearch procurement offices
… and more for BKP, NPK, OAG codes

How to use it

You’ll need Node.js 24 LTS installed on your machine. Then, open Claude Desktop, go to Settings → Developer → Edit Config, and add the following to your config file:

{
  "mcpServers": {
    "simap": {
      "command": "npx",
      "args": ["-y", "@digilac/simap-mcp"]
    }
  }
}

Once configured, restart Claude Desktop and start asking about Swiss public tenders.

How it’s built

The server is written in TypeScript and uses the official MCP SDK. Each tool is defined with a name, a description (which the AI uses to decide when to call it), and a JSON Schema for its parameters (which the AI uses to construct the call).

A simplified example of what a tool definition looks like:

server.tool(
  "list_cantons",
  "List all Swiss cantons with their codes",
  {},
  async () => {
    const cantons = await client.listCantons();
    return { content: [{ type: "text", text: formatCantons(cantons) }] };
  }
);

The actual SIMAP API calls are handled by a thin HTTP client in src/api/client.ts. The main complexity is in translating between what the AI sends (natural-language-friendly parameters) and what the API expects (specific enums, UUIDs, date formats).

One thing that took some thought: the tool descriptions matter a lot. The AI reads them to decide which tool to use. A vague description leads to wrong tool choices. A precise description — including when not to use a tool — leads to much better results.

What I learned

MCP is surprisingly easy to get started with. The SDK handles the protocol layer, so you mostly just write functions and describe them. The barrier to building an MCP server is lower than I expected.

The hard part is API design, not protocol plumbing. Getting the tool boundaries right — what should be one tool versus two, how to handle pagination, what to return when there are no results — that’s where most of the design work went.

LLMs are good at composing tools. I was skeptical at first, but watching Claude chain multiple tool calls to answer a complex question (search → get details → get history) is genuinely impressive. The model figures out the sequence on its own.

Claude Code was a genuine productivity multiplier. I leaned heavily on Claude Code throughout this project — for scaffolding the initial structure, working through the API mapping, and iterating on tool descriptions. It’s one thing to know a tool exists; it’s another to build something real with it and see where it actually helps.

Thanks for reading! If you have questions or feedback, feel free to reach out via email or LinkedIn. Next up, I’ll dig into how I used Claude Code throughout this project — stay tuned.


Share this post on:

Previous Post
Claude Code — The AI Coding Companion That Changed My Workflow
Next Post
Hello World — Why I'm Starting This Blog