warden
You can use this MCP server as a security firewall that vets MCP server tool definitions before they reach an LLM, then records an allow/block verdict.
vet_mcp_server — run the full gate chain (static-scan → threat-feed → origin → pinning) over a server identity and its tools/list, returning allow/block, a 0..1 score, findings, allowed/blocked tools, and a ruleset digest.
static_scan_tools — run only the static-scan gate to detect prompt injection, exfiltration, credential requests, and hidden-Unicode/base64 tells in tool names, descriptions, and input schemas.
classify_sensitive_tools — split tools into sensitive vs safe using operator glob patterns for per-call approval previews.
check_egress_url — check whether a URL hostname matches an operator allowlist; empty allowlist blocks everything (fail-closed).
canonicalize_json — produce RFC 8785 canonical JSON bytes for signed threat feeds, tool-def pins, or byte-comparison testing.
list_scan_rules — list the in-force static-scan rule table: version, digest, rule codes, severities, tiers, surfaces, guards, and optionally regex sources.
Everything is local, deterministic, and offline — no API keys, no proxy, no sandbox; the only optional network call is the signed threat feed.
🔄 Synced from a monorepo — but with a live history.
wardenmirrors the canonical AI-Factory monorepo. History here is append-only (no force-push). Pull requests are welcome — merged PRs are imported back into the monorepo and re-synced here, so your contribution becomes canonical. 💬 Issues · Pull requests both welcome.
WARDEN — MCP server
One MCP server. Security firewall for advertised tool definitions. Library included.
Transport: stdio (npx -y @aimarket/warden / node dist/mcp-server.js). Compatible hosts:
Claude Desktop, Cursor, Glama, and any MCP client that speaks stdio. No API keys.
Item | Location |
MCP entrypoint (stdio) |
|
Tools |
|
Library |
|
Glama / Docker (stdio) | |
Official MCP Registry |
|
Smithery |
An MCP server tells your agent what its tools do. The agent believes it — that sentence is the
attack surface. A tool description is prompt text delivered by a third party straight into your
model's context, and a schema field named api_key is a request for your secrets phrased as an API.
WARDEN vets a server before any of its tools reach the model, and returns a verdict you can record: allow/block, a 0..1 score, the findings that produced it, a per-tool partition, and the exact rule table that was in force.
Zero npm runtime dependencies. The library's only import is node:crypto. The stdio MCP
server adds other node: builtins (fs, path, process) and still pulls in no packages. It is
the firewall out of ARGUS, extracted so you can put it in front
of your own MCP host without adopting an agent.
Run as MCP server (stdio)
npx -y @aimarket/warden # bin: warden-mcp
# from this repo:
npm run build && node dist/mcp-server.jsClaude Desktop / Cursor (mcpServers entry):
{
"mcpServers": {
"warden": {
"command": "npx",
"args": ["-y", "@aimarket/warden"]
}
}
}The process never starts, proxies, or sandboxes another MCP server — you pass a tools/list dump
in, you get a verdict out.
Tool | When to use |
| Full gate chain on a server identity + advertised tools |
| Injection / exfil scan only (no origin / pinning / threat feed) |
| Operator glob split — not an injection scan |
| Hostname allowlist (empty list denies every host) |
| RFC 8785 bytes for feeds and pins |
| Published rule table + digest |
Glama TDQS: MCP annotations (readOnly / destructive / idempotent / openWorld), when-to-use /
when-not naming siblings, every inputSchema property described, outputSchema on every tool.
Publish on Glama
Listing: glama.ai/mcp/servers/alexar76/warden · quality score: glama.ai/mcp/servers/alexar76/warden/score
Same pattern as ARGUS and
aimarket-mcp: repo-root glama.json +
Dockerfile + node dist/mcp-server.js. Admin form values: docs/GLAMA.md.
Related MCP server: truecopy
Library (embed in your host)
npm install @aimarket/wardenimport { Warden, ThreatFeed, silentLogger } from "@aimarket/warden";
const threatFeed = new ThreatFeed({ feedPublicKey: process.env.FEED_PUBKEY });
await threatFeed.load(process.env.FEED_URL); // omit → built-in deny-list only, no network
const pins = new Map();
const warden = Warden.create({
policy: {
blockAtSeverity: "high",
sensitiveToolPatterns: ["*delete*", "*transfer*", "*key*"],
allowUnknownServers: false, // fail-closed: only servers you declared
pinToolDefs: true,
},
threatFeed,
store: {
getPin: async (id) => pins.get(id),
putPin: async (p) => void pins.set(p.serverId, p),
},
log: silentLogger(), // or your own logger
});
const verdict = await warden.vet(server, await client.listTools());
if (!verdict.allow) throw new Error(`blocked by ${verdict.decidedBy}`);
const usable = verdict.allowedTools; // a poisoned tool can be quarantined alone
await warden.approve(server, tools); // pin what the user acceptedvet() performs no network I/O. The only request WARDEN ever makes is the threat-feed fetch you
asked for by passing a URL to load().
The gate chain
flowchart LR
T["tool defs<br/>from the server"] --> S["static scan<br/>25 rules"]
S --> F["threat feed<br/>11 built-ins + signed"]
F --> O["origin<br/>declared vs catalog"]
O --> P["pinning<br/>drift vs approval"]
P --> V["verdict<br/>allow · score · findings<br/>allowedTools / blockedTools"]Gate | What it decides | Network | Fatal? |
static-scan | Injection, exfiltration, credential requests and hidden-Unicode/base64 tells in the tool | none | no |
threat-feed | Known-bad server identity or tool, from 11 built-in records plus an optional signed feed | only the feed fetch | yes, for a server-scoped |
origin | Whether the operator declared this server or it arrived from a remote catalog | none | yes, under |
pinning | Whether the tool defs still match what the user approved | none | yes, under |
The composite score is the product of gate contributions, so one bad gate drags the whole server
down rather than being averaged away. Severity and blocking are separate axes: an advisory finding
is reported and never blocks and never costs a tool, at any blockAtSeverity — because "how much
attention does this deserve" and "is this a defect at all" are different questions, and encoding the
second as a low severity made it blocking again for anyone who tightened the threshold.
The verdict is meant to be recorded
{
allow: false,
score: 0,
decidedBy: "threat-feed",
findings: [{ gate, severity, code: "THREAT_TOOL_MATCH", message, tool, advisory? }],
allowedTools: ["add"],
blockedTools: ["sweeper"],
rulesets: { staticScan: { version: "4", digest: "sha256-klRyTiD3…" } }
}rulesets is not decoration. The same server scores differently under a later rule table, and
without the version and a digest over the rules there is no way to tell that apart from the server
having changed. A stored scan without them is not reproducible.
Signed threat feed
WARDEN will not read an unsigned remote feed. The contract is deliberately boring:
GET <your feed url>
{ "records": [ {pattern, severity, code, reason, source, scope}, … ],
"timestamp": 1786205907380, // epoch ms, integer — required
"signature": "f588d5a4…" // Ed25519 (hex) over the RFC 8785 canonical
} // form of {records, timestamp}Three properties are checked, and any failure keeps the built-in floor rather than degrading to no protection:
authenticity — Ed25519 against the key you pinned in advance (
feedPublicKey);freshness — the signed timestamp must be inside
maxAgeMs(24 h by default), so whoever serves the URL cannot replay a months-old snapshot and silently erase every record added since. A signature says who wrote a document, never when you were handed it;determinism — RFC 8785 canonical bytes, so publisher and verifier agree regardless of JSON key order.
MOMUS is a reference publisher of this contract
(/warden/threat-feed) if you want something to point load() at.
Also in the box
EgressGuard— an outbound allowlist to wrap any request a tool makes. A tool reaching a host you never listed is the classic phone-home tell.*.example.commatches subdomains; an empty allowlist blocks everything rather than allowing everything.isSensitiveTool/classifyTools— glob classification of tools that must require per-call approval. Sensitive tools stay advertised; they just cannot run unattended.canonicalize/parseJsonStrict— a strict RFC 8785 (JCS) implementation, also exported as@aimarket/warden/jcsso another implementation can be byte-checked against it. Integers only beyondMAX_SAFE_JSON_INTEGER, refusal (not escaping) on lone surrogates, and a reason code on every refusal.
Documentation
Every rule tier, every finding code, how the composite score is built, and how to add a gate | |
The wire contract, the three checks, and how to publish a feed WARDEN will accept | |
Wiring WARDEN into your own MCP host, policy choices, and what to record | |
What WARDEN decided on real third-party tool definitions — 50 servers blocked, 4 substantiated, and the six ways the rest were wrong | |
stdio MCP server, health check, admin Build steps / CMD | |
Official Registry, Smithery, mcp.so / Pulse | |
How to report a firewall bypass | |
Zero-dep rule, ruleset PRs |
What this is not
Not a sandbox. These are in-process JS decisions. OS-level confinement of the MCP child process (seccomp/Landlock,
sandbox-exec) is not here.Not a model. No LLM is called anywhere in the chain. That is why
vet()is fast, offline and deterministic — and why the static scan is regex-shaped and will miss a paraphrase no rule covers.Not a reputation service. An earlier version had a gate that asked a trust oracle for a score it had no data to compute, then reported the oracle as unreachable without having sent a request. It was removed, and
test/no-phantom-gate.test.tsfails if any gate ever claims unreachability again.Not a substitute for reading the tool defs. 11 built-in threat records is a floor, not a catalog.
Not a proxy. The stdio MCP entry inspects advertised definitions you pass it. It does not connect to, fetch, or execute the server under scan.
Development
npm install && npm run build && npm test # 166 teststest/packaging.test.ts is what keeps the headline honest: it fails if an npm runtime dependency
appears, if any source file imports outside the package (except node: builtins), or if the entry
point stops exporting the enforcement surface. test/mcp-server.test.ts is the Glama health
check: initialize + tools/list + a tools/call.
Used by ARGUS (the reference host), MOMUS (the publisher side), and the AICOM MCP-security course.
MIT © AICOM (alexar76)
Available Tools
6 toolscanonicalize_jsonCanonicalize JSON with RFC 8785 (JCS) bytesARead-onlyIdempotent
Return the RFC 8785 JSON Canonicalization Scheme serialization WARDEN uses for threat-feed signatures and tool-def pins, so another implementation can byte-check against it. Integers only inside ±(2^53−1); lone surrogates and non-integers are refused with a reason code, not escaped.
When to use: you are publishing or verifying a signed threat feed, hashing tool defs, or comparing two JSON documents that must agree regardless of key order. Subpath @aimarket/warden/jcs is the same function.
When NOT to use: scanning tool defs (static_scan_tools); pretty-printing for humans (this output is for bytes, not display).
Behaviour: local, no network. Pass either a parsed JSON value or a JSON string (string is parsed with parseJsonStrict first). Failure returns isError with CanonicalizationCode — it does not emit partial bytes.
Returns { canonical } or an error. Example: canonicalize_json({ value: { b: 1, a: 2 } }) → {"a":2,"b":1}.
| Name | Required | Description | Default |
|---|---|---|---|
| value | Yes | JSON value to canonicalize, or a JSON string to parse first. Objects have keys sorted by UTF-16 code units. Numbers must be integers in ±(2^53−1). Do not pass undefined, functions, or cyclic structures. |
Output Schema
| Name | Required | Description |
|---|---|---|
| canonical | Yes | RFC 8785 canonical JSON text (UTF-8-ready string). Hash or sign these bytes, not JSON.stringify output. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description reveals local/no-network operation, the exact failure mode (isError with CanonicalizationCode, no partial bytes), accepted input forms (parsed value or JSON string parsed with parseJsonStrict), and strict acceptance rules (integers within ±(2^53−1), lone surrogates refused). No annotation contradiction is present.
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 well-organized with clear sections: purpose, when to use, when not to use, behavior, return shape, and example. Every sentence carries useful information, and the core purpose is front-loaded before the usage guidance.
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 single-parameter tool with an output schema, the description fully covers usage context, input constraints, failure behavior, and expected output. Nothing an agent needs to call or interpret the result correctly is missing.
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 coverage is 100%, so the baseline is 3. The description adds meaning by specifying that a string input is parsed with parseJsonStrict first, and it provides a concrete example (canonicalize_json({ value: { b: 1, a: 2 } }) → {"a":2,"b":1}) that clarifies input and output shape beyond the schema.
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 states a specific verb ('Return'), a specific resource (RFC 8785 JSON Canonicalization Scheme serialization), and the purpose (WARDEN threat-feed signatures, tool-def pins, byte-checking). It also distinguishes the tool from static_scan_tools in the 'When NOT to use' section, so it is not just a restatement of the name.
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?
The description contains dedicated 'When to use' and 'When NOT to use' sections. It lists concrete use cases (publishing/verifying signed threat feeds, hashing tool defs, comparing JSON documents independent of key order) and names an existing alternative (static_scan_tools) plus an exclusion (pretty-printing for humans).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_egress_urlCheck a URL against a WARDEN egress allowlistARead-onlyIdempotent
Ask EgressGuard whether a URL's hostname is on an operator allowlist. A tool reaching a host you never listed is the classic phone-home tell. Empty allowlist blocks everything (fail-closed), not everything-allowed.
When to use: a tool is about to fetch/post and you want the same check a host should wrap around that request. Hostnames match case-insensitively; "*.example.com" matches subdomains, not the apex.
When NOT to use: vetting tool definitions (vet_mcp_server / static_scan_tools); canonicalizing JSON (canonicalize_json). This does not fetch the URL and does not inspect tool text.
Behaviour: local URL parse + hostname match. Unparseable URLs are refused. No DNS, no HTTP. Idempotent.
Returns { allowed, host?, reason? }. Example: check_egress_url({ url: "https://api.github.com/repos", allowlist: ["api.github.com", "*.internal.example.com"] }).
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Absolute URL the tool wants to open (https://host/path). Only the hostname is compared; path, query, and credentials are not allowlisted separately. Must be parseable by the WHATWG URL parser. | |
| allowlist | Yes | Permitted hostnames. Exact match, or leading "*." for subdomains ("*.example.com" matches api.example.com, not example.com). Empty array blocks every host. Entries are trimmed and compared case-insensitively. |
Output Schema
| Name | Required | Description |
|---|---|---|
| host | No | Parsed hostname when the URL was valid. |
| reason | No | Why the request is blocked; omitted when allowed is true. |
| allowed | Yes | true only when the hostname matches an allowlist entry. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Even with annotations declaring readOnlyHint, idempotentHint, and destructiveHint, the description adds substantial behavioral context: local URL parse, no DNS/HTTP, fail-closed empty allowlist, case-insensitive matching, wildcard semantics, and refusal of unparseable URLs. This fully discloses the tool's operational behavior.
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 well-organized into short labeled sections, front-loads the core purpose, and contains no filler. Each sentence adds meaningful guidance, from scope and exclusions to behavior and return shape.
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 read-only, idempotent two-parameter tool, the description is complete: it explains purpose, when to use it, when not to use it, behavior, return value, and an example. The existing output schema covers the detailed return structure, so no necessary information is missing.
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 coverage is 100%, so the schema already documents both parameters thoroughly. The description adds value beyond this with a concrete example call, wildcard behavior restated in practical terms, and the fail-closed empty allowlist note, which helps the agent form correct invocations.
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 states a specific verb and resource: 'Ask EgressGuard whether a URL's hostname is on an operator allowlist.' It clearly distinguishes itself from siblings by naming vet_mcp_server, static_scan_tools, and canonicalize_json as tools not to use for this purpose.
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?
The description gives explicit 'When to use' and 'When NOT to use' sections, naming concrete alternatives and explaining the intended context: checking a host before a tool fetches or posts. It also specifies what the tool does NOT do, such as fetching the URL or inspecting tool text.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
classify_sensitive_toolsClassify MCP tools as sensitive vs safe by glob policyARead-onlyIdempotent
Split advertised tool names into sensitive vs safe using the operator's case-insensitive * globs (the same policy.sensitiveToolPatterns a host would use). Sensitive tools stay advertised; they require per-call approval — this tool does not run them.
When to use: show the user which names will need confirmation before they approve a server, or to preview a glob set. This is policy over identifiers, not an injection scan.
When NOT to use: scanning descriptions for poisoning (static_scan_tools or vet_mcp_server); checking whether a URL is allowed out (check_egress_url).
Behaviour: local glob match, no network. An empty patterns array marks every tool safe. Patterns match the whole name; "delete" hits create_delete_repo. Does not call Warden.vet and does not persist anything.
Returns { sensitive, safe }. Example: classify_sensitive_tools({ tools: [{ name: "delete_repo", description: "Delete a repository.", inputSchema: { type: "object" } }], patterns: ["delete"] }).
| Name | Required | Description | Default |
|---|---|---|---|
| tools | Yes | tools/list items whose names will be classified. Descriptions and schemas are ignored; only name is matched. | |
| patterns | Yes | Operator globs, case-insensitive, matched against the whole tool name. "*" is the only wildcard. Empty array → every tool is safe. Same semantics as WardenPolicy.sensitiveToolPatterns, not threat-feed matching. |
Output Schema
| Name | Required | Description |
|---|---|---|
| safe | Yes | Names matching no pattern. |
| sensitive | Yes | Names matching at least one pattern. Still advertised; require per-call approval. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnlyHint/idempotentHint/destructiveHint annotations by disclosing: local glob match with no network access, no persistence, no Warden.vet call, empty patterns array → every tool safe, whole-name matching with '*delete*' hitting create_delete_repo, and that sensitive tools are classified but not run. These are exactly the behavioral traits an agent needs to predict side effects and edge cases.
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?
Organized into labeled sections (When to use / When NOT to use / Behaviour / Returns) that make it scannable, with the core purpose front-loaded in the first sentence. Slightly verbose, but every sentence carries distinct information — no filler, and the section labels justify the length.
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?
Complete for a 2-param tool with full schema coverage, rich annotations, and an output schema. The description covers purpose, when-to-use versus three named siblings, edge cases (empty patterns), safety profile (no network, no persistence), matching semantics, and return shape. Nothing an agent needs to invoke it correctly is missing.
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 coverage is 100%, so baseline is 3. The description adds genuine value with a concrete worked example showing the call shape and return value ({ sensitive, safe }), and it reinforces the crucial nuance that descriptions/schemas are ignored in classification and only name is matched. The example clarifies the wildcard semantics beyond what the schema states.
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?
States a specific verb and resource: 'Split advertised tool names into sensitive vs safe using the operator's case-insensitive * globs'. It names the policy source (policy.sensitiveToolPatterns), explicitly declares what it is not ('This is policy over identifiers, not an injection scan'), and the title itself is an accurate summary. Distinguishes clearly from siblings static_scan_tools, vet_mcp_server, and check_egress_url.
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?
Provides explicit selection criteria: 'When to use: show the user which names will need confirmation before they approve a server, or to preview a glob set.' Then gives explicit exclusions with named alternatives: 'When NOT to use: scanning descriptions for poisoning (static_scan_tools or vet_mcp_server); checking whether a URL is allowed out (check_egress_url).' An agent can route to the correct sibling without inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_scan_rulesList the published WARDEN static-scan rule tableARead-onlyIdempotent
Return the in-force static-scan ruleset: version, digest, and every rule's code, severity, tier (block vs advise), surfaces (name / description / inputSchema), optional regex source, and named guards. A recorded verdict is only reproducible together with this identity.
When to use: explain a finding code, confirm you are on ruleset v4, or re-run a scan with the same table. include_source=true adds the regex source and flags for an independent re-implementation.
When NOT to use: evaluating a live tools/list (static_scan_tools or vet_mcp_server — those apply the table). This tool does not scan anything.
Behaviour: local snapshot of the compiled rule table, no network, no mutation. Digest is sha256 over the RFC 8785 form of {version, rules}.
Returns the ruleset object. Example: list_scan_rules({ include_source: false }).
| Name | Required | Description | Default |
|---|---|---|---|
| include_source | No | When true, each rule includes source (regex body) and flags so a third party can re-run the exact pattern. Default false — identity, tier, surfaces, and guards only, smaller payload. |
Output Schema
| Name | Required | Description |
|---|---|---|
| rules | Yes | |
| digest | Yes | sha256-<base64> of the canonical rule table. |
| version | Yes | Monotonic ruleset version. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnly, idempotent, non-destructive), the description adds genuinely new behavioral context: 'local snapshot of the compiled rule table, no network, no mutation,' the digest computation detail ('sha256 over the RFC 8785 form of {version, rules}'), and the reproducibility caveat that 'a recorded verdict is only reproducible together with this identity.' None of this is derivable from the annotations, and it does not contradict them.
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 front-loaded with the core purpose, then organized into clearly labeled sections (When to use / When NOT to use / Behaviour / Returns) that make it scannable. Every sentence earns its place — the output content is enumerated once in detail rather than repeated, and the non-use case doubles as sibling differentiation. The minimal redundancy of the example call is negligible.
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 simple 1-optional-parameter tool with an output schema present, nothing is missing: return contents, use cases, exclusion cases against siblings, network/side-effect behavior, digest identity, and an invocation example are all covered. An agent has everything needed to decide whether to call it and what to expect.
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 coverage is 100% — the input schema fully documents include_source, its default, and its effect. The description adds only marginal enrichment by framing include_source=true as enabling 'an independent re-implementation,' which gives the parameter a purpose beyond its mechanics. Baseline 3 is appropriate since the schema carries the heavy lifting.
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 opens with a specific verb and resource: 'Return the in-force static-scan ruleset' followed by an exact enumeration of contents (version, digest, rule code, severity, tier, surfaces, regex source, guards). It explicitly distinguishes itself from siblings with 'This tool does not scan anything' and names static_scan_tools and vet_mcp_server as the tools that apply the table, so an agent cannot confuse it with them.
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?
Provides labeled 'When to use' guidance (explain a finding code, confirm ruleset v4, re-run a scan with the same table) and an explicit 'When NOT to use' section naming the sibling alternatives (static_scan_tools, vet_mcp_server) and the condition that routes to them. This is exactly the when/when-not/alternatives structure the rubric asks for.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
static_scan_toolsStatic-scan MCP tool definitions for injection and exfilARead-onlyIdempotent
Run only the static-scan gate (ruleset v4, 25 signatures with context guards) over advertised tool names, descriptions, and input schemas. Returns findings, a 0..1 gate score, and the published ruleset digest.
When to use: you have a tools/list dump and want injection / credential / hidden-Unicode hits without origin, pinning, or the threat feed. Cheaper and narrower than vet_mcp_server.
When NOT to use: you need the full host decision (vet_mcp_server); you want operator glob classification (classify_sensitive_tools); you want the published rule table itself (list_scan_rules).
Behaviour: local regex+guard evaluation, no network, no mutation. Advisory-tier hits are reported with advisory=true and do not reduce the score. Does not launch servers or send tool output to a model.
Returns structured JSON matching outputSchema. Example: static_scan_tools({ tools: [{ name: "add", description: "Add two integers.", inputSchema: { type: "object" } }] }).
| Name | Required | Description | Default |
|---|---|---|---|
| tools | Yes | tools/list items to scan (1..256). Same shape as vet_mcp_server.tools. |
Output Schema
| Name | Required | Description |
|---|---|---|
| score | Yes | static-scan gate contribution: 1 minus the penalty for the worst non-advisory severity. Advisory hits do not change this number. |
| ruleset | Yes | |
| findings | Yes | Hits from the 25-rule table, including advisory-only codes. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds significant behavioral detail beyond the read-only, idempotent, non-destructive annotations: local regex+guard evaluation, no network access, no mutation, no server launching, and advisory-tier hits flagged with advisory=true that do not reduce the score. No contradiction 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 longer than average, but each section earns its place: purpose, routing conditions, behavioral caveats, and a concrete example. The When to use / When NOT to use structure is scannable and front-loads the key decisions.
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 rich annotations, full input schema, output schema, and sibling context, the description covers everything an agent needs to call correctly: scope, score range, advisory behavior, side-effect guarantees, and alternative routing. Nothing important is missing.
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 coverage is 100%, so the schema already documents the tools parameter and its nested properties. The description adds value by instructing the agent to pass descriptions and input schemas through unmodified, warning that rewriting hides injection surfaces, and noting the shape matches vet_mcp_server.tools.
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?
States a specific action ('Run only the static-scan gate') over a specific resource ('advertised tool names, descriptions, and input schemas'), then identifies the outputs. It clearly distinguishes itself from vet_mcp_server, classify_sensitive_tools, and list_scan_rules via the When NOT to use section.
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?
Contains explicit 'When to use' and 'When NOT to use' sections. It names the triggering condition (having a tools/list dump and wanting injection/credential/hidden-Unicode hits) and lists sibling tools that should be used instead for broader analysis or rule retrieval.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
vet_mcp_serverVet an MCP server through the full WARDEN gate chainARead-onlyIdempotent
Run WARDEN's ordered gate chain (static-scan → threat-feed → origin → pinning) over a server identity plus its advertised tools/list payload and return a recordable verdict (allow/block, 0..1 product score, findings, allowedTools/blockedTools, ruleset digest).
When to use: you have a complete server record and want the same decision a host should make before any of those tool definitions reach the model. Prefer this over calling the four gates yourself.
When NOT to use: inspecting descriptions only (call static_scan_tools — no origin/pinning); splitting tools by operator glob (classify_sensitive_tools); checking one outbound URL (check_egress_url); producing RFC 8785 bytes (canonicalize_json).
Behaviour: local, deterministic, no network. This stdio process uses the built-in 11-record threat floor (it does not fetch a signed feed) and an empty in-memory pin store, so every server is first-contact: TOOL_DEF_UNPINNED is advisory and does not block. Origin defaults to allowUnknownServers=true so catalog-discovered servers are not fail-closed. Override policy when you need the host's real knobs. Does not connect to, start, or approve the target server.
Returns structured JSON matching outputSchema. Example: vet_mcp_server({ server: { id: "demo@0", name: "demo", transport: "stdio", command: "npx" }, tools: [{ name: "add", description: "Add two integers.", inputSchema: { type: "object" } }] }).
| Name | Required | Description | Default |
|---|---|---|---|
| tools | Yes | Exact tools/list payload (1..256 tools). Each item is name + description + inputSchema from the server. Do not filter before vetting — blockedTools is the partition. | |
| policy | No | Optional WardenPolicy overlay. Omitted keys keep the stdio defaults: blockAtSeverity=high, empty sensitiveToolPatterns, allowUnknownServers=true, pinToolDefs=true. Use classify_sensitive_tools when you only want the glob split. | |
| server | Yes | Identity of the MCP server being vetted — the host's McpServerRef, not a live connection. WARDEN never launches this command or fetches this URL. |
Output Schema
| Name | Required | Description |
|---|---|---|
| allow | Yes | false when a gate was fatal or a non-advisory finding reached blockAtSeverity. |
| score | Yes | Product of per-gate scores in [0, 1]. One bad gate drags the whole server down; it is not an average. |
| findings | Yes | Accumulated findings across all gates, including advisory. |
| rulesets | Yes | Rule-table identity in force for this verdict. Store this with the scan. |
| decidedBy | No | Gate that produced the blocking decision, present only when allow is false. |
| allowedTools | Yes | Tool names the host may expose to the model. |
| blockedTools | Yes | Tool names quarantined; the rest of the server may still be usable. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly/idempotent/destructive annotations, the description reveals local deterministic no-network behavior, built-in threat floor, empty pin store advisory outcomes, origin default, and that it never connects/starts/approves the server. No contradiction 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 organized into labeled sections, front-loads purpose, and uses each sentence for a distinct piece of information (use, exclusions, behavior, example). Despite length, it is dense and scannable.
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?
With a rich input schema, output schema, and annotations present, the description still adds the operational context needed to call correctly: gate order, policy defaults, first-contact behavior, and exclusions. Nothing essential for selection or invocation is missing.
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 carries the parameter burden. The description adds a concrete invocation example and notes policy override semantics, but it doesn't need to compensate for missing param docs.
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 opens with a specific verb+resource: run WARDEN's ordered gate chain over a server identity plus tools/list payload and return a verdict. It also distinguishes itself from siblings by naming what it is not in the 'When NOT to use' section.
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?
Contains explicit 'When to use' and 'When NOT to use' guidance with named alternatives: static_scan_tools, classify_sensitive_tools, check_egress_url, canonicalize_json. It even says to prefer this over calling the four gates manually.
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. Dates show when Glama detected each change.
6 tool updates
v1.0.0- First observed
canonicalize_json - First observed
check_egress_url - First observed
classify_sensitive_tools - First observed
list_scan_rules - First observed
static_scan_tools - First observed
vet_mcp_server
TDQS
Most tools have sharply distinct purposes, and the one overlapping pair—vet_mcp_server vs static_scan_tools—is clearly differentiated by scope (full gate chain vs static-only) and by explicit when-to-use guidance. The remaining tools (classification, egress, canonicalization, rule listing) are cleanly separated and unlikely to be confused.
Five of six tools follow a predictable verb-oriented snake_case pattern (classify_sensitive_tools, check_egress_url, canonicalize_json, list_scan_rules, and roughly vet_mcp_server). static_scan_tools is a noun-phrase outlier, but the overall naming style is consistent and readable.
Six tools is well-scoped for the stated purpose: one comprehensive vetting entry point plus focused utilities for static scanning, policy preview, egress checks, canonicalization, and rules introspection. Each tool fills a distinct role without redundancy or bloat.
The toolset covers the full vetting decision, the static scan gate, sensitive-tool classification, outbound URL policy checks, canonicalization for signature/pin verification, and ruleset introspection. No obvious dead ends or missing operations prevent an agent from completing the typical vetting workflow.
Maintenance
Related MCP Connectors
Pre-flight MCP security. Blocks compromised deps + tool drift. HMAC-signed. Dredd judges.
Security & DLP proxy for MCP: tool-poisoning scans, PII redaction on tool args/results. Beta.
Scan any MCP server for tool-poisoning, security, auth & license. Trust score before install.
Security research: MCP registries verify identity, not tool behavior. See gtfo.dev.
Related MCP Servers
- AlicenseAqualityDmaintenanceMCP security trust layer. Continuously monitors 800+ MCP packages on npm for install scripts, command injection, hardcoded secrets, capability drift, and publisher posture. Ships a GitHub Action policy gate for PR-level allow/warn/block decisions. 5 MCP tools, no API key required.81211MIT
- AlicenseAqualityAmaintenanceSupply-chain gate for AI agent skills and MCP servers: poison-scan tool definitions, hash-pin the vetted set into a lock file and verify drift in CI. truecopy-mcp is a drop-in stdio proxy that filters a live server's tools/list down to its pinned, unmodified, unpoisoned tools.2291MIT
- AlicenseNot gradedqualityBmaintenanceSelf-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 gradedqualityBmaintenanceA task-scoped MCP stdio proxy that learns candidate least-privilege policies from labeled successful runs, requires human review, enforces exact decisions, detects tool-definition drift, and emits privacy-minimized JSONL events for Wazuh.1Apache 2.0
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/alexar76/warden'
If you have feedback or need assistance with the MCP directory API, please join our Discord server