MCP Server

DayZero exposes a Model Context Protocol (MCP) server that lets AI assistants — Claude, ChatGPT, Cursor, and other MCP-compatible clients — interact with your accounting data through natural language.

The server is mounted at /mcp and uses the Streamable HTTP transport. Authentication uses OAuth 2.1 — your client discovers the auth server, registers itself, and opens a browser for you to sign in to DayZero and approve access. No tokens to copy.

Just want to connect? Claude users can click Add DayZero to Claude — claude.ai opens the connector dialog prefilled; review it, click Add, and sign in. Every other client: add https://api.ondayzero.com/mcp with no credentials and you'll be prompted to sign in to DayZero in your browser and approve access. See Quick Connect below.

Note: REST API keys (dz_…) from Settings → Developers authenticate the REST API only. They do not work on the MCP server — use the OAuth browser flow instead.

Quick Connect

The fastest path per client. Every row uses the same server URL — https://api.ondayzero.com/mcp — and ends in the same browser OAuth sign-in. Settings → Developers inside DayZero has one-click Add to Claude / Add to Cursor buttons and copy-paste snippets for the rest.

Client Fastest path
Claude (web, desktop & mobile) Add DayZero to Claude — one click, prefilled connector dialog
Claude Code claude mcp add --transport http dayzero https://api.ondayzero.com/mcp — see Connecting Claude Code
ChatGPT Enable Developer mode, then add the URL as a connector — see Connecting ChatGPT
Codex (CLI, IDE extension & ChatGPT desktop app) Two lines in ~/.codex/config.toml — see Connecting Codex
Cursor Settings → MCP Servers → Add with the URL above, or the Add to Cursor button in DayZero — see Connecting Cursor

On Claude Team/Enterprise plans only org owners can add custom connectors — send your admin the same link on the admin path:

text
https://claude.ai/admin-settings/connectors?modal=add-custom-connector&connectorName=DayZero&connectorUrl=https%3A%2F%2Fapi.ondayzero.com%2Fmcp

Domain Profiles For Power Users

DayZero keeps the unified server at /mcp for general use and also exposes focused domain profiles for multi-server MCP setups:

  • https://api.ondayzero.com/mcp/core (dayzero-core) — discovery, search/fetch, snapshots, job polling
  • https://api.ondayzero.com/mcp/ar (dayzero-ar) — invoices, customers, credit memos, AR workflows
  • https://api.ondayzero.com/mcp/ap (dayzero-ap) — bills, vendors, vendor credits, AP workflows
  • https://api.ondayzero.com/mcp/reports (dayzero-reports) — reporting, analytics, ledgers, transaction reads
  • https://api.ondayzero.com/mcp/writes (dayzero-writes) — full write surface
  • https://api.ondayzero.com/mcp/o2c (dayzero-o2c) — order to cash: pipeline deals, sales orders, customer contracts, job-costing projects, and bulk import for each
  • https://api.ondayzero.com/mcp/personal (dayzero-personal) — personal connections: search and read the authenticated user's own linked Gmail (per-user, read-only; requires linking Gmail in Settings → Connected Accounts)

Each profile keeps tools/list scoped to that domain (well under strict hosted-client limits) while the full internal tool registry remains intact. Self-hosted deployments can force one profile process-wide with MCP_DOMAIN_PROFILE (for example MCP_DOMAIN_PROFILE=dayzero-ar).

Multi-Server Setup Examples

Use multiple MCP server entries when you want separate focused tool surfaces in one client session.

Claude Desktop (claude_desktop_config.json):

json
{
  "mcpServers": {
    "dayzero-core": {
      "url": "https://api.ondayzero.com/mcp/core"
    },
    "dayzero-ar": {
      "url": "https://api.ondayzero.com/mcp/ar"
    },
    "dayzero-ap": {
      "url": "https://api.ondayzero.com/mcp/ap"
    },
    "dayzero-reports": {
      "url": "https://api.ondayzero.com/mcp/reports"
    },
    "dayzero-writes": {
      "url": "https://api.ondayzero.com/mcp/writes"
    },
    "dayzero-o2c": {
      "url": "https://api.ondayzero.com/mcp/o2c"
    }
  }
}

Cursor: add one MCP server per profile in Settings → MCP Servers (same URLs above). Cursor will run OAuth for each server entry and keep tools separated by server.

ChatGPT: create one connector per profile under Settings → Connectors → Create using the profile URLs above.

GitHub Copilot / VS Code MCP: add each profile as a separate server in your MCP configuration (same URLs above) so tool discovery stays focused and below hosted limits.

What You Can Do

The MCP server exposes 120+ curated tools across core accounting domains. Tools marked (write) mutate data and require write permission; everything else is a read-only query. Call list_mcp_capabilities for a compact category map.

Category Highlights Description
Discovery list_my_businesses, find_business, get_business_info, list_mcp_capabilities, search_entities Find businesses and entities without knowing IDs upfront; find_business fuzzy-searches by name
Banking list_bank_accounts, get_bank_balances See connected bank accounts and current/available balances
Snapshots get_daily_brief, get_ar_snapshot, get_aged_payables, get_ap_snapshot, get_payment_priority_suggestions, get_uncategorized_summary, get_month_end_package High-signal summaries — prefer these over paging huge lists
Chart of Accounts list_ledgers, get_ledger Browse and inspect ledger accounts
Transactions list_transactions, get_transaction, update_transaction, mark_reviewed, categorize_transaction, recategorize_by_counterparty, mark_transactions_reconciled (write) Search, inspect, categorize, and mark bank lines reconciled
Invoices (AR) create_invoice, update_invoice, finalize_invoice, send_invoice, mark_invoice_paid, void_invoice, list_invoice_payments, get_customer_statement, credit memos, payments (write) Full AR lifecycle, invoice payment history, and customer statement previews
Bills (AP) create_bill, approve_bill, mark_bill_received, update_bill, cancel_bill, list_bill_payments, vendor credits, payments (write) AP approval, receive, payment workflows, and bill payment history
Estimates list_estimates, get_estimate, create_estimate, send_estimate, update_estimate, accept_estimate, convert_estimate (write) Quote → accept → convert, then create_invoice
Order-to-Cash list_deals, create_deal, update_deal, convert_deal_to_order, list_orders, create_order, update_order, advance_order_status, attach_order_document, list_contracts, create_contract, update_contract, link_invoice_to_contract, plus bulk_create_* / bulk_update_* for each (write except list/get) The whole pre-invoice chain: pipeline deal → sales order → countersigned contract → invoice. advance_order_status(target_status="countersigned") is what turns an order into a live contract. Working from a signed PDF: generate_upload_url → PUT the file → extract_document_entities to read it → create_order with the line items → attach_order_document with the same s3_key. For more than a couple of rows always use the bulk_* variant — running them one at a time races on shared contract locks
Projects (job costing) list_projects, get_project, create_project, update_project, bulk_create_projects, bulk_update_projects (write except list/get) Jobs that orders, contracts and invoices hang off; assign invoices to them with bulk_assign_invoices
Journal Entries list_journal_entries, get_journal_entry, create_journal_entry (write) Read entry lists, inspect one entry, and create journal entries
Customers & Vendors list_*, get_*, create_*, update_*, export_vendors, export_customers, bulk_create_vendors, bulk_create_customers (write) CRUD-style contact management plus CSV export/import
Counterparties search_counterparty_registry, link_counterparty, unlink_counterparty, list_counterparty_defaults, export_counterparty_audit, create_counterparty_default, update_counterparty_default, delete_counterparty_default, bulk_create_counterparty_defaults, generate_upload_url (write for link/unlink, default rules, and uploads) Registry search, link/unlink, default rules (merchant → ledger), audit export, and CSV bulk import (use generate_upload_url for the s3_key)
Credit Memos & Vendor Credits list_*, get_*, create_*, issue_*, apply_*, unapply_*, void_* (write) Full credit lifecycle for AR credit memos and AP vendor credits — create, issue, apply to / unapply from invoices and bills, and void
Delayed Charges list_delayed_charges, get_delayed_charge Unbilled charges and credits
Tags, Budgets, Tax, Products list_tag_groups, list_budgets, get_budget_vs_actual, list_tax_*, list_products Supporting master data
Close & Quality get_close_checklist, get_close_score, list_close_blockers, list_anomalies, dismiss_anomaly, list_attention_items, mark_attention_read, month-end packages DayZero close intelligence: phased checklist, readiness score, explicit blockers, plus anomalies/inbox workflows
Audit & Oversight get_entity_audit_trail, get_user_activity, get_activity_feed, analyze_audit_log Change history for an entity, what a user did ("what did Quincy do today?"), the business-wide activity feed, and AI analysis of unusual activity
Firm-wide get_firm_client_overview, get_firm_user_activity For advisory-firm users: cross-client portfolio/AR-AP/work-queue overviews and one user's activity across all the firm's clients
Reports list_report_types, generate_report, get_report P&L, balance sheet, and other exports
Async Jobs get_job_status, trigger_bulk_transaction_upload, trigger_bulk_je_upload, trigger_teal_sync, trigger_month_end_close (write except get_job_status) Start long-running workflows immediately and poll by job id
Reconciliation list_reconciliations, get_reconciliation Bank reconciliation status
Analytics get_financial_overview, get_top_expenses, get_cash_flow_totals, get_profit_trends, get_transaction_summary, compare_periods Server-side aggregations for analytical questions

You don't need to know your business ID upfront. The business_id parameter is optional on every tool — the server automatically resolves it:

  • One business — selected automatically, zero friction.
  • Multiple businesses — clients that support elicitation show a scope picker (firm-wide vs a specific business) as a native dropdown; other clients get your business list so the assistant can ask which one to use. Firm users with many clients can call find_business("name") to fuzzy-search for the right ID.
  • Explicit ID — you can always pass a business_id directly if you prefer.

For cross-client questions ("across all our clients"), advisory-firm users should use the firm-wide tools (get_firm_client_overview, get_firm_user_activity) instead of looping per business.

The assistant remembers your selection for the rest of the conversation. Say "switch to [business name]" at any time to change.

Note: All monetary amounts are in cents (e.g. $150.00 = 15000). IDs are UUID v7 strings. Pagination is cursor-based — pass the next_cursor value from a response as cursor to get the next page. All date parameters use YYYY-MM-DD format.

56 write tools require the write permission (a viewer-role token cannot call them): journal entries, transactions, bills, invoices, payments, contacts, counterparty registry links, counterparty default rules, vendor/customer exports and bulk imports, credit memos, vendor credits, reconciliation, anomalies, attention, file uploads (generate_upload_url), and more. The assistant should confirm details with you before calling them.

Bulk imports (end-to-end): call generate_upload_url(filename) to get an upload_url and s3_key, HTTP PUT your CSV/Excel file to the upload_url, then call bulk_create_vendors, bulk_create_customers, or bulk_create_counterparty_defaults (or the trigger_bulk_* uploads) with that s3_key.

Every write tool accepts an optional dry_run=true parameter. When set, the server returns a preview of what would happen without committing changes — useful for confirmation flows.

Full tool reference

Auto-generated from the live MCP registry: 225 tools (108 write, 93 approval-gated, 40 exposed by default). Regenerate with make mcp-manifest.

Tool Access Approval gate Default set
accept_payment_suggestion write confirm —
add_feedback_comment write — —
advance_order_status write confirm —
analyze_audit_log read — —
apply_credit_memo write confirm —
apply_vendor_credit write confirm —
approve_bill write confirm ✓
approve_match write confirm —
attach_order_document write confirm —
attach_transaction_receipt write confirm —
build_model write — —
bulk_approve_matches write confirm —
bulk_assign_invoices write confirm ✓
bulk_create_contracts write confirm —
bulk_create_counterparty_defaults write confirm —
bulk_create_customers write confirm —
bulk_create_deals write confirm —
bulk_create_invoices write confirm —
bulk_create_orders write confirm —
bulk_create_projects write confirm —
bulk_create_vendors write confirm —
bulk_mark_invoices_paid write confirm —
bulk_reject_matches write confirm —
bulk_retry_failed_invoices write confirm —
bulk_update_contracts write confirm —
bulk_update_counterparty write confirm —
bulk_update_deals write confirm —
bulk_update_invoices write confirm —
bulk_update_projects write confirm —
cancel_bill write confirm —
categorize_transaction write confirm ✓
compare_periods read — —
complete_reconciliation write confirm —
convert_deal_to_order write confirm —
create_bill write confirm ✓
create_contract write confirm —
create_counterparty_default write confirm —
create_credit_memo write confirm —
create_customer write confirm —
create_deal write confirm —
create_feedback write — ✓
create_feedback_linear_ticket write — —
create_invoice write confirm ✓
create_journal_entry write confirm —
create_order write confirm —
create_project write confirm —
create_vendor write confirm —
create_vendor_credit write confirm —
delete_counterparty_default write confirm —
delete_invoice write confirm —
dismiss_anomaly write confirm —
download_reconciliation_pdf read — —
email_report write — —
export_counterparty_audit read — —
export_customers write confirm —
export_dataset write confirm —
export_vendors write confirm —
extract_document_entities read — —
fetch read — ✓
finalize_invoice write confirm —
find_business read — ✓
generate_report write — ✓
generate_upload_url write — ✓
get_activity_feed read — —
get_aged_payables read — ✓
get_ap_snapshot read — ✓
get_ar_snapshot read — ✓
get_balance_sheet read — —
get_bank_balances read — —
get_bill read — ✓
get_budget read — —
get_budget_vs_actual read — —
get_bulk_suggested_matches read — —
get_business_info read — ✓
get_cash_flow read — —
get_cash_flow_totals read — —
get_close_checklist read — —
get_close_score read — —
get_contract read — —
get_credit_line read — —
get_credit_memo read — —
get_credit_portfolio read — —
get_customer read — —
get_customer_balance read — —
get_customer_statement read — —
get_daily_brief read — ✓
get_deal read — —
get_entity_audit_trail read — —
get_feedback read — —
get_feedback_attachment_url read — —
get_feedback_dom_snapshot read — —
get_feedback_screenshot read — —
get_feedback_stats read — —
get_financial_overview read — —
get_firm_client_overview read — ✓
get_firm_user_activity read — —
get_fixed_asset read — —
get_invoice read — ✓
get_invoice_create_requirements read — —
get_job_status read — ✓
get_journal_entry read — —
get_ledger read — —
get_match_review read — —
get_month_end_package read — —
get_order read — —
get_payment_priority_suggestions read — —
get_pnl_report read — —
get_profit_trends read — —
get_project read — —
get_receipt_coverage read — —
get_reconciliation read — —
get_recurring_template read — —
get_report read — —
get_top_expenses read — —
get_transaction read — ✓
get_transaction_suggested_matches read — —
get_transaction_summary read — —
get_trial_balance read — —
get_uncategorized_summary read — —
get_unmatched read — —
get_user_activity read — —
get_vendor read — —
get_vendor_balance read — —
get_vendor_credit read — —
import_model_workbook read — —
import_ramp_receipts write confirm —
issue_credit_memo write confirm —
issue_vendor_credit write confirm —
link_bill_payment write confirm ✓
link_counterparty write confirm —
link_invoice_payment write confirm ✓
link_invoice_to_contract write confirm —
list_accounting_periods read — —
list_anomalies read — —
list_attention_items read — —
list_auto_approvals read — —
list_bank_accounts read — —
list_bank_rules read — —
list_bill_payments read — —
list_bills read — ✓
list_budgets read — —
list_close_blockers read — —
list_contracts read — —
list_counterparty_defaults read — —
list_credit_lines read — —
list_credit_loans read — —
list_credit_memos read — —
list_customers read — —
list_deals read — —
list_feedback read — —
list_fixed_assets read — —
list_invoice_payments read — —
list_invoices read — ✓
list_jobs read — —
list_journal_entries read — —
list_ledgers read — —
list_mcp_capabilities read — ✓
list_month_end_packages read — —
list_my_businesses read — ✓
list_notifications read — —
list_orders read — —
list_products read — —
list_projects read — —
list_reconciliations read — —
list_recurring_templates read — —
list_report_types read — —
list_reports read — —
list_subscriptions read — —
list_tag_groups read — —
list_tags read — —
list_tax_codes read — —
list_transactions read — ✓
list_vendor_credits read — —
list_vendors read — —
mark_all_attention_read write confirm —
mark_attention_read write confirm —
mark_bill_received write confirm ✓
mark_invoice_paid write confirm ✓
mark_reviewed write — —
mark_transactions_reconciled write — —
match_transaction write confirm —
read_gmail_message read — —
read_transaction_receipt write — —
recategorize_by_counterparty write confirm —
refresh_model write — —
reject_match write confirm —
reject_payment_suggestion write confirm —
remove_transaction_receipt write confirm —
request_transaction_receipts write confirm —
revoke_auto_approval write — —
search read — ✓
search_counterparty_registry read — —
search_entities read — ✓
search_gmail read — —
search_tools read — ✓
send_invoice write confirm ✓
set_receipt_exempt write — —
split_transaction_by_receipt write confirm —
start_background_job write confirm —
start_reconciliation write confirm —
trigger_bulk_invoice_edit write confirm —
trigger_bulk_invoice_upload write confirm ✓
trigger_bulk_je_upload write confirm ✓
trigger_bulk_transaction_upload write confirm ✓
trigger_month_end_close write confirm ✓
trigger_payment_matching write — —
trigger_ramp_repair write confirm —
trigger_teal_sync write confirm ✓
unapply_credit_memo write confirm —
unapply_vendor_credit write confirm —
unlink_counterparty write confirm —
update_bill write confirm —
update_contract write confirm —
update_counterparty_default write confirm —
update_customer write confirm —
update_deal write confirm —
update_feedback write — —
update_invoice write confirm ✓
update_order write confirm —
update_project write confirm —
update_transaction write confirm ✓
update_vendor write confirm —
void_credit_memo write confirm —
void_invoice write confirm —
void_vendor_credit write confirm —

Approval gates (elicitation)

High-risk writes — payments (link_invoice_payment, link_bill_payment, mark_invoice_paid), voids (void_invoice, void_credit_memo, void_vendor_credit, cancel_bill), external sends (send_invoice, send_estimate), and irreversible state changes (finalize_invoice, convert_estimate) — pause for in-band user approval before committing, using the MCP elicitation capability. If your client supports elicitation (Claude Desktop, recent MCP hosts), you'll see a native confirm dialog summarizing the change (amounts formatted in dollars) with an approve/decline choice. Declining returns {"approved": false, ...} and commits nothing.

Clients without elicitation support are unaffected: the host's own tool-call consent plus the dry_run preview flow remain the gate, exactly as before.

UI cards (MCP Apps)

Hosts that advertise MCP Apps / mcp-ui support receive rendered HTML cards as embedded ui://dayzero/* resources alongside the JSON payload on snapshot tools: get_daily_brief, get_ar_snapshot, get_ap_snapshot, get_aged_payables, get_uncategorized_summary, and get_payment_priority_suggestions. Cards show key figures, aging tables, and dollar-formatted amounts with automatic light/dark styling. Clients without UI support receive identical JSON-only responses.

Note: create_invoice requires Stripe Connect (or a demo business) — call get_invoice_create_requirements first. Typical flow: create_invoice → finalize_invoice → send_invoice or link_invoice_payment. mark_invoice_paid is for manual paid status without a bank match. AP: approve_bill → mark_bill_received (needs expense ledger_id). get_aged_payables pairs with get_ar_snapshot; get_payment_priority_suggestions uses AI and may take a few seconds.

Long-running workflows return immediately with {job_id, status: "started", poll_tool: "get_job_status"}. Do not wait inline; poll get_job_status(job_id) until completion/failure.

Reports now follow: list_report_types → generate_report → poll get_job_status (use job_id) → optional get_report when the completed job returns a report_id.

When a tool fails

A failed tool returns a teaching error rather than a bare message — the same shape whether the failure came from the tool's own validation or from the data underneath it:

json
{
  "error": "You have access to multiple businesses. Please specify which one to use: …",
  "reason_code": "BUSINESS_SELECTION_REQUIRED",
  "how_to_fix": "Ask the user which of the businesses listed in the error to use, then pass its business_id.",
  "retryable": false,
  "did_you_mean": []
}

Read how_to_fix and did_you_mean before retrying, and don't retry at all when retryable is false. Common reason_code values:

reason_code Meaning
BUSINESS_SELECTION_REQUIRED You have several businesses; pass the business_id of the one you want (the error lists them)
FIRM_SELECTION_REQUIRED You belong to several advisory firms; pass firm_id
NOT_FOUND No record with that id in this business — look it up first, and check business_id
INVALID_ARGUMENT / VALIDATION_ERROR / INVALID_ENUM / INVALID_DATE_FORMAT Fix the named argument; did_you_mean lists the accepted values
MISSING_REQUIRED_FIELD An update with no fields to change
PERMISSION_DENIED Your role or grant doesn't cover this tool; access has to change first
MISSING_SCOPE_READ / MISSING_SCOPE_WRITE Re-authorize with the missing OAuth scope
PARTIAL_SUCCESS A bulk call saved some rows — retry only the ones in data.failed
COMMIT_FAILED Nothing was saved; retry the whole call
RATE_LIMITED / TIMEOUT Retryable — wait a moment and call again
UNKNOWN_ERROR Unclassified; read error

A bare Error executing tool <name> with no envelope means an internal fault, not something to correct in the call — it is reported to DayZero automatically.

Working with Large Datasets

List tools return a total field showing total matching records. Use this to decide how to proceed:

  • Analytical questions ("what did we spend the most on?", "are we profitable?", "how much revenue last quarter?") — use the analytics tools (get_financial_overview, get_top_expenses, get_cash_flow_totals, get_profit_trends, get_transaction_summary). These compute summaries server-side and return small payloads regardless of data volume.
  • Under ~100 matching records — paginate through them if you need individual details.
  • Over ~100 matching records — narrow with filters (date range, search text, ledger_id, status) or use analytics tools. Don't page through thousands of records.
  • Full exports — use generate_report to create a downloadable file.

Strategy: summarize first, drill down second. Start with aggregate tools to understand the big picture, then use list tools with filters to examine specific subsets.

Prerequisites

  1. A DayZero account with API access enabled (required before OAuth can mint MCP tokens)
  2. A DayZero login (Clerk) for the browser consent flow

Connecting Claude Desktop

The fastest path is the install link — Add DayZero to Claude. It opens claude.ai's Add custom connector dialog with the name and URL prefilled; review it, click Add, and sign in to DayZero when prompted. Connectors are account-level, so one add covers Claude on web, desktop, and mobile.

Prefer local config? Add the server with just its URL. Claude registers itself, opens a browser for you to sign in to DayZero and approve access, then stores the MCP OAuth token automatically:

json
{
  "mcpServers": {
    "dayzero": {
      "url": "https://api.ondayzero.com/mcp"
    }
  }
}

Config file location:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json

Restart Claude Desktop after saving. You should see DayZero tools appear in the tools menu.

Connecting Claude Code

One command:

bash
claude mcp add --transport http dayzero https://api.ondayzero.com/mcp

Then run /mcp inside Claude Code to complete the browser sign-in. To share the server with your whole team, commit a .mcp.json at the repo root instead:

json
{
  "mcpServers": {
    "dayzero": {
      "type": "http",
      "url": "https://api.ondayzero.com/mcp"
    }
  }
}

Connecting Cursor

The Add to Cursor button under Settings → Developers in DayZero opens Cursor with the server prefilled — confirm the install and sign in when the browser opens.

To add it manually, go to Settings > MCP Servers in Cursor and add a new server:

  • Name: DayZero
  • URL: https://api.ondayzero.com/mcp

Leave the headers blank — Cursor opens a browser for you to sign in and approve via OAuth.

Connecting ChatGPT

ChatGPT requires Developer mode before you can add an unlisted MCP connector:

  1. Enable it under Settings → Apps → Advanced settings → Developer mode (available on Plus and Pro; on Business, Enterprise, and Edu workspaces an admin may need to allow it — or an admin can add DayZero org-wide from the workspace admin console).
  2. Create a new connector with Name: DayZero, URL: https://api.ondayzero.com/mcp.

ChatGPT discovers the OAuth endpoints, opens a browser for you to sign in to DayZero, and stores the token automatically. Connectors are account-level, so the same connection works in the ChatGPT web, desktop, and mobile apps.

Connecting Codex

Codex CLI, the Codex IDE extension, and the ChatGPT desktop app share one MCP configuration. Add two lines to ~/.codex/config.toml:

plaintext
[mcp_servers.dayzero]
url = "https://api.ondayzero.com/mcp"

Or configure it in the ChatGPT desktop app under Settings → MCP servers → Add server — name dayzero, transport Streamable HTTP, URL above. Codex opens your browser to sign in to DayZero the first time it connects.

Connecting Other MCP Clients

Any MCP client that supports Streamable HTTP transport can connect. The server implements the full MCP authorization spec, so compatible clients configure themselves automatically from the server URL alone:

  • RFC 9728 Protected Resource Metadata — https://api.ondayzero.com/.well-known/oauth-protected-resource
  • RFC 8414 Authorization Server Metadata — https://api.ondayzero.com/.well-known/oauth-authorization-server

These advertise the authorization server, supported scopes, PKCE methods, and the authorize / token / registration endpoints.

The canonical resource URL (RFC 8707 audience) is https://api.ondayzero.com/mcp — no trailing slash.

Browser-based and hosted clients (Origin allowlist)

As part of the MCP spec's DNS-rebinding protection, the server validates the Origin header on every MCP request. Requests without an Origin header — desktop apps and CLIs such as Cursor, Claude Desktop, Codex CLI, and mcp-remote — always pass. Requests with an Origin header must match the allowlist, which covers:

  • Anthropic — https://claude.ai, https://claude.com
  • OpenAI — https://chatgpt.com, https://chat.openai.com, https://platform.openai.com
  • Cursor — https://cursor.com, https://cursor.sh
  • Local development — http://localhost:*, http://127.0.0.1:* (e.g. MCP Inspector)

A hosted client whose origin is not allowlisted receives 403 Invalid Origin header on every MCP call after OAuth succeeds — the connector shows as connected but lists zero tools. If you're building or operating a hosted MCP client that should connect to DayZero, contact us so we can add your origin (self-hosted deployments can extend the list via the MCP_ALLOWED_ORIGINS_EXTRA environment variable).

Environment URLs

Environment MCP server URL Authorization server
Production https://api.ondayzero.com/mcp https://api.ondayzero.com
Local dev http://localhost:8000/mcp http://localhost:8000

Use the MCP server URL in your client config. Discovery endpoints live on the API host (not under /mcp).

Authentication

MCP uses OAuth 2.1 with browser consent. Tokens minted through this flow are MCP-only — they cannot call the REST API (/api/v1/*). Likewise, REST API keys and SPA session tokens cannot authenticate MCP tool requests.

OAuth 2.1 flow

  1. The client connects to /mcp and receives a 401 with a WWW-Authenticate header pointing at /.well-known/oauth-protected-resource.
  2. It reads RFC 9728 protected-resource metadata and discovers the authorization server.
  3. It reads RFC 8414 authorization-server metadata at /.well-known/oauth-authorization-server.
  4. It dynamically registers itself (RFC 7591) at POST /oauth2/register — no pre-shared client credentials needed.
  5. It opens your browser to GET /oauth2/authorize with PKCE (S256) and an optional RFC 8707 resource parameter (must match the MCP server URL).
  6. You sign in via Clerk and review the consent page, which shows:
    • The app name and human-readable scope descriptions
    • Your signed-in identity and accessible businesses (via GET /oauth2/userinfo)
    • Where you'll be redirected after approval (with loopback / non-HTTPS warnings when applicable)
  7. After you click Allow access, the client exchanges the authorization code at POST /oauth2/mcp/token for an MCP access token (plus a refresh token when the grant includes offline_access).
  8. The access token is sent as Authorization: Bearer … on every MCP request and refreshed automatically when it expires.

Your account must have API access enabled before tokens can be minted. If API access is disabled for your user, authorization fails with access_denied.

OAuth scopes

Scopes appear on the consent page in plain English:

Scope What it allows
mcp:read View your accounting data — read transactions, balances, invoices, and reports
mcp:write Make changes on your behalf — create and modify journal entries, invoices, bills, and other records
offline_access Stay connected — keep this app authorized without signing in each time
mcp:admin Act as DayZero staff — staff-only tools (feedback triage, admin repairs) and every client business. Staff accounts only.

Write tools require both mcp:write and write permission on the business. A token with only mcp:read can query data but cannot call write tools.

mcp:admin is different from the other scopes: it is not advertised in scopes_supported, a client cannot obtain it by asking, and a superuser's token does not carry it by default. It is granted only when a DayZero staff member ticks Act as DayZero staff on the consent screen. Without it, a staff member's token behaves like any customer's, and the admin tools return 403 with a message pointing back to that box.

A few scope rules to be aware of:

  • Refresh tokens are only issued when the grant includes offline_access. Without it, the client gets an access token only and must re-authorize when it expires.
  • If a client requests no scopes at all, it is granted mcp:read offline_access (read-only, but able to stay connected).
  • If a client registered with an explicit scope, requests are narrowed to that registered set — a read-only registration cannot later escalate to mcp:write.

If the token is missing or invalid, the server returns 401 with a WWW-Authenticate header pointing to the resource metadata endpoint.

Revoking access

OAuth-connected MCP clients (e.g. Claude Desktop, Cursor) create a connected app grant tied to your account. Revoke a grant to immediately invalidate its refresh token:

bash
# List connected apps (requires a DayZero session or REST OAuth token)
curl "https://api.ondayzero.com/api/v1/oauth/grants" \
  -H "Authorization: Bearer YOUR_SESSION_OR_API_TOKEN"

# Revoke a grant by ID
curl -X DELETE "https://api.ondayzero.com/api/v1/oauth/grants/GRANT_ID" \
  -H "Authorization: Bearer YOUR_SESSION_OR_API_TOKEN"

Revoking a grant blacklists both its refresh token and the most recently issued access token, so access is cut off immediately — not when the access token would naturally expire.

Revoking an MCP grant does not affect REST API keys — manage those separately under Settings → Developers.

Security notes

  • MCP tokens carry JWT type=mcp and audience = the MCP resource URL — distinct from REST access, refresh, and api tokens.
  • The consent screen is a React page served at GET /oauth2/authorize; standalone error pages use Jinja2 templates. Both ship strict security headers (CSP nonce, X-Frame-Options: DENY, Referrer-Policy: no-referrer).
  • Dynamic client registrations expire after ~90 days of inactivity; clients re-register automatically.
  • Registered redirect URIs are validated: https, loopback http, and private-use app schemes (e.g. cursor://) are allowed; javascript:/data:/file: and http on non-loopback hosts are rejected.
  • PKCE (S256) is mandatory — authorization codes cannot be exchanged without a valid code verifier.
  • DNS-rebinding protection validates the Host header and allowlists the Origin header on every MCP request (see Browser-based and hosted clients). Requests with an unrecognized Origin are rejected with 403.
  • Refresh tokens are bound to the client they were issued to; the refresh grant requires client_id (and the client secret for confidential clients) and rejects tokens presented by a different client.
  • POST /oauth2/register and POST /oauth2/mcp/token are rate-limited per client IP and return 429 with a Retry-After header when exceeded.

Example Conversation

Once connected, just start asking questions — no setup required:

Single business (most common):

You: What invoices are overdue?

Assistant: You have 3 overdue invoices totaling $4,250.00…

The server auto-detected your business behind the scenes. No IDs needed.

Multiple businesses:

You: Show me last month's transactions

Assistant: You have access to 2 businesses:

  1. Acme LLC — admin
  2. Beta Corp — viewer

Which one would you like to use?

You: Acme

Assistant: Here are Acme LLC's transactions for last month…

After you pick, the assistant remembers your choice for the rest of the conversation. Say "switch to Beta Corp" at any time to change.

More things you can ask:

  • "List all bank accounts"
  • "Show me the last 10 transactions from checking"
  • "Generate a profit and loss report for Q1 2026"
  • "Create a journal entry for $500 rent expense on March 1"
  • "Search for anything related to Acme Corp"
  • "Compare the operating budget to actuals this quarter"
  • "What are our top expenses this year?"
  • "Are we cash-flow positive this quarter?"
  • "How have profit margins changed over the past 6 months?"
  • "How many transactions came in last month?"
  • "What should I look at today?" (Daily Brief)
  • "Mark that Amazon transaction as reviewed"
  • "Add a new vendor called Acme Hosting with email billing@acme.com"
  • "Create a $250 bill from Acme Hosting due next Friday"
  • "Finalize draft invoice #1042"
  • "Email invoice #1042 to the customer"
  • "How much do we owe vendors by aging bucket?"
  • "Void invoice #1042"
  • "Create a $25 credit memo for Acme and issue it"
  • "Cancel the draft bill from Acme Hosting"
  • "Mark these three bank transactions as reconciled"
  • "What's blocking us from creating invoices via API?"

Troubleshooting

Problem Solution
Tools don't appear Verify the URL is https://api.ondayzero.com/mcp (no trailing slash) and restart the client
Connected, but zero tools The client's Origin isn't allowlisted — MCP calls get 403 Invalid Origin header after OAuth. Claude, ChatGPT, and Cursor origins are supported; for other hosted clients see Origin allowlist
Browser sign-in doesn't open Ensure your MCP client supports OAuth 2.1 with dynamic client registration
access_denied during consent Your account may not have API access enabled — ask a firm admin to re-enable it
Stuck re-authorizing Your registered client may have expired — the client re-registers automatically; just retry the connection
401 Unauthorized Re-run the OAuth sign-in; REST API keys (dz_…) do not work on MCP
403 on write tools Your grant may only include mcp:read — re-authorize with write access, or check your business role
403 Forbidden (business) Your token doesn't have access to the specified business_id
404 Not Found Ensure the URL is exactly /mcp — not /mcp/ with a redirect, /mcp/mcp, or /api/mcp
Unknown application / registration expired Close the browser tab and reconnect from your MCP client so it re-registers
Connection timeout Ensure the DayZero API is reachable from your network
Session terminated on every tool call The MCP connector lost its Streamable HTTP session (common after a deploy or when the client doesn't re-initialize). Disconnect and reconnect the DayZero connector in your MCP client, or start a new chat. DayZero runs in stateless Streamable HTTP mode so new requests don't depend on a server-side session ID; if this persists after reconnecting, report it with the client name and timestamp.

What's Next?