@recomention/mcp 0.1.0 → 0.1.1

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.
package/dist/report.js ADDED
@@ -0,0 +1,46 @@
1
+ const usable = (a) => !a.failed && !a.noAnswer && (a.mentioned === true || a.mentioned === false);
2
+ export function questionSummary(q) {
3
+ const answers = Array.isArray(q.answers) ? q.answers : [];
4
+ const byEngine = new Map();
5
+ for (const a of answers) {
6
+ const e = byEngine.get(String(a.engine)) ?? { named: 0, usable: 0, notUsable: 0 };
7
+ if (usable(a)) {
8
+ e.usable++;
9
+ if (a.mentioned === true)
10
+ e.named++;
11
+ }
12
+ else
13
+ e.notUsable++;
14
+ byEngine.set(String(a.engine), e);
15
+ }
16
+ const named = [...byEngine.values()].reduce((n, e) => n + e.named, 0);
17
+ const of = [...byEngine.values()].reduce((n, e) => n + e.usable, 0);
18
+ return {
19
+ order: q.order, text: q.text, intent: q.intent, stage: q.stage,
20
+ named: `${named} of ${of} usable answers`,
21
+ engines: Object.fromEntries([...byEngine].map(([engine, e]) => [engine, { named: `${e.named} of ${e.usable}`, notUsable: e.notUsable }])),
22
+ };
23
+ }
24
+ export function compactReport(body, question) {
25
+ const report = body?.report;
26
+ if (!report || !Array.isArray(report.questions))
27
+ return body;
28
+ const questions = report.questions;
29
+ if (question !== undefined) {
30
+ // By the question's own order (real reports number from 0), never by list position.
31
+ const match = questions.find((q) => q.order === question);
32
+ if (!match) {
33
+ const orders = questions.map((q) => Number(q.order)).filter(Number.isFinite);
34
+ return { error: orders.length ? `No question with order ${question}; orders run ${Math.min(...orders)} to ${Math.max(...orders)}.` : `No question with order ${question}.` };
35
+ }
36
+ return { report: { slug: report.slug, brand: report.brand, domain: report.domain, simulated: report.simulated, enginesSkipped: report.enginesSkipped, question: match } };
37
+ }
38
+ return {
39
+ ...body,
40
+ report: {
41
+ ...report,
42
+ questions: questions.map(questionSummary),
43
+ answersOmitted: `Answer texts, brands and citations are left out to fit an agent's context. Call again with question set to a question's order (from 0) for its answers. Engines in enginesSkipped did not run.`,
44
+ },
45
+ };
46
+ }
package/dist/server.js CHANGED
@@ -3,6 +3,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { z } from "zod";
4
4
  import { ApiError, PROVIDERS, providerKeysFromEnv } from "./api.js";
5
5
  import { suggestPrompts } from "./suggest.js";
6
+ import { compactReport } from "./report.js";
6
7
  export const SERVER_NAME = "recomention";
7
8
  /**
8
9
  * package.json's version, read at startup: one number for npm, the MCP handshake and the
@@ -91,11 +92,12 @@ export function createServer(api, env = process.env) {
91
92
  }, ({ slug }) => run(() => api.status(slug)));
92
93
  server.registerTool("recomention_report_get", {
93
94
  title: "Get a report",
94
- description: "A report as JSON: the overall score, each engine's runs mentioned of runs total, competitors named instead, cited sources, the fix list and every question with every answer. " +
95
+ description: "A report as JSON: the overall score, each engine's runs mentioned of runs total, competitors named instead, cited sources, the fix list and, per question, how many usable answers named the product. " +
96
+ "Answer texts are left out to fit your context: pass question (its order, from 0) to read that question's answers and citations. " +
95
97
  "Engines in enginesSkipped did not run; never describe them as not mentioning the product. simulated: true means test data. Free.",
96
- inputSchema: { slug },
98
+ inputSchema: { slug, question: z.number().int().min(0).max(200).optional().describe("A question's order number, to read its answers.") },
97
99
  annotations: { readOnlyHint: true, openWorldHint: true },
98
- }, ({ slug }) => run(() => api.report(slug)));
100
+ }, ({ slug, question }) => run(async () => compactReport((await api.report(slug)), question)));
99
101
  server.registerTool("recomention_distribution_kit", {
100
102
  title: "Get the distribution kit",
101
103
  description: "Markdown files built from a finished report, for planning distribution: product.md, competitors.md, ai-visibility.md, where-to-get-mentioned.md (third-party pages, subreddits and videos the engines cite for a rival and not for this product) and channel-plan.md (channels ranked by those citations, each with a first task). " +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@recomention/mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "MCP server for recomention: ask whether AI engines recommend a product, from Claude Code, Cursor or Codex.",
5
5
  "type": "module",
6
6
  "license": "MIT",