@ziffer-io/mcp
v0.2.0
Published
A local MCP server so a coding agent can integrate the ZIFFER SDK and drive a live decision loop.
Downloads
169
Maintainers
Readme
@ziffer-io/mcp
A local MCP server, so a coding agent can integrate the ZIFFER SDK and drive a live decision loop.
Install
There is nothing to install first. The server runs over stdio from npx.
npx @ziffer-io/mcpConfigure your agent
Claude Code:
claude mcp add ziffer -- npx -y @ziffer-io/mcpCursor, or any client that starts an MCP server over stdio:
{
"mcpServers": {
"ziffer": { "command": "npx", "args": ["-y", "@ziffer-io/mcp"] }
}
}What it can and cannot do
It has nineteen tools.
| Tool | What it does |
| --- | --- |
| get_started | The install command for your language, the four variables, the six-line wrap and which tool to call next. |
| get_integration_guide | The SDK integration guide, for one language. |
| check_integration | Reads a repository and reports, per propose call site, whether a verify sits in the same handler. |
| lint_proposal | Validates one proposal against the wire schema locally and names the bad field before any call. |
| propose | Submits one Proposal and returns the answer verbatim. |
| check_decision | One decision by id, with its receipt when there is one. |
| get_decision | One decision by id whose receipt is VERIFIED before it is returned, or a named refusal. |
| list_decisions | Every decision and held request for your key, newest first. |
| explain_receipt | Verifies a receipt and reports valid or the clause that refused it. |
| sandbox_status | Says whether a tenant is a sandbox, and what that does and does not mean. |
| get_policy_repo_guide | The policy repository template README and the first-hour guide, verbatim. |
| check_policy_repo | Reads a clone of your policy repository and reports what is not yet true about it. |
| explain_publish_failure | Reads a failed publish run's log and names the step, the refusal and the fix. |
| search_docs | Searches every onboarding document but one (see below) and returns the matching sections whole. |
| explain_refusal | One refusal name in; what it means, who fixes it and what to do out. |
| send_feedback | Sends us a question our documentation did not answer. This sends text off your machine. |
One tool sends text off your machine, and it is send_feedback. What your agent writes in
its question and context is posted to your Ziffer deployment, stored under the tenant your
API key carries, and read by a person. It is not filtered, summarised or redacted on the way,
and nothing answers back through the tool — an answer comes by email to the people who hold the
key. Every other tool either reads locally or carries a Proposal your own code wrote.
| whoami | Which tenant your API key is bound to, and until when the key is accepted. |
| explain_policy | Reads a policy tree and says, per action, whether it runs alone, is held, or is refused. |
| simulate_decision | Grades one Proposal against a draft policy tree, offline: the verdict and the clause. |
It approves nothing. There is no approve tool and no simulated approval — a receipt
minted on a developer's laptop is the one artifact this product exists to make impossible.
simulate_decision is not one: it runs the grader, which answers what your rules say and
signs nothing. A PASSED from it is a grade, never permission to act.
It does read, and it runs one binary. check_integration opens the source files under a
path you name and reads them as text; it runs nothing. check_policy_repo opens files under
the path you give it and runs ziffer list and ziffer decide --unsigned — the two keyless, read-only
commands your own pull request workflow runs — with an argument vector and never through a
shell. It runs them so that this package is not a second implementation of your policy rules.
If ziffer is not on your PATH that check reports NOT CHECKED and names it; it never reports
PASS for something it could not look at.
It writes nothing you will ever see. Nothing in this package creates, edits or deletes a
file in your repository, in your policy tree or anywhere else you keep work. The one exception
is a temporary directory: simulate_decision has to hand the CLI a file, because that is
what --proposal takes, so it writes one into a fresh temporary directory and removes it
again.
It does read, and it runs one binary. check_policy_repo, explain_policy and
simulate_decision open files under the path you give them and run ziffer list and ziffer
decide --unsigned — the two keyless, read-only commands your own pull request workflow runs —
with an argument vector and never through a shell. They run them so that this package is not a
second implementation of your policy rules: every verdict you read here came out of the same
binary your CI runs and the same grading fold the gateway runs. If ziffer is not on your PATH
those tools report NOT CHECKED and name it; they never report PASS for something they could not
look at.
Your coding agent edits your code. The verify line in your handler is what gates the action.
Configuration
| Variable | What it is | Where the value comes from |
| --- | --- | --- |
| ZIFFER_API_KEY | Your API key. It carries your tenant, so no request names a tenant. | We issue it. It expires after 90 days unless you ask for another lifetime. |
| ZIFFER_TRUST_ANCHOR | Path to the public key file your receipts are signed under. | We give you the file. Take it from us, never from the API you are checking. |
| ZIFFER_SUITE_FLOOR | The weakest signature suite you will accept. | You choose it. There is no default. |
| ZIFFER_API_URL | The base URL of the ZIFFER deployment you call. | We give it to you with your key. |
Set them in your MCP client's own configuration. The server starts without any of them. A tool
that needs a value names the variable to set. get_started, the two guide tools,
check_integration, lint_proposal, check_policy_repo, explain_publish_failure,
search_docs and explain_refusal need nothing at all, so an agent can read how to integrate,
check the integration it just wrote, validate a proposal, set up your policy repository, debug
its pipeline and look a refusal up before you have a key. send_feedback needs
ZIFFER_API_URL and ZIFFER_API_KEY, because what it sends is attributed to your tenant.
that needs a value names the variable to set. The two guide tools, check_policy_repo,
explain_publish_failure, explain_policy and simulate_decision need nothing at all, so an
agent can read how to integrate, set up your policy repository, debug its pipeline and find out
what a draft rule would decide before you have a key.
What does not work yet
check_policy_repocompares bytes and runs the CLI. It cannot tell you the three files ZIFFER provisions are the right ones for your tenant — only that they are no longer the demonstration tenant's. Your first publish is what checks that.- It cannot see your branch protection rule. That needs a GitHub token with repository administration, this server holds no credential and opens no connection, so it prints the settings for you to confirm and says so.
explain_publish_failurereads the workflow shipped in the template. If you have edited yours, it explains the one we shipped.explain_refusalserves the support table and writes nothing of its own. Since ACP-393 that table covers both halves — the refusals your own SDK and Executor raise, and the names our API and Policy Engine answer with,TenantMismatchandProposalMalformedand a clause like8.4-3. What it gives you is a row a person wrote and dated, not a description generated from the code:tools/check-support-doc.pyholds every row to the source that publishes the name, both ways, and it checks that a row EXISTS and is filled in rather than that its advice is right.search_docsmatches literal terms. It finds the section that uses your words, not the section that means what you meant.search_docsdoes not servedocs/onboarding/journey-sheet-2026-09-15.md. Its reason, exactly as the build pins it: withheld: internal build state. It names tickets, revisions, what has never run in AWS and the open placeholders in a draft agreement, and none of that answers a developer's question. Reversible by one pin: delete its entry in scripts/embed-guide.mjs and the next build serves it.explain_policyandsimulate_decisiongrade with--unsigned, which checks no signature and performs no activation: not the bundle's expiry, not the epoch, not the attester registry, not the two doors, not the receipt identity. A tree can read clean there and still be refused at load by every reader in the deployment. They answer what your rules say.explain_policycannot tell you the tier an undeclared target is graded at. It tells you which targets your examples name thatfloors.jsondoes not declare — and it asks the CLI, with a proposal aimed at a target nothing declares, whether the grader will name a tier. It does not, so that column says NOT CHECKED rather than a number this package worked out.- An action your rules name but
policy/examples/has no proposal for is graded from a synthetic proposal, built from one of your own examples with the action swapped. Its targets and parameters are that example's, so its grade can differ from the grade the same action gets on a proposal you would really send. Every such row sayssyntheticand names the file it came from.
When a request is refused
Every refusal names the rule that fired; the table of every clause, what it means and what to do is at https://ziffer.io/docs/refusals. A refusal is deterministic, so do not retry it.
Documentation
- Quickstart: https://ziffer.io/docs/quickstart
- Integrating the SDK: https://ziffer.io/docs/developers/sdk
- Sandbox tenants: https://ziffer.io/docs/developers/sandbox
- Every refusal: https://ziffer.io/docs/refusals
- Glossary: https://ziffer.io/docs/glossary
Support
Write to [email protected]. Your API key, your trust anchor file and your suite floor come from us. So does an answer about a refusal you cannot explain.
If your agent could not find something, send_feedback puts the question in front of the same
people — it reaches us, and it is how the documentation gets the answer you needed.
License
Proprietary. Copyright (c) 2026 code75 SASU, Paris, France. ZIFFER is a registered trademark of
code75 SASU. This package is not open source. Its use is governed by your agreement with code75
and by LICENSE beside this file. The open-source components it redistributes are listed in
THIRD-PARTY-NOTICES, under their own licences.
