safe-mcp sign in

One URL in front of everything.

Your client talks to one address. The agent sees ten tools on base (nine on free), however many servers you connect. Every call takes the same eleven steps in the same order and lands in one log. The servers stay where they are.

10
tools the agent sees
11
steps per call, fixed order
1
append-only log per project
0
bodies in the model's context

Connect a client with one URL, or one pasted block.

Give Claude, Codex, Cursor or ChatGPT the project URL, https://api.safe-mcp.com/mcp/<project>, and nothing else. The gateway is its own OAuth authorization server: the client registers itself, you approve it once in the browser, and it gets a token scoped to that project for an hour, refreshed while it stays in use.

Or mint a personal token in the app and paste the config it hands you. Both speak Streamable HTTP. There is no SSE transport.

{
  "mcpServers": {
    "acme": {
      "type": "http",
      "url": "https://api.safe-mcp.com/mcp/acme",
      "headers": { "Authorization": "Bearer smcp_…" }
    }
  }
}

Example, not real data. The app generates this with your token filled in.

The agent sees ten tools, and none of yours directly.

Downstream tools are never mounted into the model's context one by one. The agent searches the catalog, reads one tool's schema together with the rules that apply to it, and calls it by name with a one-sentence reason. That is the whole surface: ten tools.

meta.searchToolssearch the catalog; ask for one tool by name to get its full schema and the rules that apply to it
meta.callToolrun one downstream tool through the gates below, with a one-sentence reason
meta.goalread or declare the goal in force; declaring measures it once, immediately
meta.plandeclare the strategy and its steps; read past plans for the same goal as evidence
meta.recommenddo / avoid / check / expect rules from this account's own measured history, with counts
meta.backfillimport your own historical KPI points and change history as evidence, marked as such forever
meta.historywhat already happened this session or project, as counts and handles, never bodies
meta.statuswhich servers are connected, whether a goal stands, whether the index is in sync
meta.mathexact arithmetic, up to 200 expressions per call
meta.pythonsandboxed, standard-library Python for results too large to read (base plan)

Eleven steps, always in this order.

The request is written to the log first, with everything the agent sent. Each step writes its decision before it refuses, so a refused call is as durable as a successful one. The code on the right is what the log records at that step: the refusal code where it stops the call, the event where it passes.

  1. loggedthe full request, before anything is decidedtool_call.started
  2. cancellationan already-aborted request is refused, not runcancelled
  3. existencethe server must be known and up; a suppressed tool does not existunknown_tool
  4. reasonone self-contained sentence, at least six wordsinvalid_reason
  5. policypreset rules: allowed keys, pinned values injected and recordedpolicy_argument
  6. schemathe tool's own JSON schema, with the failing path disclosedinvalid_arguments
  7. goala write with no goal in force is refused; reads always rungoal_required
  8. evidencea write that earlier moved the goal away is refused with the evidenceprior_harm_unacknowledged
  9. testing modewith testing on, every non-read-only tool is blocked heretesting_blocked
  10. dispatchthe downstream call, with cancellation and progress passed throughdownstream_failed
  11. resultwritten as succeeded or failed; a large body becomes a handletool_call.succeeded

One process, one runtime per project.

Your project is its own runtime: its own catalog, connections, credentials, goal and log. One gateway process serves the app, its JSON API and the MCP endpoint for every project. Editing a project's servers or presets rebuilds that runtime in place; sessions already running finish on the old configuration.

If you have never looked at what an agent actually sends, the history page is the place: every request as it was written, the step that answered it, and the time it took.

one process bun
serves
app.safe-mcp.comthe app and its JSON API
api.safe-mcp.com/mcp/<project> over Streamable HTTP, OAuth or token
per project
events.jsonlthe record: append-only, never pruned
index.duckdbderived from the log, rebuilt whenever it is lost
goals.json · oauth/the goal in force, per-user credentials
talks to
library serversthe vendor's endpoint, resolved on every load
remote serverswith headers, or OAuth as the caller

Layout, not a screen.

Limits a client will meet.

Defaults today. Only the free allowance is per account.

search results50 per page, 500 at most, paged
result bodyabove 64 KB the result becomes a handle with a preview; read it with meta.python (base plan); on free the preview is what the agent gets
server tool calls1000 a month per account on free, unlimited on base
python10 s wall clock, 256 KB of output, two interpreters at a time
idle sessionclosed after 30 minutes without a request
concurrent sessions50 at a time; the 51st gets a 503 with a code, not a hang
downstream timeout60 s unless the server sets its own

Next: what it learns from that log.