Decision record
ADR 0001: Curated task tools, toolsets and a read-only generic GET
- Status: Accepted
- Date: 2026-11-07
Context#
The sevDesk API has 154 documented operations (spec snapshot of 2026-10-06) plus several undocumented ones that real integrations need. Existing sevDesk MCP servers expose them in one of three ways:
- One tool per endpoint. For example 103 CRUD tools in one server. Every quirk of the API
(silent truncation at 100 records,
getPdfmarking documents as sent, update-2.0 tax fields, requiredcontactPerson) becomes the model's problem. Wrong hard-coded ids were the most common bug class we found across these servers. - A few generic tools that take any operation and a raw body. Small tool list, but the model has to know the API, and writes bypass any validation or confirmation.
- Dynamic toolsets or a tool-search gateway on the server: meta-tools that list, search or enable groups of tools at runtime.
Evidence that shaped the choice:
- Tool-selection accuracy drops as the number of tools grows, and every tool definition costs context tokens on every request (RAG-MCP, arXiv:2505.03275; Speakeasy, "Less is more"). Reports put the trouble zone between a few dozen and about 100 tools.
- GitHub removed the dynamic toolsets from its MCP server in May 2026 (github/github-mcp-server#2512): the mode added a parallel registration path and meta-tools, and the remote server never offered it.
- MCP clients now defer and search tool definitions themselves (progressive discovery). A server-side gateway duplicates that work and hides tools from clients that do it better.
- MCP 2026-07-28 has no protocol-level session, and our HTTP mode is stateless (ADR 0004). "Enable a toolset, then new tools appear on this connection" does not fit that model.
Decision#
- Curated, task-shaped tools. Each tool does one bookkeeping task and carries the sevDesk
knowledge for it: drafting an invoice fills
contactPersonand picks the tax fields for the account's system version; rendering a PDF always setspreventSendBy; list tools page and aggregate on the server. Tool names followsevdesk_<resource>_<verb>. - One tier per tool; reads and writes never share a tool (see ADR 0003).
- Toolsets (
meta,contacts,invoices,credit-notes,orders,vouchers,banking,parts,reports,advanced) chosen withSEVDESK_TOOLSETS. The default read-only set is about 20 tools; everything enabled stays at or below 45. - Static registration. The tool list is fixed for the life of a server instance and filtered
only by toolset and tier at startup.
tools/listreturns one page in a stable order. No meta-tools that enable, list or search tools. - Reference data as lookups and resources. Tax rules, booking accounts, units, countries and
payment methods come from a
lookuptool and MCP resources instead of ids in descriptions. - Escape hatch, read-only. The
advancedtoolset (off by default) has a genericapi_getfor any GET path, with path validation, and refuses state-changing GETs. There is no generic write tool: every write goes through a curated tool with validation, dry-run and confirmation.
Consequences#
- Good: the model chooses among a small set of clearly named tools; fewer tokens per request.
- Good: safety and API knowledge live in tested code, not in prompts.
- Good: the tool surface is reviewable. A
tools/listsnapshot per tier configuration and a metadata lint run in CI, and PRs show the tool-surface diff. - Bad: coverage grows only as fast as we write tools. An operation without a curated tool is
reachable read-only through
api_get, and not at all for writes. - Bad: more code to maintain than a generated 1:1 mapping. Contributors can request endpoints with an issue form, and "add a tool in 5 steps" is documented in CONTRIBUTING.md.
Alternatives considered#
- 1:1 endpoint mapping (generated). Rejected: too many tools for reliable selection, pushes API quirks onto the model, and inherits the spec's errors (ADR 0002).
- Generic call tool for all methods. Rejected for writes: it bypasses validation, tiers and
confirmation. Kept for reads as
api_get. - Server-side dynamic toolsets or tool-search gateway. Rejected: clients now do progressive discovery themselves, it does not fit a stateless server, and GitHub dropped the same mode.