truecopy
This server provides read-only tools for inspecting and verifying a truecopy lock of pinned skills, MCP servers, and files.
Verify pinned entries: re-hash every pinned artifact and report whether it is
ok,drifted,poisoned,untrusted,unsigned, ormissing.Require signatures optionally: set
requireSignedto treat entries lacking a trusted valid signature asunsigned.See verification summary: returns overall
ok, total entries, failed count, and per-entry statuses.List what is pinned: show lock path, total entries, artifact kind (
skill,mcp,file), recorded pin-time verdict, and whether each entry is signed.Operate safely: never re-pins, edits the lock, or fetches anything over the network.
truecopy
The supply-chain gate for agent skills and MCP servers.
68,560 skills poison-scanned · the official Claude Code plugin directory re-scanned every day · deterministic and offline · one runtime dependency · MIT
See it · Quick start · The attack · How it works · What it gates · In CI · Proof · Trust · Docs
An agent runs its tools with your privileges, and it believes what their descriptions tell it. truecopy is the supply-chain gate in front of those tools. It poison-scans a skill or MCP server before you trust it, pins exactly the bytes you vetted into truecopy.lock (signed, if you want to know who vetted them), re-checks every pin on every run and every CI pass, and enforces the lock at runtime. A poisoned tool is refused at the door; a tool you trusted last week that quietly changed is stopped before the agent ever sees it.
See it catch one
That picture is a recording, not a mock-up. scripts/readme/terminal.mjs runs each command with this checkout's CLI in a throwaway directory, against the two manifests in demo/, and refuses to redraw it if any verdict or exit code changes.
Related MCP server: mcpsentry
Quick start
npm i -g @askalf/truecopy # Node 20 or newer
truecopy scan ./mcp-server.json # poison-scan it before you trust it
truecopy add ./mcp-server.json --sign # pin the vetted bytes into truecopy.lock, signed
truecopy verify # re-check every pin; exit 1 on drift or poisonCommit truecopy.lock next to your code, the way you commit package-lock.json. Then choose where it is enforced:
# Claude Code: pin every skill, then check each one at the moment it is invoked
truecopy add --claude --claude-plugins --sign && truecopy hook install
# any MCP client: launch the server through the proxy; only pinned, unchanged tools get through
truecopy-mcp --lock truecopy.lock --name filesystem -- <server command>
# any process: refuse to launch while a pin is bad
truecopy guard -- npm startNo install needed for a one-off: npx -y @askalf/truecopy verify. In a clone, npm run demo plays the whole story offline.
The attack
Agents pull tools from places you do not control: MCP servers, skill marketplaces, a teammate's repo. OpenClaw's poisoned-skills marketplace showed what that costs. Three things go wrong, and truecopy answers each one:
The attack | What it looks like | What truecopy does |
Tool poisoning | A description, schema or |
|
The silent update | A server or skill you vetted last week ships new bytes. No diff you will ever read shows it, and the agent loads it on the next run. | Every pin records a content hash plus one hash per tool or file. |
The wrong signer | Someone substitutes the lock entry, or signs a tampered version with a key of their own. | Ed25519 signatures are checked against your trust set: your machine's own key, the keys you trust globally and the |
How it works
Stage | Command | What happens |
scan | truecopy scan | Deterministic poison detection over everything the model will read. Prose in skills is severity-aware: an instruction flags, a mere mention of a secret path is an advisory. Tool definitions keep the strict any-finding rule. |
pin | truecopy add | The vetted version goes into |
verify | truecopy verify | Every run or CI pass re-hashes and re-scans every pin. Changed bytes are |
enforce | hook · proxy · guard | The runtime gate keeps an unvetted or drifted tool from reaching the agent at all: the Claude Code hook, the |
A lock entry, as committed in this repo's own truecopy.lock:
"filesystem": {
"source": "demo/clean-mcp.json",
"kind": "mcp",
"hash": "a881946cef3fb45d7c78d3297070a179f4c55ba65f00a576ada922c39f4e1f57",
"scannedAt": "2026-07-16T17:25:56.559Z",
"verdict": "clean",
"findings": 0,
"detection": { "engine": "redstamp", "version": "0.4.1" },
"parts": {
"read_file": "cfe9acc21dbc05b0b8baa751e71fe524fd8fa15578d20610fc90b8d533bd8d8f",
"list_directory": "ac606087a4a47e23d801beb12699d87364430677f04b6709eacbaec062156227",
"(manifest)": "cac4676bf307456c45dfa470fa0b350b6b305b0d3cb86ffc3b68396625261b46"
}
}parts is why a failure can name ~read_file instead of just saying something changed: one hash per tool for an MCP server, one per file for a skill, and one for the manifest's own fields, so a swapped launch command is caught too. Paths in the lock are separator-independent, so a lock committed on one OS verifies on another.
Status | Meaning | Fails? |
| Same bytes as the pin, still scans clean (or carries findings you accepted for exactly these bytes with | no |
| The bytes changed since the pin. Lists the tools or files added, removed or changed. | yes |
| Same bytes, but they now scan poisoned: detection improved after you pinned. | yes |
| A valid signature from a key outside your trust set. | yes |
| An entry recorded as signed whose signature no longer checks; or, under | yes |
| The pinned source cannot be read. | yes |
Exit codes: 0 when every pin is ok, 1 on any failure. The Claude Code hook blocks with exit 2 and tells the model why. scan, verify, list and diff take --json with the same exit codes.
What it gates
Surface | Checked | A bad pin |
Claude Code skillsproject, user and marketplace pluginshook install | The moment a skill is invoked, against the exact directory about to run | Blocked, and the model is told the skill drifted or turned poisonous. --strict makes the lock a whitelist that fails closed. More |
MCP serversany client that launches stdio serverstruecopy-mcp | Every tools/list response, and every call against it | Stripped from tools/list, and a call to it errors. --strict blocks the whole server. More |
Process launchguard -- <cmd> | Before the command starts | The command never starts; exit 1. |
CIaskalf/truecopy-action@v1 | Every workflow run | The build fails. In CI |
Plugins already installedcheck-manifest | On demand, against the daily watch | Fails when an installed skill's bytes differ from what the watch vetted, or the watch flagged it. More |
A marketplace before you installscan --marketplace | On demand, against your clone | Every flagged skill is listed; exit 1. |
What you can pin: an MCP manifest (a .json holding the server's tools array, the shape tools/list returns; its other top-level fields, such as the launch command and args, are hashed and scanned too), a skill directory (SKILL.md plus its files, hashed per file), or any single file.
In CI
One line from the GitHub Marketplace verifies the committed lock and fails the build on drift or poison:
- uses: askalf/truecopy-action@v1Or scan a marketplace or manifest with no lock at all:
- uses: askalf/truecopy-action@v1
with:
command: scan
marketplace: ./the-repo-you-clonedWhere trust matters, require a signature from a publisher you trust, and keep the one private signing key in a single CI secret instead of on every laptop:
# every pin must carry a valid signature from a key in the runner's trust set
- run: npx -y @askalf/truecopy verify --require-signed
# sign with the CI secret: no key file and no keychain on the runner
- run: npx -y @askalf/truecopy add ./mcp-server.json --sign
env:
CANON_SIGNING_KEY: ${{ secrets.CANON_SIGNING_KEY }}On a workstation the trust set is your machine's own key, the keys you trust globally and the repo's truecopy.trust. A clean runner has no key and no global store, so there it is truecopy.trust alone: commit every publisher's public key to it, the public half of CANON_SIGNING_KEY included. That key signs but is never trusted just for being set.
Publisher keys, the committed truecopy.trust, JSON reports and the signing setup: docs/signing-and-ci.md. This repo runs the same gate on itself in truecopy-gate.yml.
Proven at ecosystem scale
Corpus | Skills | Result |
The official Claude Code plugin directory plus nine community marketplaces | 2,019 | 0 poisoned. Tuning detection against this corpus took the flag rate from 126 to 12 (0.6%), and every one of the 12 was benign on manual review. |
ClawHub, the registry whose poisoning incident started the category | 66,541 | 0 confirmed malicious. 813 deterministic alarms, every one benign on cross-check against ClawHub's own scanner. |
The official directory, re-scanned every day | 2,471 | The 2026-10-29 run covered 315 plugins: 0 under review, 463 advisories. The watch badge above always carries the latest run. |
The daily watch fetches every catalog plugin, including vendor plugins at their catalog-pinned commits, and publishes each snapshot to WATCH.md and the live observatory at truecopy.sprayberrylabs.com. Each run also publishes a directory-manifest.json of the exact bytes it vetted, so truecopy check-manifest can hold the plugins on your own machine to the same standard.
Trust and transparency
A supply-chain gate has to survive the scrutiny it applies to others.
Promise | How it holds |
Offline | No network calls and no telemetry. You clone or download; truecopy only reads. |
Deterministic | No model in the loop. The same bytes get the same verdict on every machine, every time. |
One runtime dependency |
|
Releases | Published to npm only from CI, through trusted publishing (OIDC) with provenance. Check yours with |
Eats its own cooking | This repo pins its demo manifest in its own |
Tested | Node 20, 22 and 24 on Linux and Node 22 on Windows, plus the packed npm tarball on both. Every row of the Claude Code policy table was also verified live, each in its own fresh Claude Code session. |
Hardened | Jazzer.js fuzz targets for canonical JSON, the scanner and the MCP gate; CodeQL; OpenSSF Scorecard; OpenSSF Best Practices passing. |
What truecopy is not. It is not a sandbox: once a vetted tool runs, containing what that call does is redstamp's job. A clean scan means no known poisoning shape was found, not that a tool is safe; the pin is what makes your review last. For an MCP server, truecopy pins the surface the model reads (tool names, descriptions and schemas, plus any manifest fields you include, such as the launch command), not the server's code, so pin the server's own version with your package manager as well. Found a gap? Report it privately.
Does it send anything anywhere? No. There is no network code in the CLI. Even the daily watch manifest is something you download yourself and point truecopy at.
Does it use an LLM to judge skills? No. Detection is deterministic, shared with redstamp, and every finding can be reproduced from the bytes.
What about a false positive? Read the skill, then pin it with --force: the findings are recorded as accepted for exactly those bytes, shown as · accepted findings (--force pin), and any change re-flags it. Please also report it so detection improves for everyone.
Can I use it from code? Yes: import { scan, pin, verify, diff } from '@askalf/truecopy'. See docs/commands.md.
Remote (HTTP) MCP servers? truecopy-mcp gates stdio servers. For a remote server, pin a manifest of its tools/list and verify it in CI.
Windows? Supported and tested. In Git Bash, MSYS rewrites Unix-looking path arguments before truecopy sees them; see the Windows note.
Why is the signing key variable called CANON_SIGNING_KEY? truecopy was called canon before its npm release. The repo redirects, the canon and canon-mcp commands still work, an existing canon.lock or canon.trust is read automatically, and the CANON_* environment variables keep their names so existing setups keep working.
Docs
Guide | Covers |
Every command, pinned installs, what you can pin, the library API | |
| |
| |
The lock format, publisher trust, | |
The daily scan and |
Also: Changelog · Contributing · Security policy
The agent-security stack
Three layers, one defense, each usable on its own:
Project | Layer | Answers |
the call | Is this action safe to run right now? | |
truecopy (you are here) | the tool | Is this the exact skill or server I vetted? |
the trajectory | Did the whole sequence of actions stay on the declared job? |
truecopy and redstamp share one detection engine, so they are a pair, not a duplicate: truecopy vets the tool, redstamp contains the call. plumbline watches from above, out of band and read-only, and catches an escape assembled from steps that were each allowed.
Available Tools
2 toolstruecopy-statusDescribe what this lock pinsARead-onlyIdempotent
List what this truecopy lock pins, without re-verifying it. Returns each entry with the kind of artifact (skill, mcp, or file), the scan verdict recorded at pin time, and whether it carries a signature. Use this to see the vetted set at a glance — what an agent is permitted to run — or to confirm the gate is reading the lock you expect. For whether those bytes are still unchanged, call truecopy-verify instead. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| total | Yes | Number of pinned entries. |
| entries | Yes | |
| lockPath | Yes | Path of the lock this gate was configured with. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false. Description adds value by stating 'without re-verifying it' and 'Read-only', and describes return structure, which annotations do not cover.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences, each providing distinct value: what it does, what it returns, when to use, when not. No redundancy, front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter tool with an output schema (not shown but present), the description fully covers purpose, return fields, and usage guidance. No gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Tool has zero parameters, so no parameter documentation needed. Schema description coverage is 100%. Baseline 4 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states the verb 'List' and resource 'truecopy lock pins', and explicitly details the returned fields (kind, verdict, signature). It distinguishes from sibling tool 'truecopy-verify'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use ('see the vetted set', 'confirm the gate is reading the lock you expect') and when not ('for whether those bytes are still unchanged, call truecopy-verify instead'), with direct reference to alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
truecopy-verifyVerify pinned tools against the lockARead-onlyIdempotent
Re-derive the hash of every skill and MCP server pinned in this truecopy lock and report whether each still matches what was vetted. Use this before trusting a tool surface, or to answer "has anything changed underneath me?". Each entry comes back as one of: ok (bytes identical to what was pinned), drifted (content changed since it was vetted), poisoned (re-scan found an injection or exfiltration pattern), untrusted (signed by a key that is not in the trust store), unsigned (a signature was required but is absent), or missing (the pinned source is no longer on disk). Read-only: it never re-pins, never edits the lock, and never fetches anything over the network.
| Name | Required | Description | Default |
|---|---|---|---|
| requireSigned | No | When true, an entry that verifies but carries no valid signature from a trusted key is reported as "unsigned" rather than "ok". |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | True only if every pinned entry verified cleanly. |
| total | Yes | Number of entries in the lock. |
| failed | Yes | Number of entries that did not verify. |
| results | Yes | Per-entry verification result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate readOnlyHint=true, idempotentHint=true, and destructiveHint=false. The description adds key behavioral details: 'Read-only: it never re-pins, never edits the lock, and never fetches anything over the network.' This reinforces the safety profile and aligns perfectly with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise at two sentences, with no wasted words. It front-loads the primary action and then provides a succinct list of outcomes. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's low complexity (1 optional parameter, no required params, high schema coverage), the description covers all essential aspects: purpose, usage context, behavior, and output interpretation. It is complete for an agent to correctly select and invoke this tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema documents the 'requireSigned' parameter well. The description does not add new semantic information about the parameter beyond listing possible output states (which relate to behavior, not parameter meaning). Thus, baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses specific verbs ('re-derive', 'report') and resource ('every skill and MCP server pinned in this truecopy lock'), and clearly distinguishes from the sibling tool 'truecopy-status' by focusing on verification vs general status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states 'Use this before trusting a tool surface' and provides a use case ('has anything changed underneath me?'). While it doesn't include when-not-to-use or alternative tools, the guidance is clear and actionable.
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.
13 tool updates
v0.10.0- Removed
get-annotated-message - Removed
get-env - Removed
get-resource-links - Removed
get-resource-reference - Removed
get-structured-content - Removed
get-sum - Removed
get-tiny-image - Removed
gzip-file-as-resource - Removed
simulate-research-query - Removed
toggle-subscriber-updates - Removed
trigger-long-running-operation - Added
truecopy-status - Added
truecopy-verify
9 tool updates
v0.9.0- Removed
echo - Added
get-env - Added
get-resource-links - Added
get-resource-reference - Added
get-structured-content - Added
get-sum - Added
get-tiny-image - Added
gzip-file-as-resource - Added
simulate-research-query
9 tool updates
v0.9.0- Removed
get-env - Removed
get-resource-links - Removed
get-resource-reference - Removed
get-structured-content - Removed
get-sum - Removed
get-tiny-image - Removed
gzip-file-as-resource - Removed
simulate-research-query - Removed
toggle-simulated-logging
13 tool updates
v0.1.0- First observed
echo - First observed
get-annotated-message - First observed
get-env - First observed
get-resource-links - First observed
get-resource-reference - First observed
get-structured-content - First observed
get-sum - First observed
get-tiny-image - First observed
gzip-file-as-resource - First observed
simulate-research-query - First observed
toggle-simulated-logging - First observed
toggle-subscriber-updates - First observed
trigger-long-running-operation
TDQS
Scored across 2 tools
The two tools have completely distinct purposes: one verifies integrity of pinned items, the other lists them without verification. No overlap or ambiguity.
Both tools follow a consistent 'truecopy-' prefix with a verb-noun pattern (truecopy-verify, truecopy-status).
With only 2 tools, the server is tightly scoped to its read-only verification and listing purpose. No unnecessary tools.
The server covers the full lifecycle of reading and verifying a truecopy lock. It explicitly omits write operations, which is appropriate given its read-only nature.
Maintenance
Related MCP Connectors
Scan any MCP server for tool-poisoning, security, auth & license. Trust score before install.
Security scanner for MCP servers. Detect vulnerabilities, prompt injection, and tool poisoning.
Security & DLP proxy for MCP: tool-poisoning scans, PII redaction on tool args/results. Beta.
Scan any MCP server for tool-poisoning/prompt-injection/exfiltration (OWASP LLM01/08) + Base data
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceA local-first stdio proxy for MCP servers that traps destructive tool calls and holds them for manual commit or discard, preventing accidental mutations.AGPL 3.0
- AlicenseNot gradedqualityBmaintenanceSecurity scanner and runtime proxy for MCP servers. Catches tool poisoning, prompt injection, and rug-pull attacks (silent tool description changes) before they reach your AI agent. Includes a static scanner and a runtime stdio proxy.59 npmMIT
- AlicenseAqualityAmaintenanceMCP security firewall (stdio): vet advertised tool definitions through static-scan → threat-feed → origin → pinning before they reach the model. Zero npm runtime deps. No secrets.91,488 npmMIT
- AlicenseAqualityAmaintenanceA local stdio proxy that pins every MCP tool definition when you approve a server and blocks the session with a diff if a description, schema or annotation changes later. A deterministic hash compare (RFC 8785 + SHA-256), with no model in the loop.485 npm4MIT