katman-mcp 0.1.0

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 (97) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +160 -0
  3. package/dist/cli.js +71 -0
  4. package/dist/context.js +43 -0
  5. package/dist/crawlers.js +86 -0
  6. package/dist/index.js +25 -0
  7. package/dist/license.js +448 -0
  8. package/dist/meta.js +28 -0
  9. package/dist/net.js +128 -0
  10. package/dist/server.js +17 -0
  11. package/dist/state.js +81 -0
  12. package/dist/tools/account/account.js +64 -0
  13. package/dist/tools/account/cli.js +21 -0
  14. package/dist/tools/account/index.js +48 -0
  15. package/dist/tools/account/sync.js +142 -0
  16. package/dist/tools/account/upsell.js +290 -0
  17. package/dist/tools/crawl/audit/analyze.js +719 -0
  18. package/dist/tools/crawl/audit/bots.js +114 -0
  19. package/dist/tools/crawl/audit/checks.js +126 -0
  20. package/dist/tools/crawl/audit/html.js +382 -0
  21. package/dist/tools/crawl/audit/index.js +364 -0
  22. package/dist/tools/crawl/audit/model.js +1 -0
  23. package/dist/tools/crawl/audit/report.js +199 -0
  24. package/dist/tools/crawl/audit/robots.js +199 -0
  25. package/dist/tools/crawl/audit/sitemap.js +147 -0
  26. package/dist/tools/crawl/cli.js +106 -0
  27. package/dist/tools/crawl/common/advice.js +61 -0
  28. package/dist/tools/crawl/common/findings.js +57 -0
  29. package/dist/tools/crawl/common/routes.js +80 -0
  30. package/dist/tools/crawl/common/text.js +89 -0
  31. package/dist/tools/crawl/common/urls.js +64 -0
  32. package/dist/tools/crawl/index.js +226 -0
  33. package/dist/tools/crawl/indexnow.js +271 -0
  34. package/dist/tools/crawl/keywords.js +178 -0
  35. package/dist/tools/crawl/scan/framework.js +645 -0
  36. package/dist/tools/crawl/scan/index.js +175 -0
  37. package/dist/tools/crawl/scan/project.js +180 -0
  38. package/dist/tools/crawl/scan/routes.js +354 -0
  39. package/dist/tools/crawl/scan/signals.js +414 -0
  40. package/dist/tools/method/cli.js +42 -0
  41. package/dist/tools/method/core/brief.js +302 -0
  42. package/dist/tools/method/core/extract.js +435 -0
  43. package/dist/tools/method/core/generate.js +504 -0
  44. package/dist/tools/method/core/lint.js +556 -0
  45. package/dist/tools/method/core/plan.js +405 -0
  46. package/dist/tools/method/core/prompt.js +150 -0
  47. package/dist/tools/method/core/questions.js +196 -0
  48. package/dist/tools/method/core/report.js +182 -0
  49. package/dist/tools/method/core/research-run.js +148 -0
  50. package/dist/tools/method/core/research.js +448 -0
  51. package/dist/tools/method/core/snippets.js +292 -0
  52. package/dist/tools/method/core/start.js +277 -0
  53. package/dist/tools/method/data/anatomy.js +141 -0
  54. package/dist/tools/method/data/appendices.js +202 -0
  55. package/dist/tools/method/data/cloudflare.js +192 -0
  56. package/dist/tools/method/data/evidence.js +137 -0
  57. package/dist/tools/method/data/geo-jobs.js +128 -0
  58. package/dist/tools/method/data/index.js +19 -0
  59. package/dist/tools/method/data/landing.js +67 -0
  60. package/dist/tools/method/data/lessons.js +127 -0
  61. package/dist/tools/method/data/measurement.js +142 -0
  62. package/dist/tools/method/data/method.js +141 -0
  63. package/dist/tools/method/data/offsite.js +148 -0
  64. package/dist/tools/method/data/pages.js +167 -0
  65. package/dist/tools/method/data/prompts.js +633 -0
  66. package/dist/tools/method/data/publish.js +113 -0
  67. package/dist/tools/method/data/render.js +117 -0
  68. package/dist/tools/method/data/research.js +131 -0
  69. package/dist/tools/method/data/roadmap.js +105 -0
  70. package/dist/tools/method/data/traps.js +74 -0
  71. package/dist/tools/method/data/verify.js +88 -0
  72. package/dist/tools/method/data/viral-list.js +39 -0
  73. package/dist/tools/method/index.js +209 -0
  74. package/dist/tools/method/lib/calls.js +53 -0
  75. package/dist/tools/method/lib/framework.js +525 -0
  76. package/dist/tools/method/lib/l10n.js +17 -0
  77. package/dist/tools/method/lib/routes.js +118 -0
  78. package/dist/tools/method/lib/state.js +454 -0
  79. package/dist/tools/method/lib/text.js +165 -0
  80. package/dist/tools/method/mcp/prompts.js +79 -0
  81. package/dist/tools/method/mcp/resources.js +50 -0
  82. package/dist/tools/visibility/analyze.js +463 -0
  83. package/dist/tools/visibility/cli.js +86 -0
  84. package/dist/tools/visibility/estimate.js +105 -0
  85. package/dist/tools/visibility/index.js +108 -0
  86. package/dist/tools/visibility/judge.js +156 -0
  87. package/dist/tools/visibility/manual.js +275 -0
  88. package/dist/tools/visibility/models.js +277 -0
  89. package/dist/tools/visibility/monitoring.js +165 -0
  90. package/dist/tools/visibility/openrouter.js +449 -0
  91. package/dist/tools/visibility/render.js +295 -0
  92. package/dist/tools/visibility/run.js +684 -0
  93. package/dist/tools/visibility/summary.js +164 -0
  94. package/dist/types.js +3 -0
  95. package/dist/util.js +92 -0
  96. package/package.json +54 -0
  97. package/server.json +34 -0
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2026 Rnify Limited. All rights reserved.
2
+
3
+ Katman MCP ("katman-mcp") is proprietary software. You may install and run the
4
+ published npm package to use Katman with your own projects. The free tools work
5
+ without a key; Katman Pro features require a valid Katman key and are subject to
6
+ the terms at https://katman.pro/terms.
7
+
8
+ You may not copy, modify, redistribute, sublicense or sell the software, or
9
+ remove or bypass its licence checks, except as allowed by those terms or by law.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12
+ IMPLIED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
13
+ LIABILITY ARISING FROM THE USE OF THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # Katman MCP
2
+
3
+ **Get your vibe-coded site recommended by ChatGPT.**
4
+
5
+ Point Katman at your vibe-coded site. It reads your code and your live pages the way ChatGPT's crawler does, then tells your AI coding tool exactly what to fix and which pages to write, so assistants can read, quote and recommend you. No SEO background needed.
6
+
7
+ Katman is an MCP server that works inside your AI coding tool (Cursor, Claude Code, VS Code, Windsurf, Codex, Gemini CLI, Zed, Cline). It is not an SEO agency or a rank tracker.
8
+
9
+ - Website and docs: https://katman.pro/mcp
10
+ - The method behind it: https://katman.pro/method
11
+
12
+ ## Pricing
13
+
14
+ | Plan | Price | What you get |
15
+ |---|---|---|
16
+ | **Free** | $0, no account, no key | The diagnosis: `katman_start`, `katman_research`, `katman_scan_code`, `katman_audit_site`, `katman_keywords`, `katman_account`. Katman shows what keeps assistants from reading, quoting and recommending your site. |
17
+ | **7-day trial** | $0, no card | Everything in Katman Pro. Starts when you create your account at https://katman.pro/app. |
18
+ | **Katman Pro** | **$29/month founding price** (first 100 customers, locked in) | The treatment: the plan, prompts P0–P14 with acceptance checks, page briefs, generators, content lint, AI-visibility runs, manual checks, IndexNow, reports, monitoring and the katman.pro dashboard sync. |
19
+
20
+ Pro tools need `KATMAN_KEY` in your MCP config: sign in at https://katman.pro/app → **MCP → Create key**. Without a key, a Pro tool tells you what it would do for your project and how to start the trial, instead of running. `katman_account` shows your plan at any time.
21
+
22
+ ---
23
+
24
+ ## Why
25
+
26
+ AI assistants recommend a site only when three gates are open:
27
+
28
+ | Gate | What it means | What usually breaks it |
29
+ |---|---|---|
30
+ | **Read** | Bots can reach the page and the text is in the server HTML | Cloudflare blocking AI bots, text rendered only by JavaScript (Lovable/Bolt/Vite SPAs), soft 404s |
31
+ | **Quote** | The first 1–3 sentences answer the question on their own, with the same one sentence everywhere | Pages that open with "Welcome to…", llms.txt contradicting the pricing page |
32
+ | **Fit** | There's a page for the situation the customer describes to the assistant | Pages named after the product instead of the customer's problem |
33
+
34
+ Our own sample site, an AI-powered consumer web app we launched on 21 September 2026, went through these gates in its first week. It got **333 real ChatGPT visitors in 7 days**, and **11 of its 17 paying customers** came from ChatGPT. Source: the site's own analytics, human sessions only. The site is our own product, not a client. The full story is at https://katman.pro/case-studies/sample-site-first-7-days.
35
+
36
+ ## Install
37
+
38
+ Katman runs locally with `npx`. Both keys are optional:
39
+
40
+ - `KATMAN_KEY` (`km_live_…`) unlocks the Pro tools. Get it at https://katman.pro/app → **MCP → Create key** (7-day free trial, no card). The free tools work without it.
41
+ - `OPENROUTER_API_KEY` is only for AI-visibility runs through APIs, paid with your own OpenRouter credits.
42
+
43
+ **Claude Code**
44
+ ```bash
45
+ claude mcp add katman --scope user -e KATMAN_KEY=km_live_... -e OPENROUTER_API_KEY=sk-or-... -- npx -y katman-mcp
46
+ ```
47
+ Already added without a key? Run `claude mcp remove katman --scope user` first. With the Katman Claude Code plugin, set its `katman_key` and `openrouter_api_key` options instead.
48
+
49
+ **Cursor**: add this to `~/.cursor/mcp.json` (your user file, so the key never lands in a repository), or use the "Add to Cursor" button on https://katman.pro/mcp.
50
+ ```json
51
+ { "mcpServers": { "katman": { "command": "npx", "args": ["-y", "katman-mcp"], "env": { "KATMAN_KEY": "km_live_...", "OPENROUTER_API_KEY": "${env:OPENROUTER_API_KEY}" } } } }
52
+ ```
53
+
54
+ **VS Code (Copilot)**: add this to `.vscode/mcp.json`. VS Code asks for the keys once and keeps them in its secret storage.
55
+ ```json
56
+ {
57
+ "inputs": [
58
+ { "type": "promptString", "id": "katman_key", "description": "Katman key from katman.pro/app (optional)", "password": true },
59
+ { "type": "promptString", "id": "openrouter_api_key", "description": "OpenRouter API key (optional)", "password": true }
60
+ ],
61
+ "servers": { "katman": { "type": "stdio", "command": "npx", "args": ["-y", "katman-mcp"], "env": { "KATMAN_KEY": "${input:katman_key}", "OPENROUTER_API_KEY": "${input:openrouter_api_key}" } } }
62
+ }
63
+ ```
64
+
65
+ **Codex CLI**
66
+ ```bash
67
+ codex mcp add katman --env KATMAN_KEY=km_live_... --env OPENROUTER_API_KEY=sk-or-... -- npx -y katman-mcp
68
+ ```
69
+
70
+ **Gemini CLI**: Gemini strips inherited `*KEY*` variables, so pass the keys explicitly.
71
+ ```bash
72
+ gemini mcp add -s user -e KATMAN_KEY=km_live_... -e OPENROUTER_API_KEY=sk-or-... katman npx -- -y katman-mcp
73
+ ```
74
+
75
+ **Claude Desktop, Windsurf/Devin Desktop, Zed, Cline**: use the same `mcpServers` block as for Cursor, with `command: "npx"`, `args: ["-y", "katman-mcp"]` and the keys under `env`.
76
+
77
+ After adding or changing a key, restart the Katman server (or reload the editor window) and call `katman_account` to check the plan.
78
+
79
+ ## First session
80
+
81
+ Open your project in your editor and tell your AI:
82
+
83
+ > Use Katman to make this site recommendable by ChatGPT. Start with katman_start.
84
+
85
+ Katman then walks your agent through four steps. Step 1 and the diagnosis in step 2 (scan and audit) are free; the rest is Katman Pro:
86
+
87
+ 1. **Research** (`katman_research`): your one sentence (what the customer brings, what you do, what they get, which fear is unnecessary), a not-X note, 10–12 customer situations written without your brand name, competitors (with source and date), proof and a question set.
88
+ 2. **Plan** (`katman_scan_code`, `katman_audit_site`, `katman_plan`): a report of what's broken, with no code changes. Then a page plan and a task list ordered like a 30-day plan.
89
+ 3. **Build** (`katman_prompt` P0–P14, `katman_page_brief`, `katman_generate`): exact prompts for your AI, each with an acceptance criterion, plus generated robots.txt, llms.txt, JSON-LD and analytics snippets.
90
+ 4. **Verify** (`katman_audit_site`, `katman_lint_content`, `katman_indexnow`): Katman checks that each acceptance criterion is really met, then notifies search engines about new or changed URLs only.
91
+
92
+ Measure every 14 days with `katman_visibility_run` (API) or `katman_manual_check` (answers you paste from the ChatGPT/Claude/Perplexity apps).
93
+
94
+ ## Tools
95
+
96
+ | Tool | Plan | What it does | Writes |
97
+ |---|---|---|---|
98
+ | `katman_start` | Free | Shows where you are in the method and the exact next call (plus a one-line plan status when `KATMAN_KEY` is set) | `config.json` (site URL) |
99
+ | `katman_research` | Free | Research template, merge and validation, 12+2 question set | `research.json`, `research.md` |
100
+ | `katman_keywords` | Free | Google autocomplete expansion into situation and single-question page ideas (≤40 throttled requests) | `research.json` (optional) |
101
+ | `katman_scan_code` | Free | Framework, rendering (SSR/SSG/CSR), routes, head tags, robots/sitemap/llms.txt, JSON-LD, links, images, analytics | `scan.json` |
102
+ | `katman_audit_site` | Free | Live audit as AI crawlers see it: bot access through your CDN, JS-off text, 404s, canonicals, sitemap, hreflang, JSON-LD, internal links | `audits/`, `audit.md` |
103
+ | `katman_account` | Free | Your plan (free, trial days left, Pro), features, masked account email, last key check, how to connect or upgrade | — |
104
+ | `katman_plan` | Pro | Page plan and task list with P-codes and acceptance criteria (keeps your checkboxes) | `plan.json`, `plan.md` |
105
+ | `katman_prompt` | Pro | Prompt P0–P14 filled with your data, plus Accept / Why / How Katman verifies | — |
106
+ | `katman_page_brief` | Pro | Brief for a situation, comparison, pricing, FAQ or about page, and where the file goes in your framework | — |
107
+ | `katman_generate` | Pro | robots.txt, llms.txt, Organization/WebSite/SoftwareApplication/FAQ/Breadcrumb JSON-LD, AI-channel analytics snippet, server-side bot counter, IndexNow key | only with `write: true`, never overwrites without `overwrite: true` |
108
+ | `katman_lint_content` | Pro | Checks a page against the content rules: answer-first, same one sentence, no hype, sourced numbers, FAQ, not-for, links | — |
109
+ | `katman_visibility_run` | Pro | Asks your question set to ChatGPT, Claude, Gemini, Perplexity and Grok models with live web search via OpenRouter (API answers are a proxy for the consumer apps). Dry run and cost estimate first; presets economy/full/probe; `samples` for stability | `runs/`, `measurements.md` |
110
+ | `katman_manual_check` | Pro | Records an answer you paste from a consumer app (logged out, search on); no API cost | `runs/`, `measurements.md` |
111
+ | `katman_indexnow` | Pro | Submits only new or changed URLs to IndexNow, plus a Search Console / Bing / Brave checklist | `indexnow.json` |
112
+ | `katman_setup_monitoring` | Pro | GitHub Actions workflow that re-measures weekly with your own keys | `.github/workflows/katman.yml` only with `write: true` |
113
+ | `katman_report` | Pro | Shareable report: gates, visibility deltas, competitors, cited domains, next 7 days | `report.md` |
114
+ | `katman_sync` | Pro | Sends your progress to your katman.pro dashboard: gate pass counts, visibility rates by assistant and language, plan progress, top 10 issue titles, dates, Katman version. Never code, file contents, answers, questions or prompts. Dry run (the exact JSON) by default | — |
115
+
116
+ Slash-command prompts: `katman-start`, `katman-audit`, `katman-situation-page`, `katman-measure`.
117
+
118
+ ## What Katman can and can't do
119
+
120
+ - **Reads** files in your project, but skips `node_modules`, `.git` and build output, and **never** reads `.env*`, `.dev.vars*`, keys, certificates or credential files.
121
+ - **Writes** only to `.katman/` in your project, except `katman_generate` and `katman_setup_monitoring` when you pass `write: true`.
122
+ - **Network**: your own site (audits), `openrouter.ai` (visibility runs, with your key, after a dry run), `api.indexnow.org` (only when you submit), `suggestqueries.google.com` (only `katman_keywords`), and `katman.pro` only for two things: checking your `KATMAN_KEY` (at most once a day, only when the key is set) and `katman_sync` (only with `dry_run: false`). **No telemetry**: without a key, nothing is sent to Katman; the key check sends only the key and the Katman version.
123
+ - **Your keys** (`KATMAN_KEY`, `OPENROUTER_API_KEY`) are read from the environment and are never printed or written to disk. The license check is cached in `~/.katman/license.json` under the key's SHA-256 hash (never the key); if katman.pro can't be reached, Pro keeps working for 7 days after the last successful check.
124
+ - **No automation of consumer chat apps.** Katman doesn't drive chatgpt.com, claude.ai or perplexity.ai with a headless browser: their terms forbid programmatic extraction, and it gets accounts banned. It uses the providers' own web-search models through OpenRouter. For the consumer apps, you paste answers into `katman_manual_check`.
125
+ - **No ranking guarantees.** A single measurement run is a small sample, so re-run the same questions every 14 days.
126
+
127
+ ## Costs
128
+
129
+ Katman itself: the diagnosis is free; Katman Pro is $29/month (founding price) after a 7-day free trial. On top of that, API visibility runs use your own OpenRouter credits; search results are billed as input tokens, so they're not pennies:
130
+
131
+ | Panel | What | Typical cost per run (14 questions) |
132
+ |---|---|---|
133
+ | `economy` (default) | ChatGPT, Claude, Gemini, Perplexity, Grok — smaller models, native search | ≈ $2.4 (range $1.2–3.7) |
134
+ | `full` | the same five with flagship models | ≈ $4.6 (range $2.2–6.9) |
135
+ | `probe` | ChatGPT + Perplexity only | ≈ $1.8 (range $0.7–2.8) |
136
+
137
+ The exact estimate is shown before anything runs, and `max_cost_usd` (default $3) refuses runs that would cost more. Checking the consumer apps by hand with `katman_manual_check` costs no API credits.
138
+
139
+ ## CLI
140
+
141
+ ```bash
142
+ npx -y katman-mcp scan [path] # free
143
+ npx -y katman-mcp audit https://your.site --max 50 # free
144
+ npx -y katman-mcp keywords "invoice software" --lang en # free
145
+ npx -y katman-mcp account [--refresh] # free: your plan
146
+ npx -y katman-mcp indexnow --root . [--submit] # dry run free; --submit is Pro
147
+ npx -y katman-mcp check --root . [--yes] [--max-cost 2] # Pro: visibility run; dry run without --yes
148
+ npx -y katman-mcp plan --root . # Pro
149
+ npx -y katman-mcp report --root . # Pro
150
+ ```
151
+
152
+ Pro commands read `KATMAN_KEY` from the environment (`KATMAN_KEY=km_live_... npx -y katman-mcp report --root .`; in GitHub Actions, a repository secret) and exit with code 4 and a short explanation without it.
153
+
154
+ ## Files
155
+
156
+ Everything Katman learns about your project lives in `.katman/`: `research.json`/`.md`, `scan.json`, `audits/`, `runs/`, `measurements.md`, `plan.md`, `report.md`, `log.md`. It contains no secrets, and it's worth committing, because it's the history of what you changed and what happened. The only file outside the project is the license cache `~/.katman/license.json` (key hash, plan, last check).
157
+
158
+ ---
159
+
160
+ Made by the Katman team · https://katman.pro · hello@katman.pro
package/dist/cli.js ADDED
@@ -0,0 +1,71 @@
1
+ // CLI dispatcher. Each module contributes commands via its cli.ts.
2
+ import path from "node:path";
3
+ import { VERSION } from "./meta.js";
4
+ import * as method from "./tools/method/cli.js";
5
+ import * as crawl from "./tools/crawl/cli.js";
6
+ import * as visibility from "./tools/visibility/cli.js";
7
+ import * as account from "./tools/account/cli.js";
8
+ import { EXIT_NEEDS_PRO, cliFeature, defaultLicense, requirePro, toolForFeature } from "./license.js";
9
+ import { projectFacts, renderUpsell } from "./tools/account/upsell.js";
10
+ export async function runCli(args, deps = {}) {
11
+ const [cmd, ...rest] = args;
12
+ if (cmd === "--version" || cmd === "-v" || cmd === "version") {
13
+ console.log(VERSION);
14
+ return 0;
15
+ }
16
+ const all = { ...method.commands, ...crawl.commands, ...visibility.commands, ...account.commands };
17
+ const fn = all[cmd];
18
+ if (!fn) {
19
+ printHelp();
20
+ return cmd === "help" || cmd === "--help" || cmd === "-h" ? 0 : 1;
21
+ }
22
+ // Katman Pro commands (plan, report, check, indexnow --submit): same gate as the MCP tools.
23
+ const feature = cliFeature(cmd, rest);
24
+ if (feature) {
25
+ const gate = await requirePro(feature, deps.license ?? defaultLicense());
26
+ if (!gate.allowed) {
27
+ const { _, flags } = parseArgs(rest);
28
+ const root = path.resolve(typeof flags.root === "string" ? flags.root : cmd === "plan" || cmd === "report" ? (_[0] ?? process.cwd()) : process.cwd());
29
+ const facts = await projectFacts(root).catch(() => undefined);
30
+ const command = `katman-mcp ${cmd}${cmd === "indexnow" ? " --submit" : ""}`;
31
+ console.error(renderUpsell({ tool: toolForFeature(feature), feature, status: gate.status, facts, surface: "cli", command }));
32
+ return EXIT_NEEDS_PRO;
33
+ }
34
+ }
35
+ return fn(rest, deps);
36
+ }
37
+ function printHelp() {
38
+ const lines = [
39
+ `katman-mcp ${VERSION} — make your site readable, quotable and recommendable by AI assistants`,
40
+ "",
41
+ "Usage:",
42
+ " katman-mcp start the MCP server (stdio) — add it to Cursor, Claude Code, VS Code…",
43
+ ...method.help,
44
+ ...crawl.help,
45
+ ...visibility.help,
46
+ ...account.help,
47
+ "",
48
+ "Docs: https://katman.pro/docs",
49
+ ];
50
+ console.log(lines.join("\n"));
51
+ }
52
+ /** Tiny flag parser: --key value, --flag, positional args. */
53
+ export function parseArgs(args) {
54
+ const out = { _: [], flags: {} };
55
+ for (let i = 0; i < args.length; i++) {
56
+ const a = args[i];
57
+ if (a.startsWith("--")) {
58
+ const key = a.slice(2);
59
+ const next = args[i + 1];
60
+ if (next !== undefined && !next.startsWith("--")) {
61
+ out.flags[key] = next;
62
+ i++;
63
+ }
64
+ else
65
+ out.flags[key] = true;
66
+ }
67
+ else
68
+ out._.push(a);
69
+ }
70
+ return out;
71
+ }
@@ -0,0 +1,43 @@
1
+ import path from "node:path";
2
+ import os from "node:os";
3
+ import { existsSync } from "node:fs";
4
+ import { fileURLToPath } from "node:url";
5
+ /**
6
+ * Resolve the project root: explicit argument → KATMAN_PROJECT_ROOT → first MCP root
7
+ * advertised by the client → process.cwd().
8
+ */
9
+ export async function resolveRoot(ctx, explicit) {
10
+ if (explicit && explicit.trim())
11
+ return path.resolve(explicit.trim());
12
+ if (process.env.KATMAN_PROJECT_ROOT)
13
+ return path.resolve(process.env.KATMAN_PROJECT_ROOT);
14
+ try {
15
+ const caps = ctx.server?.server.getClientCapabilities();
16
+ if (caps?.roots && ctx.server) {
17
+ const res = await ctx.server.server.listRoots();
18
+ const first = res.roots.find((r) => r.uri.startsWith("file://"));
19
+ if (first)
20
+ return fileURLToPath(first.uri);
21
+ }
22
+ }
23
+ catch {
24
+ // client doesn't support roots; fall through
25
+ }
26
+ return process.cwd();
27
+ }
28
+ const PROJECT_MARKERS = [
29
+ "package.json", "index.html", "astro.config.mjs", "astro.config.ts", "next.config.js",
30
+ "next.config.mjs", "next.config.ts", "nuxt.config.ts", "svelte.config.js", "vite.config.ts",
31
+ "vite.config.js", "wrangler.toml", "wrangler.jsonc", "hugo.toml", "_config.yml", "Gemfile",
32
+ "requirements.txt", "pyproject.toml", "composer.json", "go.mod",
33
+ ];
34
+ /** Returns a warning when the root doesn't look like a web project (e.g. the home directory). */
35
+ export function rootWarning(root) {
36
+ if (root === "/" || root === os.homedir()) {
37
+ return `Project root resolved to ${root}, which is not a project folder. Pass project_root with the absolute path of the website's codebase.`;
38
+ }
39
+ if (!PROJECT_MARKERS.some((m) => existsSync(path.join(root, m)))) {
40
+ return `No project files (package.json, index.html, framework config…) found in ${root}. If this is wrong, pass project_root.`;
41
+ }
42
+ return undefined;
43
+ }
@@ -0,0 +1,86 @@
1
+ export const CRAWLERS = [
2
+ // OpenAI
3
+ { token: "GPTBot", org: "OpenAI", purpose: "training", rendersJs: false, testAccess: true,
4
+ ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; GPTBot/1.4; +https://openai.com/gptbot",
5
+ note: "Training only. OpenAI: blocking GPTBot does not affect ChatGPT search." },
6
+ { token: "OAI-SearchBot", org: "OpenAI", purpose: "search", rendersJs: false, testAccess: true,
7
+ ua: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36; compatible; OAI-SearchBot/1.4; +https://openai.com/searchbot",
8
+ note: "Fills ChatGPT's search index. Blocking it removes you from ChatGPT search answers." },
9
+ { token: "ChatGPT-User", org: "OpenAI", purpose: "user", rendersJs: false, testAccess: true,
10
+ ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; ChatGPT-User/1.0; +https://openai.com/bot",
11
+ note: "Opens a page when a ChatGPT user asks for it; OpenAI says robots.txt may not apply." },
12
+ // Anthropic
13
+ { token: "ClaudeBot", org: "Anthropic", purpose: "training", rendersJs: false, testAccess: true,
14
+ ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)" },
15
+ { token: "Claude-SearchBot", org: "Anthropic", purpose: "search", rendersJs: false, testAccess: true,
16
+ ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Claude-SearchBot/1.0; +searchbot@anthropic.com)" },
17
+ { token: "Claude-User", org: "Anthropic", purpose: "user", rendersJs: false, testAccess: true,
18
+ ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Claude-User/1.0; +claude-user@anthropic.com)" },
19
+ // Perplexity
20
+ { token: "PerplexityBot", org: "Perplexity", purpose: "search", rendersJs: false, testAccess: true,
21
+ ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; PerplexityBot/1.0; +https://perplexity.ai/perplexitybot)" },
22
+ { token: "Perplexity-User", org: "Perplexity", purpose: "user", rendersJs: false, testAccess: true,
23
+ ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Perplexity-User/1.0; +https://perplexity.ai/perplexity-user)",
24
+ note: "Perplexity: this fetcher generally ignores robots.txt." },
25
+ // Search engines (they render JavaScript; CDNs verify them by IP)
26
+ { token: "Googlebot", org: "Google", purpose: "search+training", rendersJs: true, testAccess: true, ipVerified: true,
27
+ ua: "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.7339.207 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)",
28
+ note: "Feeds Google Search, AI Overviews and AI Mode. Google verifies Googlebot by IP; check real access in Search Console → Crawl stats." },
29
+ { token: "Bingbot", org: "Microsoft", purpose: "search+training", rendersJs: true, testAccess: true, ipVerified: true,
30
+ ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm) Chrome/140.0.7339.207 Safari/537.36",
31
+ note: "Copilot, ChatGPT search and DuckDuckGo draw on Bing's index." },
32
+ { token: "Applebot", org: "Apple", purpose: "search+training", rendersJs: true, testAccess: true, ipVerified: true,
33
+ ua: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15 (Applebot/0.1; +http://www.apple.com/go/applebot)" },
34
+ { token: "CCBot", org: "Common Crawl", purpose: "training", rendersJs: false, testAccess: true,
35
+ ua: "CCBot/2.0 (https://commoncrawl.org/faq/)" },
36
+ // Not access-tested (smaller, user-triggered or to-be-blocked), but known for robots.txt analysis.
37
+ { token: "Bytespider", org: "ByteDance", purpose: "training", rendersJs: false, testAccess: false,
38
+ ua: "Mozilla/5.0 (Linux; Android 5.0) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36 (compatible; Bytespider; spider-feedback@bytedance.com)",
39
+ note: "Widely reported to ignore robots.txt; block it with a WAF rule if you don't want it." },
40
+ { token: "Google-Agent", org: "Google", purpose: "user", rendersJs: true, testAccess: false,
41
+ ua: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko; compatible; Google-Agent; +https://developers.google.com/crawling/docs/crawlers-fetchers/google-agent) Chrome/140.0.7339.207 Safari/537.36",
42
+ note: "User-triggered browser agent; generally ignores robots.txt." },
43
+ { token: "Amazonbot", org: "Amazon", purpose: "search+training", rendersJs: false, testAccess: false,
44
+ ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Amazonbot/0.1; +https://developer.amazon.com/support/amazonbot) Chrome/119.0.6045.214 Safari/537.36" },
45
+ { token: "Amzn-SearchBot", org: "Amazon", purpose: "search", rendersJs: false, testAccess: false,
46
+ ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Amzn-SearchBot/0.1; +https://developer.amazon.com/amazonbot) Chrome/119.0.6045.214 Safari/537.36",
47
+ note: "Alexa / Amazon search answers." },
48
+ { token: "Amzn-User", org: "Amazon", purpose: "user", rendersJs: false, testAccess: false,
49
+ ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Amzn-User/0.1; +https://developer.amazon.com/amazonbot) Chrome/119.0.6045.214 Safari/537.36" },
50
+ { token: "meta-externalagent", org: "Meta", purpose: "training", rendersJs: false, testAccess: false,
51
+ ua: "meta-externalagent/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)" },
52
+ { token: "meta-webindexer", org: "Meta", purpose: "search", rendersJs: false, testAccess: false,
53
+ ua: "meta-webindexer/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/web-crawlers)",
54
+ note: "Meta AI search answers (new in 2026)." },
55
+ { token: "meta-externalfetcher", org: "Meta", purpose: "user", rendersJs: false, testAccess: false,
56
+ ua: "meta-externalfetcher/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/web-crawlers)" },
57
+ { token: "DuckAssistBot", org: "DuckDuckGo", purpose: "user", rendersJs: false, testAccess: false,
58
+ ua: "DuckAssistBot/1.2; (+http://duckduckgo.com/duckassistbot.html)" },
59
+ { token: "MistralAI-User", org: "Mistral", purpose: "user", rendersJs: false, testAccess: false,
60
+ ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; MistralAI-User/1.0; +https://docs.mistral.ai/robots)" },
61
+ { token: "MistralAI-Index", org: "Mistral", purpose: "search", rendersJs: false, testAccess: false,
62
+ ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; MistralAI-Index/1.0; +https://docs.mistral.ai/robots)" },
63
+ { token: "MistralAI-Training", org: "Mistral", purpose: "training", rendersJs: false, testAccess: false,
64
+ ua: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; MistralAI-Training/1.0; +https://docs.mistral.ai/robots)" },
65
+ ];
66
+ /** robots.txt permission tokens that are not separate crawlers (no UA to test). */
67
+ export const PERMISSION_TOKENS = ["Google-Extended", "Applebot-Extended"];
68
+ /** Tokens to name explicitly in a GEO-friendly robots.txt (book appendix A order, then 2026 search/agent tokens). */
69
+ export const ROBOTS_TOKENS = [
70
+ "Googlebot", "Bingbot", "OAI-SearchBot", "ChatGPT-User", "GPTBot", "ClaudeBot", "Claude-User",
71
+ "Claude-SearchBot", "PerplexityBot", "Perplexity-User", "Google-Extended", "Applebot",
72
+ "Applebot-Extended", "DuckAssistBot", "meta-externalagent", "Amazonbot",
73
+ "meta-webindexer", "meta-externalfetcher", "MistralAI-Index", "MistralAI-User", "Amzn-SearchBot", "Amzn-User",
74
+ ];
75
+ /** A normal desktop browser, used as the baseline when comparing what bots receive. */
76
+ export const BROWSER_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36";
77
+ export function crawlerByToken(token) {
78
+ const t = token.toLowerCase();
79
+ return CRAWLERS.find((c) => c.token.toLowerCase() === t);
80
+ }
81
+ /** Purpose of a robots.txt token, including permission-only tokens (Google-Extended → training). */
82
+ export function tokenPurpose(token) {
83
+ if (PERMISSION_TOKENS.some((p) => p.toLowerCase() === token.toLowerCase()))
84
+ return "training";
85
+ return crawlerByToken(token)?.purpose ?? "search";
86
+ }
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ // Katman MCP entry point.
3
+ // npx -y katman-mcp → MCP server on stdio
4
+ // npx -y katman-mcp <cmd> → CLI (audit, scan, check, report, account…); see `katman-mcp help`
5
+ // Tools are registered in server.ts (method, crawl, visibility and account modules).
6
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
+ import { VERSION } from "./meta.js";
8
+ import { createServer } from "./server.js";
9
+ import { runCli } from "./cli.js";
10
+ export { createServer };
11
+ async function main() {
12
+ const args = process.argv.slice(2);
13
+ if (args.length && args[0] !== "mcp" && args[0] !== "serve") {
14
+ process.exitCode = await runCli(args);
15
+ return;
16
+ }
17
+ const server = createServer();
18
+ await server.connect(new StdioServerTransport());
19
+ // stdout belongs to the protocol; log to stderr only.
20
+ console.error(`katman-mcp ${VERSION} ready (stdio)`);
21
+ }
22
+ main().catch((e) => {
23
+ console.error(e);
24
+ process.exit(1);
25
+ });