VATBuild's MCP server is directly compatible with Claude Desktop, Cursor, and Zed.
This guide covers all three clients, a recommended agent workflow, and how to get an
API key.
Endpoint: POST https://vatbuild.com/mcp
Protocol: Streamable HTTP, JSON-RPC 2.0, stateless
Auth: Authorization: Bearer <api-key> — see Getting an API key
2. OAuth / PKCE
4. Cursor
5. Zed
9. Rate limits
10. Troubleshooting
Option A — OAuth 2.1 with PKCE (recommended):
VATBuild supports OAuth 2.1 with PKCE. Claude and other MCP clients that implement
RFC 9728 auto-discover the auth endpoints and drive the PKCE flow — no static key
required. See OAuth / PKCE for discovery URLs and the
full PKCE parameter reference.
Option B — Web dashboard (bearer key):
Log in at vatbuild.com, navigate to Settings → API Keys,
and create a key. Tick the scopes you need (see Scopes reference).
Keys are shown only once — copy and store them securely.
Option C — Device-code flow via MCP (new or existing account):
create_account and authenticate_account are public tools (no key required).
> Note for Claude Desktop and other session MCP clients: Session clients can complete the initialize handshake without credentials and call check_line_item with no key. Auth is enforced per-tool: calling a gated tool without a valid Bearer token returns HTTP 401 with WWW-Authenticate: Bearer resource_metadata=… (RFC 9728), triggering OAuth discovery. Claude Desktop handles this automatically by opening a browser for the PKCE consent flow. Option C (device-code flow) is also available to all callers.
create_account — VATBuild sends a confirmation email. **No API keyis returned and no account is created until the user clicks the link.** Once confirmed,
call authenticate_account to begin the device-code flow.
authenticate_account directly with your email address.Both paths complete the same way: authenticate_account returns a user_code and
request_id. Display the user_code to the user, ask them to check their email and
click "Approve API access", then poll poll_authentication with the request_id
every 3–5 seconds. Once approved, the tool returns { status: "ready", key_retrieval_url } —
give the user key_retrieval_url and ask them to open it in their browser. The page
shows their API key and the ready-to-paste config snippet. Never display the key in
the chat and never pass it as a tool argument. After adding the key the user must
fully quit and relaunch the client (closing the chat window is not sufficient).
> Note: Claude Desktop supports OAuth 2.1 / PKCE (Option A above) and drives the
> browser consent flow automatically — no API key needs to be placed in the config.
> The device-code flow (Option C) is available for clients that cannot use OAuth.
Claude Desktop reads MCP server configuration from claude_desktop_config.json.
macOS:
~/Library/Application Support/Claude/claude_desktop_config.json
Windows:
%APPDATA%\Claude\claude_desktop_config.json
Linux:
~/.config/Claude/claude_desktop_config.json
Add (or merge) the following into the mcpServers object:
{
"mcpServers": {
"vatbuild": {
"type": "http",
"url": "https://vatbuild.com/mcp"
}
}
}
No API key is needed in the config — Claude Desktop auto-discovers the OAuth
endpoints and opens a browser window for sign-in on first use. Quit and restart
Claude Desktop after saving the config file (quit and relaunch — a window
close is not sufficient). On next launch, VATBuild tools are available and
the browser sign-in prompt appears automatically. Try:
Use the check_line_item tool to classify "Supply and fit underfloor heating" for a
new-build detached house under VAT431NB rules.
Claude Desktop sends requests server-to-server (no Origin header), which VATBuild
allows by design. No CORS or browser-origin configuration is needed.
check_line_item is a public tool and requires no key — Claude Desktop can call it
immediately after initialize without any credentials. Include a Bearer key in the
headers block to use the project tools (routing, invoices, reports); a key with no
scopes is sufficient for check_line_item-only access and avoids any OAuth prompt.
For the full workflow — account creation, project setup, invoice submission, line-item
routing — the key needs all five scopes. When creating the key in the dashboard, tick:
projects:readprojects:writeline_items:writejobs:readinvoices:writeIf you already have other MCP servers configured, add VATBuild as an additional
entry in the mcpServers object:
{
"mcpServers": {
"other-server": {
"command": "npx",
"args": ["-y", "some-mcp-server"]
},
"vatbuild": {
"type": "http",
"url": "https://vatbuild.com/mcp",
"headers": {
"Authorization": "Bearer vb_live_YOUR_API_KEY_HERE"
}
}
}
}
Cursor reads per-project MCP server config from .cursor/mcp.json in the workspace
root, or from a global user config at ~/.cursor/mcp.json.
Project-level (recommended — keeps the key scoped to this project):
.cursor/mcp.json
{
"mcpServers": {
"vatbuild": {
"type": "http",
"url": "https://vatbuild.com/mcp",
"headers": {
"Authorization": "Bearer vb_live_YOUR_API_KEY_HERE"
}
}
}
}
Global (available across all Cursor workspaces):
~/.cursor/mcp.json — same format as above.
After saving, open Cursor Settings → MCP and verify VATBuild appears with a green
status indicator. If the file was not auto-detected, click Refresh.
> Secret hygiene: Add .cursor/mcp.json to .gitignore if the file contains a
> live API key, to prevent accidental key exposure in version control.
Cursor's Composer agent can then call VATBuild tools inline. For example:
@vatbuild classify this invoice line: "Scaffolding erection and hire, net £3,500, VAT £700"
under VAT431NB rules for a new-build detached house.
Zed reads MCP server configuration from settings.json. Open Zed's settings via
Zed → Settings → Open Settings (macOS) or File → Settings (Linux/Windows),
then add the assistant.mcp_servers key:
{
"assistant": {
"mcp_servers": {
"vatbuild": {
"url": "https://vatbuild.com/mcp",
"headers": {
"Authorization": "Bearer vb_live_YOUR_API_KEY_HERE"
}
}
}
}
}
Restart the Zed assistant panel after saving. VATBuild tools are then available when
using the assistant's tool-use mode.
> Tip: Zed's Streamable HTTP MCP support (url key under assistant.mcp_servers)
> is GA. If tools do not appear after restarting the assistant panel, check
> Zed changelog to confirm you are on a recent release.
The recommended end-to-end flow for a new VATBuild user via a Claude agent:
Step 1 — create_account → authenticate_account → poll_authentication
create_account input: { "firstName": "Jane", "email": "jane@example.com" }
Output: { "status": "verification_email_sent", "verified": false, "nextStep": "check_email" }
→ No API key returned. Tell the user a confirmation email has been sent.
The account only activates when they click the link.
Once confirmed, call authenticate_account:
Input: { "email": "jane@example.com" }
Output: { "status": "magic_link_sent", "user_code": "BCDF-7823", "request_id": "..." }
→ Display user_code to the user. Ask them to check their inbox and click
"Approve API access" — the approval page shows the same code for verification.
Poll poll_authentication every 3–5 s until status is "ready":
Input: { "request_id": "..." }
Output: { "status": "ready", "key_retrieval_url": "https://vatbuild.com/..." }
→ Give the user key_retrieval_url and ask them to open it in their browser.
The page shows the API key and the ready-to-paste config snippet.
Do NOT display the key in the chat. After adding the key the user must
fully quit and relaunch the client. Never pass the key as a tool argument.
Step 2 — set_up_project
Input: { "claimantRoute": "self_build_431nb", "buildingType": "detached house",
"address": "12 Elm Lane, Bristol" }
Output: { "projectId": "uuid", "profileConfirmed": false, "projectUrl": "..." }
→ Surface projectUrl so the user can confirm their profile in the VATBuild web app.
Profile confirmation is required before invoices can be submitted.
Step 3 — assess_project_eligibility (optional, recommended)
Input: { "projectId": "uuid" }
Output: { "eligibilityStatus": "likely_eligible", "keyFindings": [...] }
→ Surface any "warning" or "blocker" findings to the user before they proceed.
Step 4 — submit_invoice_data (once profile is confirmed in the web app)
Input: { "projectId": "uuid", "imageData": "<base64>", "imageMimeType": "image/jpeg",
"supplierName": "ABC Builders Ltd", "invoiceRef": "INV-042" }
Output: { "documentId": "uuid", "status": "extracting" }
→ VATBuild queues AI extraction in the background.
→ imageMimeType accepts "image/jpeg", "image/png", "image/webp", or "application/pdf".
→ For PDFs, pass the raw PDF bytes as base64 — do not render pages to images first.
Step 5 — Poll list_invoices until extractionStatus = "complete"
Input: { "projectId": "uuid" }
→ Check each item's extractionStatus. Repeat every 5–10 seconds.
→ Once complete, proceed to step 6.
Step 6 — list_line_items
Input: { "projectId": "uuid", "status": "pending" }
Output: array of line items; each has claimRoute and vatComplexType
Step 7a — route_line_item (for items WITHOUT a pending_complex_answer)
Input: { "projectId": "uuid", "lineItemId": "uuid",
"item": { "lineText": "...", "netAmount": "...", "vatCharged": "...",
"supplyType": "labour" } }
Output: { "data.outcome.claimRoute": "zero_at_source", "data.reclaimAmount": "0" }
Step 7b — answer_vat_complex_question (for items WITH claimRoute: "pending_complex_answer")
Use this — not route_line_item — when an item requires a specific complex-VAT
confirmation. It resolves the vatComplexType question and persists the decision.
Input: { "projectId": "uuid", "lineItemId": "uuid", "confirmedAnswer": true }
Output: { "data.outcome.claimRoute": "supplier_correction", "data.crossItemContextRequired": false }
→ If data.crossItemContextRequired is true, call reclassify_companion_items next.
Step 8 — reclassify_companion_items (when crossItemContextRequired = true)
Input: { "projectId": "uuid", "documentId": "uuid" }
→ Re-routes sibling items on the same invoice using updated context.
Handling pending_complex_answer items:
When list_line_items returns an item with claimRoute: "pending_complex_answer",
read vatComplexType to know which question to ask the user. Common cases:
vatComplexType | Question to ask |
|---|---|
vague_description | "What does this line actually represent — labour, materials, a professional fee, or something else?" |
esm_install | "Is this the supply and installation of an energy-saving material (insulation, solar PV, heat pump) by the same contractor?" |
disability_adaptation | "Is this work being carried out for a disabled or chronically sick person under the HMRC disability adaptation relief?" |
fitted_furniture | "Is this either (a) a fitted kitchen that was both supplied and installed by the same contractor, or (b) a basic fitted wardrobe consisting only of a wall-to-wall unit with a hanging rail or shelf for clothes and shelves — with no drawers, shoe racks, or decorative panelling?" |
soft_landscaping | "Is this landscaping required by a planning condition?" |
Do not guess the answer. Ask the user, then call answer_vat_complex_question.
For the full confirmedAnswer value table per vatComplexType, see
| Tool | Auth required | What it does |
|---|---|---|
check_line_item | None | Classify any invoice line against HMRC Notice 708 |
create_account | None | Create a new VATBuild account |
authenticate_account | None | Sign in and retrieve a fresh API key |
check_account_status | Bearer key | Check account verification and plan |
set_up_project | projects:write | Create a new project |
assess_project_eligibility | projects:read | Run an HMRC eligibility check |
list_projects | projects:read | List all projects in your account |
list_line_items | projects:read | Retrieve invoice line items for a project |
route_line_item | line_items:write | Classify and persist a VAT routing decision |
answer_vat_complex_question | line_items:write | Resolve a pending complex-VAT confirmation |
When creating an API key, grant only the scopes the agent needs.
| Scope | Tools covered |
|---|---|
| *(none)* | check_line_item, create_account, authenticate_account |
projects:read (or any scope) | check_account_status |
projects:read | list_projects, list_line_items, assess_project_eligibility |
projects:write | set_up_project |
line_items:write | route_line_item, answer_vat_complex_question, reclassify_companion_items, submit_invoice_data |
jobs:read | Job status polling |
invoices:write | submit_invoice_data (upload-only; no line-item override access) |
Recommended scope sets:
| Use case | Scopes |
|---|---|
| Read-only classification | *(none required)* |
| Read-only project access | projects:read |
| Full end-to-end workflow | All five scopes |
See gemini-integration.md — Rate limits for
the full rate-limit table. In summary:
| Request type | Limit |
|---|---|
| All requests per authenticated user (userId or API key) or unauthenticated IP | 60 per minute |
| Write tools | 10 per minute |
create_account / authenticate_account | 3 per hour per IP |
HTTP 429 responses include a Retry-After header. Implement exponential back-off
when polling list_invoices after submit_invoice_data.
Instead of embedding a long-lived API key, agents that act on behalf of individual
users can authenticate using VATBuild's OAuth 2.1 authorization server with PKCE.
Discovery:
GET https://vatbuild.com/.well-known/oauth-authorization-server
GET https://vatbuild.com/.well-known/oauth-protected-resource
Both endpoints return RFC 8414 / RFC 9728 metadata and are always live.
PKCE flow:
1. Dynamic registration — POST /oauth/register with your client metadata
(redirect_uris, client_name). Receive client_id.
2. Authorization — Redirect the user to `/oauth/authorize?client_id=...&scope=...
&code_challenge=...&code_challenge_method=S256`.
3. Token exchange — POST /oauth/token with the auth code and code_verifier.
Receive access_token and refresh_token.
4. MCP calls — Use Authorization: Bearer <access_token> on all /mcp requests.
For the full PKCE parameter reference and discovery endpoint response, see
gemini-integration.md — OAuth / PKCE alternative.
> Prerequisite: The VATBuild deployment must have MCP_OAUTH_ENABLED=true for
> the /oauth/authorize, /oauth/token, and /oauth/register endpoints to be
> active. The discovery endpoints (/.well-known/...) are always live regardless of
> this flag. Contact the VATBuild operator if the OAuth endpoints return 404.
For agents where a single service account is appropriate (rather than per-user OAuth),
the bearer API key approach (Option B in Getting an API key) is simpler.
| Symptom | Cause | Fix |
|---|---|---|
| VATBuild tools do not appear in Claude | Config file not saved or Claude not fully restarted | Quit and relaunch Claude; validate JSON with a linter |
Tool calls return registration_required | Bearer key missing or wrong header name | Confirm the header is Authorization: Bearer vb_live_... (not X-Api-Key) |
| HTTP 403 on a specific tool | API key lacks the required scope | Regenerate the key with the missing scope ticked |
| HTTP 429 | Rate limit exceeded | Wait for Retry-After seconds |
submit_invoice_data returns ASSESSMENT_NOT_CONFIRMED | Profile wizard not completed | User must visit projectUrl in the VATBuild web app and confirm their profile |
| Claude Desktop doesn't list VATBuild tools | Config file syntax error or wrong path | Validate JSON with a linter; confirm the file is at the path for your OS |
| Cursor shows VATBuild as "disconnected" | Stale config or network issue | Open Cursor Settings → MCP and click Refresh; check for JSON syntax errors in .cursor/mcp.json |
| Zed tools unavailable | Zed version doesn't support Streamable HTTP yet | Update Zed to the latest release and check release notes for MCP HTTP support |
check_line_item always returns pending_complex_answer | vatComplexTypeHint matches a confirm type and confirmedAnswer is null | Pass confirmedAnswer with the user's answer, or omit vatComplexTypeHint and let the engine auto-detect |