@automatelab/citation-intelligence 0.6.0 → 0.8.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 (75) hide show
  1. package/README.md +43 -8
  2. package/dist/adapters/anthropic.d.ts.map +1 -1
  3. package/dist/adapters/anthropic.js +4 -2
  4. package/dist/adapters/anthropic.js.map +1 -1
  5. package/dist/adapters/bing.js +1 -1
  6. package/dist/adapters/bing.js.map +1 -1
  7. package/dist/adapters/brave.js +1 -1
  8. package/dist/adapters/brave.js.map +1 -1
  9. package/dist/adapters/gemini.d.ts.map +1 -1
  10. package/dist/adapters/gemini.js +5 -1
  11. package/dist/adapters/gemini.js.map +1 -1
  12. package/dist/adapters/openai.d.ts.map +1 -1
  13. package/dist/adapters/openai.js +3 -1
  14. package/dist/adapters/openai.js.map +1 -1
  15. package/dist/adapters/perplexity.d.ts +2 -1
  16. package/dist/adapters/perplexity.d.ts.map +1 -1
  17. package/dist/adapters/perplexity.js +5 -2
  18. package/dist/adapters/perplexity.js.map +1 -1
  19. package/dist/index.js +201 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/output-schemas.d.ts +926 -0
  22. package/dist/output-schemas.d.ts.map +1 -0
  23. package/dist/output-schemas.js +512 -0
  24. package/dist/output-schemas.js.map +1 -0
  25. package/dist/tools/am-i-cited.d.ts +73 -10
  26. package/dist/tools/am-i-cited.d.ts.map +1 -1
  27. package/dist/tools/am-i-cited.js +115 -15
  28. package/dist/tools/am-i-cited.js.map +1 -1
  29. package/dist/tools/answer-box-position.d.ts +4 -4
  30. package/dist/tools/answer-box-position.js +2 -2
  31. package/dist/tools/answer-box-position.js.map +1 -1
  32. package/dist/tools/canonical-competitor-set.d.ts +4 -4
  33. package/dist/tools/canonical-competitor-set.js +6 -7
  34. package/dist/tools/canonical-competitor-set.js.map +1 -1
  35. package/dist/tools/check-citations.d.ts +11 -5
  36. package/dist/tools/check-citations.d.ts.map +1 -1
  37. package/dist/tools/check-citations.js +29 -10
  38. package/dist/tools/check-citations.js.map +1 -1
  39. package/dist/tools/citation-evidence.d.ts +4 -4
  40. package/dist/tools/citation-evidence.js +2 -2
  41. package/dist/tools/citation-evidence.js.map +1 -1
  42. package/dist/tools/citation-freshness-score.d.ts +4 -4
  43. package/dist/tools/citation-freshness-score.js +1 -1
  44. package/dist/tools/citation-freshness-score.js.map +1 -1
  45. package/dist/tools/citation-provenance.d.ts +6 -4
  46. package/dist/tools/citation-provenance.d.ts.map +1 -1
  47. package/dist/tools/citation-provenance.js +17 -8
  48. package/dist/tools/citation-provenance.js.map +1 -1
  49. package/dist/tools/cited-for-diff.d.ts +5 -5
  50. package/dist/tools/cited-for-diff.js +1 -1
  51. package/dist/tools/cited-for-diff.js.map +1 -1
  52. package/dist/tools/cited-for.d.ts +5 -5
  53. package/dist/tools/cited-for.js +1 -1
  54. package/dist/tools/cited-for.js.map +1 -1
  55. package/dist/tools/compete-for-query.d.ts +4 -4
  56. package/dist/tools/compete-for-query.js +1 -1
  57. package/dist/tools/compete-for-query.js.map +1 -1
  58. package/dist/tools/gsc-citation-gap.d.ts +5 -5
  59. package/dist/tools/gsc-citation-gap.d.ts.map +1 -1
  60. package/dist/tools/gsc-citation-gap.js +7 -2
  61. package/dist/tools/gsc-citation-gap.js.map +1 -1
  62. package/dist/tools/run-panel.d.ts +5 -5
  63. package/dist/tools/run-panel.d.ts.map +1 -1
  64. package/dist/tools/run-panel.js +13 -4
  65. package/dist/tools/run-panel.js.map +1 -1
  66. package/dist/tools/structured-data-repair.d.ts +30 -0
  67. package/dist/tools/structured-data-repair.d.ts.map +1 -0
  68. package/dist/tools/structured-data-repair.js +297 -0
  69. package/dist/tools/structured-data-repair.js.map +1 -0
  70. package/dist/types.d.ts +10 -1
  71. package/dist/types.d.ts.map +1 -1
  72. package/dist/types.js +24 -1
  73. package/dist/types.js.map +1 -1
  74. package/package.json +61 -58
  75. package/smithery.yaml +50 -0
package/README.md CHANGED
@@ -36,10 +36,13 @@ The AI citation tracking market is dominated by VC-funded dashboards starting at
36
36
 
37
37
  ## Tools
38
38
 
39
+ **Start with `citation_provenance` or `am_i_cited`.** Single-engine results (`check_citations` with a pinned engine) are directional; multi-engine consensus is the honest signal. A URL cited by 4 of 5 engines is a very different finding than one cited by 1.
40
+
39
41
  | Tool | Purpose |
40
42
  |---|---|
41
- | `check_citations` | URLs cited by Perplexity / Claude / ChatGPT / Gemini / Bing / Brave / Google AI Mode for a query |
42
- | `am_i_cited` | Presence + rank for a domain across a query cluster |
43
+ | `citation_provenance` | **Recommended first tool.** Fan a query across engines; per-URL cross-engine consensus matrix. Returns `interpretation_note` per engine. |
44
+ | `am_i_cited` | Domain citation check. With `engine=auto` (default): fans across all available LLM engines, returns per-engine breakdown + cross-engine consensus. Pin `engine=` to reduce cost. |
45
+ | `check_citations` | URLs cited by Perplexity / Claude / ChatGPT / Gemini / Google AI Mode for a query; or web rank via bing_serp / brave_serp |
43
46
  | `ai_overview` | Google AI Overview presence + cited sources |
44
47
  | `cited_for` | Queries the domain has been cited for, from local cache |
45
48
  | `predict_citation` | Citation likelihood from public signals - no LLM fired |
@@ -82,6 +85,37 @@ Cache views the client can read or subscribe to (no tool call required):
82
85
  - `citation://docs/ai-crawlers` - AI crawlers cheatsheet (markdown)
83
86
  - `citation://domain/{domain}/cited-for` - dynamic template: citations for `{domain}`
84
87
 
88
+ ## What this actually measures
89
+
90
+ Every response includes a `surface` field that tells you exactly how the data was collected. Understanding this is important before drawing conclusions.
91
+
92
+ | Surface | Engines | What it means |
93
+ |---|---|---|
94
+ | `consumer_scrape` | `perplexity`, `google_ai_mode` | Proxied through a real consumer-facing AI search product. Closest to what your users see. |
95
+ | `api_proxy` | `claude`, `openai`, `gemini` | API call to a search-enabled LLM. **May differ from consumer product behavior** — different model versions, no UI-level ranking logic, no personalization. Use as a directional proxy, not as ground truth. |
96
+ | `web_rank` | `bing_serp`, `brave_serp` | Traditional web search rank (not LLM citation). Measures whether a URL appears in SERP results, not whether an LLM cites it. |
97
+ | `static_signal` | `predict_citation`, `wikipedia_mentions` | Offline signal computed from public data. No live LLM query. |
98
+
99
+ ### Per-engine notes
100
+
101
+ **`perplexity` (consumer_scrape)** — Sonar Pro via the Perplexity API with a consumer-equivalent system prompt. Reasonably close to Perplexity.ai. Citations come from `search_results` in the response; the `citations` fallback contains URL-only entries without title.
102
+
103
+ **`claude` (api_proxy)** — Claude Sonnet via the Anthropic Messages API with `web_search` tool enabled. The consumer Claude.ai product uses different routing and ranking logic. Citation behavior can differ, especially for recent/time-sensitive queries.
104
+
105
+ **`openai` (api_proxy)** — `gpt-4o-search-preview` via the OpenAI Responses API. This is the model OpenAI ships to mirror SearchGPT behavior — closer to consumer than `gpt-4o-mini`, but still API-tier.
106
+
107
+ **`gemini` (api_proxy)** — Gemini 2.5 Pro via the Generative Language API with `google_search` grounding. Consumer Gemini uses the same grounding index but different re-ranking. Results are directional.
108
+
109
+ **`google_ai_mode` (consumer_scrape)** — Google AI Mode results via SerpAPI. Closest to what users see in Google Search. Requires `SERPAPI_KEY`.
110
+
111
+ **`bing_serp` / `brave_serp` (web_rank)** — Traditional SERP rank. Does NOT measure LLM citations. Use `check_citations` with these engines to compare organic web rank against LLM citation rank. `am_i_cited` refuses these engines — it only measures LLM behavior.
112
+
113
+ The proxy nature of `api_proxy` engines is a feature, not a bug: it lets you run citation checks without consuming expensive consumer-product quota. Just don't report API-proxy numbers as "ChatGPT cites you" without the caveat.
114
+
115
+ Every tool response includes an `interpretation_note` field that summarizes the fidelity in one sentence. Full per-engine fidelity ratings: [docs/surface-fidelity.md](docs/surface-fidelity.md).
116
+
117
+ ---
118
+
85
119
  ## Quick start
86
120
 
87
121
  ```bash
@@ -133,12 +167,13 @@ Set only the keys you have. Any MCP client that supports stdio transport works -
133
167
 
134
168
  | Var | Purpose | Free tier? |
135
169
  |---|---|---|
136
- | `PERPLEXITY_API_KEY` | check_citations (Perplexity) | Yes |
137
- | `SERPAPI_KEY` | ai_overview | 100/month free |
138
- | `BING_API_KEY` | check_citations (Bing) | Yes |
139
- | `ANTHROPIC_API_KEY` | check_citations (Claude) | Paid only |
140
- | `OPENAI_API_KEY` | check_citations (ChatGPT) | Paid only |
141
- | `GEMINI_API_KEY` | check_citations (Gemini) | Yes |
170
+ | `PERPLEXITY_API_KEY` | check_citations (perplexity — consumer_scrape) | Yes |
171
+ | `SERPAPI_KEY` | ai_overview + check_citations (google_ai_mode — consumer_scrape) | 100/month free |
172
+ | `ANTHROPIC_API_KEY` | check_citations (claude — api_proxy) | Paid only |
173
+ | `OPENAI_API_KEY` | check_citations (openai — api_proxy) | Paid only |
174
+ | `GEMINI_API_KEY` | check_citations (gemini — api_proxy) | Yes |
175
+ | `BING_API_KEY` | check_citations (bing_serp — web_rank) | Yes |
176
+ | `BRAVE_API_KEY` | check_citations (brave_serp — web_rank) | Yes (2000/month) |
142
177
  | `CITATION_CACHE_TTL_DAYS` | Cache TTL for citation_check entries (default 7) | n/a |
143
178
  | `CITATION_AI_OVERVIEW_TTL_DAYS` | Cache TTL for ai_overview entries (default 1) | n/a |
144
179
  | `CITATION_CONFIG_DIR` | Override config dir (default `~/.config/citation-intelligence`) | n/a |
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/adapters/anthropic.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,aAAa,CAAC;AAa3D,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CAsDxB"}
1
+ {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/adapters/anthropic.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,aAAa,CAAC;AAa3D,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CAwDxB"}
@@ -10,10 +10,12 @@ export async function claudeSearch(query, maxResults) {
10
10
  message: "Set ANTHROPIC_API_KEY to use the Claude engine. Get a key at https://console.anthropic.com.",
11
11
  });
12
12
  }
13
+ // System prompt approximates Claude.ai consumer behavior: search-first with citation list.
13
14
  const body = JSON.stringify({
14
- model: "claude-sonnet-4-6",
15
+ model: "claude-sonnet-4-7",
15
16
  max_tokens: 1024,
16
- tools: [{ type: "web_search_20250305", name: "web_search", max_uses: 3 }],
17
+ system: "You are a search assistant. Answer with inline citations. List each source URL you used.",
18
+ tools: [{ type: "web_search_20250305", name: "web_search", max_uses: 5 }],
17
19
  messages: [{ role: "user", content: query }],
18
20
  });
19
21
  const res = await fetchJson("https://api.anthropic.com/v1/messages", {
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/adapters/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAc5D,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAa,EACb,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACxC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,cAAc,CAAC;YACvB,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,mBAAmB;YAC5B,OAAO,EACL,6FAA6F;SAChG,CAAC,CAAC;IACL,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,KAAK,EAAE,mBAAmB;QAC1B,UAAU,EAAE,IAAI;QAChB,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;QACzE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;KAC7C,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,MAAM,SAAS,CACzB,uCAAuC,EACvC;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,WAAW,EAAE,GAAG;YAChB,mBAAmB,EAAE,YAAY;YACjC,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI;KACL,CACF,CAAC;IAEF,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,wBAAwB,EAAE,CAAC;YACnD,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;gBACpC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;oBAAE,SAAS;gBACxC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;gBAChB,SAAS,CAAC,IAAI,CAAC;oBACb,GAAG,EAAE,CAAC,CAAC,GAAG;oBACV,KAAK,EAAE,CAAC,CAAC,KAAK;oBACd,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC;iBAC3B,CAAC,CAAC;gBACH,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU;oBAAE,MAAM;YAC5C,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;AACtE,CAAC"}
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/adapters/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAc5D,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAa,EACb,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACxC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,cAAc,CAAC;YACvB,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,mBAAmB;YAC5B,OAAO,EACL,6FAA6F;SAChG,CAAC,CAAC;IACL,CAAC;IAED,2FAA2F;IAC3F,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,KAAK,EAAE,mBAAmB;QAC1B,UAAU,EAAE,IAAI;QAChB,MAAM,EAAE,0FAA0F;QAClG,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;QACzE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;KAC7C,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,MAAM,SAAS,CACzB,uCAAuC,EACvC;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,WAAW,EAAE,GAAG;YAChB,mBAAmB,EAAE,YAAY;YACjC,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI;KACL,CACF,CAAC;IAEF,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,wBAAwB,EAAE,CAAC;YACnD,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;gBACpC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;oBAAE,SAAS;gBACxC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;gBAChB,SAAS,CAAC,IAAI,CAAC;oBACb,GAAG,EAAE,CAAC,CAAC,GAAG;oBACV,KAAK,EAAE,CAAC,CAAC,KAAK;oBACd,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC;iBAC3B,CAAC,CAAC;gBACH,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU;oBAAE,MAAM;YAC5C,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;AACtE,CAAC"}
@@ -5,7 +5,7 @@ export async function bingSearch(query, maxResults) {
5
5
  if (!key) {
6
6
  throw new ToolFetchError({
7
7
  type: "missing_key",
8
- engine: "bing",
8
+ engine: "bing_serp",
9
9
  env_var: "BING_API_KEY",
10
10
  message: "Set BING_API_KEY to use the Bing engine. Free tier at https://www.microsoft.com/en-us/bing/apis/bing-web-search-api.",
11
11
  });
@@ -1 +1 @@
1
- {"version":3,"file":"bing.js","sourceRoot":"","sources":["../../src/adapters/bing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAS5D,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,KAAa,EACb,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;IACnC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,cAAc,CAAC;YACvB,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,cAAc;YACvB,OAAO,EACL,sHAAsH;SACzH,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC5E,MAAM,GAAG,GAAG,MAAM,SAAS,CACzB,8CAA8C,MAAM,CAAC,QAAQ,EAAE,EAAE,EACjE,EAAE,OAAO,EAAE,EAAE,2BAA2B,EAAE,GAAG,EAAE,EAAE,CAClD,CAAC;IAEF,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC;QAC1C,IAAI,CAAC,CAAC,CAAC,GAAG;YAAE,SAAS;QACrB,SAAS,CAAC,IAAI,CAAC;YACb,GAAG,EAAE,CAAC,CAAC,GAAG;YACV,KAAK,EAAE,CAAC,CAAC,IAAI;YACb,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,IAAI,EAAE,IAAI,EAAE;SACb,CAAC,CAAC;QACH,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU;YAAE,MAAM;IAC5C,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"bing.js","sourceRoot":"","sources":["../../src/adapters/bing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAS5D,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,KAAa,EACb,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;IACnC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,cAAc,CAAC;YACvB,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,WAAW;YACnB,OAAO,EAAE,cAAc;YACvB,OAAO,EACL,sHAAsH;SACzH,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC5E,MAAM,GAAG,GAAG,MAAM,SAAS,CACzB,8CAA8C,MAAM,CAAC,QAAQ,EAAE,EAAE,EACjE,EAAE,OAAO,EAAE,EAAE,2BAA2B,EAAE,GAAG,EAAE,EAAE,CAClD,CAAC;IAEF,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC;QAC1C,IAAI,CAAC,CAAC,CAAC,GAAG;YAAE,SAAS;QACrB,SAAS,CAAC,IAAI,CAAC;YACb,GAAG,EAAE,CAAC,CAAC,GAAG;YACV,KAAK,EAAE,CAAC,CAAC,IAAI;YACb,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,IAAI,EAAE,IAAI,EAAE;SACb,CAAC,CAAC;QACH,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU;YAAE,MAAM;IAC5C,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,CAAC;AACvB,CAAC"}
@@ -5,7 +5,7 @@ export async function braveSearch(query, maxResults) {
5
5
  if (!key) {
6
6
  throw new ToolFetchError({
7
7
  type: "missing_key",
8
- engine: "brave",
8
+ engine: "brave_serp",
9
9
  env_var: "BRAVE_API_KEY",
10
10
  message: "Set BRAVE_API_KEY to use the Brave engine. Free tier at https://api.search.brave.com (2000 queries/month).",
11
11
  });
@@ -1 +1 @@
1
- {"version":3,"file":"brave.js","sourceRoot":"","sources":["../../src/adapters/brave.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAa5D,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAa,EACb,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;IACpC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,cAAc,CAAC;YACvB,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,OAAO;YACf,OAAO,EAAE,eAAe;YACxB,OAAO,EACL,4GAA4G;SAC/G,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1F,MAAM,GAAG,GAAG,MAAM,SAAS,CACzB,kDAAkD,MAAM,CAAC,QAAQ,EAAE,EAAE,EACrE;QACE,OAAO,EAAE;YACP,MAAM,EAAE,kBAAkB;YAC1B,sBAAsB,EAAE,GAAG;SAC5B;KACF,CACF,CAAC;IAEF,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;QACvC,IAAI,CAAC,CAAC,CAAC,GAAG;YAAE,SAAS;QACrB,SAAS,CAAC,IAAI,CAAC;YACb,GAAG,EAAE,CAAC,CAAC,GAAG;YACV,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,OAAO,EAAE,CAAC,CAAC,WAAW;YACtB,IAAI,EAAE,IAAI,EAAE;SACb,CAAC,CAAC;QACH,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU;YAAE,MAAM;IAC5C,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"brave.js","sourceRoot":"","sources":["../../src/adapters/brave.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAa5D,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAa,EACb,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;IACpC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,cAAc,CAAC;YACvB,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,YAAY;YACpB,OAAO,EAAE,eAAe;YACxB,OAAO,EACL,4GAA4G;SAC/G,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1F,MAAM,GAAG,GAAG,MAAM,SAAS,CACzB,kDAAkD,MAAM,CAAC,QAAQ,EAAE,EAAE,EACrE;QACE,OAAO,EAAE;YACP,MAAM,EAAE,kBAAkB;YAC1B,sBAAsB,EAAE,GAAG;SAC5B;KACF,CACF,CAAC;IAEF,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;QACvC,IAAI,CAAC,CAAC,CAAC,GAAG;YAAE,SAAS;QACrB,SAAS,CAAC,IAAI,CAAC;YACb,GAAG,EAAE,CAAC,CAAC,GAAG;YACV,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,OAAO,EAAE,CAAC,CAAC,WAAW;YACtB,IAAI,EAAE,IAAI,EAAE;SACb,CAAC,CAAC;QACH,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU;YAAE,MAAM;IAC5C,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,CAAC;AACvB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/adapters/gemini.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,aAAa,CAAC;AAW3D,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CA+CxB"}
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/adapters/gemini.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,aAAa,CAAC;AAW3D,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CAmDxB"}
@@ -10,11 +10,15 @@ export async function geminiSearch(query, maxResults) {
10
10
  message: "Set GEMINI_API_KEY to use the Gemini engine. Get a key at https://aistudio.google.com/apikey.",
11
11
  });
12
12
  }
13
+ // System prompt approximates Gemini consumer app behavior: search-first with source attribution.
13
14
  const body = JSON.stringify({
15
+ system_instruction: {
16
+ parts: [{ text: "You are a search assistant. Answer with inline citations. List each source URL you used." }],
17
+ },
14
18
  contents: [{ parts: [{ text: query }] }],
15
19
  tools: [{ google_search: {} }],
16
20
  });
17
- const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${encodeURIComponent(key)}`;
21
+ const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent?key=${encodeURIComponent(key)}`;
18
22
  const res = await fetchJson(url, {
19
23
  method: "POST",
20
24
  headers: { "content-type": "application/json" },
@@ -1 +1 @@
1
- {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../src/adapters/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAY5D,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAa,EACb,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACrC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,cAAc,CAAC;YACvB,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,gBAAgB;YACzB,OAAO,EACL,+FAA+F;SAClG,CAAC,CAAC;IACL,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QACxC,KAAK,EAAE,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC;KAC/B,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,gGAAgG,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;IAEtI,MAAM,GAAG,GAAG,MAAM,SAAS,CAAiB,GAAG,EAAE;QAC/C,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI;KACL,CAAC,CAAC;IAEH,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC;YAC1C,IAAI,CAAC,CAAC,IAAI;gBAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,iBAAiB,EAAE,eAAe,IAAI,EAAE,EAAE,CAAC;YAClE,MAAM,CAAC,GAAG,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC;YACzB,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YAChC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACZ,SAAS,CAAC,IAAI,CAAC;gBACb,GAAG,EAAE,CAAC;gBACN,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK;gBACvB,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC;aAC3B,CAAC,CAAC;YACH,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU;gBAAE,MAAM;QAC5C,CAAC;IACH,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;AACtE,CAAC"}
1
+ {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../src/adapters/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAY5D,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAa,EACb,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACrC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,cAAc,CAAC;YACvB,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,gBAAgB;YACzB,OAAO,EACL,+FAA+F;SAClG,CAAC,CAAC;IACL,CAAC;IAED,iGAAiG;IACjG,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,kBAAkB,EAAE;YAClB,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,0FAA0F,EAAE,CAAC;SAC9G;QACD,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QACxC,KAAK,EAAE,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC;KAC/B,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,8FAA8F,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;IAEpI,MAAM,GAAG,GAAG,MAAM,SAAS,CAAiB,GAAG,EAAE;QAC/C,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI;KACL,CAAC,CAAC;IAEH,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC;YAC1C,IAAI,CAAC,CAAC,IAAI;gBAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,iBAAiB,EAAE,eAAe,IAAI,EAAE,EAAE,CAAC;YAClE,MAAM,CAAC,GAAG,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC;YACzB,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YAChC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACZ,SAAS,CAAC,IAAI,CAAC;gBACb,GAAG,EAAE,CAAC;gBACN,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK;gBACvB,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC;aAC3B,CAAC,CAAC;YACH,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU;gBAAE,MAAM;QAC5C,CAAC;IACH,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;AACtE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/adapters/openai.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,aAAa,CAAC;AAmB3D,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CAqDxB"}
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/adapters/openai.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,aAAa,CAAC;AAmB3D,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CAuDxB"}
@@ -1,7 +1,7 @@
1
1
  import { envKey } from "../lib/config.js";
2
2
  import { fetchJson, ToolFetchError } from "../lib/fetch.js";
3
3
  import { log } from "../lib/log.js";
4
- const OPENAI_MODEL = "gpt-4o-mini";
4
+ const OPENAI_MODEL = "gpt-4o-search-preview";
5
5
  export async function openaiSearch(query, maxResults) {
6
6
  const key = envKey("OPENAI_API_KEY");
7
7
  if (!key) {
@@ -13,9 +13,11 @@ export async function openaiSearch(query, maxResults) {
13
13
  });
14
14
  }
15
15
  log.debug("openai web_search", { model: OPENAI_MODEL });
16
+ // System prompt approximates ChatGPT consumer behavior: answer with inline citations.
16
17
  const body = JSON.stringify({
17
18
  model: OPENAI_MODEL,
18
19
  tools: [{ type: "web_search_preview" }],
20
+ system: "You are a search assistant. Answer with inline citations. List each source URL you used.",
19
21
  input: query,
20
22
  });
21
23
  const res = await fetchJson("https://api.openai.com/v1/responses", {
@@ -1 +1 @@
1
- {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/adapters/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAGpC,MAAM,YAAY,GAAG,aAAa,CAAC;AAiBnC,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAa,EACb,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACrC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,cAAc,CAAC;YACvB,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,gBAAgB;YACzB,OAAO,EACL,yFAAyF;SAC5F,CAAC,CAAC;IACL,CAAC;IAED,GAAG,CAAC,KAAK,CAAC,mBAAmB,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC;QACvC,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,MAAM,SAAS,CACzB,qCAAqC,EACrC;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,GAAG,EAAE;YAC9B,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI;KACL,CACF,CAAC;IAEF,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QACnC,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;YAClC,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa,IAAI,CAAC,CAAC,IAAI;gBAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/D,KAAK,MAAM,GAAG,IAAI,CAAC,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC;gBACtC,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC;oBAC9D,SAAS;gBACX,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAClB,SAAS,CAAC,IAAI,CAAC;oBACb,GAAG,EAAE,GAAG,CAAC,GAAG;oBACZ,KAAK,EAAE,GAAG,CAAC,KAAK;oBAChB,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC;iBAC3B,CAAC,CAAC;gBACH,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU;oBAAE,MAAM;YAC5C,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;AACtE,CAAC"}
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/adapters/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAGpC,MAAM,YAAY,GAAG,uBAAuB,CAAC;AAiB7C,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAa,EACb,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACrC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,cAAc,CAAC;YACvB,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,gBAAgB;YACzB,OAAO,EACL,yFAAyF;SAC5F,CAAC,CAAC;IACL,CAAC;IAED,GAAG,CAAC,KAAK,CAAC,mBAAmB,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;IACxD,sFAAsF;IACtF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC;QACvC,MAAM,EAAE,0FAA0F;QAClG,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,MAAM,SAAS,CACzB,qCAAqC,EACrC;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,GAAG,EAAE;YAC9B,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI;KACL,CACF,CAAC;IAEF,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QACnC,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;YAClC,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa,IAAI,CAAC,CAAC,IAAI;gBAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/D,KAAK,MAAM,GAAG,IAAI,CAAC,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC;gBACtC,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC;oBAC9D,SAAS;gBACX,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAClB,SAAS,CAAC,IAAI,CAAC;oBACb,GAAG,EAAE,GAAG,CAAC,GAAG;oBACZ,KAAK,EAAE,GAAG,CAAC,KAAK;oBAChB,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC;iBAC3B,CAAC,CAAC;gBACH,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU;oBAAE,MAAM;YAC5C,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;AACtE,CAAC"}
@@ -1,3 +1,4 @@
1
1
  import type { AdapterResult } from "../types.js";
2
- export declare function perplexitySearch(query: string, maxResults: number): Promise<AdapterResult>;
2
+ export declare const PERPLEXITY_MODEL_DEFAULT = "sonar-pro";
3
+ export declare function perplexitySearch(query: string, maxResults: number, model?: string): Promise<AdapterResult>;
3
4
  //# sourceMappingURL=perplexity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"perplexity.d.ts","sourceRoot":"","sources":["../../src/adapters/perplexity.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,aAAa,CAAC;AAU3D,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CAwDxB"}
1
+ {"version":3,"file":"perplexity.d.ts","sourceRoot":"","sources":["../../src/adapters/perplexity.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,aAAa,CAAC;AAU3D,eAAO,MAAM,wBAAwB,cAAc,CAAC;AAEpD,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,KAAK,GAAE,MAAiC,GACvC,OAAO,CAAC,aAAa,CAAC,CA0DxB"}
@@ -1,6 +1,7 @@
1
1
  import { envKey } from "../lib/config.js";
2
2
  import { fetchJson, ToolFetchError } from "../lib/fetch.js";
3
- export async function perplexitySearch(query, maxResults) {
3
+ export const PERPLEXITY_MODEL_DEFAULT = "sonar-pro";
4
+ export async function perplexitySearch(query, maxResults, model = PERPLEXITY_MODEL_DEFAULT) {
4
5
  const key = envKey("PERPLEXITY_API_KEY");
5
6
  if (!key) {
6
7
  throw new ToolFetchError({
@@ -10,8 +11,10 @@ export async function perplexitySearch(query, maxResults) {
10
11
  message: "Set PERPLEXITY_API_KEY to use the Perplexity engine. Sign up at https://www.perplexity.ai/settings/api",
11
12
  });
12
13
  }
14
+ // System prompt approximates Perplexity.ai consumer behavior: search-first with citations.
13
15
  const body = JSON.stringify({
14
- model: "sonar",
16
+ model,
17
+ system: "You are a search assistant. Answer with inline citations. List each source URL you used.",
15
18
  messages: [{ role: "user", content: query }],
16
19
  return_citations: true,
17
20
  });
@@ -1 +1 @@
1
- {"version":3,"file":"perplexity.js","sourceRoot":"","sources":["../../src/adapters/perplexity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAW5D,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAAa,EACb,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACzC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,cAAc,CAAC;YACvB,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,YAAY;YACpB,OAAO,EAAE,oBAAoB;YAC7B,OAAO,EACL,wGAAwG;SAC3G,CAAC,CAAC;IACL,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,KAAK,EAAE,OAAO;QACd,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC5C,gBAAgB,EAAE,IAAI;KACvB,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,MAAM,SAAS,CACzB,4CAA4C,EAC5C;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,GAAG,EAAE;YAC9B,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI;KACL,CACF,CAAC;IAEF,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;YACnC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;gBAAE,SAAS;YACxC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAChB,SAAS,CAAC,IAAI,CAAC;gBACb,GAAG,EAAE,CAAC,CAAC,GAAG;gBACV,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC;aAC3B,CAAC,CAAC;YACH,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU;gBAAE,MAAM;QAC5C,CAAC;IACH,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,GAAG,UAAU,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QACnD,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YACpC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,SAAS,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC;YACpD,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU;gBAAE,MAAM;QAC5C,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC;IACtD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;AACnC,CAAC"}
1
+ {"version":3,"file":"perplexity.js","sourceRoot":"","sources":["../../src/adapters/perplexity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAW5D,MAAM,CAAC,MAAM,wBAAwB,GAAG,WAAW,CAAC;AAEpD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAAa,EACb,UAAkB,EAClB,QAAgB,wBAAwB;IAExC,MAAM,GAAG,GAAG,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACzC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,cAAc,CAAC;YACvB,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,YAAY;YACpB,OAAO,EAAE,oBAAoB;YAC7B,OAAO,EACL,wGAAwG;SAC3G,CAAC,CAAC;IACL,CAAC;IAED,2FAA2F;IAC3F,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,KAAK;QACL,MAAM,EAAE,0FAA0F;QAClG,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC5C,gBAAgB,EAAE,IAAI;KACvB,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,MAAM,SAAS,CACzB,4CAA4C,EAC5C;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,GAAG,EAAE;YAC9B,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI;KACL,CACF,CAAC;IAEF,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;YACnC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;gBAAE,SAAS;YACxC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAChB,SAAS,CAAC,IAAI,CAAC;gBACb,GAAG,EAAE,CAAC,CAAC,GAAG;gBACV,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC;aAC3B,CAAC,CAAC;YACH,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU;gBAAE,MAAM;QAC5C,CAAC;IACH,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,GAAG,UAAU,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QACnD,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YACpC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,SAAS,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC;YACpD,IAAI,SAAS,CAAC,MAAM,IAAI,UAAU;gBAAE,MAAM;QAC5C,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC;IACtD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;AACnC,CAAC"}
package/dist/index.js CHANGED
@@ -26,13 +26,15 @@ import { citationEvidence, citationEvidenceInputSchema } from "./tools/citation-
26
26
  import { crawlerAccessAudit, crawlerAccessAuditInputSchema } from "./tools/crawler-access-audit.js";
27
27
  import { sitemapCitationMap, sitemapCitationMapInputSchema } from "./tools/sitemap-citation-map.js";
28
28
  import { canonicalCompetitorSet, canonicalCompetitorSetInputSchema } from "./tools/canonical-competitor-set.js";
29
+ import { structuredDataRepair, structuredDataRepairInputSchema } from "./tools/structured-data-repair.js";
29
30
  import { registerPrompts } from "./prompts.js";
30
31
  import { registerResources } from "./resources.js";
31
32
  import { ToolFetchError } from "./lib/fetch.js";
32
33
  import { log } from "./lib/log.js";
34
+ import { checkCitationsOutputShape, amICitedOutputShape, aiOverviewOutputShape, citedForOutputShape, predictCitationOutputShape, trackQueriesOutputShape, runPanelOutputShape, citationTrendOutputShape, compareDomainsOutputShape, wikipediaMentionsOutputShape, auditSitemapOutputShape, competeForQueryOutputShape, citationFreshnessScoreOutputShape, citedForDiffOutputShape, gscCitationGapOutputShape, schemaAuditOutputShape, llmsTxtGeneratorOutputShape, answerBoxPositionOutputShape, citationProvenanceOutputShape, citationEvidenceOutputShape, crawlerAccessAuditOutputShape, sitemapCitationMapOutputShape, canonicalCompetitorSetOutputShape, structuredDataRepairOutputShape, } from "./output-schemas.js";
33
35
  const server = new McpServer({
34
36
  name: "@automatelab/citation-intelligence",
35
- version: "0.6.0",
37
+ version: "0.7.0",
36
38
  });
37
39
  function toolError(err) {
38
40
  return {
@@ -59,101 +61,297 @@ function wrapHandler(handler) {
59
61
  server.registerTool("check_citations", {
60
62
  description: "Return URLs cited by an AI engine (Perplexity, Claude, ChatGPT, Gemini, or Bing) for a query. Use this when an agent or user wants to see what sources an AI search engine grounds answers on. Requires at least one engine API key; auto-picks the first available.",
61
63
  inputSchema: checkCitationsInputSchema,
64
+ outputSchema: checkCitationsOutputShape,
65
+ annotations: {
66
+ title: "Check AI engine citations",
67
+ readOnlyHint: false,
68
+ destructiveHint: false,
69
+ idempotentHint: true,
70
+ openWorldHint: true,
71
+ },
62
72
  }, (args) => wrapHandler(() => checkCitations(args)));
63
73
  server.registerTool("am_i_cited", {
64
74
  description: "Check whether a domain is cited by an AI engine across a cluster of queries. Returns per-query presence, rank, and a citation-rate summary. Use to measure visibility for a brand, product, or content site in AI search.",
65
75
  inputSchema: amICitedInputSchema,
76
+ outputSchema: amICitedOutputShape,
77
+ annotations: {
78
+ title: "Check domain citation presence",
79
+ readOnlyHint: false,
80
+ destructiveHint: false,
81
+ idempotentHint: true,
82
+ openWorldHint: true,
83
+ },
66
84
  }, (args) => wrapHandler(() => amICited(args)));
67
85
  server.registerTool("ai_overview", {
68
86
  description: "Check whether Google shows an AI Overview for a query, and which URLs it cites. Uses SerpAPI (free tier: 100/month). Set SERPAPI_KEY.",
69
87
  inputSchema: aiOverviewInputSchema,
88
+ outputSchema: aiOverviewOutputShape,
89
+ annotations: {
90
+ title: "Check Google AI Overview presence",
91
+ readOnlyHint: false,
92
+ destructiveHint: false,
93
+ idempotentHint: true,
94
+ openWorldHint: true,
95
+ },
70
96
  }, (args) => wrapHandler(() => aiOverview(args)));
71
97
  server.registerTool("cited_for", {
72
98
  description: "List queries that the given domain has been cited for, served from the local cache. Build up a corpus by calling check_citations or am_i_cited first; cited_for queries it without spending API budget.",
73
99
  inputSchema: citedForInputSchema,
100
+ outputSchema: citedForOutputShape,
101
+ annotations: {
102
+ title: "List cached queries domain was cited for",
103
+ readOnlyHint: true,
104
+ destructiveHint: false,
105
+ idempotentHint: true,
106
+ openWorldHint: false,
107
+ },
74
108
  }, (args) => wrapHandler(() => citedFor(args)));
75
109
  server.registerTool("predict_citation", {
76
110
  description: "Score citation likelihood for a URL from public signals (Wikipedia link presence, schema.org markup, /llms.txt, GitHub and Reddit references, canonical hygiene, HTTPS). No LLM fired - all heuristic. Returns 0-100 score, grade, signal breakdown, and ranked fixes.",
77
111
  inputSchema: predictCitationInputSchema,
112
+ outputSchema: predictCitationOutputShape,
113
+ annotations: {
114
+ title: "Predict citation likelihood for a URL",
115
+ readOnlyHint: true,
116
+ destructiveHint: false,
117
+ idempotentHint: true,
118
+ openWorldHint: true,
119
+ },
78
120
  }, (args) => wrapHandler(() => predictCitation(args)));
79
121
  server.registerTool("track_queries", {
80
122
  description: "Save, load, or list named query panels. A panel is a persisted set of queries you want to monitor over time (e.g. editorial-watchlist). Use action=save with queries[] to create, action=load to read, action=list to enumerate. Panels live under <config>/panels/<name>.json.",
81
123
  inputSchema: trackQueriesInputSchema,
124
+ outputSchema: trackQueriesOutputShape,
125
+ annotations: {
126
+ title: "Save or load a query panel",
127
+ readOnlyHint: false,
128
+ destructiveHint: false,
129
+ idempotentHint: true,
130
+ openWorldHint: false,
131
+ },
82
132
  }, (args) => wrapHandler(() => trackQueries(args)));
83
133
  server.registerTool("run_panel", {
84
134
  description: "Run a saved panel through am_i_cited and append a timestamped snapshot. Snapshots live under <config>/snapshots/<panel>/<iso>.json. Feeds citation_trend.",
85
135
  inputSchema: runPanelInputSchema,
136
+ outputSchema: runPanelOutputShape,
137
+ annotations: {
138
+ title: "Run panel and save snapshot",
139
+ readOnlyHint: false,
140
+ destructiveHint: false,
141
+ idempotentHint: true,
142
+ openWorldHint: true,
143
+ },
86
144
  }, (args) => wrapHandler(() => runPanel(args)));
87
145
  server.registerTool("citation_trend", {
88
146
  description: "Report citation rate over time for a panel from stored snapshots. Returns the series of citation_rate per snapshot plus per-query deltas (gained/lost/unchanged) between first and last snapshot.",
89
147
  inputSchema: citationTrendInputSchema,
148
+ outputSchema: citationTrendOutputShape,
149
+ annotations: {
150
+ title: "Report citation trend over time",
151
+ readOnlyHint: true,
152
+ destructiveHint: false,
153
+ idempotentHint: true,
154
+ openWorldHint: false,
155
+ },
90
156
  }, (args) => wrapHandler(() => citationTrend(args)));
91
157
  server.registerTool("compare_domains", {
92
158
  description: "Run predict_citation on 2-10 URLs and return a side-by-side signal table plus a list of signals where the URLs diverge. Use to compare your URL to top-cited competitors for the same query.",
93
159
  inputSchema: compareDomainsInputSchema,
160
+ outputSchema: compareDomainsOutputShape,
161
+ annotations: {
162
+ title: "Compare citation signals across URLs",
163
+ readOnlyHint: true,
164
+ destructiveHint: false,
165
+ idempotentHint: true,
166
+ openWorldHint: true,
167
+ },
94
168
  }, (args) => wrapHandler(() => compareDomains(args)));
95
169
  server.registerTool("wikipedia_mentions", {
96
170
  description: "List Wikipedia articles that reference the given domain. Wikipedia citation is the highest-lift signal for LLM training corpora. Zero keys required.",
97
171
  inputSchema: wikipediaMentionsInputSchema,
172
+ outputSchema: wikipediaMentionsOutputShape,
173
+ annotations: {
174
+ title: "Find Wikipedia articles citing a domain",
175
+ readOnlyHint: true,
176
+ destructiveHint: false,
177
+ idempotentHint: true,
178
+ openWorldHint: true,
179
+ },
98
180
  }, (args) => wrapHandler(() => wikipediaMentions(args)));
99
181
  server.registerTool("audit_sitemap", {
100
182
  description: "Fetch a sitemap.xml (or sitemap index) and run predict_citation on every URL. Returns results sorted worst-score-first. Surfaces systemic issues across a whole site in one pass. Zero engine keys needed.",
101
183
  inputSchema: auditSitemapInputSchema,
184
+ outputSchema: auditSitemapOutputShape,
185
+ annotations: {
186
+ title: "Audit all URLs in a sitemap",
187
+ readOnlyHint: true,
188
+ destructiveHint: false,
189
+ idempotentHint: true,
190
+ openWorldHint: true,
191
+ },
102
192
  }, (args) => wrapHandler(() => auditSitemap(args)));
103
193
  server.registerTool("compete_for_query", {
104
194
  description: "End-to-end competitive snapshot for a single query. Calls check_citations to get the cited URLs, then runs compare_domains on your_url vs the top cited competitors. Returns your score, the average competitor score, and the gap.",
105
195
  inputSchema: competeForQueryInputSchema,
196
+ outputSchema: competeForQueryOutputShape,
197
+ annotations: {
198
+ title: "Competitive citation snapshot for a query",
199
+ readOnlyHint: true,
200
+ destructiveHint: false,
201
+ idempotentHint: true,
202
+ openWorldHint: true,
203
+ },
106
204
  }, (args) => wrapHandler(() => competeForQuery(args)));
107
205
  server.registerTool("citation_freshness_score", {
108
206
  description: "Score how recent the pages cited for a query are. Calls check_citations, then collects dateModified for each cited URL, returns a 0-100 recency_score (halflife=365d) plus per-URL freshness bucket (fresh/current/stale/ancient/unknown). Surfaces queries where AI cites old content - opportunity to ship fresher.",
109
207
  inputSchema: citationFreshnessScoreInputSchema,
208
+ outputSchema: citationFreshnessScoreOutputShape,
209
+ annotations: {
210
+ title: "Score freshness of AI-cited pages",
211
+ readOnlyHint: true,
212
+ destructiveHint: false,
213
+ idempotentHint: true,
214
+ openWorldHint: true,
215
+ },
110
216
  }, (args) => wrapHandler(() => citationFreshnessScore(args)));
111
217
  server.registerTool("cited_for_diff", {
112
218
  description: "Diff cited_for between two time windows for a domain. Returns queries gained (cited now, not before baseline_until) and queries lost (cited before, not since current_since). Cache-only, no API spend. Use to track citation drift over time after publishing or migrating content.",
113
219
  inputSchema: citedForDiffInputSchema,
220
+ outputSchema: citedForDiffOutputShape,
221
+ annotations: {
222
+ title: "Diff citation changes between time windows",
223
+ readOnlyHint: true,
224
+ destructiveHint: false,
225
+ idempotentHint: true,
226
+ openWorldHint: false,
227
+ },
114
228
  }, (args) => wrapHandler(() => citedForDiff(args)));
115
229
  server.registerTool("gsc_citation_gap", {
116
230
  description: "Join Google Search Console performance with am_i_cited per query. Surfaces queries where the domain ranks well in Google but is not cited in AI - the closest editorial wins. Requires GCP service account creds (credentials_path or GOOGLE_APPLICATION_CREDENTIALS env).",
117
231
  inputSchema: gscCitationGapInputSchema,
232
+ outputSchema: gscCitationGapOutputShape,
233
+ annotations: {
234
+ title: "Find GSC queries not cited by AI",
235
+ readOnlyHint: true,
236
+ destructiveHint: false,
237
+ idempotentHint: true,
238
+ openWorldHint: true,
239
+ },
118
240
  }, (args) => wrapHandler(() => gscCitationGap(args)));
119
241
  server.registerTool("schema_audit", {
120
242
  description: "Deep schema.org validation for a URL. Parses every JSON-LD block and microdata node, checks required fields per @type (Article needs headline+author+datePublished, FAQPage needs mainEntity, HowTo needs step, etc.), and flags missing fields and malformed JSON-LD. Returns issues list and a valid/invalid verdict. Use to fix structured-data bugs that predict_citation flags but can't explain.",
121
243
  inputSchema: schemaAuditInputSchema,
244
+ outputSchema: schemaAuditOutputShape,
245
+ annotations: {
246
+ title: "Audit schema.org structured data",
247
+ readOnlyHint: true,
248
+ destructiveHint: false,
249
+ idempotentHint: true,
250
+ openWorldHint: true,
251
+ },
122
252
  }, (args) => wrapHandler(() => schemaAudit(args)));
123
253
  server.registerTool("llms_txt_generator", {
124
254
  description: "Generate an llms.txt file (https://llmstxt.org spec) from a sitemap. Parses sitemap.xml + nested indexes, groups URLs by top-level path, and emits a Markdown document with H1+description+sectioned link lists. Set fetch_titles=true to pull <title> per URL (slower, richer output).",
125
255
  inputSchema: llmsTxtGeneratorInputSchema,
256
+ outputSchema: llmsTxtGeneratorOutputShape,
257
+ annotations: {
258
+ title: "Generate llms.txt from sitemap",
259
+ readOnlyHint: true,
260
+ destructiveHint: false,
261
+ idempotentHint: true,
262
+ openWorldHint: true,
263
+ },
126
264
  }, (args) => wrapHandler(() => llmsTxtGenerator(args)));
127
265
  server.registerTool("answer_box_position", {
128
266
  description: "Locate where each cited URL appears in the AI's raw answer text. Calls check_citations, finds the first mention of each citation's URL (or hostname) in raw_answer, and bins by char position into early/middle/late thirds. Surfaces whether your URL is cited up-front or buried near the end. Returns 'unknown' for engines without raw_answer (Bing, Brave).",
129
267
  inputSchema: answerBoxPositionInputSchema,
268
+ outputSchema: answerBoxPositionOutputShape,
269
+ annotations: {
270
+ title: "Locate citation positions in AI answer",
271
+ readOnlyHint: true,
272
+ destructiveHint: false,
273
+ idempotentHint: true,
274
+ openWorldHint: true,
275
+ },
130
276
  }, (args) => wrapHandler(() => answerBoxPosition(args)));
131
277
  server.registerTool("citation_provenance", {
132
278
  description: "Fan a query out across multiple AI engines and report per-URL cross-engine consensus. Returns each unique cited URL with the list of engines that cited it, plus a consensus_urls list (URLs cited by ALL engines). High engine_count = strong cross-engine citation signal; engine_count=1 = engine-specific.",
133
279
  inputSchema: citationProvenanceInputSchema,
280
+ outputSchema: citationProvenanceOutputShape,
281
+ annotations: {
282
+ title: "Cross-engine citation provenance",
283
+ readOnlyHint: true,
284
+ destructiveHint: false,
285
+ idempotentHint: true,
286
+ openWorldHint: true,
287
+ },
134
288
  }, (args) => wrapHandler(() => citationProvenance(args)));
135
289
  server.registerTool("citation_evidence", {
136
290
  description: "Extract the cited snippet from the AI engine's raw answer for each citation. Calls check_citations, then for each returned URL finds the first mention in raw_answer and returns a context window plus the nearest quoted span or containing sentence. Use to see *why* an engine cited a URL, not just *that* it did. Returns 'not found' for engines without raw_answer (Bing, Brave).",
137
291
  inputSchema: citationEvidenceInputSchema,
292
+ outputSchema: citationEvidenceOutputShape,
293
+ annotations: {
294
+ title: "Extract citation evidence from AI answer",
295
+ readOnlyHint: true,
296
+ destructiveHint: false,
297
+ idempotentHint: true,
298
+ openWorldHint: true,
299
+ },
138
300
  }, (args) => wrapHandler(() => citationEvidence(args)));
139
301
  server.registerTool("crawler_access_audit", {
140
302
  description: "Verify that major AI crawlers (GPTBot, OAI-SearchBot, ClaudeBot, PerplexityBot, CCBot, Google-Extended, Applebot-Extended, Bytespider, Meta-ExternalAgent, plus real-time fetch UAs) can fetch a URL. Parses robots.txt and does a live GET with each bot's User-Agent. Surfaces robots.txt blocks AND UA-based gating that breaks AI citation.",
141
303
  inputSchema: crawlerAccessAuditInputSchema,
304
+ outputSchema: crawlerAccessAuditOutputShape,
305
+ annotations: {
306
+ title: "Audit AI crawler access to a URL",
307
+ readOnlyHint: true,
308
+ destructiveHint: false,
309
+ idempotentHint: true,
310
+ openWorldHint: true,
311
+ },
142
312
  }, (args) => wrapHandler(() => crawlerAccessAudit(args)));
143
313
  server.registerTool("sitemap_citation_map", {
144
314
  description: "Cross-reference a sitemap with the citation cache. For each sitemap URL, reports whether it appears in cached citations (and how many queries/engines cited it). Inverse of audit_sitemap: not 'how citable is each URL', but 'has each URL actually been cited yet'. Cache must be primed via check_citations or run_panel first.",
145
315
  inputSchema: sitemapCitationMapInputSchema,
316
+ outputSchema: sitemapCitationMapOutputShape,
317
+ annotations: {
318
+ title: "Map sitemap URLs against citation cache",
319
+ readOnlyHint: true,
320
+ destructiveHint: false,
321
+ idempotentHint: true,
322
+ openWorldHint: false,
323
+ },
146
324
  }, (args) => wrapHandler(() => sitemapCitationMap(args)));
147
325
  server.registerTool("canonical_competitor_set", {
148
326
  description: "Fan a query across engines and aggregate citations by registered domain (not URL). Returns top competitor domains ranked by cross-engine consensus, with per-engine breakdown and top URLs per domain. Use to identify the canonical competitor set for a query - the domains every engine treats as authoritative.",
149
327
  inputSchema: canonicalCompetitorSetInputSchema,
328
+ outputSchema: canonicalCompetitorSetOutputShape,
329
+ annotations: {
330
+ title: "Identify canonical competitor domains for a query",
331
+ readOnlyHint: true,
332
+ destructiveHint: false,
333
+ idempotentHint: true,
334
+ openWorldHint: true,
335
+ },
150
336
  }, (args) => wrapHandler(() => canonicalCompetitorSet(args)));
337
+ server.registerTool("structured_data_repair", {
338
+ description: "Suggest missing JSON-LD additions for a URL. Fetches the page, detects existing schema types, and returns ready-to-paste templates for types that are missing but signalled by page content (BlogPosting from og:type=article or bylines, FAQPage from Q&A pairs, HowTo from numbered steps, BreadcrumbList from nested paths, Organization on homepages). Templates are pre-filled from page metadata where possible; fields marked FILL: require manual completion.",
339
+ inputSchema: structuredDataRepairInputSchema,
340
+ outputSchema: structuredDataRepairOutputShape,
341
+ annotations: {
342
+ title: "Suggest missing JSON-LD for a URL",
343
+ readOnlyHint: true,
344
+ destructiveHint: false,
345
+ idempotentHint: true,
346
+ openWorldHint: true,
347
+ },
348
+ }, (args) => wrapHandler(() => structuredDataRepair(args)));
151
349
  registerPrompts(server);
152
350
  registerResources(server);
153
351
  const transport = new StdioServerTransport();
154
352
  await server.connect(transport);
155
353
  log.info("server ready on stdio", {
156
- version: "0.6.0",
354
+ version: "0.7.0",
157
355
  log_level: log.level(),
158
356
  tools: [
159
357
  "check_citations",
@@ -179,6 +377,7 @@ log.info("server ready on stdio", {
179
377
  "crawler_access_audit",
180
378
  "sitemap_citation_map",
181
379
  "canonical_competitor_set",
380
+ "structured_data_repair",
182
381
  ],
183
382
  prompts: [
184
383
  "audit_citation_readiness",