notex-companion 0.3.2 → 0.3.3
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 +16 -0
- package/dist/atomicWrite.d.ts +1 -0
- package/dist/cli.d.ts +2 -3
- package/dist/cli.js +326 -221
- package/dist/cliErrors.d.ts +3 -0
- package/dist/index.js +17 -8
- package/dist/link.d.ts +15 -0
- package/dist/notexClient.d.ts +10 -0
- package/dist/notexConfig.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,10 +45,13 @@ notex-companion mcp # start the stdio MCP server — graph_statu
|
|
|
45
45
|
# graph_query, graph_path, graph_node, plus notex_list_questions,
|
|
46
46
|
# notex_get_question, notex_get_answer, notex_save_answer
|
|
47
47
|
# (require .notex/notex.json — see docs/specs/notex-mcp-server.md)
|
|
48
|
+
notex-companion link [options] # write .notex/notex.json, pairing this checkout to a Notex Repository
|
|
48
49
|
```
|
|
49
50
|
|
|
50
51
|
Setup, how it picks which checkout to serve, and a manual verification walkthrough:
|
|
51
52
|
[`docs/testing/mcp-server-setup.md`](https://github.com/bhirmbani/notex/blob/main/docs/testing/mcp-server-setup.md).
|
|
53
|
+
For `link` specifically — happy path, rotation, and every refusal path — see
|
|
54
|
+
[`docs/testing/notex-companion-link-manual-test.md`](https://github.com/bhirmbani/notex/blob/main/docs/testing/notex-companion-link-manual-test.md).
|
|
52
55
|
|
|
53
56
|
### `serve` options
|
|
54
57
|
|
|
@@ -58,6 +61,19 @@ Setup, how it picks which checkout to serve, and a manual verification walkthrou
|
|
|
58
61
|
| `--origin <url>` | — | An additional allowed CORS origin, beyond the production Notex origin (and `http://localhost:3000` outside `NODE_ENV=production`). Repeatable. |
|
|
59
62
|
| `--rotate-token` | off | Generate a new pairing token, invalidating the previous one. |
|
|
60
63
|
|
|
64
|
+
### `link` options (all required)
|
|
65
|
+
|
|
66
|
+
| Flag | Meaning |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `--organization-id <id>` | Notex organization id. |
|
|
69
|
+
| `--project-id <id>` | Notex project id — cross-checked against the Repository's actual project before writing. |
|
|
70
|
+
| `--repository-id <id>` | Notex repository id to bind this checkout to. |
|
|
71
|
+
| `--api-key <key>` | Generated once, plaintext, from Notex Settings → API keys. |
|
|
72
|
+
|
|
73
|
+
`link` validates all four against the Notex API before writing `.notex/notex.json` (mode `0600`) —
|
|
74
|
+
a bad key, an inaccessible repository, or a repository/project mismatch fails with an actionable
|
|
75
|
+
message and writes nothing.
|
|
76
|
+
|
|
61
77
|
## Pairing
|
|
62
78
|
|
|
63
79
|
On first run the companion generates a 32-byte token, persists it to `.notex/companion.json`
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function atomicWriteFile(path: string, content: string, mode: number): void;
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
}
|
|
1
|
+
import { CliUsageError } from "./cliErrors.js";
|
|
2
|
+
export { CliUsageError };
|
|
4
3
|
export type ServeArgs = {
|
|
5
4
|
port: number | undefined;
|
|
6
5
|
origins: Array<string>;
|
package/dist/cli.js
CHANGED
|
@@ -6520,10 +6520,203 @@ var require_dist = __commonJS((exports, module) => {
|
|
|
6520
6520
|
import { existsSync } from "node:fs";
|
|
6521
6521
|
import { resolve as resolve2 } from "node:path";
|
|
6522
6522
|
|
|
6523
|
+
// src/cliErrors.ts
|
|
6524
|
+
class CliUsageError extends Error {
|
|
6525
|
+
}
|
|
6526
|
+
|
|
6527
|
+
// src/atomicWrite.ts
|
|
6528
|
+
import { mkdirSync, renameSync, writeFileSync } from "node:fs";
|
|
6529
|
+
import { dirname } from "node:path";
|
|
6530
|
+
function atomicWriteFile(path, content, mode) {
|
|
6531
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
6532
|
+
const tmpPath = `${path}.${process.pid}.tmp`;
|
|
6533
|
+
writeFileSync(tmpPath, content, { mode, flag: "wx" });
|
|
6534
|
+
renameSync(tmpPath, path);
|
|
6535
|
+
}
|
|
6536
|
+
|
|
6537
|
+
// src/notexConfig.ts
|
|
6538
|
+
import { readFileSync } from "node:fs";
|
|
6539
|
+
import { join } from "node:path";
|
|
6540
|
+
function configFilePath(checkoutPath) {
|
|
6541
|
+
return join(checkoutPath, ".notex", "notex.json");
|
|
6542
|
+
}
|
|
6543
|
+
function nonEmptyString(value) {
|
|
6544
|
+
return typeof value === "string" && value.length > 0;
|
|
6545
|
+
}
|
|
6546
|
+
function loadNotexConfig(checkoutPath, env = process.env) {
|
|
6547
|
+
let raw;
|
|
6548
|
+
try {
|
|
6549
|
+
raw = readFileSync(configFilePath(checkoutPath), "utf8");
|
|
6550
|
+
} catch {
|
|
6551
|
+
return { kind: "unlinked", reason: "no .notex/notex.json found" };
|
|
6552
|
+
}
|
|
6553
|
+
let parsed;
|
|
6554
|
+
try {
|
|
6555
|
+
parsed = JSON.parse(raw);
|
|
6556
|
+
} catch {
|
|
6557
|
+
return { kind: "unlinked", reason: ".notex/notex.json is not valid JSON" };
|
|
6558
|
+
}
|
|
6559
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
6560
|
+
return { kind: "unlinked", reason: ".notex/notex.json must be a JSON object" };
|
|
6561
|
+
}
|
|
6562
|
+
const { organizationId, projectId, repositoryId, apiKey } = parsed;
|
|
6563
|
+
if (!nonEmptyString(organizationId) || !nonEmptyString(projectId) || !nonEmptyString(repositoryId)) {
|
|
6564
|
+
return { kind: "unlinked", reason: ".notex/notex.json is missing organizationId, projectId, or repositoryId" };
|
|
6565
|
+
}
|
|
6566
|
+
const resolvedKey = nonEmptyString(env.NOTEX_API_KEY) ? env.NOTEX_API_KEY : apiKey;
|
|
6567
|
+
if (!nonEmptyString(resolvedKey)) {
|
|
6568
|
+
return { kind: "unlinked", reason: ".notex/notex.json is missing apiKey, and NOTEX_API_KEY is not set" };
|
|
6569
|
+
}
|
|
6570
|
+
return { kind: "linked", config: { organizationId, projectId, repositoryId, apiKey: resolvedKey } };
|
|
6571
|
+
}
|
|
6572
|
+
|
|
6573
|
+
// src/types.ts
|
|
6574
|
+
var ERROR_CODES = {
|
|
6575
|
+
unauthorized: "unauthorized",
|
|
6576
|
+
notFound: "not_found",
|
|
6577
|
+
graphUnreadable: "graph_unreadable",
|
|
6578
|
+
invalidRequest: "invalid_request",
|
|
6579
|
+
graphLoading: "graph_loading",
|
|
6580
|
+
forbidden: "forbidden",
|
|
6581
|
+
notexApiError: "notex_api_error"
|
|
6582
|
+
};
|
|
6583
|
+
|
|
6584
|
+
class OpError extends Error {
|
|
6585
|
+
code;
|
|
6586
|
+
detail;
|
|
6587
|
+
constructor(code, message, detail) {
|
|
6588
|
+
super(message);
|
|
6589
|
+
this.name = "OpError";
|
|
6590
|
+
this.code = code;
|
|
6591
|
+
this.detail = detail;
|
|
6592
|
+
}
|
|
6593
|
+
}
|
|
6594
|
+
|
|
6595
|
+
// src/notexClient.ts
|
|
6596
|
+
var DEFAULT_API_URL = "http://localhost:3000";
|
|
6597
|
+
function resolveApiUrl(env = process.env) {
|
|
6598
|
+
return env.NOTEX_API_URL?.trim() || DEFAULT_API_URL;
|
|
6599
|
+
}
|
|
6600
|
+
async function request(config, fetchImpl, method, path, body) {
|
|
6601
|
+
let res;
|
|
6602
|
+
try {
|
|
6603
|
+
res = await fetchImpl(`${config.baseUrl}${path}`, {
|
|
6604
|
+
method,
|
|
6605
|
+
headers: {
|
|
6606
|
+
"x-api-key": config.apiKey,
|
|
6607
|
+
...body !== undefined ? { "content-type": "application/json" } : {}
|
|
6608
|
+
},
|
|
6609
|
+
body: body !== undefined ? JSON.stringify(body) : undefined
|
|
6610
|
+
});
|
|
6611
|
+
} catch (err) {
|
|
6612
|
+
throw new OpError(ERROR_CODES.notexApiError, `Could not reach the Notex API at ${config.baseUrl}`, err);
|
|
6613
|
+
}
|
|
6614
|
+
if (res.status === 401)
|
|
6615
|
+
throw new OpError(ERROR_CODES.unauthorized, "Notex rejected the API key");
|
|
6616
|
+
if (res.status === 403)
|
|
6617
|
+
throw new OpError(ERROR_CODES.forbidden, "Not authorized for this Project");
|
|
6618
|
+
if (res.status === 404)
|
|
6619
|
+
throw new OpError(ERROR_CODES.notFound, "Not found in Notex");
|
|
6620
|
+
if (!res.ok) {
|
|
6621
|
+
const body2 = await res.json().catch(() => ({}));
|
|
6622
|
+
throw new OpError(ERROR_CODES.notexApiError, body2.error?.message ?? `Notex API error (HTTP ${res.status})`);
|
|
6623
|
+
}
|
|
6624
|
+
return await res.json();
|
|
6625
|
+
}
|
|
6626
|
+
var enc = (id) => encodeURIComponent(id);
|
|
6627
|
+
function createNotexClient(config, fetchImpl = fetch) {
|
|
6628
|
+
return {
|
|
6629
|
+
getRepository: (organizationId, id) => request(config, fetchImpl, "GET", `/api/v1/organizations/${enc(organizationId)}/repositories/${enc(id)}`),
|
|
6630
|
+
listContexts: (organizationId, repositoryId) => request(config, fetchImpl, "GET", `/api/v1/organizations/${enc(organizationId)}/repositories/${enc(repositoryId)}/contexts`),
|
|
6631
|
+
getContext: (organizationId, id) => request(config, fetchImpl, "GET", `/api/v1/organizations/${enc(organizationId)}/contexts/${enc(id)}`),
|
|
6632
|
+
createContext: (organizationId, repositoryId, question) => request(config, fetchImpl, "POST", `/api/v1/organizations/${enc(organizationId)}/repositories/${enc(repositoryId)}/contexts`, { question }),
|
|
6633
|
+
listFiles: (organizationId, contextId) => request(config, fetchImpl, "GET", `/api/v1/organizations/${enc(organizationId)}/contexts/${enc(contextId)}/files`),
|
|
6634
|
+
getFile: (organizationId, id) => request(config, fetchImpl, "GET", `/api/v1/organizations/${enc(organizationId)}/files/${enc(id)}`),
|
|
6635
|
+
createFile: (organizationId, contextId, file) => request(config, fetchImpl, "POST", `/api/v1/organizations/${enc(organizationId)}/contexts/${enc(contextId)}/files`, file),
|
|
6636
|
+
deleteContext: (organizationId, id) => request(config, fetchImpl, "DELETE", `/api/v1/organizations/${enc(organizationId)}/contexts/${enc(id)}`)
|
|
6637
|
+
};
|
|
6638
|
+
}
|
|
6639
|
+
|
|
6640
|
+
// src/link.ts
|
|
6641
|
+
var REQUIRED_FLAGS = ["--organization-id", "--project-id", "--repository-id", "--api-key"];
|
|
6642
|
+
function parseLinkArgs(args) {
|
|
6643
|
+
let organizationId;
|
|
6644
|
+
let projectId;
|
|
6645
|
+
let repositoryId;
|
|
6646
|
+
let apiKey;
|
|
6647
|
+
for (let i = 0;i < args.length; i++) {
|
|
6648
|
+
const arg = args[i];
|
|
6649
|
+
switch (arg) {
|
|
6650
|
+
case "--organization-id":
|
|
6651
|
+
organizationId = args[++i];
|
|
6652
|
+
if (!organizationId)
|
|
6653
|
+
throw new CliUsageError("--organization-id needs a value");
|
|
6654
|
+
break;
|
|
6655
|
+
case "--project-id":
|
|
6656
|
+
projectId = args[++i];
|
|
6657
|
+
if (!projectId)
|
|
6658
|
+
throw new CliUsageError("--project-id needs a value");
|
|
6659
|
+
break;
|
|
6660
|
+
case "--repository-id":
|
|
6661
|
+
repositoryId = args[++i];
|
|
6662
|
+
if (!repositoryId)
|
|
6663
|
+
throw new CliUsageError("--repository-id needs a value");
|
|
6664
|
+
break;
|
|
6665
|
+
case "--api-key":
|
|
6666
|
+
apiKey = args[++i];
|
|
6667
|
+
if (!apiKey)
|
|
6668
|
+
throw new CliUsageError("--api-key needs a value");
|
|
6669
|
+
break;
|
|
6670
|
+
default:
|
|
6671
|
+
throw new CliUsageError(`unrecognised option "${arg}"`);
|
|
6672
|
+
}
|
|
6673
|
+
}
|
|
6674
|
+
if (!organizationId || !projectId || !repositoryId || !apiKey) {
|
|
6675
|
+
throw new CliUsageError(`link needs ${REQUIRED_FLAGS.join(", ")}, all required`);
|
|
6676
|
+
}
|
|
6677
|
+
return { organizationId, projectId, repositoryId, apiKey };
|
|
6678
|
+
}
|
|
6679
|
+
function writeNotexConfig(checkoutPath, config) {
|
|
6680
|
+
atomicWriteFile(configFilePath(checkoutPath), JSON.stringify(config, null, 2), 384);
|
|
6681
|
+
}
|
|
6682
|
+
function describeValidationFailure(err, args) {
|
|
6683
|
+
if (err instanceof OpError) {
|
|
6684
|
+
switch (err.code) {
|
|
6685
|
+
case ERROR_CODES.unauthorized:
|
|
6686
|
+
return "Notex rejected the API key — generate a new one from Notex Settings → API keys and try again.";
|
|
6687
|
+
case ERROR_CODES.forbidden:
|
|
6688
|
+
return `Not authorized for repository ${args.repositoryId} in organization ${args.organizationId} — check the ids, or that your API key's owner holds a Grant on this Project.`;
|
|
6689
|
+
case ERROR_CODES.notFound:
|
|
6690
|
+
return `Repository ${args.repositoryId} was not found in organization ${args.organizationId} — check the ids from Notex Settings.`;
|
|
6691
|
+
default:
|
|
6692
|
+
return `Could not verify with the Notex API: ${err.message}`;
|
|
6693
|
+
}
|
|
6694
|
+
}
|
|
6695
|
+
return `Could not verify with the Notex API: ${err instanceof Error ? err.message : String(err)}`;
|
|
6696
|
+
}
|
|
6697
|
+
async function link(args, opts = { checkoutPath: process.cwd() }) {
|
|
6698
|
+
const client = createNotexClient({ baseUrl: resolveApiUrl(), apiKey: args.apiKey }, opts.fetchImpl);
|
|
6699
|
+
let repository;
|
|
6700
|
+
try {
|
|
6701
|
+
repository = await client.getRepository(args.organizationId, args.repositoryId);
|
|
6702
|
+
} catch (err) {
|
|
6703
|
+
throw new CliUsageError(describeValidationFailure(err, args));
|
|
6704
|
+
}
|
|
6705
|
+
if (repository.projectId !== args.projectId) {
|
|
6706
|
+
throw new CliUsageError(`Repository ${args.repositoryId} belongs to project ${repository.projectId}, not ${args.projectId} — check the ids from Notex Settings.`);
|
|
6707
|
+
}
|
|
6708
|
+
writeNotexConfig(opts.checkoutPath, {
|
|
6709
|
+
organizationId: args.organizationId,
|
|
6710
|
+
projectId: args.projectId,
|
|
6711
|
+
repositoryId: args.repositoryId,
|
|
6712
|
+
apiKey: args.apiKey
|
|
6713
|
+
});
|
|
6714
|
+
}
|
|
6715
|
+
|
|
6523
6716
|
// src/graph.ts
|
|
6524
6717
|
import { createHash } from "node:crypto";
|
|
6525
6718
|
import { execSync } from "node:child_process";
|
|
6526
|
-
import { readFileSync, statSync } from "node:fs";
|
|
6719
|
+
import { readFileSync as readFileSync2, statSync } from "node:fs";
|
|
6527
6720
|
import { resolve } from "node:path";
|
|
6528
6721
|
|
|
6529
6722
|
// src/scoring.ts
|
|
@@ -6574,28 +6767,6 @@ function scoreNodes(index, queryTerms) {
|
|
|
6574
6767
|
return scored.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
|
|
6575
6768
|
}
|
|
6576
6769
|
|
|
6577
|
-
// src/types.ts
|
|
6578
|
-
var ERROR_CODES = {
|
|
6579
|
-
unauthorized: "unauthorized",
|
|
6580
|
-
notFound: "not_found",
|
|
6581
|
-
graphUnreadable: "graph_unreadable",
|
|
6582
|
-
invalidRequest: "invalid_request",
|
|
6583
|
-
graphLoading: "graph_loading",
|
|
6584
|
-
forbidden: "forbidden",
|
|
6585
|
-
notexApiError: "notex_api_error"
|
|
6586
|
-
};
|
|
6587
|
-
|
|
6588
|
-
class OpError extends Error {
|
|
6589
|
-
code;
|
|
6590
|
-
detail;
|
|
6591
|
-
constructor(code, message, detail) {
|
|
6592
|
-
super(message);
|
|
6593
|
-
this.name = "OpError";
|
|
6594
|
-
this.code = code;
|
|
6595
|
-
this.detail = detail;
|
|
6596
|
-
}
|
|
6597
|
-
}
|
|
6598
|
-
|
|
6599
6770
|
// src/graph.ts
|
|
6600
6771
|
function rootPrefixFor(checkoutPath, graphRoot) {
|
|
6601
6772
|
if (graphRoot === checkoutPath)
|
|
@@ -6607,7 +6778,7 @@ function rootPrefixFor(checkoutPath, graphRoot) {
|
|
|
6607
6778
|
}
|
|
6608
6779
|
function readGraphRoot(checkoutPath) {
|
|
6609
6780
|
try {
|
|
6610
|
-
return
|
|
6781
|
+
return readFileSync2(resolve(checkoutPath, "graphify-out/.graphify_root"), "utf8").trim();
|
|
6611
6782
|
} catch {
|
|
6612
6783
|
return checkoutPath;
|
|
6613
6784
|
}
|
|
@@ -6623,7 +6794,7 @@ function loadGraph(checkoutPath) {
|
|
|
6623
6794
|
const graphPath = resolve(checkoutPath, "graphify-out/graph.json");
|
|
6624
6795
|
let raw;
|
|
6625
6796
|
try {
|
|
6626
|
-
raw =
|
|
6797
|
+
raw = readFileSync2(graphPath, "utf8");
|
|
6627
6798
|
} catch (err) {
|
|
6628
6799
|
throw new OpError("graph_unreadable", `graph.json not found at ${graphPath}`, err);
|
|
6629
6800
|
}
|
|
@@ -7194,8 +7365,8 @@ function startNodeServer(opts) {
|
|
|
7194
7365
|
}
|
|
7195
7366
|
async function handleRequest(req, res, fetch2) {
|
|
7196
7367
|
try {
|
|
7197
|
-
const
|
|
7198
|
-
const response = await fetch2(
|
|
7368
|
+
const request2 = await toWebRequest(req);
|
|
7369
|
+
const response = await fetch2(request2);
|
|
7199
7370
|
res.statusCode = response.status;
|
|
7200
7371
|
response.headers.forEach((value, key) => res.setHeader(key, value));
|
|
7201
7372
|
res.end(Buffer.from(await response.arrayBuffer()));
|
|
@@ -7231,18 +7402,18 @@ async function toWebRequest(req) {
|
|
|
7231
7402
|
}
|
|
7232
7403
|
|
|
7233
7404
|
// src/pairing.ts
|
|
7234
|
-
import { mkdirSync, readFileSync as
|
|
7405
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
7235
7406
|
import { randomBytes } from "node:crypto";
|
|
7236
|
-
import { dirname, join } from "node:path";
|
|
7407
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
7237
7408
|
function pairingFilePath(checkoutPath) {
|
|
7238
|
-
return
|
|
7409
|
+
return join2(checkoutPath, ".notex", "companion.json");
|
|
7239
7410
|
}
|
|
7240
7411
|
function generateToken() {
|
|
7241
7412
|
return randomBytes(32).toString("base64url");
|
|
7242
7413
|
}
|
|
7243
7414
|
function readExistingToken(path2) {
|
|
7244
7415
|
try {
|
|
7245
|
-
const parsed = JSON.parse(
|
|
7416
|
+
const parsed = JSON.parse(readFileSync3(path2, "utf8"));
|
|
7246
7417
|
return typeof parsed.token === "string" ? parsed.token : undefined;
|
|
7247
7418
|
} catch {
|
|
7248
7419
|
return;
|
|
@@ -7252,14 +7423,11 @@ function tokenContents(token) {
|
|
|
7252
7423
|
return JSON.stringify({ token }, null, 2);
|
|
7253
7424
|
}
|
|
7254
7425
|
function createTokenFileExclusive(path2, token) {
|
|
7255
|
-
|
|
7256
|
-
|
|
7426
|
+
mkdirSync2(dirname2(path2), { recursive: true });
|
|
7427
|
+
writeFileSync2(path2, tokenContents(token), { mode: 384, flag: "wx" });
|
|
7257
7428
|
}
|
|
7258
7429
|
function rewriteTokenFile(path2, token) {
|
|
7259
|
-
|
|
7260
|
-
const tmpPath = `${path2}.${process.pid}.tmp`;
|
|
7261
|
-
writeFileSync(tmpPath, tokenContents(token), { mode: 384, flag: "wx" });
|
|
7262
|
-
renameSync(tmpPath, path2);
|
|
7430
|
+
atomicWriteFile(path2, tokenContents(token), 384);
|
|
7263
7431
|
}
|
|
7264
7432
|
function loadOrCreateToken(checkoutPath, opts = {}) {
|
|
7265
7433
|
const path2 = pairingFilePath(checkoutPath);
|
|
@@ -16474,14 +16642,14 @@ var CompleteRequestSchema = RequestSchema.extend({
|
|
|
16474
16642
|
method: literal("completion/complete"),
|
|
16475
16643
|
params: CompleteRequestParamsSchema
|
|
16476
16644
|
});
|
|
16477
|
-
function assertCompleteRequestPrompt(
|
|
16478
|
-
if (
|
|
16479
|
-
throw new TypeError(`Expected CompleteRequestPrompt, but got ${
|
|
16645
|
+
function assertCompleteRequestPrompt(request2) {
|
|
16646
|
+
if (request2.params.ref.type !== "ref/prompt") {
|
|
16647
|
+
throw new TypeError(`Expected CompleteRequestPrompt, but got ${request2.params.ref.type}`);
|
|
16480
16648
|
}
|
|
16481
16649
|
}
|
|
16482
|
-
function assertCompleteRequestResourceTemplate(
|
|
16483
|
-
if (
|
|
16484
|
-
throw new TypeError(`Expected CompleteRequestResourceTemplate, but got ${
|
|
16650
|
+
function assertCompleteRequestResourceTemplate(request2) {
|
|
16651
|
+
if (request2.params.ref.type !== "ref/resource") {
|
|
16652
|
+
throw new TypeError(`Expected CompleteRequestResourceTemplate, but got ${request2.params.ref.type}`);
|
|
16485
16653
|
}
|
|
16486
16654
|
}
|
|
16487
16655
|
var CompleteResultSchema = ResultSchema.extend({
|
|
@@ -17918,8 +18086,8 @@ class Protocol {
|
|
|
17918
18086
|
this._taskStore = _options?.taskStore;
|
|
17919
18087
|
this._taskMessageQueue = _options?.taskMessageQueue;
|
|
17920
18088
|
if (this._taskStore) {
|
|
17921
|
-
this.setRequestHandler(GetTaskRequestSchema, async (
|
|
17922
|
-
const task = await this._taskStore.getTask(
|
|
18089
|
+
this.setRequestHandler(GetTaskRequestSchema, async (request2, extra) => {
|
|
18090
|
+
const task = await this._taskStore.getTask(request2.params.taskId, extra.sessionId);
|
|
17923
18091
|
if (!task) {
|
|
17924
18092
|
throw new McpError(ErrorCode.InvalidParams, "Failed to retrieve task: Task not found");
|
|
17925
18093
|
}
|
|
@@ -17927,9 +18095,9 @@ class Protocol {
|
|
|
17927
18095
|
...task
|
|
17928
18096
|
};
|
|
17929
18097
|
});
|
|
17930
|
-
this.setRequestHandler(GetTaskPayloadRequestSchema, async (
|
|
18098
|
+
this.setRequestHandler(GetTaskPayloadRequestSchema, async (request2, extra) => {
|
|
17931
18099
|
const handleTaskResult = async () => {
|
|
17932
|
-
const taskId =
|
|
18100
|
+
const taskId = request2.params.taskId;
|
|
17933
18101
|
if (this._taskMessageQueue) {
|
|
17934
18102
|
let queuedMessage;
|
|
17935
18103
|
while (queuedMessage = await this._taskMessageQueue.dequeue(taskId, extra.sessionId)) {
|
|
@@ -17980,9 +18148,9 @@ class Protocol {
|
|
|
17980
18148
|
};
|
|
17981
18149
|
return await handleTaskResult();
|
|
17982
18150
|
});
|
|
17983
|
-
this.setRequestHandler(ListTasksRequestSchema, async (
|
|
18151
|
+
this.setRequestHandler(ListTasksRequestSchema, async (request2, extra) => {
|
|
17984
18152
|
try {
|
|
17985
|
-
const { tasks, nextCursor } = await this._taskStore.listTasks(
|
|
18153
|
+
const { tasks, nextCursor } = await this._taskStore.listTasks(request2.params?.cursor, extra.sessionId);
|
|
17986
18154
|
return {
|
|
17987
18155
|
tasks,
|
|
17988
18156
|
nextCursor,
|
|
@@ -17992,20 +18160,20 @@ class Protocol {
|
|
|
17992
18160
|
throw new McpError(ErrorCode.InvalidParams, `Failed to list tasks: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
17993
18161
|
}
|
|
17994
18162
|
});
|
|
17995
|
-
this.setRequestHandler(CancelTaskRequestSchema, async (
|
|
18163
|
+
this.setRequestHandler(CancelTaskRequestSchema, async (request2, extra) => {
|
|
17996
18164
|
try {
|
|
17997
|
-
const task = await this._taskStore.getTask(
|
|
18165
|
+
const task = await this._taskStore.getTask(request2.params.taskId, extra.sessionId);
|
|
17998
18166
|
if (!task) {
|
|
17999
|
-
throw new McpError(ErrorCode.InvalidParams, `Task not found: ${
|
|
18167
|
+
throw new McpError(ErrorCode.InvalidParams, `Task not found: ${request2.params.taskId}`);
|
|
18000
18168
|
}
|
|
18001
18169
|
if (isTerminal(task.status)) {
|
|
18002
18170
|
throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task.status}`);
|
|
18003
18171
|
}
|
|
18004
|
-
await this._taskStore.updateTaskStatus(
|
|
18005
|
-
this._clearTaskQueue(
|
|
18006
|
-
const cancelledTask = await this._taskStore.getTask(
|
|
18172
|
+
await this._taskStore.updateTaskStatus(request2.params.taskId, "cancelled", "Client cancelled task execution.", extra.sessionId);
|
|
18173
|
+
this._clearTaskQueue(request2.params.taskId);
|
|
18174
|
+
const cancelledTask = await this._taskStore.getTask(request2.params.taskId, extra.sessionId);
|
|
18007
18175
|
if (!cancelledTask) {
|
|
18008
|
-
throw new McpError(ErrorCode.InvalidParams, `Task not found after cancellation: ${
|
|
18176
|
+
throw new McpError(ErrorCode.InvalidParams, `Task not found after cancellation: ${request2.params.taskId}`);
|
|
18009
18177
|
}
|
|
18010
18178
|
return {
|
|
18011
18179
|
_meta: {},
|
|
@@ -18121,14 +18289,14 @@ class Protocol {
|
|
|
18121
18289
|
}
|
|
18122
18290
|
Promise.resolve().then(() => handler(notification)).catch((error2) => this._onerror(new Error(`Uncaught error in notification handler: ${error2}`)));
|
|
18123
18291
|
}
|
|
18124
|
-
_onrequest(
|
|
18125
|
-
const handler = this._requestHandlers.get(
|
|
18292
|
+
_onrequest(request2, extra) {
|
|
18293
|
+
const handler = this._requestHandlers.get(request2.method) ?? this.fallbackRequestHandler;
|
|
18126
18294
|
const capturedTransport = this._transport;
|
|
18127
|
-
const relatedTaskId =
|
|
18295
|
+
const relatedTaskId = request2.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
|
|
18128
18296
|
if (handler === undefined) {
|
|
18129
18297
|
const errorResponse = {
|
|
18130
18298
|
jsonrpc: "2.0",
|
|
18131
|
-
id:
|
|
18299
|
+
id: request2.id,
|
|
18132
18300
|
error: {
|
|
18133
18301
|
code: ErrorCode.MethodNotFound,
|
|
18134
18302
|
message: "Method not found"
|
|
@@ -18146,17 +18314,17 @@ class Protocol {
|
|
|
18146
18314
|
return;
|
|
18147
18315
|
}
|
|
18148
18316
|
const abortController = new AbortController;
|
|
18149
|
-
this._requestHandlerAbortControllers.set(
|
|
18150
|
-
const taskCreationParams = isTaskAugmentedRequestParams(
|
|
18151
|
-
const taskStore = this._taskStore ? this.requestTaskStore(
|
|
18317
|
+
this._requestHandlerAbortControllers.set(request2.id, abortController);
|
|
18318
|
+
const taskCreationParams = isTaskAugmentedRequestParams(request2.params) ? request2.params.task : undefined;
|
|
18319
|
+
const taskStore = this._taskStore ? this.requestTaskStore(request2, capturedTransport?.sessionId) : undefined;
|
|
18152
18320
|
const fullExtra = {
|
|
18153
18321
|
signal: abortController.signal,
|
|
18154
18322
|
sessionId: capturedTransport?.sessionId,
|
|
18155
|
-
_meta:
|
|
18323
|
+
_meta: request2.params?._meta,
|
|
18156
18324
|
sendNotification: async (notification) => {
|
|
18157
18325
|
if (abortController.signal.aborted)
|
|
18158
18326
|
return;
|
|
18159
|
-
const notificationOptions = { relatedRequestId:
|
|
18327
|
+
const notificationOptions = { relatedRequestId: request2.id };
|
|
18160
18328
|
if (relatedTaskId) {
|
|
18161
18329
|
notificationOptions.relatedTask = { taskId: relatedTaskId };
|
|
18162
18330
|
}
|
|
@@ -18166,7 +18334,7 @@ class Protocol {
|
|
|
18166
18334
|
if (abortController.signal.aborted) {
|
|
18167
18335
|
throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
|
|
18168
18336
|
}
|
|
18169
|
-
const requestOptions = { ...options, relatedRequestId:
|
|
18337
|
+
const requestOptions = { ...options, relatedRequestId: request2.id };
|
|
18170
18338
|
if (relatedTaskId && !requestOptions.relatedTask) {
|
|
18171
18339
|
requestOptions.relatedTask = { taskId: relatedTaskId };
|
|
18172
18340
|
}
|
|
@@ -18177,7 +18345,7 @@ class Protocol {
|
|
|
18177
18345
|
return await this.request(r, resultSchema, requestOptions);
|
|
18178
18346
|
},
|
|
18179
18347
|
authInfo: extra?.authInfo,
|
|
18180
|
-
requestId:
|
|
18348
|
+
requestId: request2.id,
|
|
18181
18349
|
requestInfo: extra?.requestInfo,
|
|
18182
18350
|
taskId: relatedTaskId,
|
|
18183
18351
|
taskStore,
|
|
@@ -18187,16 +18355,16 @@ class Protocol {
|
|
|
18187
18355
|
};
|
|
18188
18356
|
Promise.resolve().then(() => {
|
|
18189
18357
|
if (taskCreationParams) {
|
|
18190
|
-
this.assertTaskHandlerCapability(
|
|
18358
|
+
this.assertTaskHandlerCapability(request2.method);
|
|
18191
18359
|
}
|
|
18192
|
-
}).then(() => handler(
|
|
18360
|
+
}).then(() => handler(request2, fullExtra)).then(async (result) => {
|
|
18193
18361
|
if (abortController.signal.aborted) {
|
|
18194
18362
|
return;
|
|
18195
18363
|
}
|
|
18196
18364
|
const response = {
|
|
18197
18365
|
result,
|
|
18198
18366
|
jsonrpc: "2.0",
|
|
18199
|
-
id:
|
|
18367
|
+
id: request2.id
|
|
18200
18368
|
};
|
|
18201
18369
|
if (relatedTaskId && this._taskMessageQueue) {
|
|
18202
18370
|
await this._enqueueTaskMessage(relatedTaskId, {
|
|
@@ -18213,7 +18381,7 @@ class Protocol {
|
|
|
18213
18381
|
}
|
|
18214
18382
|
const errorResponse = {
|
|
18215
18383
|
jsonrpc: "2.0",
|
|
18216
|
-
id:
|
|
18384
|
+
id: request2.id,
|
|
18217
18385
|
error: {
|
|
18218
18386
|
code: Number.isSafeInteger(error2["code"]) ? error2["code"] : ErrorCode.InternalError,
|
|
18219
18387
|
message: error2.message ?? "Internal error",
|
|
@@ -18230,8 +18398,8 @@ class Protocol {
|
|
|
18230
18398
|
await capturedTransport?.send(errorResponse);
|
|
18231
18399
|
}
|
|
18232
18400
|
}).catch((error2) => this._onerror(new Error(`Failed to send response: ${error2}`))).finally(() => {
|
|
18233
|
-
if (this._requestHandlerAbortControllers.get(
|
|
18234
|
-
this._requestHandlerAbortControllers.delete(
|
|
18401
|
+
if (this._requestHandlerAbortControllers.get(request2.id) === abortController) {
|
|
18402
|
+
this._requestHandlerAbortControllers.delete(request2.id);
|
|
18235
18403
|
}
|
|
18236
18404
|
});
|
|
18237
18405
|
}
|
|
@@ -18305,11 +18473,11 @@ class Protocol {
|
|
|
18305
18473
|
async close() {
|
|
18306
18474
|
await this._transport?.close();
|
|
18307
18475
|
}
|
|
18308
|
-
async* requestStream(
|
|
18476
|
+
async* requestStream(request2, resultSchema, options) {
|
|
18309
18477
|
const { task } = options ?? {};
|
|
18310
18478
|
if (!task) {
|
|
18311
18479
|
try {
|
|
18312
|
-
const result = await this.request(
|
|
18480
|
+
const result = await this.request(request2, resultSchema, options);
|
|
18313
18481
|
yield { type: "result", result };
|
|
18314
18482
|
} catch (error2) {
|
|
18315
18483
|
yield {
|
|
@@ -18321,7 +18489,7 @@ class Protocol {
|
|
|
18321
18489
|
}
|
|
18322
18490
|
let taskId;
|
|
18323
18491
|
try {
|
|
18324
|
-
const createResult = await this.request(
|
|
18492
|
+
const createResult = await this.request(request2, CreateTaskResultSchema, options);
|
|
18325
18493
|
if (createResult.task) {
|
|
18326
18494
|
taskId = createResult.task.taskId;
|
|
18327
18495
|
yield { type: "taskCreated", task: createResult.task };
|
|
@@ -18364,7 +18532,7 @@ class Protocol {
|
|
|
18364
18532
|
};
|
|
18365
18533
|
}
|
|
18366
18534
|
}
|
|
18367
|
-
request(
|
|
18535
|
+
request(request2, resultSchema, options) {
|
|
18368
18536
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
18369
18537
|
return new Promise((resolve2, reject) => {
|
|
18370
18538
|
const earlyReject = (error2) => {
|
|
@@ -18376,9 +18544,9 @@ class Protocol {
|
|
|
18376
18544
|
}
|
|
18377
18545
|
if (this._options?.enforceStrictCapabilities === true) {
|
|
18378
18546
|
try {
|
|
18379
|
-
this.assertCapabilityForMethod(
|
|
18547
|
+
this.assertCapabilityForMethod(request2.method);
|
|
18380
18548
|
if (task) {
|
|
18381
|
-
this.assertTaskCapability(
|
|
18549
|
+
this.assertTaskCapability(request2.method);
|
|
18382
18550
|
}
|
|
18383
18551
|
} catch (e) {
|
|
18384
18552
|
earlyReject(e);
|
|
@@ -18388,16 +18556,16 @@ class Protocol {
|
|
|
18388
18556
|
options?.signal?.throwIfAborted();
|
|
18389
18557
|
const messageId = this._requestMessageId++;
|
|
18390
18558
|
const jsonrpcRequest = {
|
|
18391
|
-
...
|
|
18559
|
+
...request2,
|
|
18392
18560
|
jsonrpc: "2.0",
|
|
18393
18561
|
id: messageId
|
|
18394
18562
|
};
|
|
18395
18563
|
if (options?.onprogress) {
|
|
18396
18564
|
this._progressHandlers.set(messageId, options.onprogress);
|
|
18397
18565
|
jsonrpcRequest.params = {
|
|
18398
|
-
...
|
|
18566
|
+
...request2.params,
|
|
18399
18567
|
_meta: {
|
|
18400
|
-
...
|
|
18568
|
+
...request2.params?._meta || {},
|
|
18401
18569
|
progressToken: messageId
|
|
18402
18570
|
}
|
|
18403
18571
|
};
|
|
@@ -18573,8 +18741,8 @@ class Protocol {
|
|
|
18573
18741
|
setRequestHandler(requestSchema, handler) {
|
|
18574
18742
|
const method = getMethodLiteral(requestSchema);
|
|
18575
18743
|
this.assertRequestHandlerCapability(method);
|
|
18576
|
-
this._requestHandlers.set(method, (
|
|
18577
|
-
const parsed = parseWithCompat(requestSchema,
|
|
18744
|
+
this._requestHandlers.set(method, (request2, extra) => {
|
|
18745
|
+
const parsed = parseWithCompat(requestSchema, request2);
|
|
18578
18746
|
return Promise.resolve(handler(parsed, extra));
|
|
18579
18747
|
});
|
|
18580
18748
|
}
|
|
@@ -18647,19 +18815,19 @@ class Protocol {
|
|
|
18647
18815
|
}, { once: true });
|
|
18648
18816
|
});
|
|
18649
18817
|
}
|
|
18650
|
-
requestTaskStore(
|
|
18818
|
+
requestTaskStore(request2, sessionId) {
|
|
18651
18819
|
const taskStore = this._taskStore;
|
|
18652
18820
|
if (!taskStore) {
|
|
18653
18821
|
throw new Error("No task store configured");
|
|
18654
18822
|
}
|
|
18655
18823
|
return {
|
|
18656
18824
|
createTask: async (taskParams) => {
|
|
18657
|
-
if (!
|
|
18825
|
+
if (!request2) {
|
|
18658
18826
|
throw new Error("No request provided");
|
|
18659
18827
|
}
|
|
18660
|
-
return await taskStore.createTask(taskParams,
|
|
18661
|
-
method:
|
|
18662
|
-
params:
|
|
18828
|
+
return await taskStore.createTask(taskParams, request2.id, {
|
|
18829
|
+
method: request2.method,
|
|
18830
|
+
params: request2.params
|
|
18663
18831
|
}, sessionId);
|
|
18664
18832
|
},
|
|
18665
18833
|
getTask: async (taskId) => {
|
|
@@ -18778,8 +18946,8 @@ class ExperimentalServerTasks {
|
|
|
18778
18946
|
constructor(_server) {
|
|
18779
18947
|
this._server = _server;
|
|
18780
18948
|
}
|
|
18781
|
-
requestStream(
|
|
18782
|
-
return this._server.requestStream(
|
|
18949
|
+
requestStream(request2, resultSchema, options) {
|
|
18950
|
+
return this._server.requestStream(request2, resultSchema, options);
|
|
18783
18951
|
}
|
|
18784
18952
|
createMessageStream(params, options) {
|
|
18785
18953
|
const clientCapabilities = this._server.getClientCapabilities();
|
|
@@ -18900,12 +19068,12 @@ class Server extends Protocol {
|
|
|
18900
19068
|
this._capabilities = options?.capabilities ?? {};
|
|
18901
19069
|
this._instructions = options?.instructions;
|
|
18902
19070
|
this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new AjvJsonSchemaValidator;
|
|
18903
|
-
this.setRequestHandler(InitializeRequestSchema, (
|
|
19071
|
+
this.setRequestHandler(InitializeRequestSchema, (request2) => this._oninitialize(request2));
|
|
18904
19072
|
this.setNotificationHandler(InitializedNotificationSchema, () => this.oninitialized?.());
|
|
18905
19073
|
if (this._capabilities.logging) {
|
|
18906
|
-
this.setRequestHandler(SetLevelRequestSchema, async (
|
|
19074
|
+
this.setRequestHandler(SetLevelRequestSchema, async (request2, extra) => {
|
|
18907
19075
|
const transportSessionId = extra.sessionId || extra.requestInfo?.headers["mcp-session-id"] || undefined;
|
|
18908
|
-
const { level } =
|
|
19076
|
+
const { level } = request2.params;
|
|
18909
19077
|
const parseResult = LoggingLevelSchema.safeParse(level);
|
|
18910
19078
|
if (parseResult.success) {
|
|
18911
19079
|
this._loggingLevels.set(transportSessionId, parseResult.data);
|
|
@@ -18949,14 +19117,14 @@ class Server extends Protocol {
|
|
|
18949
19117
|
}
|
|
18950
19118
|
const method = methodValue;
|
|
18951
19119
|
if (method === "tools/call") {
|
|
18952
|
-
const wrappedHandler = async (
|
|
18953
|
-
const validatedRequest = safeParse2(CallToolRequestSchema,
|
|
19120
|
+
const wrappedHandler = async (request2, extra) => {
|
|
19121
|
+
const validatedRequest = safeParse2(CallToolRequestSchema, request2);
|
|
18954
19122
|
if (!validatedRequest.success) {
|
|
18955
19123
|
const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
|
|
18956
19124
|
throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage}`);
|
|
18957
19125
|
}
|
|
18958
19126
|
const { params } = validatedRequest.data;
|
|
18959
|
-
const result = await Promise.resolve(handler(
|
|
19127
|
+
const result = await Promise.resolve(handler(request2, extra));
|
|
18960
19128
|
if (params.task) {
|
|
18961
19129
|
const taskValidationResult = safeParse2(CreateTaskResultSchema, result);
|
|
18962
19130
|
if (!taskValidationResult.success) {
|
|
@@ -19087,10 +19255,10 @@ class Server extends Protocol {
|
|
|
19087
19255
|
}
|
|
19088
19256
|
assertToolsCallTaskCapability(this._capabilities.tasks?.requests, method, "Server");
|
|
19089
19257
|
}
|
|
19090
|
-
async _oninitialize(
|
|
19091
|
-
const requestedVersion =
|
|
19092
|
-
this._clientCapabilities =
|
|
19093
|
-
this._clientVersion =
|
|
19258
|
+
async _oninitialize(request2) {
|
|
19259
|
+
const requestedVersion = request2.params.protocolVersion;
|
|
19260
|
+
this._clientCapabilities = request2.params.capabilities;
|
|
19261
|
+
this._clientVersion = request2.params.clientInfo;
|
|
19094
19262
|
const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(requestedVersion) ? requestedVersion : LATEST_PROTOCOL_VERSION;
|
|
19095
19263
|
return {
|
|
19096
19264
|
protocolVersion,
|
|
@@ -19373,33 +19541,33 @@ class McpServer {
|
|
|
19373
19541
|
return toolDefinition;
|
|
19374
19542
|
})
|
|
19375
19543
|
}));
|
|
19376
|
-
this.server.setRequestHandler(CallToolRequestSchema, async (
|
|
19544
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request2, extra) => {
|
|
19377
19545
|
try {
|
|
19378
|
-
const tool = this._registeredTools[
|
|
19546
|
+
const tool = this._registeredTools[request2.params.name];
|
|
19379
19547
|
if (!tool) {
|
|
19380
|
-
throw new McpError(ErrorCode.InvalidParams, `Tool ${
|
|
19548
|
+
throw new McpError(ErrorCode.InvalidParams, `Tool ${request2.params.name} not found`);
|
|
19381
19549
|
}
|
|
19382
19550
|
if (!tool.enabled) {
|
|
19383
|
-
throw new McpError(ErrorCode.InvalidParams, `Tool ${
|
|
19551
|
+
throw new McpError(ErrorCode.InvalidParams, `Tool ${request2.params.name} disabled`);
|
|
19384
19552
|
}
|
|
19385
|
-
const isTaskRequest = !!
|
|
19553
|
+
const isTaskRequest = !!request2.params.task;
|
|
19386
19554
|
const taskSupport = tool.execution?.taskSupport;
|
|
19387
19555
|
const isTaskHandler = "createTask" in tool.handler;
|
|
19388
19556
|
if ((taskSupport === "required" || taskSupport === "optional") && !isTaskHandler) {
|
|
19389
|
-
throw new McpError(ErrorCode.InternalError, `Tool ${
|
|
19557
|
+
throw new McpError(ErrorCode.InternalError, `Tool ${request2.params.name} has taskSupport '${taskSupport}' but was not registered with registerToolTask`);
|
|
19390
19558
|
}
|
|
19391
19559
|
if (taskSupport === "required" && !isTaskRequest) {
|
|
19392
|
-
throw new McpError(ErrorCode.MethodNotFound, `Tool ${
|
|
19560
|
+
throw new McpError(ErrorCode.MethodNotFound, `Tool ${request2.params.name} requires task augmentation (taskSupport: 'required')`);
|
|
19393
19561
|
}
|
|
19394
19562
|
if (taskSupport === "optional" && !isTaskRequest && isTaskHandler) {
|
|
19395
|
-
return await this.handleAutomaticTaskPolling(tool,
|
|
19563
|
+
return await this.handleAutomaticTaskPolling(tool, request2, extra);
|
|
19396
19564
|
}
|
|
19397
|
-
const args = await this.validateToolInput(tool,
|
|
19565
|
+
const args = await this.validateToolInput(tool, request2.params.arguments, request2.params.name);
|
|
19398
19566
|
const result = await this.executeToolHandler(tool, args, extra);
|
|
19399
19567
|
if (isTaskRequest) {
|
|
19400
19568
|
return result;
|
|
19401
19569
|
}
|
|
19402
|
-
await this.validateToolOutput(tool, result,
|
|
19570
|
+
await this.validateToolOutput(tool, result, request2.params.name);
|
|
19403
19571
|
return result;
|
|
19404
19572
|
} catch (error2) {
|
|
19405
19573
|
if (error2 instanceof McpError) {
|
|
@@ -19482,11 +19650,11 @@ class McpServer {
|
|
|
19482
19650
|
return await Promise.resolve(typedHandler(extra));
|
|
19483
19651
|
}
|
|
19484
19652
|
}
|
|
19485
|
-
async handleAutomaticTaskPolling(tool,
|
|
19653
|
+
async handleAutomaticTaskPolling(tool, request2, extra) {
|
|
19486
19654
|
if (!extra.taskStore) {
|
|
19487
19655
|
throw new Error("No task store provided for task-capable tool.");
|
|
19488
19656
|
}
|
|
19489
|
-
const args = await this.validateToolInput(tool,
|
|
19657
|
+
const args = await this.validateToolInput(tool, request2.params.arguments, request2.params.name);
|
|
19490
19658
|
const handler = tool.handler;
|
|
19491
19659
|
const taskExtra = { ...extra, taskStore: extra.taskStore };
|
|
19492
19660
|
const createTaskResult = args ? await Promise.resolve(handler.createTask(args, taskExtra)) : await Promise.resolve(handler.createTask(taskExtra));
|
|
@@ -19511,21 +19679,21 @@ class McpServer {
|
|
|
19511
19679
|
this.server.registerCapabilities({
|
|
19512
19680
|
completions: {}
|
|
19513
19681
|
});
|
|
19514
|
-
this.server.setRequestHandler(CompleteRequestSchema, async (
|
|
19515
|
-
switch (
|
|
19682
|
+
this.server.setRequestHandler(CompleteRequestSchema, async (request2) => {
|
|
19683
|
+
switch (request2.params.ref.type) {
|
|
19516
19684
|
case "ref/prompt":
|
|
19517
|
-
assertCompleteRequestPrompt(
|
|
19518
|
-
return this.handlePromptCompletion(
|
|
19685
|
+
assertCompleteRequestPrompt(request2);
|
|
19686
|
+
return this.handlePromptCompletion(request2, request2.params.ref);
|
|
19519
19687
|
case "ref/resource":
|
|
19520
|
-
assertCompleteRequestResourceTemplate(
|
|
19521
|
-
return this.handleResourceCompletion(
|
|
19688
|
+
assertCompleteRequestResourceTemplate(request2);
|
|
19689
|
+
return this.handleResourceCompletion(request2, request2.params.ref);
|
|
19522
19690
|
default:
|
|
19523
|
-
throw new McpError(ErrorCode.InvalidParams, `Invalid completion reference: ${
|
|
19691
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid completion reference: ${request2.params.ref}`);
|
|
19524
19692
|
}
|
|
19525
19693
|
});
|
|
19526
19694
|
this._completionHandlerInitialized = true;
|
|
19527
19695
|
}
|
|
19528
|
-
async handlePromptCompletion(
|
|
19696
|
+
async handlePromptCompletion(request2, ref) {
|
|
19529
19697
|
const prompt = this._registeredPrompts[ref.name];
|
|
19530
19698
|
if (!prompt) {
|
|
19531
19699
|
throw new McpError(ErrorCode.InvalidParams, `Prompt ${ref.name} not found`);
|
|
@@ -19537,7 +19705,7 @@ class McpServer {
|
|
|
19537
19705
|
return EMPTY_COMPLETION_RESULT;
|
|
19538
19706
|
}
|
|
19539
19707
|
const promptShape = getObjectShape(prompt.argsSchema);
|
|
19540
|
-
const field = promptShape?.[
|
|
19708
|
+
const field = promptShape?.[request2.params.argument.name];
|
|
19541
19709
|
if (!isCompletable(field)) {
|
|
19542
19710
|
return EMPTY_COMPLETION_RESULT;
|
|
19543
19711
|
}
|
|
@@ -19545,22 +19713,22 @@ class McpServer {
|
|
|
19545
19713
|
if (!completer) {
|
|
19546
19714
|
return EMPTY_COMPLETION_RESULT;
|
|
19547
19715
|
}
|
|
19548
|
-
const suggestions = await completer(
|
|
19716
|
+
const suggestions = await completer(request2.params.argument.value, request2.params.context);
|
|
19549
19717
|
return createCompletionResult(suggestions);
|
|
19550
19718
|
}
|
|
19551
|
-
async handleResourceCompletion(
|
|
19719
|
+
async handleResourceCompletion(request2, ref) {
|
|
19552
19720
|
const template = Object.values(this._registeredResourceTemplates).find((t) => t.resourceTemplate.uriTemplate.toString() === ref.uri);
|
|
19553
19721
|
if (!template) {
|
|
19554
19722
|
if (this._registeredResources[ref.uri]) {
|
|
19555
19723
|
return EMPTY_COMPLETION_RESULT;
|
|
19556
19724
|
}
|
|
19557
|
-
throw new McpError(ErrorCode.InvalidParams, `Resource template ${
|
|
19725
|
+
throw new McpError(ErrorCode.InvalidParams, `Resource template ${request2.params.ref.uri} not found`);
|
|
19558
19726
|
}
|
|
19559
|
-
const completer = template.resourceTemplate.completeCallback(
|
|
19727
|
+
const completer = template.resourceTemplate.completeCallback(request2.params.argument.name);
|
|
19560
19728
|
if (!completer) {
|
|
19561
19729
|
return EMPTY_COMPLETION_RESULT;
|
|
19562
19730
|
}
|
|
19563
|
-
const suggestions = await completer(
|
|
19731
|
+
const suggestions = await completer(request2.params.argument.value, request2.params.context);
|
|
19564
19732
|
return createCompletionResult(suggestions);
|
|
19565
19733
|
}
|
|
19566
19734
|
setResourceRequestHandlers() {
|
|
@@ -19575,7 +19743,7 @@ class McpServer {
|
|
|
19575
19743
|
listChanged: true
|
|
19576
19744
|
}
|
|
19577
19745
|
});
|
|
19578
|
-
this.server.setRequestHandler(ListResourcesRequestSchema, async (
|
|
19746
|
+
this.server.setRequestHandler(ListResourcesRequestSchema, async (request2, extra) => {
|
|
19579
19747
|
const resources = Object.entries(this._registeredResources).filter(([_, resource]) => resource.enabled).map(([uri, resource]) => ({
|
|
19580
19748
|
uri,
|
|
19581
19749
|
name: resource.name,
|
|
@@ -19604,8 +19772,8 @@ class McpServer {
|
|
|
19604
19772
|
}));
|
|
19605
19773
|
return { resourceTemplates };
|
|
19606
19774
|
});
|
|
19607
|
-
this.server.setRequestHandler(ReadResourceRequestSchema, async (
|
|
19608
|
-
const uri = new URL(
|
|
19775
|
+
this.server.setRequestHandler(ReadResourceRequestSchema, async (request2, extra) => {
|
|
19776
|
+
const uri = new URL(request2.params.uri);
|
|
19609
19777
|
const resource = this._registeredResources[uri.toString()];
|
|
19610
19778
|
if (resource) {
|
|
19611
19779
|
if (!resource.enabled) {
|
|
@@ -19644,21 +19812,21 @@ class McpServer {
|
|
|
19644
19812
|
};
|
|
19645
19813
|
})
|
|
19646
19814
|
}));
|
|
19647
|
-
this.server.setRequestHandler(GetPromptRequestSchema, async (
|
|
19648
|
-
const prompt = this._registeredPrompts[
|
|
19815
|
+
this.server.setRequestHandler(GetPromptRequestSchema, async (request2, extra) => {
|
|
19816
|
+
const prompt = this._registeredPrompts[request2.params.name];
|
|
19649
19817
|
if (!prompt) {
|
|
19650
|
-
throw new McpError(ErrorCode.InvalidParams, `Prompt ${
|
|
19818
|
+
throw new McpError(ErrorCode.InvalidParams, `Prompt ${request2.params.name} not found`);
|
|
19651
19819
|
}
|
|
19652
19820
|
if (!prompt.enabled) {
|
|
19653
|
-
throw new McpError(ErrorCode.InvalidParams, `Prompt ${
|
|
19821
|
+
throw new McpError(ErrorCode.InvalidParams, `Prompt ${request2.params.name} disabled`);
|
|
19654
19822
|
}
|
|
19655
19823
|
if (prompt.argsSchema) {
|
|
19656
19824
|
const argsObj = normalizeObjectSchema(prompt.argsSchema);
|
|
19657
|
-
const parseResult = await safeParseAsync2(argsObj,
|
|
19825
|
+
const parseResult = await safeParseAsync2(argsObj, request2.params.arguments);
|
|
19658
19826
|
if (!parseResult.success) {
|
|
19659
19827
|
const error2 = "error" in parseResult ? parseResult.error : "Unknown error";
|
|
19660
19828
|
const errorMessage = getParseErrorMessage(error2);
|
|
19661
|
-
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${
|
|
19829
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request2.params.name}: ${errorMessage}`);
|
|
19662
19830
|
}
|
|
19663
19831
|
const args = parseResult.data;
|
|
19664
19832
|
const cb = prompt.callback;
|
|
@@ -32396,8 +32564,8 @@ function hex2(_params) {
|
|
|
32396
32564
|
return _stringFormat2(ZodCustomStringFormat, "hex", exports_regexes2.hex, _params);
|
|
32397
32565
|
}
|
|
32398
32566
|
function hash(alg, params) {
|
|
32399
|
-
const
|
|
32400
|
-
const format = `${alg}_${
|
|
32567
|
+
const enc2 = params?.enc ?? "hex";
|
|
32568
|
+
const format = `${alg}_${enc2}`;
|
|
32401
32569
|
const regex = exports_regexes2[format];
|
|
32402
32570
|
if (!regex)
|
|
32403
32571
|
throw new Error(`Unrecognized hash format: ${format}`);
|
|
@@ -33967,86 +34135,6 @@ ${answers.length} answer(s)`;
|
|
|
33967
34135
|
};
|
|
33968
34136
|
}
|
|
33969
34137
|
|
|
33970
|
-
// src/notexClient.ts
|
|
33971
|
-
var DEFAULT_API_URL = "http://localhost:3000";
|
|
33972
|
-
function resolveApiUrl(env = process.env) {
|
|
33973
|
-
return env.NOTEX_API_URL?.trim() || DEFAULT_API_URL;
|
|
33974
|
-
}
|
|
33975
|
-
async function request(config3, fetchImpl, method, path2, body) {
|
|
33976
|
-
let res;
|
|
33977
|
-
try {
|
|
33978
|
-
res = await fetchImpl(`${config3.baseUrl}${path2}`, {
|
|
33979
|
-
method,
|
|
33980
|
-
headers: {
|
|
33981
|
-
"x-api-key": config3.apiKey,
|
|
33982
|
-
...body !== undefined ? { "content-type": "application/json" } : {}
|
|
33983
|
-
},
|
|
33984
|
-
body: body !== undefined ? JSON.stringify(body) : undefined
|
|
33985
|
-
});
|
|
33986
|
-
} catch (err) {
|
|
33987
|
-
throw new OpError(ERROR_CODES.notexApiError, `Could not reach the Notex API at ${config3.baseUrl}`, err);
|
|
33988
|
-
}
|
|
33989
|
-
if (res.status === 401)
|
|
33990
|
-
throw new OpError(ERROR_CODES.unauthorized, "Notex rejected the API key");
|
|
33991
|
-
if (res.status === 403)
|
|
33992
|
-
throw new OpError(ERROR_CODES.forbidden, "Not authorized for this Project");
|
|
33993
|
-
if (res.status === 404)
|
|
33994
|
-
throw new OpError(ERROR_CODES.notFound, "Not found in Notex");
|
|
33995
|
-
if (!res.ok) {
|
|
33996
|
-
const body2 = await res.json().catch(() => ({}));
|
|
33997
|
-
throw new OpError(ERROR_CODES.notexApiError, body2.error?.message ?? `Notex API error (HTTP ${res.status})`);
|
|
33998
|
-
}
|
|
33999
|
-
return await res.json();
|
|
34000
|
-
}
|
|
34001
|
-
var enc = (id) => encodeURIComponent(id);
|
|
34002
|
-
function createNotexClient(config3, fetchImpl = fetch) {
|
|
34003
|
-
return {
|
|
34004
|
-
listContexts: (organizationId, repositoryId) => request(config3, fetchImpl, "GET", `/api/v1/organizations/${enc(organizationId)}/repositories/${enc(repositoryId)}/contexts`),
|
|
34005
|
-
getContext: (organizationId, id) => request(config3, fetchImpl, "GET", `/api/v1/organizations/${enc(organizationId)}/contexts/${enc(id)}`),
|
|
34006
|
-
createContext: (organizationId, repositoryId, question) => request(config3, fetchImpl, "POST", `/api/v1/organizations/${enc(organizationId)}/repositories/${enc(repositoryId)}/contexts`, { question }),
|
|
34007
|
-
listFiles: (organizationId, contextId) => request(config3, fetchImpl, "GET", `/api/v1/organizations/${enc(organizationId)}/contexts/${enc(contextId)}/files`),
|
|
34008
|
-
getFile: (organizationId, id) => request(config3, fetchImpl, "GET", `/api/v1/organizations/${enc(organizationId)}/files/${enc(id)}`),
|
|
34009
|
-
createFile: (organizationId, contextId, file2) => request(config3, fetchImpl, "POST", `/api/v1/organizations/${enc(organizationId)}/contexts/${enc(contextId)}/files`, file2),
|
|
34010
|
-
deleteContext: (organizationId, id) => request(config3, fetchImpl, "DELETE", `/api/v1/organizations/${enc(organizationId)}/contexts/${enc(id)}`)
|
|
34011
|
-
};
|
|
34012
|
-
}
|
|
34013
|
-
|
|
34014
|
-
// src/notexConfig.ts
|
|
34015
|
-
import { readFileSync as readFileSync3 } from "node:fs";
|
|
34016
|
-
import { join as join2 } from "node:path";
|
|
34017
|
-
function configFilePath(checkoutPath) {
|
|
34018
|
-
return join2(checkoutPath, ".notex", "notex.json");
|
|
34019
|
-
}
|
|
34020
|
-
function nonEmptyString(value) {
|
|
34021
|
-
return typeof value === "string" && value.length > 0;
|
|
34022
|
-
}
|
|
34023
|
-
function loadNotexConfig(checkoutPath, env = process.env) {
|
|
34024
|
-
let raw;
|
|
34025
|
-
try {
|
|
34026
|
-
raw = readFileSync3(configFilePath(checkoutPath), "utf8");
|
|
34027
|
-
} catch {
|
|
34028
|
-
return { kind: "unlinked", reason: "no .notex/notex.json found" };
|
|
34029
|
-
}
|
|
34030
|
-
let parsed;
|
|
34031
|
-
try {
|
|
34032
|
-
parsed = JSON.parse(raw);
|
|
34033
|
-
} catch {
|
|
34034
|
-
return { kind: "unlinked", reason: ".notex/notex.json is not valid JSON" };
|
|
34035
|
-
}
|
|
34036
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
34037
|
-
return { kind: "unlinked", reason: ".notex/notex.json must be a JSON object" };
|
|
34038
|
-
}
|
|
34039
|
-
const { organizationId, projectId, repositoryId, apiKey } = parsed;
|
|
34040
|
-
if (!nonEmptyString(organizationId) || !nonEmptyString(projectId) || !nonEmptyString(repositoryId)) {
|
|
34041
|
-
return { kind: "unlinked", reason: ".notex/notex.json is missing organizationId, projectId, or repositoryId" };
|
|
34042
|
-
}
|
|
34043
|
-
const resolvedKey = nonEmptyString(env.NOTEX_API_KEY) ? env.NOTEX_API_KEY : apiKey;
|
|
34044
|
-
if (!nonEmptyString(resolvedKey)) {
|
|
34045
|
-
return { kind: "unlinked", reason: ".notex/notex.json is missing apiKey, and NOTEX_API_KEY is not set" };
|
|
34046
|
-
}
|
|
34047
|
-
return { kind: "linked", config: { organizationId, projectId, repositoryId, apiKey: resolvedKey } };
|
|
34048
|
-
}
|
|
34049
|
-
|
|
34050
34138
|
// src/retrievalLog.ts
|
|
34051
34139
|
function createRetrievalLog() {
|
|
34052
34140
|
const seen = new Set;
|
|
@@ -34095,16 +34183,20 @@ var HELP = `notex-companion — local retrieval companion over a checkout's grap
|
|
|
34095
34183
|
Usage:
|
|
34096
34184
|
notex-companion [serve] [options] Start the loopback HTTP server (default command)
|
|
34097
34185
|
notex-companion mcp Start the stdio MCP server
|
|
34186
|
+
notex-companion link [options] Write .notex/notex.json, pairing this checkout to a Notex Repository
|
|
34098
34187
|
|
|
34099
34188
|
Options for serve:
|
|
34100
34189
|
--port <n> Port to bind (default 7717)
|
|
34101
34190
|
--origin <url> Additional allowed CORS origin, beyond the built-in defaults. Repeatable.
|
|
34102
34191
|
--rotate-token Generate a new pairing token, invalidating the old one
|
|
34103
34192
|
-h, --help Show this message
|
|
34104
|
-
`;
|
|
34105
34193
|
|
|
34106
|
-
|
|
34107
|
-
|
|
34194
|
+
Options for link (all required):
|
|
34195
|
+
--organization-id <id>
|
|
34196
|
+
--project-id <id>
|
|
34197
|
+
--repository-id <id>
|
|
34198
|
+
--api-key <key> Generated from Notex Settings → API keys
|
|
34199
|
+
`;
|
|
34108
34200
|
function parseServeArgs(args) {
|
|
34109
34201
|
const origins = [];
|
|
34110
34202
|
let port;
|
|
@@ -34137,6 +34229,17 @@ function parseServeArgs(args) {
|
|
|
34137
34229
|
}
|
|
34138
34230
|
return { port, origins, rotateToken };
|
|
34139
34231
|
}
|
|
34232
|
+
function runLink(args) {
|
|
34233
|
+
const parsed = parseLinkArgs(args);
|
|
34234
|
+
const checkoutPath = process.cwd();
|
|
34235
|
+
link(parsed, { checkoutPath }).then(() => {
|
|
34236
|
+
console.log(`notex-companion: linked ${checkoutPath} to repository ${parsed.repositoryId}`);
|
|
34237
|
+
console.log("notex-companion: wrote .notex/notex.json (mode 0600)");
|
|
34238
|
+
}, (err) => {
|
|
34239
|
+
console.error(`notex-companion link: ${err instanceof Error ? err.message : String(err)}`);
|
|
34240
|
+
process.exit(1);
|
|
34241
|
+
});
|
|
34242
|
+
}
|
|
34140
34243
|
function runMcp() {
|
|
34141
34244
|
startMcpServer(process.cwd()).catch((err) => {
|
|
34142
34245
|
console.error(`notex-companion mcp: failed to start: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -34178,6 +34281,8 @@ function main(argv = process.argv.slice(2)) {
|
|
|
34178
34281
|
try {
|
|
34179
34282
|
if (command === "mcp") {
|
|
34180
34283
|
runMcp();
|
|
34284
|
+
} else if (command === "link") {
|
|
34285
|
+
runLink(rest);
|
|
34181
34286
|
} else if (command === undefined || command === "serve" || command.startsWith("-")) {
|
|
34182
34287
|
runServe(command === "serve" ? rest : argv);
|
|
34183
34288
|
} else {
|
package/dist/index.js
CHANGED
|
@@ -704,9 +704,21 @@ async function toWebRequest(req) {
|
|
|
704
704
|
}
|
|
705
705
|
|
|
706
706
|
// src/pairing.ts
|
|
707
|
-
import { mkdirSync, readFileSync as readFileSync2,
|
|
707
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
708
708
|
import { randomBytes } from "node:crypto";
|
|
709
|
-
import { dirname, join } from "node:path";
|
|
709
|
+
import { dirname as dirname2, join } from "node:path";
|
|
710
|
+
|
|
711
|
+
// src/atomicWrite.ts
|
|
712
|
+
import { mkdirSync, renameSync, writeFileSync } from "node:fs";
|
|
713
|
+
import { dirname } from "node:path";
|
|
714
|
+
function atomicWriteFile(path2, content, mode) {
|
|
715
|
+
mkdirSync(dirname(path2), { recursive: true });
|
|
716
|
+
const tmpPath = `${path2}.${process.pid}.tmp`;
|
|
717
|
+
writeFileSync(tmpPath, content, { mode, flag: "wx" });
|
|
718
|
+
renameSync(tmpPath, path2);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// src/pairing.ts
|
|
710
722
|
function pairingFilePath(checkoutPath) {
|
|
711
723
|
return join(checkoutPath, ".notex", "companion.json");
|
|
712
724
|
}
|
|
@@ -725,14 +737,11 @@ function tokenContents(token) {
|
|
|
725
737
|
return JSON.stringify({ token }, null, 2);
|
|
726
738
|
}
|
|
727
739
|
function createTokenFileExclusive(path2, token) {
|
|
728
|
-
|
|
729
|
-
|
|
740
|
+
mkdirSync2(dirname2(path2), { recursive: true });
|
|
741
|
+
writeFileSync2(path2, tokenContents(token), { mode: 384, flag: "wx" });
|
|
730
742
|
}
|
|
731
743
|
function rewriteTokenFile(path2, token) {
|
|
732
|
-
|
|
733
|
-
const tmpPath = `${path2}.${process.pid}.tmp`;
|
|
734
|
-
writeFileSync(tmpPath, tokenContents(token), { mode: 384, flag: "wx" });
|
|
735
|
-
renameSync(tmpPath, path2);
|
|
744
|
+
atomicWriteFile(path2, tokenContents(token), 384);
|
|
736
745
|
}
|
|
737
746
|
function loadOrCreateToken(checkoutPath, opts = {}) {
|
|
738
747
|
const path2 = pairingFilePath(checkoutPath);
|
package/dist/link.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type LinkArgs = {
|
|
2
|
+
organizationId: string;
|
|
3
|
+
projectId: string;
|
|
4
|
+
repositoryId: string;
|
|
5
|
+
apiKey: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function parseLinkArgs(args: Array<string>): LinkArgs;
|
|
8
|
+
/**
|
|
9
|
+
* Validates `args` against the Notex API, then writes `.notex/notex.json`. Throws `CliUsageError`
|
|
10
|
+
* with an actionable message on bad input; writes nothing in that case.
|
|
11
|
+
*/
|
|
12
|
+
export declare function link(args: LinkArgs, opts?: {
|
|
13
|
+
checkoutPath: string;
|
|
14
|
+
fetchImpl?: typeof fetch;
|
|
15
|
+
}): Promise<void>;
|
package/dist/notexClient.d.ts
CHANGED
|
@@ -12,6 +12,12 @@ export type NotexFile = {
|
|
|
12
12
|
content: string;
|
|
13
13
|
createdAt: string;
|
|
14
14
|
};
|
|
15
|
+
export type NotexRepository = {
|
|
16
|
+
id: string;
|
|
17
|
+
projectId: string;
|
|
18
|
+
name: string;
|
|
19
|
+
description: string | null;
|
|
20
|
+
};
|
|
15
21
|
export type NotexClientConfig = {
|
|
16
22
|
baseUrl: string;
|
|
17
23
|
apiKey: string;
|
|
@@ -23,6 +29,10 @@ export declare function resolveApiUrl(env?: {
|
|
|
23
29
|
NOTEX_API_URL?: string;
|
|
24
30
|
}): string;
|
|
25
31
|
export declare function createNotexClient(config: NotexClientConfig, fetchImpl?: FetchImpl): {
|
|
32
|
+
/** Used by `link.ts` (TBR-85) to validate organizationId/repositoryId/apiKey together before
|
|
33
|
+
* writing `.notex/notex.json` — the returned `projectId` is checked against the one the user
|
|
34
|
+
* supplied, since no route accepts all three ids at once. */
|
|
35
|
+
getRepository: (organizationId: string, id: string) => Promise<NotexRepository>;
|
|
26
36
|
listContexts: (organizationId: string, repositoryId: string) => Promise<NotexContext[]>;
|
|
27
37
|
getContext: (organizationId: string, id: string) => Promise<NotexContext>;
|
|
28
38
|
createContext: (organizationId: string, repositoryId: string, question: string) => Promise<NotexContext>;
|
package/dist/notexConfig.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export type NotexConfigState = {
|
|
|
11
11
|
kind: "unlinked";
|
|
12
12
|
reason: string;
|
|
13
13
|
};
|
|
14
|
+
/** Also used by `link.ts` (TBR-85), the sole writer of this path — this module is the sole reader. */
|
|
15
|
+
export declare function configFilePath(checkoutPath: string): string;
|
|
14
16
|
/** `NOTEX_API_KEY` overrides the file's `apiKey` (companion-api.md / notex-mcp-server.md §7). */
|
|
15
17
|
export declare function loadNotexConfig(checkoutPath: string, env?: {
|
|
16
18
|
NOTEX_API_KEY?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "notex-companion",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Local retrieval companion for Notex — reads a checkout's graphify-out/graph.json and serves deterministic search/query/path/node lookups over loopback HTTP and MCP stdio. No LLM, no graph building, no network beyond 127.0.0.1.",
|
|
5
5
|
"keywords": ["notex", "graphify", "mcp", "code-graph"],
|
|
6
6
|
"license": "MIT",
|