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.
The MCP endpoint is live for any organization; the tools it exposes follow what your credential can reach — 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.
Claude Code signs in the same way. Add the server, then run /mcp inside Claude Code and sign in when it asks:
claude mcp add --transport http altengine https://api.altengine.net/mcpEverything 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. The same Settings page shows the endpoint and these steps under AI access.
An API key (local agents and scripts)
For a client with no sign-in flow — an editor extension, a script, anything unattended — use a key. Create one in the console on the API keys page with MCP / AI agent access switched on. That section is off by default, and a key without it is refused by the MCP endpoint even though it works normally against /v1.
{
"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_issue_signin_code, 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_delete, functions_delete_version, functions_deploy, functions_errors, functions_list, functions_rollback, functions_versions |
| Static | static_activate, static_delete_deployment, static_deploy, static_list_deployments, static_list_files |
| Automation | automation_list_agents, automation_get_agent, automation_enroll_machine, automation_list_enroll_tokens, automation_revoke_enroll_token, automation_open_dev_window, automation_close_dev_window, automation_capture, automation_list_captures, automation_get_capture, automation_list_scripts, automation_delete_script, automation_activate_script, automation_run, automation_get_run, automation_exec, automation_list_schedules, automation_set_schedule, automation_delete_schedule, automation_reveal_webhook_secret, automation_rotate_webhook_secret — scopes below |
| Live desktop | desktop_machine_info, 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 large instance is removed a batch per call: until the last batch the tool answers DELETE_INCOMPLETE, so call it again until it succeeds — each call resumes where the last stopped. 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.
functions_rollback and static_activate make any stored version live. functions_delete_version and static_delete_deployment remove one that is not live — the live one is refused — and, like functions_delete, need full plus confirm: true. functions_versions and static_list_deployments return each version's url. How many are kept is keepVersions (functions, automation) or keepDeployments (static), set with patch_instance_config.
datastore_list_collections returns up to 200 names at a time, in name order, with a cursor. Pass the cursor back for the next page; a null cursor means there are no more.
datastore_aggregate returns up to limit groups (default 25, max 499), ordered by order — a group field or metric alias, e.g. [{"field":"n","dir":"desc"}]. truncated: true means there were more groups than it returned.
Resources
The server publishes resources the agent can read on demand (resources/list, resources/read). The search and datastore query languages are App Engine's, and a model that has not read them will invent Lucene or SQL syntax instead.
| URI | Covers |
|---|---|
docs://search/query-language | The search query grammar and request shape. |
docs://datastore/query | Filters, ordering, cursors, and the rule that refuses a query no index serves. |
docs://auth/rules | How an access level and row rules combine. |
docs://functions/starter | Handler shape, the env clients, grants, egress rules and schedules. |
docs://automation/script | Writing an automation script: the desktop calls, the key grammar, waiting without sleeps, matching windows, and what bounds a run. |
docs://automation/desktop-workflow | The order of operations for automating a desktop application: find the machine, learn the task from a recording or a live screen, build against real state, save for a person to make live, prove it, schedule it — and the two steps only a person can do. |
Prompts
Three prompts (prompts/list, prompts/get) hand the agent a step-by-step plan for a workflow whose order matters:
| Prompt | Arguments | Does |
|---|---|---|
scaffold-backendless-app | description (required) | Plans and creates the instances for a static app that calls the services directly, with per-user row rules. |
add-scheduled-job | task (required), when | Writes and deploys a function that runs on a cron schedule. |
automate-desktop-app | goal (required), machine | Turns a task done by hand in a Windows application into a saved, scheduled automation script, learning it from a recording or a developer window. |
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 (alt install altlimit/altengine if you do not have the CLI) 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.
The emulator serves every cloud tool in the table above, under the same names and argument spellings. The exceptions are static_* and automation_*, which the emulator does not serve, and desktop_*, which cannot be emulated: they reach a real Windows desktop through a real agent.
Learning from a recording
When somebody already does the task by hand, record them doing it once and let the agent read the recording. automation_list_captures lists a fleet's captures; automation_get_capture returns one: the manifest (duration, counts, how it stopped), a page of the UI tree samples taken at each interaction — each with its time, the foreground window, the pointer position and the accessibility tree — and up to four keyframes of the screen as images at the times the agent asks for (frames_at_ms). Trees are trimmed to tree_depth and tree_nodes; a node marked truncated means the tree was cut there, not that the control is empty. Pass samples_cursor back until it is null.
Reading a capture needs the live desktop inspection scope at read and no developer window: the recording was consented to when it was made, by the person who started it and by the person at the machine who saw the notice. A capture that is still recording is refused until it stops.
Looking at a live desktop
The desktop_* tools let an agent look at the running application on an Automation machine: list its windows, dump the accessibility tree, photograph a window, try a selector and be told in one round trip that it was wrong, evaluate statements against real state until the script works, and save it as a version.
Looking at a machine's screen, or running code you have not deployed, 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, and on a key you switch it on deliberately.
- a developer window must be open on that specific machine. An organization owner opens one from the console, or an owner's credential at full opens one with
automation_open_dev_window. It lasts up to an hour, expires on its own, belongs to the person who opened it, and is re-checked on every single call. While it is open the machine shows a small notice at the top of its screen — an AI assistant has live access — and for a window opened over MCP it cannot be turned off (agent 0.0.15 and later).
Refusals say which of the two is missing, so an agent that hits one opens the window if its credential can, or asks you to, rather than retrying.
Doing the whole job from the agent
With full access held by an organization owner, nothing in the loop needs the console. automation_enroll_machine returns the installer link and one-time token for a new PC (the person at it downloads and runs the file; nothing to type; it appears in Installed apps — the fallback command it also returns does not), automation_open_dev_window opens live access, automation_capture records somebody doing the task, desktop_save_script then automation_activate_script make the result live, and automation_set_schedule puts it on a timetable. Every one of those asks for confirm: true. The person at the machine sees a notice for as long as the window is open, and a second one while a capture runs, which they can click to stop it. Closing the window takes both down.
desktop_eval_js additionally needs confirm: true, like every destructive tool — it types and clicks in whatever application is open.
What each automation tool needs. The live desktop inspection scope is automation on a key; a signed-in connection has it at the level you chose. A key also needs its grants to cover the automation instance at the same level.
| Tool | Scope | Developer window |
|---|---|---|
automation_list_agents, automation_get_agent | instances: read, or live desktop inspection: read | No |
automation_get_run, desktop_machine_info, automation_list_captures, automation_get_capture, automation_list_schedules, automation_list_enroll_tokens, automation_list_scripts | live desktop inspection: read | No |
automation_run (a deployed script's live version, confirm: true) | live desktop inspection: write | No |
automation_set_schedule, automation_delete_schedule (confirm: true), automation_close_dev_window | live desktop inspection: write | No |
automation_capture (confirm: true) | live desktop inspection: write | Yes |
automation_activate_script, automation_delete_script, automation_enroll_machine, automation_revoke_enroll_token (confirm: true) | live desktop inspection: full | No |
automation_open_dev_window (confirm: true) | live desktop inspection: full, and an organization owner | Opens it |
desktop_list_windows, desktop_inspect_tree, desktop_capture_screenshot, desktop_test_selector | live desktop inspection: read | Yes |
desktop_eval_js (confirm: true), desktop_end_session, desktop_save_script | live desktop inspection: write | Yes |
automation_exec (a command or code you send, confirm: true) | live desktop inspection: write | Yes |
automation_reveal_webhook_secret, automation_rotate_webhook_secret (confirm: true) | instances: full, and an organization owner | No |
The two webhook-secret tools return the secret a fleet signs its completion webhooks with. Besides the scope and a full grant on the instance, the person behind the credential must be an owner: a connector signed in as one, or a key one created.
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 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.
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.
Nothing over MCP can read or rotate a signing secret, a CAPTCHA secret or a password, and nothing can mint a channel token; those stay in the console. The one exception is an automation fleet's webhook signing secret, which only an owner's credential can read or rotate.
The one end-user credential a tool returns is auth_issue_signin_code: a one-time passwordless sign-in code for an existing, enabled user of an auth instance, handed back instead of emailed. It needs full on instances and on that auth instance, and passwordless sign-in enabled there. It is the emailed code exactly — same TTL, single use, one outstanding per user — and the person still exchanges it at POST /v1/auth/{instance-id}/passwordless/verify, so it is a way into that account, not a session or an altengine key.
Limits and cost
MCP requests are rate-limited to 600 per minute per organization. A request carrying several tool calls at once counts as that many, so batching is not a way to go faster. A JSON-RPC batch holds at most 20 messages; a larger one is refused with 400 before any of it runs.
Tool calls are metered at $0.02 per 1,000. Nothing else is billed — connecting, listing tools, reading resources and getting prompts 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.
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.