Herv.ai Sign in

Connect AI agents to Herv.ai

Herv.ai's MCP server lets an AI agent (Claude, ChatGPT, Cursor, Hermes Agent or your own) read your firm's quality-control results and do day-to-day QC work as you, with no more access than you have in Herv.ai.

Early access. AI agent access is open to early-access firms today, and general availability is coming soon. If your firm is not in early access yet, the steps below will not connect.

What an agent can do

Each tool needs one scope (see Scopes). The full list with arguments is the Tool reference.

Scope What it lets an agent do
read See clients, their checks and schedules, check results and history, reported problems and their history, exceptions, tags, team members (names and roles, no emails), the eight reports and the reports-page KPIs. Look up the QuickBooks Online or Xero transaction behind one problem, live.
write Everything in read, plus: run checks for one client, turn a check on, change a check's schedule or options, resolve or reopen a problem, remove an exception, set a client's bookkeeper or reviewer, add or remove existing tags on clients.
destructive Everything in write, plus the actions that hide findings: dismiss problems, turn a check off, add a payment or vendor exception. Each one needs confirm: true in the call, has its own daily cap, and can be undone with a write tool.

A check run started by an agent does exactly what a run started in the web app does, including the client email, Karbon work items and Zapier hooks your firm has set up.

What an agent can never do

Whatever scope it holds, an agent cannot:

  • run, turn on or configure Inactivate Unused Vendors or any other check that changes live

QuickBooks Online or Xero data (it is not even listed to agents as manageable);

  • create, delete or disable clients, use "Enable All", or run checks across every client at once;
  • connect or disconnect QuickBooks Online, Xero, Karbon or Zapier;
  • invite people, change roles or permissions, or change who can open a client;
  • touch billing, the subscription or discount codes;
  • change firm settings, 2FA, or switch AI agent access on or off;
  • change where notifications go (client email, Karbon, Zapier);
  • create, see or revoke API keys or connected apps, or read the agent activity log;
  • export reports (XLSX or PDF), edit dashboards, reorder checks or edit client templates;
  • read general accounting data (P&L, general ledger, vendor lists); the only live lookup is the

one transaction behind a problem the member can already see;

  • see bank account or routing numbers, tax ids, attachments, other people's email addresses,

or anything from another firm.

Text that came from your clients' books (names, memos, descriptions) or that another person typed is handed to the agent marked untrusted, with an instruction to report it and never follow instructions inside it.

Before you start

  1. A Super Admin (owner) turns on Settings → AI Agents → AI agent access. It is off by

default. Turning it off later revokes every key and disconnects every app in the firm at once, and turning it back on restores none of them.

  1. Platform-admin accounts can never connect agents.
  2. Creating a key or approving an app needs a sign-in within the last 15 minutes; Herv.ai asks

you to sign in again if yours is older.

  1. A firm whose subscription has lapsed into read-only mode keeps read tools; write and

destructive tools answer PAYMENT_REQUIRED, and no new app or key can be connected.

Every new key or app is emailed to the member who connected it and to every Super Admin of the firm.

Connecting with OAuth

OAuth is the normal way to connect an app: you paste the server URL into the app, sign in to Herv.ai in your browser, and approve it. Nothing secret is copied by hand. In Herv.ai, the Connect an AI agent card at the top of Connected apps & keys has the address with a Copy button and these steps; turning AI agent access on takes the owner straight there.

Client Status Redirect type
Claude.ai and Claude Desktop (custom connector) not yet verified hosted https
Claude Code not yet verified loopback (http://127.0.0.1:<port>)
ChatGPT (connector) not yet verified hosted https
Cursor not yet verified loopback or custom app redirect
Hermes Agent (Nous Research) verified 2026-11-14 loopback (http://127.0.0.1:<port>)

The steps below follow each client's published instructions and Herv.ai's discovery documents. They will be replaced by the exact verified steps, with the date, as each client passes its launch gate.

After you sign in, Herv.ai shows a consent page with:

  • the app's name, or "Unnamed app" when it registered without a usable one, and an

"Unverified app" label (Herv.ai does not vouch for any app);

  • the redirect host: where Herv.ai will send you back to. Check it is the app you meant to

connect. A warning appears when it is only your own computer (localhost / 127.0.0.1);

  • your email and your firm;
  • the access: read is always included, write is pre-checked, and destructive is

unchecked, with the warning "Lets the agent hide findings: dismiss problems, turn checks off, add exceptions."

Approve or Deny. Denying needs no recent sign-in. An approved app holds access for at most 90 days, then asks you to approve it again.

Claude.ai and Claude Desktop (not yet verified)

  1. In Claude, open Settings → Connectors → Add custom connector.
  2. Name it Herv.ai and enter the URL https://app.herv.ai/mcp. Leave any client id and

secret fields empty: Claude registers itself.

  1. Click Connect. Your browser opens Herv.ai; sign in (and complete 2FA) if asked.
  2. Check the consent screen (above) and approve.
  3. Back in Claude, ask: "Use Herv.ai's whoami tool." It should name you and your firm.

Claude Code (not yet verified)

claude mcp add --transport http herv-ai https://app.herv.ai/mcp

Then run /mcp inside Claude Code, choose herv-ai and Authenticate. Your browser opens the Herv.ai sign-in and consent pages. Claude Code receives the result on a loopback address on your own computer, which the consent page names as the redirect host.

ChatGPT (not yet verified)

  1. In ChatGPT, open Settings → Apps & Connectors (developer mode may need to be on for

custom connectors on your plan) and create a connector.

  1. Enter the MCP server URL https://app.herv.ai/mcp and choose OAuth authentication.
  2. Sign in to Herv.ai when asked, check the consent screen and approve.

Cursor (not yet verified)

Add Herv.ai to ~/.cursor/mcp.json (or the project's .cursor/mcp.json):

{
  "mcpServers": {
    "herv-ai": {
      "url": "https://app.herv.ai/mcp"
    }
  }
}

Open Cursor Settings → MCP, find herv-ai and click Login (or Connect). Approve in the browser.

Hermes Agent (verified 2026-11-14)

In ~/.hermes/config.yaml, under mcp_servers:, add:

  herv:
    url: "https://app.herv.ai/mcp"
    auth: oauth

Run /reload-mcp in Hermes (or restart it) and choose Authenticate. Your browser opens Herv.ai: sign in, check the consent screen and approve. Hermes keeps its tokens on your computer and refreshes them itself. Verified on production with Hermes Agent v0.22.5: discovery, registration, consent and a list_clients call.

Disconnecting an app

Open Connected apps & keys (user menu, or Settings → AI Agents). Under Your connected apps, click Disconnect on one connection or Disconnect all for an app. The app's access stops on its next request. Changing or resetting your Herv.ai password also disconnects every app you approved (your API keys survive, and you get an email listing them).

Connecting with an API key

Use an API key for a script or headless agent that cannot open a browser.

  1. Open Connected apps & keys → Create an API key (you may be asked to sign in again).
  2. Give it a name (up to 60 characters), choose the access (read; read + write; or

read + write + destructive, which needs an extra confirmation box) and a lifetime: 7, 30, 90 (default), 180 or 365 days. Every key expires; there is no "never".

  1. Copy the key now. It starts with hrv_k_ and is shown once only. Herv.ai stores only a

keyed hash of it.

  1. Give it to your agent as an environment variable, never in a file you commit or share:
export HERV_API_KEY='<paste the hrv_k_ key here>'
  1. Send it on every request as Authorization: Bearer <key>. Never put it in a URL: a key in

the query string is refused (400).

Each member can hold at most 10 active keys. A key keeps the scopes it was created with; to change them, create a new key and revoke the old one.

Client set-up with a key (all not yet verified on production):

  • Claude Code: the shell expands the variable when the command runs, and Claude Code then

stores the key in its own config file on your computer:

claude mcp add --transport http herv-ai https://app.herv.ai/mcp \
  --header "Authorization: Bearer $HERV_API_KEY"
  • Cursor (~/.cursor/mcp.json, reading the variable at start-up):
{
  "mcpServers": {
    "herv-ai": {
      "url": "https://app.herv.ai/mcp",
      "headers": { "Authorization": "Bearer ${env:HERV_API_KEY}" }
    }
  }
}
  • Any MCP client library: connect with the "Streamable HTTP" transport to

https://app.herv.ai/mcp and set the Authorization header above. The server is stateless and answers in JSON (no server-sent events).

To revoke a key: Connected apps & keys → Revoke. It stops working on its next request.

Scopes and what happens when one is missing

The only valid sets are read, read write and read write destructive. Any other set is refused when a key is created or an app is approved, never silently changed.

  • An agent only sees the tools its scopes and the member's permissions allow

(tools/list is filtered).

  • Calling a tool anyway returns the error SCOPE_REQUIRED with the requiredScope. For an

OAuth app, the server answers 403 with WWW-Authenticate: Bearer error="insufficient_scope", so a capable client asks you to approve the wider access. An API key cannot step up: create a new key with the access you need.

  • Permissions still apply inside a scope. A Staff member's agent sees the clients that member

manages, exactly as on the firm page; reports follow the report pages' rule. A client the member cannot open is NOT_FOUND, the same answer as an id that does not exist.

Tool errors come back as a tool result with isError: true and {"error": {"code": ..., "message": ...}}. The codes are VALIDATION, NOT_FOUND, FORBIDDEN, SCOPE_REQUIRED, PAYMENT_REQUIRED, RATE_LIMITED (with retryAfterSeconds), CONFLICT, REQUIRES_CONFIGURATION (with a webUrl to finish set-up in the web app), UNSUPPORTED_FOR_CHECK, UPSTREAM_UNAVAILABLE (QuickBooks Online or Xero down or disconnected), AUDIT_UNAVAILABLE and INTERNAL (with a ref support can look up).

Tool reference

29 tools: 15 read, 10 write, 4 destructive. This table is generated from the server's tool registry and a test fails when it drifts, so it is always the list the server actually serves. "Untrusted text" = the output carries fields marked untrusted (see above). Ids are Herv.ai ids, given as strings. Lists are paged: pass the next_cursor you got back as cursor, with the same filters.

Tool Scope What it does Arguments Untrusted text
whoami read Shows who you are acting as in Herv.ai: the user, their firm and role, this credential's scopes and expiry, the firm's subscription state (read-only means write tools are refused), and how many clients the user can see. Changes nothing. Call it first when something is refused. none no
list_check_types read Lists the quality checks Herv.ai can run: each check's code, name, description, the platforms it supports (qbo, xero), its configurable options with defaults, and whether it is destructive (modifies live accounting data; agents can never run or configure those). Optional platform filter. Changes nothing. platform no
list_clients read Lists the clients you can see in Herv.ai (owners and admins see every client; staff see the clients they manage), with platform, connection status, bookkeeper, reviewer, tags, when checks last ran and the open issue count from each check's latest run. Filters: search (name contains), tag_ids (any), bookkeeper_id, reviewer_id, connection, has_open_issues. Paged: pass next_cursor back as cursor with the same filters. Changes nothing. search, tag_ids, bookkeeper_id, reviewer_id, connection, has_open_issues, cursor, limit yes
get_client read Shows one client: the list_clients fields plus a summary of every configured check (enabled, run frequency, last run, whether it is running, open issue count and failure flag of its latest run). Use list_client_checks for schedules and options. Changes nothing. client_id (required) yes
list_client_checks read Lists a client's configured checks with their schedule (frequency and crontab), options, last run, whether each is running, and its latest result (id, time, issue count, failure flag). Poll this after running checks. Changes nothing. client_id (required) no
list_check_results read Lists a check's past runs, newest first: run time, the issue count the run reported, a failure flag, and how many problem rows were last seen in that run. Optional date range on the run date. Paged (page up to 50). Changes nothing. check_id (required), date_from, date_to, cursor, limit yes
get_check_result read Shows one check run: its time, issue count and failure flag, and the problems last seen in that run with their labelled fields, status and how many runs have seen each. Paged problems (up to 100). Changes nothing. result_id (required), cursor, limit yes
list_problems read Lists reported problems across the clients whose results you can see, newest first: client, check, status, labelled fields, the transaction date when known, how many runs have seen it and when, and whether it recurs. Filters: client_id, check_id, status (default open), recurring_only, detected_from/detected_to (date first detected). Paged. Changes nothing. client_id, check_id, status, recurring_only, detected_from, detected_to, cursor, limit yes
get_problem read Shows one reported problem: the list_problems fields, the run it was last seen in, and its status history (the newest 20 changes; status_history_truncated says whether older ones exist). Changes nothing. problem_id (required) yes
get_problem_transaction read Fetches the transaction behind one problem live from QuickBooks Online or Xero: type, date, total, currency, account, contact, memo and up to 50 lines. Only for checks whose problems name a single transaction; others answer UNSUPPORTED_FOR_CHECK. Rate limited (30 a minute, 500 a day per user). Changes nothing. problem_id (required) yes
list_exceptions read Lists the exceptions configured on a check: payment exceptions (a whole customer or one invoice skipped by Payment Date Before Invoice Date) and vendor exceptions (vendors allowed to post to several accounts). Up to 200 of each. Changes nothing. check_id (required) yes
list_tags read Lists the firm's client tags with their categories (id and name), for filtering clients and reports. Paged. Changes nothing. cursor, limit yes
list_team_members read Lists the firm's active team members (id, name, role) -- the people a client's bookkeeper or reviewer can be. No email addresses. Paged. Changes nothing. cursor, limit yes
run_report read Runs one of the eight Herv.ai reports with the report page's filter semantics: client_issues, client_recurring, bookkeeper_issues, bookkeeper_recurring, reviewer_issues (counts by client or staff), trends (new issues per month), transaction_counts (per client and month, plus monthly totals) and error_rate (with its scope, coverage and window notes). Dates default to the last 90 days and may span at most 24 months. Up to 500 rows. Changes nothing. report_type (required), date_from, date_to, tag_ids, tag_match, bookkeeper_ids, reviewer_ids, include_unassigned_reviewer, flow_codes yes
get_kpi_summary read Returns the reports index KPIs for the last 90 days: issues detected, recurring issues, average error rate and the new-issue trend (recent 45 days vs the prior 45), none of which count dismissed problems, plus how many were dismissed. A null value means there is no answer yet, and the *_reason fields say why. Changes nothing. none no
run_checks write Queues a run of one client's checks now: pass client_id with either check_ids (up to 30) or all_enabled: true. A run does everything a run started in the web app does, including emailing the client, creating Karbon work items and Zapier hooks when the firm has them set up. Runs take a while; poll list_client_checks for results. A check already queued or running is reported with queued: false (reason already_running) and is not queued twice. Checks that change live accounting data are never run (reason destructive_flow). Limit: 60 queued runs per hour per user. client_id (required), check_ids, all_enabled no
enable_check write Turns a configured check on, like the toggle on the client page, and returns its summary. A schedule the check had when it was turned off (or that was set while it was off) starts again. Enabling an enabled check changes nothing. Answers REQUIRES_CONFIGURATION with a webUrl when the check first needs accounts chosen in the web app, and UNSUPPORTED_FOR_CHECK for checks that change live accounting data or that the client's platform does not support. check_id (required) no
update_check_schedule write Sets when a check runs automatically: frequency daily, weekly (day_of_week 0-6, 0 = Sunday) or monthly (day_of_month 1-28), at hour 0-23 in the server time zone (EST). A disabled check keeps the schedule and starts running on it when enabled (enable_check); until then its summary shows frequency on_demand, because it is not running. Returns the check summary. check_id (required), frequency (required), hour (required), day_of_week, day_of_month no
update_check_options write Changes a check's settings; only the options you pass change. date_range, check_from and check_to (days; null clears) apply to checks with a date range; capitalization_threshold (100-1,000,000 dollars) and ignore_check only to checks that declare them (see list_check_types). Returns the check summary. check_id (required), date_range, check_from, check_to, capitalization_threshold, ignore_check no
remove_payment_exception write Deletes one payment exception (an id from list_exceptions) from a Payment Before Invoice check, so its findings show again on the next run. Removing an exception that is already gone answers NOT_FOUND. exception_id (required) no
remove_vendor_exception write Deletes one vendor (supplier) exception (an id from list_exceptions), so its findings show again on the next run. Removing an exception that is already gone answers NOT_FOUND. exception_id (required) no
assign_client_staff write Sets a client's bookkeeper and/or reviewer to a team member (ids from list_team_members); pass null to clear one, and leave a field out to keep it. The member must already be able to open the client (CONFLICT otherwise). This changes who is named on the client, not who can access it. Returns the client summary. client_id (required), bookkeeper_id, reviewer_id yes
tag_clients write Adds or removes existing tags (ids from list_tags, up to 20) on clients you can see (up to 50). Adding a tag a client has, or removing one it lacks, changes nothing. Never creates tags. If any id is unknown, nothing is changed. client_ids (required), tag_ids (required), action (required) no
resolve_problem write Marks a reported problem resolved: it was a real issue and it has been fixed. Resolved problems still count in reports, and a later check run that finds the problem again reopens it. Optional note (at most 1000 characters) saying what was done. Resolving a problem that is already resolved changes nothing (changed: false). Undo with reopen_problem. Returns the problem. problem_id (required), note yes
reopen_problem write Sets a resolved or dismissed problem back to open, so it counts and is notified again. Optional note (at most 1000 characters). Reopening an open problem changes nothing (changed: false). Returns the problem. problem_id (required), note yes
disable_check destructive Turns a check off, like the toggle on the client page: it stops running on its schedule and reports no new findings until someone enables it again (enable_check), which also restores its schedule. Requires confirm: true. Disabling a check that is already off changes nothing. Checks that change live accounting data cannot be managed by agents (UNSUPPORTED_FOR_CHECK). Limit: 20 disables per day per user. Returns the check summary. check_id (required), confirm (required) no
add_payment_exception destructive Stops a Payment Date Before Invoice Date check from reporting a problem again: level customer skips every invoice for the problem's customer (contact), level invoice skips only the problem's invoice. Pass a problem_id from list_problems or get_check_result; the customer and invoice are taken from that problem. Requires confirm: true. Takes effect on the next run; an identical exception already there is returned with created: false. Undo with remove_payment_exception. Limit: 50 exceptions added or changed per day per user. problem_id (required), level (required), confirm (required) yes
add_vendor_exception destructive Lets the vendor an Inconsistent Vendor Classification problem names post to more than one expense account without being reported: pass the problem_id and, optionally, permitted_accounts (how many accounts are acceptable; default and maximum are the problem's count_accounts). The vendor is taken from the problem. QuickBooks Online clients only. Requires confirm: true. An exception already there for the vendor is updated (created: false). Undo with remove_vendor_exception. Limit: 50 exceptions added or changed per day per user. problem_id (required), permitted_accounts, confirm (required) yes
dismiss_problem destructive Dismisses reported problems that are not real issues: they leave issue counts, reports and notifications, and stay dismissed when a later run finds them again. Pass up to 25 problem_ids and a note of 10-1000 characters saying why they are not problems (shown to the firm). Requires confirm: true. Every id must be visible to you or nothing is changed. Problems already dismissed are returned with changed: false. Undo with reopen_problem; get_problem shows the note and history. Limit: 100 dismissals per day per user; the firm's owners are emailed when one user passes 50 in a day. problem_ids (required), note (required), confirm (required) yes

Limits

Limits are per member unless they say otherwise, so a second key or app does not double them. A refused call returns RATE_LIMITED with retryAfterSeconds (or HTTP 429 with Retry-After before the call reaches a tool).

What Limit
Requests per key or app 120 a minute
Tool calls 120 a minute per key or app, 300 a minute per member
write tool calls 60 a minute
destructive tool calls 20 a minute
Checks queued by run_checks 60 an hour (30 per call)
disable_check 20 a day
add_payment_exception + add_vendor_exception 50 a day together
dismiss_problem 100 problems a day, 25 per call; Super Admins are emailed when one member passes 50 in a day
get_problem_transaction (live lookups) 30 a minute, 500 a day
Request body 1 MiB; at most 2000 argument elements; no JSON-RPC batches
Response 256 KB; longer lists stop early with truncated: true and a cursor
Page size up to 100 rows (list_check_results: 50); run_report: 500 rows; report dates span at most 24 months
API keys 10 active per member; lifetime 7 to 365 days
OAuth access token 1 hour, refresh token 30 days (rotated on each use), approval 90 days; at most 5 live approvals per app per member (the least recently used is replaced)

Security notes for admins

  • Who can connect. Only members of a firm whose owner turned AI agent access on. The agent

acts as that member, with that member's permissions at the moment of each request: removing a member, deactivating them, changing their role or deleting the firm revokes their keys and apps immediately.

  • Emails. Every new key and every newly approved app is emailed to the member and to every

Super Admin, with the app name, the redirect host, the scopes (with destructive called out) and the expiry. Never the key itself.

  • Connected apps & keys page (/settings/ai-agents/keys/). Everyone sees and can revoke

their own keys and apps. Super Admins and Admins also see Everyone's connections in the firm. An Admin can revoke an Admin's or a Staff member's connection, never the owner's (shown as "Owner only"); the owner can revoke anyone's. The member is not emailed when an admin revokes their connection.

  • Agent activity (/settings/ai-agents/activity/). Every tool call, newest first: who,

which key or app, which tool, the outcome and a redacted summary of the arguments (ids, numbers and choices only; free text is shown as [redacted:<length>]). Members see their own agents' calls; Super Admins and Admins see the whole firm. Filter by key or app, tool and outcome. Activity is kept for 400 days.

  • "By agent" badges. A problem resolved, reopened or dismissed by an agent shows "by agent"

on the check result page, and its history entry links to the exact call in Agent activity. Dismissals always carry the agent's reason (10 to 1000 characters).

  • The emergency stop. Settings → AI Agents → Turn off and revoke everything (owner only)

cuts every agent in the firm off on its next request.

  • What to watch for. An unfamiliar app name or redirect host in an email; a burst of

dismissals (you are emailed at 50 in a day); destructive access you did not expect. Revoke first, then ask.

Troubleshooting

Symptom Cause Fix
"AI agent access is off" on the keys page, or the consent page refuses The firm's owner has not turned it on Owner: Settings → AI Agents → Turn on AI agent access
Asked to sign in again before creating a key or approving Your last sign-in is older than 15 minutes Sign in again; Herv.ai returns you to the page
Every call answers 401 The key or app was revoked or expired, the member left the firm or changed role, or the firm turned agent access off Check Connected apps & keys; reconnect the app or create a new key
401 with "The access token has expired." Normal: the 1-hour access token ran out The client refreshes on its own; if it keeps happening, reconnect the app
The client keeps asking you to log in Its 30-day refresh token or 90-day approval ran out, the app was disconnected (also by a password change), or the client lost its stored token Reconnect: Claude Code /mcp → herv-ai → Authenticate; other clients: disconnect and connect again
401 "subscription inactive" The firm's trial or subscription has ended The owner renews the subscription
PAYMENT_REQUIRED on a write tool The firm is in read-only mode Read tools still work; renew to write
SCOPE_REQUIRED The key or app lacks write or destructive OAuth: approve the wider access when asked; key: create a new key
NOT_FOUND for an id you can see in the web app Wrong kind of id (a check id is not a client id), or the member cannot open that client Use the ids the tools return; check the member's client access
REQUIRES_CONFIGURATION from enable_check The check needs accounts chosen in the web app (Xero) Open the webUrl in the error
UNSUPPORTED_FOR_CHECK The check changes live data, isn't available on this platform, or its problems don't name one transaction Do it in the web app, or use another tool
UPSTREAM_UNAVAILABLE from get_problem_transaction QuickBooks Online or Xero is down, slow or disconnected Reconnect the client's books in Herv.ai, or retry later
RATE_LIMITED A limit in Limits Wait retryAfterSeconds
AUDIT_UNAVAILABLE or INTERNAL A server problem; nothing was returned or the change may not have been made Retry once; send support the ref
400 from /mcp The key was put in the URL, or a header was sent twice Send the key only in Authorization: Bearer
421 The client called a host other than app.herv.ai Use exactly https://app.herv.ai/mcp
Sign-in page says "start again" The browser lost the short-lived sign-in cookie, or more than 10 minutes passed Start the connection again from the app