Back to site

MCP server (AI agents)

The Contractor Codex ships its own Model Context Protocol server. Point Claude Desktop, Claude Code, or any MCP-compatible client at it, hand it an API key, and the model can run your portal — clock in and out, send invoices, draft quotes and contracts, mark payments paid, refund mistakes, post updates to your clients — without you opening the admin UI.

It sits on the same backend the admin and client portals use. No separate database, no separate auth. Every agent call lands an audit-log row, so you can see exactly what the model did and when.

Endpoint/api/mcp
AuthBearer ctsp_live_…
TransportStreamable HTTP
Tools82
Money actions2-step confirm
Quick start — 3 steps
  1. Enable MCP. Open /admin/settings/mcp and flip Enable MCP Server on the Setup tab.
  2. Create a key. Same screen → Create API Key for MCP. Pick the write groups to grant. The full key shows once, so copy it now. (Key management lives in API keys.)
  3. Wire it into your client. Drop this into your MCP client config:
claude_desktop_config.json
{
"mcpServers": {
  "contractor-codex": {
    "url": "https://contractorcodex.com/api/mcp",
    "headers": { "Authorization": "Bearer YOUR_API_KEY" }
  }
}
}

Using the Claude Code CLI instead?

terminal
claude mcp add contractor-codex --transport http \
--url https://contractorcodex.com/api/mcp \
--header "Authorization: Bearer YOUR_API_KEY"

Once connected, the client lists all 82 tools automatically and the agent can start working. (Strictly, tools/list shows 81: get_openapi is HTTP-only, so it doesn't appear on the MCP surface.)

The endpoint is locked until you do both

Until MCP is enabled and a valid key authenticates, every request is rejected — 401 without a valid key, 403 for your org until the toggle is on. A leaked key alone can't do anything if MCP is off.

How it works

Claude (or any MCP client)
  ↓  JSON-RPC over HTTPS, Bearer-token auth
https://contractorcodex.com/api/mcp
  ↓  validates token, checks your org's mcp_enabled flag
runAgentTool() dispatcher
  ↓  same handler as the /api/agent/<tool> HTTP route
your data

The MCP transport and the HTTP /api/agent/* routes call the same handler functions. Adding a tool exposes it on both surfaces at once — they never drift apart.

The mental model — three layers

Reading top to bottom, you get safer:

LayerWhat it doesExamples
ReadSurface data; no side effects.get_context, get_projects, find_client, daily_briefing
Write (no comms)Mutate state; no email, no money.start_work_session, add_session_note, create_project, update_client
Write (comms or money)Sends email, moves money, or destroys state. Two-step confirm required.send_invoice, mark_paid, send_quote, refund_invoice, archive_client

How the agent asks the right questions

Every write tool that takes a "which one?" parameter returns a structured needs_input response when called without it — instead of erroring. The response carries questions (what to ask), inventory (the real options — recent clients, draft invoices, available templates…), and next_step. The agent grounds in your actual data instead of inventing an ID.

Example — “mark the Henderson invoice paid”
AGENT → mark_paid()                              (no args)
      ← needs_input · inventory.open_invoices: [Henderson $2,400, Mosi LLC $800, …]
AGENT  "I see two open invoices for Henderson — $2,400 and $1,100. Which one?"
USER   "The 2400."
AGENT → mark_paid(snapshotId="…", method="check")
      ← confirmation_required · preview + confirmation_token
AGENT  "Mark the $2,400 Henderson invoice paid via check?"
USER   "Yes."
AGENT → mark_paid(snapshotId="…", method="check", confirmation_token="ct_…")
      ← marked_paid

Two structured round-trips before anything changes. No hallucinated IDs, no guessed amounts.

The two-step confirmation rule

Every tool that sends an email, moves money, or destructively changes state goes through a mandatory two-step confirm, regardless of dollar amount:

  1. First call returns confirmation_required + a confirmation_token + a preview the agent reads aloud.
  2. Second call repeats the params plus the token. The tool executes.

Tokens expire after 60 seconds and are bound to (keyId, action, paramsHash) — they can't be replayed or reused for a different action.

send_client_message follows the same two-step confirm — it puts a real email in a client's inbox, so a human approval sits between the agent and the send.

Auth model

ConceptDetail
Scopeadmin (any client in your org) or client (bound to one client). Keys minted in this screen are admin-scoped.
StorageStored as SHA-256 hashes. The full key shows once at creation.
ExpirySet per key via expiresAt. No auto-rotation — revoke and reissue.
Org gateThe endpoint returns 403 for your org until mcp_enabled is on (and 401 for any request without a valid key).
AuditEvery call writes an audit-log row (key, tool, status, IP, duration).
Rate limit30 calls/min per admin key, 60/min per client key (per-tool overrides available). String numeric params are accepted everywhere.

For a read-only key, untick every write group at creation — read tools require no write permission.

The tool catalog

82 tools, grouped by what they touch. Expand a group to see its tools.

Context & identityany5
ToolPurpose
get_contextThe boot call. Org name, your scope, system instructions, workflows, an inventory snapshot, and the endpoint catalog. Call this first.
whoamiThe key's scope + bound client, if any. Lightweight identity check.
get_helpKeyword search across this docs site.
get_brandingBusiness name, support email, portal greeting, portal theme, accent color (the effective one: custom or the selected theme's own), logo, default currency.
get_openapiThe full machine-readable OpenAPI 3 spec for every endpoint — params, schemas, and required flags. For codegen or deep introspection.
Admin overview — readadmin9
ToolPurpose
admin_get_clientsAll active clients with health scores and project counts.
admin_get_insightsRevenue (net of refunds), overdue counts, dunning stats, health distribution.
admin_get_activityOrg-wide activity log.
daily_briefing"What needs my attention today?" — pending approvals, drafts, overdue invoices, payments collected (net of refunds).
overdue_arOverdue invoices with aging buckets.
quote_status_checkQuote pipeline: open, signed, expiring.
get_advisor_briefingThe Advisor's cached daily briefing: AI headline, narrative, and open recommendations with their computed evidence. Never triggers an AI call.
codex_money_summaryThis month's money from Codex: collected, spent, net, transactions awaiting review, connected banks. Same math as the Codex Overview page.
get_action_centerThe org's open Action Center items (failed/overdue invoices, new and stale leads, unviewed or expiring quotes, pending cancellations, countersigns, calendar sync errors), each with severity, title, and a link. Optional severity filter. Read-only.
Website leadsadmin3
ToolPurpose
list_website_leadsQuote requests submitted through your own website (the Leads inbox).
accept_website_leadTurn a lead into a client (links by email or creates a new record). Two-step confirm.
dismiss_website_leadDismiss a new or spam lead. Reversible from the UI.
Advisor actionsadmin1
ToolPurpose
set_advisor_item_stateCheck off an Advisor recommendation: done, dismissed, or snoozed for 7 days.
Search & lookup4
ToolPurpose
find_clientResolve a natural-language name into a customerId. (admin)
find_projectResolve a project name into a projectId.
find_recentLast N quotes / contracts / invoices / clients / projects, newest first. Optional customerId narrows any kind to one client. (admin)
find_active_sessionThe work session currently running, if any.
Client data — readclient4
ToolPurpose
get_projectsA client's projects with session status, hours, billing profile, and start/end dates.
get_billingA client's invoices plus a canonical summary (owed, net paid, invoiced, refunded) and retainer usage.
get_notificationsA client's recent notifications.
get_activityA client's own activity log.
Settings & status — readadmin4
ToolPurpose
get_settingsFull admin-settings map (timezone, invoice defaults, payment methods, dunning, late fees, quote defaults).
get_billing_profileLegal name, DBA, entity type, EIN last-4, address, tax status, plus a complete boolean.
get_stripe_connect_statusDB flags plus a live Stripe diagnostics call (charges / details enabled). Handles webhook lag.
list_importsThe 20 most recent CSV import batches from the import wizard: kind, file name, status, and per-row tallies. Optional status filter. Read-only; starting or committing an import stays in the wizard UI.
Catalogadmin7
ToolPurpose
list_quote_templatesSaved whole-quote templates.
list_line_item_templatesThe line-item library (built-in starter items + your own).
list_surchargesSurcharge templates.
list_contract_templatesThe contract-template library. Built-in starter templates auto-seed the first time it's called.
save_to_catalogPromote freeform line items / surcharges from a quote into reusable templates.
list_pricing_ratesThe pricing rate card — retainer tiers, hourly rates, block packages, and fixed-fee rows, with their codes and included hours.
create_service_planGuided. Build a reusable Service Plan — net days, deposit, after-hours/weekend multipliers, min increment, auto-approve ceiling, tax behavior, schedule template. Only name required; the rest inherit org defaults.
Service plans, scheduling, costs & configadmin7
ToolPurpose
attach_service_planGuided. Apply a Service Plan to a project — locks its billing terms and regenerates the client's Billing Terms document.
add_project_discountGuided. Add a standing discount to a project (percentage / fixed / per-hour) that recurs into billing as a negative line item.
add_project_expenseGuided. Record a recurring product cost — a third-party cost the client pays the vendor directly (a disclosure, never invoiced).
create_calendar_eventGuided. Add a calendar event (UTC times; optional client/project link and client visibility). Pushes to Google when the key creator's calendar is connected.
create_delivery_requestGuided. Open a work request on the Request-to-Delivery board for a client.
update_delivery_requestMove a delivery request across the board — status / priority / due date / project.
update_settingChange one org setting from a curated safe set; money/tax keys require a two-step confirmation.
Clients — writeadmin4
ToolPurpose
create_clientFind-or-create by (org, email). Readies a portal-signup invite.
update_clientPatch name, phone, company, subscription, industry, timezone. Elicits a recent-clients list if customerId is missing.
archive_clientSoft-archive. Two-step confirm. Elicits if customerId missing.
restore_clientClears archive/delete flags. Lists archived clients if customerId missing.
Projects — writeadmin5
ToolPurpose
create_projectLocks a pricing snapshot. Optional start and end dates (the end date drives the calendar milestone). Elicits clients + billing profiles if anything's missing.
update_projectPatch name, description, billing profile, budget, start and end dates. Changing the end date keeps the calendar milestone in step.
complete_projectMarks done + completion note. Refuses if a session is still running.
reopen_projectClears the completion state so new work and billing can resume. Completed projects stay browsable without reopening; reopen only to log new work or billing.
add_project_surchargeMid-job surcharge. Auto-approved when pre-disclosed, otherwise emails the client for approval. Confirm.
Sessionsadmin5
ToolPurpose
start_work_sessionClock in. Elicits active projects if projectId missing; returns a conflict if already running elsewhere.
stop_work_sessionClock out. Auto-resolves the active session if sessionId omitted.
add_session_noteAppend a note to a session.
set_session_billableFlip a session's billable flag. Refuses if the session is already on a sent invoice — void and re-issue first.
correct_sessionAdjust a session's start / end times. Same invoiced-session guard.
Project updates & notes3
ToolPurpose
list_project_updatesNewest-first thread with replies.
post_project_updateAdmin status note — emails the client and shows on their dashboard. Two-step confirm. (admin)
reply_to_project_updateThreaded reply. Notifies admins for client replies; no email.
Quotes & contractsadmin7
ToolPurpose
draft_quote_from_descriptionPrice a whole job from plain English using the same AI drafter as the New Quote screen (catalog matches, price anchors, live material lookups). Returns a priced preview, a draft_token for create_quote, and a preview_pdf_url so the admin can see the exact client PDF before anything is saved.
modify_quote_draftEdit a drafted quote from a plain-English instruction ("make the water heater $700", "drop the cleanup line") without re-drafting. Untouched lines carry through unchanged; returns a fresh token and preview.
create_quoteThe marquee tool. Cold-call mode: tell it the client wants a quote and it asks every question (recipient, line items, billing mode, deposit %, expiry) with tailored catalog suggestions. Also persists AI drafts via draftToken. Returns a preview_pdf_url for the saved draft.
send_quoteEmail the signing link, pre-signed with the key-owner's saved signature. Two-step confirm. Elicits draft quotes.
create_contractDraft an agreement or change order from a template. Elicits a template picker (built-ins included) when templateId is missing; find-or-creates the client.
send_contractSame as send_quote for agreements / change orders. Two-step confirm.
request_approvalClient approval flow for budget overruns or undisclosed surcharges. Two-step confirm.
Invoices & paymentsadmin6
ToolPurpose
send_invoiceFinalize + email a draft. Two-step confirm. Elicits drafts. Supports dry_run.
mark_paidOut-of-band payment (check, wire, cash). Supports partial amounts (amount_paid_cents) — adds to prior payments and flips to paid only when fully covered. Two-step confirm.
list_invoice_draftsSnapshots with no Stripe invoice or draft status. Filterable by client.
void_invoiceVoid an open invoice in Stripe. Two-step confirm. (Refuses a paid invoice — refund instead.)
refund_invoiceRefund a paid invoice; partials supported; platform fee reverses proportionally. Two-step confirm.
mark_invoice_uncollectibleWrite off an open invoice as bad debt. Two-step confirm.
Client commsadmin4
ToolPurpose
send_client_messageFree-form email to a client. Two-step confirm (token) — the first call returns the message preview to read back. Elicits recipient + subject + body when missing. Your saved email signature is appended automatically. Replies land in the Email tab inbox with a copy forwarded to your own mailbox, and repeat sends to the same client continue the same thread.
list_client_repliesClient replies from the Email tab inbox, unread first, with the reply text so the agent can read it out. Never marks anything read.
list_client_emailsEverything the platform has emailed a client (composed messages, invoices, notifications), newest first. Outbound only. Read-only.
list_client_contactsPeople saved for a client (or the whole directory): name, role, email, phone, primary flag. Read-only.
Team managementadmin4
ToolPurpose
list_team_membersYour org's admin staff — roles, status, owner count. Read-only.
invite_team_memberOwner-only. Invite an admin (admin or viewer, never owner). Two-step confirmation with an email + role read-back before it sends.
list_client_teammatesA specific client's portal teammates. Read-only.
invite_client_teammateInvite a teammate to a client's portal (joins as a member). Two-step confirmation with an email read-back.
Auto pre-sign on send_quote / send_contract

Both look up the saved signature of the admin who minted the key and stamp it before the client email goes out — matching the human composer flow. The signer of record is whoever owns the key, so multi-admin teams get correct attribution. No saved signature → the send still goes through unsigned and the response returns a warning the agent should read back. Save one at Settings → Signatures and every future send is pre-signed. (Change orders skip pre-sign — they amend an already-signed parent.)

Not on the agent surface yet

Kept out of the agent surface on purpose: removing or re-roling team members — inviting admins and client teammates is now invite_team_member / invite_client_teammate, but removals and role changes stay in the admin UI, since revoking the wrong person's access is the kind of mistake a confirmation prompt can't fully undo. Two more are intentionally not agent actions: approval decisions are client-side by design — the admin opens a request with request_approval, but accepting it is the client's consent to give, never the admin's (or an agent's) to make on their behalf; and the platform-fee / subscription / API-key / MCP-toggle settings are excluded from update_setting on purpose. Need a tool we don't have? Contact support — we ship new tools fast.

Troubleshooting

Stale tool list after a deploy

If your client cached the tool list before a platform update, restart the MCP connection — the next initialize refreshes everything.

  • Confirmation token expired — tokens TTL at 60s. Re-call the same tool without the token; the response carries a fresh preview.
  • Insufficient permissions — the key lacks the right write group. Edit it under Settings → API Keys.
  • Customer not found in this organization — an admin key passed a customerId from a different org. Keys never see across orgs.
  • Stripe Connect is not configured — the org hasn't finished Connect onboarding. Call get_stripe_connect_status to see where they are; you can't send or collect invoices without it.

Best practices for client developers

  1. Call get_context first. It returns the workflows map, inventory snapshot, system instructions, and endpoint catalog — the whole session leans on it.
  2. Never pre-fill IDs from training data. When a tool returns needs_input with an inventory, use those IDs verbatim. Hallucinated IDs are the #1 failure mode.
  3. Read previews back to the user word-for-word before the second (committing) call.
  4. On second thoughts, start fresh. If the user changes their mind between calls, re-ask the params and issue a new token.
  5. Use dry_run="true" on send_invoice / mark_paid to preview the dollar amount without locking a confirmation token.

Audit & observability

Every call lands an AgentAuditLog row — tool name, status, IP + user agent, duration, and the linked key. View recent calls under Settings → API Keys → Activity. The same data feeds daily_briefing's activity rollup (calls per day, error rate, top tools).