mcpgawk 0.1.2__tar.gz → 0.1.4__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 (167) hide show
  1. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/.github/workflows/ci.yml +15 -0
  2. mcpgawk-0.1.4/.github/workflows/mcpgawk-gate-selftest.yml +20 -0
  3. mcpgawk-0.1.4/.github/workflows/publish-openvsx.yml +38 -0
  4. mcpgawk-0.1.4/.github/workflows/publish-pypi.yml +51 -0
  5. mcpgawk-0.1.4/.gitignore +39 -0
  6. mcpgawk-0.1.4/CHANGELOG.md +75 -0
  7. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/PKG-INFO +58 -6
  8. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/README.md +57 -5
  9. mcpgawk-0.1.4/action.yml +62 -0
  10. mcpgawk-0.1.4/docs/.gitignore +1 -0
  11. mcpgawk-0.1.4/docs/api/lead.js +69 -0
  12. mcpgawk-0.1.4/docs/assets/logos/alibaba.svg +1 -0
  13. mcpgawk-0.1.4/docs/assets/logos/anthropic.svg +1 -0
  14. mcpgawk-0.1.4/docs/assets/logos/baidu.svg +1 -0
  15. mcpgawk-0.1.4/docs/assets/logos/cerebras.svg +4 -0
  16. mcpgawk-0.1.4/docs/assets/logos/cloudflare.svg +1 -0
  17. mcpgawk-0.1.4/docs/assets/logos/cohere.svg +1 -0
  18. mcpgawk-0.1.4/docs/assets/logos/copilot.svg +1 -0
  19. mcpgawk-0.1.4/docs/assets/logos/cursor.svg +12 -0
  20. mcpgawk-0.1.4/docs/assets/logos/deepseek.svg +1 -0
  21. mcpgawk-0.1.4/docs/assets/logos/discord.svg +1 -0
  22. mcpgawk-0.1.4/docs/assets/logos/docker.svg +3 -0
  23. mcpgawk-0.1.4/docs/assets/logos/figma.svg +14 -0
  24. mcpgawk-0.1.4/docs/assets/logos/framer.svg +1 -0
  25. mcpgawk-0.1.4/docs/assets/logos/gemini.svg +1 -0
  26. mcpgawk-0.1.4/docs/assets/logos/github.svg +1 -0
  27. mcpgawk-0.1.4/docs/assets/logos/gitlab.svg +6 -0
  28. mcpgawk-0.1.4/docs/assets/logos/gojek.svg +1 -0
  29. mcpgawk-0.1.4/docs/assets/logos/google.svg +1 -0
  30. mcpgawk-0.1.4/docs/assets/logos/grab.svg +1 -0
  31. mcpgawk-0.1.4/docs/assets/logos/grok.svg +4 -0
  32. mcpgawk-0.1.4/docs/assets/logos/huawei.svg +1 -0
  33. mcpgawk-0.1.4/docs/assets/logos/huggingface.svg +67 -0
  34. mcpgawk-0.1.4/docs/assets/logos/inflection.svg +1 -0
  35. mcpgawk-0.1.4/docs/assets/logos/infosys.svg +1 -0
  36. mcpgawk-0.1.4/docs/assets/logos/kakaotalk.svg +1 -0
  37. mcpgawk-0.1.4/docs/assets/logos/kimi.svg +1 -0
  38. mcpgawk-0.1.4/docs/assets/logos/klarna.svg +1 -0
  39. mcpgawk-0.1.4/docs/assets/logos/langchain.svg +1 -0
  40. mcpgawk-0.1.4/docs/assets/logos/line.svg +1 -0
  41. mcpgawk-0.1.4/docs/assets/logos/linear.svg +1 -0
  42. mcpgawk-0.1.4/docs/assets/logos/mercadolibre.svg +1 -0
  43. mcpgawk-0.1.4/docs/assets/logos/meta.svg +1 -0
  44. mcpgawk-0.1.4/docs/assets/logos/microsoft.svg +1 -0
  45. mcpgawk-0.1.4/docs/assets/logos/midjourney.svg +6 -0
  46. mcpgawk-0.1.4/docs/assets/logos/mistral.svg +1 -0
  47. mcpgawk-0.1.4/docs/assets/logos/naver.svg +1 -0
  48. mcpgawk-0.1.4/docs/assets/logos/netlify.svg +1 -0
  49. mcpgawk-0.1.4/docs/assets/logos/notion.svg +1 -0
  50. mcpgawk-0.1.4/docs/assets/logos/nubank.svg +1 -0
  51. mcpgawk-0.1.4/docs/assets/logos/nvidia.svg +1 -0
  52. mcpgawk-0.1.4/docs/assets/logos/ollama.svg +7 -0
  53. mcpgawk-0.1.4/docs/assets/logos/openai.svg +1 -0
  54. mcpgawk-0.1.4/docs/assets/logos/paytm.svg +1 -0
  55. mcpgawk-0.1.4/docs/assets/logos/perplexity.svg +24 -0
  56. mcpgawk-0.1.4/docs/assets/logos/postgresql.svg +1 -0
  57. mcpgawk-0.1.4/docs/assets/logos/python.svg +1 -0
  58. mcpgawk-0.1.4/docs/assets/logos/qwen.svg +1 -0
  59. mcpgawk-0.1.4/docs/assets/logos/rakuten.svg +1 -0
  60. mcpgawk-0.1.4/docs/assets/logos/replicate.svg +12 -0
  61. mcpgawk-0.1.4/docs/assets/logos/replit.svg +1 -0
  62. mcpgawk-0.1.4/docs/assets/logos/revolut.svg +1 -0
  63. mcpgawk-0.1.4/docs/assets/logos/runway.svg +14 -0
  64. mcpgawk-0.1.4/docs/assets/logos/sap.svg +1 -0
  65. mcpgawk-0.1.4/docs/assets/logos/sentry.svg +1 -0
  66. mcpgawk-0.1.4/docs/assets/logos/shopee.svg +1 -0
  67. mcpgawk-0.1.4/docs/assets/logos/slack.svg +24 -0
  68. mcpgawk-0.1.4/docs/assets/logos/sony.svg +1 -0
  69. mcpgawk-0.1.4/docs/assets/logos/spotify.svg +1 -0
  70. mcpgawk-0.1.4/docs/assets/logos/stability.svg +1 -0
  71. mcpgawk-0.1.4/docs/assets/logos/stripe.svg +1 -0
  72. mcpgawk-0.1.4/docs/assets/logos/suno.svg +1 -0
  73. mcpgawk-0.1.4/docs/assets/logos/supabase.svg +15 -0
  74. mcpgawk-0.1.4/docs/assets/logos/swiggy.svg +1 -0
  75. mcpgawk-0.1.4/docs/assets/logos/tiktok.svg +1 -0
  76. mcpgawk-0.1.4/docs/assets/logos/togetherai.svg +1 -0
  77. mcpgawk-0.1.4/docs/assets/logos/vercel.svg +1 -0
  78. mcpgawk-0.1.4/docs/assets/logos/wechat.svg +1 -0
  79. mcpgawk-0.1.4/docs/assets/logos/weibo.svg +1 -0
  80. mcpgawk-0.1.4/docs/assets/logos/wise.svg +1 -0
  81. mcpgawk-0.1.4/docs/assets/logos/xai.svg +1 -0
  82. mcpgawk-0.1.4/docs/assets/logos/xiaomi.svg +1 -0
  83. mcpgawk-0.1.4/docs/assets/logos/zoho.svg +1 -0
  84. mcpgawk-0.1.4/docs/assets/logos/zomato.svg +1 -0
  85. mcpgawk-0.1.4/docs/index.html +610 -0
  86. mcpgawk-0.1.4/docs/logofield.js +134 -0
  87. mcpgawk-0.1.4/docs/report.html +90 -0
  88. mcpgawk-0.1.4/editors/vscode/.vscodeignore +5 -0
  89. mcpgawk-0.1.4/editors/vscode/CHANGELOG.md +18 -0
  90. mcpgawk-0.1.4/editors/vscode/LICENSE +201 -0
  91. mcpgawk-0.1.4/editors/vscode/README.md +22 -0
  92. mcpgawk-0.1.4/editors/vscode/extension.js +177 -0
  93. mcpgawk-0.1.4/editors/vscode/media/icon.png +0 -0
  94. mcpgawk-0.1.4/editors/vscode/package.json +39 -0
  95. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/pyproject.toml +2 -1
  96. mcpgawk-0.1.4/scripts/ci_gate.py +105 -0
  97. mcpgawk-0.1.4/src/mcpgawk/__init__.py +26 -0
  98. mcpgawk-0.1.4/src/mcpgawk/ambient.py +120 -0
  99. mcpgawk-0.1.4/src/mcpgawk/cli.py +317 -0
  100. mcpgawk-0.1.4/src/mcpgawk/consent.py +75 -0
  101. mcpgawk-0.1.4/src/mcpgawk/discover.py +352 -0
  102. mcpgawk-0.1.4/src/mcpgawk/drift.py +145 -0
  103. mcpgawk-0.1.4/src/mcpgawk/fleet.py +293 -0
  104. mcpgawk-0.1.4/src/mcpgawk/grade.py +132 -0
  105. mcpgawk-0.1.4/src/mcpgawk/history.py +118 -0
  106. mcpgawk-0.1.4/src/mcpgawk/label.py +419 -0
  107. mcpgawk-0.1.4/src/mcpgawk/mcp_server.py +179 -0
  108. mcpgawk-0.1.4/src/mcpgawk/measure.py +173 -0
  109. mcpgawk-0.1.4/src/mcpgawk/oauth_login.py +131 -0
  110. mcpgawk-0.1.4/src/mcpgawk/oauth_scopes.py +45 -0
  111. mcpgawk-0.1.4/src/mcpgawk/probe.py +361 -0
  112. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/src/mcpgawk/signals.py +69 -0
  113. mcpgawk-0.1.4/src/mcpgawk/supplychain.py +100 -0
  114. mcpgawk-0.1.4/src/mcpgawk/transport.py +112 -0
  115. mcpgawk-0.1.4/supabase/leads.sql +34 -0
  116. mcpgawk-0.1.4/supabase/lockdown.sql +12 -0
  117. mcpgawk-0.1.4/tests/test_ambient.py +147 -0
  118. mcpgawk-0.1.4/tests/test_cli_integration.py +68 -0
  119. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/tests/test_drift.py +3 -2
  120. mcpgawk-0.1.4/tests/test_grade.py +47 -0
  121. mcpgawk-0.1.4/tests/test_label_axes.py +118 -0
  122. mcpgawk-0.1.4/tests/test_layer_invariants.py +85 -0
  123. mcpgawk-0.1.4/tests/test_mcp_server.py +205 -0
  124. mcpgawk-0.1.4/tests/test_measure.py +122 -0
  125. mcpgawk-0.1.4/tests/test_oauth_scopes.py +61 -0
  126. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/tests/test_signals.py +50 -1
  127. mcpgawk-0.1.4/tests/test_supplychain.py +87 -0
  128. mcpgawk-0.1.4/tests/test_thin_guidance.py +162 -0
  129. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/uv.lock +1 -1
  130. mcpgawk-0.1.2/.gitignore +0 -11
  131. mcpgawk-0.1.2/CHANGELOG.md +0 -34
  132. mcpgawk-0.1.2/docs/index.html +0 -178
  133. mcpgawk-0.1.2/src/mcpgawk/__init__.py +0 -16
  134. mcpgawk-0.1.2/src/mcpgawk/cli.py +0 -121
  135. mcpgawk-0.1.2/src/mcpgawk/drift.py +0 -84
  136. mcpgawk-0.1.2/src/mcpgawk/history.py +0 -49
  137. mcpgawk-0.1.2/src/mcpgawk/label.py +0 -89
  138. mcpgawk-0.1.2/src/mcpgawk/measure.py +0 -105
  139. mcpgawk-0.1.2/src/mcpgawk/probe.py +0 -129
  140. mcpgawk-0.1.2/tests/test_measure.py +0 -63
  141. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/BRAND.md +0 -0
  142. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/CONTRIBUTING.md +0 -0
  143. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/LICENSE +0 -0
  144. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/NOTICE +0 -0
  145. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/SECURITY.md +0 -0
  146. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/THREAT-MODEL.md +0 -0
  147. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/assets/brand/demo.gif +0 -0
  148. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/assets/brand/favicon.svg +0 -0
  149. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/assets/brand/identity-board.html +0 -0
  150. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/assets/brand/nativerse-mark.svg +0 -0
  151. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/assets/brand/social-preview.png +0 -0
  152. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/assets/brand/social-preview.svg +0 -0
  153. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/assets/brand/wordmark-dark.png +0 -0
  154. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/assets/brand/wordmark-dark.svg +0 -0
  155. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/assets/brand/wordmark-light.png +0 -0
  156. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/assets/brand/wordmark-light.svg +0 -0
  157. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/docs/CNAME +0 -0
  158. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/docs/assets/brand/favicon.svg +0 -0
  159. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/docs/assets/brand/nativerse-mark.svg +0 -0
  160. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/docs/assets/brand/social-preview.png +0 -0
  161. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/docs/assets/brand/social-preview.svg +0 -0
  162. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/docs/styles.css +0 -0
  163. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/examples/mcp.json +0 -0
  164. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/src/mcpgawk/__main__.py +0 -0
  165. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/src/mcpgawk/servercard.py +0 -0
  166. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/tests/test_no_egress.py +0 -0
  167. {mcpgawk-0.1.2 → mcpgawk-0.1.4}/tests/test_servercard.py +0 -0
@@ -36,3 +36,18 @@ jobs:
36
36
  python -m venv /tmp/smoke
37
37
  /tmp/smoke/bin/pip install dist/*.whl
38
38
  /tmp/smoke/bin/mcpgawk --help
39
+
40
+ version-consistency:
41
+ runs-on: ubuntu-latest
42
+ steps:
43
+ - uses: actions/checkout@v4
44
+ - name: CLI (pyproject.toml) and VS Code extension (package.json) versions must match
45
+ run: |
46
+ PY_VERSION=$(grep -m1 '^version = ' pyproject.toml | sed -E 's/version = "(.*)"/\1/')
47
+ EXT_VERSION=$(node -p "require('./editors/vscode/package.json').version")
48
+ echo "pyproject.toml: $PY_VERSION"
49
+ echo "editors/vscode/package.json: $EXT_VERSION"
50
+ if [ "$PY_VERSION" != "$EXT_VERSION" ]; then
51
+ echo "::error::Version mismatch — CLI is $PY_VERSION but the VS Code extension is $EXT_VERSION. Bump editors/vscode/package.json (and its CHANGELOG.md) alongside every pyproject.toml release."
52
+ exit 1
53
+ fi
@@ -0,0 +1,20 @@
1
+ name: MCP hygiene gate (self-test)
2
+
3
+ # Dogfoods the mcpgawk Action against a real open server, so the marketplace
4
+ # listing has a live, passing example.
5
+ on:
6
+ push:
7
+ branches: [main]
8
+ pull_request:
9
+
10
+ jobs:
11
+ gate:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - name: MCP hygiene gate
16
+ uses: ./
17
+ with:
18
+ stdio: 'npx -y @modelcontextprotocol/server-filesystem /tmp'
19
+ max-tokens: 5000
20
+ fail-on-flagged: true
@@ -0,0 +1,38 @@
1
+ name: Publish VS Code extension (Open VSX)
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - "editors/vscode/package.json"
8
+ workflow_dispatch: {}
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ defaults:
14
+ run:
15
+ working-directory: editors/vscode
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - uses: actions/setup-node@v4
20
+ with:
21
+ node-version: "20"
22
+
23
+ - name: Fail loudly if the Open VSX token isn't configured
24
+ run: |
25
+ if [ -z "${{ secrets.OVSX_PAT }}" ]; then
26
+ echo "::error::OVSX_PAT secret is not set — add it under repo Settings > Secrets before this can publish."
27
+ exit 1
28
+ fi
29
+
30
+ - run: npm install -g @vscode/vsce ovsx
31
+
32
+ - name: Package
33
+ run: vsce package --no-dependencies
34
+
35
+ - name: Publish to Open VSX
36
+ env:
37
+ OVSX_PAT: ${{ secrets.OVSX_PAT }}
38
+ run: ovsx publish *.vsix -p "$OVSX_PAT"
@@ -0,0 +1,51 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - "pyproject.toml"
8
+ workflow_dispatch: {}
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: "3.12"
19
+
20
+ - name: Fail loudly if the PyPI token isn't configured
21
+ run: |
22
+ if [ -z "${{ secrets.PYPI_API_TOKEN }}" ]; then
23
+ echo "::error::PYPI_API_TOKEN secret is not set — add it under repo Settings > Secrets before this can publish."
24
+ exit 1
25
+ fi
26
+
27
+ - name: Skip if this version is already published (re-running main shouldn't re-publish)
28
+ run: |
29
+ LOCAL=$(grep -m1 '^version = ' pyproject.toml | sed -E 's/version = "(.*)"/\1/')
30
+ LIVE=$(python -c "import json,urllib.request; print(json.load(urllib.request.urlopen('https://pypi.org/pypi/mcpgawk/json'))['info']['version'])" 2>/dev/null || echo "none")
31
+ echo "local=$LOCAL live=$LIVE"
32
+ echo "LOCAL_VERSION=$LOCAL" >> "$GITHUB_ENV"
33
+ if [ "$LOCAL" = "$LIVE" ]; then
34
+ echo "Already published — skipping."
35
+ echo "SKIP=1" >> "$GITHUB_ENV"
36
+ fi
37
+
38
+ - run: python -m pip install --upgrade pip build
39
+ if: env.SKIP != '1'
40
+
41
+ - run: python -m build
42
+ if: env.SKIP != '1'
43
+
44
+ - name: Publish to PyPI
45
+ if: env.SKIP != '1'
46
+ env:
47
+ TWINE_USERNAME: __token__
48
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
49
+ run: |
50
+ python -m pip install twine
51
+ twine upload dist/*
@@ -0,0 +1,39 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ venv/
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .pytest_cache/
9
+
10
+ # dev-only QA harness
11
+ assets/brand/_qa.html
12
+ editors/vscode/*.vsix
13
+ editors/vscode/node_modules/
14
+ .vercel
15
+
16
+ # ===== NEVER PUBLISH — private strategy, paid product, business/internal =====
17
+ # Belt-and-braces guard: even if one of these is accidentally copied into this
18
+ # public checkout, git will not track it. Primary protection is that these live
19
+ # only in the private repo (~/devtools/mcpgawk, which has NO remote).
20
+ HANDOFF.md
21
+ # private strategy / research / business docs
22
+ docs/spec/
23
+ docs/PLATFORM-AND-FACTORY.md
24
+ docs/foundational-decisions.md
25
+ docs/track-v-verdict-strategy.md
26
+ docs/platform-system-design.md
27
+ docs/architecture-convergence.md
28
+ docs/functional-spec.md
29
+ docs/research-*.md
30
+ docs/strategy-*.md
31
+ docs/*-audit*.md
32
+ docs/adversarial-review-prompt.md
33
+ docs/vendor-outreach-*.md
34
+ # the PAID platform code (verify / monitor / enforce / forge) — never in the free repo
35
+ src/gawk_platform/
36
+ tests/test_platform_smoke.py
37
+ tests/test_forge_smoke.py
38
+ # third-party study clones (Apache-2.0) — never vendored/published
39
+ reference/
@@ -0,0 +1,75 @@
1
+ # Changelog
2
+
3
+ All notable changes to mcpgawk. Format: [Keep a Changelog](https://keepachangelog.com/); versioning: [SemVer](https://semver.org/).
4
+
5
+ ## [0.1.4] — 2026-07-20
6
+
7
+ ### Added
8
+ - `--login`: scan a remote MCP server that requires OAuth. Opens the browser, signs in once via the
9
+ server's own OAuth flow, and scans it — the token is stored locally (`~/.gawk/oauth`) and never
10
+ leaves your machine.
11
+ - Dynamic tool-dispatch detection: flags servers that hide a larger real tool catalog behind a
12
+ meta-tool (the Sentry / Docker mcp-gateway shape). A passive scan structurally can't see the hidden
13
+ tools, so this says "this scan is incomplete" rather than letting a clean-looking result be mistaken
14
+ for a clean server.
15
+
16
+ ### Changed / Fixed
17
+ - A probe that errors (unreachable host, wrong URL, an HTML docs page that isn't an MCP endpoint,
18
+ a timeout) can no longer render as CLEAN — failures are now typed, not inferred from message text.
19
+ - Remote (`--http`/`--sse`) scans fail fast (~20s) instead of hanging up to 90s on a non-MCP URL;
20
+ local stdio servers keep the generous cold-start budget.
21
+ - Heuristic signals are labelled by what they are — dynamic-dispatch, tool-name shadowing and
22
+ server-card mismatch are no longer all reported as "possible prompt-injection".
23
+ - Version is single-sourced from the installed package metadata (no more hand-maintained literal that
24
+ could go stale).
25
+
26
+ ## [0.1.3] — 2026-07-12
27
+
28
+ ### Added
29
+ - Report now covers 5 axes: cost (+ top-3 heaviest tools), trust surface (% write, % exfil-capable,
30
+ destructive-declared count), annotation completeness (score — was computed but never surfaced),
31
+ coverage (prompts/resources, previously `--json`-only), and bounded signals (unchanged).
32
+ - `--verbose` — full per-tool table in CLI text output (previously only the write/exfil-flagged subset
33
+ was shown; the full list existed only via `--json`).
34
+ - `--supply-chain` (opt-in) — checks the launched package against the public npm registry / PyPI JSON
35
+ API for deprecation (npm) / yanked (PyPI, PEP 592) status. Makes a real network call — only the package
36
+ name and version are sent, never the tool inventory. Off by default.
37
+ - `--oauth-scopes` (opt-in) — locally decodes a supplied `Authorization: Bearer <jwt>` header's `scope`/
38
+ `scp` claim. No network call; the signature is not verified (reading a declared claim, not
39
+ authenticating). Opaque (non-JWT) tokens are reported as not locally inspectable, never guessed at.
40
+
41
+ ### Changed
42
+ - `x-mcpgawk` label schema gains `top_heavy_tools`, `trust_surface`, `annotation_completeness`, and
43
+ (only when the corresponding flag is passed) `supply_chain`/`oauth_scopes`. All additive — no existing
44
+ key renamed or removed.
45
+
46
+ ## [0.1.2] — 2026-07-08
47
+
48
+ ### Changed
49
+ - Plain, reader-first README and package description.
50
+ - README images use absolute URLs so they render on PyPI (relative paths only work on GitHub).
51
+
52
+ ## [0.1.1] — 2026-07-08
53
+
54
+ ### Fixed
55
+ - A tool declaring `destructiveHint: true` is now counted as write/mutating even when its name isn't a
56
+ write-verb (e.g. a `pause_job` tool). Previously the verb heuristic could leave a declared-destructive
57
+ tool unflagged.
58
+
59
+ ## [0.1.0] — 2026-07-08
60
+
61
+ Initial release. Local-first MCP measurement.
62
+
63
+ ### Added
64
+ - `mcpgawk scan` over stdio, streamable-HTTP, and SSE via the official `mcp` SDK (protocol-version negotiated).
65
+ - Cost **index** (named tokenizer) + **EXACT** capability facts (write / exfil-capable / annotations) + **integrity pin**.
66
+ - **Bounded** heuristic signals (0-FP on the tested corpus, never verdicts): injection-shaped descriptions
67
+ (tools and prompts), cross-server name shadowing, Server-Card under-declaration.
68
+ - `--track` rug-pull / drift monitor with a local history store.
69
+ - Server Card reader (`/.well-known/mcp/server-card.json`) — reads the card when present, checks
70
+ declared-vs-measured, falls back to live-connect. Fetched with no auth and no redirect-following.
71
+ - Label output as a Server-Card extension (`x-mcpgawk`); `--json` for machine consumption.
72
+
73
+ ### Security
74
+ - Measurement layers import no network library — cannot egress by construction (enforced by test).
75
+ - Per-server timeout so a hung server degrades to one error row.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcpgawk
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
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
@@ -41,6 +41,8 @@ Description-Content-Type: text/markdown
41
41
  [![Python](https://img.shields.io/pypi/pyversions/mcpgawk.svg)](https://pypi.org/project/mcpgawk/)
42
42
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
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
+ [![Open VSX](https://img.shields.io/open-vsx/v/gawk-dev/mcpgawk?label=VS%20Code%20%2F%20Cursor)](https://open-vsx.org/extension/gawk-dev/mcpgawk)
45
+ [![GitHub Marketplace](https://img.shields.io/badge/GitHub%20Marketplace-Action-blue?logo=github)](https://github.com/marketplace/actions/mcpgawk-mcp-hygiene-gate)
44
46
  [![No egress](https://img.shields.io/badge/inventory-never%20uploaded-brightgreen.svg)](#guarantees)
45
47
 
46
48
  A local-first command that measures what a [Model Context Protocol](https://modelcontextprotocol.io)
@@ -66,18 +68,39 @@ You pay for those tokens, and you haven't checked what the tools can do. mcpgawk
66
68
  ## Features
67
69
 
68
70
  - 🔌 **Any transport** — stdio, streamable-HTTP, SSE, and OAuth remotes (via the `mcp-remote` bridge).
69
- - 💸 **Token cost index** — exactly what each tool adds to your context at connect.
70
- - 🧾 **Capability facts** — write / exfil-capable / declared annotations, straight from the schema.
71
+ - 💸 **Token cost index** — exactly what each tool adds to your context at connect, plus the 3 heaviest tools.
72
+ - 🧾 **Capability facts** — write / exfil-capable / declared annotations, straight from the schema, plus a
73
+ trust-surface summary (% write, % exfil-capable, destructive-declared count) and an annotation-completeness
74
+ score.
71
75
  - 📌 **Integrity pin + drift** — catch a server that silently rewrites its tools (`--track`).
72
76
  - 🚩 **Bounded signals** — injection-shaped descriptions, cross-server shadowing, under-declaring Server Cards — pointers for a human, never verdicts.
73
77
  - 🔒 **Zero egress, by construction** — the measurement layers import no network library. Enforced by a test.
78
+ Two checks are opt-in and make an explicit exception (see [Guarantees](#guarantees)): `--supply-chain` and
79
+ `--oauth-scopes`.
74
80
 
75
- ## Install
81
+ ## Get it — three ways
76
82
 
83
+ **CLI** (any terminal):
77
84
  ```bash
78
85
  pip install mcpgawk # or: uv tool install mcpgawk
86
+ mcpgawk scan mcp.json
79
87
  ```
80
88
 
89
+ **Editor** (VS Code / Cursor): install **mcpgawk** from the marketplace ([Open VSX](https://open-vsx.org/extension/gawk-dev/mcpgawk)). It scans your workspace `mcp.json` and shows cost + capability flags inline.
90
+
91
+ **CI** (GitHub Action): gate every PR on token budget / drift ([Marketplace](https://github.com/marketplace/actions/mcpgawk-mcp-hygiene-gate)):
92
+ ```yaml
93
+ - uses: gawk-dev/mcpgawk@v1
94
+ with: { config: mcp.json, max-tokens: 8000, fail-on-flagged: true }
95
+ ```
96
+
97
+ ## When to run it
98
+
99
+ - **Before you add a server** — see what it costs and what it can do, before you trust it.
100
+ - **When your agent feels slow or picks the wrong tool** — it's often MCP bloat (too many / too-heavy tools).
101
+ - **On every PR** — the CI gate catches drift and creeping token cost.
102
+ - **If you *publish* an MCP server** — see what it costs your users and how it reads to a client, and fix it (usually one line per tool). Lean + well-annotated is a differentiator.
103
+
81
104
  ## Use
82
105
 
83
106
  ```bash
@@ -87,24 +110,38 @@ mcpgawk scan --http https://host/mcp --header "Authorization: Bearer $TOKEN"
87
110
  mcpgawk scan --sse https://host/sse
88
111
  mcpgawk scan mcp.json --track # record + detect rug-pulls over time
89
112
  mcpgawk scan mcp.json --json # machine-readable labels
113
+ mcpgawk scan mcp.json --verbose # full per-tool table, not just flagged tools
114
+ mcpgawk scan mcp.json --supply-chain # opt-in: npm/PyPI deprecation check (network)
115
+ mcpgawk scan mcp.json --oauth-scopes # opt-in: decode a supplied Bearer JWT's scope
90
116
  ```
91
117
 
92
118
  ## What it reports
93
119
 
94
120
  - **Cost index** — tokens each tool adds at connect (named tokenizer; a comparable index, not an
95
- absolute Claude count).
96
- - **Capability facts** — write/mutating, exfil-capable, declared annotations.
121
+ absolute Claude count), plus the 3 heaviest tools.
122
+ - **Trust surface** — capability facts (write/mutating, exfil-capable, declared annotations) rolled up
123
+ into % write, % exfil-capable, and a destructive-declared count.
124
+ - **Annotation completeness** — a transparent composite (annotated ÷ total tools declaring read/write
125
+ intent), not a risk score.
126
+ - **Coverage** — tools, prompts, and resources counted (`--verbose` for the full per-tool table).
97
127
  - **Integrity pin** — a hash that changes if the server silently rewrites its tools; `--track`
98
128
  turns it into rug-pull detection over time.
99
129
  - **Bounded signals** — precise, low-false-positive pointers *for a human to review*, never verdicts:
100
130
  injection-shaped descriptions (tools **and** prompts), cross-server name shadowing, and public
101
131
  Server Cards that under-declare what the server actually exposes.
132
+ - **Supply-chain** (opt-in, `--supply-chain`) — checks the launched package against the public npm/PyPI
133
+ registry for deprecation/yank status.
134
+ - **OAuth scopes** (opt-in, `--oauth-scopes`) — locally decodes a supplied Bearer JWT's `scope` claim.
102
135
 
103
136
  ## Guarantees
104
137
 
105
138
  - **No inventory egress.** The only network is the protocol client talking to the server you point
106
139
  it at. The measurement layers import no network library — they *cannot* egress by construction
107
140
  (enforced by a test). Public Server Card discovery is fetched with no auth and no redirect-following.
141
+ Two flags are the explicit, opt-in exception: `--supply-chain` sends the launched package's name
142
+ (and pinned version, if any) — never your tool inventory — to the public npm registry or PyPI JSON
143
+ API. `--oauth-scopes` makes no network call at all; it locally decodes a Bearer JWT you already
144
+ supplied. Neither runs unless you pass the flag.
108
145
  - **Facts ≠ heuristics.** Exact capability facts and the token index never mix with the bounded
109
146
  heuristic signals — separate in code, separate in output.
110
147
  - **Reproducible.** One command, identical numbers.
@@ -116,6 +153,21 @@ mcpgawk scan mcp.json --json # machine-read
116
153
  uv run --extra dev --with mcp --with tiktoken --with httpx python -m pytest -q
117
154
  ```
118
155
 
156
+ ## CI gate — GitHub Action
157
+
158
+ Scan your MCP servers on every pull request and fail the build if one gets too heavy or trips a signal.
159
+ It runs entirely in your runner — nothing is uploaded — and posts a per-server cost/flag table to the job summary.
160
+
161
+ ```yaml
162
+ - uses: gawk-dev/mcpgawk@v1
163
+ with:
164
+ config: mcp.json # or: stdio / http / sse — a single server
165
+ max-tokens: 8000 # fail if any server loads more than this at connect
166
+ fail-on-flagged: true # fail if any bounded signal fires
167
+ ```
168
+
169
+ Available on the [GitHub Marketplace](https://github.com/marketplace/actions/mcpgawk-mcp-hygiene-gate).
170
+
119
171
  ## Contributing
120
172
 
121
173
  Issues and PRs welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) first, and see the design
@@ -12,6 +12,8 @@
12
12
  [![Python](https://img.shields.io/pypi/pyversions/mcpgawk.svg)](https://pypi.org/project/mcpgawk/)
13
13
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
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
+ [![Open VSX](https://img.shields.io/open-vsx/v/gawk-dev/mcpgawk?label=VS%20Code%20%2F%20Cursor)](https://open-vsx.org/extension/gawk-dev/mcpgawk)
16
+ [![GitHub Marketplace](https://img.shields.io/badge/GitHub%20Marketplace-Action-blue?logo=github)](https://github.com/marketplace/actions/mcpgawk-mcp-hygiene-gate)
15
17
  [![No egress](https://img.shields.io/badge/inventory-never%20uploaded-brightgreen.svg)](#guarantees)
16
18
 
17
19
  A local-first command that measures what a [Model Context Protocol](https://modelcontextprotocol.io)
@@ -37,18 +39,39 @@ You pay for those tokens, and you haven't checked what the tools can do. mcpgawk
37
39
  ## Features
38
40
 
39
41
  - 🔌 **Any transport** — stdio, streamable-HTTP, SSE, and OAuth remotes (via the `mcp-remote` bridge).
40
- - 💸 **Token cost index** — exactly what each tool adds to your context at connect.
41
- - 🧾 **Capability facts** — write / exfil-capable / declared annotations, straight from the schema.
42
+ - 💸 **Token cost index** — exactly what each tool adds to your context at connect, plus the 3 heaviest tools.
43
+ - 🧾 **Capability facts** — write / exfil-capable / declared annotations, straight from the schema, plus a
44
+ trust-surface summary (% write, % exfil-capable, destructive-declared count) and an annotation-completeness
45
+ score.
42
46
  - 📌 **Integrity pin + drift** — catch a server that silently rewrites its tools (`--track`).
43
47
  - 🚩 **Bounded signals** — injection-shaped descriptions, cross-server shadowing, under-declaring Server Cards — pointers for a human, never verdicts.
44
48
  - 🔒 **Zero egress, by construction** — the measurement layers import no network library. Enforced by a test.
49
+ Two checks are opt-in and make an explicit exception (see [Guarantees](#guarantees)): `--supply-chain` and
50
+ `--oauth-scopes`.
45
51
 
46
- ## Install
52
+ ## Get it — three ways
47
53
 
54
+ **CLI** (any terminal):
48
55
  ```bash
49
56
  pip install mcpgawk # or: uv tool install mcpgawk
57
+ mcpgawk scan mcp.json
50
58
  ```
51
59
 
60
+ **Editor** (VS Code / Cursor): install **mcpgawk** from the marketplace ([Open VSX](https://open-vsx.org/extension/gawk-dev/mcpgawk)). It scans your workspace `mcp.json` and shows cost + capability flags inline.
61
+
62
+ **CI** (GitHub Action): gate every PR on token budget / drift ([Marketplace](https://github.com/marketplace/actions/mcpgawk-mcp-hygiene-gate)):
63
+ ```yaml
64
+ - uses: gawk-dev/mcpgawk@v1
65
+ with: { config: mcp.json, max-tokens: 8000, fail-on-flagged: true }
66
+ ```
67
+
68
+ ## When to run it
69
+
70
+ - **Before you add a server** — see what it costs and what it can do, before you trust it.
71
+ - **When your agent feels slow or picks the wrong tool** — it's often MCP bloat (too many / too-heavy tools).
72
+ - **On every PR** — the CI gate catches drift and creeping token cost.
73
+ - **If you *publish* an MCP server** — see what it costs your users and how it reads to a client, and fix it (usually one line per tool). Lean + well-annotated is a differentiator.
74
+
52
75
  ## Use
53
76
 
54
77
  ```bash
@@ -58,24 +81,38 @@ mcpgawk scan --http https://host/mcp --header "Authorization: Bearer $TOKEN"
58
81
  mcpgawk scan --sse https://host/sse
59
82
  mcpgawk scan mcp.json --track # record + detect rug-pulls over time
60
83
  mcpgawk scan mcp.json --json # machine-readable labels
84
+ mcpgawk scan mcp.json --verbose # full per-tool table, not just flagged tools
85
+ mcpgawk scan mcp.json --supply-chain # opt-in: npm/PyPI deprecation check (network)
86
+ mcpgawk scan mcp.json --oauth-scopes # opt-in: decode a supplied Bearer JWT's scope
61
87
  ```
62
88
 
63
89
  ## What it reports
64
90
 
65
91
  - **Cost index** — tokens each tool adds at connect (named tokenizer; a comparable index, not an
66
- absolute Claude count).
67
- - **Capability facts** — write/mutating, exfil-capable, declared annotations.
92
+ absolute Claude count), plus the 3 heaviest tools.
93
+ - **Trust surface** — capability facts (write/mutating, exfil-capable, declared annotations) rolled up
94
+ into % write, % exfil-capable, and a destructive-declared count.
95
+ - **Annotation completeness** — a transparent composite (annotated ÷ total tools declaring read/write
96
+ intent), not a risk score.
97
+ - **Coverage** — tools, prompts, and resources counted (`--verbose` for the full per-tool table).
68
98
  - **Integrity pin** — a hash that changes if the server silently rewrites its tools; `--track`
69
99
  turns it into rug-pull detection over time.
70
100
  - **Bounded signals** — precise, low-false-positive pointers *for a human to review*, never verdicts:
71
101
  injection-shaped descriptions (tools **and** prompts), cross-server name shadowing, and public
72
102
  Server Cards that under-declare what the server actually exposes.
103
+ - **Supply-chain** (opt-in, `--supply-chain`) — checks the launched package against the public npm/PyPI
104
+ registry for deprecation/yank status.
105
+ - **OAuth scopes** (opt-in, `--oauth-scopes`) — locally decodes a supplied Bearer JWT's `scope` claim.
73
106
 
74
107
  ## Guarantees
75
108
 
76
109
  - **No inventory egress.** The only network is the protocol client talking to the server you point
77
110
  it at. The measurement layers import no network library — they *cannot* egress by construction
78
111
  (enforced by a test). Public Server Card discovery is fetched with no auth and no redirect-following.
112
+ Two flags are the explicit, opt-in exception: `--supply-chain` sends the launched package's name
113
+ (and pinned version, if any) — never your tool inventory — to the public npm registry or PyPI JSON
114
+ API. `--oauth-scopes` makes no network call at all; it locally decodes a Bearer JWT you already
115
+ supplied. Neither runs unless you pass the flag.
79
116
  - **Facts ≠ heuristics.** Exact capability facts and the token index never mix with the bounded
80
117
  heuristic signals — separate in code, separate in output.
81
118
  - **Reproducible.** One command, identical numbers.
@@ -87,6 +124,21 @@ mcpgawk scan mcp.json --json # machine-read
87
124
  uv run --extra dev --with mcp --with tiktoken --with httpx python -m pytest -q
88
125
  ```
89
126
 
127
+ ## CI gate — GitHub Action
128
+
129
+ Scan your MCP servers on every pull request and fail the build if one gets too heavy or trips a signal.
130
+ It runs entirely in your runner — nothing is uploaded — and posts a per-server cost/flag table to the job summary.
131
+
132
+ ```yaml
133
+ - uses: gawk-dev/mcpgawk@v1
134
+ with:
135
+ config: mcp.json # or: stdio / http / sse — a single server
136
+ max-tokens: 8000 # fail if any server loads more than this at connect
137
+ fail-on-flagged: true # fail if any bounded signal fires
138
+ ```
139
+
140
+ Available on the [GitHub Marketplace](https://github.com/marketplace/actions/mcpgawk-mcp-hygiene-gate).
141
+
90
142
  ## Contributing
91
143
 
92
144
  Issues and PRs welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) first, and see the design
@@ -0,0 +1,62 @@
1
+ name: 'mcpgawk MCP hygiene gate'
2
+ description: 'Scan your MCP servers in CI and fail the build if one is too heavy or trips a signal. Local, nothing uploaded.'
3
+ author: 'nativerse'
4
+ branding:
5
+ icon: 'shield'
6
+ color: 'blue'
7
+
8
+ inputs:
9
+ config:
10
+ description: 'Path to an mcp.json config to scan.'
11
+ required: false
12
+ default: 'mcp.json'
13
+ stdio:
14
+ description: 'A single stdio server command to scan instead of a config (e.g. "npx -y @modelcontextprotocol/server-filesystem /tmp").'
15
+ required: false
16
+ default: ''
17
+ http:
18
+ description: 'A single streamable-HTTP MCP server URL to scan.'
19
+ required: false
20
+ default: ''
21
+ sse:
22
+ description: 'A single SSE MCP server URL to scan.'
23
+ required: false
24
+ default: ''
25
+ max-tokens:
26
+ description: 'Fail the build if any server loads more than this many tokens at connect. 0 = no budget.'
27
+ required: false
28
+ default: '0'
29
+ fail-on-flagged:
30
+ description: 'Fail the build if any server trips a bounded signal (injection-shaped / shadowing / undeclared).'
31
+ required: false
32
+ default: 'false'
33
+ version:
34
+ description: 'mcpgawk version to install from PyPI ("latest" or e.g. "0.1.2").'
35
+ required: false
36
+ default: 'latest'
37
+
38
+ runs:
39
+ using: 'composite'
40
+ steps:
41
+ - name: Set up Python
42
+ uses: actions/setup-python@v5
43
+ with:
44
+ python-version: '3.12'
45
+ - name: Install mcpgawk
46
+ shell: bash
47
+ run: |
48
+ if [ "${{ inputs.version }}" = "latest" ]; then
49
+ pip install --quiet --disable-pip-version-check mcpgawk
50
+ else
51
+ pip install --quiet --disable-pip-version-check "mcpgawk==${{ inputs.version }}"
52
+ fi
53
+ - name: Scan and gate
54
+ shell: bash
55
+ env:
56
+ MCPGAWK_CONFIG: ${{ inputs.config }}
57
+ MCPGAWK_STDIO: ${{ inputs.stdio }}
58
+ MCPGAWK_HTTP: ${{ inputs.http }}
59
+ MCPGAWK_SSE: ${{ inputs.sse }}
60
+ MCPGAWK_MAX_TOKENS: ${{ inputs.max-tokens }}
61
+ MCPGAWK_FAIL_ON_FLAGGED: ${{ inputs.fail-on-flagged }}
62
+ run: python "${{ github.action_path }}/scripts/ci_gate.py"
@@ -0,0 +1 @@
1
+ .vercel
@@ -0,0 +1,69 @@
1
+ // Vercel serverless function — the ONLY path into the `leads` table.
2
+ // Verifies Cloudflare Turnstile server-side, runs honeypot/timing/email guards,
3
+ // then inserts using the Supabase service-role key (never exposed to the browser).
4
+ // Env vars (set in Vercel → Settings → Environment Variables):
5
+ // SUPABASE_URL, SUPABASE_SERVICE_ROLE, TURNSTILE_SECRET
6
+ const DISPOSABLE = new Set([
7
+ 'mailinator.com','guerrillamail.com','10minutemail.com','tempmail.com','temp-mail.org',
8
+ 'trashmail.com','yopmail.com','getnada.com','sharklasers.com','maildrop.cc','dispostable.com',
9
+ 'mailnesia.com','fakeinbox.com','throwawaymail.com','mohmal.com','mintemail.com'
10
+ ]);
11
+
12
+ export default async function handler(req, res) {
13
+ if (req.method !== 'POST') { res.status(405).json({ error: 'method' }); return; }
14
+
15
+ const SITE = process.env.SUPABASE_URL;
16
+ const KEY = process.env.SUPABASE_SERVICE_ROLE;
17
+ const TS = process.env.TURNSTILE_SECRET;
18
+ if (!SITE || !KEY || !TS) { res.status(500).json({ error: 'not-configured' }); return; }
19
+
20
+ let body = req.body;
21
+ if (typeof body === 'string') { try { body = JSON.parse(body); } catch (_) { body = {}; } }
22
+ body = body || {};
23
+
24
+ // 1) honeypot — a bot filled the hidden field. Pretend success, insert nothing.
25
+ if (body.website) { res.status(200).json({ ok: true }); return; }
26
+
27
+ // 2) timing — the form must have been open at least a couple of seconds (and not for days).
28
+ const elapsed = Number(body.elapsed || 0);
29
+ if (!(elapsed >= 2500 && elapsed < 1000 * 60 * 60 * 6)) { res.status(400).json({ error: 'timing' }); return; }
30
+
31
+ // 3) field validation
32
+ const name = String(body.name || '').trim().slice(0, 120);
33
+ const email = String(body.email || '').trim().slice(0, 200).toLowerCase();
34
+ if (!name || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) { res.status(400).json({ error: 'fields' }); return; }
35
+ if (DISPOSABLE.has(email.split('@')[1])) { res.status(400).json({ error: 'disposable' }); return; }
36
+
37
+ // 4) Turnstile — verify the token with Cloudflare (this is what a bot cannot fake).
38
+ const token = String(body.turnstileToken || '');
39
+ if (!token) { res.status(400).json({ error: 'captcha-missing' }); return; }
40
+ const ip = String(req.headers['x-forwarded-for'] || '').split(',')[0].trim();
41
+ let verify;
42
+ try {
43
+ verify = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
44
+ method: 'POST',
45
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
46
+ body: new URLSearchParams({ secret: TS, response: token, remoteip: ip })
47
+ }).then(r => r.json());
48
+ } catch (_) { verify = { success: false }; }
49
+ if (!verify || !verify.success) { res.status(400).json({ error: 'captcha-failed' }); return; }
50
+
51
+ // 5) insert via service-role (bypasses RLS; the public key can no longer write once the policy is dropped).
52
+ const ins = await fetch(SITE.replace(/\/$/, '') + '/rest/v1/leads', {
53
+ method: 'POST',
54
+ headers: {
55
+ apikey: KEY, Authorization: 'Bearer ' + KEY,
56
+ 'Content-Type': 'application/json', Prefer: 'return=minimal'
57
+ },
58
+ body: JSON.stringify({
59
+ name, email,
60
+ company: String(body.company || '').trim().slice(0, 160) || null,
61
+ wants_scan: !!body.wants_scan,
62
+ opt_in: !!body.opt_in,
63
+ source: 'mcp.gawk.dev'
64
+ })
65
+ });
66
+ if (ins.status === 201 || ins.status === 409) { res.status(200).json({ ok: true }); return; } // created, or already on the list
67
+ const detail = await ins.text().catch(() => '');
68
+ res.status(502).json({ error: 'insert', detail: detail.slice(0, 200) });
69
+ }
@@ -0,0 +1 @@
1
+ <svg fill="#FF6A00" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Alibaba.com</title><path d="M14.391 16.22c-.963.044-.865-.459-.302-1.234 1.32-1.768 3.82-4.236 3.906-5.982.151-2.283-2.143-3.026-4.501-3.004-1.645.022-3.344.492-4.501.906C5 8.315 2.489 10.576.909 13.076-.768 15.554-.216 17.923 3.322 18c2.716-.109 4.48-.862 6.32-1.802.01 0-5.086 1.453-6.958.383l-.008-.002c-.193-.11-.404-.264-.457-.683-.012-.885 1.46-1.802 2.283-2.097v-1.533a5.374 5.374 0 0 0 1.955.366 5.378 5.378 0 0 0 3.472-1.265c.037.13.056.278.044.447h.371c.048-.394-.172-.706-.172-.706-.333-.529-.915-.52-.915-.52s.315.137.529.466a4.953 4.953 0 0 1-4.665.932l1.21-1.2-.336-.874c2.435-.852 4.48-1.507 7.812-2.085l-.746-.624.389-.24c2.01.568 3.325.985 3.253 2.051a2.672 2.672 0 0 1-.202.611c-.584 1.158-2.326 3.09-3.029 3.898-.465.535-.92 1.06-1.245 1.562-.335.503-.54.971-.551 1.42.043 3.504 10.334-1.64 12.324-3.003-2.943 1.266-6.113 2.489-9.609 2.718Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="256" height="257" preserveAspectRatio="xMidYMid" viewBox="0 0 256 257"><path fill="#D97757" d="m50.228 170.321 50.357-28.257.843-2.463-.843-1.361h-2.462l-8.426-.518-28.775-.778-24.952-1.037-24.175-1.296-6.092-1.297L0 125.796l.583-3.759 5.12-3.434 7.324.648 16.202 1.101 24.304 1.685 17.629 1.037 26.118 2.722h4.148l.583-1.685-1.426-1.037-1.101-1.037-25.147-17.045-27.22-18.017-14.258-10.37-7.713-5.25-3.888-4.925-1.685-10.758 7-7.713 9.397.649 2.398.648 9.527 7.323 20.35 15.75L94.817 91.9l3.889 3.24 1.555-1.102.195-.777-1.75-2.917-14.453-26.118-15.425-26.572-6.87-11.018-1.814-6.61c-.648-2.723-1.102-4.991-1.102-7.778l7.972-10.823L71.42 0 82.05 1.426l4.472 3.888 6.61 15.101 10.694 23.786 16.591 32.34 4.861 9.592 2.592 8.879.973 2.722h1.685v-1.556l1.36-18.211 2.528-22.36 2.463-28.776.843-8.1 4.018-9.722 7.971-5.25 6.222 2.981 5.12 7.324-.713 4.73-3.046 19.768-5.962 30.98-3.889 20.739h2.268l2.593-2.593 10.499-13.934 17.628-22.036 7.778-8.749 9.073-9.657 5.833-4.601h11.018l8.1 12.055-3.628 12.443-11.342 14.388-9.398 12.184-13.48 18.147-8.426 14.518.778 1.166 2.01-.194 30.46-6.481 16.462-2.982 19.637-3.37 8.88 4.148.971 4.213-3.5 8.62-20.998 5.184-24.628 4.926-36.682 8.685-.454.324.519.648 16.526 1.555 7.065.389h17.304l32.21 2.398 8.426 5.574 5.055 6.805-.843 5.184-12.962 6.611-17.498-4.148-40.83-9.721-14-3.5h-1.944v1.167l11.666 11.406 21.387 19.314 26.767 24.887 1.36 6.157-3.434 4.86-3.63-.518-23.526-17.693-9.073-7.972-20.545-17.304h-1.36v1.814l4.73 6.935 25.017 37.59 1.296 11.536-1.814 3.76-6.481 2.268-7.13-1.297-14.647-20.544-15.1-23.138-12.185-20.739-1.49.843-7.194 77.448-3.37 3.953-7.778 2.981-6.48-4.925-3.436-7.972 3.435-15.749 4.148-20.544 3.37-16.333 3.046-20.285 1.815-6.74-.13-.454-1.49.194-15.295 20.999-23.267 31.433-18.406 19.702-4.407 1.75-7.648-3.954.713-7.064 4.277-6.286 25.47-32.405 15.36-20.092 9.917-11.6-.065-1.686h-.583L44.07 198.125l-12.055 1.555-5.185-4.86.648-7.972 2.463-2.593 20.35-13.999-.064.065Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="#2932E1" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Baidu</title><path d="M9.154 0C7.71 0 6.54 1.658 6.54 3.707c0 2.051 1.171 3.71 2.615 3.71 1.446 0 2.614-1.659 2.614-3.71C11.768 1.658 10.6 0 9.154 0zm7.025.594C14.86.58 13.347 2.589 13.2 3.927c-.187 1.745.25 3.487 2.179 3.735 1.933.25 3.175-1.806 3.422-3.364.252-1.555-.995-3.364-2.362-3.674a1.218 1.218 0 0 0-.261-.03zM3.582 5.535a2.811 2.811 0 0 0-.156.008c-2.118.19-2.428 3.24-2.428 3.24-.287 1.41.686 4.425 3.297 3.864 2.617-.561 2.262-3.68 2.183-4.362-.125-1.018-1.292-2.773-2.896-2.75zm16.534 1.753c-2.308 0-2.617 2.119-2.617 3.616 0 1.43.121 3.425 2.988 3.362 2.867-.063 2.553-3.238 2.553-3.988 0-.745-.62-2.99-2.924-2.99zm-8.264 2.478c-1.424.014-2.708.925-3.323 1.947-1.118 1.868-2.863 3.05-3.112 3.363-.25.309-3.61 2.116-2.864 5.42.746 3.301 3.365 3.237 3.365 3.237s1.93.19 4.171-.31c2.24-.495 4.17.123 4.17.123s5.233 1.748 6.665-1.616c1.43-3.364-.808-5.109-.808-5.109s-2.99-2.306-4.736-4.798c-1.072-1.665-2.348-2.268-3.528-2.257zm-2.234 3.84l1.542.024v8.197H7.758c-1.47-.291-2.055-1.292-2.13-1.462-.072-.173-.488-.976-.268-2.343.635-2.049 2.447-2.196 2.447-2.196h1.81zm3.964 2.39v3.881c.096.413.612.488.612.488h1.614v-4.343h1.689v5.782h-3.915c-1.517-.39-1.59-1.465-1.59-1.465v-4.317zm-5.458 1.147c-.66.197-.978.708-1.05.928-.076.22-.247.78-.1 1.269.294 1.095 1.248 1.144 1.248 1.144h1.37v-3.34z"/></svg>
@@ -0,0 +1,4 @@
1
+ <svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M29.4186 5.62713C24.2806 5.62713 19.353 7.6682 15.7199 11.3013C12.0868 14.9345 10.0457 19.862 10.0457 25C10.0457 30.1381 12.0868 35.0656 15.7199 38.6988C19.353 42.3319 24.2806 44.373 29.4186 44.373V47.2917C17.1061 47.2917 7.12695 37.3105 7.12695 24.998C7.12695 12.6855 17.104 2.7063 29.4165 2.7063V5.62505L29.4186 5.62713ZM39.3186 13.2875C37.7794 11.9809 35.9971 10.9915 34.0741 10.3761C32.1512 9.76067 30.1256 9.53147 28.1137 9.70166C26.1019 9.87185 24.1435 10.4381 22.3513 11.3677C20.559 12.2974 18.9683 13.5722 17.6704 15.1189C16.3726 16.6655 15.3933 18.4534 14.7888 20.3798C14.1844 22.3062 13.9667 24.3332 14.1483 26.344C14.33 28.3548 14.9073 30.3099 15.8472 32.0968C16.787 33.8838 18.0709 35.4673 19.6249 36.7563L17.7478 38.9938C15.9126 37.4545 14.3987 35.5687 13.2924 33.4442C12.1862 31.3197 11.5092 28.9981 11.3003 26.6119C11.0914 24.2258 11.3545 21.8219 12.0747 19.5374C12.7949 17.253 13.9581 15.1328 15.4978 13.298C17.0373 11.4628 18.9233 9.94877 21.0479 8.84247C23.1726 7.73618 25.4944 7.05923 27.8807 6.8503C30.267 6.64136 32.6711 6.90453 34.9557 7.62477C37.2403 8.34501 39.3607 9.50822 41.1957 11.048L39.3186 13.2875ZM34.6207 15.0459C31.9818 13.6829 28.9113 13.4172 26.0776 14.3068C23.2438 15.1964 20.876 17.1691 19.4895 19.7958C18.1029 22.4224 17.8099 25.4903 18.6741 28.332C19.5384 31.1736 21.4899 33.5589 24.104 34.9688L22.7374 37.5521C19.4721 35.7617 17.0418 32.7592 15.9707 29.1927C14.8996 25.6261 15.2737 21.7815 17.0123 18.4883C18.7509 15.1952 21.7146 12.7176 25.2637 11.5903C28.8129 10.463 32.663 10.7763 35.9832 12.4625L34.6207 15.0459ZM29.4165 17.7896C27.5042 17.7896 25.6702 18.5493 24.318 19.9015C22.9658 21.2537 22.2061 23.0877 22.2061 25C22.2061 26.9124 22.9658 28.7464 24.318 30.0986C25.6702 31.4508 27.5042 32.2105 29.4165 32.2105V35.1313C26.7296 35.1313 24.1526 34.0639 22.2527 32.1639C20.3527 30.2639 19.2853 27.687 19.2853 25C19.2853 22.3131 20.3527 19.7362 22.2527 17.8362C24.1526 15.9362 26.7296 14.8688 29.4165 14.8688V17.7896Z" fill="#F15A29"/>
3
+ <path d="M32.0981 22.5749C31.7875 22.2404 31.4295 21.9534 31.0356 21.7228C30.6925 21.519 30.3014 21.4097 29.9023 21.4062C29.371 21.4062 28.896 21.5041 28.4773 21.6978C28.0725 21.8802 27.7089 22.1425 27.4081 22.469C27.1074 22.7955 26.8758 23.1795 26.7273 23.5978C26.5731 24.0207 26.4981 24.4645 26.4981 24.9124C26.4981 25.3666 26.5731 25.8082 26.7273 26.227C26.877 26.6449 27.1091 27.0286 27.4096 27.3553C27.7102 27.682 28.0733 27.9451 28.4773 28.1291C28.8939 28.3228 29.3731 28.4207 29.9023 28.4207C30.3523 28.4207 30.771 28.3249 31.1564 28.1395C31.5481 27.952 31.8856 27.6707 32.146 27.3228L34.0794 29.4187C33.7877 29.7103 33.4544 29.9624 33.0752 30.1749C32.374 30.566 31.6108 30.8338 30.8189 30.9666C30.4648 31.0207 30.1585 31.0499 29.9023 31.0499C29.0617 31.0552 28.2271 30.9069 27.4398 30.6124C26.6954 30.3371 26.0144 29.914 25.4377 29.3687C24.8643 28.8208 24.4078 28.1624 24.096 27.4332C23.7572 26.6366 23.5883 25.778 23.6002 24.9124C23.6002 23.9874 23.7669 23.1478 24.096 22.3916C24.4085 21.6624 24.8627 21.0041 25.4356 20.4562C26.0148 19.9124 26.696 19.4895 27.4398 19.2145C28.2271 18.92 29.0617 18.7717 29.9023 18.777C30.6419 18.777 31.3856 18.9187 32.1356 19.202C32.8877 19.4895 33.5627 19.952 34.1023 20.5541L32.0981 22.5749Z" fill="black"/>
4
+ </svg>