@adrkit/mcp 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -4
- package/dist/bin.js +35 -30
- package/dist/index.d.ts +24 -2
- package/dist/index.js +27 -29
- package/dist/main-module.d.ts +9 -0
- package/dist/server.d.ts +17 -3
- package/dist/tools/get-decision-context.d.ts +1 -1
- package/dist/tools/get-decision.d.ts +1 -1
- package/dist/tools/list-superseded.d.ts +1 -1
- package/dist/tools/search-decisions.d.ts +1 -1
- package/dist/tools/shared.d.ts +2 -2
- package/package.json +5 -4
- package/src/index.ts +35 -20
- package/src/main-module.ts +20 -1
- package/src/server.ts +38 -5
- package/src/tools/get-decision-context.ts +1 -1
- package/src/tools/get-decision.ts +1 -1
- package/src/tools/list-superseded.ts +1 -1
- package/src/tools/search-decisions.ts +1 -1
- package/src/tools/shared.ts +10 -10
package/README.md
CHANGED
|
@@ -16,8 +16,10 @@ agents stop re-proposing paths the team already ruled out.
|
|
|
16
16
|
|
|
17
17
|
Part of [adrkit](https://adrkit.dev). The corpus lives in git as one Markdown file
|
|
18
18
|
per decision with typed YAML frontmatter (`@adrkit/core`); this server only reads
|
|
19
|
-
it.
|
|
20
|
-
|
|
19
|
+
it. Listed in the [official MCP registry](https://registry.modelcontextprotocol.io)
|
|
20
|
+
as **`dev.adrkit/mcp`** (this is the Node/npm `@adrkit/mcp` package — unrelated to
|
|
21
|
+
the `adr-kit` Python package on PyPI). A registry listing is distribution, not
|
|
22
|
+
adoption; see Maturity below.
|
|
21
23
|
|
|
22
24
|
## Maturity
|
|
23
25
|
|
|
@@ -40,6 +42,30 @@ npx -y @adrkit/mcp --cwd /path/to/your/repo --dir docs/adr
|
|
|
40
42
|
It speaks JSON-RPC over stdio, so you normally point an MCP client at it rather than
|
|
41
43
|
running it by hand. Copy-pasteable client configs follow.
|
|
42
44
|
|
|
45
|
+
### Protocol revisions
|
|
46
|
+
|
|
47
|
+
The server speaks **both** MCP protocol eras on the same stdio connection, and the
|
|
48
|
+
client picks. The opening exchange selects the era and pins it for the connection's
|
|
49
|
+
lifetime:
|
|
50
|
+
|
|
51
|
+
| Client opens with | Server serves |
|
|
52
|
+
| ----------------------------------------------------- | ------------------------------------------------- |
|
|
53
|
+
| `server/discover`, or any request carrying a 2026 `_meta` envelope | **`2026-07-28`** — stateless, no handshake |
|
|
54
|
+
| `initialize` / `notifications/initialized` | the 2025-era revision it negotiates |
|
|
55
|
+
|
|
56
|
+
On `2026-07-28` there is no `initialize` handshake and no session id: every request
|
|
57
|
+
carries its own protocol version and client capabilities in `_meta`, and every result
|
|
58
|
+
is self-describing (`resultType`, plus server identity in `_meta`). `tools/list` and
|
|
59
|
+
`server/discover` are cacheable (SEP-2549) and are served with `ttlMs: 300000,
|
|
60
|
+
cacheScope: "public"` — the four-tool surface is immutable for the life of the process
|
|
61
|
+
and carries no corpus content, so a client may reuse it instead of re-listing. Corpus
|
|
62
|
+
reads are never cacheable: every `tools/call` loads a fresh projection.
|
|
63
|
+
|
|
64
|
+
Nothing else about the tools changes between eras — same names, same schemas, same
|
|
65
|
+
annotations, same structured results. The server uses none of the features the
|
|
66
|
+
`2026-07-28` revision deprecated (roots, sampling, logging) or removed (sessions,
|
|
67
|
+
`ping`, `resources/subscribe`).
|
|
68
|
+
|
|
43
69
|
### Claude Desktop
|
|
44
70
|
|
|
45
71
|
Edit `claude_desktop_config.json` (macOS:
|
|
@@ -282,7 +308,7 @@ the underlying SDK server, its registrations, or its transport:
|
|
|
282
308
|
import { createAdrkitMcpServer } from '@adrkit/mcp';
|
|
283
309
|
|
|
284
310
|
const server = createAdrkitMcpServer({ cwd: process.cwd(), dir: 'docs/adr' });
|
|
285
|
-
await server.start(); // validates the root,
|
|
311
|
+
await server.start(); // validates the root, then serves one stdio connection
|
|
286
312
|
// ... later:
|
|
287
313
|
await server.close();
|
|
288
314
|
```
|
|
@@ -290,6 +316,12 @@ await server.close();
|
|
|
290
316
|
`createAdrkitMcpServer(options?)` performs no filesystem access at construction and
|
|
291
317
|
returns a frozen, null-prototype handle with exactly `start()` and `close()`.
|
|
292
318
|
|
|
319
|
+
Transport failures — a transport that fails to start, or a background stream error
|
|
320
|
+
such as the `EPIPE` from a client that has gone away — are reported through the
|
|
321
|
+
optional `onError` option, which defaults to a stderr diagnostic. The `adrkit-mcp`
|
|
322
|
+
binary additionally exits non-zero. Nothing is written to stdout: that is reserved
|
|
323
|
+
for protocol frames.
|
|
324
|
+
|
|
293
325
|
## Limits
|
|
294
326
|
|
|
295
327
|
`query` 1–256 code units (non-empty after trimming); `ref` 1–128; `files[]` 1–256
|
|
@@ -323,6 +355,6 @@ traversal.
|
|
|
323
355
|
|
|
324
356
|
Developed and tested with Bun; published artifacts are ESM targeting Node.js `>=22`
|
|
325
357
|
and are verified on Node 22 and 24. Runtime dependencies are exactly `@adrkit/core`,
|
|
326
|
-
`@modelcontextprotocol/
|
|
358
|
+
`@modelcontextprotocol/server` (MCP TypeScript SDK v2), and `zod`.
|
|
327
359
|
|
|
328
360
|
Apache-2.0.
|
package/dist/bin.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import {
|
|
4
|
+
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
5
5
|
import { resolve as resolve2 } from "node:path";
|
|
6
6
|
|
|
7
7
|
// src/server.ts
|
|
8
|
-
import { McpServer } from "@modelcontextprotocol/
|
|
8
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
9
9
|
|
|
10
10
|
// src/corpus/ordering.ts
|
|
11
11
|
import {
|
|
@@ -480,7 +480,7 @@ function searchDecisionsOutputSchema() {
|
|
|
480
480
|
sourcePath: z.string(),
|
|
481
481
|
matchedFields: z.array(z.enum(["id", "title", "tag", "body"]))
|
|
482
482
|
});
|
|
483
|
-
return {
|
|
483
|
+
return z.object({
|
|
484
484
|
corpusHealth: corpusHealthSchema().optional(),
|
|
485
485
|
result: z.discriminatedUnion("outcome", [
|
|
486
486
|
z.object({
|
|
@@ -492,7 +492,7 @@ function searchDecisionsOutputSchema() {
|
|
|
492
492
|
invalidCursorSchema(),
|
|
493
493
|
corpusUnavailableSchema()
|
|
494
494
|
])
|
|
495
|
-
};
|
|
495
|
+
});
|
|
496
496
|
}
|
|
497
497
|
function getDecisionOutputSchema() {
|
|
498
498
|
const fullDecision = z.object({
|
|
@@ -504,7 +504,7 @@ function getDecisionOutputSchema() {
|
|
|
504
504
|
frontmatter: AdrFrontmatter,
|
|
505
505
|
body: z.string()
|
|
506
506
|
});
|
|
507
|
-
return {
|
|
507
|
+
return z.object({
|
|
508
508
|
corpusHealth: corpusHealthSchema().optional(),
|
|
509
509
|
result: z.discriminatedUnion("outcome", [
|
|
510
510
|
z.object({ outcome: z.literal("found"), decision: fullDecision, findings: findingsPageSchema() }),
|
|
@@ -526,7 +526,7 @@ function getDecisionOutputSchema() {
|
|
|
526
526
|
invalidCursorSchema(),
|
|
527
527
|
corpusUnavailableSchema()
|
|
528
528
|
])
|
|
529
|
-
};
|
|
529
|
+
});
|
|
530
530
|
}
|
|
531
531
|
function getDecisionContextOutputSchema() {
|
|
532
532
|
const contextEntry = z.object({
|
|
@@ -537,7 +537,7 @@ function getDecisionContextOutputSchema() {
|
|
|
537
537
|
firedMatchers: z.array(z.object({ type: z.string(), pattern: z.string() })),
|
|
538
538
|
relations: relationRefsSchema()
|
|
539
539
|
});
|
|
540
|
-
return {
|
|
540
|
+
return z.object({
|
|
541
541
|
corpusHealth: corpusHealthSchema().optional(),
|
|
542
542
|
result: z.discriminatedUnion("outcome", [
|
|
543
543
|
z.object({
|
|
@@ -551,7 +551,7 @@ function getDecisionContextOutputSchema() {
|
|
|
551
551
|
invalidCursorSchema(),
|
|
552
552
|
corpusUnavailableSchema()
|
|
553
553
|
])
|
|
554
|
-
};
|
|
554
|
+
});
|
|
555
555
|
}
|
|
556
556
|
function listSupersededOutputSchema() {
|
|
557
557
|
const supersededBy = z.union([
|
|
@@ -578,7 +578,7 @@ function listSupersededOutputSchema() {
|
|
|
578
578
|
sourcePath: z.string(),
|
|
579
579
|
supersededBy
|
|
580
580
|
});
|
|
581
|
-
return {
|
|
581
|
+
return z.object({
|
|
582
582
|
corpusHealth: corpusHealthSchema().optional(),
|
|
583
583
|
result: z.discriminatedUnion("outcome", [
|
|
584
584
|
z.object({
|
|
@@ -590,7 +590,7 @@ function listSupersededOutputSchema() {
|
|
|
590
590
|
invalidCursorSchema(),
|
|
591
591
|
corpusUnavailableSchema()
|
|
592
592
|
])
|
|
593
|
-
};
|
|
593
|
+
});
|
|
594
594
|
}
|
|
595
595
|
function structuredResult(result, text, corpusHealth) {
|
|
596
596
|
const structuredContent = corpusHealth === undefined ? { result } : { corpusHealth, result };
|
|
@@ -993,21 +993,30 @@ function registerListSuperseded(server, config) {
|
|
|
993
993
|
}
|
|
994
994
|
|
|
995
995
|
// src/server.ts
|
|
996
|
-
var SERVER_INFO = { name: "@adrkit/mcp", version: "0.
|
|
996
|
+
var SERVER_INFO = { name: "@adrkit/mcp", version: "0.4.0" };
|
|
997
|
+
var CACHE_HINTS = {
|
|
998
|
+
"tools/list": { ttlMs: 300000, cacheScope: "public" },
|
|
999
|
+
"server/discover": { ttlMs: 300000, cacheScope: "public" }
|
|
1000
|
+
};
|
|
997
1001
|
function buildRegisteredServer(config) {
|
|
998
|
-
const server = new McpServer(SERVER_INFO);
|
|
999
|
-
registerSearchDecisions(server, config);
|
|
1002
|
+
const server = new McpServer(SERVER_INFO, { cacheHints: CACHE_HINTS });
|
|
1000
1003
|
registerGetDecision(server, config);
|
|
1001
1004
|
registerGetDecisionContext(server, config);
|
|
1002
1005
|
registerListSuperseded(server, config);
|
|
1006
|
+
registerSearchDecisions(server, config);
|
|
1003
1007
|
return server;
|
|
1004
1008
|
}
|
|
1005
1009
|
|
|
1006
1010
|
// src/index.ts
|
|
1011
|
+
function writeTransportDiagnostic(error) {
|
|
1012
|
+
process.stderr.write(`adrkit-mcp: transport error: ${error.message}
|
|
1013
|
+
`);
|
|
1014
|
+
}
|
|
1007
1015
|
function createAdrkitMcpServer(options) {
|
|
1008
1016
|
const cwd = resolve2(options?.cwd ?? process.cwd());
|
|
1009
1017
|
const dir = options?.dir ?? "docs/adr";
|
|
1010
|
-
|
|
1018
|
+
const onError = options?.onError ?? writeTransportDiagnostic;
|
|
1019
|
+
let connection;
|
|
1011
1020
|
let startPromise;
|
|
1012
1021
|
let closePromise;
|
|
1013
1022
|
let closed = false;
|
|
@@ -1018,7 +1027,6 @@ function createAdrkitMcpServer(options) {
|
|
|
1018
1027
|
if (startPromise)
|
|
1019
1028
|
return startPromise;
|
|
1020
1029
|
startPromise = (async () => {
|
|
1021
|
-
let nextServer;
|
|
1022
1030
|
try {
|
|
1023
1031
|
const roots = await resolveCanonicalRoots({ cwd, dir });
|
|
1024
1032
|
const config = {
|
|
@@ -1027,19 +1035,9 @@ function createAdrkitMcpServer(options) {
|
|
|
1027
1035
|
expectedCanonicalCwd: roots.canonicalCwd,
|
|
1028
1036
|
maxSourceBytes: MAX_SOURCE_BYTES
|
|
1029
1037
|
};
|
|
1030
|
-
|
|
1031
|
-
server = nextServer;
|
|
1032
|
-
await nextServer.connect(new StdioServerTransport);
|
|
1038
|
+
connection = serveStdio(() => buildRegisteredServer(config), { onerror: onError });
|
|
1033
1039
|
} catch (error) {
|
|
1034
|
-
|
|
1035
|
-
if (nextServer) {
|
|
1036
|
-
try {
|
|
1037
|
-
await nextServer.close();
|
|
1038
|
-
} catch (closeError) {
|
|
1039
|
-
startPromise = undefined;
|
|
1040
|
-
throw new AggregateError([error, closeError], "MCP server startup and cleanup failed");
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1040
|
+
connection = undefined;
|
|
1043
1041
|
startPromise = undefined;
|
|
1044
1042
|
throw error;
|
|
1045
1043
|
}
|
|
@@ -1053,8 +1051,8 @@ function createAdrkitMcpServer(options) {
|
|
|
1053
1051
|
closePromise = (async () => {
|
|
1054
1052
|
if (startPromise)
|
|
1055
1053
|
await startPromise;
|
|
1056
|
-
const current =
|
|
1057
|
-
|
|
1054
|
+
const current = connection;
|
|
1055
|
+
connection = undefined;
|
|
1058
1056
|
if (current)
|
|
1059
1057
|
await current.close();
|
|
1060
1058
|
})();
|
|
@@ -1088,6 +1086,13 @@ function reportUnhandledRejection(reason, write = writeStderr, fail2 = () => {
|
|
|
1088
1086
|
`);
|
|
1089
1087
|
fail2();
|
|
1090
1088
|
}
|
|
1089
|
+
function reportTransportError(error, write = writeStderr, fail2 = () => {
|
|
1090
|
+
process.exitCode = 1;
|
|
1091
|
+
}) {
|
|
1092
|
+
write(`adrkit-mcp: transport error: ${error.message}
|
|
1093
|
+
`);
|
|
1094
|
+
fail2();
|
|
1095
|
+
}
|
|
1091
1096
|
async function main(argv, env) {
|
|
1092
1097
|
let values;
|
|
1093
1098
|
try {
|
|
@@ -1114,7 +1119,7 @@ ${USAGE}`);
|
|
|
1114
1119
|
}
|
|
1115
1120
|
throw error;
|
|
1116
1121
|
}
|
|
1117
|
-
const handle = createAdrkitMcpServer({ cwd, dir });
|
|
1122
|
+
const handle = createAdrkitMcpServer({ cwd, dir, onError: (error) => reportTransportError(error) });
|
|
1118
1123
|
const shutdown = () => {
|
|
1119
1124
|
handle.close().finally(() => process.exit(0));
|
|
1120
1125
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,22 @@
|
|
|
9
9
|
export interface AdrkitMcpServerOptions {
|
|
10
10
|
readonly cwd: string;
|
|
11
11
|
readonly dir: string;
|
|
12
|
+
/**
|
|
13
|
+
* Called for out-of-band transport failures: a transport that fails to start,
|
|
14
|
+
* and every background error the connection reports afterwards (an stdin or
|
|
15
|
+
* stdout stream error, such as the EPIPE from a client that has gone away).
|
|
16
|
+
*
|
|
17
|
+
* This is not optional plumbing. `serveStdio` reports these **only** through
|
|
18
|
+
* its `onerror` callback — it consumes the rejected `start()` promise
|
|
19
|
+
* deliberately — so without a callback a broken transport tears the connection
|
|
20
|
+
* down while the process still exits 0. That is a dead server reporting
|
|
21
|
+
* success, the fail-quiet shape ADR-0016 rejects, so the default writes a
|
|
22
|
+
* diagnostic to stderr rather than staying silent. `main-module.ts` supplies a
|
|
23
|
+
* reporter that also fails the exit status.
|
|
24
|
+
*
|
|
25
|
+
* Never writes to stdout: that is reserved for protocol frames.
|
|
26
|
+
*/
|
|
27
|
+
readonly onError: (error: Error) => void;
|
|
12
28
|
}
|
|
13
29
|
export interface AdrkitMcpServerHandle {
|
|
14
30
|
start(): Promise<void>;
|
|
@@ -16,8 +32,14 @@ export interface AdrkitMcpServerHandle {
|
|
|
16
32
|
}
|
|
17
33
|
/**
|
|
18
34
|
* The public stdio lifecycle factory. Performs NO filesystem access at construction;
|
|
19
|
-
* `start()` validates the configured root,
|
|
20
|
-
*
|
|
35
|
+
* `start()` validates the configured root, then hands a closure-private server factory
|
|
36
|
+
* to the SDK's connection-pinned `serveStdio` entry. The concrete server, its
|
|
21
37
|
* registrations, and its transport remain unreachable to the caller.
|
|
38
|
+
*
|
|
39
|
+
* `serveStdio` — not a hand-wired `StdioServerTransport` — is what makes this server
|
|
40
|
+
* speak protocol revision 2026-07-28. The opening exchange selects the connection's
|
|
41
|
+
* era and pins one factory instance to it; `legacy: 'serve'` (the default) keeps
|
|
42
|
+
* 2025-era clients working unchanged. The four tools are registered once and served
|
|
43
|
+
* identically to both eras.
|
|
22
44
|
*/
|
|
23
45
|
export declare function createAdrkitMcpServer(options?: Partial<AdrkitMcpServerOptions>): Readonly<AdrkitMcpServerHandle>;
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
2
|
+
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
3
3
|
import { resolve as resolve2 } from "node:path";
|
|
4
4
|
|
|
5
5
|
// src/server.ts
|
|
6
|
-
import { McpServer } from "@modelcontextprotocol/
|
|
6
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
7
7
|
|
|
8
8
|
// src/corpus/ordering.ts
|
|
9
9
|
import {
|
|
@@ -478,7 +478,7 @@ function searchDecisionsOutputSchema() {
|
|
|
478
478
|
sourcePath: z.string(),
|
|
479
479
|
matchedFields: z.array(z.enum(["id", "title", "tag", "body"]))
|
|
480
480
|
});
|
|
481
|
-
return {
|
|
481
|
+
return z.object({
|
|
482
482
|
corpusHealth: corpusHealthSchema().optional(),
|
|
483
483
|
result: z.discriminatedUnion("outcome", [
|
|
484
484
|
z.object({
|
|
@@ -490,7 +490,7 @@ function searchDecisionsOutputSchema() {
|
|
|
490
490
|
invalidCursorSchema(),
|
|
491
491
|
corpusUnavailableSchema()
|
|
492
492
|
])
|
|
493
|
-
};
|
|
493
|
+
});
|
|
494
494
|
}
|
|
495
495
|
function getDecisionOutputSchema() {
|
|
496
496
|
const fullDecision = z.object({
|
|
@@ -502,7 +502,7 @@ function getDecisionOutputSchema() {
|
|
|
502
502
|
frontmatter: AdrFrontmatter,
|
|
503
503
|
body: z.string()
|
|
504
504
|
});
|
|
505
|
-
return {
|
|
505
|
+
return z.object({
|
|
506
506
|
corpusHealth: corpusHealthSchema().optional(),
|
|
507
507
|
result: z.discriminatedUnion("outcome", [
|
|
508
508
|
z.object({ outcome: z.literal("found"), decision: fullDecision, findings: findingsPageSchema() }),
|
|
@@ -524,7 +524,7 @@ function getDecisionOutputSchema() {
|
|
|
524
524
|
invalidCursorSchema(),
|
|
525
525
|
corpusUnavailableSchema()
|
|
526
526
|
])
|
|
527
|
-
};
|
|
527
|
+
});
|
|
528
528
|
}
|
|
529
529
|
function getDecisionContextOutputSchema() {
|
|
530
530
|
const contextEntry = z.object({
|
|
@@ -535,7 +535,7 @@ function getDecisionContextOutputSchema() {
|
|
|
535
535
|
firedMatchers: z.array(z.object({ type: z.string(), pattern: z.string() })),
|
|
536
536
|
relations: relationRefsSchema()
|
|
537
537
|
});
|
|
538
|
-
return {
|
|
538
|
+
return z.object({
|
|
539
539
|
corpusHealth: corpusHealthSchema().optional(),
|
|
540
540
|
result: z.discriminatedUnion("outcome", [
|
|
541
541
|
z.object({
|
|
@@ -549,7 +549,7 @@ function getDecisionContextOutputSchema() {
|
|
|
549
549
|
invalidCursorSchema(),
|
|
550
550
|
corpusUnavailableSchema()
|
|
551
551
|
])
|
|
552
|
-
};
|
|
552
|
+
});
|
|
553
553
|
}
|
|
554
554
|
function listSupersededOutputSchema() {
|
|
555
555
|
const supersededBy = z.union([
|
|
@@ -576,7 +576,7 @@ function listSupersededOutputSchema() {
|
|
|
576
576
|
sourcePath: z.string(),
|
|
577
577
|
supersededBy
|
|
578
578
|
});
|
|
579
|
-
return {
|
|
579
|
+
return z.object({
|
|
580
580
|
corpusHealth: corpusHealthSchema().optional(),
|
|
581
581
|
result: z.discriminatedUnion("outcome", [
|
|
582
582
|
z.object({
|
|
@@ -588,7 +588,7 @@ function listSupersededOutputSchema() {
|
|
|
588
588
|
invalidCursorSchema(),
|
|
589
589
|
corpusUnavailableSchema()
|
|
590
590
|
])
|
|
591
|
-
};
|
|
591
|
+
});
|
|
592
592
|
}
|
|
593
593
|
function structuredResult(result, text, corpusHealth) {
|
|
594
594
|
const structuredContent = corpusHealth === undefined ? { result } : { corpusHealth, result };
|
|
@@ -991,21 +991,30 @@ function registerListSuperseded(server, config) {
|
|
|
991
991
|
}
|
|
992
992
|
|
|
993
993
|
// src/server.ts
|
|
994
|
-
var SERVER_INFO = { name: "@adrkit/mcp", version: "0.
|
|
994
|
+
var SERVER_INFO = { name: "@adrkit/mcp", version: "0.4.0" };
|
|
995
|
+
var CACHE_HINTS = {
|
|
996
|
+
"tools/list": { ttlMs: 300000, cacheScope: "public" },
|
|
997
|
+
"server/discover": { ttlMs: 300000, cacheScope: "public" }
|
|
998
|
+
};
|
|
995
999
|
function buildRegisteredServer(config) {
|
|
996
|
-
const server = new McpServer(SERVER_INFO);
|
|
997
|
-
registerSearchDecisions(server, config);
|
|
1000
|
+
const server = new McpServer(SERVER_INFO, { cacheHints: CACHE_HINTS });
|
|
998
1001
|
registerGetDecision(server, config);
|
|
999
1002
|
registerGetDecisionContext(server, config);
|
|
1000
1003
|
registerListSuperseded(server, config);
|
|
1004
|
+
registerSearchDecisions(server, config);
|
|
1001
1005
|
return server;
|
|
1002
1006
|
}
|
|
1003
1007
|
|
|
1004
1008
|
// src/index.ts
|
|
1009
|
+
function writeTransportDiagnostic(error) {
|
|
1010
|
+
process.stderr.write(`adrkit-mcp: transport error: ${error.message}
|
|
1011
|
+
`);
|
|
1012
|
+
}
|
|
1005
1013
|
function createAdrkitMcpServer(options) {
|
|
1006
1014
|
const cwd = resolve2(options?.cwd ?? process.cwd());
|
|
1007
1015
|
const dir = options?.dir ?? "docs/adr";
|
|
1008
|
-
|
|
1016
|
+
const onError = options?.onError ?? writeTransportDiagnostic;
|
|
1017
|
+
let connection;
|
|
1009
1018
|
let startPromise;
|
|
1010
1019
|
let closePromise;
|
|
1011
1020
|
let closed = false;
|
|
@@ -1016,7 +1025,6 @@ function createAdrkitMcpServer(options) {
|
|
|
1016
1025
|
if (startPromise)
|
|
1017
1026
|
return startPromise;
|
|
1018
1027
|
startPromise = (async () => {
|
|
1019
|
-
let nextServer;
|
|
1020
1028
|
try {
|
|
1021
1029
|
const roots = await resolveCanonicalRoots({ cwd, dir });
|
|
1022
1030
|
const config = {
|
|
@@ -1025,19 +1033,9 @@ function createAdrkitMcpServer(options) {
|
|
|
1025
1033
|
expectedCanonicalCwd: roots.canonicalCwd,
|
|
1026
1034
|
maxSourceBytes: MAX_SOURCE_BYTES
|
|
1027
1035
|
};
|
|
1028
|
-
|
|
1029
|
-
server = nextServer;
|
|
1030
|
-
await nextServer.connect(new StdioServerTransport);
|
|
1036
|
+
connection = serveStdio(() => buildRegisteredServer(config), { onerror: onError });
|
|
1031
1037
|
} catch (error) {
|
|
1032
|
-
|
|
1033
|
-
if (nextServer) {
|
|
1034
|
-
try {
|
|
1035
|
-
await nextServer.close();
|
|
1036
|
-
} catch (closeError) {
|
|
1037
|
-
startPromise = undefined;
|
|
1038
|
-
throw new AggregateError([error, closeError], "MCP server startup and cleanup failed");
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1038
|
+
connection = undefined;
|
|
1041
1039
|
startPromise = undefined;
|
|
1042
1040
|
throw error;
|
|
1043
1041
|
}
|
|
@@ -1051,8 +1049,8 @@ function createAdrkitMcpServer(options) {
|
|
|
1051
1049
|
closePromise = (async () => {
|
|
1052
1050
|
if (startPromise)
|
|
1053
1051
|
await startPromise;
|
|
1054
|
-
const current =
|
|
1055
|
-
|
|
1052
|
+
const current = connection;
|
|
1053
|
+
connection = undefined;
|
|
1056
1054
|
if (current)
|
|
1057
1055
|
await current.close();
|
|
1058
1056
|
})();
|
package/dist/main-module.d.ts
CHANGED
|
@@ -8,4 +8,13 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function isMainModule(moduleUrl: string, argvPath: string | undefined): boolean;
|
|
10
10
|
export declare function reportUnhandledRejection(reason: unknown, write?: (text: string) => void, fail?: () => void): void;
|
|
11
|
+
/**
|
|
12
|
+
* Out-of-band transport failures reach the bin here.
|
|
13
|
+
*
|
|
14
|
+
* `serveStdio` reports them only through its `onerror` callback — it consumes
|
|
15
|
+
* the rejected `start()` promise itself — so this is the only path by which a
|
|
16
|
+
* broken transport can reach stderr and a non-zero exit status. Without it the
|
|
17
|
+
* connection tears down while the process still exits 0 (ADR-0016).
|
|
18
|
+
*/
|
|
19
|
+
export declare function reportTransportError(error: Error, write?: (text: string) => void, fail?: () => void): void;
|
|
11
20
|
export declare function main(argv: string[], env: Record<string, string | undefined>): Promise<0 | 1 | 2>;
|
package/dist/server.d.ts
CHANGED
|
@@ -6,11 +6,25 @@
|
|
|
6
6
|
* exposes only the sealed lifecycle handle. This module is absent from
|
|
7
7
|
* `package.json#exports` and every public subpath.
|
|
8
8
|
*/
|
|
9
|
-
import { McpServer } from '@modelcontextprotocol/
|
|
9
|
+
import { McpServer } from '@modelcontextprotocol/server';
|
|
10
10
|
import type { ToolConfig } from './tools/shared.js';
|
|
11
11
|
export declare const SERVER_INFO: {
|
|
12
12
|
readonly name: "@adrkit/mcp";
|
|
13
|
-
readonly version: "0.
|
|
13
|
+
readonly version: "0.4.0";
|
|
14
14
|
};
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* The MCP protocol revision this server serves through `serveStdio`'s modern era.
|
|
17
|
+
*
|
|
18
|
+
* The SDK keeps the revision string internal (`LATEST_PROTOCOL_VERSION` names the
|
|
19
|
+
* latest *legacy*-era version, `2025-11-25`), so the modern revision is stated here
|
|
20
|
+
* once and asserted against the wire in `test/bin.test.ts`.
|
|
21
|
+
*/
|
|
22
|
+
export declare const MODERN_PROTOCOL_VERSION: "2026-07-28";
|
|
23
|
+
/**
|
|
24
|
+
* Package-internal: build the concrete server with exactly the four ratified tools.
|
|
25
|
+
*
|
|
26
|
+
* Registration order is lexicographic by tool name so `tools/list` answers in a
|
|
27
|
+
* deterministic, self-evidently stable order (2026-07-28 minor change 3 — servers
|
|
28
|
+
* SHOULD do this so clients can cache catalogs and keep upstream prompt caches warm).
|
|
29
|
+
*/
|
|
16
30
|
export declare function buildRegisteredServer(config: ToolConfig): McpServer;
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
* reading a caller-supplied path. One canonical flat walk is paginated, then the
|
|
7
7
|
* page is partitioned by status.
|
|
8
8
|
*/
|
|
9
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
9
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
10
10
|
import { type ToolConfig } from './shared.js';
|
|
11
11
|
export declare function registerGetDecisionContext(server: McpServer, config: ToolConfig): void;
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
* resolved through the fresh local `byId` bucket into found / not-found /
|
|
7
7
|
* ambiguous-local-id. Relation refs are surfaced verbatim, never expanded.
|
|
8
8
|
*/
|
|
9
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
9
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
10
10
|
import { type ToolConfig } from './shared.js';
|
|
11
11
|
export declare function registerGetDecision(server: McpServer, config: ToolConfig): void;
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
* lineage, never embeds candidate arrays, and mints only the two specified derived
|
|
7
7
|
* finding templates.
|
|
8
8
|
*/
|
|
9
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
9
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
10
10
|
import { type ToolConfig } from './shared.js';
|
|
11
11
|
export declare function registerListSuperseded(server: McpServer, config: ToolConfig): void;
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
* normalizer. Graveyard records are included by default. Returns bounded summaries
|
|
7
7
|
* only — never a body, ranking score, or hidden index.
|
|
8
8
|
*/
|
|
9
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
9
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
10
10
|
import { type ToolConfig } from './shared.js';
|
|
11
11
|
export declare function registerSearchDecisions(server: McpServer, config: ToolConfig): void;
|
package/dist/tools/shared.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
import { AdrFrontmatter, Status, Scope, type Finding, type FiredMatcher } from '@adrkit/core';
|
|
9
|
-
import type { CallToolResult } from '@modelcontextprotocol/
|
|
9
|
+
import type { CallToolResult } from '@modelcontextprotocol/server';
|
|
10
10
|
import { type CorpusHealth, type CorpusProjection, type CorpusUnavailableReason } from '../corpus/projection.js';
|
|
11
11
|
import type { InvalidCursorReason, Page } from '../pagination/cursor.js';
|
|
12
12
|
export type { Finding, FiredMatcher } from '@adrkit/core';
|
|
@@ -215,7 +215,7 @@ export type TextSpec = {
|
|
|
215
215
|
export declare function cap512(value: string): string;
|
|
216
216
|
export declare function renderResponseText(spec: TextSpec): string;
|
|
217
217
|
export type ToolInputSchema = z.ZodType;
|
|
218
|
-
export type ToolOutputSchema = z.
|
|
218
|
+
export type ToolOutputSchema = z.ZodType;
|
|
219
219
|
export declare function searchDecisionsInputSchema(): ToolInputSchema;
|
|
220
220
|
export declare function getDecisionInputSchema(): ToolInputSchema;
|
|
221
221
|
export declare function getDecisionContextInputSchema(): ToolInputSchema;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adrkit/mcp",
|
|
3
3
|
"mcpName": "dev.adrkit/mcp",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"description": "Local, read-only Model Context Protocol server exposing adrkit decision retrieval over stdio.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "Apache-2.0",
|
|
@@ -45,11 +45,12 @@
|
|
|
45
45
|
"typecheck": "tsc --noEmit --customConditions bun --project ../../tsconfig.json"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@adrkit/core": "0.
|
|
49
|
-
"@modelcontextprotocol/
|
|
50
|
-
"zod": "^4"
|
|
48
|
+
"@adrkit/core": "0.4.0",
|
|
49
|
+
"@modelcontextprotocol/server": "2.0.0",
|
|
50
|
+
"zod": "^4.2.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
+
"@modelcontextprotocol/client": "2.0.0",
|
|
53
54
|
"@types/bun": "latest"
|
|
54
55
|
},
|
|
55
56
|
"files": [
|
package/src/index.ts
CHANGED
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
* construction time (data-model.md §8, contracts/tools.md §1).
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
10
|
+
import { serveStdio, type StdioServerHandle } from '@modelcontextprotocol/server/stdio';
|
|
12
11
|
import { resolve } from 'node:path';
|
|
13
12
|
import { buildRegisteredServer } from './server.ts';
|
|
14
13
|
import { resolveCanonicalRoots, MAX_SOURCE_BYTES } from './corpus/projection.ts';
|
|
@@ -17,6 +16,22 @@ import type { ToolConfig } from './tools/shared.ts';
|
|
|
17
16
|
export interface AdrkitMcpServerOptions {
|
|
18
17
|
readonly cwd: string;
|
|
19
18
|
readonly dir: string;
|
|
19
|
+
/**
|
|
20
|
+
* Called for out-of-band transport failures: a transport that fails to start,
|
|
21
|
+
* and every background error the connection reports afterwards (an stdin or
|
|
22
|
+
* stdout stream error, such as the EPIPE from a client that has gone away).
|
|
23
|
+
*
|
|
24
|
+
* This is not optional plumbing. `serveStdio` reports these **only** through
|
|
25
|
+
* its `onerror` callback — it consumes the rejected `start()` promise
|
|
26
|
+
* deliberately — so without a callback a broken transport tears the connection
|
|
27
|
+
* down while the process still exits 0. That is a dead server reporting
|
|
28
|
+
* success, the fail-quiet shape ADR-0016 rejects, so the default writes a
|
|
29
|
+
* diagnostic to stderr rather than staying silent. `main-module.ts` supplies a
|
|
30
|
+
* reporter that also fails the exit status.
|
|
31
|
+
*
|
|
32
|
+
* Never writes to stdout: that is reserved for protocol frames.
|
|
33
|
+
*/
|
|
34
|
+
readonly onError: (error: Error) => void;
|
|
20
35
|
}
|
|
21
36
|
|
|
22
37
|
export interface AdrkitMcpServerHandle {
|
|
@@ -24,19 +39,30 @@ export interface AdrkitMcpServerHandle {
|
|
|
24
39
|
close(): Promise<void>;
|
|
25
40
|
}
|
|
26
41
|
|
|
42
|
+
function writeTransportDiagnostic(error: Error): void {
|
|
43
|
+
process.stderr.write(`adrkit-mcp: transport error: ${error.message}\n`);
|
|
44
|
+
}
|
|
45
|
+
|
|
27
46
|
/**
|
|
28
47
|
* The public stdio lifecycle factory. Performs NO filesystem access at construction;
|
|
29
|
-
* `start()` validates the configured root,
|
|
30
|
-
*
|
|
48
|
+
* `start()` validates the configured root, then hands a closure-private server factory
|
|
49
|
+
* to the SDK's connection-pinned `serveStdio` entry. The concrete server, its
|
|
31
50
|
* registrations, and its transport remain unreachable to the caller.
|
|
51
|
+
*
|
|
52
|
+
* `serveStdio` — not a hand-wired `StdioServerTransport` — is what makes this server
|
|
53
|
+
* speak protocol revision 2026-07-28. The opening exchange selects the connection's
|
|
54
|
+
* era and pins one factory instance to it; `legacy: 'serve'` (the default) keeps
|
|
55
|
+
* 2025-era clients working unchanged. The four tools are registered once and served
|
|
56
|
+
* identically to both eras.
|
|
32
57
|
*/
|
|
33
58
|
export function createAdrkitMcpServer(
|
|
34
59
|
options?: Partial<AdrkitMcpServerOptions>,
|
|
35
60
|
): Readonly<AdrkitMcpServerHandle> {
|
|
36
61
|
const cwd = resolve(options?.cwd ?? process.cwd());
|
|
37
62
|
const dir = options?.dir ?? 'docs/adr';
|
|
63
|
+
const onError = options?.onError ?? writeTransportDiagnostic;
|
|
38
64
|
|
|
39
|
-
let
|
|
65
|
+
let connection: StdioServerHandle | undefined;
|
|
40
66
|
let startPromise: Promise<void> | undefined;
|
|
41
67
|
let closePromise: Promise<void> | undefined;
|
|
42
68
|
let closed = false;
|
|
@@ -48,7 +74,6 @@ export function createAdrkitMcpServer(
|
|
|
48
74
|
if (startPromise) return startPromise;
|
|
49
75
|
|
|
50
76
|
startPromise = (async () => {
|
|
51
|
-
let nextServer: McpServer | undefined;
|
|
52
77
|
try {
|
|
53
78
|
const roots = await resolveCanonicalRoots({ cwd, dir });
|
|
54
79
|
const config: ToolConfig = {
|
|
@@ -57,19 +82,9 @@ export function createAdrkitMcpServer(
|
|
|
57
82
|
expectedCanonicalCwd: roots.canonicalCwd,
|
|
58
83
|
maxSourceBytes: MAX_SOURCE_BYTES,
|
|
59
84
|
};
|
|
60
|
-
|
|
61
|
-
server = nextServer;
|
|
62
|
-
await nextServer.connect(new StdioServerTransport());
|
|
85
|
+
connection = serveStdio(() => buildRegisteredServer(config), { onerror: onError });
|
|
63
86
|
} catch (error) {
|
|
64
|
-
|
|
65
|
-
if (nextServer) {
|
|
66
|
-
try {
|
|
67
|
-
await nextServer.close();
|
|
68
|
-
} catch (closeError) {
|
|
69
|
-
startPromise = undefined;
|
|
70
|
-
throw new AggregateError([error, closeError], 'MCP server startup and cleanup failed');
|
|
71
|
-
}
|
|
72
|
-
}
|
|
87
|
+
connection = undefined;
|
|
73
88
|
startPromise = undefined;
|
|
74
89
|
throw error;
|
|
75
90
|
}
|
|
@@ -82,8 +97,8 @@ export function createAdrkitMcpServer(
|
|
|
82
97
|
closed = true;
|
|
83
98
|
closePromise = (async () => {
|
|
84
99
|
if (startPromise) await startPromise;
|
|
85
|
-
const current =
|
|
86
|
-
|
|
100
|
+
const current = connection;
|
|
101
|
+
connection = undefined;
|
|
87
102
|
if (current) await current.close();
|
|
88
103
|
})();
|
|
89
104
|
return closePromise;
|
package/src/main-module.ts
CHANGED
|
@@ -37,6 +37,25 @@ export function reportUnhandledRejection(
|
|
|
37
37
|
fail();
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Out-of-band transport failures reach the bin here.
|
|
42
|
+
*
|
|
43
|
+
* `serveStdio` reports them only through its `onerror` callback — it consumes
|
|
44
|
+
* the rejected `start()` promise itself — so this is the only path by which a
|
|
45
|
+
* broken transport can reach stderr and a non-zero exit status. Without it the
|
|
46
|
+
* connection tears down while the process still exits 0 (ADR-0016).
|
|
47
|
+
*/
|
|
48
|
+
export function reportTransportError(
|
|
49
|
+
error: Error,
|
|
50
|
+
write: (text: string) => void = writeStderr,
|
|
51
|
+
fail: () => void = () => {
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
},
|
|
54
|
+
): void {
|
|
55
|
+
write(`adrkit-mcp: transport error: ${error.message}\n`);
|
|
56
|
+
fail();
|
|
57
|
+
}
|
|
58
|
+
|
|
40
59
|
export async function main(
|
|
41
60
|
argv: string[],
|
|
42
61
|
env: Record<string, string | undefined>,
|
|
@@ -68,7 +87,7 @@ export async function main(
|
|
|
68
87
|
throw error;
|
|
69
88
|
}
|
|
70
89
|
|
|
71
|
-
const handle = createAdrkitMcpServer({ cwd, dir });
|
|
90
|
+
const handle = createAdrkitMcpServer({ cwd, dir, onError: (error) => reportTransportError(error) });
|
|
72
91
|
|
|
73
92
|
const shutdown = (): void => {
|
|
74
93
|
void handle.close().finally(() => process.exit(0));
|
package/src/server.ts
CHANGED
|
@@ -7,21 +7,54 @@
|
|
|
7
7
|
* `package.json#exports` and every public subpath.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { McpServer } from '@modelcontextprotocol/
|
|
10
|
+
import { McpServer, type ServerOptions } from '@modelcontextprotocol/server';
|
|
11
11
|
import { registerSearchDecisions } from './tools/search-decisions.ts';
|
|
12
12
|
import { registerGetDecision } from './tools/get-decision.ts';
|
|
13
13
|
import { registerGetDecisionContext } from './tools/get-decision-context.ts';
|
|
14
14
|
import { registerListSuperseded } from './tools/list-superseded.ts';
|
|
15
15
|
import type { ToolConfig } from './tools/shared.ts';
|
|
16
16
|
|
|
17
|
-
export const SERVER_INFO = { name: '@adrkit/mcp', version: '0.
|
|
17
|
+
export const SERVER_INFO = { name: '@adrkit/mcp', version: '0.4.0' } as const;
|
|
18
18
|
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* The MCP protocol revision this server serves through `serveStdio`'s modern era.
|
|
21
|
+
*
|
|
22
|
+
* The SDK keeps the revision string internal (`LATEST_PROTOCOL_VERSION` names the
|
|
23
|
+
* latest *legacy*-era version, `2025-11-25`), so the modern revision is stated here
|
|
24
|
+
* once and asserted against the wire in `test/bin.test.ts`.
|
|
25
|
+
*/
|
|
26
|
+
export const MODERN_PROTOCOL_VERSION = '2026-07-28' as const;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* SEP-2549 cache hints for the two cacheable results this server can answer.
|
|
30
|
+
*
|
|
31
|
+
* Both are immutable for the lifetime of the process and carry no corpus content,
|
|
32
|
+
* caller identity, or per-request state: `tools/list` is the four ratified tools with
|
|
33
|
+
* their fixed schemas and annotations, and `server/discover` is the supported
|
|
34
|
+
* revisions plus the tools capability. They are therefore honestly `public` and safe
|
|
35
|
+
* to cache, which spares an agent a round trip per re-list. Corpus reads are NOT
|
|
36
|
+
* cacheable and are unaffected — every `tools/call` still loads a fresh projection.
|
|
37
|
+
*
|
|
38
|
+
* Without this the SDK falls back to the conservative `{ ttlMs: 0, cacheScope:
|
|
39
|
+
* 'private' }`. 2025-era responses never carry these fields either way.
|
|
40
|
+
*/
|
|
41
|
+
const CACHE_HINTS = {
|
|
42
|
+
'tools/list': { ttlMs: 300_000, cacheScope: 'public' },
|
|
43
|
+
'server/discover': { ttlMs: 300_000, cacheScope: 'public' },
|
|
44
|
+
} as const satisfies ServerOptions['cacheHints'];
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Package-internal: build the concrete server with exactly the four ratified tools.
|
|
48
|
+
*
|
|
49
|
+
* Registration order is lexicographic by tool name so `tools/list` answers in a
|
|
50
|
+
* deterministic, self-evidently stable order (2026-07-28 minor change 3 — servers
|
|
51
|
+
* SHOULD do this so clients can cache catalogs and keep upstream prompt caches warm).
|
|
52
|
+
*/
|
|
20
53
|
export function buildRegisteredServer(config: ToolConfig): McpServer {
|
|
21
|
-
const server = new McpServer(SERVER_INFO);
|
|
22
|
-
registerSearchDecisions(server, config);
|
|
54
|
+
const server = new McpServer(SERVER_INFO, { cacheHints: CACHE_HINTS });
|
|
23
55
|
registerGetDecision(server, config);
|
|
24
56
|
registerGetDecisionContext(server, config);
|
|
25
57
|
registerListSuperseded(server, config);
|
|
58
|
+
registerSearchDecisions(server, config);
|
|
26
59
|
return server;
|
|
27
60
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* page is partitioned by status.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
10
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
11
11
|
import { decisionBucketFor, resolveAffects, type Adr, type Finding, type FiredMatcher } from '@adrkit/core';
|
|
12
12
|
import { compareCodeUnits, sortFindingsCanonical } from '../corpus/ordering.ts';
|
|
13
13
|
import { paginate, queryShapeHash } from '../pagination/cursor.ts';
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* ambiguous-local-id. Relation refs are surfaced verbatim, never expanded.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
10
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
11
11
|
import { parseAdrRef, type Adr } from '@adrkit/core';
|
|
12
12
|
import { paginate, queryShapeHash, checkInapplicablePrimaryCursor } from '../pagination/cursor.ts';
|
|
13
13
|
import {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* finding templates.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
10
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
11
11
|
import { parseAdrRef, type Adr, type Finding } from '@adrkit/core';
|
|
12
12
|
import { sortFindingsCanonical } from '../corpus/ordering.ts';
|
|
13
13
|
import { paginate, queryShapeHash } from '../pagination/cursor.ts';
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* only — never a body, ranking score, or hidden index.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
10
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
11
11
|
import type { Adr } from '@adrkit/core';
|
|
12
12
|
import { compareCodeUnits } from '../corpus/ordering.ts';
|
|
13
13
|
import { normalize } from '../search/normalize.ts';
|
package/src/tools/shared.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import { AdrFrontmatter, AdrRef, Status, Scope, type Finding, type FiredMatcher } from '@adrkit/core';
|
|
10
|
-
import type { CallToolResult } from '@modelcontextprotocol/
|
|
10
|
+
import type { CallToolResult } from '@modelcontextprotocol/server';
|
|
11
11
|
import {
|
|
12
12
|
loadCorpusProjection,
|
|
13
13
|
CorpusUnavailableError,
|
|
@@ -265,7 +265,7 @@ export function renderResponseText(spec: TextSpec): string {
|
|
|
265
265
|
* ------------------------------------------------------------------ */
|
|
266
266
|
|
|
267
267
|
export type ToolInputSchema = z.ZodType;
|
|
268
|
-
export type ToolOutputSchema = z.
|
|
268
|
+
export type ToolOutputSchema = z.ZodType;
|
|
269
269
|
|
|
270
270
|
const uniqueArray = <T>(schema: z.ZodType<T>, min: number, max: number) =>
|
|
271
271
|
z
|
|
@@ -392,7 +392,7 @@ export function searchDecisionsOutputSchema(): ToolOutputSchema {
|
|
|
392
392
|
sourcePath: z.string(),
|
|
393
393
|
matchedFields: z.array(z.enum(['id', 'title', 'tag', 'body'])),
|
|
394
394
|
});
|
|
395
|
-
return {
|
|
395
|
+
return z.object({
|
|
396
396
|
corpusHealth: corpusHealthSchema().optional(),
|
|
397
397
|
result: z.discriminatedUnion('outcome', [
|
|
398
398
|
z.object({
|
|
@@ -404,7 +404,7 @@ export function searchDecisionsOutputSchema(): ToolOutputSchema {
|
|
|
404
404
|
invalidCursorSchema(),
|
|
405
405
|
corpusUnavailableSchema(),
|
|
406
406
|
]),
|
|
407
|
-
};
|
|
407
|
+
});
|
|
408
408
|
}
|
|
409
409
|
|
|
410
410
|
export function getDecisionOutputSchema(): ToolOutputSchema {
|
|
@@ -417,7 +417,7 @@ export function getDecisionOutputSchema(): ToolOutputSchema {
|
|
|
417
417
|
frontmatter: AdrFrontmatter,
|
|
418
418
|
body: z.string(),
|
|
419
419
|
});
|
|
420
|
-
return {
|
|
420
|
+
return z.object({
|
|
421
421
|
corpusHealth: corpusHealthSchema().optional(),
|
|
422
422
|
result: z.discriminatedUnion('outcome', [
|
|
423
423
|
z.object({ outcome: z.literal('found'), decision: fullDecision, findings: findingsPageSchema() }),
|
|
@@ -439,7 +439,7 @@ export function getDecisionOutputSchema(): ToolOutputSchema {
|
|
|
439
439
|
invalidCursorSchema(),
|
|
440
440
|
corpusUnavailableSchema(),
|
|
441
441
|
]),
|
|
442
|
-
};
|
|
442
|
+
});
|
|
443
443
|
}
|
|
444
444
|
|
|
445
445
|
export function getDecisionContextOutputSchema(): ToolOutputSchema {
|
|
@@ -451,7 +451,7 @@ export function getDecisionContextOutputSchema(): ToolOutputSchema {
|
|
|
451
451
|
firedMatchers: z.array(z.object({ type: z.string(), pattern: z.string() })),
|
|
452
452
|
relations: relationRefsSchema(),
|
|
453
453
|
});
|
|
454
|
-
return {
|
|
454
|
+
return z.object({
|
|
455
455
|
corpusHealth: corpusHealthSchema().optional(),
|
|
456
456
|
result: z.discriminatedUnion('outcome', [
|
|
457
457
|
z.object({
|
|
@@ -465,7 +465,7 @@ export function getDecisionContextOutputSchema(): ToolOutputSchema {
|
|
|
465
465
|
invalidCursorSchema(),
|
|
466
466
|
corpusUnavailableSchema(),
|
|
467
467
|
]),
|
|
468
|
-
};
|
|
468
|
+
});
|
|
469
469
|
}
|
|
470
470
|
|
|
471
471
|
export function listSupersededOutputSchema(): ToolOutputSchema {
|
|
@@ -493,7 +493,7 @@ export function listSupersededOutputSchema(): ToolOutputSchema {
|
|
|
493
493
|
sourcePath: z.string(),
|
|
494
494
|
supersededBy,
|
|
495
495
|
});
|
|
496
|
-
return {
|
|
496
|
+
return z.object({
|
|
497
497
|
corpusHealth: corpusHealthSchema().optional(),
|
|
498
498
|
result: z.discriminatedUnion('outcome', [
|
|
499
499
|
z.object({
|
|
@@ -505,7 +505,7 @@ export function listSupersededOutputSchema(): ToolOutputSchema {
|
|
|
505
505
|
invalidCursorSchema(),
|
|
506
506
|
corpusUnavailableSchema(),
|
|
507
507
|
]),
|
|
508
|
-
};
|
|
508
|
+
});
|
|
509
509
|
}
|
|
510
510
|
|
|
511
511
|
/* ------------------------------------------------------------------ *
|