MCP Server
Connect AI agents to Anchor via the MCP server or in-tab WebMCP tools.
Two ways agents connect
Anchor exposes tools to AI agents in two different places. They are not interchangeable.
| MCP server (Cursor / Claude Desktop) | WebMCP (this browser tab) | |
|---|---|---|
| Where the agent runs | A coding agent on your computer | An AI feature in Chromium (or an extension) on the signed-in dashboard |
| How you connect | Add https://anchorapp.co.za/api/mcp as an MCP server URL | Open the dashboard in a supporting browser — the page registers tools itself |
| Auth | OAuth; Bearer tokens (mcp_at_…) | Your existing signed-in session cookie |
| What it can do | Full remote catalogue (create, post, reports, …) | Read, navigate, draft creates, and selected writes that open the existing confirm/send UI. Destructive REST is never called silently |
Do not add https://anchorapp.co.za (the website origin) as an MCP server URL for WebMCP. That URL is the app. The MCP server URL is always /api/mcp.
WebMCP is Chromium-only and experimental. Firefox and Safari keep the full human UI; they simply do not register in-tab tools. We do not claim it works in every browser.
What is MCP?
The Model Context Protocol (MCP) is an open standard that lets AI coding agents and assistants interact with external tools and data sources. Anchor's built-in MCP server gives any MCP-compatible agent direct access to your accounting data -- creating entries, managing invoices, generating reports, and more.
WebMCP (this browser tab)
WebMCP is a draft browser API (document.modelContext) so an in-tab agent can call typed tools instead of scraping buttons.
When you are signed in to Anchor in a supporting Chromium build:
- Enable WebMCP in the browser if it is still behind a flag (see Chrome WebMCP).
- Open the dashboard (not the public marketing or docs pages).
- The page registers tools named
anchor_*(context, navigate, invoices, contacts, bank lines, dashboard summary, draft creates). Invoice, contact, and journal-entry screens addanchor_this_*. Invoice send/void and journal finalize/cancel open the same dialogs a human uses — the agent waits; you confirm. - Ask the in-tab agent to list invoices or open a record — it should call those tools. Amounts are integer cents (1250 = R12.50). Draft invoice lines use
unitPriceCents. Creating a contact or draft invoice navigates this tab to the new record. Send, post, and void never run as silent REST calls.
Tools always run as you, on the current organisation. Signing out or leaving the dashboard unregisters them.
Prerequisites
- Any plan -- MCP access is available on all plans, including Free
- Any MCP-compatible client (Claude Desktop, Claude Code, Cursor, Windsurf, Cline, VS Code Copilot, or other MCP-enabled agents)
Connecting Your Agent
Any MCP-compatible agent can connect to Anchor's MCP server. The server uses Streamable HTTP transport at /api/mcp and OAuth 2.1 for authentication. Most agents handle the OAuth flow automatically -- they will open a browser window for you to sign in and authorize access on first connect.
The examples below use https://anchorapp.co.za/api/mcp (this hosted product).
Claude Desktop
Add to your Claude Desktop config (claude_desktop_config.json):
{
"mcpServers": {
"Anchor": {
"url": "https://anchorapp.co.za/api/mcp"
}
}
}Claude Code
claude mcp add Anchor --transport streamable-http https://anchorapp.co.za/api/mcpCursor
Go to Cursor Settings > MCP and add a new server, or add to .cursor/mcp.json in your project:
{
"mcpServers": {
"Anchor": {
"url": "https://anchorapp.co.za/api/mcp"
}
}
}Cursor auto-detects Streamable HTTP transport from the URL and handles OAuth automatically.
Windsurf
Go to Windsurf Settings > Cascade > MCP and click View raw config, or edit ~/.codeium/windsurf/mcp_config.json directly:
{
"mcpServers": {
"Anchor": {
"serverUrl": "https://anchorapp.co.za/api/mcp"
}
}
}VS Code (GitHub Copilot)
Add to .vscode/mcp.json in your workspace (available in VS Code 1.99+):
{
"servers": {
"Anchor": {
"type": "http",
"url": "https://anchorapp.co.za/api/mcp"
}
}
}Or add to your user settings.json to make it available across all workspaces:
{
"mcp": {
"servers": {
"Anchor": {
"type": "http",
"url": "https://anchorapp.co.za/api/mcp"
}
}
}
}Other MCP Clients
Any agent that supports the MCP Streamable HTTP transport can connect. Point it at:
https://anchorapp.co.za/api/mcpThe client will discover OAuth endpoints automatically via the /.well-known/oauth-authorization-server metadata endpoint.
OAuth Flow
Anchor uses OAuth 2.1 with PKCE for MCP authentication:
- The MCP client discovers OAuth endpoints via
/.well-known/oauth-authorization-serveron the MCP endpoint - The client registers dynamically at
/api/mcp/oauth/register - The client redirects you to
/api/mcp/oauth/authorizeto sign in - You select an organization and approve the connection
- The client receives an authorization code and exchanges it for access/refresh tokens at
/api/mcp/oauth/token - Access tokens expire after 1 hour; refresh tokens last 30 days
Available Tools
Organization
get_organization-- Get current organization details
Chart of Accounts
list_accounts-- List all accounts with optional type filterget_account-- Get account with calculated balancecreate_account-- Create a new accountupdate_account-- Update account detailsdelete_account-- Delete an account (fails if it has transactions)
Control Account Mappings
list_control_account_mappings-- List role → chart account mappings (AR, AP, VAT, bank, …)set_control_account_mapping-- Set or clear one role mapping (accountIdUUID or null to reset)
Journal Entries
list_entries-- List entries with status/date filtersget_entry-- Get entry with line itemscreate_entry-- Create a new entry (debits must equal credits)post_entry-- Post a draft entryvoid_entry-- Void a posted entry (creates reversing entry)
Contacts
list_contacts-- Search and filter contactsget_contact-- Get contact details (includesaddresses)create_contact-- Create a customer, supplier, or both. Optionaladdresses(billing/shippingstreet).addresses.billingis printed on invoices and quotes.nullclears; omit to leave empty. Country preferably ISO-2 (ZA).update_contact-- Update contact details. Provided scalar fields are patched. Whenaddressesis provided, it replaces the whole JSONB (not a deep-merge) — shipping is dropped if not included.nullclears. Omitaddressesto leave it unchanged. GET first if you must keep shipping.
Quotes
list_quotes-- List sales quotes with optional status filterget_quote-- Get a quote with line items (includesinventoryItemIdwhen set)create_quote-- Create a draft quote. Optional lineinventoryItemIdis persisted. Does not decrement stock. Amounts in integer cents. Omit linetaxRateIdto use the contact’sdefaultTaxRateId(or no tax if the contact is tax-exempt / has no default).taxRateId: nullis explicit no tax and does not fall back.update_quote-- Update a draft quote. ReplacinglineswithoutinventoryItemIdstores NULL and drops the catalogue link; GET then put to keep it. Replacinglinesthat omittaxRateIdapplies the contact default (not the old line tax).convert_quote_to_invoice-- Invoice lines inherit each quote line'sinventoryItemId. Stock is not relieved until the invoice is sent.send_quote-- Mark a draft quote as sent (does not email)accept_quote-- Mark a sent quote as accepteddecline_quote-- Mark a sent quote as declinedget_quote_pdf-- Download URL for the quote PDF (any status, including sent)
Invoices
list_invoices-- List invoices with filtersget_invoice-- Get invoice with line itemscreate_invoice-- Create an invoice with line items. Omit linetaxRateIdto use the contact’sdefaultTaxRateId(or no tax if tax-exempt / unset).taxRateId: nullis explicit no tax. REST omit is the same — TaxShopHub no-VAT lines must sendnull.update_invoice-- Update a draft invoice. Replacinglinesthat omittaxRateIdapplies the contact default.void_invoice-- Void an invoicepay_invoice-- Record a payment (amount in cents)
Bills
list_bills-- List bills with status filtercreate_bill-- Create a bill with line itemsapprove_bill-- Approve a draft/pending billpay_bill-- Record a payment (amount in cents)void_bill-- Void a bill
Reports
trial_balance-- Trial balance (mode:as_atdefault /ytdoptional)balance_sheet-- Balance sheetprofit_and_loss-- P&L with date rangeaged_receivables-- AR aging bucketsaged_payables-- AP aging buckets
Amount Conventions
- All monetary amounts in tool inputs/outputs are in integer cents (e.g.
1250= $12.50) - Invoice/bill line item
unitPriceandquantityare decimal numbers (the system converts to cents internally) - Exchange rates are stored as integers with 6 decimal places (
1000000= 1.0)
Testing
With MCP Inspector
The MCP Inspector is the easiest way to test the MCP server interactively during development.
# Start the dev server first
pnpm dev
# Launch MCP Inspector pointed at your local instance
npx @modelcontextprotocol/inspector --url http://localhost:3000/api/mcpThe Inspector will open in your browser and walk you through the OAuth flow. Once authenticated, you can browse available tools, invoke them with custom inputs, and inspect the JSON-RPC responses.
Manual OAuth Flow
If you want to test the OAuth flow step by step:
1. Check metadata discovery
curl http://localhost:3000/.well-known/oauth-authorization-server/api/mcpThis should return JSON with all OAuth endpoint URLs.
2. Register a client
curl -X POST http://localhost:3000/api/mcp/oauth/register \
-H "Content-Type: application/json" \
-d '{"redirect_uris": ["http://localhost:9999/callback"], "client_name": "Test Client"}'Save the client_id from the response.
3. Start the authorization flow
Open this URL in your browser (replace CLIENT_ID with the value from step 2):
http://localhost:3000/api/mcp/oauth/authorize?client_id=CLIENT_ID&redirect_uri=http://localhost:9999/callback&code_challenge=TEST_CHALLENGE&code_challenge_method=S256&state=test123&response_type=codeYou will be redirected to sign in (if needed) and then to the consent page. After approving, you'll be redirected to the callback URL with a code parameter.
4. Exchange code for tokens
curl -X POST http://localhost:3000/api/mcp/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "authorization_code",
"code": "THE_CODE_FROM_STEP_3",
"redirect_uri": "http://localhost:9999/callback",
"client_id": "CLIENT_ID",
"code_verifier": "THE_VERIFIER_MATCHING_YOUR_CHALLENGE"
}'This returns access_token and refresh_token.
5. Call the MCP endpoint
curl -X POST http://localhost:3000/api/mcp \
-H "Authorization: Bearer mcp_at_YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": { "name": "test", "version": "1.0" }
}
}'6. List available tools
curl -X POST http://localhost:3000/api/mcp \
-H "Authorization: Bearer mcp_at_YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}'7. Call a tool
curl -X POST http://localhost:3000/api/mcp \
-H "Authorization: Bearer mcp_at_YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_organization",
"arguments": {}
}
}'With an MCP Client (local)
For local testing with any MCP client, point it at localhost:
http://localhost:3000/api/mcpFor example, in Claude Desktop config:
{
"mcpServers": {
"Anchor-local": {
"url": "http://localhost:3000/api/mcp"
}
}
}Or in Cursor's .cursor/mcp.json:
{
"mcpServers": {
"Anchor-local": {
"url": "http://localhost:3000/api/mcp"
}
}
}Restart your agent and it will initiate the OAuth flow in your browser.
Troubleshooting
"MCP access requires a Pro plan"
On this free product, API/MCP access should already be available when the host runs without paid Stripe seat billing. If you see this error, contact support — do not purchase a “Pro” plan.
"Invalid access token"
Your access token has expired. MCP clients should automatically refresh tokens using the refresh token. If this persists, disconnect and reconnect the MCP server.
"Not a member of this organization"
Your user account has been removed from the organization. Ask an admin to re-add you.
In-tab agent does not see anchor_* tools
WebMCP only registers on the signed-in dashboard, and only in Chromium with the WebMCP API enabled. Public pages (/, /docs, /pay, …) register nothing. This is not an MCP server connection problem — do not add the website origin as an MCP server URL.