safata Download

Use Safata from another agent.

Claude Code, Claude Desktop and Codex can hand Safata a job (a report, a workbook, a deck, a picture) and get the files back. So can a script. There is no server to run, no port and no token: the other agent starts Safata on a pipe, and it stops when the pipe closes.

Connect it

Before you start

Install the app and add a model key in it once; runs an agent asks for use the same keys, and the agent never sees them. The engine is the file inside the app, /Applications/Safata.app/Contents/MacOS/safata. Nothing puts it on your PATH, so every setup below names it in full. An agent starts its tools with its own environment, not your shell’s, and the full path is the one that always resolves.

Claude Code

claude mcp add --scope user safata -- /Applications/Safata.app/Contents/MacOS/safata mcp-serve

Claude Code starts Safata in the folder it has open, so that is where the work lands. --scope user makes it available in every project.

Claude Desktop

Claude Desktop starts its servers in no particular folder, so tell Safata which of your projects to work in. In claude_desktop_config.json:

{
  "mcpServers": {
    "safata-finance": {
      "command": "/Applications/Safata.app/Contents/MacOS/safata",
      "args": ["mcp-serve", "--project", "Finance"]
    }
  }
}

One entry per project, on purpose: the entry’s name tells the agent what each one is for. The project name is checked when the server starts, and an unknown one is refused before any agent connects.

Codex

In ~/.codex/config.toml:

[mcp_servers.safata]
command = "/Applications/Safata.app/Contents/MacOS/safata"
args = ["mcp-serve", "--project", "Finance"]

Any other MCP host

A host that starts local servers over stdio needs the same two things: the path above as the command, and mcp-serve as its argument, plus --project and a project name if the host starts servers in no particular folder.

ChatGPT

ChatGPT can’t start a program on your computer; it only reaches servers over the internet. OpenAI’s Secure MCP Tunnel client is a relay you run on your Mac: it opens a connection out to OpenAI, starts Safata on a pipe and passes ChatGPT’s calls to it. That works, and three things are true if you do it.

It is your act, not Safata’s. Safata doesn’t open the connection, can’t see it, and can’t tell a relay from an agent on your desk. Its traffic is not in safata egress. The prompt ChatGPT sends, and the answer and files it reads back, travel on the relay’s own connection, outside the log. The run is unattended and read-only by default, like every run an agent asks for.

The four tools

safata_run: one job, start to finish

One unattended turn in a fresh session: research, a document, a spreadsheet, a deck. It takes a prompt and, optionally, a JSON schema the answer must fit, allow rules that widen what the run may touch, and a model id. It returns {outcome, exitCode, result, sessionId, costUsd, denied, deliverables}. deliverables lists every file the run made (name, path, kind, bytes), so the agent collects the file, not a description of it.

safata_sessions: what has run

The sessions list, newest first: id, title, folder, model and when it last moved, and for a run an agent asked for, which agent asked.

safata_read_deliverable: collect a file

One file a session declared, by the path its envelope gave. Text comes back as text; a workbook, a deck or a PDF comes back as bytes. A path the session never declared is refused.

safata_image: one picture, no turn in between

One picture, drawn with the image model you set up in Safata (GPT Image 2.5 through an OpenAI key by default, or Gemini through a Google key) and saved as a PNG where the agent says. The agent already wrote the brief, so it goes to the image model word for word, and no Safata model bills a turn to relay it.

It takes prompt and path, and optionally size (square, landscape or portrait), quality (low, medium or high: about 1¢, 3¢ and 12¢ a picture), background (opaque or transparent) and up to four references, pictures to follow. It returns {outcome, path, width, height, model, costUsd, reason}, where the outcome is drawn, refused or failed. The agent can open the file to look at what it got.

What an agent may do

Read anything you can, change nothing, unless you allow it

A run an agent asks for can read anything you can, and change nothing. A step that would write a file, run a command with side effects or reach a service is refused as permission_required; the run carries on, and the refusal is listed in denied with the exact rule that would have let it through. The agent can pass that rule on its next call, as allow: ["write(./reports/**)"]. Your own deny rules outrank anything an agent passes.

A picture’s path is its own approval

safata_run writes where Safata’s model decides, so it needs a rule. safata_image writes exactly where the agent said, so naming the path is the approval: no rule and no card, however many pictures. What still refuses: your own deny or ask rules, protected folders such as ~/.ssh, and a path that leaves the folder through a symlink. The brief goes to one party only, the vendor whose key draws.

Your spending caps bind

Every run and every picture counts against your spending caps. A run nobody is watching has a ceiling of its own, and your daily and monthly caps bind too; a picture is checked against them before it is drawn.

No self-granted trust, a fresh session every call, one at a time

An agent can’t give itself the trust you’d give a session you’re sitting in; the server never accepts --trust. There is no resuming over MCP: each safata_run starts clean and is recorded like any other session. Calls on one connection run in order; a host that wants runs in parallel starts a second server.

What you’ll see

Every run, under the agent’s name

In the app’s session list, a run an agent asked for says via Claude Code, or whatever name the agent gave itself when it connected. The name is a label to find your own work by, not proof of anything. In safata egress, every request the run made is there with the agent named as the one who asked.

What it was refused, waiting on you

When an agent’s run is refused something, a note lands in Waiting on you: one per agent and folder, not one per attempt. Got it dismisses it. Always saves the rule for that folder into your permissions file, which means your own sessions in that folder stop being asked about the same thing too. It takes effect on the agent’s next call.

Pictures

The file where the agent put it, one row on your spending ledger at the vendor’s exact charge, and one row in safata egress naming the agent. A picture isn’t a session, so it doesn’t appear in the session list.

From a script

The same engine, without MCP

In a terminal, an alias saves typing the path (add the line to ~/.zshrc to keep it):

alias safata=/Applications/Safata.app/Contents/MacOS/safata

Then one job is one line, for a shell script or a cron entry:

safata -p "summarize the findings in ./reports" --output json --schema report.schema.json

The prompt comes from the argument or from stdin; the answer goes to stdout and everything else to stderr. The exit code is 0 for completed, 1 for failed, and 2 when a refusal or the spending cap touched the run; the JSON envelope’s outcome says which. --schema makes the answer validate against a JSON Schema. --allow 'write(./reports/**)' widens the read-only default one rule at a time. --trust gives the run the trust you’d give a session you’re sitting in, and the floor (sending things out, destroying things outside the folder, secrets) still refuses. --project and a name opens the run in that project’s folder.

One picture

safata image is the same direct draw as safata_image:

safata image --out assets/hero.png --size landscape --quality high "a lighthouse at dusk, warm light, open sky on the left for a headline"

--ref and a file (up to four) passes pictures to follow, and --output json prints the envelope. The exit code is 0 for drawn, 1 for refused or failed.

The manual is in the binary

safata help mcp prints the server’s page, safata help image the picture’s, and safata help lists the rest.