ZIFFER home

The ZIFFER MCP server

Who this is for: the developer whose AI assistant helps set ZIFFER up: it scans the code, writes the integration, checks the policy and tries it in a sandbox.

Who this is for: the developer whose AI assistant helps set ZIFFER up: it scans the code, writes the integration, checks the policy and tries it in a sandbox.

The ZIFFER MCP server is a program your AI assistant starts on your machine, and it gives the assistant ZIFFER's tools. It is a helper for setting ZIFFER up and checking it: it is not what decides whether an AI agent's tool call runs, which ZIFFER's service decides and the verify line in your own code enforces. Nothing ZIFFER guarantees depends on your AI assistant behaving well.

This page describes @ziffer-io/mcp version 0.3.0. explain_scan_finding needs version 0.3.0 or later.


1. What you need

  • Node.js 22 or later. Your AI assistant starts the server with npx.
  • An AI assistant that starts MCP servers over standard input and output. Section 2 shows two.
  • Nothing else for most of the tools. No account and no key: section 3 says which tools need more, and what.

2. Install it in your AI assistant

Claude Code:

claude mcp add ziffer -- npx -y @ziffer-io/mcp@0.3.0

If you use Claude Code, the ZIFFER plugin starts this same server for you and adds written procedures on top of it.

Cursor, or any AI assistant that starts an MCP server over standard input and output, takes this in its MCP configuration:

{
  "mcpServers": {
    "ziffer": { "command": "npx", "args": ["-y", "@ziffer-io/mcp@0.3.0"] }
  }
}

The server starts without any configuration. To see it work, ask your assistant how to add ZIFFER to your project: it calls get_started, which needs nothing.


3. Its tools, and what each one needs

It has 21 tools. Most need nothing at all: no account, no key, no network. The tools that call ZIFFER need an API key, and while you set up that is a sandbox key (what a sandbox gives you, and what you receive). "Sends to ZIFFER" means the tool opens a connection to the ZIFFER service at ZIFFER_API_URL; every other tool works on your machine alone.

You areToolWhat it answersNeedsSends to ZIFFER
Scanning the codescanEvery tool your code, and the AI tools installed on your machine, give a model; ZIFFER's decision for each under a draft policy; and the one place to put ZIFFER.Nothing. Python on your PATH to read Python files.No
explain_scan_findingWhy ZIFFER decided what it did about one tool from the last scan, the draft policy entry that decided it, and how to change it.A scan earlier in the same session.No
Putting ZIFFER in the codeget_startedThe steps from nothing, in order: the scan, where an account comes from, the install command per language, the values to set, and which tool to call next.Nothing.No
get_integration_guideThe SDK guide for one language.Nothing.No
check_integrationFor each place your code proposes an action, whether a verify runs beside it.Your code on disk.No
lint_proposalWhether one proposal has the shape ZIFFER accepts, naming the field that is wrong.Nothing.No
Writing and checking the policyget_policy_repo_guideThe policy repository template's instructions and the first-hour guide, word for word.Nothing.No
check_policy_repoWhat is left to do in your clone of the policy repository, one line and one fix per check.The policy repository on disk, and the ziffer command line tool.No
explain_policyFor each action in your policy, whether it runs alone, runs with somebody told, is held for approval, or is refused, and by which rule.The policy repository on disk, and the ziffer command line tool.No
simulate_decisionWhat your policy decides about one proposal, graded on your machine.The policy repository on disk, and the ziffer command line tool.No
Publishingexplain_publish_failureFrom a failed publishing run's log: the step that failed, the refusal and what to do.The run's log, pasted.No
Trying it against a sandboxwhoamiWhich tenant your API key belongs to, and until when the key is accepted.An API key.Yes
sandbox_statusWhether a tenant is a sandbox and what that means; with a decision_id, whether that decision is decided or still waiting.Nothing. An API key when you pass a decision_id.Only with a decision_id
proposeSends one proposal and returns ZIFFER's answer as it came.An API key.Yes
check_decisionOne decision by id, with its receipt when there is one, not verified.An API key.Yes
Reading a decision, a receipt, a refusalget_decisionOne decision by id, its receipt verified on your machine before you see it, or the rule that refused it.An API key, and your trust anchor file and suite floor.Yes
list_decisionsEvery decision for your key, and every request still waiting for approval, newest first.An API key.Yes
explain_receiptWhether a receipt is valid for a proposal, or the rule that refused it.Your trust anchor file and suite floor.No
explain_refusalWhat one refusal name means, who fixes it and what to do now.Nothing.No
Documentation and feedbacksearch_docsThe sections of the ZIFFER guides that use your words, whole.Nothing.No
send_feedbackNothing back: it sends us a question the documentation did not answer, to be read by a person.An API key.Yes

send_feedback sends text your assistant wrote. What goes into its question and context is stored under your tenant and read by a person at ZIFFER, as written. Nothing in it is filtered or removed on the way, so it must never carry a key, a token, a customer's name or anything from a private file.

simulate_decision and explain_policy read your policy without checking its signature. They answer what your rules decide. A policy can read clean there and still be refused when it is published, for example because its signature does not verify or it has expired.


4. The values it reads

It reads four environment variables of its own. A tool reads a value when it is called, never before, so the server starts without any of them.

VariableWhat it isWhere the value comes fromRead by
ZIFFER_API_URLThe address of the ZIFFER service you call. It must start with https://; http:// is accepted only for your own machine.Your hand-over sheet (where every value comes from); for a sandbox, what ZIFFER hands you with its key.The tools marked "Yes" in section 3
ZIFFER_API_KEYYour API key. It carries your tenant, so no request names one.Your hand-over sheet; for a sandbox, the sandbox key.The tools marked "Yes" in section 3
ZIFFER_TRUST_ANCHORThe path to the public key file your receipts are signed under.A file written by ziffer pubkey and handed to you out of band, never fetched from the service it checks (your trust anchor file). A sandbox has its own.get_decision, explain_receipt
ZIFFER_SUITE_FLOORThe weakest signature you accept. There is no default.Your hand-over sheet.get_decision, explain_receipt

Set them in the environment your AI assistant starts in, or in its own MCP settings. Never type a key into the conversation with your assistant, and never put one in a file inside your repository. If your assistant keeps its MCP settings in a file inside your project, that file is part of your repository: keep the key out of it.

When the server starts, it writes one line to your assistant's log saying which of the four are set. It never writes their values.

explain_receipt takes a trust_anchor_path for one call, to check a receipt against a second key file without restarting your assistant. A key file that holds a private key is refused, and nothing is read from it.


5. What it writes, and where

Two tools write files. Neither writes inside your project or your repository unless you name the folder.

  • scan creates a new folder under your system's temporary folder, named ziffer-scan- followed by random characters, and writes into it the draft policy folder ziffer-policy-<date> and, beside it, the same files the scan writes from a terminal (what the scan writes). The HTML report and the review archive are included unless your assistant passes report: false. The files stay there for you to read and delete.
  • The one exception is a folder you name. Once you have agreed to start the installed tool servers (section 6), your assistant can pass out to name the draft policy folder. The files then go there, including inside your project if that is where the folder is. The folder must not exist yet. A relative out is taken from the folder your assistant started the server in. The first call, and a scan of your code only, always write into a new temporary folder.
  • simulate_decision writes the proposal it grades to a file in a new temporary folder, because the ziffer command line tool reads a proposal from a file, and removes that folder when it is done.

No other tool creates, changes or deletes a file.


6. What it starts

The first scan call starts no tool server. It reads your code, and it lists the tool servers your installed AI tools are configured to start, each with the command it would run and with credentials shown as [redacted]. To read Python it runs your machine's own Python on the reader shipped in the package.

A second call with confirm: true starts the servers on that list, asks each one for its list of tools and nothing else, and writes one draft policy covering your code and those tools. With include_installed: false the scan reads your code only, in one call, and starts nothing.

The confirmation is an instruction to your assistant, not a lock. The tool tells your assistant to show you the list and to make the second call only once you agree. The server cannot tell whether you agreed: an assistant that calls with confirm: true straight away starts the servers. The servers are programs your own AI tools already start, and they do what they do when those tools start them (what the scan starts).

Three tools run the ziffer command line tool. check_policy_repo runs ziffer list and ziffer decide --unsigned, the two commands your policy repository's own pull request check runs; explain_policy and simulate_decision run ziffer decide --unsigned. They run it directly, never through a shell, so that every verdict comes from the same program your pipeline uses. If ziffer is not on your PATH, those tools say NOT CHECKED and name it (installing the ziffer command line tool).


7. What it never does

These are properties of the server's code. An AI assistant can be talked into calling a tool, but no tool here can do any of the following.

  • It never creates, asks for or keeps your policy signing key. The scan signs its draft policy with a key made for that run inside the scan, never written to disk and dropped at the end; nothing of yours or ZIFFER's trusts it. check_policy_repo reports a failure if the private key file is inside your policy repository.
  • It never publishes a policy, and never commits or merges anything. No tool calls the service that publishes policy, and the only program it runs besides the scan is the ziffer command in section 6.
  • It never approves anything and never names an approver. There is no approve tool. sandbox_status reports and approves nothing. simulate_decision grades and signs nothing, and a PASSED from it is a grade, never permission to act.
  • It sends nothing to ZIFFER except through the seven tools marked "Yes" in section 3. The scan sends nothing anywhere, and no tool asks an AI model anything.

8. When a tool answers "not set", "not found" or NOT CHECKED

  • Not set. A tool that needs a value you have not set answers with a name ending in Unconfigured, and the variable to set: ApiUrlUnconfigured, ApiKeyUnconfigured, TrustAnchorUnconfigured or SuiteFloorUnconfigured. Nothing was sent and nothing was verified. There is no default address and no default key.
  • Not found. What you asked about is not where the tool looked, and the tool does not guess. explain_scan_finding answers ToolNotInScan with the names the last scan did find. explain_refusal answers RefusalUnknown rather than the row for the nearest name. A decision id that belongs to another tenant reads as DecisionUnknown, exactly like one that does not exist.
  • NOT CHECKED. The tool could not look: ziffer is not on your PATH, or a verify sits in another file where a text reading cannot follow it. NOT CHECKED is never a pass. A FAIL is different: the tool looked and found something, which is an answer and not a tool error.

Every other refusal is named; the refusal table says what each name means and what to do. A refusal is the same every time for the same input: fix what it names rather than retrying.


9. Where to go next

On this page