Skip to content

MCP server

MCP (Model Context Protocol) is how AI clients are given tools. schakl. serves one at /mcp: every endpoint of the API is a tool there. You connect Claude Desktop, Claude Code or any other MCP client to your own installation and ask your question in plain language, without exporting anything first. What the AI may see and do is decided by the scopes on the key you hand it.

The server runs at https://<your-host>/mcp, on your organisation’s own address. Its screen is Instellingen → API en MCP (Settings → API and MCP): that is where you mint a key and get the connection line for your client with it, where the clients you connected over OAuth are listed, and where an admin creates OAuth clients with a fixed client id and secret. Keys that belong to nobody live under Instellingen → Service-accounts (Settings → Service accounts).

Claude (as a custom connector, in the browser or in Claude Desktop) and ChatGPT speak OAuth 2.1, and then need nothing but the address.

  1. In your client, add a custom connector with the address https://<your-host>/mcp. For ChatGPT that is https://<your-host>/mcp/compact: the full list does not fit its budget.
  2. The client registers itself and sends you to your own workspace’s sign-in screen. You sign in as always, with two-factor verification or your organisation’s SSO.
  3. On Toegang toestaan (Allow access) you choose what the client may do:
    • Alles wat ik zelf mag (everything I may do): read and change, in every module you can reach;
    • Alleen lezen (read only): everything you may read, never changing anything;
    • Zelf kiezen (choose myself): a list you tick yourself, grouped per module.
  4. Press Toestaan (Allow). No secret is copied anywhere.

What that flow hands back is an ordinary personal API key, not a second kind of access: every request is capped by your live permissions and your client selection. The first two choices are stored as a rule, not as today’s list: a module switched on later is included by itself, because a chat client never asks for consent again. Zelf kiezen stays a fixed list; a new module only joins when you reconnect.

Your connections are listed under Gekoppelde clients (connected clients) on Instellingen → API en MCP. Ontkoppelen (disconnect) ends every session of that client, and it cannot refresh itself.

A client that asks for a client id and secret

Section titled “A client that asks for a client id and secret”

Claude needs no client id. Tools that do not register themselves, such as an enterprise connector config or an automation tool, do ask for a fixed client id and secret. An admin holding settings.oauth.manage creates one under OAuth-clients on the same screen: a name and one redirect URI per line. The secret is shown exactly once. The same card also lists the clients that registered themselves, marked apart, with Nieuw secret (new secret; only for clients created here) and Intrekken (revoke), which switches the client off along with every key it ever issued, whoever’s.

  1. Open Instellingen → API en MCP. Under Wat koppel je? (what are you connecting) choose AI-assistent (MCP).

  2. Give the key a Naam (name) and choose under Wat mag die doen? (what may it do): Alleen lezen (read only, the default and usually enough), Lezen en schrijven (read and write), or Kies per recht (per permission).

  3. Choose the section that fits your client under Welke tools (which tools; see below). Leave Verloopt op (expires on) empty for a key that never expires and press Sleutel aanmaken.

  4. The screen prints the connection line with the key already in it. For Claude Code:

    Terminal window
    claude mcp add --transport http schakl https://<your-host>/mcp \
    --header "Authorization: Bearer schakl_…"

    Copy it straight away: the key is shown only once.

  5. Any other client that speaks Streamable HTTP: point it at the same address with the header Authorization: Bearer schakl_… or X-API-Key: schakl_….

  6. Ask the client to list its tools. If nothing appears, check that you used your organisation’s own address — a key only exists on its own organisation’s hostname.

The tool list is derived straight from the API itself, so it never lags behind. Every endpoint under /api/v1 is a tool, named after the operation: list_companies, create_task, time_summary.

Only the modules your organisation has switched on have endpoints, so the tool list automatically follows which modules you use. Turn a module off and its tools disappear.

A few things are deliberately left out:

Not a toolWhy
Signing in and the first-run wizardsession handling, meaningless for a key
The instance operator’s administration surfacethat belongs to the instance owner, not the organisation
Import: inspecting a file and importing itthose need a file, plus the human judgement of mapping columns

The rest of import and export stays available: reading an entity’s columns and exporting data are both useful to an AI.

The full surface is ~620 tools, and that is the right default for a coding agent, which reads the list once. It is the wrong one for a chat client, which puts every tool in the model’s context on every turn and therefore budgets: ChatGPT allows 5,000 tokens for all tools together, and the full list is around 527,000. No amount of trimming closes that gap — at that count you have about 85 tokens per tool, which does not buy a name. Only fewer tools works.

So the same server answers on narrower URLs, and you paste whichever one fits the client:

URLWhat it offers
/mcpEverything. The default, and what a coding agent wants
/mcp/compactA curated read-only fourteen, small enough for a chat client’s ceiling
/mcp/<module>One module’s own endpoints, for example /mcp/google-ads or /mcp/invoicing
/mcp/<bundle>A named job spanning several modules (see below)
BundleModules
/mcp/agentClients, contacts, projects, tasks, hours, contact moments
/mcp/infraDomains, websites, hosting, Cloudflare, OXXA, uptime, WordPress
/mcp/financeInvoicing, subscriptions, Mollie
/mcp/growthGoogle Ads, Google Analytics, Search Console, Tag Manager, marketing, reporting

Google Analytics, Search Console and WordPress also have a section of their own (/mcp/google-analytics, /mcp/google-search-console, /mcp/wordpress): an agent asked to work a client’s Analytics wants seventeen tools, not the hundred of the whole bundle.

A module section is derived from that module’s own routes, so an endpoint added tomorrow is served tomorrow; a bundle names modules, never tools, which keeps it as self-maintaining as the sections it unions. A URL that names nothing is refused and tells you what does exist, rather than quietly falling back to all 620 — that would look like it worked and could not be spotted by reading.

A tool call travels exactly the same path as an ordinary request from a screen: hostname to organisation, row-level isolation in the database, then that endpoint’s own permission check. There is no second route to the data. A call the key holds no permission for simply comes back as an error on the tool.

Do watch what you tick: the tool surface includes writes, because it is the whole API. If you want the AI to read along only, give the key read permissions only. That is a choice you make when minting the key, not something the server enforces for you.

PermissionWhat it opensDefault
apikeys.personal.manageManage own API keysAdministrator, Member
apikeys.service_account.manageManage service accountsAdministrator
settings.oauth.manageManage OAuth clients with a fixed client id and secretAdministrator

After that the key decides everything: each ticked scope is a permission from the same catalogue as Instellingen → Rollen (Settings → Roles). A personal key is additionally re-capped to its owner’s live permissions on every single request.

  • An anonymous call gets a 401, with the invitation to do OAuth. That is how a client discovers the server, by being refused, and it means a stranger cannot see which modules you run.
  • The transport is Streamable HTTP with self-contained JSON responses. The older SSE transport is not offered.
  • 600 calls per minute per key. An AI that searches enthusiastically can run into that.
  • Intrekken (Revoke) in the key screen removes access immediately, with nothing to change on the client side.
  • The tool list comes entirely from the API description. Hand-written, smarter tools (such as “find this client”, or a client’s performance with the comparison already worked out) do exist inside the in-app assistant, but are not part of this list.
  • A route that takes a file as multipart is not a tool. Where it matters there is a JSON twin, such as uploading an attachment as base64.
  • To not serve the server at all, set SCHAKL_MCP_ENABLED=false on the server. That removes /mcp entirely.