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 plugin
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.
Connect
The server speaks streamable-http at a fixed address. An MCP client connects over OAuth 2.1 with PKCE — the path the ChatGPT plugin takes — or with a company API key, which is the shortest route for a local client.
With an API key
The key goes in the Authorization header, as in the API. Over HTTP the credential always comes from the request: one process serves many companies and never falls back to a key from the environment.
{
"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.