Skip to main content

Authenticating

Every request to https://mcp.numeralhq.com/mcp carries your Numeral secret API key as a bearer token:
There is no OAuth flow and no separate MCP credential — the MCP server uses the same keys, lookup, and status checks as the Platform REST API. Create and manage keys in your developer dashboard under the API Keys tab.
Only secret keys (sk_) work. Publishable keys are rejected. A secret key grants full read access to your tax data, so keep it server-side — never ship it to a browser or commit it to a repository.
The server is stateless: each POST is authenticated independently and handled by a fresh server instance. Nothing is cached between requests, so revoking a key takes effect immediately.

Test mode and live mode

The mode is derived from the key itself — you never pass a mode parameter: Merchant, transaction, and product data are fully segregated between modes. A test key cannot see production records and vice versa.
Explore with an sk_test_ key first. It is the safe way to see tool shapes and let an agent experiment. Switch to sk_prod_ when you want answers about your real tax position.
One exception worth knowing: exemption certificate tools are live mode only. Calling them with a test key returns live_mode_required.

Tenant isolation

Your key resolves to exactly one Numeral account, and every query the server runs is scoped to that account server-side. No tool accepts an account or client identifier, so there is no parameter an agent could set — accidentally or otherwise — that would reach another tenant’s data. The same applies to merchant lookups. merchant_id accepts either a Numeral ID (mer_...) or your own reference_merchant_id, but resolution is always constrained to merchants owned by your account in your current mode. An ID belonging to another account simply returns merchant_not_found.

Rate limits

Requests are rate limited per account over a sliding window. The limit is sized for agent traffic — a single agent turn typically makes several requests (initialize, tools/list, then one or more tools/call) and that pattern fits comfortably within it. Two details worth knowing:
  • MCP usage is metered separately from the REST API. Heavy REST traffic does not consume your MCP quota, or vice versa.
  • The limit is keyed to your account, not to individual keys. Minting additional API keys does not grant additional quota.
Exceeding the limit returns HTTP 429 with JSON-RPC error code -32005. Back off and retry; if you are hitting it consistently, look for a tool that answers the question in one call — get_sales_summary instead of paging through list_transactions, for example — or contact us about a higher limit.

Two kinds of errors

The MCP server distinguishes between protocol errors (something is wrong with the request or the connection) and tool errors (the request was valid but the operation could not be completed). This matters when writing an agent: protocol errors need your code to react, while tool errors are meant for the model to read and correct.

Protocol errors

Returned as JSON-RPC errors with a non-2xx HTTP status: The message field distinguishes the 401 cases: Bearer token not provided., Invalid API key format., API key not found., API key is not active., or Public API keys are not supported. Use a secret key.
GET and DELETE on /mcp return 405 by design. The server runs in stateless mode, so there are no SSE streams to resume and no sessions to terminate. For health checks use GET /ping, which returns {"status":"ok"}.

Tool errors

A tool that cannot complete returns a normal MCP tool result flagged with isError, whose content is:
error_code values are stable and machine-readable, and error_message is written to be actionable — an agent can usually read it and fix its own call. Unexpected exceptions are caught and returned this way too (as internal_error) rather than breaking the JSON-RPC connection, so one bad call never takes down an agent’s session.

Tool error codes

Six of these — product_not_found, invalid_product_category, filing_not_approvable, filing_not_rejectable, buyer_identity_conflict, and processing_start_failed — only occur on write tools, which are not enabled by default.

Handling errors in an agent loop

Feed tool errors back to the model as tool results rather than throwing. The codes are designed for self-correction — a model that receives invalid_product_category will typically call list_tax_categories and retry with a valid value, and one that receives live_mode_required will explain the mode requirement instead of retrying blindly. Reserve exceptions for protocol errors. A 401 means your key is wrong and no retry will help; a 429 means back off and retry with a delay. The demo application implements this split in app/api/chat/route.ts.