katman-mcp 0.2.0 → 0.3.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 (38) hide show
  1. package/README.md +48 -42
  2. package/dist/license.js +201 -12
  3. package/dist/meta.js +25 -13
  4. package/dist/plans.js +13 -6
  5. package/dist/tools/account/account.js +19 -10
  6. package/dist/tools/account/cli.js +4 -3
  7. package/dist/tools/account/index.js +1 -1
  8. package/dist/tools/account/upsell.js +156 -39
  9. package/dist/tools/crawl/audit/report.js +2 -2
  10. package/dist/tools/crawl/cli.js +18 -6
  11. package/dist/tools/crawl/index.js +39 -8
  12. package/dist/tools/crawl/scan/framework.js +4 -1
  13. package/dist/tools/method/core/lint.js +15 -1
  14. package/dist/tools/method/core/prompt.js +2 -1
  15. package/dist/tools/method/data/_store.js +17 -0
  16. package/dist/tools/method/data/anatomy.js +12 -141
  17. package/dist/tools/method/data/appendices.js +22 -202
  18. package/dist/tools/method/data/cloudflare.js +28 -192
  19. package/dist/tools/method/data/evidence.js +18 -137
  20. package/dist/tools/method/data/geo-jobs.js +8 -128
  21. package/dist/tools/method/data/landing.js +14 -67
  22. package/dist/tools/method/data/lessons.js +16 -127
  23. package/dist/tools/method/data/measurement.js +38 -142
  24. package/dist/tools/method/data/offsite.js +16 -148
  25. package/dist/tools/method/data/pages.js +16 -167
  26. package/dist/tools/method/data/prompts.js +8 -633
  27. package/dist/tools/method/data/publish.js +14 -113
  28. package/dist/tools/method/data/research.js +14 -131
  29. package/dist/tools/method/data/roadmap.js +12 -105
  30. package/dist/tools/method/data/traps.js +6 -74
  31. package/dist/tools/method/data/verify.js +12 -88
  32. package/dist/tools/method/data/viral-list.js +12 -39
  33. package/dist/tools/method/lib/prompt-lookup.js +18 -0
  34. package/dist/tools/method/mcp/resources.js +4 -3
  35. package/dist/tools/visibility/index.js +2 -2
  36. package/dist/tools/visibility/monitoring.js +3 -0
  37. package/package.json +5 -3
  38. package/server.json +3 -3
@@ -1,39 +1,12 @@
1
- export const VERDICT_LABEL = {
2
- true: { en: "TRUE", tr: "DOĞRU" },
3
- partly: { en: "PARTLY", tr: "KISMEN" },
4
- myth: { en: "MYTH", tr: "EFSANE" },
5
- };
6
- export const VIRAL_LIST = [
7
- { n: 1, item: { en: "noindex tags", tr: "noindex etiketleri" }, verdict: "true", google: { en: "noindex removes a page from the index. If robots.txt closes the same URL, Google never sees the noindex and the URL can still appear.", tr: "noindex sayfayı dizinden çıkarır. robots.txt aynı adresi kapatırsa Google noindex’i hiç göremez ve adres yine çıkabilir." } },
8
- { n: 2, item: { en: "Page titles", tr: "Sayfa başlıkları (title)" }, verdict: "true", google: { en: "A separate, descriptive title for every page. Google may write the title it shows itself, from the H1 or og:title.", tr: "Her sayfaya ayrı, içeriği anlatan bir title. Google sonuçta gösterdiği başlığı kendisi yazabilir; H1’i ya da og:title’ı kullanabilir." } },
9
- { n: 3, item: { en: "Meta descriptions", tr: "Meta açıklamalar" }, verdict: "partly", google: { en: "The snippet mostly comes from the page text; the meta description is used only sometimes. No ranking effect is stated. Write a separate one for every page.", tr: "Snippet çoğunlukla sayfanın metninden çıkar; meta açıklama yalnızca bazen kullanılır. Sıralama etkisi yazmıyor. Her sayfaya ayrı yazın." } },
10
- { n: 4, item: { en: "Image alt text", tr: "Görsellere alt metin" }, verdict: "true", google: { en: "Google understands images through alt text, image recognition and page text. Keyword-stuffed alt text can count as spam.", tr: "Google görseli alt metin, görüntü tanıma ve sayfa metniyle anlar. Anahtar kelime doldurulmuş alt metin spam sayılabilir." } },
11
- { n: 5, item: { en: "Core Web Vitals", tr: "Core Web Vitals" }, verdict: "partly", google: { en: "Used in ranking. Good: LCP ≤ 2.5 s, INP ≤ 200 ms, CLS ≤ 0.1 (at 75% of page loads). Not decisive on its own; relevance comes first.", tr: "Sıralamada kullanılır. İyi: LCP ≤ 2,5 sn, INP ≤ 200 ms, CLS ≤ 0,1 (sayfa yüklemelerinin %75’inde). Tek başına belirleyici değil; alakalılık önce gelir." } },
12
- { n: 6, item: { en: "sitemap.xml", tr: "sitemap.xml" }, verdict: "partly", google: { en: "Helps discovery but doesn't guarantee indexing. A well-linked site under ~500 pages may not need one.", tr: "Keşfe yardım eder, dizine almayı garanti etmez. Yaklaşık 500 sayfanın altındaki, iyi linklenmiş bir site ihtiyaç duymayabilir." } },
13
- { n: 7, item: { en: "og:image", tr: "og:image" }, verdict: "partly", google: { en: "The Open Graph protocol is for share previews. Since 2 March 2026 Google also uses og:image as a source for the search thumbnail; no effect on indexing or ranking.", tr: "Açık Grafik protokolü paylaşım önizlemesi içindir. Google 2 Mart 2026’dan beri og:image’i arama küçük resmi için bir kaynak olarak da kullanıyor; dizine alma ya da sıralama etkisi yok." } },
14
- { n: 8, item: { en: "Broken links", tr: "Kırık linkler" }, verdict: "partly", google: { en: "404s don't penalise the rest of the site on their own; a URL returning 4xx drops out of the index. Fix broken links to important pages; 301 moved URLs.", tr: "404’ler sitenin geri kalanını tek başına cezalandırmaz; 4xx dönen adres dizinden düşer. Önemli sayfalara giden kırık linkleri düzeltin, taşınan adresi 301 ile yönlendirin." } },
15
- { n: 9, item: { en: "Heading hierarchy", tr: "Başlık hiyerarşisi" }, verdict: "partly", google: { en: "Heading order doesn't matter to Google; a clean order helps screen readers. Bing wants a logical H1–H6 structure.", tr: "Google için başlıkların sırası önemli değil; düzgün sıra ekran okuyuculara yardım eder. Bing mantıklı bir H1–H6 düzeni istiyor." } },
16
- { n: 10, item: { en: "Backlink strategy", tr: "Backlink stratejisi" }, verdict: "partly", google: { en: "Google finds pages mostly through links and expects links to come naturally over time. Buying links and excessive link exchanges are spam.", tr: "Google sayfaları çoğunlukla linklerle bulur ve linklerin zamanla doğal gelmesini bekler. Link satın almak ve aşırı link takası spam." } },
17
- { n: 11, item: { en: "Clean URLs", tr: "Temiz URL’ler" }, verdict: "partly", google: { en: "Readable words, hyphens instead of underscores. Words in the URL have very little ranking effect. If you change a live URL, a 301 is a must.", tr: "Okunabilir kelimeler, alt çizgi yerine tire. URL’deki kelimelerin sıralamaya etkisi çok az. Canlı adresi değiştirirseniz 301 şart." } },
18
- { n: 12, item: { en: "Internal links", tr: "İç linkler" }, verdict: "true", google: { en: "Link to every page you care about from another page. A link must be an <a> with an href; the link text should describe the target.", tr: "Önem verdiğiniz her sayfaya başka bir sayfadan link verin. Link, href’i olan bir <a> olmalı; link metni hedefi anlatmalı." } },
19
- { n: 13, item: { en: "Canonical tags", tr: "Canonical etiketleri" }, verdict: "partly", google: { en: "rel=canonical is a strong signal but a hint, not a rule. None of it is mandatory; Google also picks the canonical itself.", tr: "rel=canonical güçlü bir işaret ama kural değil, ipucu. Hiçbiri zorunlu değil; Google kanonik adresi kendisi de seçer." } },
20
- { n: 14, item: { en: "HTTPS everywhere", tr: "HTTPS zorunlu" }, verdict: "true", google: { en: "HTTPS is a small ranking signal and part of page experience. Among equivalent pages Google picks the HTTPS one as canonical.", tr: "HTTPS küçük bir sıralama işareti ve sayfa deneyiminin parçası. Google eşdeğer sayfalarda HTTPS olanı kanonik seçer." } },
21
- { n: 15, item: { en: "Compress images", tr: "Görselleri sıkıştır" }, verdict: "true", google: { en: "Images are often the biggest part of page weight. The benefit is indirect: speed and LCP. WebP and AVIF are supported.", tr: "Görseller çoğu zaman sayfa boyutunun en büyük parçası. Faydası dolaylı: hız ve LCP. WebP ve AVIF destekleniyor." } },
22
- { n: 16, item: { en: "Schema markup", tr: "Schema işaretlemesi" }, verdict: "partly", google: { en: "Only makes you eligible for rich results, no guarantee; must match the visible content. The HowTo rich result was retired in 2023, the FAQ rich result on 7 May 2026.", tr: "Yalnızca zengin sonuca aday yapar, garanti vermez; görünen içerikle aynı olmalı. HowTo zengin sonucu 2023’te, SSS zengin sonucu 7 Mayıs 2026’da kalktı." } },
23
- { n: 17, item: { en: "Verify Search Console", tr: "Search Console doğrula" }, verdict: "partly", google: { en: "Not required to be indexed; most pages are found on their own. It's a monitoring and debugging tool (URL Inspection, sitemap report).", tr: "İndekslenmek için gerekmez; sayfaların çoğu kendiliğinden bulunur. İzleme ve hata ayıklama aracıdır (URL denetimi, sitemap raporu)." } },
24
- { n: 18, item: { en: "One H1 per page", tr: "Sayfada tek H1" }, verdict: "myth", google: { en: "According to Google there's no ideal number of headings; more than one H1 is fine.", tr: "Google’a göre ideal bir başlık sayısı yok; birden fazla H1 sorun değil." } },
25
- { n: 19, item: { en: "Write a robots.txt", tr: "robots.txt yaz" }, verdict: "partly", google: { en: "Not mandatory: if robots.txt returns 404, Google treats it as no restrictions. It manages crawling, not indexing; it doesn't hide a page from Google.", tr: "Zorunlu değil: robots.txt 404 dönerse Google kısıtlama yok sayar. Taramayı yönetir, dizine almayı değil; bir sayfayı Google’dan saklamaz." } },
26
- { n: 20, item: { en: "Mobile friendly", tr: "Mobil uyum" }, verdict: "true", google: { en: "Google indexes and ranks the mobile version of the site. Responsive design is recommended; content, headings and schema must be the same on mobile.", tr: "Google sitenin mobil hâlini dizine alır ve sıralar. Duyarlı tasarım önerilir; içerik, başlıklar ve schema mobilde de aynı olmalı." } },
27
- ];
28
- export const VIRAL_LIST_MISLEADING = [
29
- { en: "One H1: according to Google there's no ideal number of headings. One H1 is a tidy habit, not a ranking rule.", tr: "Tek H1: Google’a göre ideal bir başlık sayısı yok. Tek H1 düzenli bir alışkanlık, sıralama kuralı değil." },
30
- { en: "“Write a robots.txt”: you can do without one. Written wrong, it does harm: Disallow: / closes the whole site; closing a noindex page in robots.txt hides the noindex from Google.", tr: "“robots.txt yaz”: Yazmasanız da olur. Yanlış yazarsanız zarar verir: Disallow: / bütün siteyi kapatır; noindex’li bir sayfayı robots.txt’de kapatırsanız Google noindex’i göremez." },
31
- { en: "Schema: no rich-result guarantee. HowTo was retired in 2023, the FAQ rich result on 7 May 2026.", tr: "Schema: Zengin sonuç garantisi yok. HowTo 2023’te, SSS zengin sonucu 7 Mayıs 2026’da Google’dan kalktı." },
32
- { en: "Search Console: not needed to get indexed; needed to see the errors.", tr: "Search Console: İndekslenmek için gerekmez; hatayı görmek için gerekir." },
33
- { en: "Backlink “strategy”: bought links and link exchanges are in Google's spam policy. Links come when you publish something others want to quote.", tr: "Backlink “stratejisi”: Satın alınan link ve link takası Google’ın spam politikasında. Link, başkasının alıntılamak isteyeceği bir şey yayınlayınca gelir." },
34
- { en: "The real gap: the three things that most often make a vibe-coded site invisible aren't on the list at all — text in JavaScript, soft 404s, and open preview copies.", tr: "Asıl eksik: Vibe-coded bir siteyi en sık görünmez yapan üç şey listede hiç yok: metnin JavaScript’te olması, sahte 404’ler, açık kalmış önizleme kopyaları." },
35
- ];
36
- export const VIRAL_LIST_FOUND = {
37
- en: "Not on the list, found on katman.pro: AI training bots (GPTBot, ClaudeBot, CCBot, Bytespider) were getting 403 because of Cloudflare's legacy “Block AI bots” toggle. The list's biggest gap: if a bot can't get through the door, none of the other 20 items matter to the assistant.",
38
- tr: "Listede yok, katman.pro’da bulduk: Yapay zekâ eğitim botları (GPTBot, ClaudeBot, CCBot, Bytespider) Cloudflare’deki eski “Block AI bots” düğmesi yüzünden 403 alıyordu. Listenin en büyük eksiği bu: bot kapıdan giremiyorsa diğer 20 maddenin asistan tarafında karşılığı yok.",
39
- };
1
+ // Filled from katman.pro at run time for a paid key (see _store.js); the package carries no method text.
2
+ import { onMethod } from "./_store.js";
3
+ export let VERDICT_LABEL = {};
4
+ export let VIRAL_LIST = [];
5
+ export let VIRAL_LIST_FOUND = {};
6
+ export let VIRAL_LIST_MISLEADING = [];
7
+ onMethod((d) => {
8
+ if (d.VERDICT_LABEL !== undefined) VERDICT_LABEL = d.VERDICT_LABEL;
9
+ if (d.VIRAL_LIST !== undefined) VIRAL_LIST = d.VIRAL_LIST;
10
+ if (d.VIRAL_LIST_FOUND !== undefined) VIRAL_LIST_FOUND = d.VIRAL_LIST_FOUND;
11
+ if (d.VIRAL_LIST_MISLEADING !== undefined) VIRAL_LIST_MISLEADING = d.VIRAL_LIST_MISLEADING;
12
+ });
@@ -0,0 +1,18 @@
1
+ // Look-ups over the prompt book. Kept out of data/prompts.ts so every data module holds data only: the published
2
+ // build replaces data modules with stubs filled from katman.pro (scripts/postbuild.mjs, src/tools/method/data/_store.ts).
3
+ import { PLACEHOLDERS, PROMPTS } from "../data/prompts.js";
4
+ export function getPrompt(id) {
5
+ const m = /^p?\s*(\d{1,2})$/i.exec(id.trim());
6
+ const norm = m ? `P${Number(m[1])}` : id.trim().toUpperCase();
7
+ return PROMPTS.find((p) => p.id === norm);
8
+ }
9
+ export function promptPlaceholders(p) {
10
+ const found = new Set();
11
+ for (const text of [p.body.en, p.body.tr]) {
12
+ for (const m of text.matchAll(/\{([a-z_]+)\}/g)) {
13
+ if (PLACEHOLDERS.includes(m[1]))
14
+ found.add(m[1]);
15
+ }
16
+ }
17
+ return [...found];
18
+ }
@@ -1,9 +1,10 @@
1
1
  // MCP resources: the method, checklists, content rules, raw prompts and the current project's research.md.
2
- // Each needs a plan like the tools do: the method texts need "start" (Audit Week or Pro), research.md "research",
2
+ // Each needs a plan like the tools do: the method texts need "start" (Katman Pro), research.md "research",
3
3
  // the prompt bodies "prompts" (Katman Pro). The resource list itself stays visible.
4
4
  import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { resolveRoot } from "../../../context.js";
6
- import { PROMPTS, getPrompt } from "../data/prompts.js";
6
+ import { PROMPTS } from "../data/prompts.js";
7
+ import { getPrompt } from "../lib/prompt-lookup.js";
7
8
  import { contentRulesMd, methodOverviewMd, prepublishChecklistMd, rawPromptMd, siteAuditChecklistMd } from "../data/render.js";
8
9
  import { loadConfigSafe, loadResearchSafe, loadRuns } from "../lib/state.js";
9
10
  import { renderResearchMd } from "../core/research.js";
@@ -45,7 +46,7 @@ export function registerMethodResources(server, ctx) {
45
46
  if (!p)
46
47
  return md(uri, `Unknown prompt "${raw}". Valid ids: ${PROMPTS.map((x) => x.id).join(", ")}.`);
47
48
  // The prompt book is Katman Pro content, like the katman_prompt tool: the titles stay listed, the prompt bodies
48
- // need Pro (the Audit Week doesn't include them).
49
+ // need Pro (an older Audit Week doesn't include them).
49
50
  const then = `Then read this resource again, or call katman_prompt {"id": "${p.id}"} to get it filled with this project's data.`;
50
51
  return gated(ctx, uri, "prompts", `${p.id} · ${p.title.en}`, then, () => rawPromptMd(p, "en"));
51
52
  });
@@ -1,6 +1,6 @@
1
1
  // Visibility module: measures whether AI assistants name, recommend and cite the brand.
2
2
  // Tools: katman_visibility_run (OpenRouter, user's key, dry run first) and katman_manual_check (no API cost: the user
3
- // pastes consumer-app answers), both Audit Week or Pro; katman_setup_monitoring (GitHub Actions, Pro).
3
+ // pastes consumer-app answers), both Katman Pro; katman_setup_monitoring (GitHub Actions, Pro).
4
4
  import { z } from "zod";
5
5
  import { resolveRoot, rootWarning } from "../../context.js";
6
6
  import { fail, ok } from "../../util.js";
@@ -88,7 +88,7 @@ export function registerVisibilityTools(server, ctx) {
88
88
  inputSchema: {
89
89
  project_root: projectRoot,
90
90
  schedule: z.string().optional().describe('Cron (UTC), default "0 6 * * 1" (Mondays 06:00). The book re-measures every 14 days: "0 6 1,15 * *".'),
91
- include_audit: z.boolean().optional().describe("Also run the live site audit each time (default true)."),
91
+ include_audit: z.boolean().optional().describe("Also run the live site audit each time (default true). Each audit counts as one of the plan's monthly audits (1 site: 5, 3 sites: 30)."),
92
92
  write: z.boolean().optional().describe("Write .github/workflows/katman.yml (default false: preview only)."),
93
93
  overwrite: z.boolean().optional().describe("Replace an existing workflow file."),
94
94
  max_cost_usd: z.number().positive().optional().describe("Per-run cap passed to katman-mcp check; default: the estimate's high end (at least 2)."),
@@ -155,6 +155,9 @@ export async function setupMonitoring(root, args) {
155
155
  "- The book re-measures every 14 days; for that cadence use schedule `0 6 1,15 * *`. Weekly runs show the trend sooner at twice the cost.",
156
156
  "- A run whose estimate is above the cap refuses to start (nothing spent). A run that hits 402 (no credit) saves its partial results.",
157
157
  "- .katman/cache/ is never committed; .katman/ never contains either key.",
158
+ ...(includeAudit
159
+ ? ["- Each run's live audit step counts as one audit of your Katman Pro plan for the month (1 site: 5 audits; 3 sites: 30). A weekly schedule uses 4–5 a month, so for the 1-site plan use `0 6 1,15 * *` or pass include_audit: false and audit by hand."]
160
+ : []),
158
161
  ...notes.map((n) => `- ${n}`),
159
162
  ];
160
163
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "katman-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Katman MCP: makes your vibe-coded site readable, quotable and recommendable by ChatGPT, Claude, Gemini and Perplexity. Scans your codebase, audits your live site like AI crawlers do, measures AI visibility with your own OpenRouter key.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,10 +17,12 @@
17
17
  "node": ">=20"
18
18
  },
19
19
  "scripts": {
20
- "build": "tsc -p tsconfig.json && node scripts/postbuild.mjs",
20
+ "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json && node scripts/postbuild.mjs",
21
21
  "dev": "tsx src/index.ts",
22
22
  "typecheck": "tsc --noEmit -p tsconfig.json",
23
- "test": "vitest run"
23
+ "test": "vitest run",
24
+ "smoke": "node scripts/smoke-dist.mjs",
25
+ "prepublishOnly": "npm run build && npm test && node scripts/smoke-dist.mjs"
24
26
  },
25
27
  "keywords": [
26
28
  "mcp",
package/server.json CHANGED
@@ -3,20 +3,20 @@
3
3
  "name": "pro.katman/mcp",
4
4
  "title": "Katman",
5
5
  "description": "Get your vibe-coded site read, quoted and recommended by ChatGPT, Claude, Gemini and Perplexity.",
6
- "version": "0.2.0",
6
+ "version": "0.3.0",
7
7
  "websiteUrl": "https://katman.pro/mcp",
8
8
  "packages": [
9
9
  {
10
10
  "registryType": "npm",
11
11
  "registryBaseUrl": "https://registry.npmjs.org",
12
12
  "identifier": "katman-mcp",
13
- "version": "0.2.0",
13
+ "version": "0.3.0",
14
14
  "runtimeHint": "npx",
15
15
  "transport": { "type": "stdio" },
16
16
  "environmentVariables": [
17
17
  {
18
18
  "name": "KATMAN_KEY",
19
- "description": "Required. Your Katman licence key (km_live_…) from katman.pro/app/mcp. Every tool except katman_account needs it with an active plan: Audit Week ($3 for 7 days: audit and recommendations) or Katman Pro ($29/month founding price: everything). Plans: katman.pro/pricing.",
19
+ "description": "Required. Your Katman licence key (km_live_…) from katman.pro/app/mcp. Every tool except katman_account needs it with an active Katman Pro plan: 1 site $49/month (1 site, 5 audits a month) or 3 sites $88/month (3 sites, 30 audits a month), both with every tool. An audit is one run of katman_audit_site, counted per calendar month. Plans: katman.pro/pricing.",
20
20
  "isRequired": true,
21
21
  "isSecret": true,
22
22
  "format": "string"