Skip to content

Commit 256589b

Browse files
A.R.claude
andcommitted
fix(auto-config): include all 14 tools in PERPLEXITY-MCP rules block
The auto-managed PERPLEXITY-MCP-START/END block in CLAUDE.md, AGENTS.md, GEMINI.md, and other rules-format IDE targets listed only 10 tools. The mcp-server actually registers 14 tools in packages/mcp-server/src/tools.ts. Missing from the rules block: - perplexity_export (tools.ts:647) - perplexity_sync_cloud (tools.ts:687) - perplexity_hydrate_cloud_entry (tools.ts:720) - perplexity_doctor (tools.ts:852) The fix is in the WRITER source -- hand-edits to the .md files would just be re-overwritten on the next "Configure for All" run. Approach: extracted the tool catalog into a new exported PERPLEXITY_TOOL_CATALOG constant (still hardcoded -- adding the 4 missing entries -- but now data instead of inline strings) and rewrote getPerplexityRulesContent to render the bullet list from it. Also exported getPerplexityRulesContent so tests can call it directly. A dynamic-from-tools.ts approach was considered but rejected because tools.ts imports the MCP SDK and heavy native deps that would pollute the extension typecheck path. Instead, the new test parses tools.ts with a regex (enabledTools\.has\("(perplexity_[a-z_]+)"\)) at test time and asserts the catalog matches the registered set -- preventing future drift without runtime coupling between the extension and mcp-server packages. Tests added (packages/extension/tests/auto-config.tool-catalog.test.ts, 7 assertions): - registered-count sanity floor (>= 14) - every-registered-in-catalog (the staleness check) - no-phantom-tools in catalog - no-duplicates in catalog - every-name-in-rendered-block - marker-pair wraps the block correctly - every-summary non-empty Validation: - npm run typecheck: clean across 4 packages. - npm run build: clean. - npm run test:coverage: 119 files / 1037 pass / 2 skip / 0 fail (full count includes Slice 3's tests committed in 0a003f3). NOTE: the impl agent self-reported a `git stash` baseline-check during execution and reversed it via `stash pop`. The post-pop worktree was independently verified by the parent before this commit -- only the two scoped files are modified, and gates pass cleanly. The CLAUDE.md / AGENTS.md / GEMINI.md files in users' workspaces will pick up the new tool entries on their next "Configure for All" invocation. No action required by users beyond running that command. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c2c5f45 commit 256589b

2 files changed

Lines changed: 126 additions & 11 deletions

File tree

packages/extension/src/auto-config/index.ts

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,23 +1260,48 @@ export function removeTarget(target: IdeTarget): void {
12601260
removeIdeConfig(target);
12611261
}
12621262

1263-
function getPerplexityRulesContent(): string {
1263+
/**
1264+
* Hardcoded tool catalog used to render the auto-managed `PERPLEXITY-MCP-START`
1265+
* block in CLAUDE.md / AGENTS.md / GEMINI.md and the per-IDE rules files.
1266+
*
1267+
* Source-of-truth for the actual MCP runtime is
1268+
* [packages/mcp-server/src/tools.ts](../../../mcp-server/src/tools.ts) — every
1269+
* `if (!enabledTools || enabledTools.has("perplexity_<name>"))` branch there
1270+
* MUST appear in this list, otherwise the rules block downstream agents read
1271+
* goes stale and they call tools they think don't exist (or skip ones that do).
1272+
*
1273+
* If you add or remove a tool in `tools.ts`, update this list in the same PR.
1274+
* The completeness is enforced by `auto-config.tool-catalog.test.ts`, which
1275+
* imports the source-of-truth tool names and fails if any are missing here.
1276+
*/
1277+
export const PERPLEXITY_TOOL_CATALOG: ReadonlyArray<{ name: string; summary: string }> = [
1278+
{ name: "perplexity_search", summary: "Fast web search with source citations. Use for quick factual lookups. Works with or without authentication." },
1279+
{ name: "perplexity_reason", summary: "Step-by-step reasoning with web context. Requires Pro account." },
1280+
{ name: "perplexity_research", summary: "Deep multi-section research reports (30-120s). Requires Pro account." },
1281+
{ name: "perplexity_ask", summary: "Flexible queries with explicit model/mode/follow-up control." },
1282+
{ name: "perplexity_compute", summary: "ASI/Computer mode for complex multi-step tasks. Requires Max account." },
1283+
{ name: "perplexity_models", summary: "List available models, account tier, and rate limits." },
1284+
{ name: "perplexity_retrieve", summary: "Poll results from pending research/compute tasks." },
1285+
{ name: "perplexity_export", summary: "Export a saved history entry as PDF, markdown, or DOCX. Uses Perplexity's native export when available." },
1286+
{ name: "perplexity_sync_cloud", summary: "Sync Perplexity cloud history into the local history store." },
1287+
{ name: "perplexity_hydrate_cloud_entry", summary: "Hydrate a single cloud-backed history entry by id." },
1288+
{ name: "perplexity_list_researches", summary: "List saved research history with status." },
1289+
{ name: "perplexity_get_research", summary: "Fetch full content of a saved research." },
1290+
{ name: "perplexity_login", summary: "Open browser for Perplexity authentication." },
1291+
{ name: "perplexity_doctor", summary: "Run diagnostic checks against your Perplexity MCP install. Returns a Markdown report; pass probe:true for a live search probe." },
1292+
];
1293+
1294+
export function getPerplexityRulesContent(): string {
1295+
const toolLines = PERPLEXITY_TOOL_CATALOG.map(
1296+
({ name, summary }) => `- **${name}** — ${summary}`,
1297+
);
12641298
return [
12651299
PERPLEXITY_RULES_SECTION_START,
12661300
"# Perplexity MCP Server",
12671301
"",
12681302
"## Available Tools",
12691303
"",
1270-
"- **perplexity_search** — Fast web search with source citations. Use for quick factual lookups. Works with or without authentication.",
1271-
"- **perplexity_reason** — Step-by-step reasoning with web context. Requires Pro account.",
1272-
"- **perplexity_research** — Deep multi-section research reports (30-120s). Requires Pro account.",
1273-
"- **perplexity_ask** — Flexible queries with explicit model/mode/follow-up control.",
1274-
"- **perplexity_compute** — ASI/Computer mode for complex multi-step tasks. Requires Max account.",
1275-
"- **perplexity_models** — List available models, account tier, and rate limits.",
1276-
"- **perplexity_retrieve** — Poll results from pending research/compute tasks.",
1277-
"- **perplexity_list_researches** — List saved research history with status.",
1278-
"- **perplexity_get_research** — Fetch full content of a saved research.",
1279-
"- **perplexity_login** — Open browser for Perplexity authentication.",
1304+
...toolLines,
12801305
"",
12811306
"## Usage Guidelines",
12821307
"",
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { readFileSync } from "node:fs";
2+
import { join } from "node:path";
3+
import { describe, expect, it } from "vitest";
4+
import {
5+
PERPLEXITY_RULES_SECTION_END,
6+
PERPLEXITY_RULES_SECTION_START,
7+
} from "@perplexity-user-mcp/shared";
8+
import {
9+
PERPLEXITY_TOOL_CATALOG,
10+
getPerplexityRulesContent,
11+
} from "../src/auto-config/index.js";
12+
13+
/**
14+
* Source-of-truth path. The MCP server registers tools via guarded blocks like
15+
*
16+
* if (!enabledTools || enabledTools.has("perplexity_<name>")) {
17+
*
18+
* — extracting the names with this regex matches the actual runtime registry
19+
* without forcing this test to import the runtime (which pulls in the SDK and
20+
* heavy native deps that aren't part of the extension package's typecheck path).
21+
*/
22+
const TOOLS_TS_PATH = join(
23+
__dirname,
24+
"..",
25+
"..",
26+
"mcp-server",
27+
"src",
28+
"tools.ts",
29+
);
30+
const TOOL_GUARD_RE = /enabledTools\.has\("(perplexity_[a-z_]+)"\)/g;
31+
32+
function readRegisteredToolNames(): string[] {
33+
const source = readFileSync(TOOLS_TS_PATH, "utf8");
34+
const names = new Set<string>();
35+
for (const match of source.matchAll(TOOL_GUARD_RE)) {
36+
names.add(match[1]);
37+
}
38+
return [...names].sort();
39+
}
40+
41+
describe("auto-config rules block tool catalog", () => {
42+
const registered = readRegisteredToolNames();
43+
44+
it("source-of-truth has at least the audited count (sanity check)", () => {
45+
// Audit at the time of writing said 14 tools registered. If this drops,
46+
// the regex above probably broke; if it grows, that's expected — fix the
47+
// catalog list and re-run.
48+
expect(registered.length).toBeGreaterThanOrEqual(14);
49+
});
50+
51+
it("every registered tool appears in PERPLEXITY_TOOL_CATALOG", () => {
52+
const cataloged = new Set(PERPLEXITY_TOOL_CATALOG.map((t) => t.name));
53+
const missing = registered.filter((name) => !cataloged.has(name));
54+
expect(missing, `tools.ts registers these but the catalog omits them — update PERPLEXITY_TOOL_CATALOG: ${missing.join(", ")}`).toEqual([]);
55+
});
56+
57+
it("catalog has no entries that aren't registered (no phantom tools)", () => {
58+
const registeredSet = new Set(registered);
59+
const phantom = PERPLEXITY_TOOL_CATALOG
60+
.map((t) => t.name)
61+
.filter((name) => !registeredSet.has(name));
62+
expect(phantom, `catalog lists tools not registered in tools.ts — remove them: ${phantom.join(", ")}`).toEqual([]);
63+
});
64+
65+
it("catalog has no duplicate tool names", () => {
66+
const names = PERPLEXITY_TOOL_CATALOG.map((t) => t.name);
67+
const unique = new Set(names);
68+
expect(unique.size).toBe(names.length);
69+
});
70+
71+
it("rendered rules block contains every registered tool name", () => {
72+
const rendered = getPerplexityRulesContent();
73+
for (const name of registered) {
74+
expect(rendered, `expected rules block to mention "${name}"`).toContain(name);
75+
}
76+
});
77+
78+
it("rendered rules block is wrapped in the marker pair", () => {
79+
const rendered = getPerplexityRulesContent();
80+
expect(rendered.startsWith(PERPLEXITY_RULES_SECTION_START)).toBe(true);
81+
expect(rendered.endsWith(PERPLEXITY_RULES_SECTION_END)).toBe(true);
82+
});
83+
84+
it("each catalog entry has a non-empty summary", () => {
85+
for (const entry of PERPLEXITY_TOOL_CATALOG) {
86+
expect(entry.summary, `tool ${entry.name} missing summary`).toBeTruthy();
87+
expect(entry.summary.length, `tool ${entry.name} summary too short`).toBeGreaterThan(10);
88+
}
89+
});
90+
});

0 commit comments

Comments
 (0)