MCP (AI agents)
The LedgerBee MCP server gives AI agents (Claude, ChatGPT, Cursor, and other MCP clients) a curated tool set rather than an auto-generated wrapper around every REST endpoint. Transport is Streamable-HTTP; authorization is OAuth 2.1 with a browser sign-in. Add the server URL to your client and approve the sign-in — no API keys required.
One server per company
A LedgerBee MCP connection is scoped to exactly one company: the OAuth grant behind it binds to a single company, and every tool call runs against that company's books. Two URL forms exist:
| URL | Use |
|---|---|
https://api.ledgerbee.com/api/v1/mcp | Single company — binds to the company you pick at sign-in |
https://api.ledgerbee.com/api/v1/mcp/c/{appCompanyId} | One connection per company — the path pins which company the grant binds to |
The /c/{appCompanyId} discriminator exists for users with access to more
than one company. MCP clients dedupe connections by URL and server name, so
without it only one LedgerBee connection could be registered. With it, each
company gets a distinct URL + server name pair (ledgerbee-{company}), and an
agent can hold one connection per company side by side.
Marketplace → Connected apps in the LedgerBee app renders the setup below pre-filled for the company you're signed in to — URL, server name, and per-client commands, including a one-click Add to Cursor button and buttons that copy the URL and open Claude's and ChatGPT's settings.
Connect a client
Use the plain server URL — the connection binds to the company you pick at sign-in:
Code
Claude (Web / Desktop) — both share one connector list:
- Open claude.ai/customize/connectors (Settings → Connectors).
- Click Add custom connector and paste the server URL.
- Complete the LedgerBee sign-in and approve the requested access.
ChatGPT — requires a Plus, Pro, Business, Enterprise, or Edu plan:
- Turn on Developer mode under Settings → Security and login.
- Open chatgpt.com/plugins, create an app, and paste the server URL. Choose OAuth as the authentication.
- Complete the LedgerBee sign-in and approve the requested access.
- Select the app in the message composer to use it in a conversation.
On a Business or Enterprise workspace, an admin must allow custom MCP apps in the workspace settings first. OpenAI moves these menus between releases; the current path is in OpenAI's Developer mode help article.
Claude Code — run this in your terminal, then run /mcp inside Claude
Code to complete the OAuth sign-in:
Code
Cursor — Add to Cursor,
or paste this into your mcp.json:
Code
VS Code — run this in your terminal (requires the VS Code CLI):
Code
Codex:
Code
Other clients — most accept the universal installer; the JSON snippet above also works in most clients' MCP config. The full client list is at modelcontextprotocol.io/clients.
Code
Register one connection per company with the company-pinned URL. The commands
below carry two placeholders — {appCompanyId} and the server name
ledgerbee-{company} — replace both, or copy the pre-filled commands from
Marketplace → Connected apps. To add the next company, switch company in
the app and reopen the card.
Code
Claude (Web / Desktop) — both share one connector list:
- Open claude.ai/customize/connectors (Settings → Connectors).
- Click Add custom connector and paste the company-pinned URL.
- Complete the LedgerBee sign-in and approve the requested access.
ChatGPT — requires a Plus, Pro, Business, Enterprise, or Edu plan:
- Turn on Developer mode under Settings → Security and login.
- Open chatgpt.com/plugins, create an app, and paste the company-pinned URL. Choose OAuth as the authentication.
- Complete the LedgerBee sign-in and approve the requested access.
- Select the app in the message composer to use it in a conversation.
On a Business or Enterprise workspace, an admin must allow custom MCP apps in the workspace settings first. OpenAI moves these menus between releases; the current path is in OpenAI's Developer mode help article.
Claude Code — run this in your terminal, then run /mcp inside Claude
Code to complete the OAuth sign-in:
Code
Cursor — the one-click Add to Cursor button lives in the in-app card
(the deep link needs a concrete URL). Or paste this into your mcp.json:
Code
VS Code — run this in your terminal (requires the VS Code CLI):
Code
Codex:
Code
Other clients — most accept the universal installer; the JSON snippet above also works in most clients' MCP config. The full client list is at modelcontextprotocol.io/clients.
Code
Anonymous developer server
/api/v1/mcp/developer is an anonymous server that exposes a single tool,
read_development_docs, over the LedgerBee developer docs. Use it to verify
your client setup before connecting the tenant server:
Code
The developer MCP endpoint in the API Reference renders the same install card with copy-paste setup for popular clients.
Tools
Every tool declares an inputSchema and an outputSchema, and every call
returns its result twice: as structuredContent, the typed object the
outputSchema describes, and as a text block carrying the same value as
JSON. Parse structuredContent — for a tool whose result is wrapped, below, the
two blocks carry different shapes. Read both schemas from tools/list, which is
the live contract. Which tools appear there depends on the toolsets the
connection has enabled; for a read outside them, reached through
call_mcp_tool, call list_mcp_tools with response_format: "detailed", whose
rows carry the same two schemas.
structuredContent is always an object. lookup_bookkeeping_rule returns a
list of hits and get_business_entity_details returns null for an entity it
cannot find, so those two values arrive wrapped as { "result": … }, which is
what their outputSchema declares. Their text block carries the value
unwrapped.
A connection reaches only what the user who authorized it can reach in
LedgerBee. Each scope stands for the permission the app requires for the same
data: subscriptions-read needs the subscriptions.read permission and the
Subscription licence. The consent screen offers only the scopes the user's
role covers, and every call checks the role as it is at that moment, so a scope
stops working when the role stops covering it and works again when it does. A
call the role does not cover is refused with that reason. Only an admin
changing the role clears it; request_permissions cannot.
Read tools — available with the mcp:read scope on a tenant server; some also require the listed domain read scope:
| Toolset | Resource | Tool | Operation | Additional scopes | Also requires |
|---|---|---|---|---|---|
| every connection | development_doc | read_development_docs | action | — | — |
| every connection | mcp_grant | request_permissions | action | — | user token |
| every connection | mcp_tool | list_mcp_tools | list | — | — |
| every connection | mcp_tool | call_mcp_tool | action | — | — |
context | accounting_period | get_accounting_period | get | journal-entries-read | — |
context | bookkeeping_rule | lookup_bookkeeping_rule | action | company-read | — |
context | dimension | list_dimensions | list | dimensions-read | — |
context | journal_category | list_journal_categories | list | journal-categories-read | — |
context | ledger_account | list_ledger_accounts | list | accounts-read | accounting.read permission |
context | ledger_account | get_ledger_account | get | accounts-read, journal-entries-read | — |
context | vat_code | list_vat_codes | list | vat-codes-read | — |
ledger | accounting_period | check_period_close_readiness | action | accounts-read, journal-entries-read | accounting.read permission |
ledger | journal_entry | list_journal_entries | list | — | user token, accounting.read permission |
ledger | journal_entry | get_journal_entry | get | journal-entries-read | — |
ledger | journal_entry | search_journal_entries | action | journal-entries-read | — |
ledger | tax_account_statement | get_tax_account_statement | get | journal-entries-read | — |
ledger | vat_readiness_finding | list_vat_readiness_findings | list | journal-entries-read, vat-codes-read | accounting.read permission |
ledger | vat_report | check_vat_readiness | action | journal-entries-read, vat-codes-read | accounting.read permission |
ledger | vat_report | get_vat_breakdown | action | journal-entries-read, vat-codes-read | — |
ledger_analysis | bank_account | find_duplicate_bank_postings | action | journal-entries-read | accounting.read permission |
ledger_analysis | journal_entry | find_counterparty_mispostings | action | customers-read, journal-entries-read, suppliers-read | — |
ledger_analysis | journal_entry | find_related_entries | action | journal-entries-read | — |
counterparties | counterparty | list_counterparties | list | customers-read or suppliers-read | records.read permission |
counterparties | counterparty | get_counterparty | get | customers-read or suppliers-read | — |
counterparties | counterparty | find_duplicate_counterparties | action | customers-read or suppliers-read | records.read permission |
counterparties | counterparty_group | list_counterparty_groups | list | customers-read or suppliers-read | — |
counterparties | cvr_registration | lookup_cvr | action | company-read | — |
documents | document | list_documents | list | documents-read | — |
documents | document | get_document | get | documents-read | — |
documents | document_file | get_document_file | get | documents-read | — |
bookkeeping | draft_journal_entry | get_draft_journal_entry | get | journal-entries-read | — |
bookkeeping | open_entry | list_open_entries | list | journal-entries-read | — |
bookkeeping | open_entry | get_open_entry | get | journal-entries-read | — |
staging | staged_write | list_staged_writes | list | — | user token |
staging | staged_write | get_staged_write | get | — | user token |
staging | staged_write | create_decision_link | action | — | user token |
api | public_api | api_details | action | — | — |
api | public_api | api_read | action | — | — |
api | public_api | api_search | action | — | — |
reports | cash_flow_forecast | get_cash_flow_forecast | get | — | Cashflow licence, user token, cashflow.read permission |
reports | financial_report | get_financial_report | get | accounts-read | — |
reports | invoice | list_invoices | list | customers-read | — |
reports | invoice | get_invoice_payment_status | action | journal-entries-read, customers-read or suppliers-read | — |
reports | invoice | get_invoice_reminders | action | customers-read | records.read permission |
reports | invoice | get_overdue_invoices | action | journal-entries-read, customers-read or suppliers-read | — |
my_work | notification | list_notifications | list | — | user token |
my_work | task | list_tasks | list | — | user token |
banking | bank_account | list_bank_accounts | list | — | accounting.read permission |
banking | bank_payment | list_bank_payments | list | — | BillPayment licence, payments.read permission |
banking | bank_reconciliation | list_bank_reconciliations | list | — | accounting.read permission |
banking | bank_transaction | list_bank_transactions | list | journal-entries-read | — |
banking | bank_transaction | get_bank_transaction | get | — | accounting.read permission |
banking | card_account_statement | get_card_account_statement | get | — | accounting.read permission |
bank_matching | bank_reconciliation | get_dismissed_suggestions | action | — | accounting.read permission |
bank_matching | bank_transaction | find_inbox_document_candidates | action | — | accounting.read permission |
bank_matching | bank_transaction | find_invoice_anchor_candidates | action | — | accounting.read permission |
bank_matching | bank_transaction | find_journal_entry_candidates | action | — | accounting.read permission |
bank_matching | business_entity | get_business_entity | get | — | accounting.read permission |
bank_matching | business_entity | resolve_business_entity | action | — | accounting.read permission |
subscriptions | subscription | list_subscriptions | list | subscriptions-read | Subscription licence, subscriptions.read permission |
subscriptions | subscription | get_subscription | get | subscriptions-read | Subscription licence |
subscriptions | subscription | get_saas_metrics | action | subscriptions-read | Subscription licence |
subscriptions | subscription | list_mrr_by_customer | action | subscriptions-read | Subscription licence |
products | product | list_products | list | products-read | records.read permission |
payroll | payroll_run | list_payroll_runs | list | — | PayRoll licence, user token, payroll.read permission |
ui | page | inspect_page | action | — | user token |
ui | route | list_routes | list | — | — |
ui | route | find_view | action | — | — |
dashboards | dashboard | list_dashboards | list | — | user token, dashboard.read permission |
dashboards | dashboard | get_dashboard | get | — | user token, dashboard.read permission |
budgets | budget | get_budget | get | budgets-read | Budgets licence |
approvals | workflow_approval | list_workflow_approvals | list | — | user token |
approvals | workflow_approval | get_workflow_approval | get | — | user token |
Write tools — available with the mcp:write scope, or the listed domain write scope. Writes always require an OAuth grant:
| Toolset | Resource | Tool | Operation | Additional scopes | Also requires |
|---|---|---|---|---|---|
| every connection | mcp_tool | call_mcp_tool_batch | action | mcp:read or mcp:write | — |
| every connection | tool_gap | report_tool_gap | action | — | user token |
counterparties | counterparty | create_counterparty | create | customers-write or suppliers-write | user token |
counterparties | counterparty | update_counterparty | update | customers-write or suppliers-write | user token |
counterparties | counterparty | change_supplier_bank_account | action | suppliers-write | user token, records.write permission |
bookkeeping | credit_note | link_credit_note | action | journal-entries-write | user token |
bookkeeping | document | bin_inbox_document | action | journal-entries-write | user token, inbox.write permission |
bookkeeping | document | book_inbox_document | action | journal-entries-write | user token, inbox.write permission, with settlement 'pay': suppliers-write scope, payments.write permission, BillPayment licence |
bookkeeping | document | upload_document | action | journal-entries-write | — |
bookkeeping | draft_journal_entry | create_draft_journal_entry | create | journal-entries-write | user token |
bookkeeping | draft_journal_entry | update_draft_journal_entry | update | journal-entries-write | user token |
bookkeeping | draft_journal_entry | delete_draft_journal_entry | delete | journal-entries-write | user token |
bookkeeping | draft_journal_entry | book_journal_entry | action | journal-entries-write | user token |
bookkeeping | journal_entry | reverse_journal_entry | action | journal-entries-write | user token |
bookkeeping | ledger_account | create_ledger_account | create | accounts-write | user token |
bookkeeping | ledger_account | update_ledger_account | update | accounts-write | user token |
bookkeeping | open_entry | settle_open_entries | action | journal-entries-write | user token |
bookkeeping | settlement | unsettle_entries | action | journal-entries-write | user token |
bookkeeping | vat_period | settle_vat_period | action | journal-entries-write | user token |
staging | staged_write | approve_staged_write | action | — | user token |
staging | staged_write | cancel_staged_write | action | — | user token |
staging | staged_write | present_for_approval | action | — | user token |
staging | staged_write | revise_staged_write | action | — | user token |
staging | staged_write | start_chain | action | — | user token |
staging | staged_write | submit_chain | action | — | user token |
api | public_api | api_write | action | — | user token |
my_work | notification | dismiss_notifications | action | — | user token |
my_work | notification | mark_notifications_read | action | — | user token |
banking | bank_payment | pay_supplier_bill | action | suppliers-write | BillPayment licence, user token, payments.write permission |
bank_matching | bank_transaction | dismiss_bank_transaction | action | bank-reconciliations-write | user token |
bank_matching | bank_transaction | link_bank_transaction | action | bank-reconciliations-write | user token |
bank_matching | bank_transaction | unlink_bank_transaction | action | bank-reconciliations-write | user token |
ui | page | fire_action | action | — | user token |
ui | page | highlight_rows | action | — | user token |
ui | page | spotlight | action | — | user token |
ui | route | navigate | action | — | user token |
dashboards | dashboard | create_dashboard | create | — | user token, dashboard.write permission |
dashboards | dashboard | edit_dashboard_global_config | action | — | user token, dashboard.write permission |
dashboards | dashboard | edit_dashboard_layout | action | — | user token, dashboard.write permission |
dashboards | dashboard | edit_dashboard_metadata | action | — | user token, dashboard.write permission |
dashboards | dashboard | save_dashboard | action | — | user token, dashboard.write permission |
dashboards | dashboard_widget | create_dashboard_widget | create | — | user token, dashboard.write permission |
dashboards | dashboard_widget | update_dashboard_widget | update | — | user token, dashboard.write permission |
dashboards | dashboard_widget | delete_dashboard_widget | delete | — | user token, dashboard.write permission |
budgets | budget | set_budget_figures | action | budgets-write | Budgets licence, user token |
approvals | workflow_approval | decide_workflow_approvals | action | — | user token, core.write permission |
approvals | workflow_approval | reassign_workflow_approval | action | — | user token, core.write permission |
Every tool names the toolset it belongs to, the resource it acts on and its operation. A toolset is the job a tool serves; the tools marked every connection belong to none and are always advertised. list, get, create, update and delete tools are named <operation>_<resource>; an action is a lifecycle step or lookup with no CRUD shape, such as booking a draft. A renamed tool stops answering to its old name in the same release.
This table is the full catalog. Call tools/list on the server for the tools your connection advertises, with each tool’s parameters and description.
A connection advertises the tools of its enabled toolsets plus the tools that
belong to none. The toolsets are picked on the consent screen, which offers
the ones the requested permissions can serve and ticks the recommended set —
or, when a client re-authorizes a connection you already have, that
connection's own set. An admin changes it afterwards under Settings →
Integrations → OAuth clients → Connected apps, where each connection has a
toolset checkbox per toolset; the change reaches the client the next time it
reconnects. A toolset decides what the connection
is shown, not what it may do: a read outside the enabled set stays reachable
through call_mcp_tool, and what bounds a write is its scopes and the
company's write policy. The page-control and dashboard-editing tools are off
unless a connection enables ui or dashboards; their reads stay reachable
through call_mcp_tool, and their writes need the toolset ticked on a hosted
client. An agent asks for a toolset with request_permissions, the same call
it uses for a missing scope.
MCP scopes are OAuth-only and cannot be assigned to API keys. Write tools always require an OAuth grant with the matching scope.
Discovery
Two tools search the catalog and reach a tool by name, so an agent never has to guess whether a capability exists. Both are advertised to every connection.
list_mcp_toolssearches the full catalog by keyword, toolset or kind. Each row carries the tool's name, toolset, resource, operation and description, andinvokeVia:directwhen the tool is in this connection'stools/list,call_mcp_toolwhen it is a read outside it, orenable_toolsetwhen it is a write outside it, in which caseenableToolsetnames the toolset to ask for.blockedReasoncarries the messagetools/listputs on a tool the caller cannot invoke yet, ornullwhen no gate is visible at list time. Called with no arguments it returns the toolset index; a query that matches nothing returns the index and the closest names rather than an empty list.response_format: "detailed"adds each tool's input schema and annotations.call_mcp_toolruns one read tool by name, with the target's own arguments underarguments, and returns that tool's own result. The target is checked exactly as a direct call: its scopes, licence, rate limit and AI budget apply, and a refusal carries the same message. A write tool is refused with its toolset named. Neither discovery tool can be run through it.
A wrapped call is recorded in the MCP audit log as call_mcp_tool:<tool>,
with the target's route in targetOperation when the target is a facade
tool. The usage statistics report the target under its own name and count the
wrapped calls separately.
Resources
The server publishes your company's reference data as MCP resources, so a
client can attach it to a conversation instead of spending a tool call on it
every session. resources/list returns the ones this connection can read:
| URI | What it holds |
|---|---|
ledgerbee://company/{companyId}/chart-of-accounts | Every ledger account you post to — number, name, type, parent |
ledgerbee://company/{companyId}/vat-codes | Your VAT codes with the ids a draft entry carries |
ledgerbee://company/{companyId}/journal-categories | Your own classification of an entry, with its ids |
Each returns application/json, the same payload the matching tool returns —
reading both gains nothing.
A read runs as the tool call it is: the same scopes, the same licence check,
the same rate limit, and a row in the MCP audit log under resource:<name>.
A resource therefore reaches nothing a tool would not, and a connection
missing the scope or the toolset behind one is not shown that resource.
The company is in the URI because one person can connect several companies, and a client that keys a cache or a saved attachment by URI would otherwise hold two companies' charts under one name. A URI naming a company other than the one that consented to the connection is not found; it never resolves to yours.
Results carry a one-minute cache hint on protocol revision 2026-07-28, so an
account or a VAT code you add is visible to the agent the next time it reads.
There are no resource templates. Every resource is fixed, and the only variable a template would have taken is the company, which your bearer token already decides.
Prompts
prompts/list returns three workflows a client turns into something you pick
— a slash command in Claude Code and VS Code, an entry in the conversation in
Claude.ai:
| Prompt | Arguments | What it asks for |
|---|---|---|
month-end-close | period | A verdict on whether a month can be closed, and what is in the way. Reads only |
book-document | document | A draft journal entry for one document in your inbox, grounded in your own chart |
reconcile-bank-period | from, to, bankAccount | A bank account's transactions over a range matched against what is posted, and a list of what is left |
A prompt is text. It reads none of your data and calls no tool — it states the job and the order to do it in, and the agent then works through your tools under every gate that already applies, including the write policy. A prompt naming tools this connection is not served is left out of the list.
A host that collects no arguments still gets a usable brief: the step that needed the value says so, and the agent asks you.
Direct mode
The curated tools above cover the common accounting tasks. For everything
else, the four tools on the public_api resource call the public REST API
directly, as the connection's own access token. Any route the grant can
already call over HTTP is reachable through them, and no other route is: the
facade grants nothing on its own.
- Search finds routes by keyword. Each result carries the method, the path
template, the required scopes, whether this connection can call it
(
callable), andcuratedToolwhen a dedicated tool already covers the route. - Details describes one route: path and query parameters, the request body schema, the success response shape, required scopes, and deprecation.
- Read performs a
GETand returns the status and JSON body. A body longer thanmaxBytes(default 64 KB) is cut, and the result names the route's paging parameters. - Write performs a
POST,PATCH,PUTorDELETE. It takes a one-linesummaryof the change, shown on the approval card and kept in the audit log.
The agent searches first and uses the curated tool whenever one is named: the curated tools validate input, stage the change for review in LedgerBee, and can be reversed. The facade does none of that.
Enabling writes
Direct writes are off by default. Turn them on under Home → Hivey → Permissions, where the Direct API writes row takes one of three values.
| Value | Effect on the write tool |
|---|---|
| Deny (default) | The call is refused. The tool is advertised with an "unavailable" marker that names this setting. |
| Approval | The request is stored and appears in the approval queue. It runs when you approve it, as the same grant that staged it. The tool returns executed: false and a chainId. |
| Auto | The request runs immediately and the tool returns executed: true with the route's status and body. No approval card is shown. |
The three read-side tools have no setting: a read the grant can perform over HTTP is not more dangerous when performed through MCP.
What Auto holds back
Auto commits in the call: the server asks the user nothing first. A curated
tool's write that looks risky is staged instead, exactly as under Approval, and
waits in the approval queue; the result carries autoCommitHolds naming each
reason, and re-reading the proposal adds the same reasons as sentences in
autoCommitHeldBecause. A write is held when:
| Rule | Held when |
|---|---|
| Amount ceiling | An amount is at or above the company's Amount ceiling, set under Automatic booking in the Hivey settings. An amount in another currency than the company's is held whenever a ceiling is set, since it cannot be compared with it. No ceiling, no rule. |
| First posting | A line posts to an account the company has never posted to. |
| First booking against a counterparty | The entry names a supplier or customer the company has never booked an entry against. |
| Delete | The write deletes a draft entry or bins an inbox document. |
| Large batch | The call is one item of a call_mcp_tool_batch with more than 10 items. |
A write with a blocker, and one that redirects money (a payment from an inbox booking, a supplier's bank account), waits for a person as it always has. The Write tool's direct requests are not covered by these rules: under Auto they run as sent.
Tools that drive your browser tab
Some tools act on a live LedgerBee tab and need one paired to the session; each
says so in its description. When none is paired and the client declares URL
elicitation, the call returns an input_required result asking the user to open
a page rather than failing — the approval queue, the connection's permission
editor, or the app itself, whichever the call needs them to reach. Retry once
they have. A second retry that is still blocked returns the refusal instead of
the link again.
Scopes
Search, details and read require mcp:read; write requires mcp:write and a
user-consented grant. Every route also enforces its own scopes, exactly as it
does for an HTTP client: a read of /v1/customers needs customers-read, and
a write of POST /v1/customers needs customers-write. A call the connection
cannot make with its scopes, as its user's role narrows them, is refused before
any request is made, with the
missing scope named. Search reports the same check as callable, so the agent
can see which routes it can reach.
Idempotency
Every write call carries an idempotency key. When the agent passes
idempotencyKey, that key is sent; otherwise the server derives one from the
grant, the method, the route template and the request body. The result echoes
the key as idempotencyKey and sets replayed: true when the API served the
stored outcome of an earlier identical request instead of writing again. A
retry after a timeout therefore creates no duplicate.
Excluded routes
Routes that manage authentication, single sign-on, API keys and the MCP
endpoint itself (/v1/auth, /v1/oauth, /v1/api-key, /v1/scim,
/v1/portal-sso, /v1/mcp) are never reachable through the facade, whatever
scopes the grant holds, and neither is any write to /v1/webhook-endpoints: a
webhook endpoint created under prompt injection would be an exfiltration
channel. A request to one of them is refused with a message saying so.
Errors
A route that answers 400 or above does not fail the tool call. The result
carries error: true, the status, and the route's own error body, so the agent
can read a validation message and correct the request. The facade's own
refusals raise a tool error: an unknown path, an excluded route, a missing
scope, or direct writes set to Deny.
Audit
Every facade call is recorded in the MCP audit log with the tool name, the
grant, the outcome, and targetOperation, the route it hit, as
"POST /v1/customers". The loopback request carries the same request id as
the MCP call, so the API request log and the MCP audit row share one id.
Endpoints
| Endpoint | Auth | Purpose |
|---|---|---|
POST /api/v1/mcp | OAuth 2.1 bearer, Dynamic Client Registration grant (mcp:read scope) | Tenant-scoped server |
POST /api/v1/mcp/c/{appCompanyId} | same | Tenant-scoped server, company pinned in the URL |
POST /api/v1/mcp/developer | Anonymous (any bearer) | Developer/docs server |
Authorization (OAuth 2.1)
The tenant-scoped servers follow the MCP authorization spec. A compliant
client handles the whole flow on its own: it discovers the authorization
server via .well-known, registers itself, and opens the browser sign-in.
The metadata endpoints:
/.well-known/oauth-protected-resource(and a per-company variant)/.well-known/oauth-authorization-server/.well-known/openid-configuration/.well-known/jwks.json
Clients that self-register use Dynamic Client Registration. An MCP client is a DCR-registered connected app — the same record backs both terms:
Code
A partner app that LedgerBee registers for you (rather than self-registering via DCR) runs the same authorization flow. See Connected Apps.
See Authentication for the full OAuth flow.