honeybook-mcp 0.4.3 → 0.4.4
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/bundle.js +582 -98
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/server.json +2 -2
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "HoneyBook client-portal tools for Claude Code",
|
|
10
|
-
"version": "0.4.
|
|
10
|
+
"version": "0.4.4"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"displayName": "HoneyBook",
|
|
16
16
|
"source": "./",
|
|
17
17
|
"description": "HoneyBook client-portal MCP for Claude — view wedding-vendor contracts and invoices via MCP",
|
|
18
|
-
"version": "0.4.
|
|
18
|
+
"version": "0.4.4",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Chall"
|
|
21
21
|
},
|
package/dist/bundle.js
CHANGED
|
@@ -3650,7 +3650,12 @@ var require_fast_uri = __commonJS({
|
|
|
3650
3650
|
}
|
|
3651
3651
|
function resolve(baseURI, relativeURI, options) {
|
|
3652
3652
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3653
|
-
const
|
|
3653
|
+
const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
|
|
3654
|
+
const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
|
|
3655
|
+
if (baseMalformed || relativeMalformed) {
|
|
3656
|
+
throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
|
|
3657
|
+
}
|
|
3658
|
+
const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
|
|
3654
3659
|
schemelessOptions.skipEscape = true;
|
|
3655
3660
|
return serialize(resolved, schemelessOptions);
|
|
3656
3661
|
}
|
|
@@ -3776,6 +3781,7 @@ var require_fast_uri = __commonJS({
|
|
|
3776
3781
|
}
|
|
3777
3782
|
var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
|
|
3778
3783
|
var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
|
|
3784
|
+
var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
|
|
3779
3785
|
function getParseError(parsed, matches) {
|
|
3780
3786
|
if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
|
|
3781
3787
|
return 'URI path must start with "/" when authority is present.';
|
|
@@ -3810,6 +3816,20 @@ var require_fast_uri = __commonJS({
|
|
|
3810
3816
|
parsed.error = "URI authority must not contain a literal backslash.";
|
|
3811
3817
|
malformedAuthorityOrPort = true;
|
|
3812
3818
|
}
|
|
3819
|
+
const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
|
|
3820
|
+
if (introducerMatch !== null) {
|
|
3821
|
+
const region = introducerMatch[1];
|
|
3822
|
+
const normalizedRegion = region.replace(/[\t\n\r]/g, "");
|
|
3823
|
+
if (normalizedRegion.length >= 2) {
|
|
3824
|
+
if (normalizedRegion.slice(0, 2) !== "//") {
|
|
3825
|
+
parsed.error = parsed.error || "URI authority must not contain a literal backslash.";
|
|
3826
|
+
malformedAuthorityOrPort = true;
|
|
3827
|
+
} else if (region.length !== normalizedRegion.length) {
|
|
3828
|
+
parsed.error = parsed.error || "URI authority introducer must not contain whitespace.";
|
|
3829
|
+
malformedAuthorityOrPort = true;
|
|
3830
|
+
}
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3813
3833
|
const matches = uri.match(URI_PARSE);
|
|
3814
3834
|
if (matches) {
|
|
3815
3835
|
parsed.scheme = matches[1];
|
|
@@ -10572,7 +10592,7 @@ var require_websocket_server = __commonJS({
|
|
|
10572
10592
|
});
|
|
10573
10593
|
|
|
10574
10594
|
// src/index.ts
|
|
10575
|
-
import { dirname as dirname2, join as
|
|
10595
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
10576
10596
|
import { fileURLToPath } from "url";
|
|
10577
10597
|
|
|
10578
10598
|
// node_modules/zod/v3/helpers/util.js
|
|
@@ -26728,17 +26748,33 @@ function normalizeObjectSchema(schema) {
|
|
|
26728
26748
|
}
|
|
26729
26749
|
return void 0;
|
|
26730
26750
|
}
|
|
26751
|
+
function getDotPath(path) {
|
|
26752
|
+
if (path.length === 0) {
|
|
26753
|
+
return "object root";
|
|
26754
|
+
}
|
|
26755
|
+
return path.reduce((acc, seg, index) => {
|
|
26756
|
+
if (index === 0) {
|
|
26757
|
+
return String(seg);
|
|
26758
|
+
}
|
|
26759
|
+
if (typeof seg === "number") {
|
|
26760
|
+
return `${acc}[${seg}]`;
|
|
26761
|
+
}
|
|
26762
|
+
return `${acc}.${seg}`;
|
|
26763
|
+
}, "");
|
|
26764
|
+
}
|
|
26731
26765
|
function getParseErrorMessage(error51) {
|
|
26732
26766
|
if (error51 && typeof error51 === "object") {
|
|
26767
|
+
if ("issues" in error51 && Array.isArray(error51.issues) && error51.issues.length > 0) {
|
|
26768
|
+
return error51.issues.map((i) => {
|
|
26769
|
+
if (!i.path?.length) {
|
|
26770
|
+
return i.message;
|
|
26771
|
+
}
|
|
26772
|
+
return `${i.message} at ${getDotPath(i.path)}`;
|
|
26773
|
+
}).join("\n");
|
|
26774
|
+
}
|
|
26733
26775
|
if ("message" in error51 && typeof error51.message === "string") {
|
|
26734
26776
|
return error51.message;
|
|
26735
26777
|
}
|
|
26736
|
-
if ("issues" in error51 && Array.isArray(error51.issues) && error51.issues.length > 0) {
|
|
26737
|
-
const firstIssue = error51.issues[0];
|
|
26738
|
-
if (firstIssue && typeof firstIssue === "object" && "message" in firstIssue) {
|
|
26739
|
-
return String(firstIssue.message);
|
|
26740
|
-
}
|
|
26741
|
-
}
|
|
26742
26778
|
try {
|
|
26743
26779
|
return JSON.stringify(error51);
|
|
26744
26780
|
} catch {
|
|
@@ -33353,16 +33389,7 @@ var Server = class extends Protocol {
|
|
|
33353
33389
|
if (!methodSchema) {
|
|
33354
33390
|
throw new Error("Schema is missing a method literal");
|
|
33355
33391
|
}
|
|
33356
|
-
|
|
33357
|
-
if (isZ4Schema(methodSchema)) {
|
|
33358
|
-
const v4Schema = methodSchema;
|
|
33359
|
-
const v4Def = v4Schema._zod?.def;
|
|
33360
|
-
methodValue = v4Def?.value ?? v4Schema.value;
|
|
33361
|
-
} else {
|
|
33362
|
-
const v3Schema = methodSchema;
|
|
33363
|
-
const legacyDef = v3Schema._def;
|
|
33364
|
-
methodValue = legacyDef?.value ?? v3Schema.value;
|
|
33365
|
-
}
|
|
33392
|
+
const methodValue = getLiteralValue(methodSchema);
|
|
33366
33393
|
if (typeof methodValue !== "string") {
|
|
33367
33394
|
throw new Error("Schema method literal must be a string");
|
|
33368
33395
|
}
|
|
@@ -34550,8 +34577,17 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
34550
34577
|
import process3 from "node:process";
|
|
34551
34578
|
|
|
34552
34579
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
|
|
34580
|
+
var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
|
|
34553
34581
|
var ReadBuffer = class {
|
|
34582
|
+
constructor(options) {
|
|
34583
|
+
this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
|
|
34584
|
+
}
|
|
34554
34585
|
append(chunk2) {
|
|
34586
|
+
const newSize = (this._buffer?.length ?? 0) + chunk2.length;
|
|
34587
|
+
if (newSize > this._maxBufferSize) {
|
|
34588
|
+
this.clear();
|
|
34589
|
+
throw new Error(`ReadBuffer exceeded maximum size of ${this._maxBufferSize} bytes`);
|
|
34590
|
+
}
|
|
34555
34591
|
this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk2]) : chunk2;
|
|
34556
34592
|
}
|
|
34557
34593
|
readMessage() {
|
|
@@ -34579,18 +34615,24 @@ function serializeMessage(message) {
|
|
|
34579
34615
|
|
|
34580
34616
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
34581
34617
|
var StdioServerTransport = class {
|
|
34582
|
-
constructor(_stdin = process3.stdin, _stdout = process3.stdout) {
|
|
34618
|
+
constructor(_stdin = process3.stdin, _stdout = process3.stdout, options) {
|
|
34583
34619
|
this._stdin = _stdin;
|
|
34584
34620
|
this._stdout = _stdout;
|
|
34585
|
-
this._readBuffer = new ReadBuffer();
|
|
34586
34621
|
this._started = false;
|
|
34587
34622
|
this._ondata = (chunk2) => {
|
|
34588
|
-
|
|
34589
|
-
|
|
34623
|
+
try {
|
|
34624
|
+
this._readBuffer.append(chunk2);
|
|
34625
|
+
this.processReadBuffer();
|
|
34626
|
+
} catch (error51) {
|
|
34627
|
+
this.onerror?.(error51);
|
|
34628
|
+
this.close().catch(() => {
|
|
34629
|
+
});
|
|
34630
|
+
}
|
|
34590
34631
|
};
|
|
34591
34632
|
this._onerror = (error51) => {
|
|
34592
34633
|
this.onerror?.(error51);
|
|
34593
34634
|
};
|
|
34635
|
+
this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
|
|
34594
34636
|
}
|
|
34595
34637
|
/**
|
|
34596
34638
|
* Starts listening for messages on stdin.
|
|
@@ -35058,8 +35100,15 @@ async function getActiveClient(origin) {
|
|
|
35058
35100
|
}
|
|
35059
35101
|
|
|
35060
35102
|
// node_modules/@fetchproxy/protocol/dist/frames.js
|
|
35061
|
-
var PROTOCOL_VERSION =
|
|
35103
|
+
var PROTOCOL_VERSION = 3;
|
|
35062
35104
|
var HKDF_SESSION_INFO = "fetchproxy/1.0.0/session";
|
|
35105
|
+
function readySignaturePayload(mcpHelloNonce, extHelloNonce, extensionSessionPub) {
|
|
35106
|
+
const out = new Uint8Array(mcpHelloNonce.length + extHelloNonce.length + extensionSessionPub.length);
|
|
35107
|
+
out.set(mcpHelloNonce, 0);
|
|
35108
|
+
out.set(extHelloNonce, mcpHelloNonce.length);
|
|
35109
|
+
out.set(extensionSessionPub, mcpHelloNonce.length + extHelloNonce.length);
|
|
35110
|
+
return out;
|
|
35111
|
+
}
|
|
35063
35112
|
var KNOWN_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
35064
35113
|
"fetch",
|
|
35065
35114
|
"read_cookies",
|
|
@@ -35070,7 +35119,8 @@ var KNOWN_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
|
35070
35119
|
"read_indexed_db",
|
|
35071
35120
|
"read_dom",
|
|
35072
35121
|
"download",
|
|
35073
|
-
"graphql"
|
|
35122
|
+
"graphql",
|
|
35123
|
+
"write_cookies"
|
|
35074
35124
|
]);
|
|
35075
35125
|
|
|
35076
35126
|
// node_modules/@fetchproxy/protocol/dist/mcp-id.js
|
|
@@ -35181,6 +35231,15 @@ function assertHttpUrl(x, label) {
|
|
|
35181
35231
|
throw new ProtocolError(`${label}: must be http(s), got ${u.protocol}`);
|
|
35182
35232
|
}
|
|
35183
35233
|
}
|
|
35234
|
+
function assertCookiePath(x, label) {
|
|
35235
|
+
assertString(x, label);
|
|
35236
|
+
if (!x.startsWith("/") || x.startsWith("//")) {
|
|
35237
|
+
throw new ProtocolError(`${label}: must be an absolute path like "/campus"`);
|
|
35238
|
+
}
|
|
35239
|
+
if (x.includes("?") || x.includes("#") || x.includes("\\")) {
|
|
35240
|
+
throw new ProtocolError(`${label}: must not contain a query, fragment, or backslash`);
|
|
35241
|
+
}
|
|
35242
|
+
}
|
|
35184
35243
|
function assertHttpsOriginOnly(x, label) {
|
|
35185
35244
|
assertString(x, label);
|
|
35186
35245
|
let u;
|
|
@@ -35625,13 +35684,43 @@ function validateInnerRequest(raw) {
|
|
|
35625
35684
|
}
|
|
35626
35685
|
assertHttpsOriginOnly(raw.init.origin, "inner.init.origin");
|
|
35627
35686
|
assertNonEmptyKeyArray(raw.init.keys, "inner.init.keys");
|
|
35687
|
+
if (raw.init.path !== void 0)
|
|
35688
|
+
assertCookiePath(raw.init.path, "inner.init.path");
|
|
35628
35689
|
for (const k of Object.keys(raw.init)) {
|
|
35629
|
-
if (k !== "origin" && k !== "keys") {
|
|
35690
|
+
if (k !== "origin" && k !== "keys" && k !== "path") {
|
|
35630
35691
|
throw new ProtocolError(`inner.init: unexpected field ${JSON.stringify(k)} on read_cookies`);
|
|
35631
35692
|
}
|
|
35632
35693
|
}
|
|
35633
35694
|
return raw;
|
|
35634
35695
|
}
|
|
35696
|
+
if (raw.op === "write_cookies") {
|
|
35697
|
+
assertObject(raw.init, "inner.init");
|
|
35698
|
+
assertHttpsOriginOnly(raw.init.origin, "inner.init.origin");
|
|
35699
|
+
if (!Array.isArray(raw.init.cookies) || raw.init.cookies.length === 0) {
|
|
35700
|
+
throw new ProtocolError("inner.init.cookies: must be a non-empty array");
|
|
35701
|
+
}
|
|
35702
|
+
for (const [i, entry] of raw.init.cookies.entries()) {
|
|
35703
|
+
assertObject(entry, `inner.init.cookies[${i}]`);
|
|
35704
|
+
assertString(entry.name, `inner.init.cookies[${i}].name`);
|
|
35705
|
+
if (!SCOPE_KEY_RE.test(entry.name)) {
|
|
35706
|
+
throw new ProtocolError(`inner.init.cookies[${i}].name: invalid key ${JSON.stringify(entry.name)}`);
|
|
35707
|
+
}
|
|
35708
|
+
assertString(entry.value, `inner.init.cookies[${i}].value`);
|
|
35709
|
+
for (const k of Object.keys(entry)) {
|
|
35710
|
+
if (k !== "name" && k !== "value") {
|
|
35711
|
+
throw new ProtocolError(`inner.init.cookies[${i}]: unexpected field ${JSON.stringify(k)}`);
|
|
35712
|
+
}
|
|
35713
|
+
}
|
|
35714
|
+
}
|
|
35715
|
+
if (raw.init.path !== void 0)
|
|
35716
|
+
assertCookiePath(raw.init.path, "inner.init.path");
|
|
35717
|
+
for (const k of Object.keys(raw.init)) {
|
|
35718
|
+
if (k !== "origin" && k !== "cookies" && k !== "path") {
|
|
35719
|
+
throw new ProtocolError(`inner.init: unexpected field ${JSON.stringify(k)} on write_cookies`);
|
|
35720
|
+
}
|
|
35721
|
+
}
|
|
35722
|
+
return raw;
|
|
35723
|
+
}
|
|
35635
35724
|
if (raw.op === "read_local_storage" || raw.op === "read_session_storage") {
|
|
35636
35725
|
assertObject(raw.init, "inner.init");
|
|
35637
35726
|
if (raw.init.origin === void 0) {
|
|
@@ -35818,7 +35907,7 @@ function validateInnerRequest(raw) {
|
|
|
35818
35907
|
}
|
|
35819
35908
|
return raw;
|
|
35820
35909
|
}
|
|
35821
|
-
throw new ProtocolError(`inner.op: must be one of "fetch", "read_cookies", "read_local_storage", "read_session_storage", "capture_request_header", "capture_redirect", "read_indexed_db", "read_dom", "download", "graphql_query"; got ${JSON.stringify(raw.op)}`);
|
|
35910
|
+
throw new ProtocolError(`inner.op: must be one of "fetch", "read_cookies", "read_local_storage", "read_session_storage", "capture_request_header", "capture_redirect", "read_indexed_db", "read_dom", "download", "graphql_query", "write_cookies"; got ${JSON.stringify(raw.op)}`);
|
|
35822
35911
|
}
|
|
35823
35912
|
function assertNonEmptyKeyArray(value, label) {
|
|
35824
35913
|
if (!Array.isArray(value)) {
|
|
@@ -35879,6 +35968,18 @@ function validateInnerResponse(raw) {
|
|
|
35879
35968
|
}
|
|
35880
35969
|
return raw;
|
|
35881
35970
|
}
|
|
35971
|
+
if (op === "write_cookies") {
|
|
35972
|
+
if (raw.written === void 0) {
|
|
35973
|
+
throw new ProtocolError("inner.written: missing on write_cookies response");
|
|
35974
|
+
}
|
|
35975
|
+
if (!Array.isArray(raw.written)) {
|
|
35976
|
+
throw new ProtocolError("inner.written: must be an array");
|
|
35977
|
+
}
|
|
35978
|
+
for (const [i, name] of raw.written.entries()) {
|
|
35979
|
+
assertString(name, `inner.written[${i}]`);
|
|
35980
|
+
}
|
|
35981
|
+
return raw;
|
|
35982
|
+
}
|
|
35882
35983
|
if (op === "read_local_storage" || op === "read_session_storage") {
|
|
35883
35984
|
if (raw.values === void 0) {
|
|
35884
35985
|
throw new ProtocolError(`inner.values: missing on ${String(op)} response`);
|
|
@@ -36356,6 +36457,148 @@ async function awaitSessionReady(ready, opts) {
|
|
|
36356
36457
|
}
|
|
36357
36458
|
}
|
|
36358
36459
|
|
|
36460
|
+
// node_modules/@fetchproxy/server/dist/extension-trust.js
|
|
36461
|
+
import { readFile as readFile2, writeFile as writeFile2, rename, unlink, mkdir as mkdir2, chmod as chmod2 } from "node:fs/promises";
|
|
36462
|
+
import { join as join3 } from "node:path";
|
|
36463
|
+
|
|
36464
|
+
// node_modules/@fetchproxy/server/dist/identity.js
|
|
36465
|
+
import { readFile, writeFile, mkdir, chmod } from "node:fs/promises";
|
|
36466
|
+
import { join as join2 } from "node:path";
|
|
36467
|
+
import { homedir as homedir2 } from "node:os";
|
|
36468
|
+
var SAFE_PLAIN = /^[A-Za-z0-9._-]+$/;
|
|
36469
|
+
var SAFE_SCOPED = /^@[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|
|
36470
|
+
function defaultIdentityDir() {
|
|
36471
|
+
return join2(homedir2(), ".fetchproxy", "identity");
|
|
36472
|
+
}
|
|
36473
|
+
function safeIdentityFileBase(serverName) {
|
|
36474
|
+
if (!serverName || serverName === ".." || serverName.includes("..") || !SAFE_PLAIN.test(serverName) && !SAFE_SCOPED.test(serverName)) {
|
|
36475
|
+
throw new Error(`unsafe serverName for identity file: ${JSON.stringify(serverName)}`);
|
|
36476
|
+
}
|
|
36477
|
+
return serverName.replace(/\//g, "_");
|
|
36478
|
+
}
|
|
36479
|
+
async function loadOrCreateIdentity(serverName, dir = defaultIdentityDir()) {
|
|
36480
|
+
const safeFile = safeIdentityFileBase(serverName);
|
|
36481
|
+
const path = join2(dir, `${safeFile}.json`);
|
|
36482
|
+
await mkdir(dir, { recursive: true, mode: 448 });
|
|
36483
|
+
try {
|
|
36484
|
+
const raw = await readFile(path, "utf8");
|
|
36485
|
+
const j2 = JSON.parse(raw);
|
|
36486
|
+
return {
|
|
36487
|
+
x25519Priv: fromB64(j2.x25519Priv),
|
|
36488
|
+
x25519Pub: fromB64(j2.x25519Pub),
|
|
36489
|
+
ed25519Priv: fromB64(j2.ed25519Priv),
|
|
36490
|
+
ed25519Pub: fromB64(j2.ed25519Pub),
|
|
36491
|
+
createdAt: j2.createdAt
|
|
36492
|
+
};
|
|
36493
|
+
} catch (e) {
|
|
36494
|
+
if (e.code !== "ENOENT")
|
|
36495
|
+
throw e;
|
|
36496
|
+
}
|
|
36497
|
+
const x = await generateX25519();
|
|
36498
|
+
const ed = await generateEd25519();
|
|
36499
|
+
const id = {
|
|
36500
|
+
x25519Priv: x.privateKey,
|
|
36501
|
+
x25519Pub: x.publicKey,
|
|
36502
|
+
ed25519Priv: ed.privateKey,
|
|
36503
|
+
ed25519Pub: ed.publicKey,
|
|
36504
|
+
createdAt: Date.now()
|
|
36505
|
+
};
|
|
36506
|
+
const j = {
|
|
36507
|
+
x25519Priv: toB64(id.x25519Priv),
|
|
36508
|
+
x25519Pub: toB64(id.x25519Pub),
|
|
36509
|
+
ed25519Priv: toB64(id.ed25519Priv),
|
|
36510
|
+
ed25519Pub: toB64(id.ed25519Pub),
|
|
36511
|
+
createdAt: id.createdAt
|
|
36512
|
+
};
|
|
36513
|
+
await writeFile(path, JSON.stringify(j, null, 2), { mode: 384 });
|
|
36514
|
+
await chmod(path, 384);
|
|
36515
|
+
return id;
|
|
36516
|
+
}
|
|
36517
|
+
|
|
36518
|
+
// node_modules/@fetchproxy/server/dist/extension-trust.js
|
|
36519
|
+
function fileExtensionTrust(args) {
|
|
36520
|
+
return {
|
|
36521
|
+
allowNew: args.allowNew,
|
|
36522
|
+
location: extensionTrustPath(args.serverName, args.dir ?? defaultIdentityDir()),
|
|
36523
|
+
read: () => readExtensionPin(args.serverName, args.dir ?? defaultIdentityDir()),
|
|
36524
|
+
write: (pin) => writeExtensionPin(args.serverName, pin, args.dir ?? defaultIdentityDir())
|
|
36525
|
+
};
|
|
36526
|
+
}
|
|
36527
|
+
var TRUST_NEW_EXTENSION_ENV = "FETCHPROXY_TRUST_NEW_EXTENSION";
|
|
36528
|
+
function allowNewExtensionIdentity(explicit, env = process.env) {
|
|
36529
|
+
if (explicit !== void 0)
|
|
36530
|
+
return explicit;
|
|
36531
|
+
return env[TRUST_NEW_EXTENSION_ENV] === "1";
|
|
36532
|
+
}
|
|
36533
|
+
function decideExtensionTrust(args) {
|
|
36534
|
+
const { pin, hello, allowNew, serverName } = args;
|
|
36535
|
+
if (!pin)
|
|
36536
|
+
return { decision: "first-use" };
|
|
36537
|
+
if (pin.identityX25519Pub === hello.identityX25519Pub && pin.identityEd25519Pub === hello.identityEd25519Pub) {
|
|
36538
|
+
return { decision: "pinned" };
|
|
36539
|
+
}
|
|
36540
|
+
const trustPath = args.location ?? extensionTrustPathHint(serverName);
|
|
36541
|
+
if (allowNew) {
|
|
36542
|
+
return {
|
|
36543
|
+
decision: "replace",
|
|
36544
|
+
message: `[fetchproxy] ${serverName}: accepting a NEW extension identity because ${TRUST_NEW_EXTENSION_ENV}=1 \u2014 re-pinning. Unset it once the browser you expect is connected.`
|
|
36545
|
+
};
|
|
36546
|
+
}
|
|
36547
|
+
return {
|
|
36548
|
+
decision: "refused",
|
|
36549
|
+
message: `[fetchproxy] ${serverName}: refusing an extension whose identity is not the one this MCP paired with. If you re-installed the extension or moved to another browser, re-pair deliberately: run this MCP once with ${TRUST_NEW_EXTENSION_ENV}=1, or delete ${trustPath}. If you did neither, something else is answering as your browser.`
|
|
36550
|
+
};
|
|
36551
|
+
}
|
|
36552
|
+
function extensionTrustPath(serverName, dir = defaultIdentityDir()) {
|
|
36553
|
+
return join3(dir, `${safeIdentityFileBase(serverName)}.extension-trust.json`);
|
|
36554
|
+
}
|
|
36555
|
+
function extensionTrustPathHint(serverName) {
|
|
36556
|
+
try {
|
|
36557
|
+
return extensionTrustPath(serverName);
|
|
36558
|
+
} catch {
|
|
36559
|
+
return join3(defaultIdentityDir(), "<server-name>.extension-trust.json");
|
|
36560
|
+
}
|
|
36561
|
+
}
|
|
36562
|
+
function isPin(x) {
|
|
36563
|
+
if (!x || typeof x !== "object")
|
|
36564
|
+
return false;
|
|
36565
|
+
const r = x;
|
|
36566
|
+
return typeof r.identityX25519Pub === "string" && typeof r.identityEd25519Pub === "string" && typeof r.pinnedAt === "number";
|
|
36567
|
+
}
|
|
36568
|
+
async function readExtensionPin(serverName, dir = defaultIdentityDir()) {
|
|
36569
|
+
const path = extensionTrustPath(serverName, dir);
|
|
36570
|
+
let raw;
|
|
36571
|
+
try {
|
|
36572
|
+
raw = await readFile2(path, "utf8");
|
|
36573
|
+
} catch (e) {
|
|
36574
|
+
if (e.code === "ENOENT")
|
|
36575
|
+
return null;
|
|
36576
|
+
throw e;
|
|
36577
|
+
}
|
|
36578
|
+
let parsed;
|
|
36579
|
+
try {
|
|
36580
|
+
parsed = JSON.parse(raw);
|
|
36581
|
+
} catch {
|
|
36582
|
+
throw new Error(`unreadable extension pin at ${path} (not JSON) \u2014 delete it to re-pair`);
|
|
36583
|
+
}
|
|
36584
|
+
if (!isPin(parsed)) {
|
|
36585
|
+
throw new Error(`unreadable extension pin at ${path} (wrong shape) \u2014 delete it to re-pair`);
|
|
36586
|
+
}
|
|
36587
|
+
return {
|
|
36588
|
+
identityX25519Pub: parsed.identityX25519Pub,
|
|
36589
|
+
identityEd25519Pub: parsed.identityEd25519Pub,
|
|
36590
|
+
pinnedAt: parsed.pinnedAt
|
|
36591
|
+
};
|
|
36592
|
+
}
|
|
36593
|
+
async function writeExtensionPin(serverName, pin, dir = defaultIdentityDir()) {
|
|
36594
|
+
const path = extensionTrustPath(serverName, dir);
|
|
36595
|
+
await mkdir2(dir, { recursive: true, mode: 448 });
|
|
36596
|
+
const tmp = `${path}.tmp`;
|
|
36597
|
+
await writeFile2(tmp, JSON.stringify(pin, null, 2), { mode: 384 });
|
|
36598
|
+
await chmod2(tmp, 384);
|
|
36599
|
+
await rename(tmp, path);
|
|
36600
|
+
}
|
|
36601
|
+
|
|
36359
36602
|
// node_modules/@fetchproxy/server/dist/host.js
|
|
36360
36603
|
var PUBLIC_ORIGIN_RE = /^https?:\/\/(?!(127\.0\.0\.1|localhost)(:|$))/i;
|
|
36361
36604
|
var enc2 = new TextEncoder();
|
|
@@ -36409,9 +36652,12 @@ async function startHost(opts) {
|
|
|
36409
36652
|
}
|
|
36410
36653
|
resetSessionPromise();
|
|
36411
36654
|
let extensionHello = null;
|
|
36655
|
+
let extensionClaim = null;
|
|
36412
36656
|
wss.on("connection", (ws) => {
|
|
36413
36657
|
let identified = null;
|
|
36414
36658
|
let peerMcpId = null;
|
|
36659
|
+
let closed = false;
|
|
36660
|
+
let pinOnReady = false;
|
|
36415
36661
|
ws.on("message", async (data) => {
|
|
36416
36662
|
try {
|
|
36417
36663
|
let frame;
|
|
@@ -36423,10 +36669,43 @@ async function startHost(opts) {
|
|
|
36423
36669
|
return;
|
|
36424
36670
|
}
|
|
36425
36671
|
if (frame.type === "hello" && frame.role === "extension") {
|
|
36426
|
-
if (extensionWs) {
|
|
36672
|
+
if (extensionWs || extensionClaim) {
|
|
36427
36673
|
ws.close(1008, "extension already connected");
|
|
36428
36674
|
return;
|
|
36429
36675
|
}
|
|
36676
|
+
extensionClaim = ws;
|
|
36677
|
+
let pin;
|
|
36678
|
+
try {
|
|
36679
|
+
pin = await opts.extensionTrust.read();
|
|
36680
|
+
} catch (e) {
|
|
36681
|
+
console.error(`[fetchproxy] ${String(e)}`);
|
|
36682
|
+
if (extensionClaim === ws)
|
|
36683
|
+
extensionClaim = null;
|
|
36684
|
+
ws.close(1008, "extension pin unreadable");
|
|
36685
|
+
return;
|
|
36686
|
+
}
|
|
36687
|
+
const outcome = decideExtensionTrust({
|
|
36688
|
+
pin,
|
|
36689
|
+
hello: frame,
|
|
36690
|
+
allowNew: opts.extensionTrust.allowNew,
|
|
36691
|
+
serverName: opts.ownServerName,
|
|
36692
|
+
location: opts.extensionTrust.location
|
|
36693
|
+
});
|
|
36694
|
+
if (outcome.decision === "refused") {
|
|
36695
|
+
console.warn(outcome.message);
|
|
36696
|
+
if (extensionClaim === ws)
|
|
36697
|
+
extensionClaim = null;
|
|
36698
|
+
ws.close(1008, "extension identity is not the pinned one");
|
|
36699
|
+
return;
|
|
36700
|
+
}
|
|
36701
|
+
if (outcome.decision === "replace")
|
|
36702
|
+
console.warn(outcome.message);
|
|
36703
|
+
if (closed || ws.readyState !== import_websocket.default.OPEN) {
|
|
36704
|
+
if (extensionClaim === ws)
|
|
36705
|
+
extensionClaim = null;
|
|
36706
|
+
return;
|
|
36707
|
+
}
|
|
36708
|
+
pinOnReady = outcome.decision !== "pinned";
|
|
36430
36709
|
identified = "extension";
|
|
36431
36710
|
extensionWs = ws;
|
|
36432
36711
|
extensionHello = frame;
|
|
@@ -36438,6 +36717,8 @@ async function startHost(opts) {
|
|
|
36438
36717
|
console.error("[fetchproxy] onPairCode threw:", e);
|
|
36439
36718
|
}
|
|
36440
36719
|
}
|
|
36720
|
+
for (const slot of peers.values())
|
|
36721
|
+
slot.ws.send(JSON.stringify(frame));
|
|
36441
36722
|
ws.send(JSON.stringify(ownHello));
|
|
36442
36723
|
for (const slot of peers.values()) {
|
|
36443
36724
|
ws.send(JSON.stringify(slot.helloFrame));
|
|
@@ -36473,6 +36754,8 @@ async function startHost(opts) {
|
|
|
36473
36754
|
peers.set(frame.mcpId, { ws, helloFrame: frame });
|
|
36474
36755
|
if (extensionWs)
|
|
36475
36756
|
extensionWs.send(JSON.stringify(frame));
|
|
36757
|
+
if (extensionHello)
|
|
36758
|
+
ws.send(JSON.stringify(extensionHello));
|
|
36476
36759
|
return;
|
|
36477
36760
|
}
|
|
36478
36761
|
if (frame.type === "ready") {
|
|
@@ -36484,7 +36767,7 @@ async function startHost(opts) {
|
|
|
36484
36767
|
}
|
|
36485
36768
|
const extEdPub = fromB64(extensionHello.identityEd25519Pub);
|
|
36486
36769
|
const extNonce = fromB64(extensionHello.sessionNonce);
|
|
36487
|
-
const msg =
|
|
36770
|
+
const msg = readySignaturePayload(ownSessionNonce, extNonce, fromB64(frame.extensionSessionPub));
|
|
36488
36771
|
const sig = fromB64(frame.sessionSig);
|
|
36489
36772
|
let sigOk = false;
|
|
36490
36773
|
try {
|
|
@@ -36497,6 +36780,18 @@ async function startHost(opts) {
|
|
|
36497
36780
|
ws.close(1008, "extension session signature invalid");
|
|
36498
36781
|
return;
|
|
36499
36782
|
}
|
|
36783
|
+
if (pinOnReady) {
|
|
36784
|
+
pinOnReady = false;
|
|
36785
|
+
try {
|
|
36786
|
+
await opts.extensionTrust.write({
|
|
36787
|
+
identityX25519Pub: extensionHello.identityX25519Pub,
|
|
36788
|
+
identityEd25519Pub: extensionHello.identityEd25519Pub,
|
|
36789
|
+
pinnedAt: Date.now()
|
|
36790
|
+
});
|
|
36791
|
+
} catch (e) {
|
|
36792
|
+
console.error(`[fetchproxy] could not persist the extension pin: ${String(e)}`);
|
|
36793
|
+
}
|
|
36794
|
+
}
|
|
36500
36795
|
const extPub = fromB64(frame.extensionSessionPub);
|
|
36501
36796
|
const shared = await ecdhX25519(opts.ownIdentity.x25519Priv, extPub);
|
|
36502
36797
|
const key = await hkdfSha256(shared, ownSessionNonce, enc2.encode(HKDF_SESSION_INFO), 32);
|
|
@@ -36550,6 +36845,9 @@ async function startHost(opts) {
|
|
|
36550
36845
|
}
|
|
36551
36846
|
});
|
|
36552
36847
|
ws.on("close", () => {
|
|
36848
|
+
closed = true;
|
|
36849
|
+
if (extensionClaim === ws)
|
|
36850
|
+
extensionClaim = null;
|
|
36553
36851
|
if (identified === "extension" && extensionWs === ws) {
|
|
36554
36852
|
extensionWs = null;
|
|
36555
36853
|
extensionHello = null;
|
|
@@ -36640,11 +36938,84 @@ async function startPeer(opts) {
|
|
|
36640
36938
|
resolveFirstReady = resolve;
|
|
36641
36939
|
rejectFirstReady = reject;
|
|
36642
36940
|
});
|
|
36941
|
+
let extensionHello = null;
|
|
36942
|
+
let warnedUnverifiable = false;
|
|
36943
|
+
let cachedPin = void 0;
|
|
36944
|
+
const authenticateExtension = async (sessionSig, extensionSessionPub) => {
|
|
36945
|
+
if (!extensionHello) {
|
|
36946
|
+
if (opts.requireExtensionIdentity) {
|
|
36947
|
+
console.error(`[fetchproxy] ${opts.serverName}: the concentrator does not forward the extension's identity, so this session cannot be verified \u2014 refusing. Upgrade the MCP holding the bridge port to 1.12.0 or later.`);
|
|
36948
|
+
return false;
|
|
36949
|
+
}
|
|
36950
|
+
if (!warnedUnverifiable) {
|
|
36951
|
+
warnedUnverifiable = true;
|
|
36952
|
+
console.warn(`[fetchproxy] ${opts.serverName}: the concentrator does not forward the extension's identity (pre-1.12.0), so this peer cannot verify which browser it is talking to. Upgrade the MCP holding the bridge port to close this.`);
|
|
36953
|
+
}
|
|
36954
|
+
return true;
|
|
36955
|
+
}
|
|
36956
|
+
const payload = readySignaturePayload(sessionNonce, fromB64(extensionHello.sessionNonce), fromB64(extensionSessionPub));
|
|
36957
|
+
let sigOk = false;
|
|
36958
|
+
try {
|
|
36959
|
+
sigOk = await ed25519Verify(fromB64(extensionHello.identityEd25519Pub), payload, fromB64(sessionSig));
|
|
36960
|
+
} catch {
|
|
36961
|
+
sigOk = false;
|
|
36962
|
+
}
|
|
36963
|
+
if (!sigOk) {
|
|
36964
|
+
console.warn(`[fetchproxy] ${opts.serverName}: extension session signature invalid \u2014 refusing (the concentrator may be answering in the browser's place)`);
|
|
36965
|
+
return false;
|
|
36966
|
+
}
|
|
36967
|
+
if (cachedPin === void 0) {
|
|
36968
|
+
try {
|
|
36969
|
+
cachedPin = await opts.extensionTrust.read();
|
|
36970
|
+
} catch (e) {
|
|
36971
|
+
console.error(`[fetchproxy] ${String(e)}`);
|
|
36972
|
+
return false;
|
|
36973
|
+
}
|
|
36974
|
+
}
|
|
36975
|
+
const pin = cachedPin;
|
|
36976
|
+
const outcome = decideExtensionTrust({
|
|
36977
|
+
pin,
|
|
36978
|
+
hello: extensionHello,
|
|
36979
|
+
allowNew: opts.extensionTrust.allowNew,
|
|
36980
|
+
serverName: opts.serverName,
|
|
36981
|
+
location: opts.extensionTrust.location
|
|
36982
|
+
});
|
|
36983
|
+
if (outcome.decision === "refused") {
|
|
36984
|
+
console.warn(outcome.message);
|
|
36985
|
+
return false;
|
|
36986
|
+
}
|
|
36987
|
+
if (outcome.decision === "replace")
|
|
36988
|
+
console.warn(outcome.message);
|
|
36989
|
+
if (outcome.decision !== "pinned") {
|
|
36990
|
+
try {
|
|
36991
|
+
const written = {
|
|
36992
|
+
identityX25519Pub: extensionHello.identityX25519Pub,
|
|
36993
|
+
identityEd25519Pub: extensionHello.identityEd25519Pub,
|
|
36994
|
+
pinnedAt: Date.now()
|
|
36995
|
+
};
|
|
36996
|
+
await opts.extensionTrust.write(written);
|
|
36997
|
+
cachedPin = written;
|
|
36998
|
+
} catch (e) {
|
|
36999
|
+
console.error(`[fetchproxy] could not persist the extension pin: ${String(e)}`);
|
|
37000
|
+
}
|
|
37001
|
+
}
|
|
37002
|
+
return true;
|
|
37003
|
+
};
|
|
36643
37004
|
const onMessage = async (data) => {
|
|
36644
37005
|
try {
|
|
36645
37006
|
const raw = JSON.parse(data.toString());
|
|
36646
37007
|
const frame = validateFrame(raw);
|
|
37008
|
+
if (frame.type === "hello" && frame.role === "extension") {
|
|
37009
|
+
extensionHello = frame;
|
|
37010
|
+
return;
|
|
37011
|
+
}
|
|
36647
37012
|
if (frame.type === "ready" && frame.mcpId === opts.mcpId) {
|
|
37013
|
+
const authorised = await authenticateExtension(frame.sessionSig, frame.extensionSessionPub);
|
|
37014
|
+
if (!authorised) {
|
|
37015
|
+
ws.close(1008, "extension identity refused");
|
|
37016
|
+
rejectFirstReady(new Error("peer: extension identity refused"));
|
|
37017
|
+
return;
|
|
37018
|
+
}
|
|
36648
37019
|
const extPub = fromB64(frame.extensionSessionPub);
|
|
36649
37020
|
const shared = await ecdhX25519(opts.identity.x25519Priv, extPub);
|
|
36650
37021
|
const sessionKey = await hkdfSha256(shared, sessionNonce, enc3.encode(HKDF_SESSION_INFO), 32);
|
|
@@ -36725,57 +37096,6 @@ async function startPeer(opts) {
|
|
|
36725
37096
|
return handle;
|
|
36726
37097
|
}
|
|
36727
37098
|
|
|
36728
|
-
// node_modules/@fetchproxy/server/dist/identity.js
|
|
36729
|
-
import { readFile, writeFile, mkdir, chmod } from "node:fs/promises";
|
|
36730
|
-
import { join as join2 } from "node:path";
|
|
36731
|
-
import { homedir as homedir2 } from "node:os";
|
|
36732
|
-
var SAFE_PLAIN = /^[A-Za-z0-9._-]+$/;
|
|
36733
|
-
var SAFE_SCOPED = /^@[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|
|
36734
|
-
function defaultIdentityDir() {
|
|
36735
|
-
return join2(homedir2(), ".fetchproxy", "identity");
|
|
36736
|
-
}
|
|
36737
|
-
async function loadOrCreateIdentity(serverName, dir = defaultIdentityDir()) {
|
|
36738
|
-
if (!serverName || serverName === ".." || serverName.includes("..") || !SAFE_PLAIN.test(serverName) && !SAFE_SCOPED.test(serverName)) {
|
|
36739
|
-
throw new Error(`unsafe serverName for identity file: ${JSON.stringify(serverName)}`);
|
|
36740
|
-
}
|
|
36741
|
-
const safeFile = serverName.replace(/\//g, "_");
|
|
36742
|
-
const path = join2(dir, `${safeFile}.json`);
|
|
36743
|
-
await mkdir(dir, { recursive: true, mode: 448 });
|
|
36744
|
-
try {
|
|
36745
|
-
const raw = await readFile(path, "utf8");
|
|
36746
|
-
const j2 = JSON.parse(raw);
|
|
36747
|
-
return {
|
|
36748
|
-
x25519Priv: fromB64(j2.x25519Priv),
|
|
36749
|
-
x25519Pub: fromB64(j2.x25519Pub),
|
|
36750
|
-
ed25519Priv: fromB64(j2.ed25519Priv),
|
|
36751
|
-
ed25519Pub: fromB64(j2.ed25519Pub),
|
|
36752
|
-
createdAt: j2.createdAt
|
|
36753
|
-
};
|
|
36754
|
-
} catch (e) {
|
|
36755
|
-
if (e.code !== "ENOENT")
|
|
36756
|
-
throw e;
|
|
36757
|
-
}
|
|
36758
|
-
const x = await generateX25519();
|
|
36759
|
-
const ed = await generateEd25519();
|
|
36760
|
-
const id = {
|
|
36761
|
-
x25519Priv: x.privateKey,
|
|
36762
|
-
x25519Pub: x.publicKey,
|
|
36763
|
-
ed25519Priv: ed.privateKey,
|
|
36764
|
-
ed25519Pub: ed.publicKey,
|
|
36765
|
-
createdAt: Date.now()
|
|
36766
|
-
};
|
|
36767
|
-
const j = {
|
|
36768
|
-
x25519Priv: toB64(id.x25519Priv),
|
|
36769
|
-
x25519Pub: toB64(id.x25519Pub),
|
|
36770
|
-
ed25519Priv: toB64(id.ed25519Priv),
|
|
36771
|
-
ed25519Pub: toB64(id.ed25519Pub),
|
|
36772
|
-
createdAt: id.createdAt
|
|
36773
|
-
};
|
|
36774
|
-
await writeFile(path, JSON.stringify(j, null, 2), { mode: 384 });
|
|
36775
|
-
await chmod(path, 384);
|
|
36776
|
-
return id;
|
|
36777
|
-
}
|
|
36778
|
-
|
|
36779
37099
|
// node_modules/@fetchproxy/server/dist/error-kind.js
|
|
36780
37100
|
function classifyFetchError(error51) {
|
|
36781
37101
|
if (/Could not establish connection/i.test(error51) || /Receiving end does not exist/i.test(error51)) {
|
|
@@ -36857,6 +37177,39 @@ var FetchproxyBridgeDownError = class extends FetchproxyProtocolError {
|
|
|
36857
37177
|
this.hint = hint;
|
|
36858
37178
|
}
|
|
36859
37179
|
};
|
|
37180
|
+
var FetchproxyHintedError = class extends FetchproxyProtocolError {
|
|
37181
|
+
/** The extension's raw rejection, unmodified. */
|
|
37182
|
+
originalError;
|
|
37183
|
+
/** What the user should actually do, in prose. */
|
|
37184
|
+
hint;
|
|
37185
|
+
constructor(originalError, hint) {
|
|
37186
|
+
super(`${originalError} \u2014 ${hint}`);
|
|
37187
|
+
this.name = "FetchproxyHintedError";
|
|
37188
|
+
this.originalError = originalError;
|
|
37189
|
+
this.hint = hint;
|
|
37190
|
+
}
|
|
37191
|
+
};
|
|
37192
|
+
var FetchproxyScopeError = class extends FetchproxyHintedError {
|
|
37193
|
+
constructor(originalError) {
|
|
37194
|
+
super(originalError, "the declared scope changed since you paired, so the extension is refusing the request. Revoke this MCP in the Transporter extension popup, then re-run \u2014 you will be asked to approve the new scope. This is not a version problem and does not need an update.");
|
|
37195
|
+
this.name = "FetchproxyScopeError";
|
|
37196
|
+
}
|
|
37197
|
+
};
|
|
37198
|
+
var FetchproxyNoTabError = class extends FetchproxyHintedError {
|
|
37199
|
+
constructor(originalError) {
|
|
37200
|
+
super(originalError, "open a tab on that host and sign in, then re-run. This is not a version problem and does not need an update.");
|
|
37201
|
+
this.name = "FetchproxyNoTabError";
|
|
37202
|
+
}
|
|
37203
|
+
};
|
|
37204
|
+
var SCOPE_REJECTION = /not in declared/;
|
|
37205
|
+
var NO_TAB_REJECTION = /no tab matching (?!.*content script loaded)/;
|
|
37206
|
+
function protocolErrorFrom(error51) {
|
|
37207
|
+
if (SCOPE_REJECTION.test(error51))
|
|
37208
|
+
return new FetchproxyScopeError(error51);
|
|
37209
|
+
if (NO_TAB_REJECTION.test(error51))
|
|
37210
|
+
return new FetchproxyNoTabError(error51);
|
|
37211
|
+
return new FetchproxyProtocolError(error51);
|
|
37212
|
+
}
|
|
36860
37213
|
var FetchproxyTimeoutError = class extends FetchproxyProtocolError {
|
|
36861
37214
|
url;
|
|
36862
37215
|
timeoutMs;
|
|
@@ -36886,6 +37239,17 @@ var FetchproxyTimeoutError = class extends FetchproxyProtocolError {
|
|
|
36886
37239
|
}
|
|
36887
37240
|
};
|
|
36888
37241
|
var SUBDOMAIN_LABEL_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
|
|
37242
|
+
function normalizeCookiePath(path) {
|
|
37243
|
+
if (path === void 0 || path === "")
|
|
37244
|
+
return void 0;
|
|
37245
|
+
const trimmed = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
|
|
37246
|
+
try {
|
|
37247
|
+
assertCookiePath(trimmed, "path");
|
|
37248
|
+
} catch (e) {
|
|
37249
|
+
throw new Error(`FetchproxyServer: ${e instanceof Error ? e.message : String(e)} (got ${JSON.stringify(path)})`);
|
|
37250
|
+
}
|
|
37251
|
+
return trimmed;
|
|
37252
|
+
}
|
|
36889
37253
|
function assertSubdomainLabel(label) {
|
|
36890
37254
|
if (!SUBDOMAIN_LABEL_RE.test(label)) {
|
|
36891
37255
|
throw new Error(`FetchproxyServer: subdomain must be a DNS label like "www" or "api" (or dot-separated like "auth.api"), got ${JSON.stringify(label)}`);
|
|
@@ -36950,6 +37314,9 @@ var FetchproxyServer = class {
|
|
|
36950
37314
|
// them off from `pending` (fetch) and `pendingReadCookies` (legacy
|
|
36951
37315
|
// string-shape) so the response routing in `onInner` stays linear.
|
|
36952
37316
|
pendingStorage = /* @__PURE__ */ new Map();
|
|
37317
|
+
// 1.12.0+: write-cookies awaiters resolve the list of names actually
|
|
37318
|
+
// written, so a caller can confirm rather than assume.
|
|
37319
|
+
pendingWriteCookies = /* @__PURE__ */ new Map();
|
|
36953
37320
|
// 0.3.0+: capture-header awaiters resolve a single string.
|
|
36954
37321
|
pendingCapture = /* @__PURE__ */ new Map();
|
|
36955
37322
|
// capture_redirect awaiters resolve the captured redirect URL string.
|
|
@@ -37076,6 +37443,8 @@ var FetchproxyServer = class {
|
|
|
37076
37443
|
keepAliveIntervalMs: opts.keepAliveIntervalMs ?? 2e4,
|
|
37077
37444
|
keepAliveMaxIdleMs: opts.keepAliveMaxIdleMs ?? 5 * 60 * 1e3,
|
|
37078
37445
|
identityDir: opts.identityDir,
|
|
37446
|
+
allowNewExtensionIdentity: opts.allowNewExtensionIdentity,
|
|
37447
|
+
requireExtensionIdentity: opts.requireExtensionIdentity,
|
|
37079
37448
|
onPairCode: opts.onPairCode
|
|
37080
37449
|
};
|
|
37081
37450
|
}
|
|
@@ -37175,7 +37544,8 @@ var FetchproxyServer = class {
|
|
|
37175
37544
|
ownSessionStoragePointers: this.opts.sessionStoragePointers,
|
|
37176
37545
|
ownDomSelectors: this.opts.domSelectors,
|
|
37177
37546
|
ownGraphqlOps: this.opts.graphqlOps,
|
|
37178
|
-
onPairCode: this.opts.onPairCode
|
|
37547
|
+
onPairCode: this.opts.onPairCode,
|
|
37548
|
+
extensionTrust: this.extensionTrust()
|
|
37179
37549
|
});
|
|
37180
37550
|
this.hostHandle.onOwnInner((inner) => this.onInner(inner));
|
|
37181
37551
|
this.hostHandle.onExtensionDisconnect(() => {
|
|
@@ -37204,7 +37574,9 @@ var FetchproxyServer = class {
|
|
|
37204
37574
|
localStoragePointers: this.opts.localStoragePointers,
|
|
37205
37575
|
sessionStoragePointers: this.opts.sessionStoragePointers,
|
|
37206
37576
|
domSelectors: this.opts.domSelectors,
|
|
37207
|
-
graphqlOps: this.opts.graphqlOps
|
|
37577
|
+
graphqlOps: this.opts.graphqlOps,
|
|
37578
|
+
extensionTrust: this.extensionTrust(),
|
|
37579
|
+
requireExtensionIdentity: this.opts.requireExtensionIdentity
|
|
37208
37580
|
});
|
|
37209
37581
|
this.peerHandle.onInner((inner) => this.onInner(inner));
|
|
37210
37582
|
this.peerHandle.onRenegotiate(() => {
|
|
@@ -37343,6 +37715,23 @@ var FetchproxyServer = class {
|
|
|
37343
37715
|
markActive() {
|
|
37344
37716
|
this.noteActivityForKeepalive();
|
|
37345
37717
|
}
|
|
37718
|
+
/**
|
|
37719
|
+
* #208: this MCP's pin on the extension's identity, stored beside its own
|
|
37720
|
+
* identity key and so following `identityDir` wherever the caller put it.
|
|
37721
|
+
*
|
|
37722
|
+
* `allowNewExtensionIdentity` falls back to an environment variable when the
|
|
37723
|
+
* caller expressed no opinion, because the thirteen MCPs that construct this
|
|
37724
|
+
* class are separate packages: an operator whose extension re-install has
|
|
37725
|
+
* just locked all of them out needs one lever that does not require patching
|
|
37726
|
+
* every one of them.
|
|
37727
|
+
*/
|
|
37728
|
+
extensionTrust() {
|
|
37729
|
+
return fileExtensionTrust({
|
|
37730
|
+
serverName: this.opts.serverName,
|
|
37731
|
+
dir: this.opts.identityDir,
|
|
37732
|
+
allowNew: allowNewExtensionIdentity(this.opts.allowNewExtensionIdentity)
|
|
37733
|
+
});
|
|
37734
|
+
}
|
|
37346
37735
|
noteActivityForKeepalive() {
|
|
37347
37736
|
const intervalMs = this.opts.keepAliveIntervalMs;
|
|
37348
37737
|
if (intervalMs <= 0)
|
|
@@ -37406,6 +37795,7 @@ var FetchproxyServer = class {
|
|
|
37406
37795
|
this.pending.delete(id);
|
|
37407
37796
|
this.pendingReadCookies.delete(id);
|
|
37408
37797
|
this.pendingStorage.delete(id);
|
|
37798
|
+
this.pendingWriteCookies.delete(id);
|
|
37409
37799
|
this.pendingCapture.delete(id);
|
|
37410
37800
|
this.pendingRedirect.delete(id);
|
|
37411
37801
|
this.pendingDownload.delete(id);
|
|
@@ -37527,7 +37917,7 @@ var FetchproxyServer = class {
|
|
|
37527
37917
|
port: this.opts.port
|
|
37528
37918
|
});
|
|
37529
37919
|
}
|
|
37530
|
-
return
|
|
37920
|
+
return protocolErrorFrom(result.error);
|
|
37531
37921
|
}
|
|
37532
37922
|
/**
|
|
37533
37923
|
* Convenience wrapper around `fetch()`. Builds the URL from a path
|
|
@@ -37563,10 +37953,20 @@ var FetchproxyServer = class {
|
|
|
37563
37953
|
}
|
|
37564
37954
|
const url2 = isAbsolute ? path : `https://${host}${path}`;
|
|
37565
37955
|
assertUrlInDomains("request url", url2, this.opts.domains);
|
|
37956
|
+
let tabUrl = `https://${host}/`;
|
|
37957
|
+
if (opts.viaTab !== void 0) {
|
|
37958
|
+
try {
|
|
37959
|
+
new URL(opts.viaTab);
|
|
37960
|
+
} catch {
|
|
37961
|
+
throw new Error(`FetchproxyServer.request: viaTab is not a valid URL: ${JSON.stringify(opts.viaTab)}`);
|
|
37962
|
+
}
|
|
37963
|
+
assertUrlInDomains("viaTab", opts.viaTab, this.opts.domains);
|
|
37964
|
+
tabUrl = opts.viaTab;
|
|
37965
|
+
}
|
|
37566
37966
|
const init = {
|
|
37567
37967
|
url: url2,
|
|
37568
37968
|
method,
|
|
37569
|
-
tabUrl
|
|
37969
|
+
tabUrl,
|
|
37570
37970
|
headers: opts.headers,
|
|
37571
37971
|
body: opts.body
|
|
37572
37972
|
};
|
|
@@ -37778,9 +38178,14 @@ var FetchproxyServer = class {
|
|
|
37778
38178
|
let inner;
|
|
37779
38179
|
if (opts.keys !== void 0) {
|
|
37780
38180
|
this.assertScopeSubset(opts.keys, this.opts.cookieKeys, "cookieKeys");
|
|
38181
|
+
const cookiePath = normalizeCookiePath(opts.path);
|
|
37781
38182
|
const initV3 = {
|
|
38183
|
+
// Origin stays BARE. The path travels as its own validated field —
|
|
38184
|
+
// `assertHttpsOriginOnly` deliberately refuses a path here so one
|
|
38185
|
+
// cannot be used to re-point the read past the domain gate.
|
|
37782
38186
|
origin: `https://${host}`,
|
|
37783
|
-
keys: [...opts.keys]
|
|
38187
|
+
keys: [...opts.keys],
|
|
38188
|
+
...cookiePath !== void 0 ? { path: cookiePath } : {}
|
|
37784
38189
|
};
|
|
37785
38190
|
inner = { type: "request", id, op: "read_cookies", init: initV3 };
|
|
37786
38191
|
} else {
|
|
@@ -37793,10 +38198,65 @@ var FetchproxyServer = class {
|
|
|
37793
38198
|
await this.sendInnerFrame(inner);
|
|
37794
38199
|
const result = await this._withVerbTimeout(pending, this.pendingReadCookies, id, `https://${host}`);
|
|
37795
38200
|
if (!result.ok) {
|
|
37796
|
-
throw
|
|
38201
|
+
throw protocolErrorFrom(result.error);
|
|
37797
38202
|
}
|
|
37798
38203
|
return result.cookies;
|
|
37799
38204
|
}
|
|
38205
|
+
/**
|
|
38206
|
+
* 1.12.0+: overwrite the value of cookies this MCP already declares.
|
|
38207
|
+
*
|
|
38208
|
+
* The bridge's only write verb, and it exists for one failure class. Sites
|
|
38209
|
+
* that ROTATE a credential cookie hand back a new value on every refresh; if
|
|
38210
|
+
* the MCP refreshes and keeps the result to itself, the copy in the browser's
|
|
38211
|
+
* cookie jar is dead, and the user gets signed out of a tab they never
|
|
38212
|
+
* touched — usually reported to them as "inactivity". Writing the rotated
|
|
38213
|
+
* value back is the only thing that repairs it.
|
|
38214
|
+
*
|
|
38215
|
+
* Requires `'write_cookies'` in capabilities, which the user approves at pair
|
|
38216
|
+
* time as its own line. Every name must ALSO be in declared `cookieKeys`: a
|
|
38217
|
+
* write can never reach a cookie the MCP was not already trusted to read, so
|
|
38218
|
+
* granting it cannot widen which cookies are in play — only what may be done
|
|
38219
|
+
* to the ones already listed.
|
|
38220
|
+
*
|
|
38221
|
+
* The extension refuses the whole request unless every named cookie already
|
|
38222
|
+
* exists; this refreshes a value in place and deliberately cannot author new
|
|
38223
|
+
* cookies. Returns the names actually written.
|
|
38224
|
+
*/
|
|
38225
|
+
async writeCookies(opts) {
|
|
38226
|
+
if (!this.opts.capabilities.includes("write_cookies")) {
|
|
38227
|
+
throw new Error('FetchproxyServer.writeCookies(): MCP did not declare "write_cookies" in capabilities \u2014 add it to FetchproxyServerOpts.capabilities to enable this verb');
|
|
38228
|
+
}
|
|
38229
|
+
const names = Object.keys(opts.cookies);
|
|
38230
|
+
if (names.length === 0) {
|
|
38231
|
+
throw new Error("FetchproxyServer.writeCookies(): no cookies given");
|
|
38232
|
+
}
|
|
38233
|
+
await this.ensureConnected();
|
|
38234
|
+
this.throwIfPendingPair();
|
|
38235
|
+
if (opts.subdomain !== void 0)
|
|
38236
|
+
assertSubdomainLabel(opts.subdomain);
|
|
38237
|
+
const baseDomain = this.resolveBaseDomain(opts.domain);
|
|
38238
|
+
const host = opts.subdomain ? `${opts.subdomain}.${baseDomain}` : baseDomain;
|
|
38239
|
+
this.assertScopeSubset(names, this.opts.cookieKeys, "cookieKeys");
|
|
38240
|
+
const cookiePath = normalizeCookiePath(opts.path);
|
|
38241
|
+
const id = this.nextRequestId++;
|
|
38242
|
+
const inner = {
|
|
38243
|
+
type: "request",
|
|
38244
|
+
id,
|
|
38245
|
+
op: "write_cookies",
|
|
38246
|
+
init: {
|
|
38247
|
+
// Bare origin, same invariant as the read path: a path must never be
|
|
38248
|
+
// able to move the request past the domain gate.
|
|
38249
|
+
origin: `https://${host}`,
|
|
38250
|
+
cookies: Object.entries(opts.cookies).map(([name, value]) => ({ name, value })),
|
|
38251
|
+
...cookiePath !== void 0 ? { path: cookiePath } : {}
|
|
38252
|
+
}
|
|
38253
|
+
};
|
|
38254
|
+
const pending = new Promise((resolve, reject) => {
|
|
38255
|
+
this.pendingWriteCookies.set(id, { resolve, reject });
|
|
38256
|
+
});
|
|
38257
|
+
await this.sendInnerFrame(inner);
|
|
38258
|
+
return this._withVerbTimeout(pending, this.pendingWriteCookies, id, `https://${host}`);
|
|
38259
|
+
}
|
|
37800
38260
|
/**
|
|
37801
38261
|
* 0.3.0+: read declared localStorage keys from the user's signed-in
|
|
37802
38262
|
* tab. Requires `'read_local_storage'` in capabilities AND each key
|
|
@@ -38343,7 +38803,7 @@ var FetchproxyServer = class {
|
|
|
38343
38803
|
storageCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on storage awaiter`));
|
|
38344
38804
|
}
|
|
38345
38805
|
} else {
|
|
38346
|
-
storageCb.reject(
|
|
38806
|
+
storageCb.reject(protocolErrorFrom(inner.error));
|
|
38347
38807
|
}
|
|
38348
38808
|
return;
|
|
38349
38809
|
}
|
|
@@ -38357,7 +38817,7 @@ var FetchproxyServer = class {
|
|
|
38357
38817
|
captureCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on capture awaiter`));
|
|
38358
38818
|
}
|
|
38359
38819
|
} else {
|
|
38360
|
-
captureCb.reject(
|
|
38820
|
+
captureCb.reject(protocolErrorFrom(inner.error));
|
|
38361
38821
|
}
|
|
38362
38822
|
return;
|
|
38363
38823
|
}
|
|
@@ -38371,7 +38831,7 @@ var FetchproxyServer = class {
|
|
|
38371
38831
|
redirectCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on capture_redirect awaiter`));
|
|
38372
38832
|
}
|
|
38373
38833
|
} else {
|
|
38374
|
-
redirectCb.reject(
|
|
38834
|
+
redirectCb.reject(protocolErrorFrom(inner.error));
|
|
38375
38835
|
}
|
|
38376
38836
|
return;
|
|
38377
38837
|
}
|
|
@@ -38385,7 +38845,7 @@ var FetchproxyServer = class {
|
|
|
38385
38845
|
idbCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on read_indexed_db awaiter`));
|
|
38386
38846
|
}
|
|
38387
38847
|
} else {
|
|
38388
|
-
idbCb.reject(
|
|
38848
|
+
idbCb.reject(protocolErrorFrom(inner.error));
|
|
38389
38849
|
}
|
|
38390
38850
|
return;
|
|
38391
38851
|
}
|
|
@@ -38399,7 +38859,7 @@ var FetchproxyServer = class {
|
|
|
38399
38859
|
downloadCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on download awaiter`));
|
|
38400
38860
|
}
|
|
38401
38861
|
} else {
|
|
38402
|
-
downloadCb.reject(
|
|
38862
|
+
downloadCb.reject(protocolErrorFrom(inner.error));
|
|
38403
38863
|
}
|
|
38404
38864
|
return;
|
|
38405
38865
|
}
|
|
@@ -38413,7 +38873,17 @@ var FetchproxyServer = class {
|
|
|
38413
38873
|
graphqlCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on graphql_query awaiter`));
|
|
38414
38874
|
}
|
|
38415
38875
|
} else {
|
|
38416
|
-
graphqlCb.reject(
|
|
38876
|
+
graphqlCb.reject(protocolErrorFrom(inner.error));
|
|
38877
|
+
}
|
|
38878
|
+
return;
|
|
38879
|
+
}
|
|
38880
|
+
const writeCookiesCb = this.pendingWriteCookies.get(inner.id);
|
|
38881
|
+
if (writeCookiesCb) {
|
|
38882
|
+
this.pendingWriteCookies.delete(inner.id);
|
|
38883
|
+
if (inner.ok && inner.op === "write_cookies") {
|
|
38884
|
+
writeCookiesCb.resolve([...inner.written]);
|
|
38885
|
+
} else {
|
|
38886
|
+
writeCookiesCb.reject(protocolErrorFrom(inner.ok ? "write_cookies response had the wrong op" : inner.error));
|
|
38417
38887
|
}
|
|
38418
38888
|
return;
|
|
38419
38889
|
}
|
|
@@ -38456,6 +38926,9 @@ var FetchproxyServer = class {
|
|
|
38456
38926
|
for (const { reject } of this.pendingStorage.values())
|
|
38457
38927
|
reject(err);
|
|
38458
38928
|
this.pendingStorage.clear();
|
|
38929
|
+
for (const { reject } of this.pendingWriteCookies.values())
|
|
38930
|
+
reject(err);
|
|
38931
|
+
this.pendingWriteCookies.clear();
|
|
38459
38932
|
for (const { reject } of this.pendingCapture.values())
|
|
38460
38933
|
reject(err);
|
|
38461
38934
|
this.pendingCapture.clear();
|
|
@@ -38525,6 +38998,9 @@ var FetchproxyServer = class {
|
|
|
38525
38998
|
// node_modules/@fetchproxy/bootstrap/dist/index.js
|
|
38526
38999
|
var defaultFactory = (opts) => new FetchproxyServer(opts);
|
|
38527
39000
|
async function bootstrap(opts) {
|
|
39001
|
+
return runOneLift(opts);
|
|
39002
|
+
}
|
|
39003
|
+
async function runOneLift(opts) {
|
|
38528
39004
|
const envVar = opts.serverName.toUpperCase().replace(/[^A-Z0-9]/g, "_").replace(/^_+/, "") + "_DISABLE_FETCHPROXY";
|
|
38529
39005
|
const envVal = process.env[envVar];
|
|
38530
39006
|
if (envVal !== void 0 && envVal !== "" && envVal !== "0" && envVal !== "false") {
|
|
@@ -38593,7 +39069,9 @@ async function bootstrap(opts) {
|
|
|
38593
39069
|
if (opts.declare.cookies.length > 0) {
|
|
38594
39070
|
const joined = await server.readCookies({
|
|
38595
39071
|
keys: opts.declare.cookies,
|
|
38596
|
-
...storageDomainOpts
|
|
39072
|
+
...storageDomainOpts,
|
|
39073
|
+
// Cookie-only: the other buckets are origin-scoped and ignore path.
|
|
39074
|
+
...opts.storagePath !== void 0 ? { path: opts.storagePath } : {}
|
|
38597
39075
|
});
|
|
38598
39076
|
for (const piece of joined.split("; ")) {
|
|
38599
39077
|
if (!piece)
|
|
@@ -38651,12 +39129,18 @@ async function bootstrap(opts) {
|
|
|
38651
39129
|
});
|
|
38652
39130
|
indexedDbBucket[`${d.database}/${d.store}`] = values;
|
|
38653
39131
|
}
|
|
39132
|
+
const absent = (declared, got) => declared.filter((k) => !(k in got));
|
|
38654
39133
|
return {
|
|
38655
39134
|
cookies,
|
|
38656
39135
|
localStorage,
|
|
38657
39136
|
sessionStorage,
|
|
38658
39137
|
capturedHeaders,
|
|
38659
|
-
indexedDb: indexedDbBucket
|
|
39138
|
+
indexedDb: indexedDbBucket,
|
|
39139
|
+
missing: {
|
|
39140
|
+
cookies: absent(opts.declare.cookies, cookies),
|
|
39141
|
+
localStorage: absent(opts.declare.localStorage, localStorage),
|
|
39142
|
+
sessionStorage: absent(opts.declare.sessionStorage, sessionStorage)
|
|
39143
|
+
}
|
|
38660
39144
|
};
|
|
38661
39145
|
} finally {
|
|
38662
39146
|
try {
|
|
@@ -38712,7 +39196,7 @@ function bridgeErrorInfo(err) {
|
|
|
38712
39196
|
// package.json
|
|
38713
39197
|
var package_default = {
|
|
38714
39198
|
name: "honeybook-mcp",
|
|
38715
|
-
version: "0.4.
|
|
39199
|
+
version: "0.4.4",
|
|
38716
39200
|
mcpName: "io.github.chrischall/honeybook-mcp",
|
|
38717
39201
|
description: "HoneyBook client-portal MCP server for Claude",
|
|
38718
39202
|
author: "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -38753,8 +39237,8 @@ var package_default = {
|
|
|
38753
39237
|
},
|
|
38754
39238
|
dependencies: {
|
|
38755
39239
|
"@chrischall/mcp-utils": "^0.14.0",
|
|
38756
|
-
"@fetchproxy/bootstrap": "^
|
|
38757
|
-
"@fetchproxy/server": "^
|
|
39240
|
+
"@fetchproxy/bootstrap": "^2.0.0",
|
|
39241
|
+
"@fetchproxy/server": "^2.0.0",
|
|
38758
39242
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
38759
39243
|
dotenv: "^17.4.2",
|
|
38760
39244
|
zod: "^4.4.3"
|
|
@@ -39346,10 +39830,10 @@ function registerInvoiceTools(server) {
|
|
|
39346
39830
|
|
|
39347
39831
|
// src/index.ts
|
|
39348
39832
|
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
39349
|
-
await loadDotenvSafely({ path:
|
|
39833
|
+
await loadDotenvSafely({ path: join4(__dirname, "..", ".env"), override: false });
|
|
39350
39834
|
await runMcp({
|
|
39351
39835
|
name: "honeybook-mcp",
|
|
39352
|
-
version: "0.4.
|
|
39836
|
+
version: "0.4.4",
|
|
39353
39837
|
// x-release-please-version
|
|
39354
39838
|
tools: [
|
|
39355
39839
|
registerSessionTools,
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { registerContractTools } from './tools/contracts.js';
|
|
|
11
11
|
import { registerInvoiceTools } from './tools/invoices.js';
|
|
12
12
|
await runMcp({
|
|
13
13
|
name: 'honeybook-mcp',
|
|
14
|
-
version: '0.4.
|
|
14
|
+
version: '0.4.4', // x-release-please-version
|
|
15
15
|
tools: [
|
|
16
16
|
registerSessionTools,
|
|
17
17
|
registerWorkspaceFileTools,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "honeybook-mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"mcpName": "io.github.chrischall/honeybook-mcp",
|
|
5
5
|
"description": "HoneyBook client-portal MCP server for Claude",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@chrischall/mcp-utils": "^0.14.0",
|
|
44
|
-
"@fetchproxy/bootstrap": "^
|
|
45
|
-
"@fetchproxy/server": "^
|
|
44
|
+
"@fetchproxy/bootstrap": "^2.0.0",
|
|
45
|
+
"@fetchproxy/server": "^2.0.0",
|
|
46
46
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
47
47
|
"dotenv": "^17.4.2",
|
|
48
48
|
"zod": "^4.4.3"
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/chrischall/honeybook-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.4.
|
|
9
|
+
"version": "0.4.4",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "honeybook-mcp",
|
|
14
|
-
"version": "0.4.
|
|
14
|
+
"version": "0.4.4",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|