Platform features need a Cordango workspace. You can
sign up here and create one in a few minutes. The open
foundation, meaning the CLI, the compiler and the standalone generator, needs no account and
is available to everyone.
UI
The rendered application people use.
REST
Baseline on every app. See the API reference.
MCP
The same operations, addressed by an AI client.
Two endpoints
A Cordango workspace serves MCP at two levels. Same tools, same auth, different blast radius.
Point a general assistant at the workspace endpoint. Point a narrow agent, the one that files
tickets and does nothing else, at the app endpoint.
The exact addresses for any app are on that app’s Connect page in the product, which also carries
a ready-made client configuration.
The tools
Ten, whatever the workspace contains. The app endpoint serves nine of them, droppinglist_apps
along with the app parameter, because there’s no second app for it to mean anything about.
Deliberately not one tool set per entity, and not one per app. A client pays for the tool list on
every request, so a workspace with six apps and twenty entities each would otherwise publish several
hundred tools and charge for that list every time. This way the cost stays constant no matter how
big the workspace gets, and
describe_app is how a client learns the shape of one app when it needs
to.
Every tool projects an operation the REST facade already has. That is the whole reason MCP is on
by default rather than being an integration with a permission model of its own, and it is why there
is no tool here that REST cannot do. Cross-app search, and “everything that points at this record”,
arrive when the queries behind them do.
On the app endpoint, entity carries an enum of that app’s real keys, so a client’s first call can
be right without a round trip. The workspace endpoint has no such list: the tool list is identical for
every caller, and one that varied by role could not be cached and would say what your role is. Call
list_apps first instead, and if you guess a name wrong, the refusal names the ones that exist.
Cross-app questions
Every app in a workspace shares one data model. A customer is a record in core Organizations, and the support app, the billing app and the sales app all reference that same record rather than keeping three copies of a company with three addresses. So a question that spans them is a lookup rather than an integration project.Today an assistant does this the direct way:
list_apps, then describe_app on the ones that
matter, then list_records in each. Dedicated search and find_related tools are planned and
arrive with the cross-app queries behind them. We would rather not publish a tool that answers
“not yet”.aggregate_records runs inside one app, because the aggregate is computed by the database and the
answer has to be exact. It can still group by a cross-app reference, so “open tickets by customer
industry” works even though industry lives in core Organizations and tickets live in support.
Permissions
Nothing is reachable over MCP that isn’t reachable over REST. Every tool goes through the same gateway the controllers do, so:- The app’s roles decide each call.
- Fields your role may not read are absent from the answer rather than null.
- A refusal carries the same stable code the REST route returns, for example
common.forbidden,app.fields_not_writableorcommand.wrong_state, with the sentence beside it. Fixtures run the same operation over both surfaces and compare the codes, so this stays true rather than being asserted once. - An app you have no relationship with answers as a missing app does, because whether an app exists isn’t something an unrelated caller gets to learn.
- Permissions are per entity. A role that may read an entity reads every row of it: there is no row-level predicate outside the calendar yet, and MCP inherits that exactly, no better and no worse.
describe_app and list_apps both report your own permissions, so a client can plan instead of
discovering its limits by being refused halfway through a task.
Every call lands in the workspace audit log with the key that made it. An answer an assistant gave
last Tuesday is a question you can still ask the audit log about.
Connecting
An MCP client isn’t a browser, so it has no session cookie. Mint a personal access key under your avatar menu, Personal Access Keys, and send it as a bearer token.2026-07-28, which is stateless. There’s no
initialize handshake and no session, so the endpoint works unchanged behind a load balancer or
scaled across containers.
Discovery is provisional. The workspace serves a server card at
/.well-known/mcp/server-card.json, the path
SEP-2127 proposes. That’s
an open proposal, not part of the specification. Expect it to move, and don’t build anything that
depends on finding it there.In a standalone app
An application generated bycordango build serves its own MCP endpoint, on by default. One app,
one process, nothing above it, so the tool list is eight: describe_app and the seven record tools.
No list_apps, because there’s no second app to choose between.
Authentication works the same way, with a key minted under Access keys in the app’s navigation.
The prefix differs,
cordango_pat. rather than cord_pat., because a standalone app mints its own
keys and answers to nothing above it.
To turn it off, delete the AddCordangoMcp and MapCordangoMcp lines from api/Program.cs. The
REST facade and the OpenAPI document are unaffected.

