Simple BackendLog inStart

Connect your LLM

One URL for Claude Desktop, Claude Code, claude.ai or any MCP client. Your browser opens, you approve, and it can build everything a workspace has.

https://app.simplebackend.dev/mcp

There is no API key to create, copy or paste. That URL is the whole of it, because the client asks the server what it needs.

Two ways in, and the same URL

In a terminal

Claude Code, from the directory you work in.

claude mcp add --transport http \
  simple-backend \
  https://app.simplebackend.dev/mcp

In Claude Desktop

Or claude.ai. Both take the same URL as a custom connector, with nothing to install.

1Settings → Connectors
2Add custom connector
3Paste the URL above

Either way, your browser opens. Not on a form asking you for a token — on this platform's own sign-in, and then on a consent page naming the workspaces the credential would cover. You approve it there, on a page you can read, and the client is handed something scoped to exactly that.

What happens when you run it

  1. OneThe client asks the URL what it needs, and is told to authorize.
  2. TwoYour browser opens the console. You sign in the way you always do.
  3. ThreeA consent page lists the workspaces the credential will cover, each one a tick you can clear, plus a tick for write access. Read-only is the one to pick the first time.
  4. FourThe client holds a token. Ask Claude to deploy.

The tools

Each one is a thin call into a path the console and the CLI already take, so nothing here is a second implementation that could disagree with them.

GroupTools
Docsread_docs is the manual, served by the server itself, so a model connected to nothing else still finds it; get_skill hands back the Skill and where to save it
Accountlist_workspaces create_workspace list_keys
Teamlist_members invite_member remove_member — by email; the owner invites
Functionsget_source list_functions add_function remove_function deploy releases rollback
Running ittest_function runs a candidate in the real sandbox and stores nothing; call_function calls what is live, with no key in the conversation
Settingssettings set_var set_secret remove_setting; outbound_calls restrict_outbound for where functions may call
Sign-insign_in configure_sign_in — off until switched on; list_users disable_user delete_user
Pageslist_pages publish_page unpublish_page
Previewpreview promote_preview discard_preview rotate_preview — a release and pages staged together at a private address, then made live together; preview: true on the tools that change things stages instead
Datalist_kinds describe_kind find_records index_field declare_field forget_field set_strict
Watching itread_log is the last runs; usage is how many and how slow, per function; cache_usage and list_files are what the workspace is holding against its quotas
Feedbacksend_feedback sends a feature request or a bug report to the team, when you agree to it

The data tools read your records. describe_kind reports what is stored under each field without returning a value; find_records returns the records themselves. Grant read-only, or a single workspace, on the consent page if that is not what you want a chat to reach. read_log returns the run log — function names, releases, timings, whether each run succeeded, the caller (an IP, or an API key’s label, never a key) and the error text, which is whatever your function put there. If your errors quote customer data back, the log carries it and so does anything you connect to the log.

What it can reach

The workspaces your account owns or is a member of, and nothing else. Every tool checks the workspace argument against that list before it touches anything — the check is not on the model's good behaviour.

PropertyHow it works
Access tokenSigned, one hour, and bound to this host as its audience — a token minted for another server fails here exactly like a forgery
RefreshRotates on every use, stored only as a hash. Replaying a rotated token revokes the whole chain
ExpiryThirty days idle, ninety days absolute, both enforced as refusals
RedirectsAn allowlist in code, matched on scheme, host, port and path prefix. A look-alike host is rejected, and the rejection is recorded
RevokingAccess is re-checked when the credential refreshes, so removing an account’s access to a workspace stops the credential reaching it within the hour. [a screen for revoking one credential on its own is not built yet]

Give Claude the rules

The tools are discoverable; the two rules that are not are how deploy wants a whole release, and that rollback wants the full hash. This is that, in markdown.

Paste into a CLAUDE.md, or hand it over in chat. Click in the box, select all, copy.

For writing the functions themselves rather than operating a workspace, take the Skill instead.

Which clients

Anything that speaks MCP over streamable HTTP and can register itself. Claude Code and Claude.ai are what we use daily; ChatGPT, Cursor, VS Code and Perplexity are in the redirect allowlist and should work.

A client we have not listed cannot connect until its callback is added — a deliberate trade. In a flow with no client secret, the redirect is the only thing standing between an authorization code and whoever asked for it, so it fails closed.

Try it

Once connected, ask in plain language:

> which workspaces do I have?
> deploy this manifest to acme
> why did the last five runs fail?
> roll acme back to the release before this one

Without the browser

The console writes a manifest for you. If you would rather write it yourself, it is a file called swarm.edn beside your source, and it is what the CLI and Claude both deploy.

{:backend/version 1
 :tenant "acme"
 :functions
 [{:name    :contact/submit
   :file    "fns/contact.clj"
   :entry   contact/submit
   :trigger {:type :http :method :post :path "/contact"}
   :allow   :public
   :needs   #{:db/write}}

  {:name    :report/nightly
   :file    "fns/report.clj"
   :entry   report/run
   :trigger {:type :cron :schedule "0 3 * * *"}
   :needs   #{:db/read :http/out}
   :limits  {:timeout-ms 30000 :isolation :process}}]}

Each key is a control from the Settings tab: :allow is who may call it, :needs is the checkboxes, :limits is the time limit and whether the function is isolated. The nightly function has no :allow because it has no URL — it is required on an HTTP trigger and never defaulted, since the value a forgetful default would pick is either an endpoint nobody decided to expose or a function that is deployed and unreachable.

One value exists in the file that the console does not offer: :allow :signed-in, for your own signed-in end users. It refuses every call. End-user authentication is not built, and the gateway refuses rather than guessing — the console leaves it out entirely so nobody picks it by accident.

The CLI

sb test      # run the candidate ON THE PLATFORM, in the real sandbox
sb deploy    # validate, store, activate
sb rollback  # the release before this one

sb test is the reason it exists: it is the Run panel without the browser. A function that passes on your laptop has been tested in a different language, so it sends the candidate to the workspace and runs it there. It writes to the real database, so point pipelines at a staging workspace.

Start with a workspace

The MCP manages workspaces you already have. Make the first one in the console — that part takes a browser exactly once.

Open the console Read the docs