Give Claude Code a Backend: Connect It to Datastore, Auth and Functions over MCP

Run claude mcp add --transport http altengine https://api.altengine.net/mcp, then type /mcp inside Claude Code and sign in. You pick one organization and one access level (read, write or full) on the consent screen, and from then on Claude Code can create a datastore, write documents, set auth rules and deploy functions as tool calls. It cannot reach billing, members or API keys, whatever you pick.

Step 1: add the server and sign in

The whole connection is one command and one sign-in. No key to paste, no config file to edit:

claude mcp add --transport http altengine https://api.altengine.net/mcp

Start Claude Code, run /mcp, and pick the altengine server when it asks you to authenticate. Claude Code finds the sign-in details on its own, registers itself, and opens a browser tab on altengine. If you do not have an organization yet, create one in the console first. It is free and does not need a card.

The consent screen names the application as it described itself, and shows the address it will send you back to. Any application can claim to be called Claude Code, so read the address, not the name. If you do not recognise it, deny the request.

Step 2: the two choices on the consent screen

Those two choices are the whole of what the connection can do.

Which organization

A connection is tied to exactly one organization. If you belong to several, the agent sees only the one you pick and cannot list or touch the others. To work in a different one, connect again.

How much access

MCP access levels and when to pick each
LevelLets the agentPick it when
readLook at everything, change nothing.You want it to explain an existing setup, read error groups or answer a usage question.
writeCreate instances, write data, deploy functions.You are building. This is the right default for a development organization.
fullEverything in write, plus delete data and instances.You want it to clean up after itself, and you have read what confirm: true means below.

A signed-in connection is one dial, applied the same way across every service in the organization. If the agent should only ever touch one instance, a signed-in connection is the wrong tool; use a scoped key instead (see the end of this post).

Even at full, destructive tools such as delete_instance, datastore_delete or auth_delete_user refuse to run without an explicit confirm: true argument. The agent can pass that flag itself, so Claude Code's approval prompt for each tool call is the human check: read it before you approve one that carries confirm.

Step 3: let it call whoami

The first useful thing to type is something like check what you can do on altengine. The agent calls whoami, which reports the organization, the scopes the connection carries and the instance quota. That one call stops most of the guessing: an agent that knows it has read will not try to deploy and then get confused by the refusal.

The agent is also only shown the tools its credential can actually call. On a read connection it never sees functions_deploy in its tool list.

Three first asks, in rising size

1. What is here?

List my altengine instances and tell me what this month has cost so far. The agent calls list_instances and usage_summary. Instance names from list_instances are accepted anywhere a tool asks for an instance, so from here on you and the agent both say appdb, not an opaque id.

2. Create a datastore and put a document

Create a datastore instance called appdb and store a signup for [email protected] in a waitlist collection. Expect create_instance, then datastore_put, then usually a datastore_get to check the write landed. Collections are created on first write, so there is no schema step.

Ask for a filtered query next. A well-behaved agent reads the docs://datastore/query resource before it writes one. That is worth watching for, because the query language is App Engine's, and a model that has not read it tends to invent SQL. Queries are served from indexes. With auto-indexing on (the default), a query that needs an index gets one built and runs, and the response says so in an auto_indexed field. That build is billed as writes. In production you will want auto-indexing off, so that a missing index returns 400 INDEX_REQUIRED naming the field instead of quietly costing you writes. Ask the agent to say which indexes it created.

3. Deploy a function that reads it

Deploy a function called waitlist on a functions instance that returns the signups from the launch campaign. Before it writes code it should read docs://functions/starter, which covers the handler shape, the env clients, grants and schedules. What it deploys with functions_deploy should look like this:

export default {
  async fetch(request, env) {
    const res = await env.datastore.query({ instance: "appdb" }, "waitlist", {
      where: [{ field: "source", op: "=", value: "launch" }],
    });
    return Response.json(res.documents.map((d) => d.data));
  },
};

Two things to check in what it did, not just in what it says it did:

  • The grant. A function reaches only the services it was granted, as a map of service or service:instance to read, write or full. This one needs datastore:appdb at read and nothing else. If the grant is missing, env.datastore is simply absent, and the first call fails with env.datastore is undefined. Row-level rules do not apply inside a function; its grants are its whole blast radius.
  • The URL is public. Anyone can call a function URL. A function returning a waitlist with email addresses in it should check an identity token first, or not exist.

When it fails at runtime, ask the agent to look. functions_errors returns error groups, functions_versions lists what is stored, and functions_rollback makes an earlier version live. Every stored version is a line on the bill: $0.10 per retained version per month whether or not it is called, with keepVersions (3 by default) as the setting.

What it costs

Tool calls are $0.02 per 1,000. Connecting, listing tools, reading resources and getting prompts are not billed. The work a tool does bills to its own service exactly as the same REST call would, so datastore_query costs its datastore reads plus the per-call fee. A session of a few hundred calls costs well under a cent, and the first $3 of usage each month is free across every service. There is a rate limit of 600 MCP requests per minute per organization, and a batch of several tool calls counts as that many.

What it will not do

  • No governance tools. No billing, members, invitations, API key creation or closing the organization, at any access level. The agent cannot rotate a key for you, and that is on purpose.
  • No secrets. Nothing over MCP reads or rotates an auth signing secret, a CAPTCHA secret or a password, or mints a channel token. Those stay in the console. (The one exception is an automation fleet's webhook secret, which only an owner's credential can read.)

Revoking is immediate. Settings → Connected applications lists every connection with a revoke button that takes effect on the next call, and access is re-checked against your membership on every call. Leave the organization and the connection stops working at once.

When to skip the sign-in

To pin the agent to one instance, create an API key in the console with MCP / AI agent access switched on (it is off by default, and a key without it is refused by the MCP endpoint), give it scopes per area and a grants map naming only that instance, and add it as a header:

claude mcp add --transport http altengine https://api.altengine.net/mcp \
  --header "Authorization: Bearer ae_yourkeyid.your-api-key-secret"

A key is also the only option for anything unattended, since nobody is there to click allow. Revoking or narrowing a key takes about a minute to take effect, not one call.

To rehearse first, install the CLI (alt install altlimit/altengine), run altengine dev, and point Claude Code at the emulator, which serves the same MCP endpoint with the same tool names and argument spellings. Any bearer token is accepted locally:

claude mcp add --transport http altengine-local http://127.0.0.1:9191/mcp \
  --header "Authorization: Bearer local"

Let it make its mistakes against localhost, then run the same sequence against your organization. The emulator does not serve static_* or automation_*, and it is never more permissive than the cloud. Where a tool exists in both it behaves the same, so a call the cloud would refuse is refused locally too.

The full tool list, resources and prompts are in the MCP server reference. For what a whole session looks like when the agent builds an app end to end, see the post that introduced the MCP server.

Start free in the console

← Back to the blog