Using VATBuild MCP with ChatGPT

VATBuild's MCP server is directly compatible with the ChatGPT desktop app, ChatGPT

in-browser connectors, and custom GPT Actions. This guide covers all three entry

points, the OAuth 2.1 PKCE flow that ChatGPT uses for user-delegated auth, and a

recommended agent workflow.

Endpoint: POST https://vatbuild.com/mcp

Protocol: Streamable HTTP, JSON-RPC 2.0, stateless

Auth: Authorization: Bearer <api-key> (for Desktop/API) or OAuth 2.1 with PKCE (for Connectors)


Contents

1. Getting an API key

2. ChatGPT desktop app

3. ChatGPT in-browser connector (Tools panel)

4. Custom GPT Actions

5. Canonical agent workflow

6. Scopes reference

7. Rate limits

8. OAuth 2.1 PKCE — user-delegated auth

9. Troubleshooting


Getting an API key

Option A — OAuth 2.1 with PKCE (recommended):

VATBuild supports OAuth 2.1 with PKCE. ChatGPT connectors and Custom GPT Actions that

implement RFC 9728 auto-discover the auth endpoints and drive the PKCE flow per user —

no static key required. See OAuth 2.1 PKCE — user-delegated auth

for discovery URLs, authorization server metadata, and the four-step PKCE flow.

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).

is returned and no account is created until the user clicks the link.** Once confirmed,

call authenticate_account to begin the device-code flow.

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: ChatGPT 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.


ChatGPT desktop app

The ChatGPT desktop app (macOS and Windows) supports MCP servers through a

shared TOML configuration file used by the ChatGPT desktop app, Codex CLI, and

IDE extension. MCP support for the ChatGPT desktop app is documented on

platform.openai.com — the exact URL may

change as OpenAI continues to roll out MCP support; if a direct link is stale,

search "MCP" on the OpenAI platform docs or use the in-app UI setup described

below, which is always authoritative.

> Note: The ChatGPT desktop app MCP feature requires a recent version of

> the app and is rolling out progressively. If you do not see **Settings → MCP

> servers**, update to the latest release.

Option A — UI setup (recommended)

1. Open Settings in the ChatGPT desktop app, then select MCP servers.

2. Select Add server.

3. Enter a name (e.g. vatbuild), choose Streamable HTTP, and enter the

URL: https://vatbuild.com/mcp

4. Save the server, then select Restart.

In the composer, type /mcp to view connected servers and confirm VATBuild is

listed.

Option B — Edit the config file directly

The config file is the same path on both macOS and Windows:


~/.codex/config.toml

Add a [mcp_servers.vatbuild] block:


[mcp_servers.vatbuild]
url = "https://vatbuild.com/mcp"

[mcp_servers.vatbuild.http_headers]
Authorization = "Bearer vb_live_YOUR_API_KEY_HERE"

Alternatively, keep the key out of the file by sourcing it from an environment

variable:


[mcp_servers.vatbuild]
url = "https://vatbuild.com/mcp"
bearer_token_env_var = "VATBUILD_API_KEY"

Set VATBUILD_API_KEY in your shell profile (~/.zshrc, ~/.bashrc, or the

Windows system environment variables), then restart ChatGPT.

> Config file vs. UI: ~/.codex/config.toml is the shared configuration

> file for the ChatGPT desktop app, Codex CLI, and IDE extension. If you have

> also added the server via Settings → MCP servers in the ChatGPT UI, the

> UI setting takes precedence. When in doubt, use the UI setup — it is always

> the authoritative source.

Quit and restart ChatGPT after saving config.toml. On next launch, VATBuild

tools appear in the tool picker. Try:


Use the check_line_item tool to classify "Supply and fit underfloor heating" for a
new-build detached house under VAT431NB rules.

The desktop app sends requests server-to-server (no Origin header), which VATBuild

allows by design. No CORS configuration is needed.

Minimal setup (read-only, no project data)

check_line_item is a public tool and requires no key — the ChatGPT connector can call

it immediately after initialize without any credentials. Include a Bearer key in the

config 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.

Full setup (end-to-end workflow)

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:


ChatGPT in-browser connector (Tools panel)

ChatGPT Plus, Team, and Enterprise users can connect MCP servers from the ChatGPT

web interface without installing the desktop app.

1. Open chatgpt.com and start a new chat.

2. Click the Tools icon (grid/wrench icon) near the message input.

3. Select Connect more toolsCustom connector (or Add MCP server,

depending on your plan tier and rollout).

4. Enter the server URL: https://vatbuild.com/mcp

5. In the Authentication section, choose Bearer token and paste your

vb_live_... API key.

6. Click Connect. ChatGPT discovers the available tools automatically.

> Note: In-browser connectors persist per account but not per conversation. If

> VATBuild tools are missing in a new chat, re-open the Tools panel and re-enable

> the connector for the session.


Custom GPT Actions

If you are building a Custom GPT (GPT Builder), you can connect VATBuild as an

Action using the OpenAPI specification.

1. In GPT Builder, go to Configure → Actions

and click Add action.

2. Set the Authentication type to Bearer token and enter your vb_live_...

key, or choose OAuth to use user-delegated auth (see

OAuth 2.1 PKCE below).

3. Import the VATBuild OpenAPI specification from:

```

https://vatbuild.com/docs/openapi.yaml

```

This exposes the REST companion endpoint (POST /api/v1/check-line-item) and

the project/invoice management routes. For the full MCP tool surface, the

Streamable HTTP endpoint (https://vatbuild.com/mcp) is the primary integration

point — use the desktop app or in-browser connector rather than GPT Actions for

multi-tool MCP workflows.

> Tip: Custom GPT Actions work best for focused, single-purpose workflows (e.g.

> "Classify this line item"). For the full VATBuild agent workflow across multiple

> tools, the desktop app MCP connector is a better fit.


Canonical agent workflow

The recommended end-to-end flow for a new VATBuild user via a ChatGPT 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.
  → Requires the `invoices:write` scope (or `line_items:write`). See
    [Scopes reference](#scopes-reference) for details.

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" }

  Valid supplyType values:
    "materials"             — goods only, no installation by the same party
    "labour"                — construction labour (time and skill, no goods)
    "subcontractor"         — subcontracted construction works
    "supply_and_install"    — goods supplied and installed by the same contractor
    "installation_service"  — installation only (goods supplied separately)
    "professional_services" — architect, engineer, QS, planning consultant, etc.
    null                    — unknown; engine infers from line text heuristics

  See mcp-tool-reference.md → supplyType values for the full reference table.

  > **Breaking change (v2 → v3) — esmStatus rename:**
  > If you pass `item.esmStatus`, use the new values:
  >   - Old `"supply_and_install"` → **`"qualifying_esm"`**
  >   - Old `"none"` → **`null`** (omit the field or pass `null`)
  > Sending the old strings is treated as `null`, which means ESM items will
  > be silently misclassified and will not receive ESM zero-rating.

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.

For full details on handling pending_complex_answer items, see

mcp-tool-reference.md.


Scopes reference

When creating an API key, grant only the scopes the agent needs.

|---|---|---|

ScopeMCP tools coveredREST endpoints
*(none)*check_line_item, create_account, authenticate_accountGET /api/v1/check-line-item
projects:readcheck_account_status, list_projects, list_line_items, list_invoices, assess_project_eligibilityGET /api/v1/projects, GET /api/v1/line-items
projects:writeset_up_projectPOST /api/v1/projects
line_items:writeroute_line_item, answer_vat_complex_question, reclassify_companion_items, submit_invoice_data
jobs:readGET /api/v1/jobs/{jobId}
invoices:writesubmit_invoice_data (upload-only)POST /api/v1/invoices, POST /api/v1/invoices/lock

Recommended scope sets:

|---|---|

Use caseScopes
Read-only analysis (classify only)*(none required)*
Read-only project accessprojects:read
Full end-to-end workflowAll five scopes

Rate limits

See gemini-integration.md — Rate limits for

the full rate-limit table. In summary:

|---|---|

Request typeLimit
All requests per authenticated user (userId or API key) or unauthenticated IP60 per minute
Write tools (route_line_item, answer_vat_complex_question, reclassify_companion_items, submit_invoice_data)10 per minute
create_account / authenticate_account3 per hour per IP

HTTP 429 responses include a Retry-After header. Implement exponential back-off

when polling list_invoices after submit_invoice_data.


OAuth 2.1 PKCE — user-delegated auth

ChatGPT is the MCP client that most commonly uses OAuth rather than a static API key,

because GPT Actions and the in-browser connector can drive a full browser-based

auth flow for each end-user. This is the recommended approach for multi-tenant GPTs

where each end-user holds their own VATBuild account.

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.

Authorization server metadata (key fields):


{
  "issuer": "https://vatbuild.com",
  "authorization_endpoint": "https://vatbuild.com/oauth/authorize",
  "token_endpoint": "https://vatbuild.com/oauth/token",
  "registration_endpoint": "https://vatbuild.com/oauth/register",
  "scopes_supported": ["projects:read", "projects:write", "line_items:write", "jobs:read", "invoices:write"],
  "code_challenge_methods_supported": ["S256"],
  "pkce_required": true,
  "grant_types_supported": ["authorization_code", "refresh_token"]
}

PKCE flow (four steps):

1. Dynamic registrationPOST /oauth/register with your client metadata

(redirect_uris, client_name). Receive client_id. In GPT Builder, enter

this client_id in the OAuth configuration panel.

2. Authorization — ChatGPT redirects the user to:

```

https://vatbuild.com/oauth/authorize

?client_id=<your_client_id>

&response_type=code

&scope=projects:read projects:write line_items:write

&redirect_uri=<your_redirect_uri>

&code_challenge=<S256_challenge>

&code_challenge_method=S256

&state=<csrf_state>

```

The user logs in to VATBuild (or creates an account) and grants consent.

3. Token exchangePOST /oauth/token with the auth code and code_verifier.

Receive access_token and refresh_token.

4. MCP calls — ChatGPT uses Authorization: Bearer <access_token> on all /mcp

requests on behalf of the user.

Configuring OAuth in GPT Builder:

|---|---|

FieldValue
Authentication typeOAuth
Grant typeAuthorization code with PKCE
Authorization URLhttps://vatbuild.com/oauth/authorize
Token URLhttps://vatbuild.com/oauth/token
Scopeprojects:read projects:write line_items:write jobs:read invoices:write
Token exchange methodDefault (POST body)

> 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 single-tenant agents where one service account is appropriate, the static API key

approach (Bearer token in headers) is simpler and equally secure.


Troubleshooting

|---|---|---|

SymptomCauseFix
Tool calls return registration_requiredBearer key missing or wrong header nameConfirm the header is Authorization: Bearer vb_live_... (not X-Api-Key)
HTTP 429 on tool callsRate limit exceededWait for Retry-After seconds; reduce polling frequency
HTTP 403 on a specific toolAPI key lacks the required scopeRegenerate the key with the missing scope ticked
OAuth redirect returns 404MCP_OAUTH_ENABLED not setContact the VATBuild operator to enable the OAuth endpoints
submit_invoice_data returns ASSESSMENT_NOT_CONFIRMEDProfile wizard not completedUser must visit projectUrl in the VATBuild web app and confirm their profile
Tools panel shows "Connection failed"ChatGPT cannot reach the MCP endpointVerify the URL is exactly https://vatbuild.com/mcp (no trailing slash)
GPT Action always returns an error schemaOpenAPI import issueRe-import from https://vatbuild.com/docs/openapi.yaml and check the schema validates
Connector does not appear after setupChatGPT session not refreshedReload the ChatGPT page and start a new conversation
OAuth authorisation page returns 404OAuth not enabled on the VATBuild deploymentContact the VATBuild operator to enable MCP_OAUTH_ENABLED=true