@adrkit/mcp 0.2.0 → 0.3.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/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/sdk/server/mcp.js';
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.1.0' } as const;
17
+ export const SERVER_INFO = { name: '@adrkit/mcp', version: '0.3.0' } as const;
18
18
 
19
- /** Package-internal: build the concrete server with exactly the four ratified tools. */
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,8 +7,8 @@
7
7
  * page is partitioned by status.
8
8
  */
9
9
 
10
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
11
- import { resolveAffects, type Adr, type Finding, type FiredMatcher } from '@adrkit/core';
10
+ import type { McpServer } from '@modelcontextprotocol/server';
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';
14
14
  import {
@@ -37,10 +37,12 @@ interface GetDecisionContextArgs {
37
37
 
38
38
  type Bucket = 'governing' | 'activeProposals' | 'history';
39
39
 
40
+ /**
41
+ * Delegates to `@adrkit/core`'s `decisionBucketFor` so this tool and the CLI/Action
42
+ * cannot drift apart on what counts as governing (#39).
43
+ */
40
44
  function bucketFor(status: string): Bucket {
41
- if (status === 'accepted') return 'governing';
42
- if (status === 'draft' || status === 'proposed') return 'activeProposals';
43
- return 'history';
45
+ return decisionBucketFor(status);
44
46
  }
45
47
 
46
48
  function contextEntry(record: Adr, firedMatchers: readonly FiredMatcher[]): ContextEntry {
@@ -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/sdk/server/mcp.js';
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/sdk/server/mcp.js';
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/sdk/server/mcp.js';
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';
@@ -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/sdk/types.js';
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.ZodRawShape;
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
  /* ------------------------------------------------------------------ *