You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Security Audit: Unconstrained string parameters across all official servers #3537
I ran automated security audits on 7 official MCP servers using mcp-security-audit. All servers scored Grade A or B (85-100/100), which is great. But there's one consistent finding across every server except mcp-server-fetch:
No string parameters use maxLength, pattern, or enum constraints.
Findings
Server
Score
Unconstrained Strings
Fetch
100/100
0
SQLite
95/100
5 (SQL queries, table names)
Time
95/100
4 (timezone identifiers)
GitHub
94/100
Multiple
Memory
92/100
1 (search query)
Git
90/100
18 (paths, messages, branches)
Filesystem
85/100
Multiple (paths)
Why this matters
DoS vector — Unconstrained strings accept arbitrarily long input. A 10MB commit message or 50K-char file path must be processed by the server.
No boundary validation — If an LLM is compromised via prompt injection, the tool schema provides no defense against malformed input.
Network transports — With MCP supporting Streamable HTTP, servers may be network-exposed where these constraints become critical.
MCPS (MCP Secure) solves this at the message layer — every JSON-RPC body is SHA-256 hashed and ECDSA P-256 signed before transmission. A compromised proxy can't inject malformed parameters because the signature covers the full message body. Nonce + timestamp binding prevents replay. Designed as a drop-in security layer for MCP servers. IETF Internet-Draft | github.com/razashariff/mcps
Good findings. The unconstrained string issue is real, and schema-level constraints (maxLength, pattern, enum) would help with the DoS vector.
But there's a deeper problem: even with schema constraints in place, the content of those strings can still carry prompt injection payloads. A maxLength: 500 parameter still has room for "Ignore previous instructions and..." — the constraint prevents oversized input but not malicious input.
We've been looking at this from the content analysis side with ClawGuard (open-source, regex-based MCP scanner). Some data points:
A peer-reviewed study (Queen's University, 1,899 MCP servers) found 5.5% are susceptible to Tool Poisoning — where tool descriptions themselves contain hidden behavioral directives
System prompt restrictions ("don't follow injected instructions") reduce attack success rate by only 0.65% — essentially useless as a defense
The git server's 18 unconstrained strings are particularly concerning because commit_message and branch_name parameters are user-controlled in collaborative workflows
Schema constraints + content scanning are complementary layers. Neither alone is sufficient.
I'd like to take the memory server part of this audit.
Proposed change (ready locally, small diff):
Add maxLength to search_nodes' query parameter via a named constant (SEARCH_QUERY_MAX_LENGTH = 2048) and an exported SearchNodesQuerySchema, following the pattern in Add schema constraints to time server strings #4282 (time server string constraints).
Vitest coverage for the boundary: normal query accepted, at-limit accepted, over-limit rejected, non-string still rejected.
Scope kept to query as named in the audit's memory-server finding; remaining parameters can be separate follow-ups. Happy to adjust the limit value or shape if maintainers prefer.
The audit and the enforcement discussion address the current state well: constraints are missing (the audit), they are advisory not enforced (pshkv), guardrails enforce them at call time (piiiico), and tool descriptions are the model's decision surface (PengSpirit). One dimension no comment has addressed: what happens when the constraints themselves change between releases?
A server that adds maxLength: 2048 to a string parameter today has closed one gap. But in the next release: the constraint can be removed (the gap returns silently), the parameter can be renamed (the constraint is orphaned), or a new unconstrained parameter can appear (a new gap nobody audited). The audit in this issue is a point-in-time assessment — valuable, but it decays the moment any official server ships a new version.
The missing layer is a surface pin: hash each tool's full definition (name, description, inputSchema, annotations) at a known-good baseline, and diff on every subsequent release. A removed constraint flags as LOOSENED (the DoS vector is back). A new unconstrained parameter flags as NEW (needs the same audit applied to the old set). A tightened constraint flags as BREAKING (callers may now fail). And serialization churn flags as NOTATION — not a finding, not an alert.
In production monitoring across 14 public MCP servers, one recent vendor major changed all 20 pinned tools in one release: 15 were pure serialization churn, 5 were real constraint tightenings. Without grading, the 5 real findings sat in a 20-line alert wall. With grading, the signal is clean.
The official servers in this repo are the canonical reference implementations. If they shipped with a committed pin file (hashes of each tool's definition at each tagged release), downstream servers could diff against them the same way package managers verify checksums — and audits like this one would become continuous rather than point-in-time.
This is a security report, so it is handled by the maintainers and the details aren't discussed here. If you have more to add, please use the advisory form rather than this thread.
Board: Servers V2 (#43), Status Incoming, awaiting maintainer review (no milestone yet). Part of the backlog triage in #4875.
Blanket maxLength / pattern constraints aren't planned for the rest. These servers run with the privileges of the client that starts them, so a schema limit on a string doesn't stop a malicious value; it only rejects a long one. The checks that matter run when the tool executes: filesystem keeps every path inside the allowed directories, git validates diff targets and refs before running them, and time rejects unknown timezone names. An over-long path is refused by the OS, and an over-long commit message only affects the client that sent it.
If you find a specific input on one of these servers that causes harm through its size or format, please open an issue for that case with steps to reproduce, or report it through the advisory form if it's a vulnerability.
Thanks for the detailed re-check and for separating the already-addressed findings from the cases that need runtime-specific evidence.
Understood on the scope and the decision to close this issue. We'll treat the current findings as resolved or out of scope here, and use the appropriate issue or private advisory channel if we identify a concrete, independently reproducible problem.
Activity
razashariff commentedon Mar 16, 2026
MCPS (MCP Secure) solves this at the message layer — every JSON-RPC body is SHA-256 hashed and ECDSA P-256 signed before transmission. A compromised proxy can't inject malformed parameters because the signature covers the full message body. Nonce + timestamp binding prevents replay. Designed as a drop-in security layer for MCP servers. IETF Internet-Draft | github.com/razashariff/mcps
joergmichno commentedon Mar 21, 2026
Good findings. The unconstrained string issue is real, and schema-level constraints (maxLength, pattern, enum) would help with the DoS vector.
But there's a deeper problem: even with schema constraints in place, the content of those strings can still carry prompt injection payloads. A
maxLength: 500parameter still has room for"Ignore previous instructions and..."— the constraint prevents oversized input but not malicious input.We've been looking at this from the content analysis side with ClawGuard (open-source, regex-based MCP scanner). Some data points:
gitserver's 18 unconstrained strings are particularly concerning becausecommit_messageandbranch_nameparameters are user-controlled in collaborative workflowsSchema constraints + content scanning are complementary layers. Neither alone is sufficient.
khalidsaidi commentedon Mar 25, 2026
piiiico commentedon Apr 3, 2026
pshkv commentedon Apr 4, 2026
hidearmoon commentedon Apr 17, 2026
kinthaiofficial commentedon Apr 28, 2026
piiiico commentedon Apr 28, 2026
research: MCP official servers defense audit (2026-04-29)
ppcvote commentedon Apr 29, 2026
ppcvote commentedon Apr 30, 2026
jashshah999 commentedon May 5, 2026
PengSpirit commentedon May 9, 2026
xlyoung commentedon Jun 6, 2026
feiiiiii5 commentedon Aug 18, 2026
I'd like to take the memory server part of this audit.
Proposed change (ready locally, small diff):
maxLengthtosearch_nodes'queryparameter via a named constant (SEARCH_QUERY_MAX_LENGTH = 2048) and an exportedSearchNodesQuerySchema, following the pattern in Add schema constraints to time server strings #4282 (time server string constraints).Scope kept to
queryas named in the audit's memory-server finding; remaining parameters can be separate follow-ups. Happy to adjust the limit value or shape if maintainers prefer.DSHCorrectover commentedon Aug 23, 2026
DSHCorrectover commentedon Aug 24, 2026
fix(memory): constrain search_nodes query length (#4662)
Paraphern commentedon Oct 12, 2026
The audit and the enforcement discussion address the current state well: constraints are missing (the audit), they are advisory not enforced (pshkv), guardrails enforce them at call time (piiiico), and tool descriptions are the model's decision surface (PengSpirit). One dimension no comment has addressed: what happens when the constraints themselves change between releases?
A server that adds
maxLength: 2048to a string parameter today has closed one gap. But in the next release: the constraint can be removed (the gap returns silently), the parameter can be renamed (the constraint is orphaned), or a new unconstrained parameter can appear (a new gap nobody audited). The audit in this issue is a point-in-time assessment — valuable, but it decays the moment any official server ships a new version.The missing layer is a surface pin: hash each tool's full definition (name, description, inputSchema, annotations) at a known-good baseline, and diff on every subsequent release. A removed constraint flags as LOOSENED (the DoS vector is back). A new unconstrained parameter flags as NEW (needs the same audit applied to the old set). A tightened constraint flags as BREAKING (callers may now fail). And serialization churn flags as NOTATION — not a finding, not an alert.
In production monitoring across 14 public MCP servers, one recent vendor major changed all 20 pinned tools in one release: 15 were pure serialization churn, 5 were real constraint tightenings. Without grading, the 5 real findings sat in a 20-line alert wall. With grading, the signal is clean.
The official servers in this repo are the canonical reference implementations. If they shipped with a committed pin file (hashes of each tool's definition at each tagged release), downstream servers could diff against them the same way package managers verify checksums — and audits like this one would become continuous rather than point-in-time.
We ship the pin-and-graded-diff layer open-source (rugsnare, Apache-2.0); per-version drift records for 14 public MCP servers: https://github.com/Paraphern/rugsnare/tree/main/docs/servers
cliffhall commentedon Oct 19, 2026
Triage: Priority Low (total 4)
This is a security report, so it is handled by the maintainers and the details aren't discussed here. If you have more to add, please use the advisory form rather than this thread.
Board: Servers V2 (#43), Status
Incoming, awaiting maintainer review (no milestone yet). Part of the backlog triage in #4875.cliffhall commentedon Nov 5, 2026
Thank you for the audit, and to everyone who added to the thread. Re-checking it against the current code on
v2/main, I'm closing this as not planned:search_nodes'queryhas had a length limit since fix(memory): constrain search_nodes query length #4662. SQLite and GitHub have moved toservers-archived. Fetch needed nothing.maxLength/patternconstraints aren't planned for the rest. These servers run with the privileges of the client that starts them, so a schema limit on a string doesn't stop a malicious value; it only rejects a long one. The checks that matter run when the tool executes: filesystem keeps every path inside the allowed directories, git validates diff targets and refs before running them, and time rejects unknown timezone names. An over-long path is refused by the OS, and an over-long commit message only affects the client that sent it.If you find a specific input on one of these servers that causes harm through its size or format, please open an issue for that case with steps to reproduce, or report it through the advisory form if it's a vulnerability.
Paraphern commentedon Nov 5, 2026
Thanks for the detailed re-check and for separating the already-addressed findings from the cases that need runtime-specific evidence.
Understood on the scope and the decision to close this issue. We'll treat the current findings as resolved or out of scope here, and use the appropriate issue or private advisory channel if we identify a concrete, independently reproducible problem.
Appreciate the careful review.