projectionlab-mcp 1.1.0 → 1.2.0
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/HOW-IT-WORKS.md +2 -2
- package/README.md +1 -0
- package/REFERENCE.md +11 -2
- package/dist/bootstrap.js +5 -3
- package/dist/bootstrap.js.map +1 -1
- package/dist/bridge.d.ts +2 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +41 -6
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +10 -0
- package/dist/errors.js.map +1 -1
- package/dist/extensionBridge.d.ts +14 -1
- package/dist/extensionBridge.js +74 -20
- package/dist/extensionBridge.js.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.js +1 -1
- package/dist/largeOutput.js +25 -0
- package/dist/largeOutput.js.map +1 -1
- package/dist/logger.js +11 -4
- package/dist/logger.js.map +1 -1
- package/dist/pathPatch.js +5 -0
- package/dist/pathPatch.js.map +1 -1
- package/dist/plans.d.ts +45 -10
- package/dist/plans.js +139 -42
- package/dist/plans.js.map +1 -1
- package/dist/redact.d.ts +24 -0
- package/dist/redact.js +91 -0
- package/dist/redact.js.map +1 -1
- package/dist/resources/planData.js +3 -1
- package/dist/resources/planData.js.map +1 -1
- package/dist/resources/summary.js +4 -1
- package/dist/resources/summary.js.map +1 -1
- package/dist/snapshot.js +18 -11
- package/dist/snapshot.js.map +1 -1
- package/dist/tools/clonePlan.js +3 -3
- package/dist/tools/clonePlan.js.map +1 -1
- package/dist/tools/connection.js +21 -3
- package/dist/tools/connection.js.map +1 -1
- package/dist/tools/diff.js +7 -7
- package/dist/tools/diff.js.map +1 -1
- package/dist/tools/getPlan.js +4 -4
- package/dist/tools/getPlan.js.map +1 -1
- package/dist/tools/getPlanSection.js +5 -5
- package/dist/tools/getPlanSection.js.map +1 -1
- package/dist/tools/getPlanSettings.js +3 -3
- package/dist/tools/getPlanSettings.js.map +1 -1
- package/dist/tools/restore.js +18 -1
- package/dist/tools/restore.js.map +1 -1
- package/dist/tools/snapshots.js +17 -1
- package/dist/tools/snapshots.js.map +1 -1
- package/dist/tools/updatePlanEvent.js +63 -37
- package/dist/tools/updatePlanEvent.js.map +1 -1
- package/dist/tools/updatePlanSection.js +11 -6
- package/dist/tools/updatePlanSection.js.map +1 -1
- package/dist/tools/updatePlanSettings.js +3 -3
- package/dist/tools/updatePlanSettings.js.map +1 -1
- package/dist/types/wire.d.ts +27 -2
- package/dist/types/wire.js +6 -2
- package/dist/types/wire.js.map +1 -1
- package/dist/updateCheck.d.ts +22 -0
- package/dist/updateCheck.js +58 -0
- package/dist/updateCheck.js.map +1 -0
- package/dist/versionCompare.d.ts +5 -0
- package/dist/versionCompare.js +33 -0
- package/dist/versionCompare.js.map +1 -0
- package/dist/versionNotices.d.ts +9 -0
- package/dist/versionNotices.js +42 -0
- package/dist/versionNotices.js.map +1 -0
- package/package.json +1 -1
- package/skills/projectionlab/SKILL.md +55 -15
package/dist/types/wire.js
CHANGED
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Transport: JSON text frames over ws://127.0.0.1:<port>. The browser side is
|
|
9
9
|
* always the WebSocket client. The server accepts one active executor
|
|
10
|
-
* connection at a time
|
|
10
|
+
* connection at a time. The incumbent keeps it: a second authenticated
|
|
11
|
+
* executor is refused with 4004 and retries until the incumbent goes away,
|
|
12
|
+
* unless it requests takeover or repeats the incumbent's instanceId.
|
|
11
13
|
*/
|
|
12
14
|
/** Bumped on any breaking change to frame shapes or semantics. */
|
|
13
15
|
export const PROTOCOL_VERSION = 1;
|
|
@@ -21,7 +23,9 @@ export const CLOSE_CODES = {
|
|
|
21
23
|
AUTH_FAILED: 4001,
|
|
22
24
|
/** Protocol version mismatch. */
|
|
23
25
|
VERSION_MISMATCH: 4002,
|
|
24
|
-
/** A
|
|
26
|
+
/** A takeover replaced this executor; retry slowly, the next attempt normally gets BUSY. */
|
|
25
27
|
REPLACED: 4003,
|
|
28
|
+
/** Another executor holds the connection; retry later. */
|
|
29
|
+
BUSY: 4004,
|
|
26
30
|
};
|
|
27
31
|
//# sourceMappingURL=wire.js.map
|
package/dist/types/wire.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wire.js","sourceRoot":"","sources":["../../src/types/wire.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"wire.js","sourceRoot":"","sources":["../../src/types/wire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,kEAAkE;AAClE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAElC,kFAAkF;AAClF,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AAEpC,kFAAkF;AAClF,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAqG5C,gDAAgD;AAChD,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,8FAA8F;IAC9F,WAAW,EAAE,IAAI;IACjB,iCAAiC;IACjC,gBAAgB,EAAE,IAAI;IACtB,4FAA4F;IAC5F,QAAQ,EAAE,IAAI;IACd,0DAA0D;IAC1D,IAAI,EAAE,IAAI;CACF,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type FetchLike = (url: string, init: {
|
|
2
|
+
headers: Record<string, string>;
|
|
3
|
+
signal: AbortSignal;
|
|
4
|
+
}) => Promise<{
|
|
5
|
+
ok: boolean;
|
|
6
|
+
json(): Promise<unknown>;
|
|
7
|
+
}>;
|
|
8
|
+
/**
|
|
9
|
+
* Read the `version` field of the registry's latest-release document.
|
|
10
|
+
* Any failure (offline, timeout, non-200, bad JSON, missing or unparsable
|
|
11
|
+
* version) resolves to null; this never rejects.
|
|
12
|
+
*/
|
|
13
|
+
export declare function fetchLatestVersion(fetchImpl?: FetchLike, timeoutMs?: number): Promise<string | null>;
|
|
14
|
+
export interface UpdateCheck {
|
|
15
|
+
/** Start the lookup; without PL_UPDATE_CHECK=false and at most once. */
|
|
16
|
+
start(fetchImpl?: FetchLike): void;
|
|
17
|
+
/** The latest version found, or null while unknown or disabled. */
|
|
18
|
+
latest(): string | null;
|
|
19
|
+
}
|
|
20
|
+
/** A fresh holder; the module-level one is what the server uses. */
|
|
21
|
+
export declare function createUpdateCheck(): UpdateCheck;
|
|
22
|
+
export declare const updateCheck: UpdateCheck;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// One request per server process asks the npm registry for the latest
|
|
2
|
+
// published version of this package, so check_connection can tell the user
|
|
3
|
+
// an update is available. The request carries no user data: no query string,
|
|
4
|
+
// no custom headers beyond Accept, nothing from config or the plan.
|
|
5
|
+
// PL_UPDATE_CHECK=false turns the lookup off entirely.
|
|
6
|
+
import { config } from "./config.js";
|
|
7
|
+
import { log } from "./logger.js";
|
|
8
|
+
import { compareVersions } from "./versionCompare.js";
|
|
9
|
+
import { VERSION } from "./version.js";
|
|
10
|
+
const REGISTRY_URL = "https://registry.npmjs.org/projectionlab-mcp/latest";
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 3000;
|
|
12
|
+
/**
|
|
13
|
+
* Read the `version` field of the registry's latest-release document.
|
|
14
|
+
* Any failure (offline, timeout, non-200, bad JSON, missing or unparsable
|
|
15
|
+
* version) resolves to null; this never rejects.
|
|
16
|
+
*/
|
|
17
|
+
export async function fetchLatestVersion(fetchImpl = fetch, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
18
|
+
try {
|
|
19
|
+
const response = await fetchImpl(REGISTRY_URL, {
|
|
20
|
+
headers: { Accept: "application/json" },
|
|
21
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
22
|
+
});
|
|
23
|
+
if (!response.ok)
|
|
24
|
+
return null;
|
|
25
|
+
const body = (await response.json());
|
|
26
|
+
if (typeof body?.version !== "string")
|
|
27
|
+
return null;
|
|
28
|
+
// Unparsable versions are as good as unknown.
|
|
29
|
+
return compareVersions(body.version, "0.0.0") === null ? null : body.version;
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
log.debug("updateCheck", `update lookup failed: ${err}`);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** A fresh holder; the module-level one is what the server uses. */
|
|
37
|
+
export function createUpdateCheck() {
|
|
38
|
+
let latest = null;
|
|
39
|
+
let started = false;
|
|
40
|
+
return {
|
|
41
|
+
start(fetchImpl = fetch) {
|
|
42
|
+
if (started || !config.updateCheck)
|
|
43
|
+
return;
|
|
44
|
+
started = true;
|
|
45
|
+
void fetchLatestVersion(fetchImpl).then((found) => {
|
|
46
|
+
latest = found;
|
|
47
|
+
if (found && compareVersions(found, VERSION) === 1) {
|
|
48
|
+
log.info("updateCheck", `projectionlab-mcp ${found} is available (running ${VERSION})`);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
latest() {
|
|
53
|
+
return latest;
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export const updateCheck = createUpdateCheck();
|
|
58
|
+
//# sourceMappingURL=updateCheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updateCheck.js","sourceRoot":"","sources":["../src/updateCheck.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,2EAA2E;AAC3E,6EAA6E;AAC7E,oEAAoE;AACpE,uDAAuD;AAEvD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,MAAM,YAAY,GAAG,qDAAqD,CAAC;AAC3E,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAUhC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,YAAuB,KAA6B,EACpD,YAAoB,kBAAkB;IAEtC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,YAAY,EAAE;YAC7C,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;YACvC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;SACvC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA0B,CAAC;QAC9D,IAAI,OAAO,IAAI,EAAE,OAAO,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACnD,8CAA8C;QAC9C,OAAO,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;IAC/E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,aAAa,EAAE,yBAAyB,GAAG,EAAE,CAAC,CAAC;QACzD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AASD,oEAAoE;AACpE,MAAM,UAAU,iBAAiB;IAC/B,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,OAAO;QACL,KAAK,CAAC,YAAuB,KAA6B;YACxD,IAAI,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW;gBAAE,OAAO;YAC3C,OAAO,GAAG,IAAI,CAAC;YACf,KAAK,kBAAkB,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;gBAChD,MAAM,GAAG,KAAK,CAAC;gBACf,IAAI,KAAK,IAAI,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;oBACnD,GAAG,CAAC,IAAI,CACN,aAAa,EACb,qBAAqB,KAAK,0BAA0B,OAAO,GAAG,CAC/D,CAAC;gBACJ,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QACD,MAAM;YACJ,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,iBAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Release versions are major.minor.patch triples of non-negative integers.
|
|
2
|
+
// Anything else (a leading v, prerelease or build suffixes, extra segments)
|
|
3
|
+
// is unparsable, and callers treat unparsable as "say nothing".
|
|
4
|
+
function parse(version) {
|
|
5
|
+
if (typeof version !== "string")
|
|
6
|
+
return null;
|
|
7
|
+
const parts = version.split(".");
|
|
8
|
+
if (parts.length !== 3)
|
|
9
|
+
return null;
|
|
10
|
+
const numbers = [];
|
|
11
|
+
for (const part of parts) {
|
|
12
|
+
if (!/^(0|[1-9]\d*)$/.test(part))
|
|
13
|
+
return null;
|
|
14
|
+
numbers.push(Number(part));
|
|
15
|
+
}
|
|
16
|
+
return [numbers[0], numbers[1], numbers[2]];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Compare two `major.minor.patch` versions numerically. Returns -1, 0, or 1,
|
|
20
|
+
* or null when either input is not three dot-separated non-negative integers.
|
|
21
|
+
*/
|
|
22
|
+
export function compareVersions(a, b) {
|
|
23
|
+
const left = parse(a);
|
|
24
|
+
const right = parse(b);
|
|
25
|
+
if (!left || !right)
|
|
26
|
+
return null;
|
|
27
|
+
for (let i = 0; i < 3; i++) {
|
|
28
|
+
if (left[i] !== right[i])
|
|
29
|
+
return left[i] < right[i] ? -1 : 1;
|
|
30
|
+
}
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=versionCompare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versionCompare.js","sourceRoot":"","sources":["../src/versionCompare.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,4EAA4E;AAC5E,gEAAgE;AAEhE,SAAS,KAAK,CAAC,OAAe;IAC5B,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAC9C,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,CAAS,EAAE,CAAS;IAClD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACvB,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface VersionInfo {
|
|
2
|
+
/** The server's own release. */
|
|
3
|
+
server: string;
|
|
4
|
+
/** Connected executor, "extension/<v>" or "bootstrap/<v>", or null. */
|
|
5
|
+
executor: string | null;
|
|
6
|
+
/** Newest release on npm, or null when unknown or the check is off. */
|
|
7
|
+
latest: string | null;
|
|
8
|
+
}
|
|
9
|
+
export declare function versionNotices(info: VersionInfo): string[];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Plain-language update notices for projectionlab_check_connection. Each
|
|
2
|
+
// rule says nothing whenever a needed comparison is unparsable or a version
|
|
3
|
+
// is unknown; being out of date is never reported as a connection failure.
|
|
4
|
+
import { compareVersions } from "./versionCompare.js";
|
|
5
|
+
const SERVER_FIX = "To update it, fully quit and reopen the AI assistant's host so npx fetches the new release; " +
|
|
6
|
+
"if the old version is still reported afterwards, set the server args in the host " +
|
|
7
|
+
"configuration to -y projectionlab-mcp@latest.";
|
|
8
|
+
const EXTENSION_FIX = "To update it, download the zip for the browser from " +
|
|
9
|
+
"https://github.com/Backroads4Me/projectionlab-mcp/releases/latest, replace the unpacked " +
|
|
10
|
+
"extension folder with its contents, and reload the extension from the browser's extensions " +
|
|
11
|
+
"page. Settings are kept.";
|
|
12
|
+
export function versionNotices(info) {
|
|
13
|
+
const notices = [];
|
|
14
|
+
const serverOutOfDate = info.latest !== null && compareVersions(info.latest, info.server) === 1;
|
|
15
|
+
if (serverOutOfDate) {
|
|
16
|
+
notices.push(`The ProjectionLab MCP server is running version ${info.server}, but version ` +
|
|
17
|
+
`${info.latest} is available on npm. ${SERVER_FIX}`);
|
|
18
|
+
}
|
|
19
|
+
if (info.executor) {
|
|
20
|
+
const slash = info.executor.lastIndexOf("/");
|
|
21
|
+
const kind = slash === -1 ? "" : info.executor.slice(0, slash);
|
|
22
|
+
const version = slash === -1 ? "" : info.executor.slice(slash + 1);
|
|
23
|
+
const cmp = compareVersions(version, info.server);
|
|
24
|
+
if (kind === "extension" && cmp === -1) {
|
|
25
|
+
notices.push(`The ProjectionLab browser extension is version ${version}, older than the server ` +
|
|
26
|
+
`(${info.server}). ${EXTENSION_FIX}`);
|
|
27
|
+
}
|
|
28
|
+
else if (kind === "extension" && cmp === 1 && !serverOutOfDate) {
|
|
29
|
+
// Only worth saying when npm has nothing newer to offer as well.
|
|
30
|
+
notices.push(`The ProjectionLab MCP server is version ${info.server}, older than the browser ` +
|
|
31
|
+
`extension (${version}). ${SERVER_FIX}`);
|
|
32
|
+
}
|
|
33
|
+
else if (kind === "bootstrap" && cmp !== null && cmp !== 0) {
|
|
34
|
+
notices.push(`The bootstrap snippet in the ProjectionLab tab is version ${version}, pasted from a ` +
|
|
35
|
+
`different server release than the running server (${info.server}). Ask the user to ` +
|
|
36
|
+
`reload the tab, then call projectionlab_get_bootstrap and paste the fresh snippet ` +
|
|
37
|
+
`into the browser console.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return notices;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=versionNotices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versionNotices.js","sourceRoot":"","sources":["../src/versionNotices.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,4EAA4E;AAC5E,2EAA2E;AAE3E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,UAAU,GACd,8FAA8F;IAC9F,mFAAmF;IACnF,+CAA+C,CAAC;AAClD,MAAM,aAAa,GACjB,sDAAsD;IACtD,0FAA0F;IAC1F,6FAA6F;IAC7F,0BAA0B,CAAC;AAW7B,MAAM,UAAU,cAAc,CAAC,IAAiB;IAC9C,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,eAAe,GACnB,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,eAAe,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAE1E,IAAI,eAAe,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CACV,mDAAmD,IAAI,CAAC,MAAM,gBAAgB;YAC5E,GAAG,IAAI,CAAC,MAAM,yBAAyB,UAAU,EAAE,CACtD,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACnE,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAClD,IAAI,IAAI,KAAK,WAAW,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YACvC,OAAO,CAAC,IAAI,CACV,kDAAkD,OAAO,0BAA0B;gBACjF,IAAI,IAAI,CAAC,MAAM,MAAM,aAAa,EAAE,CACvC,CAAC;QACJ,CAAC;aAAM,IAAI,IAAI,KAAK,WAAW,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YACjE,iEAAiE;YACjE,OAAO,CAAC,IAAI,CACV,2CAA2C,IAAI,CAAC,MAAM,2BAA2B;gBAC/E,cAAc,OAAO,MAAM,UAAU,EAAE,CAC1C,CAAC;QACJ,CAAC;aAAM,IAAI,IAAI,KAAK,WAAW,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;YAC7D,OAAO,CAAC,IAAI,CACV,6DAA6D,OAAO,kBAAkB;gBACpF,qDAAqD,IAAI,CAAC,MAAM,qBAAqB;gBACrF,oFAAoF;gBACpF,2BAA2B,CAC9B,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/package.json
CHANGED
|
@@ -21,9 +21,22 @@ also serves this guide as the MCP resource `projectionlab://guide`.
|
|
|
21
21
|
## Start every session with a connection check
|
|
22
22
|
|
|
23
23
|
Call `projectionlab_check_connection` before any other ProjectionLab tool. It
|
|
24
|
-
returns `browser`, `pageLoaded`, `apiAvailable`, `apiKeyValid`, `errors
|
|
25
|
-
`writesEnabled`. When the first four flags are
|
|
26
|
-
the first failing item with the user, one step
|
|
24
|
+
returns `browser`, `pageLoaded`, `apiAvailable`, `apiKeyValid`, `errors`,
|
|
25
|
+
`writesEnabled`, `versions` and `notices`. When the first four flags are
|
|
26
|
+
`true`, proceed. Otherwise fix the first failing item with the user, one step
|
|
27
|
+
at a time, then check again.
|
|
28
|
+
|
|
29
|
+
`notices` are plain sentences about releases, never connection failures. When
|
|
30
|
+
the list is not empty, tell the user each one once, in plain words, then
|
|
31
|
+
carry on; do not repeat them later in the session and do not refuse to work
|
|
32
|
+
because of them. The two fixes: an out-of-date server is updated by fully
|
|
33
|
+
quitting and reopening the AI assistant's host so `npx` fetches the new
|
|
34
|
+
release (if it still reports the old version, set the server args to
|
|
35
|
+
`["-y", "projectionlab-mcp@latest"]`); an out-of-date extension is updated by
|
|
36
|
+
downloading the browser zip from
|
|
37
|
+
`https://github.com/Backroads4Me/projectionlab-mcp/releases/latest`,
|
|
38
|
+
replacing the unpacked extension folder with its contents, and reloading the
|
|
39
|
+
extension from the browser's extensions page (settings are kept).
|
|
27
40
|
|
|
28
41
|
| Failing item | What it means | What to do with the user |
|
|
29
42
|
|---|---|---|
|
|
@@ -66,6 +79,11 @@ Recommended, persistent: the **MCP Bridge for ProjectionLab** browser extension.
|
|
|
66
79
|
check.
|
|
67
80
|
4. Open https://app.projectionlab.com and log in.
|
|
68
81
|
|
|
82
|
+
If another browser is already connected to the server, the status reads
|
|
83
|
+
**Standing by** instead: that extension connects on its own when the other
|
|
84
|
+
browser disconnects, and pressing Save on the options page makes this
|
|
85
|
+
browser the active one now.
|
|
86
|
+
|
|
69
87
|
A Firefox user whose setup worked before only needs step 2 again after
|
|
70
88
|
restarting Firefox: load `manifest.json` from the same folder, and re-enter
|
|
71
89
|
the port and token if the options page is empty. Do not repeat the rest of
|
|
@@ -77,9 +95,12 @@ Console). It must be pasted again after every page reload.
|
|
|
77
95
|
|
|
78
96
|
## Reading the plan
|
|
79
97
|
|
|
80
|
-
Plans are addressed by `planIdentifier`: a plan name (case-insensitive
|
|
81
|
-
partial match
|
|
82
|
-
|
|
98
|
+
Plans are addressed by `planIdentifier`: a plan name (case-insensitive; a
|
|
99
|
+
partial match must identify exactly one plan, and an ambiguous one is refused
|
|
100
|
+
with the candidates listed) or an exact plan ID. The same rule covers
|
|
101
|
+
`eventIdentifier` and the `planA`/`planB` names of `projectionlab_diff`, so a
|
|
102
|
+
read and the following write can never target different items. Prefer the
|
|
103
|
+
narrowest read that answers the question.
|
|
83
104
|
|
|
84
105
|
| Question | Tool |
|
|
85
106
|
|---|---|
|
|
@@ -150,8 +171,8 @@ question.
|
|
|
150
171
|
what to use when the user wants to know whether two plans agree without sending
|
|
151
172
|
their contents to the model.
|
|
152
173
|
- Results over about 100,000 characters are written to
|
|
153
|
-
`~/.projectionlab-mcp/exports/` and returned as a file path
|
|
154
|
-
you need from the file.
|
|
174
|
+
`~/.projectionlab-mcp/exports/` and returned as a file path (pruned
|
|
175
|
+
automatically after 30 days). Read the parts you need from the file.
|
|
155
176
|
- `projectionlab_describe_plugin_api` lists the Plugin API methods the running
|
|
156
177
|
app exposes. Use it before concluding that a capability does not exist.
|
|
157
178
|
|
|
@@ -227,6 +248,9 @@ Change the plan only when the user explicitly asks for that change.
|
|
|
227
248
|
- Never replace a whole section when a targeted tool can make the change.
|
|
228
249
|
`projectionlab_update_plan_section` overwrites the complete array, so any
|
|
229
250
|
event missing from what you send is gone.
|
|
251
|
+
- Never write payloads carrying `[REDACTED]` under secret fields. Reads scrub
|
|
252
|
+
secrets with this placeholder, and write tools refuse payloads containing it
|
|
253
|
+
to prevent overwriting real credentials or keys with placeholder text.
|
|
230
254
|
|
|
231
255
|
**Writes are off by default.** `writesEnabled` in the connection check shows
|
|
232
256
|
the current mode. While it is `false`, every write tool, dry runs included,
|
|
@@ -276,12 +300,18 @@ values intact. Paths must already exist and keep their JSON type; get them from
|
|
|
276
300
|
the matching read tool (`get_plan_settings`, `get_plan_section` with
|
|
277
301
|
`listPaths: true`, or `get_current_finances` with `listPaths: true`). The
|
|
278
302
|
older `patch` (events) and `data` (accounts) inputs merge top-level fields
|
|
279
|
-
only;
|
|
303
|
+
only; existing fields keep their JSON type and are validated like
|
|
304
|
+
`pathChanges`, and new fields must be plain, non-secret names. Supply
|
|
305
|
+
exactly one of the patch inputs.
|
|
280
306
|
|
|
281
307
|
Milestones: ProjectionLab always keeps a Retirement and a Life Expectancy
|
|
282
308
|
milestone and disables deleting them in the app, so never remove either one.
|
|
283
309
|
`projectionlab_update_plan_section` replaces the whole `milestones` array, so
|
|
284
|
-
send both of them back in it.
|
|
310
|
+
send both of them back in it. Its `sectionData` must also match the section's
|
|
311
|
+
current container shape - an array for milestones, an object with an `events`
|
|
312
|
+
array for event sections, an object for `variables`, `montecarlo` and
|
|
313
|
+
`withdrawalStrategy` - or it is refused with `INVALID_INPUT`, dry runs
|
|
314
|
+
included.
|
|
285
315
|
|
|
286
316
|
To explore a what-if without touching the original, clone the plan first and
|
|
287
317
|
patch the copy.
|
|
@@ -294,6 +324,13 @@ refuses the last remaining plan. Its snapshot lets
|
|
|
294
324
|
`projectionlab_restore_progress` and `projectionlab_restore_settings` replace
|
|
295
325
|
an entire data area. Use them only when the user wants exactly that.
|
|
296
326
|
|
|
327
|
+
Reads hide secret values: a secret-named field reads back as `[REDACTED]`. A
|
|
328
|
+
`sectionData` or `restore_*` payload that still carries that sentinel under a
|
|
329
|
+
secret-named key is refused with `INVALID_INPUT`, dry runs included, naming the
|
|
330
|
+
paths. Type the real value or change single fields with
|
|
331
|
+
`projectionlab_update_plan_event` or `projectionlab_update_plan_settings`,
|
|
332
|
+
which patch by path and never round-trip hidden values.
|
|
333
|
+
|
|
297
334
|
### Change workflow
|
|
298
335
|
|
|
299
336
|
Copy this checklist and work through it for each change:
|
|
@@ -365,8 +402,10 @@ Retire at 55 plan."
|
|
|
365
402
|
2. `projectionlab_update_plan_event` with
|
|
366
403
|
`{"planIdentifier": "Retire at 55", "section": "expenses", "eventIdentifier": "Groceries", "pathChanges": {"amount": 600}}`
|
|
367
404
|
returns `dryRun: true`, the `planName`, `section`, the `event` label, and
|
|
368
|
-
`changes` such as `{"amount": {"before": 500, "after": 600}}
|
|
369
|
-
|
|
405
|
+
`changes` such as `{"amount": {"before": 500, "after": 600}}`; the `patch`
|
|
406
|
+
input reports the same before/after objects, and a field it adds reports
|
|
407
|
+
`"before": "[ABSENT]"`. Use `pathChanges`, not `patch`, so sibling fields
|
|
408
|
+
are kept. Show the before and
|
|
370
409
|
after values to the user in plain words: "In Retire at 55, Groceries goes
|
|
371
410
|
from $500 to $600 a month. Want me to make that change?"
|
|
372
411
|
3. After the user approves, repeat the call with `"confirm": true`. The reply
|
|
@@ -448,9 +487,10 @@ at 60."
|
|
|
448
487
|
and picks the same snapshot, so its field-level list of what differs from the
|
|
449
488
|
live data shows exactly what that rollback would undo. Show it with the
|
|
450
489
|
preview when the user asks what will come back.
|
|
451
|
-
4. After the user approves, repeat with `{"nBack": 1, "confirm": true}`.
|
|
452
|
-
|
|
453
|
-
|
|
490
|
+
4. After the user approves, repeat with `{"nBack": 1, "confirm": true}`. The
|
|
491
|
+
reply reports `verified: true` (or lists `unverifiedSections` if the
|
|
492
|
+
re-read differed). A confirmed rollback saves the current state first, so
|
|
493
|
+
another `nBack: 1` right after a rollback undoes that rollback.
|
|
454
494
|
5. `snapshotId` names one id from the list. `beforeTime` takes an ISO 8601
|
|
455
495
|
time carrying a `Z` or `±HH:MM` offset, such as `2026-11-08T15:30:00Z`,
|
|
456
496
|
and restores the latest snapshot strictly before it; a time with no
|