Tabba.
IntegrationerSådan virker TabbaFeatures

© 2026 Tabba. Alle rettigheder forbeholdes.

Udviklere
Udviklere
Udviklere
OverviewConnect claude.aiConnect Claude CodeConnect ChatGPTConnect CursorPlan and commitAuth and scopes

Andre spor

REST APIAPI reference

Plan and commit

Why no Tabba tool writes on its own, what a plan and its token are, and what the server holds a commit to.

Tabba works on one rule: the agent prepares, a person approves. On the MCP server that rule is two tool calls. An agent that hears "five hours" and takes seven off a line has changed what a customer is invoiced, so the server has no tool that both decides and writes.

Two steps, always

  1. Plan. A plan_* tool resolves what was asked, holds it to the rules Tabba holds any write to, and answers a summary in plain language and an opaque proposal_token. Nothing is written.
  2. Commit. The separate commit tool takes that token and nothing else, and applies the plan the server kept.

What is written is the plan you read. The client never sends the change a second time, so it cannot commit something other than what it showed you.

Which plan tools you are offered follows your role. A standard user is offered plan_register_work and commit. An auditor is offered no plan tool and no commit. Every other plan tool is an admin's, and the organisation admin, an admin and a bookkeeper are admins here: plan_upsert_customer, plan_upsert_product, plan_create_invoice, plan_delete_invoice, plan_create_series, plan_cancel_series, plan_setup_from_contract, and with mcp:issue also plan_issue_invoice, plan_issue_occurrence and plan_create_credit_note.

What the server enforces, and what the client is asked to do

These are different things, and only the first is a guarantee.

The server enforces:

  • No tool but commit writes. Every plan tool is marked read-only in its annotations, and commit is marked destructive.
  • commit applies a plan that exists on the server, belongs to you and to the company of your token, has not run out, and still describes the records as they are.
  • A plan is applied once.
  • The token's scope and your role are asked again at the commit.

The client is asked:

  • To show you the plan and wait for your confirmation before it calls commit. The server's instructions and the description of every plan tool say so, and for a plan that books they say to name the very document.
  • To put a plan tool's questions to you and never answer them itself.

The server cannot see whether a client showed you anything. If your client can require approval per tool, require it for commit: that is the one call where a change becomes real, and reading and planning stay free.

What a plan looks like

A plan is answered as text the agent can show you, with the same content in structured form beside it:

**Register work** for Jane Doe on "Development (Bredgade ApS)".

| figure | now | becomes | change |
|---|---|---|---|
| hours | 12 h | 17 h | +5 h |

An hourly line: the hours move its revenue.

Nothing is written yet. Show the user this plan, and once they confirm call
commit with proposal_token="mcpp_6f1c2b9e-…" (runs out
2026-10-13T09:27:33+00:00; takes mcp:write).

The last sentence is one line on the wire. A plan that reduces a figure says so under Warnings. If the summary is right, the agent calls commit with the token. If it is not, you say so and nothing was written.

The token

  • Fifteen minutes. After that the commit is answered PROPOSAL_EXPIRED and the plan tool is called again. A plan describes the records as they stood when it was made.
  • Applied once. Committing the same token again writes nothing and is answered the first commit's result. A client that retries a commit that timed out cannot apply it twice. The result is kept for a day after the plan's expiry. After that the token is PROPOSAL_NOT_FOUND.
  • Its planner's. A token is good for the person and the company it was planned for. Another member of the company, an admin included, finds no plan under it and is answered PROPOSAL_NOT_FOUND.
  • Opaque. It is mcpp_ and an id. It carries nothing of the plan.

PROPOSAL_STALE means a record changed

A plan keeps the state of every record it reads or writes, and the commit compares them again before its first write. If a colleague changed the invoice, or registered hours on the line, after the plan was made, the commit is refused: what you confirmed is no longer what would be written. Call the same plan tool again, read the new plan, and commit that token. Nothing is absorbed into the old one.

A plan that books depends on more than the records it names. A plan to issue an invoice is stale once work is registered on its lines. A plan to issue an occurrence is stale once what the occurrence bills has changed. A plan to end a recurring series is stale once the end would credit, drop or issue something else than the plan listed.

When a commit fails part-way

A plan can hold several steps: the contract plan creates a customer, its products and a recurring series in one commit. Each step is kept as soon as it is applied.

  • If a step fails, commit the same token again. The applied steps are taken as they came out and the rest are applied. No record is created twice.
  • A resume compares the records again, for everything the first attempt did not itself write. A change in between is PROPOSAL_STALE, and the answer says that part of the plan was applied: read what is there, then plan what is left.
  • A resume is bounded by the plan's fifteen minutes.
  • Two commits of one token never both apply it. The second is answered COMMIT_IN_PROGRESS: wait a few seconds and commit the same token again. Do not plan again.

When a plan tool asks back

A plan tool can answer needs_clarification with questions in place of a plan. The agent puts them to you and calls the same tool again with your answers. Nothing is kept on the server for a question.

More is needed before this can be planned:
1. [hours_mode] Jane Doe already has 12 h on "Development (Bredgade ApS)". You gave 5 h. Does that REPLACE the total, or is it ADDED to it?
   - replace: the total becomes 5 h (a change of -7)  [answer: replace]
   - add: the total becomes 17 h (a change of +5)  [answer: add]

Ask the user, then call this tool again with the same arguments and hours_mode filled in (top-level, or under "answers"). Send an [answer: …] value exactly as written.

Each option carries the exact value to send back, so an agent that reads only text can still answer.

Hours are a total

plan_register_work registers a person's total on a line, not an increment. It shows what is registered already, and it takes either form:

ArgumentMeaning
hoursThe new total of hours on the line.
hours_deltaA signed change: 5 adds five hours, -2 takes two off.
hours_modeThe answer to the question above: replace or add.

Passing hours: 5 on a line where 12 are registered is a request to take seven off, so the tool stops and asks. Where nothing is registered yet the two readings are the same number, and it goes ahead. Pass hours_delta when you mean to add.

Completion works the same way: completion_rate (0 to 1) or completion_percent (0 to 100) for the new value, completion_rate_delta or completion_percent_delta for a change, and completion_mode for the same question. Hours and completion are both kept on every line. The line's recognition method, hourly or value, decides which of them moves revenue.

The line is named by a line_id from get_my_work. A recurring series shows the lines of its current occurrence there before the occurrence is an invoice. Registering work on such a line makes the occurrence an invoice at the commit, and the plan says so.

A standard user registers their own work. Naming a colleague in user is refused with REGISTER_FOR_OTHERS_FORBIDDEN. An admin may name a colleague who holds a place on the line.

Other scales

  • Shares. The plan tools take distribution_percentage, a person's share of a value line, as a percentage from 0 to 100, and the shares of a line add up to 100. The read tools state the same share as a fraction: 0.6 is 60 %. An hourly line takes no share: each person on it bills at a rate.
  • Money is in major units, and an amount a plan tool takes is before VAT. get_config states the company's base currency.
  • An occurrence is named by period, the first day of its billing period, as YYYY-MM-DD.

What takes mcp:issue

A token with mcp:write drafts and registers: nothing it commits is booked in the accounting system, and nothing is sent to a customer. That is decided by what a plan leads to, not by which tool made it. These plans take mcp:issue, and all of them are an admin's:

PlanWhy
plan_issue_invoice, plan_issue_occurrence, plan_create_credit_noteThey book in the accounting system and send to the customer.
plan_create_invoice with auto_send: trueThe daily run issues the invoice on its issue date.
plan_create_series or plan_setup_from_contract with activate: trueThe series runs from the commit, and the daily run issues its occurrences.
plan_cancel_series, where the end credits what was sent past it or issues what is dueThe end books a credit note or sends an invoice.

By default a created invoice is a draft that waits until it is issued, and a created series is a draft that invoices nothing until an admin makes it active in Tabba.

A plan that takes mcp:issue is refused at plan time for a token that holds only mcp:write, with SCOPE_INSUFFICIENT, before you are asked to confirm anything. Its commit is refused for such a token too.

The contract plan and its form

plan_setup_from_contract, an admin's tool, plans the whole billing setup of a contract at once: a new customer where they are not in Tabba yet, the new products it needs, and either a one-off draft invoice (billing: "one_off") or a recurring series. You confirm once, and one commit writes all of it.

What the contract leaves open is asked: a line's product, how a new product is filed and counted, and, as one question per line, the line's amount and who works on it at what split or rate. The agent is told not to divide a price or suggest amounts itself.

In a client that renders MCP Apps, those questions are shown as a form under the tool's result. The form posts your answers as your own message in the conversation. It plans nothing and commits nothing: the agent calls the plan tool again with the answers, and you still confirm the plan before commit. Where a client shows no form, the same questions are asked in the conversation. Whether the form shows is the client's to decide.

Every error says what to do next

A refusal is a tool result the agent reads, not a transport fault. It carries a code, a message and a next_step:

Error [PROPOSAL_STALE]: A record the plan depends on changed after it was made. Nothing was written.
Next: Call the plan_* tool again: it plans from what is there now. Show the user the new plan before committing it.

The codes are the ones the REST API uses, listed under Errors, plus a few that only a surface with a plan and an acting person can answer. Where the accounting system refused, its reason and its own words are in the message.

CodeWhat happenedWhat to do
PROPOSAL_NOT_FOUNDThe token names no plan of yours on this company: never issued, another person's, or removed after its time.Plan again and commit the new token.
PROPOSAL_EXPIREDThe plan ran out before it was committed. Nothing was written.Plan again, read the new plan, commit its token.
PROPOSAL_STALEA record the plan depends on changed after it was made.Plan again: the tool plans from what is there now.
COMMIT_IN_PROGRESSAnother commit of the same token is applying the plan.Wait a few seconds and commit the same token again.
REGISTER_FOR_OTHERS_FORBIDDENA standard user named a colleague in a work registration.Register your own work, or ask an admin.
AMBIGUOUS_REFERENCEA name matched several records.Pick from the candidates the answer lists.
SCOPE_INSUFFICIENTThe plan books or sends, and the connection holds no mcp:issue.Connect again with a client that asks for issuing, or issue it in Tabba.
ADMIN_REQUIREDThe plan is an admin's to commit, and you are no longer one.Ask an admin.
READ_ONLY_ROLEYou are an auditor of the company now, and an auditor commits nothing.Ask an admin or a bookkeeper to make the change.

What cannot be undone

Some commits cannot be taken back from Tabba:

  • Issuing books the invoice in the accounting system and sends it to the customer. An issued invoice is not changed or deleted. It is corrected with a credit note.
  • A credit note is itself booked and sent, and is not taken back.
  • An invoice set to auto_send, an activated series, and an end of a series that credits lead to the same bookings without a further confirmation.

All of these take mcp:issue, so a connection without it can draft everything and book nothing. See Auth and scopes.

A record of every commit

A commit that applies a plan is written to the activity log once, with the person it was made as, the operation and the client. A second commit of the same token writes no entry.

Connect Cursor

Add the Tabba MCP server to Cursor, for one project or for your whole machine.

Auth and scopes

OAuth is the only MCP credential: the flow, the three scopes, the five roles, whose work an answer is about, and how access ends.

På denne side

Two steps, alwaysWhat the server enforces, and what the client is asked to doWhat a plan looks likeThe tokenWhen a commit fails part-wayWhen a plan tool asks backHours are a totalOther scalesWhat takes mcp:issueThe contract plan and its formEvery error says what to do nextWhat cannot be undoneA record of every commit

Denne side

Vis som MarkdownÅbn i Claude