@cubiczan/chp-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@@cubiczan/chp-mcp Check if a $300 ETH long with 0.9 confidence passes the spend gate policy."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
@cubiczan/chp-mcp
One-command MCP install for CHP Profile B spend / capital gates and tool-approval receipts (an allowlist is not authorization), plus a structured deny ledger and receipt-gated finance tools.
Wraps @cubiczan/chp so Cursor,
Claude Code, or any MCP client can call evaluate_spend_gate without vendoring
protocol code. Engine digests match the normative golden vectors
(Profile B 30/30).
How the pieces fit
MCP client (Cursor / Claude / …)
│ tools/call
▼
┌───────────────────────────┐
│ MCP server (transport) │ ← you are here (@cubiczan/chp-mcp)
│ evaluate_spend_gate │
│ approve_spend │
│ evaluate_tool_approval │ allowlist ≠ authorization
│ issue_approval_receipt │
│ authorize_tool_call │
│ request_authorization │ finance-tool receipt / HITL / deny
│ place_equity_order │ scoped + receipt-gated (synthetic)
│ wire_treasury_transfer │
│ rebalance_portfolio │
│ inspect_audit_ledger │ CHP-signed deny / authorize / execute
│ chp_content_hash │
└─────────────┬─────────────┘
│ depends on
▼
┌───────────────────────────┐
│ Published CHP packages │
│ npm: @cubiczan/chp (Profile B)
│ PyPI: consensus-hardening-protocol (Profile A)
└───────────────────────────┘For AGENTS.md + skills + Profile A decision_gate / decision_adversary, use
agent-conductor instead.
Related MCP server: SentinelMCP
Install
npm install -g @cubiczan/chp-mcp
# or one-shot
npx -y @cubiczan/chp-mcpCursor / Claude Desktop
{
"mcpServers": {
"chp": {
"command": "npx",
"args": ["-y", "@cubiczan/chp-mcp"]
}
}
}Claude Code
claude mcp add chp -- npx -y @cubiczan/chp-mcpTools
Tool | Maps to | Purpose |
|
| LOCKED / HITL_REQUIRED / BLOCKED + claims + content hash. |
|
| Human lock when HITL_REQUIRED (cannot override hard fails). Optional |
|
| Allowlist is a pre-filter; host-bound fields merge into |
|
| Human allow/deny → HMAC-signed receipt + decision log |
|
| Consume a receipt; deny on drift, host-bound override, expiry, replay, or a bad MAC |
| runtime | Mint a receipt bound to a scoped reference tool, or return HITL / structured deny |
| reference | Synthetic equity order — scope |
| reference | Synthetic treasury wire — scope |
| reference | Synthetic rebalance — scope |
| ledger | Trailing CHP-chained deny / authorize / execute entries |
|
| Float-aware canonical SHA-256 |
| — | Server + protocol versions + deny reason codes + receipt schema |
Example — evaluate a spend
// tools/call evaluate_spend_gate
{
"action": { "action": "LONG", "asset": "ETH", "notional": 300, "confidence": 0.9 },
"policy": {
"max_notional": 500,
"daily_cap": 2500,
"hitl_threshold": 250,
"min_confidence": 0.55,
"allowed_actions": ["LONG", "SHORT"]
}
}Cookbook — Claude / Cursor tool approval
Managed MCP allowlists (Cursor mcpServers, Claude Desktop, Claude Code)
only answer “is this tool name installed?”. They do not bind tenant,
arguments, risk, or a human decision. This server treats that gap as a
hard deny unless a signed approval receipt still matches the call
that is about to run.
Receipts are HMAC-SHA256 over CHP canonical JSON
(the same payload discipline as Profile B contentHash / audit-ledger
sig). The MAC covers:
Field | Role |
| Human who allowed or denied |
| Concrete tool name (no |
| Tenant / resource binding (no |
|
|
| Policy the human saw |
| Policy risk for that tool |
| Lifetime |
|
|
| Single-use; replay denies |
| HMAC-SHA256 hex |
Set CHP_RECEIPT_KEY (or AUDIT_LEDGER_KEY) in the MCP server env.
Without it the process falls back to a documented insecure default —
fine for the local cookbook, not for production.
Example policy: examples/tool-approval-policy.json.
stripe.create_charge is on the allowlist and still cannot run
without a receipt bound to acct_live_acme and the exact charge args.
Host-injected tenant/index bindings use
examples/host-injected-policy.json
(see the host-injected args cookbook below).
{
"mcpServers": {
"chp": {
"command": "npx",
"args": ["-y", "@cubiczan/chp-mcp"],
"env": { "CHP_RECEIPT_KEY": "replace-me" }
}
}
}1. Allowlist alone — denied
Claude/Cursor has stripe.create_charge enabled. That is not a grant.
// tools/call evaluate_tool_approval
{
"call": {
"tool": "stripe.create_charge",
"resource": "acct_live_acme",
"arguments": { "amount": 2500, "currency": "usd", "customer": "cus_123" }
},
"policy": { "$ref": "examples/tool-approval-policy.json" }
}Result: RECEIPT_REQUIRED, deny_code: "allowlist_is_not_authorization".
Calling authorize_tool_call with the same payload and no receipt
returns DENIED / allowlist_is_not_authorization.
2. Human allow — then authorize
// tools/call issue_approval_receipt
{
"actor": "cfo@acme.example",
"decision": "allow",
"reason": "invoice INV-104 matches amount",
"ttl_seconds": 120,
"call": { /* same as above */ },
"policy": { /* same as above */ }
}The response includes receipt (take the whole object) and
decision_log (actor, decision, args hash, nonce). Pass that receipt
into authorize_tool_call with the same call. Result: AUTHORIZED.
3. Human deny
Issue with "decision": "deny". The decision is logged. Authorizing
with that receipt returns DENIED / human_denied. A deny receipt
cannot be flipped to allow by editing decision — the MAC breaks.
4. Changed arguments after approval — denied
Approve { "amount": 2500, ... }, then authorize with
{ "amount": 2500000, ... }. Result: DENIED / changed_arguments.
Key order does not matter; the hash is CHP canonical. The original
receipt remains valid for the args that were approved (until expiry or
a successful consume).
5. Expired receipt — denied
Issue with ttl_seconds: 30. After the expiry instant,
authorize_tool_call returns DENIED / expired_receipt. The nonce is
consumed so a clock rewind cannot resurrect it.
6. Replayed receipt — denied
A successful AUTHORIZED consume burns the nonce. Presenting the same
receipt again returns DENIED / replayed_receipt.
7. Ambiguity — denied
These never produce a usable allow receipt:
resource: "*",any,all, or an empty stringmissing
argumentspolicy without a concrete
versionactor / tool wildcards
extra keys on a receipt (strict parse)
Fail-closed: deny_on_ambiguity cannot be turned off.
Cookbook — host-injected args + gateway _meta
Semantic Kernel and other hosts need to pass index, key, and tenant
without letting the model choose them
(SO-style routing).
Putting those fields on the tool schema so the LLM can “decide” is the
bug. An MCP allowlist does not fix it: the tool name can stay
allowlisted while the model swaps index_name to another tenant.
The host (or a gateway in front of this server) injects bound fields. This package hashes host ∪ model arguments into the receipt and denies when the model overrides a host-bound field. The allowlist is still only a pre-filter.
Contract — _meta.cubiczan (no hard dependency)
@cubiczan/governed-mcp-gateway
already injects identity on every tools/call and SSE frame:
{
"_meta": {
"cubiczan": {
"principal": {
"id": "agt_search",
"kind": "agent",
"orgId": "org_acme",
"displayName": "Search Runner"
}
}
}
}This server does not import that package. It reads the same
envelope. Hosts MAY add host_bound next to principal. A trusted
gateway should overwrite _meta.cubiczan so the model cannot self-attest.
{
"_meta": {
"cubiczan": {
"principal": { "id": "agt_search", "kind": "agent", "orgId": "org_acme" },
"host_bound": { "tenant_id": "acme", "index_name": "prod-docs" }
}
}
}Library callers can also pass host_bound on the proposed call
(explicit keys overlay _meta). Policy
examples/host-injected-policy.json
declares host_bound_fields so index_name and tenant_id must be
host-injected and concrete. If tenant_id is declared and omitted,
_meta.cubiczan.principal.orgId may fill it.
model args ──┐
├─ override check ─→ deny host_bound_override
host_bound ──┘ │
▼
merged args → args_hash → receipt MAC
│
allowlist ──── pre-filter only (never a grant)1. Host injects index + tenant — allowlist still denied
The model chose query / top_k. The host chose the index.
// tools/call evaluate_tool_approval
{
"call": {
"tool": "search.azure_ai",
"resource": "tenant:acme",
"arguments": { "query": "Q3 revenue", "top_k": 5 },
"_meta": {
"cubiczan": {
"principal": { "id": "agt_search", "kind": "agent", "orgId": "org_acme" },
"host_bound": { "tenant_id": "acme", "index_name": "prod-docs" }
}
}
},
"policy": { "$ref": "examples/host-injected-policy.json" }
}Result: RECEIPT_REQUIRED, deny_code: "allowlist_is_not_authorization".
args_hash is contentHash of
{ query, top_k, tenant_id, index_name } — not the model object alone.
2. Model changes a host-bound field — denied
Same host _meta, but the model adds "index_name": "other-index".
"arguments": { "query": "Q3 revenue", "top_k": 5, "index_name": "other-index" }evaluate_tool_approval, issue_approval_receipt (decision: "allow"),
and authorize_tool_call all return DENIED /
host_bound_override. Matching the host value is not an override.
3. Human allow — then authorize the merged args
Issue a receipt for the host-injected call. Authorize with the same
arguments and _meta. Result: AUTHORIZED. Change query after
approve → changed_arguments. Change index_name in model args →
host_bound_override. Omit declared host fields → ambiguous.
4. Semantic Kernel / host wiring
Do the routing in the host, not the model: disable auto-invoke, then
inject index/tenant (or put a gateway in front that writes
_meta.cubiczan.host_bound) before evaluate_tool_approval /
authorize_tool_call. Secrets such as API keys belong in the host or
the gateway vault — not in the tool schema the LLM sees.
Cookbook — deny telemetry and receipts
MCP denials are usually a bare error string. That string is gone when the client disconnects. This server treats a refuse as a structured event that must hit a CHP-signed ledger before the caller sees it.
Finance tools (place_equity_order, wire_treasury_transfer,
rebalance_portfolio) are synthetic — no live venue or bank rail — and
use a separate kind: "authorization" receipt bound to tool, scope, and
args hash. That is not the same object as a chp.tool_approval_receipt.
Reason codes
Code | When |
| Hard CHP rule failed ( |
| Receipt |
| Receipt already consumed by a successful execute |
| Tool, scope, or args hash no longer matches the receipt |
| No receipt, or the content hash does not verify |
| Unknown tool, scope mismatch, or incomplete policy |
Signing is the existing Profile B primitives: contentHash on the
receipt / ledger payload, chainHash between ledger rows. Set
CHP_AUDIT_LEDGER to a JSONL path (default ./data/chp-audit.jsonl),
or :memory: for tests.
1. Request a bound receipt
Under the HITL threshold the gate auto-locks and mints a receipt. At or
above it, pass approver (or call approve_spend with tool +
bound_args).
// tools/call request_authorization
{
"tool": "place_equity_order",
"args": {
"symbol": "AAPL",
"side": "BUY",
"quantity": 10,
"notional": 300,
"confidence": 0.9
},
"approver": "cfo@example.com"
}Treasury wires use hitl_threshold: 0. A request without approver
returns HITL_REQUIRED and no receipt — that is the approval gate,
not a weather-API demo.
2. Execute only with that receipt
receipt is optional on the wire so a missing token is a logged
missing_receipt deny, not a schema 400 that never hits the ledger.
// tools/call place_equity_order
{
"symbol": "AAPL",
"side": "BUY",
"quantity": 10,
"notional": 300,
"confidence": 0.9,
"receipt": { "kind": "authorization", "receipt_id": "…", "content_hash": "…" }
}Change notional or quantity after approve → args_changed, and the
ledger has the deny. Call again with the same receipt → replay.
Call with no receipt → missing_receipt. All three are durable.
3. Inspect the chain
// tools/call inspect_audit_ledger
{ "limit": 20 }Each row carries content_hash and sig = chainHash(prev_sig, { seq, ts, event, content_hash }).
chain.ok is false if anyone rewrote history.
Tests
npm testThis Cubiczan mirror may omit GitHub Actions; run the suite locally.
npm test builds, then runs node --test dist/*.test.js (approval
receipts + host-injected bindings) and
node --import tsx --test test/**/*.test.ts (deny ledger). Invariants
covered: an unlogged deny is impossible (ledger failure throws instead
of returning a deny object); changed args after approve deny; a receipt
is required for every gated reference tool; allowlist is not
authorization; host-bound tenant/index cannot be overridden by the
model; receipt args_hash covers host ∪ model args.
Related
Package / repo | Role |
Profile B library (this server’s dependency) | |
Profile A + normative spec | |
Full MCP: contracts, skills, Profile A gates | |
HTTP MCP control plane | |
Codebase health MCP | |
Shared retry / timeout / audit primitives |
Licence
MIT.
Available Tools
12 toolsapprove_spendA
Human-in-the-loop approval for a proposal that returned HITL_REQUIRED. Cannot approve BLOCKED / hard-rule failures (spec §6.3 / §6.5). When tool + bound_args are supplied, mints a signed authorization receipt.
Inputs: action (required), policy (required), approver (required), tool, +4 more.
authorize_tool_callA
Authorize a tool call against a previously issued receipt. Changed arguments (including host-bound tenant/index), expired or replayed receipts, MAC failure, host-bound override, and binding mismatch all deny. Presenting only an allowlist match denies with allowlist_is_not_authorization.
Inputs: call (required), policy (required), receipt, signing_key.
chp_content_hashA
SHA-256 over float-aware canonical JSON (CHP §3.1) — matches Python consensus-hardening-protocol digests for the same object.
Inputs: value.
chp_versionA
Report MCP server and CHP Profile B protocol versions.
Inputs: no named parameters.
evaluate_spend_gateB
Run CHP Profile B capital/spend gate on a proposed action. Returns LOCKED, HITL_REQUIRED, or BLOCKED with claims and a content hash. Hard policy violations cannot be overridden by a human. BLOCKED is also written as a structured policy_deny to the CHP-signed audit ledger.
Inputs: action (required), policy (required), committed_today.
evaluate_tool_approvalA
Evaluate a proposed MCP tool call. A managed allowlist is not a grant — allowlisted tools still return RECEIPT_REQUIRED. Host-injected fields (host_bound / _meta.cubiczan.host_bound) are merged into args_hash; the model cannot override them. Wildcards, missing resource, or unparseable arguments deny on ambiguity.
Inputs: call (required), policy (required).
inspect_audit_ledgerA
Read the CHP-signed deny / authorize / execute ledger and verify the chain. Also lists the synthetic scoped reference tools.
Inputs: limit.
issue_approval_receiptA
Record a human allow/deny and return a signed approval receipt. The MAC covers actor, tool, resource, args hash (host ∪ model), policy version, risk, expiry, decision, and nonce (HMAC-SHA256 over CHP canonical JSON). Signing key from CHP_RECEIPT_KEY / AUDIT_LEDGER_KEY, or the documented insecure default.
Inputs: call (required), actor (required), policy (required), decision (required), +3 more.
place_equity_orderB
Synthetic equity order (scope trading:equities:place). No live venue. Requires a signed authorization receipt bound to these args. Receipt is optional on the wire so a missing receipt becomes a logged missing_receipt deny.
Inputs: side (required), symbol (required), notional (required), quantity (required), +4 more.
rebalance_portfolioB
Synthetic portfolio rebalance (scope portfolio:rebalance). No live desk. Requires a signed authorization receipt; HITL at/above $1,000 notional.
Inputs: notional (required), portfolio_id (required), receipt, confidence, +1 more.
request_authorizationC
Request a signed authorization receipt for a scoped reference tool (place_equity_order, wire_treasury_transfer, rebalance_portfolio). Auto-lock mints a receipt; HITL_REQUIRED waits for approver; hard fails return a structured deny that is already on the audit ledger.
Inputs: args (required), tool (required), scope, policy, +3 more.
wire_treasury_transferB
Synthetic treasury wire (scope treasury:wire). Default policy always requires a human-issued receipt. No live bank rail. Missing receipt is a logged deny, not a bare MCP error string.
Inputs: amount (required), to_account (required), from_account (required), memo, +3 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 12 tools
The authorization lifecycle is split across several tools with fuzzy boundaries: evaluate_spend_gate and evaluate_tool_approval both evaluate proposed actions, while approve_spend, issue_approval_receipt, and request_authorization can all produce signed receipts. Detailed descriptions help, but an agent selecting by name alone will struggle to identify the correct phase.
Most tools follow a clear lowercase verb_noun pattern such as evaluate_spend_gate, place_equity_order, inspect_audit_ledger, and authorize_tool_call. The only deviations are the two chp_* utility tools, chp_content_hash and chp_version, which are consistently prefixed but not verb-led.
Twelve tools is within the ideal range for a policy/authorization MCP and the count is not bloated. However, there is some redundancy in the approval and receipt-issuance tools, so a couple of tools could be consolidated without losing functionality.
The core flow is covered end-to-end: evaluate policy, request/approve authorization, issue and verify receipts, execute scoped reference tools, and inspect the audit ledger. Missing pieces are mostly non-critical, such as explicit receipt revocation or allowlist management, but agents can work around these.
Related MCP Connectors
Governed MCP: agent audit, provenance, deterministic checks, and receipt-backed FragGate execution.
Find, vet, and run MCP tools through a secure audited gateway with prompt-injection risk scoring
Authorize AI agent tool calls before they run: policy-as-code, human approval, signed receipts.
Dispatch-boundary attestation: prove a tool call was issued for its exact arguments.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceSelf-hosted MCP gateway that applies deterministic, compiled policy to tool discovery, invocation, and outbound data flow, with no model in the enforcement path. Every decision emits a hash-chained receipt sealed with Ed25519 and verifiable using public keys only.Apache 2.0
- AlicenseNot gradedqualityDmaintenanceEnforces deterministic security policies as an inline firewall for MCP server tool calls, with AST-based validation, cryptographic audit logging, and CLI-based evaluation and verification.MIT
- FlicenseNot gradedqualityBmaintenanceEnables secure MCP tool execution through a gateway that validates tool metadata, permissions, and integrity, with approval workflows and optional onchain registry verification.-
- AlicenseNot gradedqualityBmaintenanceProvides a governed execution boundary for AI agents, enforcing deterministic policy, one-shot human approvals, and signed receipts for MCP tool effects.MIT