Agents and Slack
MCP server
Connect Claude, Cursor, VS Code, Codex or any other MCP client to Zuse, and ask about licensed professionals in your own words.
MCP is a connection that lets AI assistants use Zuse's tools directly. It stands for Model Context Protocol: a standard way for an AI application, such as Claude, Cursor, VS Code or Codex, to call outside tools. Once connected, you ask the assistant a question and it looks people up in Zuse for you.
Zuse runs a hosted server that works in any MCP client that can send a header:
https://mcp.zuse.live/mcpThree things to know before you start:
- It signs in with an API key. Create one in the console. The connection belongs to the team the key was made in.
- There is nothing separate to buy. MCP is not its own product or plan. What the assistant does through the connection comes out of that team's credit, at the same prices as the REST API and the console.
- The same limits apply. Tool calls count towards the team's rate limits, and each one appears in the console's Request history with the source MCP.
Install
Replace YOUR_ZUSE_KEY with a key from the console.
Claude Code
claude mcp add --transport http zuse https://mcp.zuse.live/mcp \
--header "Authorization: Bearer YOUR_ZUSE_KEY"Start a new session and ask for something Zuse knows. /mcp lists the servers that are connected.
Claude Desktop
Open Settings, then Developer, then Edit Config, and add Zuse. Claude Desktop reaches a server that signs in with a key through a small bridge, which needs Node.js on the computer:
{
"mcpServers": {
"zuse": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.zuse.live/mcp", "--header", "Authorization:${ZUSE_AUTH}"],
"env": { "ZUSE_AUTH": "Bearer YOUR_ZUSE_KEY" }
}
}
}Restart Claude Desktop. The "Add connector" screen in Claude on the web and in Claude Desktop expects a sign-in in the browser, which Zuse does not offer yet, so Zuse cannot be added there.
Cursor
Add Zuse to ~/.cursor/mcp.json (Cursor Settings, then MCP, then Add new MCP server):
{
"mcpServers": {
"zuse": {
"url": "https://mcp.zuse.live/mcp",
"headers": { "Authorization": "Bearer YOUR_ZUSE_KEY" }
}
}
}VS Code
Create .vscode/mcp.json in your project. VS Code asks for the key the first time the server starts and stores it for you, so the key is not written into a file you might commit:
{
"servers": {
"zuse": {
"type": "http",
"url": "https://mcp.zuse.live/mcp",
"headers": { "Authorization": "Bearer ${input:zuse-key}" }
}
},
"inputs": [
{ "id": "zuse-key", "type": "promptString", "description": "Zuse API key", "password": true }
]
}Codex
Put the key in your environment as ZUSE_API_KEY, then add Zuse to ~/.codex/config.toml:
[mcp_servers.zuse]
url = "https://mcp.zuse.live/mcp"
bearer_token_env_var = "ZUSE_API_KEY"ChatGPT's own connector screen expects a sign-in in the browser, which Zuse does not offer yet. Codex, with the key as above, works today.
Other clients
Any client that can reach a remote MCP server over streamable HTTP and send a header will work. Most use this shape:
{
"mcpServers": {
"zuse": {
"type": "http",
"url": "https://mcp.zuse.live/mcp",
"headers": { "Authorization": "Bearer YOUR_ZUSE_KEY" }
}
}
}| Client | Where to add it | What it calls the address |
|---|---|---|
| Windsurf | ~/.codeium/windsurf/mcp_config.json, under mcpServers | serverUrl |
| Gemini CLI | ~/.gemini/settings.json, under mcpServers | httpUrl |
| Zed | settings.json, under context_servers | url |
Clients change where their settings live. If one of these has moved, the client's own documentation is the place to check. A client that only runs local servers can use the bridge shown under Claude Desktop.
Authentication
| Way in | Status | How |
|---|---|---|
| API key | Available | Send Authorization: Bearer YOUR_ZUSE_KEY on every request. Usage belongs to the key's team. |
| Sign in in the browser (OAuth) | Not yet | Needed by the connector screens in Claude and ChatGPT on the web. Planned. |
| No key | Not offered | Every request is tied to a team, so there is no anonymous use. |
A key is shown once, when it is made. If one is exposed, revoke it in the console: it stops working on the next request.
Tools
| Tool | Does | Arguments |
|---|---|---|
source_professionals | Lists licensed professionals, or veterinary practices, near a location, a page at a time. | A location: postal_code, or latitude and longitude together. Optionally radius_miles, profession (veterinarian, vet_tech, vet_assistant, or premises for practices), jurisdiction (the licensing state, such as NJ), certification (any, vts, or a specialty such as Avian), min_years_licensed, max_years_licensed, practice_type, active_only, sort, limit (10 if omitted, at most 50), cursor |
verify_license | Checks a person against the board of record, by name, by license number, or both. | name or license_number (a number alone finds whoever holds it), and optionally jurisdiction, profession (veterinarian, vet_tech or vet_assistant), limit, cursor |
enrich_person | Returns cited, web-derived context for one person: where they work, education, public contact details. | first_name, last_name, and optionally city, state, title, license_number, license_state, refresh |
research_brief | Writes intro notes on one person: a summary, recent changes, and talking points for a first message. | first_name, last_name, and optionally city, state, title, current_employer, license_number, license_state, refresh |
find_people_on_web | Finds people in a role no board licenses (groomers, receptionists, kennel staff, practice managers) on their public profiles near a ZIP code. | role, postal_code, and optionally radius_miles (25 if omitted), include_contacts |
find_clinics | Lists veterinary clinics near a location (state boards' premises registers, practice managers' clinics, OpenStreetMap), a page at a time. | A location: postal_code, or latitude and longitude together. Optionally radius_miles, kind (plain words: emergency, mobile, VCA), sort, limit (10 if omitted, at most 50), cursor |
get_clinic | One clinic with the people tied to it: the veterinarian in charge, practice managers, and the vets and techs on record at its address. | clinic (a ref from find_clinics) |
research_clinic | Researches one clinic's owner, team, phone, email, website and hours on the public web. | clinic, and optionally refresh |
find_contact | Finds one person's email and phone (People Data Labs), cross-checked against what is known about them. | first_name, last_name, and optionally city, state, postal_code, linkedin_url, current_employer, email, phone, license_number, license_state, refresh |
The tools read the same data as the v2 resources and are metered the same way. Their results are flat JSON rather than the v2 envelope, with dates as the board publishes them (08-31-2027).
Each tool's JSON Schema is published by the server in tools/list, with a description on every argument, the values an argument may take, a display name (title) and behaviour hints (annotations): the two lookups are marked read-only, and the research, web search and contact tools are marked as reaching the open web.
The server accepts exactly what the schema lists. An argument it does not have (radius for radius_miles, say), a value outside the listed ones, a search with no location, or half a coordinate pair comes back as invalid_arguments with the reason, and is not charged. It is never answered with an empty result, because an assistant would repeat "nobody found" as a fact.
Research is kept
What enrich_person, research_brief, find_contact and research_clinic find is kept on your team's records. When the assistant asks about a person your team has already researched, on any channel, the result comes back with from_records: true and cost_usd: 0, and nothing is researched again. refresh: true runs it again, at the usual price; the tool descriptions tell the model to use it only when you ask for new research.
What to ask
One conversation can take an opening from "who is out there" to a first message. You speak plainly; the assistant picks the tool.
| You say | The assistant calls | What comes back |
|---|---|---|
| "Who are the licensed vet techs within 10 miles of 90013? Show me the ten nearest." | source_professionals | Names, credentials, distance and years licensed. One request. |
| "Is the third one's license active, and when does it expire?" | verify_license | The board record, which the assistant can state as fact and attribute to the board. |
| "Find out where the first three work now and how to reach them." | enrich_person, three times | Employer, education and public contact details, with the pages they came from. Kept on your team's records, so the same question tomorrow is free. |
| "Write what I should say to the second one in a first message." | research_brief | Notes the assistant drafts from. Read the draft before you send it: it is written from public web pages. |
| "Find dog groomers within 15 miles of 85281." | find_people_on_web | Named groomers from their own public profiles, with title, employer and town. People with veterinary experience come first. |
| "Which emergency clinics are within 10 miles of 85004?" | find_clinics | Clinics nearest first, each with its kind, address and, where known, phone and website. |
| "Who owns the second one, and who works there?" | research_clinic | The owner, medical director and team from the clinic's own pages, each matched to a board licence where one fits. |
| "Get me a phone and personal email for the first one." | find_contact | Every email and phone on record, each marked recommended, work, older or lower confidence, and how confident the contact match is. |
Every result carries cost_usd and credit_remaining_usd, so you can ask the assistant what a step cost and what is left.
Troubleshooting
| What you see | What it means | What to do |
|---|---|---|
401, missing or invalid API key | The header is absent or wrong, or the key was revoked, or its owner was removed from the team. | The header must read Authorization: Bearer followed by the whole key, which starts with zuse_sk_. Create a new key if needed. |
402, credit used up | The team has spent its credit. | Top up in Billing ($10, $25, $50 or any amount); a practice's plan also adds $10 of credit each month. Reading back research your team already has still works. |
402, no plan | A practice account has no live plan. | Start the practice plan from Billing (first month free). |
429, too many requests | The team reached a rate limit. | The answer carries retry_after_seconds. See Rate limits. |
| The tools do not appear | Most clients read their configuration only when they start. | Save the file, then restart the client or open a new session. |
| The client asks you to sign in | It is trying a browser sign-in, which Zuse does not offer yet. | Use a way that sends the API key as a header: Claude Code, Cursor, VS Code, Codex, or the bridge shown under Claude Desktop. |
403, origin not allowed | The request came from a web page on a site Zuse does not know. | The MCP server is for assistants and servers, not for calling from a browser page. |
Results
A tool call returns its result twice: as text in content[0].text, and as an object in structuredContent. cost_usd and credit_remaining_usd are on every metered result.
{
"content": [{ "type": "text", "text": "{\"classification\": \"found\", ...}" }],
"structuredContent": { "classification": "found", "matches": [], "cost_usd": 0.02, "credit_remaining_usd": 9.98 },
"isError": false
}When a call fails because of its arguments, the tool returns isError: true with an explanation the model can act on, instead of a protocol error:
{
"structuredContent": {
"error": "invalid_arguments",
"detail": [{ "type": "missing", "loc": ["first_name"], "msg": "Field required" }]
},
"isError": true
}A misnamed argument also lists the ones the tool does have:
{
"structuredContent": {
"error": "invalid_arguments",
"detail": [{ "type": "unknown_argument", "loc": ["radius"], "msg": "source_professionals has no argument called radius." }],
"arguments": ["active_only", "cursor", "latitude", "limit", "longitude", "max_years_licensed", "min_years_licensed", "postal_code", "practice_type", "profession", "radius_miles", "sort"]
},
"isError": true
}Paging
source_professionals returns ten people unless limit asks for more (up to 100). Each answer carries total (everyone who matches), remaining (how many come after this page), has_more and next_cursor. The tool description tells the model to say how many remain and offer the next ten, so in practice you see ten people and are asked whether you want more. When you say yes, the agent calls the tool again with the same arguments plus cursor.
Each call is charged by the number of people it returns: the base price covers the first ten, so a page of ten is the base price with nothing added. Asking for a longer list in one call costs the base price plus the fee for each record after the tenth. See Pricing and metering.
The REST API is unchanged: GET /v2/professionals returns 50 a page unless limit says otherwise.
Limits and errors
A tool call that runs into a limit, an empty credit balance, or a restriction comes back as isError: true with the HTTP status and the same detail the REST API gives, including retry_after_seconds for a rate limit:
{
"structuredContent": {
"error": "request_failed",
"status": 429,
"detail": { "error": "rate_limited", "message": "Too many source calls in a minute. Wait 24 seconds and retry.", "retry_after_seconds": 24 }
},
"isError": true
}The limits are the ones in Rate limits and retries, shared with the REST API.
Check the connection
curl https://mcp.zuse.live/mcp \
-H "Authorization: Bearer $ZUSE_KEY" \
-H "Content-Type: application/json" \
-d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }'Protocol details
- The server is stateless. Each
POSTcarries one JSON-RPC message and is answered withapplication/json. There is no session to initialize or resume. - Supported protocol versions:
2025-06-18,2025-03-26,2024-11-05. - Supported methods:
initialize,ping,tools/list,tools/call. GET /mcpreturns405: the server does not open a server-to-client stream.- A missing or invalid key returns HTTP
401before any JSON-RPC handling.
Billing
Tool calls are metered exactly like the REST endpoints they map to. initialize, ping, and tools/list are free. A result read from your team's records (from_records: true) is free too. See Pricing and metering.
Guidance for agents
verify_license answers from the board of record and can be stated as fact, with the board named as the source. find_people_on_web lists people from their public profiles: never call them licensed. find_contact comes from People Data Labs: use personal_email, work_email and the mobile phone for outreach, and when confidence is check, say the record may be someone else. enrich_person and research_brief are web-derived: present them as reported context, cite the source URLs they return, and do not describe them as verified. When a result has from_records: true, say when it was researched (researched_at or written_at) and offer to refresh it rather than refreshing unasked.