phaedo-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/README.md +40 -29
- package/consult-runtime.js +2 -1
- package/fingerprint-source.js +20 -4
- package/lib/config-dir.js +43 -0
- package/package.json +2 -2
- package/pair.js +3 -2
- package/phone-source.js +4 -3
- package/server.js +2 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -22,12 +22,46 @@ when your phone is offline, is encrypted too.
|
|
|
22
22
|
- **the Phaedo phone app** — your full profile lives there. The app isn't open
|
|
23
23
|
to the public yet; [phaedo.so](https://phaedo.so) has a link to hear when it is.
|
|
24
24
|
- **your own Claude Code sessions** — no phone needed. Phaedo builds a starter
|
|
25
|
-
profile on this computer from how you work in Claude Code (see
|
|
26
|
-
2. **Node.js**, free from [nodejs.org](https://nodejs.org) (the LTS button)
|
|
25
|
+
profile on this computer from how you work in Claude Code (see *Install*).
|
|
26
|
+
2. **Node.js**, free from [nodejs.org](https://nodejs.org) (the LTS button), for
|
|
27
|
+
Claude Code and other apps. Claude Desktop brings its own.
|
|
27
28
|
|
|
28
29
|
With neither, there is no profile, and the server says so plainly rather than
|
|
29
30
|
guessing. You can still try Phaedo with a made-up example profile.
|
|
30
31
|
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
**Claude Code** (and Cowork) — run these two commands in Terminal:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
claude plugin marketplace add phaedo-labs/phaedo-mcp
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
claude plugin install phaedo@phaedo
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then start a new Claude Code session. That's all: Phaedo loads your profile at the
|
|
45
|
+
start of each session, checks with you before Claude merges, deploys or publishes,
|
|
46
|
+
and learns from how you work. Without the phone app, it builds a starter profile from
|
|
47
|
+
your own sessions (for example, that you want short answers without headers). It
|
|
48
|
+
starts thin and grows as you work. In Claude Code, type `/phaedo:status` to see how
|
|
49
|
+
far along it is, and `/phaedo:pair` to pair the phone app.
|
|
50
|
+
|
|
51
|
+
**Claude Desktop** — download
|
|
52
|
+
[phaedo.mcpb](https://github.com/phaedo-labs/phaedo-mcp/raw/main/desktop/phaedo.mcpb)
|
|
53
|
+
and double-click it. Claude Desktop shows what it installs and asks you to confirm.
|
|
54
|
+
Node.js comes with Claude Desktop, so nothing else is needed. Desktop uses the same
|
|
55
|
+
profile and pairing as the Claude Code plugin on this computer.
|
|
56
|
+
|
|
57
|
+
**Other apps** (Cursor and others) — see *Use your own profile in other apps* below,
|
|
58
|
+
or the manual setup under *Reference*.
|
|
59
|
+
|
|
60
|
+
What Phaedo reads stays on this computer: each Claude Code session is reduced to
|
|
61
|
+
counts (like "asked for a shorter answer, 4 times"), and the text itself is
|
|
62
|
+
discarded. If you pair the phone app later, your full profile replaces the starter
|
|
63
|
+
one and keeps what it learned.
|
|
64
|
+
|
|
31
65
|
## Try it with the example profile (no phone needed)
|
|
32
66
|
|
|
33
67
|
This loads a made-up person's profile, so you can see what Phaedo adds before you
|
|
@@ -66,32 +100,7 @@ example, delete the `env` line in Claude Desktop, or in Claude Code run
|
|
|
66
100
|
`claude mcp remove -s user phaedo` and add it again without `-e …`. The server
|
|
67
101
|
will then tell you there's no profile until you pair your phone.
|
|
68
102
|
|
|
69
|
-
##
|
|
70
|
-
|
|
71
|
-
If you use Claude Code, Phaedo can learn from how you work there and build a
|
|
72
|
-
starter profile on this computer — for example, that you want short answers
|
|
73
|
-
without headers. It starts thin and grows as you work.
|
|
74
|
-
|
|
75
|
-
1. **Get this folder** as in step 1 below (Code → Download ZIP, unzip, open the
|
|
76
|
-
`mcp` folder).
|
|
77
|
-
2. **Install the Claude Code hook.** In Terminal, from the `mcp` folder:
|
|
78
|
-
```bash
|
|
79
|
-
npm install
|
|
80
|
-
```
|
|
81
|
-
```bash
|
|
82
|
-
npm run setup:code-hook
|
|
83
|
-
```
|
|
84
|
-
This adds Phaedo to your AI apps and turns on three things in Claude Code:
|
|
85
|
-
loading your profile when a session starts, checking with you before it merges
|
|
86
|
-
or publishes, and learning from your sessions.
|
|
87
|
-
3. **Quit and reopen Claude Code**, then keep working. The first sessions build the
|
|
88
|
-
profile, and each later session picks up what's been learned.
|
|
89
|
-
|
|
90
|
-
What it reads stays on this computer: each session is reduced to counts (like "asked
|
|
91
|
-
for a shorter answer, 4 times"), and the text itself is discarded. If you pair the
|
|
92
|
-
phone app later, your full profile replaces the starter one and keeps what it learned.
|
|
93
|
-
|
|
94
|
-
## Use your own profile (with the phone app)
|
|
103
|
+
## Use your own profile in other apps (with the phone app)
|
|
95
104
|
|
|
96
105
|
1. **Get this folder.** On [the GitHub page](https://github.com/phaedo-labs/phaedo-mcp),
|
|
97
106
|
click **Code → Download ZIP**, unzip it, and open the `mcp` folder inside.
|
|
@@ -102,7 +111,9 @@ phone app later, your full profile replaces the starter one and keeps what it le
|
|
|
102
111
|
- Linux: run `bash installers/linux/install-phaedo-mcp.sh`.
|
|
103
112
|
|
|
104
113
|
It adds Phaedo to Claude Desktop, Cursor and Claude Code — keeping your other
|
|
105
|
-
settings, and backing each file up first — then starts pairing.
|
|
114
|
+
settings, and backing each file up first — then starts pairing. (In Claude Code
|
|
115
|
+
and Claude Desktop, the plugin and the `.mcpb` above are simpler; use one route
|
|
116
|
+
per app, not both.)
|
|
106
117
|
3. **Pair with your phone.** Open Phaedo on your phone. On the same Wi-Fi, approve
|
|
107
118
|
**"Phaedo MCP Server"** and check that the code matches the one in Terminal.
|
|
108
119
|
On a different network, tap **Pair with code** and type the code Terminal shows.
|
package/consult-runtime.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// policies or delegation signals its own way would be the third time.
|
|
8
8
|
|
|
9
9
|
import { join } from 'path';
|
|
10
|
+
import { configDir } from './lib/config-dir.js';
|
|
10
11
|
import { readFileSync, existsSync } from 'fs';
|
|
11
12
|
import { loadFingerprint, loadFingerprintPreferCache } from './fingerprint-source.js';
|
|
12
13
|
import { readServingClientId } from './phone-source.js';
|
|
@@ -24,7 +25,7 @@ export function buildConsultOpts(baseDir, env = process.env, { warn = (m) => pro
|
|
|
24
25
|
// the server (mcp/consult-policies.json — what policy-editor.html produces). Zero
|
|
25
26
|
// config: drop the file in and restart the client. (Portable vault policies in
|
|
26
27
|
// phaedo_consult_policies always apply too, regardless of this file.)
|
|
27
|
-
const policiesPath = env.PHAEDO_CONSULT_POLICIES || join(baseDir, 'consult-policies.json');
|
|
28
|
+
const policiesPath = env.PHAEDO_CONSULT_POLICIES || join(configDir(baseDir, env), 'consult-policies.json');
|
|
28
29
|
if (existsSync(policiesPath)) {
|
|
29
30
|
try { opts.policies = JSON.parse(readFileSync(policiesPath, 'utf8')); }
|
|
30
31
|
catch (e) { warn(`[phaedo-mcp] ignoring malformed consult policies (${policiesPath}): ${e.message}\n`); }
|
package/fingerprint-source.js
CHANGED
|
@@ -18,6 +18,7 @@ import { defaultStateDir, writeCache, readCache, clearCache } from './cache.js';
|
|
|
18
18
|
import { foldLocalCapture, isLocalCaptureOwner, localCaptureSessionCount } from './capture-local.js';
|
|
19
19
|
import { Capabilities, AuthoredSource } from './lib/phaedo-crypto.js';
|
|
20
20
|
import { PhaedoError } from './errors.js';
|
|
21
|
+
import { configDir } from './lib/config-dir.js';
|
|
21
22
|
|
|
22
23
|
// §4.1 protocol-identity normalization (consumer defense-in-depth, 2026-06-16).
|
|
23
24
|
// A stored fingerprint from a producer that predates identity stamping (or whose
|
|
@@ -95,7 +96,7 @@ function resolveSourcePath(baseDir) {
|
|
|
95
96
|
const env = process.env.PHAEDO_FINGERPRINT;
|
|
96
97
|
if (env === SAMPLE_KEYWORD) return SAMPLE_PATH;
|
|
97
98
|
if (env) return resolve(process.cwd(), env);
|
|
98
|
-
return resolve(baseDir, 'fingerprint.json');
|
|
99
|
+
return resolve(configDir(baseDir), 'fingerprint.json');
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
// Read by the assistant (a tool error) and, through it, by the person — so plain words, and
|
|
@@ -108,11 +109,26 @@ export const NO_PROFILE_MESSAGE =
|
|
|
108
109
|
'is installed (see the README). To try a made-up example profile instead, set ' +
|
|
109
110
|
'PHAEDO_FINGERPRINT=sample in this server\'s settings.';
|
|
110
111
|
|
|
112
|
+
// Inside the Claude plugin the Claude Code hook is already installed — the plugin is the hook —
|
|
113
|
+
// so telling the person to install it would send them looking for a step that doesn't exist.
|
|
114
|
+
// Claude Code exports CLAUDE_PLUGIN_ROOT to a plugin's MCP server and hooks, and to nothing else.
|
|
115
|
+
export const PLUGIN_NO_PROFILE_MESSAGE =
|
|
116
|
+
'No Phaedo profile is set up yet, so Phaedo has not changed how the assistant responds. It ' +
|
|
117
|
+
'starts one from your own Claude Code sessions on this computer: after a session or two, it ' +
|
|
118
|
+
'loads at the start of the next one. For your full profile, pair the Phaedo phone app with ' +
|
|
119
|
+
'/phaedo:pair (the app is not open to the public yet; https://phaedo.so to hear when it is).';
|
|
120
|
+
|
|
121
|
+
export function noProfileMessage(env = process.env) {
|
|
122
|
+
return env.CLAUDE_PLUGIN_ROOT ? PLUGIN_NO_PROFILE_MESSAGE : NO_PROFILE_MESSAGE;
|
|
123
|
+
}
|
|
124
|
+
|
|
111
125
|
// The same news for the server's log (Claude Desktop's MCP log shows stderr), one line.
|
|
112
126
|
const NO_PROFILE_LOG =
|
|
113
127
|
'no profile is set up on this computer, so Phaedo will not change how the assistant responds. ' +
|
|
114
128
|
'Pair the Phaedo phone app, install the Claude Code hook to start one from your own sessions, ' +
|
|
115
129
|
'or set PHAEDO_FINGERPRINT=sample to try the example.';
|
|
130
|
+
const PLUGIN_NO_PROFILE_LOG =
|
|
131
|
+
'no profile yet; one starts from your Claude Code sessions on this computer, or pair the phone app with /phaedo:pair.';
|
|
116
132
|
|
|
117
133
|
// STARTER PROFILE — the phone-less path.
|
|
118
134
|
//
|
|
@@ -172,7 +188,7 @@ export async function profileStatusLine(baseDir) {
|
|
|
172
188
|
if (env === SAMPLE_KEYWORD) return 'using the made-up example profile (PHAEDO_FINGERPRINT=sample).';
|
|
173
189
|
if (env) return null;
|
|
174
190
|
if (hasPairingRecord(baseDir)) return null;
|
|
175
|
-
if (existsSync(resolve(baseDir, 'fingerprint.json'))) return null;
|
|
191
|
+
if (existsSync(resolve(configDir(baseDir), 'fingerprint.json'))) return null;
|
|
176
192
|
const starter = await loadStarterProfile();
|
|
177
193
|
if (starter) {
|
|
178
194
|
const { learned, areas, sessions, stage } = await summarizeStarter(starter);
|
|
@@ -180,7 +196,7 @@ export async function profileStatusLine(baseDir) {
|
|
|
180
196
|
`${learned} thing${learned === 1 ? '' : 's'} learned across ${areas} area${areas === 1 ? '' : 's'}` +
|
|
181
197
|
`${sessions ? ` from ${sessions} session${sessions === 1 ? '' : 's'}` : ''} — ${stage}`;
|
|
182
198
|
}
|
|
183
|
-
return NO_PROFILE_LOG;
|
|
199
|
+
return process.env.CLAUDE_PLUGIN_ROOT ? PLUGIN_NO_PROFILE_LOG : NO_PROFILE_LOG;
|
|
184
200
|
}
|
|
185
201
|
|
|
186
202
|
// Returns the vault payload:
|
|
@@ -273,7 +289,7 @@ export async function loadFingerprint(baseDir) {
|
|
|
273
289
|
if (!process.env.PHAEDO_FINGERPRINT && err?.code === 'ENOENT') {
|
|
274
290
|
const starter = await loadStarterProfile();
|
|
275
291
|
if (starter) return starter;
|
|
276
|
-
throw new PhaedoError('decryption_failed',
|
|
292
|
+
throw new PhaedoError('decryption_failed', noProfileMessage());
|
|
277
293
|
}
|
|
278
294
|
throw new PhaedoError('decryption_failed', `Could not read the Phaedo profile at ${path} (${err?.code || err?.message || err}).`);
|
|
279
295
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Where this install keeps the subject's own config files: pairing.json, identity.json,
|
|
2
|
+
// consult-policies.json, escalation-policy.json and fingerprint.json.
|
|
3
|
+
//
|
|
4
|
+
// A clone or the npm package keeps them beside server.js, as it always has. A packaged install
|
|
5
|
+
// can't: a Claude Code plugin's folder is replaced on every update (`${CLAUDE_PLUGIN_ROOT}` is a
|
|
6
|
+
// per-version cache directory), and a Claude Desktop bundle's folder belongs to Claude Desktop.
|
|
7
|
+
// A pairing written there is lost on the next update. So both packages ship an
|
|
8
|
+
// `install-layout.json` marker beside server.js, and keep these files in the state dir
|
|
9
|
+
// (~/.phaedo-mcp, or PHAEDO_MCP_STATE_DIR) instead — the same folder for both, so pairing once
|
|
10
|
+
// from either covers the other.
|
|
11
|
+
//
|
|
12
|
+
// A file marker rather than an environment variable, because the plugin's hooks and its
|
|
13
|
+
// `pair.js` / `pull.js` run in processes that don't all get the plugin's environment (commands
|
|
14
|
+
// Claude runs through its Bash tool see no CLAUDE_PLUGIN_ROOT). The marker travels with the
|
|
15
|
+
// files. PHAEDO_CONFIG_DIR still overrides both.
|
|
16
|
+
|
|
17
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
18
|
+
import { homedir } from 'node:os';
|
|
19
|
+
import { join, resolve } from 'node:path';
|
|
20
|
+
|
|
21
|
+
export const LAYOUT_MARKER = 'install-layout.json';
|
|
22
|
+
|
|
23
|
+
const cache = new Map();
|
|
24
|
+
|
|
25
|
+
/** True when the package at `baseDir` is a packaged install (plugin or Desktop bundle). */
|
|
26
|
+
export function isPackagedInstall(baseDir) {
|
|
27
|
+
const key = resolve(baseDir);
|
|
28
|
+
if (!cache.has(key)) {
|
|
29
|
+
let packaged = false;
|
|
30
|
+
try {
|
|
31
|
+
const marker = join(key, LAYOUT_MARKER);
|
|
32
|
+
packaged = existsSync(marker) && JSON.parse(readFileSync(marker, 'utf8'))?.config === 'state-dir';
|
|
33
|
+
} catch { packaged = false; } // an unreadable marker → the old, beside-the-server layout
|
|
34
|
+
cache.set(key, packaged);
|
|
35
|
+
}
|
|
36
|
+
return cache.get(key);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function configDir(baseDir, env = process.env) {
|
|
40
|
+
if (env.PHAEDO_CONFIG_DIR) return resolve(env.PHAEDO_CONFIG_DIR);
|
|
41
|
+
if (isPackagedInstall(baseDir)) return env.PHAEDO_MCP_STATE_DIR || join(homedir(), '.phaedo-mcp');
|
|
42
|
+
return baseDir;
|
|
43
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "phaedo-mcp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"unsetup": "node install.mjs --uninstall",
|
|
93
93
|
"pair": "node pair.js",
|
|
94
94
|
"pull": "node pull.js",
|
|
95
|
-
"test": "node test-smoke.mjs && node test-no-profile.mjs && node test-phone-boundary.mjs && node test-cache.mjs && node test-pairing.mjs && node test-revoke.mjs && node test-install.mjs && node test-consult.mjs && node test-consult-policy.mjs && node test-amount-bands.mjs && node test-resolver-revision.mjs && node test-ship-actions.mjs && node test-ship-hook.mjs && node test-ship-readout.mjs && node test-action-binding.mjs && node test-autonomy-promotion.mjs && node test-ap2.mjs && node test-delegation-sync.mjs && node test-delegation-relay.mjs && node test-capture-sync.mjs && node test-capture-hook.mjs && node test-stop-capture.mjs && node test-capture-local.mjs && node test-capture-a11y.mjs && node test-vendored-capture.mjs && node test-escalation.mjs && node test-domain.mjs && node test-receipts.mjs && node test-receipts-sync.mjs && node test-agreement.mjs && node test-calibration.mjs && node test-identity.mjs && node test-signing.mjs && node test-sign-request.mjs && node test-signing-audit.mjs && node test-delegation-domain-fragmentation.mjs && node test-stale-authority.mjs && node test-config-home-fold.mjs && node test-precedence-and-ceiling.mjs && node test-package-closure.mjs",
|
|
95
|
+
"test": "node test-smoke.mjs && node test-no-profile.mjs && node test-phone-boundary.mjs && node test-cache.mjs && node test-pairing.mjs && node test-revoke.mjs && node test-install.mjs && node test-consult.mjs && node test-consult-policy.mjs && node test-amount-bands.mjs && node test-resolver-revision.mjs && node test-ship-actions.mjs && node test-ship-hook.mjs && node test-ship-readout.mjs && node test-action-binding.mjs && node test-autonomy-promotion.mjs && node test-ap2.mjs && node test-delegation-sync.mjs && node test-delegation-relay.mjs && node test-capture-sync.mjs && node test-capture-hook.mjs && node test-stop-capture.mjs && node test-capture-local.mjs && node test-capture-a11y.mjs && node test-vendored-capture.mjs && node test-escalation.mjs && node test-domain.mjs && node test-receipts.mjs && node test-receipts-sync.mjs && node test-agreement.mjs && node test-calibration.mjs && node test-identity.mjs && node test-signing.mjs && node test-sign-request.mjs && node test-signing-audit.mjs && node test-delegation-domain-fragmentation.mjs && node test-stale-authority.mjs && node test-config-home-fold.mjs && node test-precedence-and-ceiling.mjs && node test-package-closure.mjs && node test-plugin.mjs",
|
|
96
96
|
"receipts": "node receipts.js list",
|
|
97
97
|
"agreement": "node agreement.js",
|
|
98
98
|
"ship-readout": "node ship-readout.js",
|
package/pair.js
CHANGED
|
@@ -12,14 +12,15 @@
|
|
|
12
12
|
import { fileURLToPath } from 'url';
|
|
13
13
|
import { dirname, resolve } from 'path';
|
|
14
14
|
import { pair } from './pairing.js';
|
|
15
|
+
import { configDir } from './lib/config-dir.js';
|
|
15
16
|
|
|
16
17
|
const BASE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
17
18
|
const identityPath = process.env.PHAEDO_IDENTITY
|
|
18
19
|
? resolve(process.cwd(), process.env.PHAEDO_IDENTITY)
|
|
19
|
-
: resolve(BASE_DIR, 'identity.json');
|
|
20
|
+
: resolve(configDir(BASE_DIR), 'identity.json');
|
|
20
21
|
const pairingPath = process.env.PHAEDO_PAIRING
|
|
21
22
|
? resolve(process.cwd(), process.env.PHAEDO_PAIRING)
|
|
22
|
-
: resolve(BASE_DIR, 'pairing.json');
|
|
23
|
+
: resolve(configDir(BASE_DIR), 'pairing.json');
|
|
23
24
|
|
|
24
25
|
pair({ identityPath, pairingPath, log: (m) => process.stdout.write(m + '\n') })
|
|
25
26
|
.then(() => process.exit(0))
|
package/phone-source.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
// after the one-time pairing.
|
|
24
24
|
|
|
25
25
|
import { readFile } from 'fs/promises';
|
|
26
|
+
import { configDir } from './lib/config-dir.js';
|
|
26
27
|
import { existsSync, readFileSync } from 'fs';
|
|
27
28
|
import { resolve } from 'path';
|
|
28
29
|
import { Phaedo, b64uEnc, b64uDec, Capabilities } from './lib/phaedo-crypto.js';
|
|
@@ -55,7 +56,7 @@ const APPROVE_TIMEOUT_MS = 120000; // human-in-the-loop: /v1/authorize
|
|
|
55
56
|
export async function loadPairingRecord(baseDir) {
|
|
56
57
|
const path = process.env.PHAEDO_PAIRING
|
|
57
58
|
? resolve(process.cwd(), process.env.PHAEDO_PAIRING)
|
|
58
|
-
: resolve(baseDir, 'pairing.json');
|
|
59
|
+
: resolve(configDir(baseDir), 'pairing.json');
|
|
59
60
|
let raw;
|
|
60
61
|
try {
|
|
61
62
|
raw = await readFile(path, 'utf8');
|
|
@@ -78,7 +79,7 @@ export async function loadPairingRecord(baseDir) {
|
|
|
78
79
|
// True iff a pairing record is configured (→ use the phone source over local JSON).
|
|
79
80
|
export function hasPairingRecord(baseDir) {
|
|
80
81
|
if (process.env.PHAEDO_PAIRING) return true;
|
|
81
|
-
return existsSync(resolve(baseDir, 'pairing.json'));
|
|
82
|
+
return existsSync(resolve(configDir(baseDir), 'pairing.json'));
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
// This server's OWN paired-client identifier — the identity §10.5 `agents` scoping
|
|
@@ -96,7 +97,7 @@ export function hasPairingRecord(baseDir) {
|
|
|
96
97
|
export function readServingClientId(baseDir) {
|
|
97
98
|
const path = process.env.PHAEDO_PAIRING
|
|
98
99
|
? resolve(process.cwd(), process.env.PHAEDO_PAIRING)
|
|
99
|
-
: resolve(baseDir, 'pairing.json');
|
|
100
|
+
: resolve(configDir(baseDir), 'pairing.json');
|
|
100
101
|
try {
|
|
101
102
|
const id = JSON.parse(readFileSync(path, 'utf8'))?.client_id;
|
|
102
103
|
return typeof id === 'string' && id.trim() ? id.trim() : null;
|
package/server.js
CHANGED
|
@@ -17,6 +17,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
17
17
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
18
18
|
|
|
19
19
|
import { loadFingerprint, sourceOf, profileStatusLine } from './fingerprint-source.js';
|
|
20
|
+
import { configDir } from './lib/config-dir.js';
|
|
20
21
|
import { hasPairingRecord, loadPairingRecord } from './phone-source.js';
|
|
21
22
|
import { depositDelegationSuggestions } from './delegation-sync.js';
|
|
22
23
|
import { depositReceiptsDigest, drainAndApplyMarks } from './receipts-sync.js';
|
|
@@ -178,7 +179,7 @@ const ESCALATION_WINDOW_MS = (() => {
|
|
|
178
179
|
// over the stakes-gated default. Local file (PHAEDO_ESCALATION_POLICY, else
|
|
179
180
|
// mcp/escalation-policy.json beside the server) overrides; the portable vault policy
|
|
180
181
|
// (phaedo_fingerprint.escalation_policy) is merged in per-call (loadEscalationPolicy).
|
|
181
|
-
const ESCALATION_POLICY_PATH = process.env.PHAEDO_ESCALATION_POLICY || join(BASE_DIR, 'escalation-policy.json');
|
|
182
|
+
const ESCALATION_POLICY_PATH = process.env.PHAEDO_ESCALATION_POLICY || join(configDir(BASE_DIR), 'escalation-policy.json');
|
|
182
183
|
let ESCALATION_POLICY_FILE = null;
|
|
183
184
|
if (existsSync(ESCALATION_POLICY_PATH)) {
|
|
184
185
|
try { ESCALATION_POLICY_FILE = JSON.parse(readFileSync(ESCALATION_POLICY_PATH, 'utf8')); }
|
package/server.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "io.github.phaedo-labs/phaedo-mcp",
|
|
4
4
|
"description": "Make AI apps answer your way, check with you before acting, and get approvals on your phone.",
|
|
5
|
-
"version": "0.3.
|
|
5
|
+
"version": "0.3.5",
|
|
6
6
|
"repository": {
|
|
7
7
|
"url": "https://github.com/phaedo-labs/phaedo-mcp",
|
|
8
8
|
"source": "github",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "phaedo-mcp",
|
|
16
|
-
"version": "0.3.
|
|
16
|
+
"version": "0.3.5",
|
|
17
17
|
"transport": {
|
|
18
18
|
"type": "stdio"
|
|
19
19
|
}
|