@agentskit/doc-bridge 1.2.4 → 1.2.6

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/docs/mcp.md CHANGED
@@ -43,6 +43,19 @@ Or with a global/local bin:
43
43
 
44
44
  Run from the repo root (or pass config discovery that resolves to it). Always `ak-docs index` after doc changes (or gate in CI).
45
45
 
46
+ ## Claude Desktop MCP Bundle
47
+
48
+ Maintainers can build the local desktop extension from a clean checkout:
49
+
50
+ ```bash
51
+ pnpm install --frozen-lockfile
52
+ pnpm mcpb:pack
53
+ ```
54
+
55
+ The bundle asks the user to select the repository's `doc-bridge.config.json` and uses that file's directory as the project boundary. It contains a self-contained MCP runtime rather than the optional RAG, chat, or model-provider packages. The build validates the manifest, checks the archive inventory, and exercises all eight tools from the staged runtime.
56
+
57
+ The stdio server accepts the newline-delimited JSON transport used by current MCP clients and the legacy `Content-Length` framing used by older integrations. Responses use the same framing as each request.
58
+
46
59
  ## Tools
47
60
 
48
61
  | Tool | Purpose |
@@ -53,6 +66,9 @@ Run from the repo root (or pass config discovery that resolves to it). Always `a
53
66
  | `gate.status` | Freshness / configured gates |
54
67
  | `retriever.query` | Local retriever chunks |
55
68
  | `memory.classify` / `memory.promoteDraft` | Memory pipeline |
69
+ | `registry.topology` | Static curator and delegate topology |
70
+
71
+ Every tool is annotated read-only. None of these MCP calls writes project files or publishes a memory promotion.
56
72
 
57
73
  ## Agent guidance (paste into AGENTS.md)
58
74
 
@@ -0,0 +1,8 @@
1
+ .env*
2
+ *.log
3
+ *.map
4
+ *.mcpb
5
+ coverage/
6
+ test/
7
+ tests/
8
+ src/
package/mcpb/icon.png ADDED
Binary file
@@ -0,0 +1,96 @@
1
+ {
2
+ "manifest_version": "0.3",
3
+ "name": "doc-bridge",
4
+ "display_name": "Doc Bridge",
5
+ "version": "1.2.6",
6
+ "description": "Deterministic repository handoffs for coding agents, running locally without an LLM or API key.",
7
+ "long_description": "Doc Bridge turns a repository's own documentation and ownership metadata into deterministic handoffs: where an agent should start, which paths it may edit, which checks it must run, and when a human must take over. The local connector exposes the same read-only contract available through Doc Bridge CLI and CI.",
8
+ "author": {
9
+ "name": "AgentsKit",
10
+ "url": "https://agentskit.io"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/AgentsKit-io/doc-bridge.git"
15
+ },
16
+ "homepage": "https://doc-bridge.agentskit.io/",
17
+ "documentation": "https://doc-bridge.agentskit.io/docs/mcp",
18
+ "support": "https://github.com/AgentsKit-io/doc-bridge/issues",
19
+ "icon": "icon.png",
20
+ "server": {
21
+ "type": "node",
22
+ "entry_point": "server/ak-docs.js",
23
+ "mcp_config": {
24
+ "command": "node",
25
+ "args": [
26
+ "${__dirname}/server/ak-docs.js",
27
+ "mcp",
28
+ "--config",
29
+ "${user_config.project_config}"
30
+ ],
31
+ "env": {}
32
+ }
33
+ },
34
+ "tools": [
35
+ {
36
+ "name": "handoff.resolve",
37
+ "description": "Resolve a package or ownership id to its deterministic AgentHandoff."
38
+ },
39
+ {
40
+ "name": "doc.search",
41
+ "description": "Search the deterministic Doc Bridge index for repository documentation."
42
+ },
43
+ {
44
+ "name": "doc.get",
45
+ "description": "Read one indexed agent documentation file by id or indexed path."
46
+ },
47
+ {
48
+ "name": "gate.status",
49
+ "description": "Evaluate documentation gates without writing files."
50
+ },
51
+ {
52
+ "name": "retriever.query",
53
+ "description": "Return relevant local Doc Bridge index chunks for a query."
54
+ },
55
+ {
56
+ "name": "memory.classify",
57
+ "description": "Classify local memory candidates into agent, human, playbook, or discard routes."
58
+ },
59
+ {
60
+ "name": "memory.promoteDraft",
61
+ "description": "Build a reviewable draft promotion body from local memory candidates without publishing it."
62
+ },
63
+ {
64
+ "name": "registry.topology",
65
+ "description": "Return the static Doc Bridge curator and delegate topology."
66
+ }
67
+ ],
68
+ "tools_generated": false,
69
+ "keywords": [
70
+ "coding-agents",
71
+ "documentation",
72
+ "handoff",
73
+ "repository",
74
+ "developer-tools"
75
+ ],
76
+ "license": "MIT",
77
+ "privacy_policies": [
78
+ "https://github.com/AgentsKit-io/doc-bridge/blob/master/PRIVACY.md"
79
+ ],
80
+ "compatibility": {
81
+ "platforms": [
82
+ "darwin"
83
+ ],
84
+ "runtimes": {
85
+ "node": ">=22"
86
+ }
87
+ },
88
+ "user_config": {
89
+ "project_config": {
90
+ "type": "file",
91
+ "title": "Doc Bridge configuration",
92
+ "description": "Select the doc-bridge.config.json file at the root of the repository Doc Bridge may read.",
93
+ "required": true
94
+ }
95
+ }
96
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentskit/doc-bridge",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "mcpName": "io.github.AgentsKit-io/doc-bridge",
5
5
  "description": "Human↔agent documentation bridge — deterministic handoffs, doc-site links, memory→docs, optional AgentsKit RAG/chat.",
6
6
  "type": "module",
@@ -31,11 +31,13 @@
31
31
  "docs",
32
32
  "examples",
33
33
  "README.md",
34
+ "PRIVACY.md",
34
35
  "CONTRIBUTING.md",
35
36
  "SECURITY.md",
36
37
  "CODE_OF_CONDUCT.md",
37
38
  "CHANGELOG.md",
38
39
  "LICENSE",
40
+ "mcpb",
39
41
  "tsup.config.ts",
40
42
  "tsconfig.json",
41
43
  "src"
@@ -53,6 +55,11 @@
53
55
  "smoke:docsites": "node scripts/smoke-docsites.mjs",
54
56
  "smoke:real-docsites": "node scripts/smoke-real-docsites.mjs",
55
57
  "smoke:ollama": "node scripts/smoke-ollama.mjs",
58
+ "mcpb:stage": "npm run build && node scripts/build-mcpb.mjs stage",
59
+ "mcpb:validate": "node scripts/build-mcpb.mjs validate",
60
+ "mcpb:smoke": "node scripts/smoke-mcpb.mjs",
61
+ "mcpb:pack": "npm run mcpb:stage && npm run mcpb:smoke && node scripts/build-mcpb.mjs pack",
62
+ "test:mcpb": "node --test scripts/mcpb-contract.test.mjs",
56
63
  "coverage:badge": "node scripts/update-coverage-badge.mjs",
57
64
  "changeset": "changeset",
58
65
  "version-packages": "changeset version && node scripts/sync-version.mjs",
@@ -132,6 +139,7 @@
132
139
  "@agentskit/core": "1.12.3",
133
140
  "@agentskit/ink": "0.10.4",
134
141
  "@agentskit/react": "0.7.4",
142
+ "@anthropic-ai/mcpb": "2.1.2",
135
143
  "@changesets/cli": "^2.31.0",
136
144
  "@lhci/cli": "^0.15.1",
137
145
  "@playwright/test": "1.61.1",
@@ -140,11 +148,12 @@
140
148
  "@types/react": "19.2.17",
141
149
  "@types/react-dom": "19.2.3",
142
150
  "@vitest/coverage-v8": "4.1.10",
151
+ "esbuild": "^0.28.1",
143
152
  "fumadocs-core": "15.6.5",
144
153
  "fumadocs-mdx": "11.6.4",
145
154
  "fumadocs-ui": "15.6.5",
146
155
  "lucide-react": "0.460.0",
147
- "next": "15.5.19",
156
+ "next": "15.5.21",
148
157
  "react": "19.2.0",
149
158
  "react-dom": "19.2.0",
150
159
  "tailwindcss": "4.3.2",
@@ -152,14 +161,6 @@
152
161
  "typescript": "^6.0.3",
153
162
  "vitest": "^4.1.9"
154
163
  },
155
- "pnpm": {
156
- "overrides": {
157
- "esbuild": "^0.28.1",
158
- "postcss": "8.5.19",
159
- "tmp": "0.2.7",
160
- "uuid": "11.1.1"
161
- }
162
- },
163
164
  "directories": {
164
165
  "doc": "docs",
165
166
  "example": "examples",
package/src/mcp/server.ts CHANGED
@@ -30,7 +30,9 @@ type McpContext = {
30
30
  export const MCP_TOOLS = [
31
31
  {
32
32
  name: 'handoff.resolve',
33
- description: 'Resolve a package or ownership id to an AgentHandoff.',
33
+ title: 'Resolve repository handoff',
34
+ description: 'Resolve a package or ownership id to its deterministic AgentHandoff.',
35
+ annotations: { readOnlyHint: true },
34
36
  inputSchema: {
35
37
  type: 'object',
36
38
  properties: { id: { type: 'string' }, kind: { type: 'string', enum: ['package', 'ownership'] } },
@@ -39,7 +41,9 @@ export const MCP_TOOLS = [
39
41
  },
40
42
  {
41
43
  name: 'doc.search',
42
- description: 'Search the deterministic doc-bridge index.',
44
+ title: 'Search repository documentation',
45
+ description: 'Search the deterministic Doc Bridge index for repository documentation.',
46
+ annotations: { readOnlyHint: true },
43
47
  inputSchema: {
44
48
  type: 'object',
45
49
  properties: { term: { type: 'string' }, limit: { type: 'number' } },
@@ -48,7 +52,9 @@ export const MCP_TOOLS = [
48
52
  },
49
53
  {
50
54
  name: 'doc.get',
51
- description: 'Read an indexed agent documentation file by id or path.',
55
+ title: 'Read indexed documentation',
56
+ description: 'Read one indexed agent documentation file by id or indexed path.',
57
+ annotations: { readOnlyHint: true },
52
58
  inputSchema: {
53
59
  type: 'object',
54
60
  properties: { id: { type: 'string' }, path: { type: 'string' } },
@@ -56,12 +62,16 @@ export const MCP_TOOLS = [
56
62
  },
57
63
  {
58
64
  name: 'gate.status',
59
- description: 'Run the index-freshness gate.',
65
+ title: 'Check documentation gates',
66
+ description: 'Evaluate documentation gates without writing files.',
67
+ annotations: { readOnlyHint: true },
60
68
  inputSchema: { type: 'object', properties: {} },
61
69
  },
62
70
  {
63
71
  name: 'retriever.query',
64
- description: 'Return local doc-bridge retriever chunks for a query.',
72
+ title: 'Retrieve documentation context',
73
+ description: 'Return relevant local Doc Bridge index chunks for a query.',
74
+ annotations: { readOnlyHint: true },
65
75
  inputSchema: {
66
76
  type: 'object',
67
77
  properties: { query: { type: 'string' }, limit: { type: 'number' } },
@@ -70,17 +80,23 @@ export const MCP_TOOLS = [
70
80
  },
71
81
  {
72
82
  name: 'memory.classify',
73
- description: 'Classify local memory candidates into agent/human/playbook/discard routes.',
83
+ title: 'Classify memory candidates',
84
+ description: 'Classify local memory candidates into agent, human, playbook, or discard routes.',
85
+ annotations: { readOnlyHint: true },
74
86
  inputSchema: { type: 'object', properties: {} },
75
87
  },
76
88
  {
77
89
  name: 'memory.promoteDraft',
78
- description: 'Build a safe draft promotion body for local memory candidates.',
90
+ title: 'Draft memory promotion',
91
+ description: 'Build a reviewable draft promotion body from local memory candidates without publishing it.',
92
+ annotations: { readOnlyHint: true },
79
93
  inputSchema: { type: 'object', properties: {} },
80
94
  },
81
95
  {
82
96
  name: 'registry.topology',
83
- description: 'Return the doc-curator registry topology.',
97
+ title: 'Inspect registry topology',
98
+ description: 'Return the static Doc Bridge curator and delegate topology.',
99
+ annotations: { readOnlyHint: true },
84
100
  inputSchema: { type: 'object', properties: {} },
85
101
  },
86
102
  ] as const
@@ -223,12 +239,14 @@ export const handleMcpRequest = (ctx: McpContext, request: JsonRpcRequest): unkn
223
239
  throw new Error(`Unsupported MCP method "${request.method ?? ''}"`)
224
240
  }
225
241
 
226
- const writeFrame = (payload: unknown): void => {
242
+ type StdioFraming = 'content-length' | 'json-line'
243
+
244
+ const writeFrame = (payload: unknown, framing: StdioFraming): void => {
227
245
  const body = JSON.stringify(payload)
228
- process.stdout.write(`Content-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`)
246
+ process.stdout.write(framing === 'json-line' ? `${body}\n` : `Content-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`)
229
247
  }
230
248
 
231
- const respond = (ctx: McpContext, request: JsonRpcRequest): void => {
249
+ const respond = (ctx: McpContext, request: JsonRpcRequest, framing: StdioFraming): void => {
232
250
  if (request.id === undefined) {
233
251
  try {
234
252
  handleMcpRequest(ctx, request)
@@ -240,13 +258,13 @@ const respond = (ctx: McpContext, request: JsonRpcRequest): void => {
240
258
 
241
259
  try {
242
260
  const result = handleMcpRequest(ctx, request)
243
- writeFrame({ jsonrpc: '2.0', id: request.id, result: result ?? {} })
261
+ writeFrame({ jsonrpc: '2.0', id: request.id, result: result ?? {} }, framing)
244
262
  } catch (error) {
245
263
  writeFrame({
246
264
  jsonrpc: '2.0',
247
265
  id: request.id,
248
266
  error: { code: -32000, message: error instanceof Error ? error.message : String(error) },
249
- })
267
+ }, framing)
250
268
  }
251
269
  }
252
270
 
@@ -255,21 +273,35 @@ export const startMcpStdioServer = (ctx: McpContext): void => {
255
273
  process.stdin.on('data', (chunk: Buffer) => {
256
274
  buffer = Buffer.concat([buffer, chunk])
257
275
  while (true) {
258
- const headerEnd = buffer.indexOf('\r\n\r\n')
259
- if (headerEnd === -1) return
260
- const header = buffer.subarray(0, headerEnd).toString('utf8')
261
- const match = /content-length:\s*(\d+)/i.exec(header)
262
- if (!match?.[1]) {
263
- buffer = buffer.subarray(headerEnd + 4)
276
+ if (/^content-length:/i.test(buffer.subarray(0, Math.min(buffer.length, 32)).toString('utf8'))) {
277
+ const headerEnd = buffer.indexOf('\r\n\r\n')
278
+ if (headerEnd === -1) return
279
+ const header = buffer.subarray(0, headerEnd).toString('utf8')
280
+ const match = /content-length:\s*(\d+)/i.exec(header)
281
+ if (!match?.[1]) {
282
+ buffer = buffer.subarray(headerEnd + 4)
283
+ continue
284
+ }
285
+ const length = Number(match[1])
286
+ const bodyStart = headerEnd + 4
287
+ const bodyEnd = bodyStart + length
288
+ if (buffer.length < bodyEnd) return
289
+ const raw = buffer.subarray(bodyStart, bodyEnd).toString('utf8')
290
+ buffer = buffer.subarray(bodyEnd)
291
+ respond(ctx, JSON.parse(raw) as JsonRpcRequest, 'content-length')
264
292
  continue
265
293
  }
266
- const length = Number(match[1])
267
- const bodyStart = headerEnd + 4
268
- const bodyEnd = bodyStart + length
269
- if (buffer.length < bodyEnd) return
270
- const raw = buffer.subarray(bodyStart, bodyEnd).toString('utf8')
271
- buffer = buffer.subarray(bodyEnd)
272
- respond(ctx, JSON.parse(raw) as JsonRpcRequest)
294
+
295
+ const lineEnd = buffer.indexOf('\n')
296
+ if (lineEnd === -1) return
297
+ const raw = buffer.subarray(0, lineEnd).toString('utf8').trim()
298
+ buffer = buffer.subarray(lineEnd + 1)
299
+ if (!raw) continue
300
+ try {
301
+ respond(ctx, JSON.parse(raw) as JsonRpcRequest, 'json-line')
302
+ } catch {
303
+ writeFrame({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Parse error' } }, 'json-line')
304
+ }
273
305
  }
274
306
  })
275
307
  process.stdin.resume()
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const PACKAGE_VERSION = '1.2.4'
1
+ export const PACKAGE_VERSION = '1.2.6'