Skip to main content
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.
An app is meant to have three faces over one security, entitlement and audit layer:

UI

The rendered application people use.

REST

Baseline on every app. See the API reference.

MCP

The same operations, addressed by an AI client.
MCP isn’t a separate integration with its own permission model. That’s the whole point of splitting it this way. It projects operations that already exist, through the same checks the REST facade goes through, so an AI client acting as you can reach exactly what you can reach and nothing else.

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 app endpoint narrows what a client sees, not what a key may do. A personal access key acts as the person who made it, everywhere they can reach, so a client pointed at one app can still call the workspace endpoint with the same key. It’s worth having, because an agent that can’t see billing can’t be talked into touching billing, but it isn’t a boundary until keys can be scoped to an app.
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, dropping list_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_writable or command.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.
A key acts as the person who created it, with the same relationships, the same roles and the same audit trail. It expires if they gave it a date, and it stops working the moment their account is locked. Only a hash of it is stored, so it’s shown once and can’t be recovered. Mint a new one instead.
cordango logout forgets a credential on the machine it runs on. It doesn’t revoke it. To stop a key working everywhere, delete it on the instance. See Security.
The transport is Streamable HTTP at protocol revision 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 by cordango 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.

The REST route

Everything above is also reachable over HTTP, and each app publishes its own OpenAPI document generated from its manifest:
Point a tool generator at that and you get the app’s real entities and fields rather than a generic shape. Worth knowing for a client that speaks OpenAPI and not MCP. See the API reference.