@pharaoh-so/mcp 0.3.4 → 0.3.5

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/auth.d.ts CHANGED
@@ -1,7 +1,3 @@
1
- /**
2
- * Device flow client for Pharaoh MCP proxy (RFC 8628).
3
- * All user-visible output goes to stderr — stdout is reserved for JSON-RPC.
4
- */
5
1
  /** Response from POST /device. */
6
2
  export interface DeviceCodeResponse {
7
3
  device_code: string;
@@ -36,9 +32,10 @@ export declare function requestDeviceCode(baseUrl: string): Promise<DeviceCodeRe
36
32
  export declare function pollForToken(baseUrl: string, deviceCode: string, interval: number): Promise<TokenResponse>;
37
33
  /**
38
34
  * Print the device activation prompt to stderr.
39
- * Shows the user code and verification URI in a visible box.
35
+ * Shows the user code and verification URI in a visible box,
36
+ * and opens the verification URL in the default browser.
40
37
  */
41
- export declare function printActivationPrompt(userCode: string, verificationUri: string): void;
38
+ export declare function printActivationPrompt(userCode: string, verificationUri: string, verificationUriComplete?: string): void;
42
39
  /**
43
40
  * Print authentication success message to stderr.
44
41
  */
package/dist/auth.js CHANGED
@@ -2,6 +2,7 @@
2
2
  * Device flow client for Pharaoh MCP proxy (RFC 8628).
3
3
  * All user-visible output goes to stderr — stdout is reserved for JSON-RPC.
4
4
  */
5
+ import { execFile } from "node:child_process";
5
6
  /**
6
7
  * Request a device code from the Pharaoh server (RFC 8628 §3.1).
7
8
  * Returns the device code, user code, and verification URIs.
@@ -55,31 +56,54 @@ export async function pollForToken(baseUrl, deviceCode, interval) {
55
56
  throw new Error(`Device token error: ${body.error} — ${body.error_description ?? ""}`);
56
57
  }
57
58
  }
59
+ /**
60
+ * Open a URL in the user's default browser.
61
+ * Uses platform-specific commands; fails silently if unavailable.
62
+ */
63
+ function openBrowser(url) {
64
+ if (process.platform === "darwin") {
65
+ execFile("open", [url], () => { });
66
+ }
67
+ else if (process.platform === "win32") {
68
+ execFile("cmd.exe", ["/c", "start", "", url], () => { });
69
+ }
70
+ else {
71
+ execFile("xdg-open", [url], () => { });
72
+ }
73
+ }
58
74
  /**
59
75
  * Print the device activation prompt to stderr.
60
- * Shows the user code and verification URI in a visible box.
76
+ * Shows the user code and verification URI in a visible box,
77
+ * and opens the verification URL in the default browser.
61
78
  */
62
- export function printActivationPrompt(userCode, verificationUri) {
79
+ export function printActivationPrompt(userCode, verificationUri, verificationUriComplete) {
80
+ // Auto-open browser with the complete URI (pre-fills the code)
81
+ openBrowser(verificationUriComplete ?? verificationUri);
82
+ const W = 48;
83
+ const pad = (s) => `│ ${s.padEnd(W - 2)}│`;
84
+ const empty = `│${" ".repeat(W)}│`;
85
+ const top = `┌${"─".repeat(W)}┐`;
86
+ const bot = `└${"─".repeat(W)}┘`;
63
87
  const lines = [
64
88
  "",
65
- "┌───────────────────────────────────────────┐",
66
- "│ Pharaoh — Device Authorization │",
67
- "│ │",
68
- `│ Code: ${userCode.padEnd(33)}│`,
69
- `│ URL: ${verificationUri.padEnd(33)}│`,
70
- "│ │",
71
- "│ Open the URL and enter the code to sign │",
72
- "│ in. You can do this on any device. │",
73
- "└───────────────────────────────────────────┘",
89
+ top,
90
+ empty,
91
+ pad(`Your code: ${userCode}`),
92
+ empty,
93
+ pad("A browser window should open automatically."),
94
+ pad("If it didn't open, visit:"),
95
+ pad(verificationUri),
96
+ empty,
97
+ bot,
74
98
  "",
75
99
  ];
76
- process.stderr.write(lines.join("\n") + "\n");
100
+ process.stderr.write(`${lines.join("\n")}\n`);
77
101
  }
78
102
  /**
79
103
  * Print authentication success message to stderr.
80
104
  */
81
105
  export function printAuthSuccess(login, tenant, repoCount) {
82
- const parts = ["Pharaoh: authenticated"];
106
+ const parts = [" Authenticated"];
83
107
  if (login)
84
108
  parts.push(`as ${login}`);
85
109
  if (tenant)
package/dist/index.js CHANGED
@@ -12,9 +12,17 @@
12
12
  */
13
13
  import { pollForToken, printActivationPrompt, printAuthSuccess, requestDeviceCode, } from "./auth.js";
14
14
  import { deleteCredentials, isExpired, readCredentials, writeCredentials } from "./credentials.js";
15
- import { NPX_COMMAND, formatTtl, parseArgs, printLines, printUsage, resolveSseUrl, tokenToCredentials, } from "./helpers.js";
15
+ import { formatTtl, NPX_COMMAND, parseArgs, printLines, printUsage, resolveSseUrl, tokenToCredentials, } from "./helpers.js";
16
16
  import { runInstallSkills } from "./install-skills.js";
17
17
  import { startProxy, TenantSuspendedError, TokenExpiredError } from "./proxy.js";
18
+ const BANNER = [
19
+ "",
20
+ " ___ _ _ _ ___ _ ___ _ _ ",
21
+ " | _ \\| || | /_\\ | _ \\ /_\\ / _ \\| || |",
22
+ " | _/| __ |/ _ \\| / / _ \\| (_) | __ |",
23
+ " |_| |_||_/_/ \\_\\_|_\\/_/ \\_\\\\___/|_||_|",
24
+ "",
25
+ ].join("\n");
18
26
  async function main() {
19
27
  const args = process.argv.slice(2);
20
28
  if (args.includes("--help") || args.includes("-h")) {
@@ -45,11 +53,11 @@ async function main() {
45
53
  // Full setup every time: fresh auth → register MCP → install skills → done.
46
54
  // Running `npx @pharaoh-so/mcp` is the only command a user needs.
47
55
  if (isInteractive) {
56
+ process.stderr.write(`${BANNER}\n`);
48
57
  // Always re-authenticate for a fresh session
49
- printLines("Pharaoh: starting device authorization...");
50
58
  deleteCredentials();
51
59
  const deviceCode = await requestDeviceCode(server);
52
- printActivationPrompt(deviceCode.user_code, deviceCode.verification_uri);
60
+ printActivationPrompt(deviceCode.user_code, deviceCode.verification_uri, deviceCode.verification_uri_complete);
53
61
  const token = await pollForToken(server, deviceCode.device_code, deviceCode.interval);
54
62
  if (token.provisional) {
55
63
  printLines(`Pharaoh: provisional access — install the GitHub App to map your repos: ${token.install_url ?? ""}`);
@@ -58,12 +66,13 @@ async function main() {
58
66
  const newCreds = tokenToCredentials(token, sseUrl);
59
67
  writeCredentials(newCreds);
60
68
  printAuthSuccess(token.github_login ?? null, token.tenant_name ?? null, token.repos?.length ?? 0);
61
- // Register MCP server in Claude Code (remove stale + add fresh)
69
+ // Register MCP server + install skills (silent user doesn't need internals)
70
+ process.stderr.write(" Setting up...");
62
71
  const { runSetup } = await import("./setup.js");
63
- runSetup();
64
- // Install skills to all detected platforms
65
- runInstallSkills();
66
- printLines("", "Pharaoh is ready. Start a new Claude Code conversation and ask:", ' "What modules does this codebase have?"', "");
72
+ runSetup({ silent: true });
73
+ runInstallSkills(undefined, { silent: true });
74
+ process.stderr.write(" done.\n");
75
+ printLines("", "Pharaoh is ready. Try these in a new Claude Code conversation:", "", ' "What modules does this codebase have?"', ' "Search for functions related to authentication"', ' "What breaks if I change this file?"', "");
67
76
  process.exit(0);
68
77
  }
69
78
  // ── Proxy mode (Claude Code spawned us as a stdio MCP server) ──
@@ -30,4 +30,6 @@ export declare function mergeOpenClawConfig(home?: string): boolean;
30
30
  *
31
31
  * @param home - Home directory override (defaults to os.homedir()).
32
32
  */
33
- export declare function runInstallSkills(home?: string): void;
33
+ export declare function runInstallSkills(home?: string, options?: {
34
+ silent?: boolean;
35
+ }): void;
@@ -242,7 +242,8 @@ function configureClaudeCodePermissions(home = homedir()) {
242
242
  *
243
243
  * @param home - Home directory override (defaults to os.homedir()).
244
244
  */
245
- export function runInstallSkills(home = homedir()) {
245
+ export function runInstallSkills(home = homedir(), options) {
246
+ const silent = options?.silent ?? false;
246
247
  const hasClaudeCode = detectClaudeCode(home);
247
248
  const hasOpenClaw = detectOpenClaw(home);
248
249
  let installed = false;
@@ -251,21 +252,23 @@ export function runInstallSkills(home = homedir()) {
251
252
  const count = installClaudeCodePlugin(home);
252
253
  if (count >= 0) {
253
254
  const regResult = registerClaudeCodePlugin(home);
254
- const regMessages = {
255
- added: "Plugin registered in installed_plugins.json",
256
- updated: "Plugin version updated in installed_plugins.json",
257
- current: "Plugin already registered and up-to-date",
258
- error: "Could not update installed_plugins.json",
259
- };
260
255
  // Silently ensure Pharaoh tools auto-approve (no user prompt on first use)
261
256
  configureClaudeCodePermissions(home);
262
- process.stderr.write([
263
- `Pharaoh: installed ${count} skills as Claude Code plugin`,
264
- ` → ~/.claude/plugins/data/pharaoh/`,
265
- ` → ${regMessages[regResult]}`,
266
- "",
267
- ...(regResult === "added" ? ["Restart Claude Code to pick up the new skills.", ""] : []),
268
- ].join("\n"));
257
+ if (!silent) {
258
+ const regMessages = {
259
+ added: "Plugin registered in installed_plugins.json",
260
+ updated: "Plugin version updated in installed_plugins.json",
261
+ current: "Plugin already registered and up-to-date",
262
+ error: "Could not update installed_plugins.json",
263
+ };
264
+ process.stderr.write([
265
+ `Pharaoh: installed ${count} skills as Claude Code plugin`,
266
+ ` → ~/.claude/plugins/data/pharaoh/`,
267
+ ` → ${regMessages[regResult]}`,
268
+ "",
269
+ ...(regResult === "added" ? ["Restart Claude Code to pick up the new skills.", ""] : []),
270
+ ].join("\n"));
271
+ }
269
272
  installed = true;
270
273
  }
271
274
  }
@@ -273,20 +276,22 @@ export function runInstallSkills(home = homedir()) {
273
276
  if (hasOpenClaw) {
274
277
  const count = installSkills(home);
275
278
  const configUpdated = mergeOpenClawConfig(home);
276
- const configMsg = configUpdated
277
- ? "Pharaoh MCP server added to ~/.openclaw/openclaw.json"
278
- : "Pharaoh already present in ~/.openclaw/openclaw.json — skipped";
279
- process.stderr.write([
280
- `Pharaoh: installed ${count} skills to ~/.openclaw/skills/`,
281
- ` ${configMsg}`,
282
- "",
283
- "Restart OpenClaw to pick up the new skills.",
284
- "",
285
- ].join("\n"));
279
+ if (!silent) {
280
+ const configMsg = configUpdated
281
+ ? "Pharaoh MCP server added to ~/.openclaw/openclaw.json"
282
+ : "Pharaoh already present in ~/.openclaw/openclaw.json — skipped";
283
+ process.stderr.write([
284
+ `Pharaoh: installed ${count} skills to ~/.openclaw/skills/`,
285
+ ` → ${configMsg}`,
286
+ "",
287
+ "Restart OpenClaw to pick up the new skills.",
288
+ "",
289
+ ].join("\n"));
290
+ }
286
291
  installed = true;
287
292
  }
288
293
  // ── Neither detected ──
289
- if (!installed) {
294
+ if (!installed && !silent) {
290
295
  process.stderr.write([
291
296
  "Pharaoh: no supported skill platform detected.",
292
297
  "",
package/dist/setup.d.ts CHANGED
@@ -4,4 +4,6 @@
4
4
  *
5
5
  * @returns true if setup succeeded, false if Claude CLI not found.
6
6
  */
7
- export declare function runSetup(): boolean;
7
+ export declare function runSetup(options?: {
8
+ silent?: boolean;
9
+ }): boolean;
package/dist/setup.js CHANGED
@@ -46,12 +46,14 @@ function runClaude(args) {
46
46
  *
47
47
  * @returns true if setup succeeded, false if Claude CLI not found.
48
48
  */
49
- export function runSetup() {
49
+ export function runSetup(options) {
50
+ const silent = options?.silent ?? false;
50
51
  if (!hasClaude()) {
51
52
  printLines("Pharaoh: Claude Code CLI not found.", "", "Install Claude Code first: https://claude.ai/download", `Then re-run: ${NPX_COMMAND}`, "");
52
53
  return false;
53
54
  }
54
- printLines("Pharaoh: setting up...");
55
+ if (!silent)
56
+ printLines("Pharaoh: setting up...");
55
57
  // Step 1: Remove any stale entry (ignore failures — may not exist)
56
58
  runClaude(["mcp", "remove", "pharaoh"]);
57
59
  // Step 2: Register as global stdio MCP server
@@ -69,8 +71,7 @@ export function runSetup() {
69
71
  printLines("Pharaoh: failed to register MCP server.", `Try manually: ${NPX_COMMAND}`, "");
70
72
  return false;
71
73
  }
72
- printLines("Pharaoh: registered as global MCP server (scope: user)");
73
- // Step 3: Install skills (handled by caller via runInstallSkills)
74
- // We just report the MCP registration success here.
74
+ if (!silent)
75
+ printLines("Pharaoh: registered as global MCP server (scope: user)");
75
76
  return true;
76
77
  }
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",
4
+ "version": "0.3.5",
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",