Skip to main content
Glama

zshield-mcp banner

zshield-mcp

Open-source MCP server that exposes one tool, zcash_pay, for agent-initiated shielded ZEC payments under human policy:

  • Hard per-call spending cap

  • Recipient allow-list

  • Append-only viewing-key fingerprinted audit log

Spend keys never belong in the model. Mock chain by default. A live adapter sends real shielded payments on Zcash testnet only through zingo-cli.

Flow: AI agent to zshield-mcp to Zcash testnet

Purpose

Agents need a payment tool they can call safely. This server enforces policy before any payment attempt, records an audit row for every call (success or reject), and fingerprints viewing-key material so auditors can correlate receipts without storing raw keys in the log.

Built as a concrete, minimal artifact for design-partner review of shielded agent-payment MCP surfaces (see Zcash Community Grants discussions around agent/MCP payment tooling).

Related MCP server: cedulon

Demo

Real output of npm run demo:mock (mock chain, no coins spent): one payment allowed, one blocked by the spending cap, one blocked by the allow-list, then the audit log.

Mock demo terminal output

Quickstart

git clone <this-repo> zshield-mcp && cd zshield-mcp
cp .env.example .env
npm install
npm test
npm run build
npm start   # stdio MCP server

Point your MCP client (Cursor, Claude Desktop, etc.) at:

{
  "mcpServers": {
    "zshield": {
      "command": "node",
      "args": ["/absolute/path/to/zshield-mcp/dist/index.js"],
      "env": {
        "SPENDING_CAP_ZEC": "0.1",
        "ALLOWLIST_PATH": "/absolute/path/to/zshield-mcp/allowlists/recipients.example.json",
        "CHAIN_MODE": "mock",
        "AUDIT_DIR": "/absolute/path/to/zshield-mcp/audits"
      }
    }
  }
}

Or during development: "command": "npx", "args": ["tsx", "src/index.ts"] with cwd set to the repo.

Tool: zcash_pay

Arg

Type

Required

Description

amount_zec

number

yes

Must be > 0 and <= SPENDING_CAP_ZEC

recipient

string

yes

Must exactly match an allow-list address

memo

string

no

Optional memo (mock)

Resource zshield://policy returns the active cap, allow-list path, and chain mode.

Config: caps & allow-list

Precedence: environment variables override config/default.yaml.

Variable

Default

Meaning

SPENDING_CAP_ZEC

0.1

Hard max ZEC per tool call

ALLOWLIST_PATH

./allowlists/recipients.example.json

JSON allow-list

CHAIN_MODE

mock

mock or testnet (any other live value exits)

AUDIT_DIR

./audits

JSONL audit directory

VIEW_KEY_FINGERPRINT_SALT

from yaml

Salt for view-key fingerprints

Allow-list shape:

{
  "recipients": [
    { "address": "u1…", "label": "Demo merchant" }
  ]
}

Audit log format

JSONL per day: audits/YYYY-MM-DD.jsonl. Example success line:

{"ts":"2026-10-05T19:00:00.000Z","tool":"zcash_pay","amount_zec":0.05,"recipient":"u1…","allowlist_hit":true,"cap_zec":0.1,"status":"ok","mock_txid":"mock_…","view_key_fingerprint":"a1b2c3d4e5f60718","memo":"test"}

Raw viewing-key material is never written, only a short fingerprint. See docs/AUDIT.md.

Layout

src/policy/   caps + allow-list
src/chain/    mock adapter, testnet adapter, address checks
src/audit/    JSONL writer + fingerprint
src/tools/    zcash_pay
src/server.ts MCP registration
tests/        happy path + rejections

Testnet mode

What it does: with CHAIN_MODE=testnet the server checks cap, allow-list, and address network first, writes the audit row, and only then calls zingo-cli quicksend against a public testnet lightwalletd (default https://testnet.zec.rocks:443). Mainnet addresses (u1, zs1, t1) are rejected; testnet ones (utest, ztestsapling, tm) are accepted. CHAIN_MODE=live alone is refused.

Setup:

# build zingo-cli (Rust)
git clone https://github.com/zingolabs/zingolib && cd zingolib
cargo build --release -p zingo-cli
# create a fresh testnet wallet outside the repo and print its address
zingo-cli --chain testnet --server https://testnet.zec.rocks:443 \
  --data-dir ~/.zshield-testnet/wallet addresses

Fund that address with TAZ from a testnet faucet such as https://faucet.testnet.valargroup.dev (API claim) or https://zcashfaucet.jinolabs.xyz and wait for it to confirm. Keep ~/.zshield-testnet/ private; it holds the wallet seed and is never inside the repo.

Run the MCP server in testnet mode:

CHAIN_MODE=testnet ZINGO_BIN=$(which zingo-cli) \
ZINGO_DATA_DIR=~/.zshield-testnet/wallet \
ALLOWLIST_PATH=./allowlists/testnet.json npm start

Optional variables: ZINGO_BIN, ZINGO_DATA_DIR, LIGHTWALLETD_URL.

Run the demo:

npm run demo:testnet

The demo creates a second fresh testnet wallet as the recipient, writes a one-entry allow-list for it, and sends 0.001 TAZ through executePay, the same policy path the zcash_pay tool uses. It prints the txid and a link on https://blockexplorer.one/zcash/testnet. A confirmed txid proves the full path works end to end: policy checks, audit write, and a real shielded transaction on the Zcash testnet.

Proof: live testnet payment

One real shielded 0.001 TAZ payment through zcash_pay, plus a blocked over-cap attempt: tx 59d9cb1b5a47... on blockexplorer.one

Live testnet demo

Status / non-goals

  • ✅ Mock payments, policy enforcement, audit

  • ✅ Testnet shielded sends via zingo-cli

  • ❌ Mainnet (refused by design)

  • ❌ Custody of user spend keys

License

MIT

Available Tools

1 tool
zcash_payA

Pay shielded ZEC to an allow-listed recipient under a hard per-call spending cap. Mock chain by default; writes a viewing-key-fingerprinted audit log entry.

ParametersJSON Schema
NameRequiredDescriptionDefault
memoNoOptional memo (mock only)
recipientYesRecipient address; must be on the allow-list
amount_zecYesAmount of ZEC to pay (must be <= spending cap)

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full disclosure and does add real behavioral context: it discloses the mock-chain default (important so the agent doesn't assume real funds move), a hard per-call spending cap, and an audit-log side effect keyed by viewing-key fingerprint. It stops short of stating permissions, failure behavior, or reversibility, which is a gap for a payment tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, zero filler, with the core action and its constraints front-loaded before the secondary details (mock default, audit log). Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a payment tool with no annotations and no output schema, the description covers purpose, constraints, default execution mode, and a side effect. It omits error/failure semantics and whether the cap is configurable, but the essentials an agent needs to invoke it are present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all three parameters (memo, recipient, amount_zec) are already documented in the schema, including the allow-list and cap constraints. The description adds no parameter-level syntax or format detail beyond that, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb and resource ('Pay shielded ZEC'), and further narrows scope with two constraints ('allow-listed recipient', 'hard per-call spending cap'). No siblings exist, so no differentiation is required, and the purpose is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Mock chain by default' implies a real-chain mode exists, but the description never states when to use this tool versus other operations, nor any preconditions beyond the allow-list/spending-cap facts already implied by the schema. Usage is inferable but not spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 1 tool updatev0.1.0
    • First observedzcash_pay

TDQS

A3.9/5.0

Scored across 1 tool

Disambiguation5/5

With only one tool, there is no possibility of overlap or misselection between tools. The single tool's purpose—paying shielded ZEC under a spending cap—is singular and unambiguous.

Naming Consistency5/5

zcash_pay follows a clear snake_case resource_action pattern. There are no competing conventions to create inconsistency, so the naming is fully predictable for this server.

Tool Count3/5

A single tool is very thin for a server named zshield-mcp, which implies broader policy, allow-list, and audit functionality. The pay tool itself is well-scoped, but the overall surface feels under-provisioned.

Completeness2/5

The description implies allow-listed recipients, spending caps, and audit logging, yet only the payment action is exposed. There are no tools to inspect the audit log, manage the allow-list, check balances, or query transaction status, leaving significant operational gaps.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to propose wallet payments while a local, human-authored policy decides whether each transaction is approved, requires human confirmation, or is refused, and records every decision in a signed, append-only ledger.
    -
  • A
    license
    A
    quality
    A
    maintenance
    Policy-gated agent spend with signed receipts and rail-extract audit: a fail-closed gate issues a COSE receipt for every allowed payment, and the audit reconciles those receipts against a rail extract so a settlement with no receipt is named rather than assumed. It settles on a mock rail, holding no wallet and signing no transaction.
    5
    1
    Apache 2.0
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables agents to inspect pot balances, deposits, deposit addresses, and spend approvals, and to pay for PaidResources on spark/USDB or send to allowlisted Spark addresses within a per-spend cap. Transactions above the cap become human approval tickets, and the pot key remains in the OS keychain so nothing is signed until approval.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables an AI agent to manage a Bitcoin wallet and request payments through MCP, while every spend requires a per-payment human approval code and defaults to signet.
    MIT