MCP server
altengine speaks MCP, so an AI coding agent can build on it directly — create instances, configure them, read and write data, deploy functions, and read what has been failing. Everything you would otherwise do by hand in the console.
Beta. The MCP endpoint is live for any organization; the tools it exposes follow what your credential can reach. Functions, container and automation tools additionally require their closed betas — see Functions, Containers and Automation.
Connecting
There are two ways in, and which one you want depends on where the agent runs. Both reach the same endpoint and the same tools.
Sign in (hosted clients)
For a client that runs somewhere you cannot paste a secret into — claude.ai, Claude Desktop, anything that asks you to add a connector — give it the endpoint and nothing else:
https://api.altengine.net/mcpThe client discovers the rest on its own, registers itself, and sends you to altengine to sign in. You then choose two things, and they are the whole of what the connection can do:
- Which organization it acts on. A connection is tied to exactly one. If you belong to several, the agent works in the one you pick here and cannot see the others — pick again by connecting again.
- How much access it has: read (look at everything, change nothing), write (create instances, write data, deploy functions), or full (also delete data).
The screen names the application as it described itself and shows the address it will be sent back to. Any application can claim any name, so the address is the part worth reading: if you do not recognise it, deny the request.
Everything you have connected is listed under Settings → Connected applications, with a revoke button that takes effect on the next call. Access is also re-checked against your membership every time the agent does anything — leave the organization, or have someone remove you from it, and the connection stops working immediately rather than whenever its token happens to expire.
An API key (local agents)
For an agent running on your own machine — Claude Code, an editor extension, a script — a key is simpler than a browser round-trip. Create one in the console with MCP / AI agent access switched on (Settings → API keys). That section is off by default, and a key without it is refused by the MCP endpoint even though it works normally against /v1 — the two capabilities are separate on purpose.
{
"mcpServers": {
"altengine": {
"url": "https://api.altengine.net/mcp",
"headers": { "Authorization": "Bearer ae_..." }
}
}
}A key is also the right choice for anything unattended, since nobody is present to click allow.
Either way the server is stateless: each call carries its own credential and is answered on its own, so there is no session to keep alive and nothing to reconnect.
What an agent can do
| Area | Tools |
|---|---|
| Orientation | list_instances, usage_summary, whoami |
| Instances | create_instance, delete_instance, get_instance_config, patch_instance_config |
| Datastore | datastore_aggregate, datastore_create_index, datastore_delete, datastore_get, datastore_list_collections, datastore_list_indexes, datastore_put, datastore_query |
| Search | search_delete_documents, search_get_documents, search_get_schema, search_list_indexes, search_put_documents, search_query |
| Auth | auth_create_user, auth_delete_user, auth_disable_user, auth_get_rules, auth_list_users, auth_set_rules, auth_set_user_claims |
| Channel | channel_list_rooms, channel_presence, channel_publish |
| Blob | blob_delete, blob_get, blob_list, blob_put, blob_set_public, blob_upload_url |
| Containers | container_cancel_job, container_get_job, container_job_logs, container_list_jobs, container_run, container_sizes |
| Functions | functions_deploy, functions_errors, functions_list, functions_rollback, functions_versions |
| Automation | automation_list_agents, automation_get_agent |
| Live desktop | desktop_list_windows, desktop_inspect_tree, desktop_capture_screenshot, desktop_test_selector, desktop_eval_js, desktop_end_session, desktop_save_script |
Every service can be created, configured and deleted this way — search, datastore, channel, auth, functions, blob, containers, automation and static. create_instance needs write on instances; delete_instance needs full plus confirm: true, and removes the instance and everything it holds. A delete is refused while something else depends on it: a blob instance holding an automation fleet's run artifacts, or a fleet with machines still enrolled.
The server also publishes resources the agent can read on demand — the search query grammar, the datastore query shape and its index rule, how auth levels and row rules combine, and the shape of a function. These matter more than they sound: the search and datastore query languages are App Engine's, and a model that has not read them will confidently invent Lucene or SQL syntax instead.
Developing against the emulator first
The local emulator serves the same MCP endpoint, with the same argument names. Point your agent at http://127.0.0.1:9191/mcp while altengine dev is running and it can create instances, write data and deploy functions without any of it touching your live org — then run the identical sequence of calls against the hosted service when it works.
Any bearer token is accepted locally, so the only difference between the two configurations is the key. Where a tool exists in both, it behaves the same, and the emulator is never the more permissive of the two — a workflow learned locally that fails in production is worse than not having it.
The emulator serves every cloud tool in the table above, under the same names and argument spellings. The desktop_* tools are the exception and cannot be emulated: they reach a real Windows desktop through a real agent, and there is nothing local to stand in for one.
Looking at a live desktop
The desktop_* tools are the reason Automation is worth connecting an agent to at all. Nobody can write selectors for a twenty-year-old line-of-business application from documentation, because there is none — so the agent looks at the running application instead: lists its windows, dumps the accessibility tree, photographs a window, tries a selector and is told in one round trip that it was wrong, evaluates statements against real state until the script works, and saves it as a version.
This is the one place on the platform where a tool reaches a machine somebody is sitting in front of, so it takes two separate permissions and both are required:
- the credential must carry the live desktop inspection scope. It is not implied by any other scope — a key that manages instances does not silently also look at screens — and on a key you switch it on deliberately.
- a developer window must be open on that specific machine. Only a signed-in person can open one, from the console; it lasts up to an hour and expires on its own. No tool can open it, no key can extend it, and it is re-checked on every single call.
A leaked key gets a list of machine names. It does not get a photograph of somebody's desk. Refusals say which of the two is missing, so an agent that hits one asks you to open the window rather than retrying.
desktop_eval_js additionally needs confirm: true, like every destructive tool — it types and clicks in whatever application is open, in front of whoever is there.
What it deliberately cannot do
The MCP surface covers building, not governance. There is no tool for billing, members and invitations, deleting an organization, or creating API keys — and no scope that could grant one. That last exclusion is the important one: a credential that can mint credentials escapes its own limits, which would make every other restriction here decorative.
That holds at every access level, including full. There is no combination of choices on the consent screen, and no scope on a key, that produces a governance tool — so nothing you can grant an agent lets it spend money, add a person, or issue itself a credential.
The two credentials differ in how finely you can cut what remains. A key carries scopes per area — instances, functions, usage, live desktop inspection — each at read, write or full, and data access goes through the key's ordinary grants map, so a key can be pinned to a single instance. A signed-in connection is one dial applied evenly: the level you chose, across every service in that organization. When an agent should only ever touch one instance, use a key.
An agent is only shown the tools its credential can actually call, either way.
Destructive tools — deleting documents, users or an instance — require an explicit confirm: true argument on top of the access level, so a deletion is never one hallucinated token away.
Some things stay in the console on purpose. Nothing over MCP can read or rotate a signing secret, a CAPTCHA secret or a password, and nothing can mint a channel token: those are credentials that outlive the conversation, and they should be issued by someone who meant to issue them.
Limits and cost
MCP requests are rate-limited to 600 per minute per organization — an order of magnitude tighter than the data planes, because an agent doing real work makes a handful of calls a second while a runaway loop makes thousands. A request carrying several tool calls at once counts as that many, so batching is not a way to go faster.
Tool calls are metered at $0.02 per 1,000. Connecting, listing tools and reading resources are free of charge — they still count against the rate limit, but they never appear on a bill.
Work done through a tool bills to its own service as usual, exactly as the same operation over REST would — a datastore_query costs the datastore reads it performs, plus the per-call fee.
In practice an agent session of a few hundred calls costs well under a cent. The price exists so that a stuck agent is bounded rather than free.
Working well with an agent
- Let it call
whoamifirst. It reports the organization, the scopes and the instance quota, which stops the agent guessing at what it may do. - Instance names work everywhere. Anywhere a tool asks for an instance, the name from
list_instancesis accepted — no ids to carry around. - Config changes are merges, one level deep.
patch_instance_configtakes only the top-level fields you want changed and preserves the rest, reporting back which ones actually changed. A field you do send is replaced, not merged into — so to change one thing inside a nested object (auth'ssettings,access,signuporemail), read it withget_instance_config, edit it, and send the whole object back. - A wrong argument name is an error, not a silent no-op. The server refuses a call carrying an argument it does not recognise and names the valid ones, rather than ignoring it and returning an answer that looks right.