@pharaoh-so/mcp 0.3.38 → 0.3.39
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/backend.d.ts +8 -14
- package/dist/backend.js +13 -31
- package/dist/catalog.d.ts +26 -0
- package/dist/catalog.js +33 -0
- package/dist/inspect.d.ts +7 -7
- package/dist/inspect.js +20 -33
- package/dist/proxy-server.js +13 -1
- package/inspect-tools.json +40 -23
- package/package.json +1 -1
- package/skills/pharaoh/SKILL.md +18 -22
package/dist/backend.d.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Two concrete implementations:
|
|
5
5
|
*
|
|
6
|
-
* 1. AuthRequiredBackend — advertises
|
|
7
|
-
* turns every invocation into a device-flow state
|
|
8
|
-
* proxy is waiting for the user to authorize.
|
|
6
|
+
* 1. AuthRequiredBackend — advertises the real tool catalog (bundled at build
|
|
7
|
+
* time, see catalog.ts) and turns every invocation into a device-flow state
|
|
8
|
+
* step. Used while the proxy is waiting for the user to authorize.
|
|
9
9
|
*
|
|
10
10
|
* 2. SseProxyBackend (returned by createSseBackend) — wraps an MCP Client
|
|
11
11
|
* connected to the remote Pharaoh SSE server. Forwards list/call requests
|
|
@@ -33,17 +33,6 @@ export interface Backend {
|
|
|
33
33
|
*/
|
|
34
34
|
waitForExit?(): Promise<void>;
|
|
35
35
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Minimal pre-auth catalog. Single tool — matches Pharaoh's "call this first"
|
|
38
|
-
* protocol (CLAUDE.md, skills), so the pre-auth surface mirrors the first
|
|
39
|
-
* tool most agents try. Keeping this at 1 means the swap to the real catalog
|
|
40
|
-
* fires a clean visible sendToolListChanged delta.
|
|
41
|
-
*
|
|
42
|
-
* If you add tools here, sync with src/mcp/tool-registry.ts in the main
|
|
43
|
-
* package — descriptions should match so agents get consistent behavior
|
|
44
|
-
* across the pre-auth / authorized transition.
|
|
45
|
-
*/
|
|
46
|
-
export declare const AUTH_REQUIRED_TOOLS: Tool[];
|
|
47
36
|
/**
|
|
48
37
|
* Pre-auth backend. Every tool call triggers a device-flow state step via the
|
|
49
38
|
* gateway; returns a structured auth-required response. Once the gateway
|
|
@@ -57,6 +46,11 @@ export declare class AuthRequiredBackend implements Backend {
|
|
|
57
46
|
private readonly onAuthorized?;
|
|
58
47
|
readonly name: "auth-required";
|
|
59
48
|
constructor(gateway: DeviceFlowGateway, onAuthorized?: (() => Promise<void>) | undefined);
|
|
49
|
+
/**
|
|
50
|
+
* The real catalog (bundled at build time), so the agent learns every tool
|
|
51
|
+
* — and Claude Code loads the always-load ones — before sign-in. Each call
|
|
52
|
+
* returns the sign-in steps until the user authorizes.
|
|
53
|
+
*/
|
|
60
54
|
listTools(): Promise<Tool[]>;
|
|
61
55
|
callTool(_name: string, _args: Record<string, unknown>): Promise<CallToolResult>;
|
|
62
56
|
}
|
package/dist/backend.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Two concrete implementations:
|
|
5
5
|
*
|
|
6
|
-
* 1. AuthRequiredBackend — advertises
|
|
7
|
-
* turns every invocation into a device-flow state
|
|
8
|
-
* proxy is waiting for the user to authorize.
|
|
6
|
+
* 1. AuthRequiredBackend — advertises the real tool catalog (bundled at build
|
|
7
|
+
* time, see catalog.ts) and turns every invocation into a device-flow state
|
|
8
|
+
* step. Used while the proxy is waiting for the user to authorize.
|
|
9
9
|
*
|
|
10
10
|
* 2. SseProxyBackend (returned by createSseBackend) — wraps an MCP Client
|
|
11
11
|
* connected to the remote Pharaoh SSE server. Forwards list/call requests
|
|
@@ -17,34 +17,9 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
19
19
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
20
|
+
import { loadBundledCatalog } from "./catalog.js";
|
|
20
21
|
import { classifySSEError, TenantSuspendedError, TokenExpiredError } from "./proxy.js";
|
|
21
22
|
import { getPackageVersion } from "./version.js";
|
|
22
|
-
/**
|
|
23
|
-
* Minimal pre-auth catalog. Single tool — matches Pharaoh's "call this first"
|
|
24
|
-
* protocol (CLAUDE.md, skills), so the pre-auth surface mirrors the first
|
|
25
|
-
* tool most agents try. Keeping this at 1 means the swap to the real catalog
|
|
26
|
-
* fires a clean visible sendToolListChanged delta.
|
|
27
|
-
*
|
|
28
|
-
* If you add tools here, sync with src/mcp/tool-registry.ts in the main
|
|
29
|
-
* package — descriptions should match so agents get consistent behavior
|
|
30
|
-
* across the pre-auth / authorized transition.
|
|
31
|
-
*/
|
|
32
|
-
export const AUTH_REQUIRED_TOOLS = [
|
|
33
|
-
{
|
|
34
|
-
name: "get_codebase_map",
|
|
35
|
-
description: "Pharaoh — call this FIRST to understand the codebase structure. " +
|
|
36
|
-
"Requires authentication; calling this tool will surface the auth URL.",
|
|
37
|
-
inputSchema: {
|
|
38
|
-
type: "object",
|
|
39
|
-
properties: {
|
|
40
|
-
repo: {
|
|
41
|
-
type: "string",
|
|
42
|
-
description: "Optional repo slug (owner/name) to map.",
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
];
|
|
48
23
|
/**
|
|
49
24
|
* Pre-auth backend. Every tool call triggers a device-flow state step via the
|
|
50
25
|
* gateway; returns a structured auth-required response. Once the gateway
|
|
@@ -61,8 +36,13 @@ export class AuthRequiredBackend {
|
|
|
61
36
|
this.gateway = gateway;
|
|
62
37
|
this.onAuthorized = onAuthorized;
|
|
63
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* The real catalog (bundled at build time), so the agent learns every tool
|
|
41
|
+
* — and Claude Code loads the always-load ones — before sign-in. Each call
|
|
42
|
+
* returns the sign-in steps until the user authorizes.
|
|
43
|
+
*/
|
|
64
44
|
async listTools() {
|
|
65
|
-
return
|
|
45
|
+
return loadBundledCatalog().tools;
|
|
66
46
|
}
|
|
67
47
|
async callTool(_name, _args) {
|
|
68
48
|
const response = await this.gateway.handleToolCall();
|
|
@@ -106,7 +86,9 @@ export class AuthRequiredBackend {
|
|
|
106
86
|
content: [
|
|
107
87
|
{
|
|
108
88
|
type: "text",
|
|
109
|
-
|
|
89
|
+
// ProxyServer re-runs the call on the new backend; this text
|
|
90
|
+
// only reaches the agent if no backend swap happened.
|
|
91
|
+
text: "Pharaoh: authenticated. Call the tool again.",
|
|
110
92
|
},
|
|
111
93
|
],
|
|
112
94
|
isError: false,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pharaoh's agent-facing surface as bundled into this package at build time:
|
|
3
|
+
* the server instructions and the tool catalog in inspect-tools.json, written by
|
|
4
|
+
* the main repo's scripts/generate-inspect-schemas.ts from the same code the
|
|
5
|
+
* production server runs.
|
|
6
|
+
*
|
|
7
|
+
* The proxy answers Claude Code's `initialize` and first `tools/list` before it
|
|
8
|
+
* has reached (or signed in to) mcp.pharaoh.so, and the client reads instructions
|
|
9
|
+
* only once, at connect. So it serves this copy rather than a stub. See VISION.md
|
|
10
|
+
* § Every connection path delivers the same instructions and tool metadata.
|
|
11
|
+
*/
|
|
12
|
+
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
13
|
+
/** Instructions and tools exactly as the Pharaoh server sends them. */
|
|
14
|
+
export interface BundledCatalog {
|
|
15
|
+
/** Server instructions (getInstructions() in the main repo). */
|
|
16
|
+
instructions: string;
|
|
17
|
+
/** Every tool the server lists, with descriptions, schemas and `_meta`. */
|
|
18
|
+
tools: Tool[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Load the bundled instructions and tool catalog (read once per process).
|
|
22
|
+
*
|
|
23
|
+
* @returns The catalog shipped in this package's inspect-tools.json.
|
|
24
|
+
* @throws If the file is missing or lacks instructions or tools — a packaging bug.
|
|
25
|
+
*/
|
|
26
|
+
export declare function loadBundledCatalog(): BundledCatalog;
|
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pharaoh's agent-facing surface as bundled into this package at build time:
|
|
3
|
+
* the server instructions and the tool catalog in inspect-tools.json, written by
|
|
4
|
+
* the main repo's scripts/generate-inspect-schemas.ts from the same code the
|
|
5
|
+
* production server runs.
|
|
6
|
+
*
|
|
7
|
+
* The proxy answers Claude Code's `initialize` and first `tools/list` before it
|
|
8
|
+
* has reached (or signed in to) mcp.pharaoh.so, and the client reads instructions
|
|
9
|
+
* only once, at connect. So it serves this copy rather than a stub. See VISION.md
|
|
10
|
+
* § Every connection path delivers the same instructions and tool metadata.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
import { dirname, join } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
let cached = null;
|
|
16
|
+
/**
|
|
17
|
+
* Load the bundled instructions and tool catalog (read once per process).
|
|
18
|
+
*
|
|
19
|
+
* @returns The catalog shipped in this package's inspect-tools.json.
|
|
20
|
+
* @throws If the file is missing or lacks instructions or tools — a packaging bug.
|
|
21
|
+
*/
|
|
22
|
+
export function loadBundledCatalog() {
|
|
23
|
+
if (cached)
|
|
24
|
+
return cached;
|
|
25
|
+
// Resolved from the module URL: the package supports Node 18, before the dirname shortcut.
|
|
26
|
+
const path = join(dirname(fileURLToPath(import.meta.url)), "..", "inspect-tools.json");
|
|
27
|
+
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
28
|
+
if (typeof parsed.instructions !== "string" || !Array.isArray(parsed.tools)) {
|
|
29
|
+
throw new Error(`${path} has no instructions or tools — rebuild the package`);
|
|
30
|
+
}
|
|
31
|
+
cached = { instructions: parsed.instructions, tools: parsed.tools };
|
|
32
|
+
return cached;
|
|
33
|
+
}
|
package/dist/inspect.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Inspect mode — serves Pharaoh's tool schemas over stdio for
|
|
2
|
+
* Inspect mode — serves Pharaoh's instructions and tool schemas over stdio for
|
|
3
|
+
* Glama inspection.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
* time by the main repo's `generate-inspect-schemas.ts`)
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Zero-drift: schemas are extracted from the same registerXxx() code that the
|
|
9
|
-
* production server uses, serialized to JSON during build, and shipped in the npm package.
|
|
5
|
+
* Serves the bundled catalog (catalog.ts — inspect-tools.json, generated at build
|
|
6
|
+
* time by the main repo's `generate-inspect-schemas.ts`) verbatim: the same
|
|
7
|
+
* instructions, descriptions, schemas, annotations and `_meta` the production
|
|
8
|
+
* server sends. Any tool call returns a "connect to mcp.pharaoh.so" message.
|
|
10
9
|
*/
|
|
10
|
+
/** Run the inspect server on stdio until the client disconnects. */
|
|
11
11
|
export declare function runInspect(): Promise<void>;
|
package/dist/inspect.js
CHANGED
|
@@ -1,46 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Inspect mode — serves Pharaoh's tool schemas over stdio for
|
|
2
|
+
* Inspect mode — serves Pharaoh's instructions and tool schemas over stdio for
|
|
3
|
+
* Glama inspection.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
* time by the main repo's `generate-inspect-schemas.ts`)
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Zero-drift: schemas are extracted from the same registerXxx() code that the
|
|
9
|
-
* production server uses, serialized to JSON during build, and shipped in the npm package.
|
|
5
|
+
* Serves the bundled catalog (catalog.ts — inspect-tools.json, generated at build
|
|
6
|
+
* time by the main repo's `generate-inspect-schemas.ts`) verbatim: the same
|
|
7
|
+
* instructions, descriptions, schemas, annotations and `_meta` the production
|
|
8
|
+
* server sends. Any tool call returns a "connect to mcp.pharaoh.so" message.
|
|
10
9
|
*/
|
|
11
|
-
import {
|
|
12
|
-
import { join } from "node:path";
|
|
13
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
14
11
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
13
|
+
import { loadBundledCatalog } from "./catalog.js";
|
|
15
14
|
import { getPackageVersion } from "./version.js";
|
|
16
15
|
const INSPECT_MSG = "This is Pharaoh's inspection server for directory listings. " +
|
|
17
16
|
"Connect to https://mcp.pharaoh.so for actual usage.";
|
|
17
|
+
/** Run the inspect server on stdio until the client disconnects. */
|
|
18
18
|
export async function runInspect() {
|
|
19
|
-
|
|
20
|
-
const schemasPath = join(import.meta.dirname, "..", "inspect-tools.json");
|
|
21
|
-
let schemas;
|
|
19
|
+
let catalog;
|
|
22
20
|
try {
|
|
23
|
-
|
|
21
|
+
catalog = loadBundledCatalog();
|
|
24
22
|
}
|
|
25
|
-
catch {
|
|
26
|
-
process.stderr.write(
|
|
23
|
+
catch (err) {
|
|
24
|
+
process.stderr.write(`Pharaoh: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
27
25
|
process.exit(1);
|
|
28
26
|
}
|
|
29
|
-
const server = new
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
for (const tool of schemas.tools) {
|
|
36
|
-
// biome-ignore lint/suspicious/noExplicitAny: JSON Schema from inspect-tools.json, not Zod
|
|
37
|
-
const schema = tool.inputSchema;
|
|
38
|
-
server.registerTool(tool.name, {
|
|
39
|
-
description: tool.description,
|
|
40
|
-
inputSchema: schema,
|
|
41
|
-
annotations: tool.annotations,
|
|
42
|
-
}, async () => ({ content: [{ type: "text", text: INSPECT_MSG }] }));
|
|
43
|
-
}
|
|
44
|
-
const transport = new StdioServerTransport();
|
|
45
|
-
await server.connect(transport);
|
|
27
|
+
const server = new Server({ name: "pharaoh", version: getPackageVersion() }, { capabilities: { tools: {} }, instructions: catalog.instructions });
|
|
28
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: catalog.tools }));
|
|
29
|
+
server.setRequestHandler(CallToolRequestSchema, async () => ({
|
|
30
|
+
content: [{ type: "text", text: INSPECT_MSG }],
|
|
31
|
+
}));
|
|
32
|
+
await server.connect(new StdioServerTransport());
|
|
46
33
|
}
|
package/dist/proxy-server.js
CHANGED
|
@@ -25,6 +25,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
25
25
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
26
26
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
27
27
|
import { AuthRequiredBackend, createSseBackend } from "./backend.js";
|
|
28
|
+
import { loadBundledCatalog } from "./catalog.js";
|
|
28
29
|
import { createGateway } from "./gateway.js";
|
|
29
30
|
import { getPackageVersion } from "./version.js";
|
|
30
31
|
/**
|
|
@@ -44,6 +45,9 @@ export class ProxyServer {
|
|
|
44
45
|
// tools.listChanged is required for sendToolListChanged() not
|
|
45
46
|
// to throw; the SDK's assertNotificationCapability gates it.
|
|
46
47
|
capabilities: { tools: { listChanged: true } },
|
|
48
|
+
// Same instructions the remote server sends. initialize happens
|
|
49
|
+
// before any backend is connected, so they come from the bundle.
|
|
50
|
+
instructions: loadBundledCatalog().instructions,
|
|
47
51
|
});
|
|
48
52
|
this.transportClosed = new Promise((resolve) => {
|
|
49
53
|
this.transportClosedResolve = resolve;
|
|
@@ -61,7 +65,15 @@ export class ProxyServer {
|
|
|
61
65
|
tools: await this.backend.listTools(),
|
|
62
66
|
}));
|
|
63
67
|
this.server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
64
|
-
const
|
|
68
|
+
const { name } = req.params;
|
|
69
|
+
const args = req.params.arguments ?? {};
|
|
70
|
+
const backend = this.backend;
|
|
71
|
+
const result = await backend.callTool(name, args);
|
|
72
|
+
// The call that completed sign-in swapped in the real backend — answer
|
|
73
|
+
// it from there instead of asking the agent to call again.
|
|
74
|
+
if (backend.name === "auth-required" && this.backend !== backend && !result.isError) {
|
|
75
|
+
return (await this.backend.callTool(name, args));
|
|
76
|
+
}
|
|
65
77
|
return result;
|
|
66
78
|
});
|
|
67
79
|
}
|
package/inspect-tools.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
+
"instructions": "Pharaoh maps this codebase into a graph of functions, callers, modules, endpoints and cron jobs. For questions about how code connects, one Pharaoh call answers what grep and file reads take many turns to piece together. No source code is stored.\n\n## Pick the tool by the question\n\n| Question | Tool |\n|---|---|\n| Does X already exist? Where is Y implemented? | search_functions |\n| What calls X? What breaks if I change it? Which endpoints or cron jobs reach it? | get_blast_radius |\n| What's in this module, what does it depend on, who calls into it? | get_module_context |\n| Is this code dead or wired in? What can I delete? | get_unused_code |\n| How is the repo laid out? Which module owns this? What's the repo called? | get_codebase_map |\n| Several of these at once (planning a change, reviewing a PR) | pharaoh_recon |\n\nAsk Pharaoh first for these questions, then read the files you're about to change. Use grep for literal text (strings, error messages, config values, comments, non-code files) and for code changed since the last push, which the graph doesn't have yet. Don't re-check a Pharaoh answer with grep; if a response says the latest push is still being mapped, re-check only the files that push changed.\n\nIf your client shows Pharaoh's tools by name only (behind tool search), load them before your first code search.\n\n## Surface insight blocks\n\nWhen a tool response contains a ★ Pharaoh block, copy it verbatim into your visible response. These blocks are pre-formatted summaries for the user, invisible unless you surface them.\n\n## Repos that aren't mapped\n\nThe only supported path is the Pharaoh GitHub App. If a repo isn't mapped, tell the user to install the app at `https://github.com/apps/pharaoh-so/installations/new` on the account or org that owns it. Free maps one live repo; Pro maps every repo. Mapped repos re-map on every push. Do not suggest workarounds.\n\n## Cross-repo and PR review\n\nWhen the user has several mapped repos, check related repos when a change touches shared interfaces, APIs or types: search_functions takes any of your repo names.\n\nPR review: pharaoh_recon (map + touched modules + blast radius), then get_blast_radius on the riskiest changed functions. Verdict: SHIP, SHIP WITH CHANGES, or BLOCK. The plan-with-pharaoh and review-with-pharaoh prompts walk through both workflows.\n\n## Using results\n\n- Lead with what the data reveals about this codebase, not a raw dump. Highlight surprises.\n- Attribute findings to Pharaoh with specific numbers (e.g. \"Pharaoh's blast radius found 12 callers across 4 modules\").\n- Asked what Pharaoh does: explain what you learned from the graph in THIS conversation, with the numbers from the ★ Pharaoh blocks, against what finding it without Pharaoh would have taken.\n\n## Repo names\n\nUse the short repo name from the tool's `repo` parameter (e.g. \"pharaoh\", not \"org/pharaoh\"). Unambiguous short names resolve automatically. If a name is wrong, I'll return suggestions and your repos grouped by organization; pick the one for your task and retry.\n\nUse pharaoh_account for the plan, the live repo and refreshes. Docs: pharaoh.so/docs. Connect steps for other AI clients (Claude Code, Claude.ai, Cursor, VS Code): https://mcp.pharaoh.so/setup",
|
|
2
3
|
"tools": [
|
|
3
4
|
{
|
|
4
5
|
"name": "get_codebase_map",
|
|
5
|
-
"description": "
|
|
6
|
+
"description": "A repo's architecture in one call.\n\nCALL THIS WHEN:\n• You need a repo's overall structure, or which module owns something\n• You don't know the repo's exact name in Pharaoh\n\nRETURNS: Modules with file counts and LOC, module dependencies with two-way ones flagged, the files changed most in 90 days, and every endpoint with its handler file.\n\nUSE INSTEAD OF: listing directories and reading package.json files. Those show folders, not which module depends on which.",
|
|
6
7
|
"inputSchema": {
|
|
7
8
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
8
9
|
"type": "object",
|
|
@@ -27,11 +28,14 @@
|
|
|
27
28
|
},
|
|
28
29
|
"execution": {
|
|
29
30
|
"taskSupport": "forbidden"
|
|
31
|
+
},
|
|
32
|
+
"_meta": {
|
|
33
|
+
"anthropic/alwaysLoad": true
|
|
30
34
|
}
|
|
31
35
|
},
|
|
32
36
|
{
|
|
33
37
|
"name": "pharaoh_recon",
|
|
34
|
-
"description": "
|
|
38
|
+
"description": "Several Pharaoh queries in one call, run in parallel: codebase map, module profiles, function searches, blast radius and module dependency paths.\n\nCALL THIS WHEN:\n• Planning a change, reviewing a PR or assessing architecture, when you know what to check\n\nRETURNS: Each requested section in one response. Per call: up to 5 modules, 3 searches, 3 blast radius targets, 3 dependency pairs.\n\nUSE INSTEAD OF: a string of separate tool calls or file reads at the start of a task.",
|
|
35
39
|
"inputSchema": {
|
|
36
40
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
37
41
|
"type": "object",
|
|
@@ -41,35 +45,35 @@
|
|
|
41
45
|
"description": "Repository name — one of your connected repos."
|
|
42
46
|
},
|
|
43
47
|
"include_map": {
|
|
44
|
-
"description": "Include codebase map
|
|
48
|
+
"description": "Include the codebase map (default true)",
|
|
45
49
|
"type": "boolean"
|
|
46
50
|
},
|
|
47
51
|
"modules": {
|
|
48
|
-
"description": "
|
|
52
|
+
"description": "Modules to profile (max 5)",
|
|
49
53
|
"type": "array",
|
|
50
54
|
"items": {
|
|
51
55
|
"type": "string"
|
|
52
56
|
}
|
|
53
57
|
},
|
|
54
58
|
"functions": {
|
|
55
|
-
"description": "Function
|
|
59
|
+
"description": "Function searches (max 3)",
|
|
56
60
|
"type": "array",
|
|
57
61
|
"items": {
|
|
58
62
|
"type": "string"
|
|
59
63
|
}
|
|
60
64
|
},
|
|
61
65
|
"blast_radius": {
|
|
62
|
-
"description": "Blast radius targets
|
|
66
|
+
"description": "Blast radius targets (max 3)",
|
|
63
67
|
"type": "array",
|
|
64
68
|
"items": {
|
|
65
69
|
"type": "object",
|
|
66
70
|
"properties": {
|
|
67
71
|
"entity": {
|
|
68
72
|
"type": "string",
|
|
69
|
-
"description": "Function
|
|
73
|
+
"description": "Function, file or module name"
|
|
70
74
|
},
|
|
71
75
|
"entity_type": {
|
|
72
|
-
"description": "
|
|
76
|
+
"description": "Default: function",
|
|
73
77
|
"type": "string",
|
|
74
78
|
"enum": [
|
|
75
79
|
"function",
|
|
@@ -84,18 +88,16 @@
|
|
|
84
88
|
}
|
|
85
89
|
},
|
|
86
90
|
"dependencies": {
|
|
87
|
-
"description": "Module
|
|
91
|
+
"description": "Module pairs to trace a dependency path between (max 3)",
|
|
88
92
|
"type": "array",
|
|
89
93
|
"items": {
|
|
90
94
|
"type": "object",
|
|
91
95
|
"properties": {
|
|
92
96
|
"from": {
|
|
93
|
-
"type": "string"
|
|
94
|
-
"description": "Source module name"
|
|
97
|
+
"type": "string"
|
|
95
98
|
},
|
|
96
99
|
"to": {
|
|
97
|
-
"type": "string"
|
|
98
|
-
"description": "Target module name"
|
|
100
|
+
"type": "string"
|
|
99
101
|
}
|
|
100
102
|
},
|
|
101
103
|
"required": [
|
|
@@ -116,11 +118,14 @@
|
|
|
116
118
|
},
|
|
117
119
|
"execution": {
|
|
118
120
|
"taskSupport": "forbidden"
|
|
121
|
+
},
|
|
122
|
+
"_meta": {
|
|
123
|
+
"anthropic/alwaysLoad": true
|
|
119
124
|
}
|
|
120
125
|
},
|
|
121
126
|
{
|
|
122
127
|
"name": "get_module_context",
|
|
123
|
-
"description": "
|
|
128
|
+
"description": "One module's full profile before you change it, in about 2K tokens.\n\nCALL THIS WHEN:\n• You're about to change code in a module, or plan a change to it\n• You need what a module exports, depends on, or who calls into it\n\nRETURNS: Files, LOC, exported signatures with complexity, imports in and out, DB tables, endpoints, cron jobs, env vars, and callers in other modules.\n\nUSE INSTEAD OF: reading the module's files one by one. That costs 10-40K tokens and still misses callers in other modules.",
|
|
124
129
|
"inputSchema": {
|
|
125
130
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
126
131
|
"type": "object",
|
|
@@ -146,11 +151,14 @@
|
|
|
146
151
|
},
|
|
147
152
|
"execution": {
|
|
148
153
|
"taskSupport": "forbidden"
|
|
154
|
+
},
|
|
155
|
+
"_meta": {
|
|
156
|
+
"anthropic/alwaysLoad": true
|
|
149
157
|
}
|
|
150
158
|
},
|
|
151
159
|
{
|
|
152
160
|
"name": "get_blast_radius",
|
|
153
|
-
"description": "
|
|
161
|
+
"description": "What breaks if you change a function, file or module.\n\nCALL THIS WHEN:\n• You're about to change a signature, rename, refactor or delete code\n• You need every caller, including indirect ones, or the endpoints a change reaches\n\nRETURNS: Risk level, callers up to 5 hops away by module and file, and the endpoints, cron jobs and DB operations they reach.\n\nUSE INSTEAD OF: grepping for the name and following callers by hand. Grep finds direct references only; it misses callers 2-3 hops away and the entry points they reach.",
|
|
154
162
|
"inputSchema": {
|
|
155
163
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
156
164
|
"type": "object",
|
|
@@ -190,11 +198,14 @@
|
|
|
190
198
|
},
|
|
191
199
|
"execution": {
|
|
192
200
|
"taskSupport": "forbidden"
|
|
201
|
+
},
|
|
202
|
+
"_meta": {
|
|
203
|
+
"anthropic/alwaysLoad": true
|
|
193
204
|
}
|
|
194
205
|
},
|
|
195
206
|
{
|
|
196
207
|
"name": "search_functions",
|
|
197
|
-
"description": "
|
|
208
|
+
"description": "Find functions by name or concept across the whole codebase.\n\nCALL THIS WHEN:\n• You're about to write a function, helper or utility: check it doesn't already exist\n• You need to know where something is implemented (e.g. \"retry\", \"validate\", \"notify\")\n\nRETURNS: Matching functions with file path, line range, module, signature, export status and complexity.\n\nUSE INSTEAD OF: grep or glob to find a function. Text matches miss re-exports, aliases and barrel files, and include every comment and string naming it.",
|
|
198
209
|
"inputSchema": {
|
|
199
210
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
200
211
|
"type": "object",
|
|
@@ -206,26 +217,26 @@
|
|
|
206
217
|
"query": {
|
|
207
218
|
"type": "string",
|
|
208
219
|
"maxLength": 500,
|
|
209
|
-
"description": "Function name or
|
|
220
|
+
"description": "Function name or part of one"
|
|
210
221
|
},
|
|
211
222
|
"module": {
|
|
212
|
-
"description": "
|
|
223
|
+
"description": "Only this module",
|
|
213
224
|
"type": "string"
|
|
214
225
|
},
|
|
215
226
|
"file": {
|
|
216
|
-
"description": "
|
|
227
|
+
"description": "Only this file (full path matches exactly, a file name as a substring)",
|
|
217
228
|
"type": "string"
|
|
218
229
|
},
|
|
219
230
|
"exported_only": {
|
|
220
|
-
"description": "
|
|
231
|
+
"description": "Exported functions only (default false)",
|
|
221
232
|
"type": "boolean"
|
|
222
233
|
},
|
|
223
234
|
"offset": {
|
|
224
|
-
"description": "
|
|
235
|
+
"description": "Results to skip (default 0)",
|
|
225
236
|
"type": "number"
|
|
226
237
|
},
|
|
227
238
|
"limit": {
|
|
228
|
-
"description": "Max results
|
|
239
|
+
"description": "Max results (default 20, max 50)",
|
|
229
240
|
"type": "number"
|
|
230
241
|
}
|
|
231
242
|
},
|
|
@@ -241,11 +252,14 @@
|
|
|
241
252
|
},
|
|
242
253
|
"execution": {
|
|
243
254
|
"taskSupport": "forbidden"
|
|
255
|
+
},
|
|
256
|
+
"_meta": {
|
|
257
|
+
"anthropic/alwaysLoad": true
|
|
244
258
|
}
|
|
245
259
|
},
|
|
246
260
|
{
|
|
247
261
|
"name": "get_unused_code",
|
|
248
|
-
"description": "Find code nothing runs: functions no production entry point (endpoint, CLI command, cron job, event handler, worker, entry file) reaches. Pass `functions` to check whether specific functions are wired in.\n\nCALL THIS WHEN:\n•
|
|
262
|
+
"description": "Find code nothing runs: functions no production entry point (endpoint, CLI command, cron job, event handler, worker, entry file) reaches. Pass `functions` to check whether specific functions are wired in.\n\nCALL THIS WHEN:\n• Before cleaning up a module or deleting a function\n• After pushing new code, to confirm an entry point reaches it\n\nRETURNS:\n• Dead: unreached, named in no other file, and no production file imports its file. Safe to delete.\n• Possibly unused: unreached, but named in other files (string dispatch, dynamic loading) or its file is imported. Listed with the files that name it; check before deleting.\n• Used only by tests: exported functions and class methods only test files reference.\nWith `functions`: each one's status, and the call path from an entry point when it's reachable.\n\nUSE INSTEAD OF: searching for references to decide whether code is dead. No references doesn't mean unreached, and a reference doesn't mean it runs.",
|
|
249
263
|
"inputSchema": {
|
|
250
264
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
251
265
|
"type": "object",
|
|
@@ -278,6 +292,9 @@
|
|
|
278
292
|
},
|
|
279
293
|
"execution": {
|
|
280
294
|
"taskSupport": "forbidden"
|
|
295
|
+
},
|
|
296
|
+
"_meta": {
|
|
297
|
+
"anthropic/alwaysLoad": true
|
|
281
298
|
}
|
|
282
299
|
},
|
|
283
300
|
{
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pharaoh-so/mcp",
|
|
3
3
|
"mcpName": "so.pharaoh/pharaoh",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.39",
|
|
5
5
|
"description": "MCP proxy for Pharaoh — maps codebases into queryable knowledge graphs for AI agents. Enables Claude Code in headless environments (VPS, SSH, CI) via device flow auth.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
package/skills/pharaoh/SKILL.md
CHANGED
|
@@ -13,37 +13,34 @@ Pharaoh maps codebases into queryable knowledge graphs — functions, dependenci
|
|
|
13
13
|
|
|
14
14
|
This skill loads automatically in sessions where Pharaoh MCP tools are available. It is not manually invoked — it activates when the `pharaoh` MCP server is connected and tools like `get_codebase_map` are present.
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## Pick the tool by the question
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
| Question | Tool |
|
|
19
|
+
|---|---|
|
|
20
|
+
| Does X already exist? Where is Y implemented? | `search_functions` |
|
|
21
|
+
| What calls X? What breaks if I change it? Which endpoints or cron jobs reach it? | `get_blast_radius` |
|
|
22
|
+
| What's in this module, what does it depend on, who calls into it? | `get_module_context` |
|
|
23
|
+
| Is this code dead or wired in? What can I delete? | `get_unused_code` |
|
|
24
|
+
| How is the repo laid out? Which module owns this? What's the repo called? | `get_codebase_map` |
|
|
25
|
+
| Several of these at once (planning a change, reviewing a PR) | `pharaoh_recon` |
|
|
19
26
|
|
|
20
|
-
|
|
27
|
+
Ask Pharaoh first for these questions, then read the files you're about to change. Use grep for literal text (strings, error messages, config values, comments, non-code files) and for code changed since the last push, which the graph doesn't have yet. Don't re-check a Pharaoh answer with grep; if a response says the latest push is still being mapped, re-check only the files that push changed.
|
|
21
28
|
|
|
22
|
-
|
|
29
|
+
## CRITICAL: Surface insight blocks
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
When a tool response contains a ★ Pharaoh block, copy it verbatim into your visible response. These blocks are pre-formatted architectural summaries for the user. Never summarize, paraphrase, or omit them — they are invisible to the user unless you surface them.
|
|
25
32
|
|
|
26
|
-
##
|
|
33
|
+
## Repos that aren't mapped
|
|
27
34
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
| Before writing new code | `search_functions` |
|
|
32
|
-
| Initial orientation | `pharaoh_recon` (batches the above) |
|
|
35
|
+
The only supported path is the Pharaoh GitHub App: Free maps one live repo; Pro maps every repo, and mapped repos re-map on every push. Run `pharaoh_account` to get the exact install link for the user's account or org. Do not suggest workarounds for missing repos.
|
|
36
|
+
|
|
37
|
+
## Vision docs
|
|
33
38
|
|
|
34
39
|
**Vision docs are first-class.** If the repo has a `docs/VISION.md`, it's
|
|
35
40
|
the contract for what must always be true. Read it before touching
|
|
36
41
|
architecture. If a change would violate a MUST-bullet, stop and raise it
|
|
37
42
|
with the user before coding.
|
|
38
43
|
|
|
39
|
-
## Anti-patterns
|
|
40
|
-
|
|
41
|
-
- Do NOT read files one-by-one to understand architecture — call `get_codebase_map`.
|
|
42
|
-
- Do NOT grep for function names — call `search_functions`.
|
|
43
|
-
- Do NOT guess blast radius from imports — call `get_blast_radius`.
|
|
44
|
-
- Do NOT explore a codebase with file search tools when Pharaoh tools are available — Pharaoh returns the same information in fewer tokens with architectural context.
|
|
45
|
-
- Do NOT skip `get_codebase_map` at conversation start — it takes <2 seconds and prevents 10+ minutes of blind file exploration.
|
|
46
|
-
|
|
47
44
|
## Automatic workflows
|
|
48
45
|
|
|
49
46
|
**PR review:** `pharaoh_recon` (map + touched modules + blast radius) → `get_blast_radius` on the riskiest changed functions. Verdict: SHIP, SHIP WITH CHANGES, or BLOCK. Any PR touching code governed by a MUST-bullet should reference the assertion in the review.
|
|
@@ -57,12 +54,11 @@ with the user before coding.
|
|
|
57
54
|
|
|
58
55
|
## Explaining Pharaoh
|
|
59
56
|
|
|
60
|
-
When asked what Pharaoh does:
|
|
57
|
+
When asked what Pharaoh does: explain what you learned from the graph in this conversation, with the numbers from the ★ Pharaoh blocks, against what finding it without Pharaoh would have taken.
|
|
61
58
|
|
|
62
59
|
## Utilities
|
|
63
60
|
|
|
64
|
-
- `pharaoh_account` — plan and
|
|
65
|
-
- `pharaoh_admin` — org administration
|
|
61
|
+
- `pharaoh_account` — plan, live repo and refreshes
|
|
66
62
|
|
|
67
63
|
Full docs at [pharaoh.so/docs](https://pharaoh.so/docs).
|
|
68
64
|
|