MCP server
Stackin exposes the API as MCP tools: a connected agent issues, consults, cancels and explains rejections through conversation, with no request to write. Twelve tools, eight prompts, and the scope checked on every call.
A conversation, not a request
The agent calls the tool, builds the payload and reads the result. You describe what you want issued.
Scope checked on every call
Each tool declares the scope it needs, and the token is verified at call time — not when the plugin was registered.
Twelve tools, eight prompts
Issue, look up, cancel, correct, manifest and explain a rejection — the same surface as the API.
Start from ChatGPT
Registering the plugin takes a URL and a login. The rest is conversation.
https://mcp.stackin.io/mcpCopyThe same address for both authentication modes.
ChatGPT
In ChatGPT, Stackin is an MCP plugin: you register https://mcp.stackin.io/mcp, sign in through OAuth, and from then on you issue, look up, explain a rejection and cancel by talking. The recording below walks the whole path, from registering the plugin to cancelling the invoice, in the sandbox.
Screen recording, no audio, in the sandbox environment — invoices issued there carry no fiscal value.
- Details
On a Business, Enterprise or Edu workspace an admin allows it first, under Workspace settings → Permissions & Roles → Connected data, enabling the creation of custom MCP connectors. The toggle is per user: every admin turns on their own. With the mode on, connector setup shows up under Settings → Connectors → Create.
Developer mode and MCP apps in ChatGPT — OpenAI Help Center - Details
The advanced OAuth settings cover Dynamic Client Registration (DCR), the Client Identifier Metadata Document (CIMD), ChatGPT's client metadata URL, the OAuth callback URL and the default scopes. Before saving, ChatGPT warns that a custom MCP server should only be added if you trust whoever runs it.
- Details
The consent screen lists the application's details and the scopes it asks for, before any call happens. Once authorized, the plugin page shows the connection state, the granted permissions, and when ChatGPT must ask before running an action.
Claude Code
The plugin brings the seven skills and points at the endpoint. Authentication is OAuth 2.1 with PKCE and no key at all: the client registers itself and you sign in through the browser.
Screen recording, no audio, in the sandbox environment — invoices issued there carry no fiscal value.
- 01Install the pluginThe repository is its own marketplace:
claude plugin marketplace add stackin-io/stackin-mcp-plugin, thenclaude plugin install stackin@stackin. - 02Sign inRun
/mcpin the session. The first call answers 401, the client registers itself and opens the consent screen in your browser. Authorize, and the token is stored. - 03Use itAsk in plain language — “issue an NFS-e for this consulting work”, “why was my last invoice rejected?” — or call a skill by name.
The skills that come with it
A skill exists where the order of operations, or a fiscal rule, carries something a tool description cannot carry on its own.
/stackin:issue-invoiceWhich document the situation needs, and validating before issuing./stackin:consult-invoiceFinding a document, and which identifier each operation takes./stackin:cancel-invoiceConfirming before an irreversible act, and when a correction letter is the right tool./stackin:diagnose-invoiceReading the authority's own rejection code, and choosing between reissue and correction./stackin:correct-invoiceFiling a CC-e against an authorized NF-e, and what it may not change./stackin:invalidate-numberingDeclaring a numbering range reserved and never used./stackin:received-invoicesReading what others issued against the company, and answering it.Connect
The server speaks streamable-http at one fixed address. An MCP client signs in over OAuth 2.1 with PKCE — no key at all, which is how the plugin connects in ChatGPT and Claude Code — or carries a company API key, useful where no browser is available.
Signing in through the browser
In Claude Code, install the plugin and run /mcp in the session: the first call answers 401 with WWW-Authenticate, the client follows it to the protected-resource document, registers itself and opens the consent screen. There is no client ID to paste, and the callback is whatever localhost port the client binds at that moment.
With an API key
The key travels in the Authorization header, as it does on the API. Over HTTP the credential always comes from the request — the server serves many companies and never falls back to a key from its environment.
claude plugin marketplace add stackin-io/stackin-mcp-plugin
claude plugin install stackin@stackin{
"mcpServers": {
"stackin": {
"type": "http",
"url": "https://mcp.stackin.io/mcp"
}
}
}{
"mcpServers": {
"stackin": {
"type": "http",
"url": "https://mcp.stackin.io/mcp",
"headers": {
"Authorization": "Bearer ${STACKIN_API_KEY}"
}
}
}
}Scopes
An OAuth token carries the scopes its owner approved, and the API compares the scope against the operation on every call. The refusal happens in the API, not in the agent.
- invoice:read
- Consult, list, download the PDF and read the submission history.
- invoice:issue
- Issue and reissue a document.
- invoice:cancel
- Cancel a document and invalidate a numbering range.
- invoice:write
- Correction letters and answering a received invoice.
curl https://mcp.stackin.io/.well-known/oauth-protected-resource
{
"resource": "https://mcp.stackin.io",
"authorization_servers": ["https://api.stackin.io"],
"scopes_supported": [
"invoice:read",
"invoice:issue",
"invoice:write",
"invoice:cancel"
]
}Tools
Twelve tools. Seven change a document and declare destructiveHint; the other five read. validate_invoice_payload never calls the API, so it requires no scope.
| Tool | Scope | Required | Effect |
|---|---|---|---|
issue_invoice | invoice:issue | document_typeclient_nametax_iditems | Changes a document |
reissue_invoice | invoice:issue | invoice_id | Changes a document |
cancel_invoice | invoice:cancel | access_keydocument_typereason | Changes a document |
correct_invoice | invoice:write | access_keycorrection | Changes a document |
invalidate_numbering | invoice:cancel | seriesnumber_startnumber_endreason | Changes a document |
manifest_received_invoice | invoice:write | access_keymanifestation | Changes a document |
consult_invoice | invoice:read | access_keydocument_type | Read only |
list_invoices | invoice:read | — | Read only |
get_invoice_pdf | invoice:read | access_keydocument_type | Read only |
get_invoice_submissions | invoice:read | invoice_id | Read only |
list_received_invoices | invoice:read | — | Read only |
validate_invoice_payload | noauth | document_typeitems | Read only |
Prompts
Eight prompts steer the model through the common tasks — what to ask before issuing, what never to invent, and when cancelling is not the answer.
issue_nfseIssue an invoice for services rendered.issue_nfeIssue an invoice for goods sold.recent_invoicesShow the company's latest documents.diagnose_rejectionExplain why a document was rejected and what to fix.inspect_invoiceShow a document's current state.correct_nfeFile a correction letter for wording, not for values.manifest_receivedDeclare this company's position on a supplier's NF-e.cancel_invoice_promptCancel an authorized document, with confirmation.Connector permissions
The tools that change a document declare readOnlyHint: false and openWorldHint: true, because they transmit to the tax authority. That combination is what the default setting blocks.
Beyond that, every irreversible operation asks first: the skills require reading the data back before issuing, cancelling, invalidating numbering or filing a position — and issuing and cancelling accept an idempotency_key, so repeating a call does not create a second document.
Always ask in the connector's Permissions before issuing. On the default, Allow low-risk actions, every write is denied silently — the call never leaves the platform and nothing reaches us.