| Internet-Draft | MCP Parameter Risk Classes | October 2026 |
| Wang | Expires 17 April 2027 | [Page] |
This document defines a fixed set of five canonical MCP parameter risk classes: semantic categories for sensitive parameters that appear in the JSON Schemas of Model Context Protocol (MCP) tool definitions, together with deterministic decision rules and boundary conditions for assigning those labels. The classes are designed to supply machine-readable, pre-invocation risk metadata about tool inputs, independent of any particular gateway, routing, or capability-profile specification.¶
The classes and their decision rules were derived from, and applied against, a SHA-256-frozen empirical census of publicly reachable MCP servers. The frozen census and the classification suite that implements these rules are archived on Zenodo; the archived suite is the normative tie-breaker for exact matching behaviour.¶
This is an Informational, individual submission. It defines the canonical classes and decision rules. Other specifications MAY cite this document as the canonical source of the class labels and MAY define their own operators that consume those labels; they SHOULD NOT re-derive or inline the normative decision rules into their own normative text.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 17 April 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document.¶
An agent system that invokes a remote Model Context Protocol (MCP) [ref-mcp] server is presented with a list of tools, each carrying a name, a free-text description, and an input JSON Schema. These artifacts are descriptive, not normative about risk: there is no standardized, machine-readable signal indicating that a given parameter carries a secret, names a filesystem path, executes a command string, moves money, or addresses an external network endpoint.¶
Gateway pre-invocation checks, routing policies, and bounded-capability profiles all need exactly that signal, expressed in a small stable vocabulary that can be derived deterministically from a tool schema. This document specifies such a vocabulary: five canonical parameter risk classes and the exact lexical decision rules that assign them.¶
The rules were not designed in isolation. They were developed against a census of publicly reachable MCP servers and run over a frozen snapshot of 229,539 live tools on 13,089 reachable servers; the resulting class frequencies and high-consequence intersections are reported in Section 6. The frozen census, its hashes, and the exact classification suite are archived on Zenodo (see Appendix A).¶
Throughout this document the measured dataset is referred to as G-G, the MCP runtime measurement and audit dataset. The two Zenodo records that archive the frozen snapshot were deposited under the earlier working title "MCP Atlas"; the archived bytes and hashes are unchanged by the rename.¶
This document is standalone. It cites a separate Correctover Conformance Shape (CCS) document only for the unrelated runtime attestation layer ([ref-ccs]); the parameter classification logic here does not depend on CCS.¶
A single callable capability exposed by an MCP server, with a name, a description, and an input JSON Schema.¶
A named property in a tool input JSON Schema, identified by its property name and optional description string.¶
One of the five canonical identifiers defined in Section 4.¶
The deterministic predicate that assigns a class label to a parameter property, defined in Section 3 and Section 4.¶
The byte-frozen 2026-10-12 census corpus and the archived classification suite described in Appendix A.¶
The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
For every MCP tool, the classifier enumerates parameter properties as follows and applies each decision rule independently.¶
Properties are enumerated from the input JSON Schema in two locations:¶
properties object; and¶
definitions
or $defs key that itself contains a properties
object.¶
For each enumerated property the classifier records three values: the
property name, the value of its description keyword as a string
(empty when absent or non-string), and whether the property name appears
in that schema's required array. The property name and
description are combined into a single match target¶
haystack = lowercase(property_name + " : " + description)¶
Each decision rule is one or more regular expressions evaluated with
Unicode case-insensitive matching against haystack using a
substring (search) match. A class label is assigned when any
of that class's patterns matches. A single property
MAY receive multiple class labels; classification is not
exclusive. A tool is counted under a class when at least one of its
properties carries that label. A separate per-class count records whether
at least one labelled property was also declared
required.¶
All patterns use the boundaries described in Section 3.3; the archived Python classification suite is the normative tie-breaker for regex dialect and matching semantics.¶
Class patterns are written as whole word-form tokens. The archived suite implements the left and right boundaries in a Python regular expression as negative lookbehind and negative lookahead over ASCII lowercase letters, digits, and underscore:¶
(?<![a-z0-9_]) <alternation of word forms> (?![a-z0-9_])¶
Consequently a form matches when bounded by punctuation, hyphens,
whitespace, the start/end of the target, or an underscore-delimited
segment boundary, but MUST NOT match as a substring of a
longer contiguous letter/digit/underscore identifier. Hyphenated
forms do match, because the hyphen is a boundary (for example
client-secret matches the secret form); a
camelCase identifier with no hyphen or underscore delimiter, such as
clientSecret, is not segmented and does not match on
the property name alone. This is a known lexical false-negative
surface; an informative word in the property description can still
produce a match through the haystack combination in
Section 3.1.¶
Five classes are normative. Each has a stable identifier, a definition,
its decision rule, and positive and boundary examples. The identifiers
use the gg: prefix to keep the canonical namespace independent of
any consuming specification. Examples that match use a ✓ form; boundary
cases that do not match use a ✗ form.¶
Definition. A parameter intended to carry an authentication secret or credential material, such as a token, API key, password, private key, or bearer/authorization value. Only presence and scope of such a parameter are classified; the secret value itself is never part of the label.¶
(?<![a-z0-9_])(token|apikey|api_key|secret|password|passwd| private_key|access_key|credential|client_secret|bearer| authorization)(?![a-z0-9_])¶
✓ api_key, client_secret, client-secret
(hyphen-bounded secret), "bearer token",
authorization.¶
✗ secretariat_note, tokenizer_model,
authorization_flow_name (the contiguous underscore forms do
not present a bounded word token), clientSecret,
accessKeyId (delimiter-free camelCase is not segmented; known
false negatives).¶
Definition. A parameter that names a filesystem location: a file path, file name, directory, folder, or an explicit target/ destination path.¶
(?<![a-z0-9_])(path|filepath|filename|file_path|dir|directory| folder|target_path|destination_path|dest_path)(?![a-z0-9_])¶
✓ file_path, destination_path, dir,
"output folder", target_path.¶
✗ pathology_code, directory_sync_toggle (no bounded
token), destinationPath without an informative description
(delimiter-free camelCase is not segmented; known false negative), a bare
OS-specific path string with no matching property name or
description.¶
Definition. A parameter intended to carry an executable string or fragment: a shell/system command, script, code, expression, SQL statement, arguments vector, or a free-form instruction interpreted by an execution engine.¶
(?<![a-z0-9_])(command|cmd|script|code|expression|sql| statement|shell_cmd|args?|arguments?|instruction|prompt)(?![a-z0-9_])¶
The forms args? and arguments? denote the singular
and plural alternation. ✓ shell_cmd, sql,
"execute code", args, prompt.¶
✗ commandment_count, scripture_ref (no bounded
token). Note that prompt is a deliberately high-recall form:
ordinary LLM prompt text is included, so this label indicates
interpretability risk surface, not guaranteed command execution.¶
Definition. A parameter that carries a monetary quantity or currency designation: an amount, price, total, sum, currency, or a domain-specific refund/charge/unit amount expressed in cents.¶
The class is assigned if either of the two patterns matches:¶
(?<![a-z0-9_])(amount|price|total|sum|currency|refund_amount| charge_amount|unit_amount|cents)(?![a-z0-9_])¶
\b(amount in|monetary|currency code)\b¶
✓ refund_amount, "total in cents", currency,
"amount in USD", unit_amount.¶
✗ total_results — the word form total is bounded on
the left but is followed by the underscore-adjacent token
results with no boundary, so it does not match; summed
(no bounded sum token).¶
Definition. A parameter that addresses an external network destination: a URL/URI, endpoint, host/hostname/domain, link, webhook, or an explicit base/target/callback URL.¶
(?<![a-z0-9_])(url|uri|endpoint|webhook_url|target_url|host| hostname|domain|link|webhook|base_url|callback_url)(?![a-z0-9_])¶
✓ target_url, base_url, webhook,
hostname, "callback URL".¶
✗ curriculum, hostname_label_prefix when presented
as one contiguous token without a boundary, domainKnowledge,
baseUrl (delimiter-free camelCase, not segmented; known false
negative).¶
The frozen suite also computes two additional buckets that are deliberately high-recall and low-specificity. They are reported for completeness but are not canonical parameter risk classes: they are too generic to function as standalone containment or grant domains, and they are excluded from the five-class risk vocabulary.¶
account_identity matches identifiers such as account,
account_id, iban, wallet, recipient, customer_id, user_id, beneficiary,
card_token, payment_method, bank, routing_number, and bounded
email, to, and from forms. It fires on roughly half of all live tools.¶
raw_content_blob matches generic payload forms such as
content, body, data, payload, html, markdown, file_content, and text.¶
Consuming specifications MAY reproduce these buckets for corpus comparability, but they are not part of the canonical risk-class vocabulary and carry no normative containment semantics here.¶
The rules in Section 4 were applied to the frozen
snapshot. A tool is counted in a class when at least one of its properties
matches; a separate column counts tools with at least one matching property
that is also required. Percentages are of 229,539 live
tools.¶
| Class | Tools (≥1 property) | % of live | ≥1 required |
|---|---|---|---|
| gg:url-endpoint | 31,880 | 13.889 | 22,891 |
| gg:command-string | 23,847 | 10.389 | 13,619 |
| gg:money-amount | 15,229 | 6.635 | 4,449 |
| gg:credential-value | 14,753 | 6.427 | 3,418 |
| gg:filesystem-path | 5,569 | 2.426 | 1,690 |
The non-normative contextual buckets fire on 44,429 tools (raw_content_blob, 19.356%) and 117,085 tools (account_identity, 51.009%) respectively, illustrating why they are not treated as risk classes.¶
A separate, tool-surface analysis (distinct from the parameter labels above) intersects a high-consequence tool-name/description signal — tools signalling payment/money, filesystem write or delete, command/code execution, infrastructure deployment, or credential handling — with an outbound-network signal. That intersection contains 995 tools, 0.43% of live tools. It is empirical evidence that risk is set jointly by a tool's functional class and by its outbound parameter surface; neither axis alone fully describes it. This intersection is reproduced by the archived suite, not defined by the parameter-class rules in Section 4.¶
Class frequency is heavily skewed and dominated by read/retrieval tools: the read/retrieval control surface alone contains 67,423 tools across 11,520 distinct servers. This motivates attaching containment semantics to a class selectively rather than requiring a domain on every node of a capability profile.¶
This document is the canonical source for the five parameter risk class identifiers, their definitions, and the normative decision rules in Section 4.¶
Other specifications — including capability-profile languages, intent-based selection and routing schemes, gateway policy models, and the various drafts discussed in the DMSC BoF context — MAY cite this document as the source of the class labels and MAY define their own mapping, comparison, or containment operators that consume those labels.¶
To keep the semantics stable across future census releases, such specifications SHOULD reference this document rather than copy or re-derive the decision rules in their own normative sections. A specification that inlines or alters the predicates creates an independent, divergent vocabulary and SHOULD NOT present it as the classes defined here. Defining a downstream operator over the labels (for example, a containment or grant function) is explicitly out of scope here and remains entirely the work of the consuming specification.¶
Maintaining these definitions in a standalone Informational document lets them evolve independently and be reused by multiple specifications without coupling to any single working-group draft.¶
The five classes are static, lexical pre-invocation signals derived only from tool property names and descriptions in a schema. They do not observe runtime behavior, data flow, or server-side vulnerabilities, and they cannot confirm that a labelled parameter actually receives sensitive input.¶
Lexical matching has known limits. It produces false positives for
high-recall word forms (for example prompt and
total), and false negatives when a sensitive parameter uses an
opaque or delimiter-free identifier with no informative description. It
does not perform semantic type inference or string-value taint tracking.
The boundary rules in Section 3.3 and the examples in
Section 4 delimit the intended behaviour; the archived suite
is authoritative for edge cases.¶
These labels are advisory inputs. They MUST NOT be used as the sole authorization decision and SHOULD be combined with runtime verification, canonicalization of the actual values, and auditable attestation receipts (such as the bounded receipts discussed in the CCS work) before a sensitive tool invocation is permitted.¶
This document has no IANA actions. The gg: class identifiers
are document-local labels, not IANA registrations.¶
The empirical baseline is a byte-frozen census with snapshot date 2026-10-12, covering 13,089 reachable servers and 229,539 live tools. Two Zenodo records archive it:¶
mcp-atlas-milestone-a-baseline-20261013.zip (MD5
9d383214877ad5c06749995e67a893f5).¶
mcp-atlas-audit-20261013-public.zip, 11,044,763 bytes (MD5
a22741da7be8622cdaf1783afa91e225).¶
The normative text source of this specification and the single archived
canonical checker (the PARAM_CLASSES table and param_hits
routine) are also archived together as [ref-zensource], so
the governing text and code share one citable record. That archive adds no
new data and does not change any decision rule in Section 4;
it is the companion software/text record for this document.¶
The pinned SHA-256 of the frozen corpus files verified by the suite are:¶
servers.jsonl.gz:
3071866184521fb36aeac6ac715687ed4bf99b9b034646da61176f54a3dfa9d8¶
server_tools.jsonl.gz:
2350dc773dc43fd6ed0b012588f3ff295fb358106a0291a7dc59c57427f5780f¶
candidates_remotes.jsonl:
2f24ccc39ffeebe17f2dfe3c59f04b5b2f816c886239fdfe8df4995848326157¶
All three pinned hashes matched at audit time. The decision rules in
Section 4 are the human-readable expression of the
PARAM_CLASSES table and param_hits routine in the
archived atlas_analyze.py; where prose and code differ, the
archived code at the pinned release governs the reported counts.¶
Representative property vectors (informative):¶
{"name":"cmd","description":"shell command for execution"} →
gg:command-string.¶
{"name":"destPath","description":"destination file path"}
→ gg:filesystem-path.¶
{"name":"webhook_url","description":"callback endpoint"}
→ gg:url-endpoint (both the webhook_url and url/
endpoint forms apply; one label).¶
{"name":"refund_amount","description":"amount in cents"}
→ gg:money-amount.¶
{"name":"Authorization","description":"bearer token"}
→ gg:credential-value.¶
{"name":"total_results","description":"page size"} → no
risk class.¶
{"name":"data","description":"request body"} → only the
non-normative raw_content_blob bucket.¶