mcpgawk 0.1.0__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/CHANGELOG.md +14 -1
  2. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/PKG-INFO +11 -13
  3. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/README.md +9 -11
  4. mcpgawk-0.1.2/assets/brand/social-preview.png +0 -0
  5. {mcpgawk-0.1.0/docs → mcpgawk-0.1.2}/assets/brand/social-preview.svg +2 -2
  6. mcpgawk-0.1.2/docs/CNAME +1 -0
  7. mcpgawk-0.1.2/docs/assets/brand/social-preview.png +0 -0
  8. mcpgawk-0.1.2/docs/index.html +178 -0
  9. mcpgawk-0.1.2/docs/styles.css +530 -0
  10. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/pyproject.toml +2 -2
  11. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/src/mcpgawk/measure.py +2 -0
  12. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/tests/test_measure.py +7 -0
  13. mcpgawk-0.1.0/assets/brand/social-preview.png +0 -0
  14. mcpgawk-0.1.0/docs/assets/brand/social-preview.png +0 -0
  15. mcpgawk-0.1.0/docs/index.html +0 -112
  16. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/.github/workflows/ci.yml +0 -0
  17. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/.gitignore +0 -0
  18. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/BRAND.md +0 -0
  19. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/CONTRIBUTING.md +0 -0
  20. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/LICENSE +0 -0
  21. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/NOTICE +0 -0
  22. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/SECURITY.md +0 -0
  23. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/THREAT-MODEL.md +0 -0
  24. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/assets/brand/demo.gif +0 -0
  25. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/assets/brand/favicon.svg +0 -0
  26. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/assets/brand/identity-board.html +0 -0
  27. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/assets/brand/nativerse-mark.svg +0 -0
  28. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/assets/brand/wordmark-dark.png +0 -0
  29. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/assets/brand/wordmark-dark.svg +0 -0
  30. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/assets/brand/wordmark-light.png +0 -0
  31. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/assets/brand/wordmark-light.svg +0 -0
  32. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/docs/assets/brand/favicon.svg +0 -0
  33. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/docs/assets/brand/nativerse-mark.svg +0 -0
  34. {mcpgawk-0.1.0 → mcpgawk-0.1.2/docs}/assets/brand/social-preview.svg +0 -0
  35. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/examples/mcp.json +0 -0
  36. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/src/mcpgawk/__init__.py +0 -0
  37. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/src/mcpgawk/__main__.py +0 -0
  38. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/src/mcpgawk/cli.py +0 -0
  39. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/src/mcpgawk/drift.py +0 -0
  40. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/src/mcpgawk/history.py +0 -0
  41. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/src/mcpgawk/label.py +0 -0
  42. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/src/mcpgawk/probe.py +0 -0
  43. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/src/mcpgawk/servercard.py +0 -0
  44. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/src/mcpgawk/signals.py +0 -0
  45. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/tests/test_drift.py +0 -0
  46. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/tests/test_no_egress.py +0 -0
  47. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/tests/test_servercard.py +0 -0
  48. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/tests/test_signals.py +0 -0
  49. {mcpgawk-0.1.0 → mcpgawk-0.1.2}/uv.lock +0 -0
@@ -2,7 +2,20 @@
2
2
 
3
3
  All notable changes to mcpgawk. Format: [Keep a Changelog](https://keepachangelog.com/); versioning: [SemVer](https://semver.org/).
4
4
 
5
- ## [0.1.0] — unreleased
5
+ ## [0.1.2] — 2026-07-08
6
+
7
+ ### Changed
8
+ - Plain, reader-first README and package description.
9
+ - README images use absolute URLs so they render on PyPI (relative paths only work on GitHub).
10
+
11
+ ## [0.1.1] — 2026-07-08
12
+
13
+ ### Fixed
14
+ - A tool declaring `destructiveHint: true` is now counted as write/mutating even when its name isn't a
15
+ write-verb (e.g. a `pause_job` tool). Previously the verb heuristic could leave a declared-destructive
16
+ tool unflagged.
17
+
18
+ ## [0.1.0] — 2026-07-08
6
19
 
7
20
  Initial release. Local-first MCP measurement.
8
21
 
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcpgawk
3
- Version: 0.1.0
4
- Summary: Local-first MCP measurement gawk at an MCP server before you trust it. No inventory ever leaves your machine.
3
+ Version: 0.1.2
4
+ Summary: A local-first CLI that measures what an MCP server costs and what it can do. Nothing leaves your machine.
5
5
  Project-URL: Homepage, https://mcp.gawk.dev
6
6
  Project-URL: Documentation, https://mcp.gawk.dev
7
7
  Project-URL: Repository, https://github.com/gawk-dev/mcpgawk
@@ -29,11 +29,11 @@ Description-Content-Type: text/markdown
29
29
 
30
30
  <p align="center">
31
31
  <picture>
32
- <source media="(prefers-color-scheme: dark)" srcset="assets/brand/wordmark-dark.png">
33
- <img alt="mcpgawk by nativerse" src="assets/brand/wordmark-light.png" width="320">
32
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/gawk-dev/mcpgawk/main/assets/brand/wordmark-dark.png">
33
+ <img alt="mcpgawk by nativerse" src="https://raw.githubusercontent.com/gawk-dev/mcpgawk/main/assets/brand/wordmark-light.png" width="320">
34
34
  </picture>
35
35
  </p>
36
- <p align="center"><em>gawk at it before you trust it.</em></p>
36
+ <p align="center"><em>Make MCP lean and honest.</em></p>
37
37
 
38
38
  # mcpgawk
39
39
 
@@ -43,20 +43,18 @@ Description-Content-Type: text/markdown
43
43
  [![CI](https://github.com/gawk-dev/mcpgawk/actions/workflows/ci.yml/badge.svg)](https://github.com/gawk-dev/mcpgawk/actions/workflows/ci.yml)
44
44
  [![No egress](https://img.shields.io/badge/inventory-never%20uploaded-brightgreen.svg)](#guarantees)
45
45
 
46
- **gawk at an MCP server before you trust it.** A single, local-first command that connects to any
47
- [Model Context Protocol](https://modelcontextprotocol.io) server and measures what it will cost and
48
- expose — **without the server's inventory ever leaving your machine.**
46
+ A local-first command that measures what a [Model Context Protocol](https://modelcontextprotocol.io)
47
+ server costs and what it can do. It runs on your machine and uploads nothing.
49
48
 
50
49
  <p align="center">
51
- <img src="assets/brand/demo.gif" alt="mcpgawk scanning an MCP server — tools, token cost, and capability flags, locally" width="760">
50
+ <img src="https://raw.githubusercontent.com/gawk-dev/mcpgawk/main/assets/brand/demo.gif" alt="mcpgawk scanning an MCP server — tools, token cost, and capability flags, locally" width="760">
52
51
  </p>
53
52
  <p align="center"><sub>Real output. Reproducible on your machine — no account, nothing uploaded.</sub></p>
54
53
 
55
54
  ## Why
56
55
 
57
- Every MCP server you connect dumps *all* its tool definitions into your model's context at connect
58
- time whether you use one tool or none. That's a hidden **token tax** and an unvetted **trust
59
- surface**. mcpgawk measures both, **locally**, and never phones home about what it saw.
56
+ Connect an MCP server and it loads all its tools into your AI's context. Every request. Used or not.
57
+ You pay for those tokens, and you haven't checked what the tools can do. mcpgawk shows you both, locally.
60
58
 
61
59
  ## How it's different
62
60
 
@@ -110,7 +108,7 @@ mcpgawk scan mcp.json --json # machine-read
110
108
  - **Facts ≠ heuristics.** Exact capability facts and the token index never mix with the bounded
111
109
  heuristic signals — separate in code, separate in output.
112
110
  - **Reproducible.** One command, identical numbers.
113
- - **Rides protocol evolution.** Built on the official `mcp` SDK, which negotiates the protocol version.
111
+ - **Tracks the protocol.** Built on the official `mcp` SDK, which negotiates the protocol version.
114
112
 
115
113
  ## Develop
116
114
 
@@ -1,10 +1,10 @@
1
1
  <p align="center">
2
2
  <picture>
3
- <source media="(prefers-color-scheme: dark)" srcset="assets/brand/wordmark-dark.png">
4
- <img alt="mcpgawk by nativerse" src="assets/brand/wordmark-light.png" width="320">
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/gawk-dev/mcpgawk/main/assets/brand/wordmark-dark.png">
4
+ <img alt="mcpgawk by nativerse" src="https://raw.githubusercontent.com/gawk-dev/mcpgawk/main/assets/brand/wordmark-light.png" width="320">
5
5
  </picture>
6
6
  </p>
7
- <p align="center"><em>gawk at it before you trust it.</em></p>
7
+ <p align="center"><em>Make MCP lean and honest.</em></p>
8
8
 
9
9
  # mcpgawk
10
10
 
@@ -14,20 +14,18 @@
14
14
  [![CI](https://github.com/gawk-dev/mcpgawk/actions/workflows/ci.yml/badge.svg)](https://github.com/gawk-dev/mcpgawk/actions/workflows/ci.yml)
15
15
  [![No egress](https://img.shields.io/badge/inventory-never%20uploaded-brightgreen.svg)](#guarantees)
16
16
 
17
- **gawk at an MCP server before you trust it.** A single, local-first command that connects to any
18
- [Model Context Protocol](https://modelcontextprotocol.io) server and measures what it will cost and
19
- expose — **without the server's inventory ever leaving your machine.**
17
+ A local-first command that measures what a [Model Context Protocol](https://modelcontextprotocol.io)
18
+ server costs and what it can do. It runs on your machine and uploads nothing.
20
19
 
21
20
  <p align="center">
22
- <img src="assets/brand/demo.gif" alt="mcpgawk scanning an MCP server — tools, token cost, and capability flags, locally" width="760">
21
+ <img src="https://raw.githubusercontent.com/gawk-dev/mcpgawk/main/assets/brand/demo.gif" alt="mcpgawk scanning an MCP server — tools, token cost, and capability flags, locally" width="760">
23
22
  </p>
24
23
  <p align="center"><sub>Real output. Reproducible on your machine — no account, nothing uploaded.</sub></p>
25
24
 
26
25
  ## Why
27
26
 
28
- Every MCP server you connect dumps *all* its tool definitions into your model's context at connect
29
- time whether you use one tool or none. That's a hidden **token tax** and an unvetted **trust
30
- surface**. mcpgawk measures both, **locally**, and never phones home about what it saw.
27
+ Connect an MCP server and it loads all its tools into your AI's context. Every request. Used or not.
28
+ You pay for those tokens, and you haven't checked what the tools can do. mcpgawk shows you both, locally.
31
29
 
32
30
  ## How it's different
33
31
 
@@ -81,7 +79,7 @@ mcpgawk scan mcp.json --json # machine-read
81
79
  - **Facts ≠ heuristics.** Exact capability facts and the token index never mix with the bounded
82
80
  heuristic signals — separate in code, separate in output.
83
81
  - **Reproducible.** One command, identical numbers.
84
- - **Rides protocol evolution.** Built on the official `mcp` SDK, which negotiates the protocol version.
82
+ - **Tracks the protocol.** Built on the official `mcp` SDK, which negotiates the protocol version.
85
83
 
86
84
  ## Develop
87
85
 
@@ -8,8 +8,8 @@
8
8
  <text x="256" y="196" dominant-baseline="central" font-family="Sentient, Georgia, serif" font-weight="500" font-size="104" letter-spacing="-3" fill="#16160F">mcpgawk</text>
9
9
  <text x="258" y="270" font-family="Supreme, -apple-system, sans-serif" font-size="30" letter-spacing="0.5" fill="#6B6B5E">by <tspan fill="#2A33C2" font-weight="500">nativerse</tspan></text>
10
10
  <line x1="96" y1="352" x2="1184" y2="352" stroke="#E7E6E0" stroke-width="1.5"/>
11
- <text x="96" y="438" font-family="Sentient, Georgia, serif" font-size="42" fill="#3A3A30">gawk at it before you trust it.</text>
11
+ <text x="96" y="438" font-family="Sentient, Georgia, serif" font-size="42" fill="#3A3A30">Make it lean. Make it honest.</text>
12
12
  <text x="96" y="524" font-family="Tabular, ui-monospace, Menlo, monospace" font-size="25" fill="#717166"><tspan fill="#2A9d6b">●</tspan> cli-stdio 14 tools 2061 tok@connect 4 flagged</text>
13
13
  <circle cx="102" cy="576" r="6" fill="#2A9d6b"/>
14
- <text x="120" y="576" dominant-baseline="central" font-family="Tabular, ui-monospace, Menlo, monospace" font-size="24" letter-spacing="1" fill="#6B6B5E">local · no inventory ever leaves your machine</text>
14
+ <text x="120" y="576" dominant-baseline="central" font-family="Tabular, ui-monospace, Menlo, monospace" font-size="24" letter-spacing="1" fill="#6B6B5E">local · nothing leaves your machine</text>
15
15
  </svg>
@@ -0,0 +1 @@
1
+ mcp.gawk.dev
@@ -0,0 +1,178 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>mcpgawk · measure any MCP server, locally</title>
7
+ <meta name="description" content="A local-first CLI that measures what an MCP server costs and what it can do. Make MCP lean and honest. Nothing leaves your machine.">
8
+ <meta property="og:title" content="mcpgawk by nativerse">
9
+ <meta property="og:description" content="Every MCP server loads all its tools into your AI's context, on every request. mcpgawk measures the cost and the risk, locally.">
10
+ <meta property="og:image" content="assets/brand/social-preview.png">
11
+ <meta name="twitter:card" content="summary_large_image">
12
+ <link rel="icon" href="assets/brand/favicon.svg">
13
+ <link rel="preconnect" href="https://api.fontshare.com" crossorigin>
14
+ <link rel="stylesheet" href="https://api.fontshare.com/v2/css?f[]=sentient@400,500,700&f[]=supreme@400,500&f[]=tabular@400,500&display=swap">
15
+ <link rel="stylesheet" href="styles.css">
16
+ <style>
17
+ /* nav CTA is an .n-btn inside .nav-links, whose `a{color}` rule would darken the
18
+ label to ink on the blue button (reads as black). Keep the button's own colours. */
19
+ .nav-links a.n-btn{color:var(--on-accent)}
20
+ .nav-links a.n-btn:hover{color:var(--on-accent)}
21
+ .nav-links a.n-btn--secondary{color:var(--ink)}
22
+ </style>
23
+ </head>
24
+ <body>
25
+
26
+ <header class="nav">
27
+ <div class="nav-in">
28
+ <a class="brand" href="/">
29
+ <img class="brand-icon" src="assets/brand/nativerse-mark.svg" alt="">
30
+ <span class="brand-name">mcpgawk</span>
31
+ </a>
32
+ <nav class="nav-links">
33
+ <a href="https://github.com/gawk-dev/mcpgawk">GitHub</a>
34
+ <a href="https://pypi.org/project/mcpgawk/">PyPI</a>
35
+ <a class="n-btn n-btn--sm cta" href="#measure">Measure your stack</a>
36
+ </nav>
37
+ </div>
38
+ </header>
39
+
40
+ <!-- HERO -->
41
+ <section class="sec sec--hero">
42
+ <div class="wrap">
43
+ <div class="hero-grid">
44
+ <div class="hero-copy">
45
+ <p class="n-kicker n-kicker--accent">MCP connects your AI to everything</p>
46
+ <h1 class="n-hero" style="margin-top:16px">Make it lean. Make it honest.</h1>
47
+ <p class="n-lead" style="margin-top:20px;max-width:54ch">Connect an MCP server and it loads every tool into your AI's context. Every message. Used or not. You pay for those tokens, and you haven't checked what the tools can do. mcpgawk shows you both. <span class="n-em">Locally.</span></p>
48
+ <div class="cta-row">
49
+ <a class="n-btn" href="#measure">Measure your stack</a>
50
+ <a class="n-btn n-btn--secondary" href="https://github.com/gawk-dev/mcpgawk">View on GitHub →</a>
51
+ </div>
52
+ <p class="hero-trust">local · nothing uploaded · reproducible · Apache-2.0</p>
53
+ </div>
54
+ <div class="hero-proof">
55
+ <div class="proof-stack">
56
+ <div class="receipt">
57
+ <div class="receipt__head">
58
+ <span class="receipt__mark">$ mcpgawk scan mcp.json</span>
59
+ <span class="n-tag n-tag--ok"><span class="n-tag__dot"></span>LOCAL</span>
60
+ </div>
61
+ <div class="receipt__body">
62
+ <div class="receipt__line"><span class="receipt__k" style="color:var(--ok)">●</span><span class="receipt__id">cli-stdio&nbsp;&nbsp;proto=2025-11-25</span></div>
63
+ <div class="receipt__line"><span class="receipt__k"></span><span class="receipt__faint">14 tools&nbsp;&nbsp;2061 tok@connect</span></div>
64
+ <div class="receipt__line"><span class="receipt__k">·</span><span>write_file&nbsp;&nbsp;<span class="receipt__faint">131 tok · write, destructive-declared</span></span></div>
65
+ <div class="receipt__line"><span class="receipt__k">·</span><span>fetch_url&nbsp;&nbsp;<span class="receipt__faint">120 tok · exfil-capable</span></span></div>
66
+ <div class="receipt__sep"></div>
67
+ <div class="receipt__line"><span class="receipt__k"></span><span class="receipt__id">TOTAL&nbsp;&nbsp;14 tools · 2061 tok · 4 flagged</span></div>
68
+ </div>
69
+ <p class="receipt__cap">nothing uploaded · reproduce it yourself</p>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </section>
76
+
77
+ <!-- THE NUMBERS -->
78
+ <section class="sec">
79
+ <div class="wrap">
80
+ <div class="sec-head">
81
+ <p class="n-kicker">What we measured</p>
82
+ <h2 class="n-display">The two most-installed dev-tool MCPs add <span class="n-em">35,600 tokens</span> to every request.</h2>
83
+ <p class="n-lead">That's a quarter of your context window, spent before you ask anything. And it's each tool's weight that drives it, not how many: 20 tools can cost 6.5× more than 23.</p>
84
+ </div>
85
+ <div class="prose wide">
86
+ <table>
87
+ <thead><tr><th>Server</th><th class="num">Tools</th><th class="num">Tokens at connect</th></tr></thead>
88
+ <tbody>
89
+ <tr><td>Heaviest dev-tool MCP</td><td class="num">20</td><td class="num">23,085</td></tr>
90
+ <tr><td>The most-installed MCP</td><td class="num">44</td><td class="num">12,511</td></tr>
91
+ <tr><td>Cloudflare</td><td class="num">23</td><td class="num">3,570</td></tr>
92
+ <tr><td>Exa</td><td class="num">2</td><td class="num">520</td></tr>
93
+ </tbody>
94
+ </table>
95
+ </div>
96
+ <p class="n-small" style="margin-top:10px">Measured locally. Heavy servers anonymised; lean ones named.</p>
97
+ </div>
98
+ </section>
99
+
100
+ <!-- ACCURACY -->
101
+ <section class="sec">
102
+ <div class="wrap">
103
+ <div class="measure">
104
+ <p class="n-kicker">It also costs accuracy</p>
105
+ <h2 class="n-title" style="margin-top:14px">More tools. Worse answers.</h2>
106
+ <p class="n-body" style="margin-top:16px">Past a long tool list, the model picks the wrong tool more often. One study measured <span class="n-data">13.6%</span> correct, <span class="n-data">43.1%</span> once trimmed. Lean servers exist. It's a choice.</p>
107
+ </div>
108
+ </div>
109
+ </section>
110
+
111
+ <!-- MEASURE / CTA -->
112
+ <section class="sec" id="measure">
113
+ <div class="wrap">
114
+ <div class="flagship-grid">
115
+ <div>
116
+ <p class="n-kicker n-kicker--accent">Measure your own</p>
117
+ <h2 class="n-display" style="margin-top:14px">One command. Nothing leaves your machine.</h2>
118
+ <p class="n-lead" style="margin-top:16px;max-width:46ch">It talks only to the server you point it at. Nothing is uploaded, ever.</p>
119
+ <div class="links" style="display:flex;gap:20px;margin-top:24px;flex-wrap:wrap;align-items:center">
120
+ <a class="n-btn" href="https://github.com/gawk-dev/mcpgawk">Read the docs →</a>
121
+ <a class="n-arrowlink n-arrowlink--mono" href="https://pypi.org/project/mcpgawk/">pypi.org/project/mcpgawk</a>
122
+ </div>
123
+ </div>
124
+ <div class="codeblock">
125
+ <div class="codeblock__bar">
126
+ <span class="codeblock__dot"></span><span class="codeblock__dot"></span><span class="codeblock__dot"></span>
127
+ <span class="codeblock__file">terminal</span>
128
+ </div>
129
+ <pre class="codeblock__body"><span class="cb-c"># install</span>
130
+ <span class="cb-k">pip</span> install mcpgawk
131
+
132
+ <span class="cb-c"># scan your whole MCP config</span>
133
+ <span class="cb-k">mcpgawk</span> scan mcp.json
134
+
135
+ <span class="cb-c"># or a single server / remote</span>
136
+ <span class="cb-k">mcpgawk</span> scan --http https://host/mcp
137
+ <span class="cb-k">mcpgawk</span> scan mcp.json --track <span class="cb-c"># detect rug-pulls</span></pre>
138
+ </div>
139
+ </div>
140
+ </div>
141
+ </section>
142
+
143
+ <!-- FOOTER -->
144
+ <footer>
145
+ <div class="foot-grid">
146
+ <div class="foot-brand">
147
+ <a class="brand" href="/"><img class="brand-icon" src="assets/brand/nativerse-mark.svg" alt=""><span class="brand-name">mcpgawk</span></a>
148
+ <p class="footer-motto">Make MCP lean and honest.<small>by nativerse · gawk.dev</small></p>
149
+ </div>
150
+ <div class="foot-col">
151
+ <p class="n-kicker">Project</p>
152
+ <div class="links">
153
+ <a href="https://github.com/gawk-dev/mcpgawk">GitHub</a>
154
+ <a href="https://pypi.org/project/mcpgawk/">PyPI</a>
155
+ <a href="https://github.com/gawk-dev/mcpgawk/releases">Releases</a>
156
+ </div>
157
+ </div>
158
+ <div class="foot-col">
159
+ <p class="n-kicker">Trust</p>
160
+ <div class="links">
161
+ <a href="https://github.com/gawk-dev/mcpgawk/blob/main/THREAT-MODEL.md">Threat model</a>
162
+ <a href="https://github.com/gawk-dev/mcpgawk/blob/main/SECURITY.md">Security</a>
163
+ <a href="https://github.com/gawk-dev/mcpgawk/blob/main/LICENSE">Apache-2.0</a>
164
+ </div>
165
+ </div>
166
+ <div class="foot-col">
167
+ <p class="n-kicker">Family</p>
168
+ <div class="links">
169
+ <a href="https://nativerse-ventures.com">nativerse</a>
170
+ <a href="https://gawk.dev">gawk.dev</a>
171
+ </div>
172
+ </div>
173
+ </div>
174
+ <p class="foot-legal n-small">Tokens counted with a named index (cl100k) — a comparable ruler, not exact Claude billing; the <em>relative</em> picture holds. This is a cost view; per-vendor capability findings go to vendors privately. Free &amp; open-source.</p>
175
+ </footer>
176
+
177
+ </body>
178
+ </html>
@@ -0,0 +1,530 @@
1
+ /* Fonts load via <link rel="stylesheet"> in each page <head> (parallel fetch,
2
+ better LCP than @import). Fontshare: Sentient · Supreme · Tabular. */
3
+
4
+ /* ============================================================================
5
+ NATIVERSE DESIGN SYSTEM · light, editorial, paper-and-ink, one royal-blue
6
+ accent. Hairline-first structure, 4px spacing scale, restrained radii/motion.
7
+ Type: "Warm technical" — Sentient (display) · Supreme (text) · Tabular (mono).
8
+ ============================================================================ */
9
+
10
+ :root{
11
+ /* paper */
12
+ --paper:#FFFFFF; --paper-2:#FAFAF6; --paper-3:#F2F1EA;
13
+ /* ink (warm near-black) */
14
+ --ink:#16160F; --ink-2:#3A3A30; --ink-muted:#6B6B5E; --ink-faint:#717166;
15
+ /* hairlines */
16
+ --hairline:#E7E6DE; --hairline-2:#D8D7CC;
17
+ /* ink-faint darkened for WCAG AA: #9A9A8B -> #75756A -> #717166. The 4.66:1 figure
18
+ was on pure-white --paper; small mono text sits on --paper-2 #FAFAF6 where #75756A
19
+ was 4.45:1 (fails). #717166 is 4.71:1 on #FAFAF6, still lighter than --ink-muted. */
20
+ /* accent */
21
+ --blue:#2A33C2; --blue-strong:#1F2799;
22
+ --blue-tint:rgba(42,51,194,.06); --blue-tint-2:rgba(42,51,194,.12); --blue-ring:rgba(42,51,194,.38);
23
+ /* status */
24
+ --ok:#157A40; --warn:#B26A00; --danger:#C0392B; /* ok darkened #1F8A4C -> #157A40: 3.83:1 -> 4.75:1 as tag text on its tint */
25
+ /* aliases */
26
+ --surface:var(--paper); --text-strong:var(--ink); --text-body:var(--ink-2);
27
+ --text-muted:var(--ink-muted); --text-faint:var(--ink-faint);
28
+ --border:var(--hairline); --accent:var(--blue); --accent-press:var(--blue-strong);
29
+ --link:var(--blue); --focus-ring:var(--blue-ring); --on-accent:var(--paper);
30
+
31
+ /* spacing (4px base) */
32
+ --space-1:4px; --space-2:8px; --space-3:12px; --space-4:16px; --space-5:24px;
33
+ --space-6:32px; --space-7:48px; --space-8:64px; --space-9:96px; --space-10:128px;
34
+ /* layout */
35
+ --measure:66ch; --measure-sm:48ch; --gutter:clamp(20px,5vw,56px);
36
+ --maxw:1180px; --maxw-wide:1320px; --section-y:clamp(48px,6vw,88px);
37
+ /* radii */
38
+ --radius-sm:2px; --radius:4px; --radius-lg:8px; --radius-pill:999px;
39
+ /* elevation */
40
+ --shadow-1:0 1px 2px rgba(22,22,15,.05);
41
+ --shadow-2:0 10px 28px -16px rgba(22,22,15,.14);
42
+ --shadow-3:0 24px 56px -28px rgba(22,22,15,.20);
43
+ /* motion */
44
+ --ease:cubic-bezier(.2,0,0,1); --ease-out:cubic-bezier(.16,1,.3,1);
45
+ --dur-fast:120ms; --dur:200ms; --dur-slow:360ms;
46
+
47
+ /* type */
48
+ --fallback-serif:"Iowan Old Style",Palatino,Georgia,"Times New Roman",serif;
49
+ --fallback-sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
50
+ --fallback-mono:ui-monospace,"SF Mono","Roboto Mono",Menlo,Consolas,monospace;
51
+ --font-display:"Sentient",var(--fallback-serif);
52
+ --font-text:"Supreme",var(--fallback-sans);
53
+ --font-mono:"Tabular",var(--fallback-mono);
54
+ --text-2xs:.6875rem; --text-xs:.75rem; --text-sm:.8125rem;
55
+ --text-base:clamp(1rem,.97rem + .16vw,1.125rem);
56
+ --text-md:clamp(1.125rem,1.08rem + .22vw,1.3125rem);
57
+ --text-lg:clamp(1.3125rem,1.22rem + .45vw,1.625rem);
58
+ --text-xl:clamp(1.625rem,1.45rem + .85vw,2.25rem);
59
+ --text-2xl:clamp(2.1rem,1.75rem + 1.7vw,3.25rem);
60
+ --text-3xl:clamp(2.9rem,2rem + 4.4vw,5.5rem);
61
+ --weight-regular:400; --weight-medium:500; --weight-bold:700;
62
+ --tracking-hero:-.025em; --tracking-display:-.018em; --tracking-tight:-.008em;
63
+ --tracking-mono:.01em; --tracking-kicker:.18em;
64
+ --leading-hero:1.02; --leading-display:1.1; --leading-snug:1.28; --leading-body:1.62; --leading-mono:1.45;
65
+ --otf-data:"tnum" 1,"lnum" 1,"zero" 1; --otf-prose:"kern" 1,"liga" 1,"calt" 1;
66
+ font-synthesis:none;
67
+ }
68
+
69
+ /* ---- base ---- */
70
+ *,*::before,*::after{box-sizing:border-box}
71
+ html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;font-feature-settings:var(--otf-prose);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background:var(--paper)}
72
+ body{margin:0;background:transparent;color:var(--text-body);font-family:var(--font-text);font-size:var(--text-base);line-height:var(--leading-body);text-rendering:optimizeLegibility}
73
+ /* Ambient boids background layer (boids.js): sits behind all content over the
74
+ page ground; the readable column is kept clear in JS so it only paints in the
75
+ margins. Disabled under reduced motion (JS renders one static frame). */
76
+ #nv-boids{position:fixed;inset:0;width:100%;height:100%;z-index:-1;pointer-events:none}
77
+ h1,h2,h3,h4,h5,h6{margin:0;font-family:var(--font-display);font-weight:var(--weight-medium);color:var(--text-strong);letter-spacing:var(--tracking-display);line-height:var(--leading-display);text-wrap:balance}
78
+ p{margin:0;text-wrap:pretty}
79
+ a{color:var(--link);text-decoration:none;text-underline-offset:.16em;transition:color var(--dur-fast) var(--ease)}
80
+ a:hover{color:var(--accent-press)}
81
+ code,kbd,samp,pre{font-family:var(--font-mono);font-feature-settings:var(--otf-data)}
82
+ img,svg{vertical-align:middle;max-width:100%}
83
+ ::selection{background:var(--blue-tint-2);color:var(--text-strong)}
84
+ :focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px;border-radius:var(--radius-sm)}
85
+
86
+ /* ---- layout helpers ---- */
87
+ .wrap{max-width:var(--maxw);margin:0 auto;padding:0 var(--gutter)}
88
+ .sec{padding-bottom:var(--section-y)}
89
+ .sec--tight{padding-bottom:var(--space-6)}
90
+ .sec--hero{padding-top:var(--section-y);padding-bottom:var(--space-8)}
91
+ .measure{max-width:var(--measure)}
92
+ .sec-head{max-width:var(--measure);margin-bottom:var(--space-7)}
93
+ .sec-head h2{margin-top:14px}
94
+ .sec-head>h2:first-child{margin-top:0}
95
+ .lab .statement:first-child{margin-top:0}
96
+ .sec-head .n-lead{margin-top:16px}
97
+
98
+ /* ============================================================================
99
+ TYPOGRAPHY ROLE CLASSES
100
+ ============================================================================ */
101
+ .n-kicker{font-family:var(--font-mono);font-size:var(--text-sm);font-weight:var(--weight-medium);letter-spacing:var(--tracking-kicker);text-transform:uppercase;font-variant-caps:all-small-caps;font-feature-settings:"smcp" 1,"c2sc" 1;color:var(--text-muted);line-height:1;display:inline-block}
102
+ .n-kicker--accent{color:var(--accent)}
103
+ .n-hero{font-family:var(--font-display);font-size:var(--text-3xl);font-weight:var(--weight-medium);letter-spacing:var(--tracking-hero);line-height:var(--leading-hero);color:var(--text-strong);text-wrap:balance}
104
+ .n-display{font-family:var(--font-display);font-size:var(--text-2xl);font-weight:var(--weight-medium);letter-spacing:var(--tracking-display);line-height:var(--leading-display);color:var(--text-strong);text-wrap:balance}
105
+ .n-title{font-family:var(--font-display);font-size:var(--text-xl);font-weight:var(--weight-medium);letter-spacing:var(--tracking-tight);line-height:var(--leading-display);color:var(--text-strong);text-wrap:balance}
106
+ .n-subtitle{font-family:var(--font-display);font-size:var(--text-lg);font-weight:var(--weight-regular);letter-spacing:var(--tracking-tight);line-height:var(--leading-snug);color:var(--text-strong)}
107
+ .n-lead{font-family:var(--font-text);font-size:var(--text-md);font-weight:var(--weight-regular);line-height:var(--leading-snug);color:var(--text-body);text-wrap:pretty}
108
+ .n-body{font-family:var(--font-text);font-size:var(--text-base);font-weight:var(--weight-regular);line-height:var(--leading-body);color:var(--text-body);font-feature-settings:var(--otf-prose);text-wrap:pretty}
109
+ .n-small{font-family:var(--font-text);font-size:var(--text-sm);line-height:var(--leading-snug);color:var(--text-muted)}
110
+ .n-mono{font-family:var(--font-mono);font-size:var(--text-sm);letter-spacing:var(--tracking-mono);line-height:var(--leading-mono);font-feature-settings:var(--otf-data);color:var(--text-body)}
111
+ .n-data{font-family:var(--font-mono);font-weight:var(--weight-medium);font-variant-numeric:tabular-nums lining-nums slashed-zero;font-feature-settings:var(--otf-data);letter-spacing:-.01em;color:var(--text-strong)}
112
+
113
+ /* emphasis device — royal-blue phrase on a hairline blue underline rule */
114
+ .n-em{color:var(--accent);font-weight:inherit;text-decoration:underline;text-decoration-color:var(--accent);text-decoration-thickness:1.5px;text-underline-offset:.16em;text-decoration-skip-ink:none}
115
+ .n-hero .n-em,.n-display .n-em,.n-title .n-em{text-decoration-thickness:1px;text-underline-offset:.12em}
116
+
117
+ /* ============================================================================
118
+ COMPONENTS
119
+ ============================================================================ */
120
+ /* Button */
121
+ .n-btn{--_bg:var(--blue);--_fg:var(--on-accent);--_bd:transparent;display:inline-flex;align-items:center;gap:.55em;font-family:var(--font-text);font-weight:var(--weight-medium);line-height:1;white-space:nowrap;border:1px solid var(--_bd);background:var(--_bg);color:var(--_fg);border-radius:var(--radius);cursor:pointer;text-decoration:none;font-size:var(--text-base);padding:.7em 1.15em;transition:background var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease)}
122
+ .n-btn:hover{background:var(--blue-strong);color:var(--on-accent);text-decoration:none}
123
+ .n-btn:active{transform:translateY(.5px)}
124
+ .n-btn--sm{font-size:var(--text-sm);padding:.55em .85em}
125
+ .n-btn--secondary{--_bg:transparent;--_fg:var(--ink);--_bd:var(--hairline-2)}
126
+ .n-btn--secondary:hover{background:var(--paper-2);border-color:var(--ink);color:var(--ink)}
127
+ .n-btn--ghost{--_bg:transparent;--_fg:var(--ink);--_bd:transparent;padding-left:.4em;padding-right:.4em}
128
+ .n-btn--ghost:hover{background:transparent;color:var(--blue)}
129
+ .n-btn__arrow{transition:transform var(--dur) var(--ease-out)}
130
+ .n-btn:hover .n-btn__arrow--right{transform:translateX(3px)}
131
+ .n-btn:hover .n-btn__arrow--down{transform:translateY(2px)}
132
+
133
+ /* ArrowLink */
134
+ .n-arrowlink{display:inline-flex;align-items:center;gap:.4em;font-family:var(--font-text);font-weight:var(--weight-medium);font-size:var(--text-base);color:var(--blue);text-decoration:none;cursor:pointer;width:fit-content}
135
+ .n-arrowlink--mono{font-family:var(--font-mono);font-size:var(--text-sm);letter-spacing:.02em}
136
+ .n-arrowlink__label{text-decoration:underline;text-decoration-color:transparent;text-underline-offset:.16em;transition:text-decoration-color var(--dur-fast) var(--ease)}
137
+ .n-arrowlink:hover{color:var(--blue-strong)}
138
+ .n-arrowlink:hover .n-arrowlink__label{text-decoration-color:currentColor}
139
+ .n-arrowlink__arrow{transition:transform var(--dur) var(--ease-out)}
140
+ .n-arrowlink:hover .n-arrowlink__arrow--right{transform:translateX(3px)}
141
+ .n-arrowlink:hover .n-arrowlink__arrow--down{transform:translateY(2px)}
142
+ .n-arrowlink:hover .n-arrowlink__arrow--external{transform:translate(2px,-2px)}
143
+
144
+ /* Tag */
145
+ .n-tag{display:inline-flex;align-items:center;gap:.4em;font-family:var(--font-mono);font-size:var(--text-xs);letter-spacing:.06em;text-transform:uppercase;font-weight:var(--weight-medium);padding:.28em .6em;border-radius:var(--radius-sm);border:1px solid var(--hairline-2);color:var(--ink-muted);background:transparent;white-space:nowrap}
146
+ .n-tag__dot{width:6px;height:6px;border-radius:999px;background:currentColor;flex:none}
147
+ .n-tag--ok{color:var(--ok);border-color:color-mix(in srgb,var(--ok) 40%,transparent);background:color-mix(in srgb,var(--ok) 7%,transparent)}
148
+ .n-tag--accent{color:var(--blue);border-color:color-mix(in srgb,var(--blue) 40%,transparent);background:var(--blue-tint)}
149
+
150
+ /* Card */
151
+ .n-card{display:block;background:var(--surface);border:1px solid var(--hairline);border-radius:var(--radius);padding:var(--space-6);text-decoration:none;color:inherit;transition:border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease),transform var(--dur) var(--ease)}
152
+ .n-card--sunk{background:var(--paper-2)}
153
+ .n-card--interactive{cursor:pointer}
154
+ .n-card--interactive:hover{border-color:var(--ink);box-shadow:var(--shadow-2);transform:translateY(-2px);text-decoration:none}
155
+ .n-card--accent{position:relative}
156
+ .n-card--accent::before{content:"";position:absolute;left:-1px;right:-1px;top:-1px;height:2px;background:var(--blue);border-radius:var(--radius) var(--radius) 0 0;opacity:0;transition:opacity var(--dur) var(--ease)}
157
+ .n-card--accent.n-card--interactive:hover::before{opacity:1}
158
+ .n-card--dashed{background:transparent;border-style:dashed}
159
+
160
+ /* Stat */
161
+ .n-stat{display:flex;flex-direction:column;gap:.35em}
162
+ .n-stat--rule{padding-left:.7em;border-left:2px solid var(--blue)}
163
+ .n-stat__value{font-family:var(--font-mono);font-weight:var(--weight-medium);font-variant-numeric:tabular-nums lining-nums slashed-zero;letter-spacing:-.01em;line-height:1;color:var(--ink);font-size:var(--text-2xl)}
164
+ .n-stat__label{font-family:var(--font-mono);font-size:var(--text-xs);letter-spacing:.04em;color:var(--ink-muted);line-height:1.3}
165
+
166
+ /* Avatar */
167
+ .n-avatar{width:80px;height:80px;border-radius:var(--radius-pill);background:var(--blue);color:var(--paper);display:flex;align-items:center;justify-content:center;font-family:var(--font-display);font-size:2rem;letter-spacing:-.02em;flex:none}
168
+
169
+ /* ============================================================================
170
+ SITE CHROME — nav + footer
171
+ ============================================================================ */
172
+ .nav{position:sticky;top:0;z-index:20;background:color-mix(in srgb,var(--paper) 88%,transparent);backdrop-filter:saturate(140%) blur(10px);-webkit-backdrop-filter:saturate(140%) blur(10px);border-bottom:1px solid var(--hairline)}
173
+ .nav-in{max-width:var(--maxw);margin:0 auto;padding:0 var(--gutter);height:72px;display:flex;align-items:center;justify-content:space-between;gap:24px}
174
+ .brand{display:flex;align-items:center;gap:10px;text-decoration:none}
175
+ .brand-icon{height:34px;width:auto;display:block}
176
+ .brand-name{font-family:var(--font-text);font-weight:600;font-size:22px;letter-spacing:-.02em;color:var(--ink);line-height:1}
177
+ .brand:hover .brand-name{color:var(--ink)}
178
+ .nav-links{display:flex;align-items:center;gap:28px}
179
+ .nav-links a{font-family:var(--font-text);font-size:14px;font-weight:500;color:var(--ink-2);text-decoration:none}
180
+ .nav-links a:hover{color:var(--ink)}
181
+ .nav-links a.is-active{color:var(--blue)}
182
+
183
+ /* hero CTA group */
184
+ .cta-row{display:flex;gap:14px;margin-top:var(--space-6);flex-wrap:wrap}
185
+ .cta-row>.n-btn{flex:0 0 auto}
186
+
187
+ /* Hero split + proof receipt (the artifact equiv emits, rendered as the hero visual) */
188
+ .hero-grid{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,.9fr);gap:clamp(32px,5vw,64px);align-items:stretch}
189
+ .hero-proof{align-self:stretch;display:flex}
190
+ .proof-stack{display:flex;flex-direction:column;gap:18px;justify-content:flex-start;width:100%}
191
+ .hero-trust{margin-top:22px;font-family:var(--font-mono);font-size:var(--text-xs);letter-spacing:.03em;color:var(--ink-faint);line-height:1.7;max-width:52ch}
192
+ .sec--hero .n-hero{font-size:clamp(2.4rem,4.4vw,3.5rem)}
193
+ .hero-tracks{margin-top:14px;font-family:var(--font-mono);font-size:var(--text-sm);letter-spacing:.05em;color:var(--blue)}
194
+ .track-lift{margin-top:20px;max-width:58ch;padding-top:16px;border-top:1px solid var(--hairline);color:var(--ink-muted);line-height:var(--leading-snug)}
195
+ .track-lift__k{display:block;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:.14em;text-transform:uppercase;color:var(--blue);margin-bottom:6px}
196
+ .track-lift__k::before{content:"// "}
197
+ .hero-gawk .receipt__body{gap:0}
198
+ .hero-gawk .srcrow{padding:10px 0;border-bottom:1px solid var(--hairline)}
199
+ .hero-gawk .srcrow:last-of-type{border-bottom:0}
200
+
201
+ /* code artifact (equiv flagship) */
202
+ .codeblock{background:var(--paper);border:1px solid var(--hairline-2);border-radius:var(--radius-lg);overflow:hidden;font-family:var(--font-mono);align-self:start}
203
+ .codeblock__bar{display:flex;align-items:center;gap:7px;padding:11px 16px;background:var(--paper-3);border-bottom:1px solid var(--hairline)}
204
+ .codeblock__dot{width:9px;height:9px;border-radius:50%;background:var(--hairline-2);display:inline-block}
205
+ .codeblock__file{margin-left:10px;font-size:var(--text-2xs);color:var(--ink-muted);letter-spacing:.02em}
206
+ .codeblock__body{margin:0;padding:18px;font-size:var(--text-sm);line-height:1.7;color:var(--ink-2);white-space:pre-wrap;overflow-x:auto}
207
+ .cb-c{color:var(--ink-faint)}
208
+ .cb-k{color:var(--blue)}
209
+ .cb-ok{color:var(--ok)}
210
+ .cb-no{color:var(--danger)}
211
+ .cb-d{color:var(--ink-muted)}
212
+ .receipt{font-family:var(--font-mono);font-size:var(--text-sm);line-height:1.55;color:var(--ink-2);background:var(--paper-2);border:1px solid var(--hairline-2);border-radius:var(--radius);padding:var(--space-5)}
213
+ .receipt__head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-bottom:12px;margin-bottom:14px;border-bottom:1px solid var(--hairline)}
214
+ .receipt__mark{color:var(--ink-muted);letter-spacing:.02em}
215
+ .receipt__status{font-size:var(--text-2xs);letter-spacing:.08em;text-transform:uppercase;color:var(--danger);border:1px solid color-mix(in srgb,var(--danger) 40%,transparent);background:color-mix(in srgb,var(--danger) 7%,transparent);border-radius:var(--radius-sm);padding:.25em .55em;white-space:nowrap}
216
+ .receipt__body{display:flex;flex-direction:column;gap:6px}
217
+ .receipt__line{display:grid;grid-template-columns:1.6em 1fr;gap:10px;align-items:baseline}
218
+ .receipt__k{color:var(--ink-faint)}
219
+ .receipt__diverge .receipt__k,.receipt__diverge span{color:var(--danger)}
220
+ .receipt__faint{color:var(--ink-faint)}
221
+ .receipt__id{color:var(--ink);font-weight:var(--weight-medium);letter-spacing:.01em}
222
+ .receipt__sep{height:1px;background:var(--hairline);margin:8px 0}
223
+ .receipt__cap{margin:14px 0 0;font-size:var(--text-2xs);letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint)}
224
+
225
+ /* Flagship / Gawk */
226
+ .flagship-grid{display:grid;grid-template-columns:1.5fr 1fr;gap:48px;align-items:start}
227
+ .flagship-grid .meta-row{display:flex;align-items:center;gap:12px;margin-bottom:16px}
228
+ .flagship-grid .stat-col{display:flex;flex-direction:column;gap:24px;padding-top:6px}
229
+ .flagship-grid .links{display:flex;gap:20px;margin-top:24px;flex-wrap:wrap;align-items:center}
230
+
231
+ /* Gawk proof artifact — shows the cite + grey-out mechanic (replaces hero-metric stats) */
232
+ .gawk-proof{border:1px solid var(--hairline-2);border-radius:var(--radius);background:var(--paper);overflow:hidden;font-family:var(--font-mono)}
233
+ .srcrow{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:12px;padding:13px 16px;border-bottom:1px solid var(--hairline);font-size:var(--text-sm)}
234
+ .srcrow__dot{width:7px;height:7px;border-radius:50%;background:var(--ok);flex:none}
235
+ .srcrow__name{color:var(--ink-2);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
236
+ .srcrow__cite{font-size:var(--text-2xs);letter-spacing:.04em;color:var(--blue);white-space:nowrap}
237
+ /* stale source: dim via colour, not opacity (opacity:.5 halved contrast below AA) */
238
+ .srcrow--stale .srcrow__name{color:var(--ink-muted)}
239
+ .srcrow--stale .srcrow__dot{background:var(--ink-faint)}
240
+ .srcrow--stale .srcrow__cite{color:var(--ink-faint)}
241
+ .gawk-proof__cap{margin:0;padding:12px 16px;background:var(--paper-2);border-top:1px solid var(--hairline);font-size:var(--text-2xs);text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint)}
242
+
243
+ /* Tools grid */
244
+ .tools-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}
245
+ .tools-grid--three{grid-template-columns:repeat(3,1fr)}
246
+ .tool-group{margin-top:44px}
247
+ .tool-group:first-of-type{margin-top:8px}
248
+ .tool-group__label{display:block;margin-bottom:18px;color:var(--ink-muted)}
249
+ .risk-note{color:var(--ink-2);background:var(--blue-tint);border-radius:var(--radius);padding:13px 16px}
250
+ /* subordinate edge tools */
251
+ .edge-card{display:flex;flex-direction:column;padding:var(--space-5);background:var(--paper-2)}
252
+ .edge-card h3{margin-top:0;font-size:var(--text-lg)}
253
+ .edge-card .body{margin-top:10px;flex:1;color:var(--ink-muted)}
254
+ .edge-card .foot{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:16px}
255
+ .work-cta{margin-top:32px;display:flex;align-items:center;gap:20px;flex-wrap:wrap}
256
+ .work-cta .n-small{max-width:46ch}
257
+ /* blog index */
258
+ .blog-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:20px}
259
+ .blog-card{display:flex;flex-direction:column;min-height:230px}
260
+ .blog-card .meta{display:flex;align-items:center;gap:10px;margin-bottom:14px}
261
+ .blog-card h3{margin:0}
262
+ .blog-card .excerpt{margin-top:12px;flex:1 1 auto;color:var(--ink-2);text-wrap:pretty}
263
+ .blog-card .foot{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:20px;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:.05em;text-transform:uppercase;color:var(--ink-muted)}
264
+ .blog-card .foot .go{color:var(--blue)}
265
+ .blog-card--lead{grid-column:1 / -1}
266
+ .blog-card--lead h3{font-size:var(--text-xl)}
267
+ .blog-card--lead .excerpt{max-width:64ch;font-size:var(--text-md)}
268
+ /* mandate form */
269
+ .mandate-form{margin-top:36px;max-width:620px}
270
+ .mandate-form__intro{color:var(--ink-muted);max-width:56ch;margin-bottom:22px}
271
+ .mf-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
272
+ .mf-field{display:block;margin-bottom:16px}
273
+ .mf-label{display:block;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:.12em;text-transform:uppercase;color:var(--ink-muted);margin-bottom:7px}
274
+ .mf-input{width:100%;font-family:var(--font-text);font-size:var(--text-base);color:var(--ink);background:var(--paper);border:1px solid var(--hairline-2);border-radius:var(--radius);padding:11px 13px;transition:border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
275
+ .mf-input:focus{outline:none;border-color:var(--blue);box-shadow:0 0 0 3px var(--focus-ring)}
276
+ .mf-textarea{resize:vertical;min-height:108px;line-height:var(--leading-snug)}
277
+ .mf-hp{position:absolute;left:-9999px;width:1px;height:1px;opacity:0;pointer-events:none}
278
+ .mf-actions{display:flex;align-items:center;gap:18px;margin-top:6px;flex-wrap:wrap}
279
+ .mf-status{color:var(--ink-muted)}
280
+ .mf-status.is-ok{color:var(--ok)}
281
+ .mf-status.is-err{color:var(--danger)}
282
+ .mandate-form.is-sent .mf-grid,.mandate-form.is-sent .mf-textarea,.mandate-form.is-sent #mf-submit{display:none}
283
+ @media(max-width:560px){.mf-grid{grid-template-columns:1fr}}
284
+ .tool-card{display:flex;flex-direction:column}
285
+ .tool-card h3{margin-top:8px}
286
+ .tool-card .body{margin-top:12px;flex:1}
287
+ .tool-card .foot{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:18px}
288
+ .tool-card.dashed{align-items:center;justify-content:center}
289
+
290
+ /* Research rows */
291
+ .research-row{display:grid;grid-template-columns:minmax(0,1fr) 200px;gap:32px;padding:var(--space-6) 0;border-top:1px solid var(--hairline);text-decoration:none}
292
+ .research-row:last-of-type{border-bottom:1px solid var(--hairline)}
293
+ .research-row .meta-row{display:flex;align-items:center;gap:10px;margin-bottom:10px}
294
+ .research-row h3{max-width:30ch}
295
+ .research-row .read{display:flex;align-items:flex-start;justify-content:flex-end;padding-top:28px}
296
+
297
+ /* Services / Work */
298
+ .service-list{border-top:1px solid var(--hairline)}
299
+ .service-row{display:grid;grid-template-columns:220px minmax(0,1fr);gap:32px;padding:var(--space-6) 0;border-bottom:1px solid var(--hairline)}
300
+ .service-row__label{font-family:var(--font-mono);font-size:var(--text-sm);letter-spacing:.02em;color:var(--ink-muted);line-height:1.4}
301
+ .service-row h3{margin:0}
302
+ .service-row p{margin-top:10px;max-width:58ch}
303
+ @media(max-width:640px){.service-row{grid-template-columns:1fr;gap:8px}}
304
+ @media(max-width:640px){.nav-links{gap:14px}.nav-links a:not(.cta){display:none}}
305
+ @media(max-width:560px){.sec--hero .n-hero{font-size:clamp(2rem,8.5vw,2.9rem);max-width:none}.cta-row .n-btn--ghost{display:none}}
306
+
307
+ /* Lab dark band */
308
+ .lab{background:var(--ink);color:var(--paper)}
309
+ .lab .wrap{padding-top:var(--space-9);padding-bottom:var(--space-9)}
310
+ .lab .n-kicker{color:var(--ink-faint)}
311
+ .lab .statement{margin-top:20px;max-width:60ch;color:var(--paper);font-family:var(--font-display);font-size:var(--text-lg);line-height:var(--leading-snug);letter-spacing:var(--tracking-tight)}
312
+ .lab .statement .em{color:#9aa0ff;text-decoration:underline;text-decoration-color:#9aa0ff;text-underline-offset:.12em;text-decoration-thickness:1px}
313
+ .lab .sub{margin-top:22px;max-width:60ch;color:rgba(255,255,255,.72)}
314
+ .lab .links{display:flex;gap:22px;margin-top:30px;flex-wrap:wrap}
315
+ .lab .links a{font-family:var(--font-text);font-weight:500;font-size:15px;text-decoration:none}
316
+ .lab .links a.accent{color:#9aa0ff}
317
+ .lab .links a.dim{color:rgba(255,255,255,.72)}
318
+
319
+ /* People */
320
+ .person-grid{display:grid;grid-template-columns:auto 1fr;gap:32px;align-items:start}
321
+ .person-grid .chips{display:flex;gap:8px;flex-wrap:wrap;margin-top:16px}
322
+ .person-grid .links{display:flex;gap:20px;margin-top:22px;flex-wrap:wrap}
323
+ .domain-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}
324
+ .domain-card .links{display:flex;gap:16px;margin-top:16px;flex-wrap:wrap}
325
+ .collab-band{background:var(--paper-2);border-top:1px solid var(--hairline);border-bottom:1px solid var(--hairline)}
326
+ .collab-band .wrap{padding-top:var(--space-8);padding-bottom:var(--space-8);display:flex;align-items:center;justify-content:space-between;gap:32px;flex-wrap:wrap}
327
+
328
+ /* ---- Proof pill (hero) ---- */
329
+ .pill{display:inline-flex;align-items:center;gap:12px;border:1px solid var(--hairline-2);border-radius:var(--radius-pill);padding:5px 6px 5px 16px;font-size:var(--text-sm);color:var(--ink-2);background:var(--paper);text-decoration:none;width:fit-content;max-width:100%;transition:border-color var(--dur) var(--ease)}
330
+ .pill:hover{border-color:var(--ink);text-decoration:none}
331
+ .pill .dot{width:6px;height:6px;border-radius:50%;background:var(--blue);flex:none}
332
+ .pill .txt{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
333
+ .pill .read{display:inline-flex;align-items:center;gap:5px;font-family:var(--font-mono);font-size:var(--text-2xs);text-transform:uppercase;letter-spacing:.08em;color:var(--blue);background:var(--blue-tint);border-radius:var(--radius-pill);padding:5px 11px;flex:none}
334
+
335
+ /* ---- Ecosystem sources row (monochrome marks) ---- */
336
+ .sources{display:flex;align-items:center;gap:20px;flex-wrap:wrap;margin-top:26px;padding-top:22px;border-top:1px solid var(--hairline)}
337
+ .sources .lbl{font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:.1em;text-transform:uppercase;color:var(--ink-faint)}
338
+ .sources .marks{display:flex;align-items:center;gap:20px;flex-wrap:wrap}
339
+ .mark{width:22px;height:22px;background:var(--ink-faint);-webkit-mask:var(--m) center/contain no-repeat;mask:var(--m) center/contain no-repeat;transition:background var(--dur) var(--ease)}
340
+ .sources:hover .mark{background:var(--ink-muted)}
341
+
342
+ /* ---- Scroll reveal (no-JS safe: only hides once .reveal-on is set by JS) ---- */
343
+ .reveal-on .reveal{opacity:0;transform:translateY(14px);transition:opacity .6s var(--ease-out),transform .6s var(--ease-out)}
344
+ .reveal-on .reveal.is-visible{opacity:1;transform:none}
345
+ @media(prefers-reduced-motion:reduce){.reveal-on .reveal{opacity:1;transform:none;transition:none}}
346
+
347
+ /* ---- Staggered reveal: children cascade in (replaces the uniform section fade) ---- */
348
+ .reveal-on .stagger>*{opacity:0;transform:translateY(10px);transition:opacity .5s var(--ease-out),transform .5s var(--ease-out)}
349
+ .reveal-on .stagger.is-visible>*{opacity:1;transform:none}
350
+ .reveal-on .stagger.is-visible>*:nth-child(2){transition-delay:55ms}
351
+ .reveal-on .stagger.is-visible>*:nth-child(3){transition-delay:110ms}
352
+ .reveal-on .stagger.is-visible>*:nth-child(4){transition-delay:165ms}
353
+ .reveal-on .stagger.is-visible>*:nth-child(5){transition-delay:220ms}
354
+ .reveal-on .stagger.is-visible>*:nth-child(6){transition-delay:275ms}
355
+ @media(prefers-reduced-motion:reduce){.reveal-on .stagger>*{opacity:1;transform:none;transition:none}}
356
+
357
+ /* Gawk grey-out demo: a live source visibly drops to its kept last value */
358
+ .srcrow{transition:opacity .55s var(--ease-out)}
359
+ .srcrow__dot{transition:background .55s var(--ease-out)}
360
+
361
+ /* ---- Watery cursor glow ---- */
362
+ .cursor-glow{position:fixed;left:0;top:0;width:360px;height:360px;border-radius:50%;pointer-events:none;z-index:3;opacity:0;transition:opacity .5s ease;background:radial-gradient(circle,rgba(42,51,194,.10),rgba(42,51,194,.05) 38%,transparent 70%);mix-blend-mode:multiply;filter:blur(6px);will-change:transform}
363
+ .cursor-on .cursor-glow{opacity:1}
364
+ @media(prefers-reduced-motion:reduce){.cursor-glow{display:none}}
365
+
366
+ /* ---- Craft note ---- */
367
+ .craft{font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint)}
368
+ .craft a{color:var(--ink-muted);text-decoration:none}
369
+ .craft a:hover{color:var(--blue)}
370
+
371
+ /* ---- Advisors ---- */
372
+ .advisor-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
373
+ .advisor{border:1px solid var(--hairline);border-radius:var(--radius);padding:var(--space-5);display:flex;flex-direction:column;gap:16px;transition:border-color var(--dur) var(--ease)}
374
+ .advisor:hover{border-color:var(--hairline-2)}
375
+ .advisor .top{display:flex;align-items:center;gap:14px}
376
+ .advisor .ava{width:54px;height:54px;border-radius:var(--radius-pill);flex:none;background:var(--paper-3);color:var(--ink-2);display:flex;align-items:center;justify-content:center;font-family:var(--font-display);font-size:19px;letter-spacing:-.01em;overflow:hidden}
377
+ .advisor .ava img{width:100%;height:100%;object-fit:cover;display:block;filter:grayscale(1) contrast(1.03)}
378
+ .advisor h4{font-family:var(--font-display);font-weight:500;font-size:18px;margin:0;letter-spacing:-.01em;line-height:1.15}
379
+ .advisor .role{font-size:13px;color:var(--ink-muted);margin:3px 0 0;line-height:1.45}
380
+ .advisor .ln{margin-top:auto}
381
+ @media(max-width:760px){.advisor-grid{grid-template-columns:1fr 1fr}}
382
+ @media(max-width:520px){.advisor-grid{grid-template-columns:1fr}}
383
+
384
+ /* Footer */
385
+ footer{border-top:1px solid var(--hairline);background:var(--paper-2)}
386
+ .foot-grid{max-width:var(--maxw);margin:0 auto;padding:var(--space-8) var(--gutter);display:grid;grid-template-columns:1.6fr 1fr 1fr 1fr;gap:40px}
387
+ .foot-brand{display:flex;flex-direction:column;gap:14px;max-width:300px}
388
+ .footer-motto{font-family:var(--font-display);font-weight:var(--weight-medium);font-size:17px;letter-spacing:-.01em;line-height:1.25;color:var(--ink);margin:0}
389
+ .footer-motto .fm-blue{color:var(--blue)}
390
+ .footer-motto small{display:block;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:.1em;text-transform:uppercase;color:var(--ink-faint);margin-top:5px;line-height:1.2}
391
+ .foot-brand img{height:30px;width:auto}
392
+ .foot-brand .brand-icon{height:28px}
393
+ .foot-brand .brand-name{font-size:19px}
394
+ .foot-col{display:flex;flex-direction:column;gap:12px}
395
+ .foot-col .links{display:flex;flex-direction:column;gap:9px}
396
+ .foot-col a{font-family:var(--font-text);font-size:14px;color:var(--ink-2);text-decoration:none;width:fit-content}
397
+ .foot-col a:hover{color:var(--blue)}
398
+ .foot-legal{max-width:var(--maxw);margin:0 auto;padding:0 var(--gutter) var(--space-7)}
399
+
400
+ /* ============================================================================
401
+ LONG-FORM / PROSE — writing, essays, legal, support
402
+ ============================================================================ */
403
+ .reading{max-width:none;margin:0 auto;padding:0 var(--gutter)}
404
+ .reading-col{max-width:46rem;margin:0 auto}
405
+ .article-head{padding-top:var(--space-9);padding-bottom:var(--space-6);border-bottom:1px solid var(--hairline);margin-bottom:var(--space-7)}
406
+ .article-head .n-kicker{margin-bottom:18px}
407
+ .article-head h1{font-family:var(--font-display);font-weight:var(--weight-medium);font-size:var(--text-2xl);letter-spacing:var(--tracking-display);line-height:var(--leading-display);color:var(--text-strong);max-width:24ch}
408
+ .article-head .standfirst{margin-top:18px;font-family:var(--font-text);font-size:var(--text-md);line-height:var(--leading-snug);color:var(--text-body);max-width:60ch}
409
+ .article-head .byline{margin-top:22px;display:flex;gap:14px;flex-wrap:wrap;align-items:center;font-family:var(--font-mono);font-size:var(--text-xs);letter-spacing:.04em;text-transform:uppercase;color:var(--text-muted)}
410
+
411
+ .prose{font-family:var(--font-text);font-size:var(--text-base);line-height:1.72;color:var(--text-body);max-width:42rem}
412
+ .prose.wide{max-width:none}
413
+ .prose > * + *{margin-top:var(--space-4)}
414
+ .prose h2{font-family:var(--font-display);font-weight:var(--weight-medium);font-size:var(--text-xl);letter-spacing:var(--tracking-tight);line-height:var(--leading-display);color:var(--text-strong);margin-top:var(--space-8);margin-bottom:var(--space-1)}
415
+ .prose h3{font-family:var(--font-display);font-weight:var(--weight-medium);font-size:var(--text-lg);color:var(--text-strong);margin-top:var(--space-6);margin-bottom:var(--space-1)}
416
+ .prose h4{font-family:var(--font-mono);font-size:var(--text-xs);letter-spacing:.1em;text-transform:uppercase;color:var(--text-muted);margin-top:var(--space-5)}
417
+ .prose p{margin:0}
418
+ .prose a{color:var(--blue);text-decoration:underline;text-decoration-color:color-mix(in srgb,var(--blue) 35%,transparent);text-underline-offset:.16em}
419
+ /* Buttons inside prose must keep their own foreground, not the prose link colour
420
+ (which is blue, and rendered the CTA label invisible on the blue button). */
421
+ .prose a.n-btn{color:var(--_fg);text-decoration:none}
422
+ .prose a.n-btn:hover{color:var(--on-accent)}
423
+ .prose a:hover{color:var(--blue-strong);text-decoration-color:currentColor}
424
+ .prose strong{color:var(--text-strong);font-weight:var(--weight-medium)}
425
+ .prose ul,.prose ol{margin:0;padding-left:1.35em}
426
+ .prose li{margin-top:var(--space-2)}
427
+ .prose li::marker{color:var(--ink-faint)}
428
+ .prose blockquote{margin:var(--space-5) 0;padding:2px 0 2px var(--space-4);border-left:2px solid var(--blue);color:var(--text-strong);font-style:normal}
429
+ .prose code{font-family:var(--font-mono);font-size:.9em;background:var(--paper-3);padding:.12em .38em;border-radius:var(--radius-sm);font-feature-settings:var(--otf-data)}
430
+ .prose pre{background:var(--paper-3);padding:var(--space-4);border-radius:var(--radius);overflow:auto;border:1px solid var(--hairline)}
431
+ .prose pre code{background:none;padding:0}
432
+ .prose hr{border:0;border-top:1px solid var(--hairline);margin:var(--space-7) 0}
433
+ .prose img{border-radius:var(--radius);border:1px solid var(--hairline)}
434
+
435
+ /* prose tables (the essay data tables) */
436
+ .prose table{width:100%;border-collapse:collapse;margin:var(--space-6) 0;font-size:var(--text-sm)}
437
+ .prose thead th{font-family:var(--font-mono);font-size:var(--text-xs);letter-spacing:.06em;text-transform:uppercase;color:var(--text-muted);text-align:left;font-weight:var(--weight-medium);padding:0 16px 10px 0;border-bottom:1px solid var(--ink)}
438
+ .prose tbody td{padding:12px 16px 12px 0;border-bottom:1px solid var(--hairline);vertical-align:top;color:var(--text-body)}
439
+ .prose tbody td:first-child{font-family:var(--font-text);color:var(--text-strong)}
440
+ .prose .num,.prose td[align="right"]{font-family:var(--font-mono);font-variant-numeric:tabular-nums lining-nums slashed-zero;text-align:right;padding-right:24px}
441
+
442
+ /* profiles row (security platforms) */
443
+ .profiles{display:flex;align-items:center;gap:24px;flex-wrap:wrap;margin-top:var(--space-7);padding-top:var(--space-5);border-top:1px solid var(--hairline)}
444
+
445
+ /* writing index list */
446
+ .writing-list{border-top:1px solid var(--ink);margin-top:var(--space-7)}
447
+ .writing-row{display:grid;grid-template-columns:minmax(0,1fr) 160px;gap:32px;padding:var(--space-6) 0;border-bottom:1px solid var(--hairline);text-decoration:none}
448
+ .writing-row:hover{text-decoration:none}
449
+ .writing-row .meta{display:flex;align-items:center;gap:10px;margin-bottom:10px}
450
+ .writing-row h3{max-width:34ch}
451
+ .writing-row .when{display:flex;align-items:flex-start;justify-content:flex-end;padding-top:6px}
452
+ @media(max-width:640px){.writing-row{grid-template-columns:1fr;gap:6px}.writing-row .when{justify-content:flex-start}}
453
+
454
+ /* ---- responsive hardening: no horizontal overflow on any screen ---- */
455
+ html,body{overflow-x:clip;max-width:100%}
456
+ .flagship-grid>*,.tools-grid>*,.research-row>*,.foot-grid>*,.person-grid>*,.domain-grid>*,.cta-row>*,.links>*{min-width:0}
457
+ .prose{overflow-wrap:break-word}
458
+ .prose a,.n-mono,code{overflow-wrap:anywhere;word-break:break-word}
459
+ .n-hero,.n-display,.n-title{overflow-wrap:break-word}
460
+
461
+ /* ---- responsive ---- */
462
+ @media(max-width:880px){
463
+ .hero-grid{grid-template-columns:minmax(0,1fr);gap:36px}
464
+ .hero-copy,.hero-proof{min-width:0}
465
+ .hero-proof{max-width:100%}
466
+ .cta-row{flex-wrap:wrap}
467
+ .receipt{overflow:hidden}
468
+ .flagship-grid{grid-template-columns:1fr;gap:32px}
469
+ .flagship-grid .stat-col{flex-direction:row;gap:32px;flex-wrap:wrap;border-top:1px solid var(--hairline);padding-top:28px}
470
+ .tools-grid,.domain-grid{grid-template-columns:1fr}
471
+ .foot-grid{grid-template-columns:1fr 1fr;gap:32px}
472
+ }
473
+ @media(max-width:640px){
474
+ .nav-in{gap:14px}
475
+ .nav-links{gap:18px}
476
+ .nav-links a{font-size:13px}
477
+ .nav-links a.cta{display:none} /* contact stays reachable in the footer */
478
+ .brand img{height:28px}
479
+ .research-row{grid-template-columns:1fr;gap:8px}
480
+ .research-row .read{justify-content:flex-start;padding-top:8px}
481
+ .person-grid{grid-template-columns:1fr}
482
+ .foot-grid{grid-template-columns:1fr}
483
+ }
484
+ @media(max-width:480px){
485
+ .n-hero{font-size:clamp(2.05rem,1.5rem+3vw,2.6rem)}
486
+ .n-card[style*="space-8"]{padding:var(--space-5)}
487
+ .reading-col[style*="60rem"]{max-width:none}
488
+ }
489
+ @media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}
490
+
491
+ /* ---- research article components (Friction Matrix engine) ---- */
492
+ .friction-matrix,.divergence{width:100%;height:auto;margin:var(--space-5) 0;display:block}
493
+ .friction-matrix .fm-quad{font-family:var(--font-mono);font-size:10px;letter-spacing:.04em;text-transform:uppercase;fill:var(--ink-muted)}
494
+ .friction-matrix .fm-axis{font-family:var(--font-mono);font-size:10px;fill:var(--ink-faint)}
495
+ .friction-matrix .fm-pt{font-family:var(--font-mono);font-size:10px;fill:var(--ink-2)}
496
+ .divergence .dv-label{font-family:var(--font-text);font-size:12px;fill:var(--ink-2)}
497
+ .divergence .dv-gap{font-family:var(--font-mono);font-size:11px;fill:var(--danger);font-variant-numeric:tabular-nums}
498
+ .quad-legend{display:grid;gap:8px;margin:var(--space-4) 0 var(--space-5)}
499
+ .quad-legend__row{font-size:var(--text-sm);color:var(--ink-2);line-height:1.5}
500
+ .quad-dot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:8px;vertical-align:baseline}
501
+ .arche-chips{display:flex;flex-wrap:wrap;gap:8px;margin:6px 0 14px}
502
+ .n-quote{border-left:2px solid var(--blue);margin:14px 0;padding:6px 0 6px 16px;font-family:var(--font-text);color:var(--ink-2);font-style:normal}
503
+ .n-quote cite{display:block;margin-top:6px;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:.04em;text-transform:uppercase;color:var(--ink-faint);font-style:normal}
504
+ .card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:14px;margin:var(--space-4) 0}
505
+ .app-tile{display:flex;flex-direction:column;align-items:center;text-align:center;gap:8px;padding:16px 10px;border:1px solid var(--hairline);border-radius:var(--radius-lg);text-decoration:none;transition:border-color .15s ease,transform .15s ease,box-shadow .15s ease}
506
+ .app-tile:hover{border-color:var(--hairline-2);transform:translateY(-2px);box-shadow:0 6px 18px rgba(22,22,15,.06)}
507
+ .app-tile__icon{width:52px;height:52px;border-radius:13px;border:1px solid var(--hairline)}
508
+ .app-tile__name{font-family:var(--font-text);font-size:var(--text-sm);color:var(--ink);line-height:1.25}
509
+ .app-tile__stat{font-family:var(--font-mono);font-size:var(--text-2xs);font-variant-numeric:tabular-nums;letter-spacing:.02em}
510
+ .app-tile__stat small{color:var(--ink-faint);text-transform:uppercase;letter-spacing:.06em}
511
+ .n-btn--sm{padding:6px 12px;font-size:var(--text-2xs)}
512
+ .deep-cta{margin:var(--space-5) 0;display:flex;flex-direction:column;gap:12px;align-items:flex-start}
513
+ .refs{margin-top:var(--space-4);border-top:1px solid var(--hairline)}
514
+ /* Friction Matrix quadrant grid */
515
+ .fmx{margin:var(--space-5) 0}
516
+ .fmx-axis-note{font-family:var(--font-mono);font-size:var(--text-2xs);text-transform:uppercase;letter-spacing:.05em;color:var(--ink-faint);margin:0 0 10px}
517
+ .fmx-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
518
+ .fmx-cell{border:1px solid var(--hairline);border-radius:var(--radius-lg);padding:18px 18px 20px;background:var(--paper-2)}
519
+ .fmx-cell__head{display:flex;align-items:center;gap:9px;font-family:var(--font-text);font-size:var(--text-md);color:var(--ink)}
520
+ .fmx-cell__head .quad-dot{width:10px;height:10px}
521
+ .fmx-count{margin-left:auto;font-family:var(--font-mono);font-size:var(--text-sm);color:var(--ink-faint);font-variant-numeric:tabular-nums}
522
+ .fmx-cell__desc{font-size:var(--text-sm);color:var(--ink-muted);margin:8px 0 16px;line-height:1.55}
523
+ .fmx-apps{display:flex;flex-wrap:wrap;gap:9px}
524
+ .fmx-app{display:inline-flex;width:42px;height:42px;border-radius:11px;overflow:hidden;border:1px solid var(--hairline);background:#fff;transition:transform .15s ease,box-shadow .15s ease}
525
+ .fmx-app:hover{transform:translateY(-2px);box-shadow:0 5px 14px rgba(22,22,15,.10)}
526
+ .fmx-app img{width:100%;height:100%;object-fit:cover;display:block}
527
+ .fmx-noicon{display:flex;align-items:center;justify-content:center;width:100%;height:100%;font-family:var(--font-mono);font-size:12px;background:var(--paper-3);color:var(--ink-muted)}
528
+ .fmx-empty{font-size:var(--text-sm);color:var(--ink-faint);margin:0}
529
+ .fmx-xaxis{display:flex;justify-content:space-between;margin-top:12px;font-family:var(--font-mono);font-size:var(--text-2xs);text-transform:uppercase;letter-spacing:.05em;color:var(--ink-faint)}
530
+ @media(max-width:600px){.fmx-grid{grid-template-columns:1fr}}
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "mcpgawk"
7
- version = "0.1.0"
8
- description = "Local-first MCP measurement gawk at an MCP server before you trust it. No inventory ever leaves your machine."
7
+ version = "0.1.2"
8
+ description = "A local-first CLI that measures what an MCP server costs and what it can do. Nothing leaves your machine."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  license = "Apache-2.0"
@@ -70,6 +70,8 @@ def _exfil_capable(tool: dict[str, Any]) -> bool:
70
70
 
71
71
 
72
72
  def _is_write(tool: dict[str, Any], ann: dict[str, Any]) -> bool:
73
+ if ann.get("destructiveHint") is True: # a declared-destructive tool mutates, even if the verb heuristic misses it
74
+ return True # (e.g. Emergent's `pause_job` — "pause" isn't a write-verb)
73
75
  if ann.get("readOnlyHint") is True: # declared read-only wins over the verb heuristic
74
76
  return False
75
77
  text = tool.get("name", "") + " " + (tool.get("description") or "")
@@ -31,6 +31,13 @@ def test_readonly_hint_overrides_write_verb():
31
31
  assert m.tools[0].write is False # declared read-only wins over the verb heuristic
32
32
 
33
33
 
34
+ def test_destructive_hint_marks_write_even_without_verb():
35
+ # Emergent's `pause_job`: "pause" isn't a write-verb, but destructiveHint:true means it mutates.
36
+ m = measure(_snap([{"name": "pause_job", "description": "Pause a running job",
37
+ "annotations": {"readOnlyHint": False, "destructiveHint": True}}]))
38
+ assert m.tools[0].write is True # declared destructive => mutating
39
+
40
+
34
41
  def test_write_and_exfil_facts():
35
42
  m = measure(_snap([
36
43
  {"name": "delete_entities", "description": "delete stuff"},
@@ -1,112 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>mcpgawk · gawk at an MCP server before you trust it</title>
7
- <meta name="description" content="A local-first CLI that measures what an MCP server costs and exposes — before you trust it. Nothing ever leaves your machine.">
8
- <meta property="og:title" content="mcpgawk by nativerse">
9
- <meta property="og:description" content="MCP's hidden tax — you pay it on every prompt, before you type a word. Measure it locally.">
10
- <meta property="og:image" content="assets/brand/social-preview.png">
11
- <link rel="icon" href="assets/brand/favicon.svg">
12
- <link href="https://api.fontshare.com/v2/css?f[]=sentient@400,500,700&f[]=supreme@400,500&f[]=tabular@400,500&display=swap" rel="stylesheet">
13
- <style>
14
- :root{
15
- --paper:#FFFFFF;--paper-2:#FAFAF6;--ink:#16160F;--ink-2:#3A3A30;--muted:#6B6B5E;--faint:#8f8f84;
16
- --blue:#2A33C2;--blue-strong:#1F2799;--green:#2A9D6B;--hair:#E7E6E0;
17
- --disp:'Sentient',Georgia,serif;--text:'Supreme',-apple-system,BlinkMacSystemFont,sans-serif;--mono:'Tabular',ui-monospace,Menlo,monospace;
18
- }
19
- *{box-sizing:border-box}
20
- body{margin:0;background:var(--paper);color:var(--ink-2);font-family:var(--text);font-size:17px;line-height:1.6;-webkit-font-smoothing:antialiased}
21
- .wrap{max-width:720px;margin:0 auto;padding:0 22px}
22
- a{color:var(--blue);text-decoration-thickness:1.5px;text-underline-offset:.15em}
23
- h1,h2{font-family:var(--disp);color:var(--ink);font-weight:500;letter-spacing:-.01em;line-height:1.1}
24
- .mono{font-family:var(--mono)}
25
- header{padding:40px 0 0}
26
- .lock{display:flex;align-items:center;gap:14px}
27
- .lock img{height:44px}
28
- .lock .wm{font-family:var(--disp);font-weight:500;font-size:30px;color:var(--ink);line-height:1}
29
- .lock .by{font-family:var(--text);font-size:13px;color:var(--muted);margin-top:2px}
30
- .hero{padding:60px 0 24px;border-bottom:1px solid var(--hair)}
31
- .kick{font-family:var(--mono);font-size:13px;letter-spacing:.14em;text-transform:uppercase;color:var(--blue);margin:0 0 16px}
32
- .hero h1{font-size:clamp(36px,7vw,60px);margin:0 0 14px}
33
- .hero p{font-size:21px;color:var(--muted);font-style:italic;margin:0}
34
- section{padding:40px 0;border-bottom:1px solid var(--hair)}
35
- .big{font-size:22px;line-height:1.4;color:var(--ink)}
36
- .accent{color:var(--blue);font-weight:600}
37
- table{border-collapse:collapse;width:100%;font-family:var(--text);font-size:15px;margin:14px 0 6px}
38
- th,td{padding:9px 10px;border-bottom:1px solid var(--hair);text-align:right}
39
- th:first-child,td:first-child{text-align:left}
40
- th{font-family:var(--mono);font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);font-weight:500}
41
- td:first-child{font-weight:600;color:var(--ink)}
42
- .num{font-family:var(--mono);font-variant-numeric:tabular-nums}
43
- .heavy td{background:#FBF3F1}.lean td{background:#EEF6F1}
44
- .cap{font-family:var(--mono);font-size:12px;color:var(--faint);margin-top:2px}
45
- .cta{background:var(--paper-2);border:1px solid var(--hair);border-radius:14px;padding:26px}
46
- .cta pre{font-family:var(--mono);font-size:15px;background:#16160F;color:#ECEBE3;border-radius:9px;padding:16px 18px;overflow-x:auto;margin:14px 0 0}
47
- .promise{display:inline-flex;align-items:center;gap:8px;font-family:var(--mono);font-size:13px;color:var(--muted);margin-top:14px}
48
- .dot{width:8px;height:8px;border-radius:50%;background:var(--green);display:inline-block}
49
- footer{padding:34px 0 60px;color:var(--faint);font-family:var(--mono);font-size:12.5px}
50
- .btnrow{display:flex;gap:12px;flex-wrap:wrap;margin-top:18px}
51
- .btn{font-family:var(--text);font-weight:500;font-size:15px;padding:.7em 1.15em;border-radius:10px;text-decoration:none}
52
- .btn-p{background:var(--blue);color:#fff}.btn-s{border:1px solid var(--hair);color:var(--ink)}
53
- </style>
54
- </head>
55
- <body>
56
- <div class="wrap">
57
- <header>
58
- <div class="lock">
59
- <img src="assets/brand/nativerse-mark.svg" alt="">
60
- <div><div class="wm">mcpgawk</div><div class="by">by <span style="color:var(--blue);font-weight:500">nativerse</span></div></div>
61
- </div>
62
- </header>
63
-
64
- <div class="hero">
65
- <p class="kick">The hidden tax of MCP</p>
66
- <h1>gawk at an MCP server before you trust it.</h1>
67
- <p>You pay its tax on every prompt — before you type a word.</p>
68
- <div class="btnrow">
69
- <a class="btn btn-p" href="#measure">Measure your stack</a>
70
- <a class="btn btn-s" href="https://github.com/gawk-dev/mcpgawk">View on GitHub →</a>
71
- </div>
72
- </div>
73
-
74
- <section>
75
- <p class="big">Connect an MCP server and it loads <em>every</em> tool definition into your model — used or not, on every request.</p>
76
- <p>We measured the ones developers actually use.</p>
77
- <p class="big">The two most-installed dev-tool MCPs load <span class="accent">35,600 tokens</span> at connect — roughly a quarter of your context, gone before your first prompt.</p>
78
- <p>The surprise: it isn't how <em>many</em> tools. It's how <em>heavy</em>. The heaviest packs 20 tools into 23,085 tokens; Cloudflare fits 23 into 3,570 — <span class="accent">6.5× lighter</span>, same job.</p>
79
- <table>
80
- <thead><tr><th>Server</th><th>Tools</th><th>Tokens at connect</th></tr></thead>
81
- <tbody>
82
- <tr class="heavy"><td>Heaviest dev-tool MCP</td><td class="num">20</td><td class="num">23,085</td></tr>
83
- <tr><td>The most-installed MCP</td><td class="num">44</td><td class="num">12,511</td></tr>
84
- <tr class="lean"><td>Cloudflare</td><td class="num">23</td><td class="num">3,570</td></tr>
85
- <tr class="lean"><td>Exa</td><td class="num">2</td><td class="num">520</td></tr>
86
- </tbody>
87
- </table>
88
- <p class="cap">Measured locally with mcpgawk. Heavy servers anonymised; lean ones named with credit. Nothing uploaded.</p>
89
- </section>
90
-
91
- <section>
92
- <p class="big">And heavy isn't just expensive.</p>
93
- <p>In controlled tests, once the tool list gets long the model picks the <em>wrong</em> tool more often — one study: <span class="accent">13.6%</span> right, versus <span class="accent">43.1%</span> when the list is trimmed. The fix already exists: Cloudflare and Exa prove lean is a choice.</p>
94
- </section>
95
-
96
- <section id="measure">
97
- <div class="cta">
98
- <h2 style="margin:0 0 6px">See your own tax.</h2>
99
- <p style="margin:0;color:var(--muted)">One command. Nothing leaves your machine.</p>
100
- <pre>pip install mcpgawk
101
- mcpgawk scan mcp.json</pre>
102
- <span class="promise"><span class="dot"></span> local · no inventory ever leaves your machine</span>
103
- </div>
104
- </section>
105
-
106
- <footer>
107
- Tokens counted with a named index (cl100k) — a comparable ruler, not exact Claude billing; the <em>relative</em> picture holds.
108
- This is a cost view; per-vendor capability findings go to vendors privately. Free &amp; open-source · Apache-2.0 · part of <span style="color:var(--blue)">nativerse</span> · gawk.dev
109
- </footer>
110
- </div>
111
- </body>
112
- </html>
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes