Vektis MCP
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Vektis MCPFind dental practices with postcode 9403 and list their KvK-nummer."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Vektis MCP
MCP server for the public AGB-register of Vektis: search Dutch healthcare providers, practices and locations by AGB-code, name, zorgsoort, place or postcode, and read a full registration with its kwalificaties, erkenningen and relations. No API key, no account.
Unofficial. This project is not affiliated with or endorsed by Vektis. Every tool call reads the public register live; nothing is stored.
Voor Nederlandse gebruikers: zoek zorgverleners, ondernemingen en vestigingen in het AGB-register vanuit Claude, Cursor of een andere MCP-client. Stel vragen in het Nederlands; de veldnamen in de antwoorden zijn dezelfde als op vektis.nl.
Quick start
You need uv and internet access. The package is
not on PyPI yet, so uvx installs it straight from GitHub.
Claude Desktop: add to claude_desktop_config.json and restart.
{
"mcpServers": {
"vektis": {
"command": "uvx",
"args": ["--from", "git+https://github.com/pooryam92/vektis-mcp", "vektis-mcp"]
}
}
}Claude Code:
claude mcp add vektis -- uvx --from git+https://github.com/pooryam92/vektis-mcp vektis-mcpOther clients use the same command and args under their own top-level key:
Client | File | Key |
Cursor |
|
|
VS Code |
|
|
Windsurf |
|
|
git clone https://github.com/pooryam92/vektis-mcp
cd vektis-mcp
uv sync
uv run vektis-mcpIn a client config, point command at uv with
"args": ["--directory", "/absolute/path/to/vektis-mcp", "run", "vektis-mcp"].
pip install . followed by vektis-mcp, or python -m vektis_mcp, works too.
uvx --from git+https://github.com/pooryam92/vektis-mcp vektis-mcp --transport streamable-http --host 127.0.0.1 --port 8000
claude mcp add --transport http vektis http://127.0.0.1:8000/mcpThe server binds to localhost. Before exposing it further, put HTTPS, authentication, Origin validation and request limits in front of it. Run one process: request pacing is per process.
Related MCP server: NPI MCP Server
Example prompts
"Zoek huisartsenpraktijken in Assen."
"Wat is AGB-code 01000451 en is die nog actief?"
"Welke zorgverleners werken bij vestiging 71001001?"
"Geef de contactgegevens en erkenningen van onderneming 01010001."
"Find dental practices with postcode 9403 and list their KvK-nummer."
"Which kwalificatie codes exist for zorgsoort 01?"
A typical flow is search, then agb_get_record with the record_type from the
result. Search results are candidates, not confirmed identities.
Tools
Tool | What it does |
| Individual care providers by AGB-code or name, filtered by zorgsoort and kwalificaties |
| Ondernemingen and vestigingen by code or name, plus plaats, postcode and kvknummer |
| One exact registration with optional sections: basisregistratie, contact, kwalificaties, erkenningen, relaties |
| Zorgsoort and kwalificatie codes by code or label, offline |
The code tables are also served as the resource vektis://codes/{side}. Full
parameter and response semantics are in docs/tools.md.
Terminology
Term | Meaning |
AGB-code | Eight-digit identifier, leading zeros included. People and businesses can share a |
Zorgverlener | An individual care provider (huisarts, tandarts, fysiotherapeut, ...). Search shows initials only and no address. |
Onderneming | The legal entity: a practice, hospital or company. |
Vestiging | A location of an onderneming. Its code appears only in the page URL, never on the page itself. |
Zorgsoort | Two-digit care category, e.g. |
Kwalificatie | Four-digit qualification under a zorgsoort, e.g. |
Erkenning | A recognition such as a BIG or KvK registration or a WTZA notification. |
Configuration
All optional, set as environment variables (the env block of a client config).
Variable | Default | Purpose |
|
| Minimum interval between requests to vektis.nl |
|
| Per-request timeout |
|
| Total budget per tool call, including pacing and retries |
|
| Retries after the first attempt |
| a generic Chrome string | User-Agent sent to Vektis |
Bad values fail at startup. By default the client sends the headers of an
ordinary browser visit, so the traffic does not stand out; set
VEKTIS_USER_AGENT if you want to identify your deployment instead.
Data source and limits
The source is the public register at vektis.nl, read as HTML. There is no official API behind this server, so a site redesign can break parsing; such failures surface as
parse_errortool errors, never as empty results.The register renders at most 500 rows per search.
source_truncatedin the response tells you the cap was hit; narrow the search by zorgsoort, plaats or postcode.Requests are paced and retried with backoff to stay polite to Vektis. One tool call is one to four live requests.
The register contains names and contact details of care professionals. Use the data within Vektis's terms of use and applicable privacy law.
Only the AGB-register is covered today. Other public Vektis sources are listed in ROADMAP.md.
Development
uv sync
uv run pytest -q
npx @modelcontextprotocol/inspector uv run vektis-mcpThe test suite runs fully offline against synthetic fixtures. How the server is built is described in docs/design.md; what the live site does, field by field, in docs/agb-source-notes.md.
License
MIT for the code, see LICENSE. The register data belongs to Vektis and is subject to the terms of use on vektis.nl.
Available Tools
4 toolsagb_get_recordA
Retrieve one exact AGB registration, preserving leading zeros.
Return found, not_found or ambiguous. For ambiguous, return candidates to retry with record_type. A found record includes identity, status, source_url and retrieved_at. Source failures are tool errors, never not_found responses.
| Name | Required | Description | Default |
|---|---|---|---|
| agbcode | Yes | Eight-digit AGB-code. | |
| sections | No | Requested sections. Omitted means basisregistratie and kwalificaties; [] means identity and status only. | |
| record_type | No | zorgverlener (person), onderneming (business) or vestiging (location). Pass the type from search. If omitted, return alternatives when ambiguous; never choose the first match. |
Output Schema
| Name | Required | Description |
|---|---|---|
| record | No | |
| outcome | Yes | |
| candidates | No | Alternatives for an ambiguous lookup; retry with record_type. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so well: it enumerates the three outcome states (found/not_found/ambiguous), specifies that ambiguous results return candidates, lists the fields of a found record, and critically clarifies that source failures surface as tool errors rather than not_found — a distinction that prevents misreading failures as absence.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Five tight sentences, each carrying a distinct piece of operational information, with the core purpose front-loaded and the error-handling caveat last where it is most likely to be read as an exception to the outcome list.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 no prose, yet the description still names the outcome states and found-record fields, and it covers the failure-mode edge case that a schema alone cannot express. Nothing an agent needs to invoke this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description adds a non-obvious semantic constraint by noting that leading zeros in agbcode are preserved, which guards against the common pitfall of numeric coercion on an 8-digit code. The record_type retry semantics are also referenced in prose, reinforcing the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Retrieve one exact AGB registration') and scopes it as a single-record exact lookup, which cleanly separates it from the agb_search_* siblings. The leading-zeros clause signals the precision of the key being matched.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It tells the agent what to do when the result is ambiguous (retry with record_type) and distinguishes exact retrieval from search. It does not explicitly state when to prefer this over the sibling search tools, but the 'one exact registration' framing makes the division of labor inferable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
agb_lookup_codesA
Discover zorgsoort and kwalificatie filter codes and labels.
Without filters, list zorgsoorten. With zorgsoort, list its kwalificaties. With query alone, search both levels. With both, search kwalificaties under that zorgsoort. Each kwalificatie includes its parent_code. Unknown parents are input errors; an unmatched query returns an empty results list.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | Optional code or label substring, matched case-insensitively. Return all matches; never guess an ambiguous label. | |
| zorgsoort | No | Optional two-digit parent zorgsoort code. | |
| entity_kind | Yes | zorgverlener for people or organisatie for businesses and locations. |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes | |
| entity_kind | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does disclose meaningful behavior: unknown parents are input errors, unmatched queries return an empty results list, and each kwalificatie includes its parent_code. It stops short of stating read-only/permission/rate-limit traits, but for a lookup tool it adds substantive error and output semantics beyond the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded and every sentence earns its place: purpose first, then parameter-combination rules, then output/error behavior. It is appropriately sized for the tool's complexity and contains no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given a low-complexity lookup with an output schema and 100% schema description coverage, the description supplies the missing usage and error context. An agent has enough to call the tool correctly, and return-value details are properly handled by the output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description adds critical combinatorial semantics not present in the schema: default lists zorgsoorten, zorgsoort lists its kwalificaties, query alone searches both levels, and both together search kwalificaties under that zorgsoort. This fully specifies how the optional parameters interact for correct invocation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb (Discover) and resource (zorgsoort and kwalificatie filter codes and labels), and its combinatorial behavior makes it clearly distinct from the sibling search/get-record tools. An agent can identify this as a reference-code lookup rather than a record search without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit conditions for each parameter combination (no filters, zorgsoort alone, query alone, both together), which is strong operational guidance. However, it never names or contrasts with the sibling tools (e.g., agb_search_zorgverleners), so the 'vs alternatives' aspect is absent; clear context but no exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
agb_search_organisatiesA
Search the AGB-register for ondernemingen (businesses) and vestigingen (locations).
Require at least one non-empty query, location, KvK, zorgsoort or kwalificatie criterion. Results include record_type and an address when available.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | No | AGB-code or name. Digits match as a substring of the code, letters match the name. May be omitted when filtering on plaats, postcode or kvknummer. | |
| plaats | No | City name. | |
| postcode | No | Four digits, optionally followed by two letters, e.g. '9403' or '9403AA'. | |
| kvknummer | No | KvK-nummer, up to 12 digits. | |
| zorgsoort | No | Two-digit zorgsoort code. Omit for all. 01 Huisartsen | 02 Apothekers | 03 Medisch Specialisten | 04 Fysiotherapeuten | 05 Logopedisten | 06 Ziekenhuizen | 07 Oefentherapeuten | 08 Verloskundigen | 11 Tandarts - Specialisten (Mondziekten en Kaakchirurgie) | 12 Tandartsen | 13 Tandarts - Specialisten (Dento-Maxillaire Orthopedie) | 14 Bedrijfsartsen | 17 Rechtspersonen | 18 Dialyse Centra | 19 Audiologische Centra | 20 Radiotherapeutische Centra | 21 Dienstenstructuren (ANW-Diensten) | 22 Zelfstandige Behandelcentra | 23 Instellingen voor Revalidatiedagbehandeling | 24 Dietisten | 25 Instellingen voor Psychiatrische Deeltijdbehandeling | 26 Podotherapeuten | 30 Instellingen voor Verstandelijk Gehandicapten | 31 Bloedbanken | 32 GGD | 33 Kraamzorg | 34 Trombosediensten | 35 Instellingen voor Visueel Gehandicapten | 36 Ambulancediensten | 37 Gezondheidscentra | 38 Tandheelkundige Centra | 39 Instellingen voor Jeugdtandverzorging | 40 Instellingen voor Auditief Gehandicapten | 41 ZZP-ers in wijkverpleging/ PGB aanbieders / Beheerstichtingen | 42 Verzorgingshuizen | 43 Beheerstichtingen Verzorgingstehuizen | 44 Optometristen | 45 Verpleeginrichtingen voor Somatische Ziekten | 46 Verpleeginrichtingen voor Psycho-Geriatrische Patienten | 47 Gecombineerde Verpleeginrichtingen | 48 Overige Instellingen | 49 Abortusklinieken | 50 Laboratoria(Huisartsenlab./Gemeensch.Lab/Gemeensch Apoth+Lab | 51 Klinisch-Genetische Centra | 52 Eurotransplant | 53 Diverse Samenwerkingsverbanden | 54 GGZ Instellingen (PUK/PAAZ) | 56 Consultatiebureaus voor Alcohol en Drugs | 58 Centrale Post Ambulancediensten - CPA | 60 Instellingen voor Dagverpleging voor Ouderen | 61 Beheerstichtingen Dagverblijven | 65 Gezinsvervangende Tehuizen | 66 Koepels en Beheerstichtingen WLZ | 67 Netwerk organisaties | 70 Kinderdagverblijven | 72 RIBW | 73 WLZ Gecombineerd | 74 Arbodiensten | 75 Thuiszorginstellingen | 76 Leveranciers Hulpmiddelen | 78 Sociaal Pedagogische Diensten | 79 RIAGG | 84 Overige Artsen | 85 Taxivervoerders | 87 Mondhygienisten | 88 Ergotherapeuten | 89 Schoonheidsspecialisten | 90 Overige therapeuten en Complementair en Aanvullende zorg | 91 Verpleegkundigen | 93 Tandtechnici / Tandprothetici | 94 Psychologische Zorgverleners | 96 Pedicuren | 98 Declaranten/Servicebureaus/Zorgverzekeraars | |
| include_ended | No | Keep records that have an einddatum. | |
| kwalificaties | No | Kwalificatie codes to filter on, e.g. ['0100']. See agb_lookup_codes. |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes | |
| has_more | Yes | More matching rows are known to exist beyond the returned limit. |
| source_total | No | Source match count before local filtering; null when unavailable. |
| returned_count | Yes | Number of returned results after local filtering and limit. |
| applied_filters | Yes | |
| source_truncated | Yes | Source render cap prevents inspecting all matches; narrow the search. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It does disclose a validation rule (at least one filter required) and that results carry record_type and an address 'when available', which is useful. It says nothing about permissions, pagination, or the limit ceiling, leaving meaningful gaps for a search tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the search scope and then the precondition. The closing note on record_type/address borders on redundant given an output schema exists, but it is brief and does not bloat the definition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 detail is not required, and the description supplies the one piece of structured data that is missing: the at-least-one-criterion constraint. The remaining gaps (result volume/limit behavior, include_ended semantics) are minor given the rich schema descriptions.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 88%, so the schema already documents each of the 8 parameters in detail, including the full zorgsoort enum and postcode/KvK patterns. The description only restates the filter categories that must be satisfied, adding little parameter-level meaning beyond the baseline for high-coverage schemas.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb ('Search') and resource ('AGB-register for ondernemingen and vestigingen'), and the entity scope (businesses/locations) implicitly separates it from the sibling agb_search_zorgverleners. It does not name that sibling explicitly, so the differentiation still requires a small inference.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The second sentence states a genuine precondition – at least one of query, location, KvK, zorgsoort or kwalificatie must be non-empty – which is real usage guidance beyond the schema. However, it never routes the agent toward agb_search_zorgverleners, agb_get_record, or agb_lookup_codes, so when-not-to-use and alternative selection are left implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
agb_search_zorgverlenersA
Search the AGB-register for individual zorgverleners (huisartsen, tandartsen, fysiotherapeuten, ...).
Results carry initials-only names and no address; use agb_get_record for detail sections.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | Yes | AGB-code or name. Digits match as a substring of the code, letters match the name. | |
| zorgsoort | No | Two-digit zorgsoort code. Omit for all. 01 Huisartsen | 02 Apothekers | 03 Medisch Specialisten | 04 Fysiotherapeuten | 05 Logopedisten | 07 Oefentherapeuten | 08 Verloskundigen | 11 Tandarts - Specialisten (Mondziekten en Kaakchirurgie) | 12 Tandartsen | 13 Tandarts - Specialisten (Dento - Maxillaire Orthopedie) | 14 Bedrijfsartsen | 24 Dietisten | 26 Podotherapeuten | 33 Kraamzorg | 44 Optometristen | 57 Physician Assistant | 84 Overige Artsen | 87 Mondhygienisten | 88 Ergotherapeuten | 89 Schoonheidsspecialisten | 90 Overige therapeuten en Complementair en Aanvullende zorg | 91 Verpleegkundigen | 93 Tandtechnici / Tandprothetici | 94 Psychologische Zorgverleners | 96 Pedicuren | |
| include_ended | No | Keep records that have an einddatum. | |
| kwalificaties | No | Kwalificatie codes to filter on, e.g. ['0101']. See agb_lookup_codes. |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes | |
| has_more | Yes | More matching rows are known to exist beyond the returned limit. |
| source_total | No | Source match count before local filtering; null when unavailable. |
| returned_count | Yes | Number of returned results after local filtering and limit. |
| applied_filters | Yes | |
| source_truncated | Yes | Source render cap prevents inspecting all matches; narrow the search. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does disclose a genuinely useful behavior: results carry initials-only names and no address. However, it says nothing about result limits, pagination, or why ended records are excluded by default, leaving notable behavioral gaps for a search tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with zero waste; the primary purpose is front-loaded and the follow-up caveat about initials/no address plus the routing hint are tightly packed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return structure needn't be explained, and the description sensibly warns about the limited identity data returned. It is nearly complete for a search tool, missing only paging/limit behavior hints.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 80%, so the schema already documents query, zorgsoort, kwalificaties, limit and include_ended in detail. The description adds nothing about parameter usage or syntax, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (search) and resource (AGB-register for individual zorgverleners), with examples of the categories covered. The word 'individual' implicitly contrasts with the sibling agb_search_organisaties, though that sibling is never named, so differentiation is left partly to inference.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent to agb_get_record when detail sections are needed, which is actionable guidance. It lacks an exclusion against agb_search_organisaties or advice on query construction, so it stops short of full when/when-not coverage.
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.
4 tool updates
v0.1.0- First observed
agb_get_record - First observed
agb_lookup_codes - First observed
agb_search_organisaties - First observed
agb_search_zorgverleners
TDQS
Scored across 4 tools
The two search tools are cleanly split by target (zorgverleners individuals vs organisaties businesses/locations), agb_get_record is a distinct exact-retrieval operation, and agb_lookup_codes handles filter metadata. No two tools overlap in purpose.
All four tools use a consistent agb_ prefix with a clear verb_noun/snake_case pattern (search_x, get_record, lookup_codes). Predictable and readable throughout.
Four tools are well-scoped for a register-lookup service: two search entry points, one exact-get, one code-discovery helper. Each earns its place with no redundancy.
The read-only AGB domain is fully covered: search for individuals and organizations, exact record retrieval handling found/not_found/ambiguous, and code discovery for filters. No obvious operational gaps for a lookup surface.
Maintenance
Related MCP Connectors
WHO ICD-10/ICD-11 diagnosis codes. Lookup, search, chapters via official WHO API.
Search and retrieve job postings on Nationale Vacaturebank, the largest Dutch jobs site (read-only).
NIH Clinical Tables: ICD-10/9, RxTerms, LOINC, NPI, conditions search. Keyless.
Search a healthcare provider directory and get full provider details by id.
Related MCP Servers
- AlicenseBqualityCmaintenanceProvides tools to query Dutch vehicle registration data through the official RDW open API, allowing users to look up vehicle information by license plate, retrieve fuel/emissions data, and search vehicles by brand and model.181MIT
- -licenseNot gradedqualityNot gradedmaintenanceEnables interaction with the CMS NPPES NPI Registry to search, lookup, and validate National Provider Identifier records. It features offline-capable search using a local SQLite database that automatically updates with the latest provider data.-
- AlicenseAqualityBmaintenanceEnables lookup, search, browsing, and explanation of WHO's International Classification of Functioning, Disability and Health (ICF) codes and qualifiers via the WHO ICD-API.171MIT
- AlicenseNot gradedqualityCmaintenanceEnables querying German distribution grid operator data from vnbdigital.de and BDEW register, including lookup by ID, postcode, coordinates, and free-text search.MIT