@gmickel/gno 1.18.0 → 1.19.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.
Files changed (129) hide show
  1. package/README.md +5 -3
  2. package/assets/skill/SKILL.md +27 -0
  3. package/package.json +2 -1
  4. package/spec/AGENTS.md +83 -0
  5. package/spec/CLAUDE.md +83 -0
  6. package/spec/bench-fixture.schema.json +137 -0
  7. package/spec/cli.md +2894 -0
  8. package/spec/db/schema.sql +442 -0
  9. package/spec/evals-agentic.md +510 -0
  10. package/spec/evals.md +1106 -0
  11. package/spec/mcp.md +2229 -0
  12. package/spec/output-schemas/activation-verification.schema.json +515 -0
  13. package/spec/output-schemas/ask.schema.json +366 -0
  14. package/spec/output-schemas/backlinks.schema.json +131 -0
  15. package/spec/output-schemas/bench-result.schema.json +120 -0
  16. package/spec/output-schemas/capture-receipt.schema.json +143 -0
  17. package/spec/output-schemas/collection-list.schema.json +45 -0
  18. package/spec/output-schemas/context-capsule-v1.schema.json +691 -0
  19. package/spec/output-schemas/context-capsule-verification.schema.json +1338 -0
  20. package/spec/output-schemas/context-list.schema.json +21 -0
  21. package/spec/output-schemas/doctor.schema.json +313 -0
  22. package/spec/output-schemas/error.schema.json +30 -0
  23. package/spec/output-schemas/expansion.schema.json +37 -0
  24. package/spec/output-schemas/get.schema.json +140 -0
  25. package/spec/output-schemas/graph-query.schema.json +99 -0
  26. package/spec/output-schemas/graph.schema.json +371 -0
  27. package/spec/output-schemas/links-list.schema.json +186 -0
  28. package/spec/output-schemas/mcp-add-collection-result.schema.json +23 -0
  29. package/spec/output-schemas/mcp-capture-result.schema.json +152 -0
  30. package/spec/output-schemas/mcp-http-error.schema.json +30 -0
  31. package/spec/output-schemas/mcp-job-list.schema.json +58 -0
  32. package/spec/output-schemas/mcp-job-status.schema.json +224 -0
  33. package/spec/output-schemas/mcp-remove-result.schema.json +39 -0
  34. package/spec/output-schemas/mcp-sync-result.schema.json +41 -0
  35. package/spec/output-schemas/mcp-tag-result.schema.json +33 -0
  36. package/spec/output-schemas/models-list.schema.json +93 -0
  37. package/spec/output-schemas/multi-get.schema.json +103 -0
  38. package/spec/output-schemas/process-status.schema.json +119 -0
  39. package/spec/output-schemas/query-diagnose.schema.json +123 -0
  40. package/spec/output-schemas/resident-status.schema.json +154 -0
  41. package/spec/output-schemas/retrieval-trace-common.schema.json +492 -0
  42. package/spec/output-schemas/retrieval-trace-delete.schema.json +16 -0
  43. package/spec/output-schemas/retrieval-trace-export.schema.json +61 -0
  44. package/spec/output-schemas/retrieval-trace-filters.schema.json +139 -0
  45. package/spec/output-schemas/retrieval-trace-judgment.schema.json +15 -0
  46. package/spec/output-schemas/retrieval-trace-list.schema.json +18 -0
  47. package/spec/output-schemas/retrieval-trace-payloads.schema.json +178 -0
  48. package/spec/output-schemas/retrieval-trace-purge.schema.json +31 -0
  49. package/spec/output-schemas/retrieval-trace-qrels.schema.json +303 -0
  50. package/spec/output-schemas/retrieval-trace-replay.schema.json +286 -0
  51. package/spec/output-schemas/retrieval-trace-show.schema.json +69 -0
  52. package/spec/output-schemas/retrieval-trace-summary.schema.json +65 -0
  53. package/spec/output-schemas/search-result.schema.json +154 -0
  54. package/spec/output-schemas/search-results.schema.json +338 -0
  55. package/spec/output-schemas/similar.schema.json +84 -0
  56. package/spec/output-schemas/status.schema.json +676 -0
  57. package/spec/output-schemas/tags-list.schema.json +48 -0
  58. package/src/app/context-runtime-types.ts +3 -0
  59. package/src/app/context-runtime.ts +15 -1
  60. package/src/cli/commands/ask.ts +106 -36
  61. package/src/cli/commands/context-build.ts +56 -9
  62. package/src/cli/commands/get.ts +64 -3
  63. package/src/cli/commands/query.ts +62 -23
  64. package/src/cli/commands/replay.ts +140 -0
  65. package/src/cli/commands/search.ts +48 -3
  66. package/src/cli/commands/shared.ts +3 -1
  67. package/src/cli/commands/trace.ts +200 -0
  68. package/src/cli/commands/vsearch.ts +75 -53
  69. package/src/cli/program.ts +255 -0
  70. package/src/config/index.ts +9 -0
  71. package/src/config/retrieval-traces.ts +56 -0
  72. package/src/config/types.ts +4 -0
  73. package/src/core/context-compiler.ts +11 -4
  74. package/src/core/retrieval-qrels.ts +405 -0
  75. package/src/core/retrieval-replay-candidate.ts +368 -0
  76. package/src/core/retrieval-replay-types.ts +109 -0
  77. package/src/core/retrieval-replay-validation.ts +89 -0
  78. package/src/core/retrieval-replay.ts +441 -0
  79. package/src/core/retrieval-trace-evidence-origin.ts +175 -0
  80. package/src/core/retrieval-trace-export.ts +113 -0
  81. package/src/core/retrieval-trace-filter-normalization.ts +27 -0
  82. package/src/core/retrieval-trace-filters.ts +19 -0
  83. package/src/core/retrieval-trace-management-helpers.ts +247 -0
  84. package/src/core/retrieval-trace-management-types.ts +132 -0
  85. package/src/core/retrieval-trace-management.ts +422 -0
  86. package/src/core/retrieval-trace-request.ts +141 -0
  87. package/src/core/retrieval-trace-session.ts +494 -0
  88. package/src/core/retrieval-trace.ts +472 -0
  89. package/src/mcp/tools/context.ts +59 -8
  90. package/src/mcp/tools/get.ts +35 -1
  91. package/src/mcp/tools/index.ts +74 -0
  92. package/src/mcp/tools/query.ts +95 -64
  93. package/src/mcp/tools/search.ts +36 -13
  94. package/src/mcp/tools/trace.ts +143 -0
  95. package/src/mcp/tools/vsearch.ts +71 -38
  96. package/src/pipeline/answer.ts +167 -26
  97. package/src/pipeline/graph-retrieval.ts +15 -1
  98. package/src/pipeline/hybrid.ts +151 -43
  99. package/src/pipeline/search.ts +36 -3
  100. package/src/pipeline/trace-metadata.ts +47 -0
  101. package/src/pipeline/types.ts +43 -0
  102. package/src/pipeline/vsearch.ts +101 -38
  103. package/src/sdk/client.ts +380 -71
  104. package/src/sdk/documents.ts +48 -1
  105. package/src/sdk/index.ts +17 -0
  106. package/src/sdk/types.ts +28 -0
  107. package/src/serve/context-capsule.ts +67 -8
  108. package/src/serve/public/app.tsx +12 -1
  109. package/src/serve/public/globals.built.css +1 -1
  110. package/src/serve/public/lib/workspace-tabs.ts +2 -0
  111. package/src/serve/public/pages/Dashboard.tsx +10 -0
  112. package/src/serve/public/pages/TraceHistory.tsx +478 -0
  113. package/src/serve/public/pages/trace-history-detail.tsx +224 -0
  114. package/src/serve/retrieval-trace.ts +28 -0
  115. package/src/serve/routes/api.ts +366 -72
  116. package/src/serve/routes/traces.ts +156 -0
  117. package/src/serve/server.ts +87 -2
  118. package/src/store/index.ts +31 -0
  119. package/src/store/migrations/014-retrieval-traces.ts +303 -0
  120. package/src/store/migrations/index.ts +2 -0
  121. package/src/store/retrieval-trace-codec.ts +384 -0
  122. package/src/store/sqlite/adapter.ts +153 -1
  123. package/src/store/sqlite/retrieval-trace-management-store.ts +341 -0
  124. package/src/store/sqlite/retrieval-trace-retention.ts +349 -0
  125. package/src/store/sqlite/retrieval-trace-rows.ts +267 -0
  126. package/src/store/sqlite/retrieval-trace-store.ts +515 -0
  127. package/src/store/types.ts +297 -0
  128. package/src/store/vector/sqlite-vec.ts +76 -1
  129. package/src/store/vector/types.ts +1 -1
package/spec/mcp.md ADDED
@@ -0,0 +1,2229 @@
1
+ # GNO MCP Specification
2
+
3
+ **Version:** 1.0.0
4
+ **Last Updated:** 2026-04-24
5
+ **Protocol:** Model Context Protocol (MCP) 2025-11-25
6
+ **Transport:** JSON-RPC 2.0 over stdio or resident Streamable HTTP
7
+
8
+ This document specifies the MCP server interface for GNO.
9
+
10
+ ## Server Information
11
+
12
+ | Property | Value |
13
+ | --------- | -------------------- |
14
+ | Name | `gno` |
15
+ | Version | `1.0.0` |
16
+ | Command | `gno mcp` |
17
+ | Transport | stdio (stdin/stdout) |
18
+
19
+ ## Capabilities
20
+
21
+ ```json
22
+ {
23
+ "capabilities": {
24
+ "tools": {
25
+ "listChanged": false
26
+ },
27
+ "resources": {
28
+ "subscribe": false,
29
+ "listChanged": false
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Security Model
38
+
39
+ ### Write Tool Gating
40
+
41
+ Write tools are **disabled by default**. Enable explicitly:
42
+
43
+ ```bash
44
+ gno mcp --enable-write
45
+ # or
46
+ GNO_MCP_ENABLE_WRITE=1 gno mcp
47
+ ```
48
+
49
+ When disabled, write tools are not registered and cannot be invoked.
50
+
51
+ ### Collection Root Validation
52
+
53
+ `gno_add_collection` rejects dangerous roots to avoid indexing broad/system paths:
54
+
55
+ - `/` (root)
56
+ - `~` (entire home dir)
57
+ - `/etc`, `/usr`, `/bin`, `/var`, `/System`, `/Library`
58
+ - `~/.config`, `~/.local`, `~/.ssh`, `~/.gnupg`
59
+
60
+ ### Write Lock
61
+
62
+ All write tools acquire an OS-backed advisory lock at `.mcp-write.lock` under the index directory.
63
+ If another process holds the lock, tools return `LOCKED`.
64
+ For async jobs, the lock is held for the full job duration.
65
+
66
+ ### Resident Streamable HTTP boundary
67
+
68
+ `gno serve` and `gno daemon` mount the same stateful MCP surface at `/mcp`.
69
+ The default listener is the literal IPv4 loopback address `127.0.0.1`. Each
70
+ HTTP session owns one SDK server and transport while sharing the resident
71
+ store, jobs, and model lifecycle. POST, GET, and DELETE follow MCP 2025-11-25;
72
+ resumption is not advertised.
73
+
74
+ The external boundary runs before JSON parsing or SDK dispatch on every HTTP
75
+ method. It uses Bun `server.requestIP(request)` as the peer source and never
76
+ trusts `Forwarded` or `X-Forwarded-*`. Host and present Origin headers must
77
+ match exact allowlists. Loopback defaults allow only the selected port on
78
+ `127.0.0.1` and `localhost` (or explicit `::1`).
79
+
80
+ Wildcard and non-loopback binds fail startup unless all three controls exist:
81
+
82
+ - a bearer token file readable only by its owner (`0600` or stricter on POSIX),
83
+ - at least one exact Host value, and
84
+ - at least one exact HTTP(S) Origin.
85
+
86
+ An explicitly configured missing token file is generated with a random 256-bit
87
+ token and restrictive creation mode. The token is never printed or included in
88
+ errors. Rotation, deletion, invalid content, or permission relaxation revokes
89
+ existing authenticated sessions. Session IDs are bound to the identity that
90
+ initialized them, preventing reuse with a different bearer token.
91
+
92
+ Because `gno serve` shares this listener with its Web UI and REST API, it
93
+ remains loopback-only. Use the headless `gno daemon` command for an explicitly
94
+ authenticated non-loopback MCP listener.
95
+
96
+ HTTP MCP remains read-only unless `gateway.enableWrite: true` or
97
+ `--mcp-enable-write` is explicitly set. Bearer authentication alone does not
98
+ authorize mutation. Unauthorized calls to write tools fail with HTTP 403 before
99
+ SDK dispatch.
100
+
101
+ Boundary failures use the closed
102
+ [`mcp-http-error`](./output-schemas/mcp-http-error.schema.json) body with stable,
103
+ redacted statuses: 401 (authentication), 403 (peer/Host/Origin/write), 413
104
+ (declared or streamed body), 429 (rate/request/queue/session pressure), and 503
105
+ (shutdown, revoked credentials, or unavailable runtime). Defaults are 1 MiB per
106
+ POST body, 120 requests/minute per actual peer, 64 active requests, 16 queued
107
+ requests, 32 sessions, and a five-minute idle session timeout.
108
+
109
+ ### Packaged gateway conformance
110
+
111
+ `bun run test:package` installs the generated npm tarball into an isolated
112
+ environment and exercises the shipped binary. It proves two concurrent HTTP
113
+ MCP clients plus one stdio client observe equivalent tools, resources, and
114
+ search results; repeated HTTP calls reuse the same resident store and model
115
+ lifecycle. The same run validates the redacted resident-status schema,
116
+ loopback-only app-status boundary, Host/Origin,
117
+ body-size, bearer-token, token-rotation, session-identity, and write-authorization
118
+ boundaries, daemon-only authenticated non-loopback binding, and detached
119
+ restart/shutdown behavior. Windows package and binary artifact jobs remain the
120
+ final platform-specific sweep for detach rejection and known interrupt exits.
121
+
122
+ ## Collection Name Rules
123
+
124
+ Collection names are case-insensitive on input and normalized to lowercase in responses.
125
+
126
+ ## Job Management
127
+
128
+ - Single active job per MCP server process
129
+ - Completed job retention: 1 hour, max 100 entries
130
+ - Jobs are in-memory per process (lost on restart)
131
+ - Poll with `gno_job_status`; if the job is missing after restart, return `NOT_FOUND`
132
+
133
+ ## Tools
134
+
135
+ ### Agent Retrieval Playbook
136
+
137
+ - Prefer `gno_context` when the agent needs a complete, bounded evidence handoff
138
+ for one goal. It compiles exact source spans, coverage gaps, omissions, and
139
+ verification fingerprints in one call.
140
+ - Prefer `gno_query` for normal questions. It is the default hybrid path and returns `uri`, `docid`, snippets, and `line` anchors for follow-up reads.
141
+ - Use `gno_search` for exact phrases, filenames, identifiers, error messages, and known symbols.
142
+ - Use `gno_vsearch` for semantic similarity when wording differs and embeddings are current.
143
+ - Use `intent` to disambiguate short or overloaded terms without changing the searched text.
144
+ - Use `queryModes` when the caller has typed retrieval text: `term` for lexical anchors, `intent` for disambiguation, and at most one `hyde` hypothetical answer/document.
145
+ - Use `gno_query_diagnose` when a specific important document is missing from results or when you need per-stage retrieval evidence before changing query strategy.
146
+ - Use `gno_graph_query` for bounded typed-edge traversal over `doc_edges`; keep `gno_graph_neighbors`/`gno_graph_path` for the legacy graph projection.
147
+ - After search/query returns a `line`, call `gno_get` with `fromLine` and `lineCount` before fetching whole documents.
148
+ - Use `gno_multi_get` to batch the top result refs. Keep `maxBytes` bounded to avoid flooding client context.
149
+ - Check `gno_status` when results look stale, vector search is unavailable, or embedding backlog may explain missing results.
150
+
151
+ ### Private retrieval metadata
152
+
153
+ When local tracing is enabled, successful `gno_search`, `gno_vsearch`,
154
+ `gno_query`, `gno_get`, and `gno_context` results include non-model-visible
155
+ top-level response metadata:
156
+
157
+ ```json
158
+ {
159
+ "_meta": {
160
+ "gno": {
161
+ "retrievalTrace": {
162
+ "traceId": "..."
163
+ }
164
+ }
165
+ }
166
+ }
167
+ ```
168
+
169
+ `structuredContent` and model-visible `content` are unchanged. `gno_get`
170
+ accepts optional `traceId` to continue an open retrieval trace and records
171
+ evidence only when a valid exact line range is returned. Out-of-range and
172
+ failed gets never fabricate evidence. Disabled tracing omits `_meta` and does
173
+ no trace ID or fingerprint work.
174
+
175
+ Trace receipt management is split into read and mutation tool names so HTTP
176
+ authorization can reject mutations before dispatch:
177
+
178
+ | Tool | Class | Contract |
179
+ | ------------------ | -------- | ---------------------------------------------------------- |
180
+ | `gno_trace_list` | read | Bounded cursor page; summaries omit replay query/goal text |
181
+ | `gno_trace_show` | read | One bounded detail receipt with exact totals/truncation |
182
+ | `gno_trace_label` | mutation | Explicit relevant/irrelevant/missing_expected judgment |
183
+ | `gno_trace_export` | mutation | Deterministic multi-trace `agentic-receipt` |
184
+ | `gno_trace_delete` | mutation | Delete one trace and owned records |
185
+ | `gno_trace_purge` | mutation | Delete all receipts; requires `confirm: true` |
186
+
187
+ Read tools are always registered. Mutation tools are registered only when
188
+ `enableWrite` is true and every handler rechecks that state. HTTP MCP also
189
+ classifies all four mutation names in its pre-dispatch write set. A bearer
190
+ identity authenticates a principal but never grants trace-write authority.
191
+ Denied calls return `403`/`WRITE_DISABLED` without echoing trace content.
192
+
193
+ Relevant and irrelevant targets must resolve to exact recorded evidence.
194
+ `missing_expected` accepts only a safe document identity, never raw document
195
+ content or a filesystem path. Aggregate exports reject open/missing traces and
196
+ preserve each stored terminal state without treating partial, failed, or
197
+ cancelled as negative feedback.
198
+
199
+ ### gno_context
200
+
201
+ Compile a deterministic, extractive Context Capsule. The active MCP server
202
+ supplies the canonical index name; callers cannot switch indexes in the request.
203
+ The complete canonical payload—not each document independently—must fit the
204
+ requested token and optional byte budget.
205
+
206
+ Required input:
207
+
208
+ ```json
209
+ {
210
+ "goal": "Compare the launch proposals",
211
+ "budgetTokens": 12000
212
+ }
213
+ ```
214
+
215
+ Optional input fields are `query`, `collections`, `uriPrefix`, `queryModes`,
216
+ `tagsAll`, `tagsAny`, `categories`, `author`, `lang`, `since`, `until`, `graph`,
217
+ `limit`, `candidateLimit`, `budgetBytes`, `safetyMarginTokens`,
218
+ `safetyMarginBytes`, `depthPolicy` (`fast`, `balanced`, or `thorough`), and
219
+ `format` (`json` or `md`). Input objects are closed: unknown fields return
220
+ `invalid_input`. Unknown collections return `invalid_filter` before model or
221
+ retrieval setup. Tag filters are NFC-normalized, lowercased, deduplicated, and
222
+ validated before retrieval. `limit` and `candidateLimit` are global across all
223
+ requested collections: result admission is capped after merging, and
224
+ rerank/graph candidate work is distributed deterministically in canonical
225
+ collection order.
226
+
227
+ `structuredContent` is the complete canonical Context Capsule object for
228
+ application clients. Model-visible text is always one deterministic
229
+ `gno-context-agent-v1` JSON projection, even when the compatibility `format`
230
+ field is present. The compact keys and tuple positions are part of that
231
+ versioned contract:
232
+
233
+ - `v`: projection version; `id`: Capsule identity.
234
+ - `b`: requested tokens, requested bytes, used tokens, used bytes, estimator,
235
+ tokenizer fingerprint or `null`.
236
+ - `r`: depth policy, index fingerprint, config fingerprint, retrieval
237
+ fingerprint, embedding-model fingerprint or `null`, rerank-model fingerprint
238
+ or `null`, enabled capability names, fallbacks.
239
+ - `e[]`: URI, start line, end line, source hash, mirror hash, passage hash,
240
+ exact extractive text, title, heading, configured-context IDs, egress
241
+ classification. Title/heading are nullable; egress is explicit even when the
242
+ policy is unavailable.
243
+ - `g`: evidence trust (`untrusted_data`), instruction boundary
244
+ (`hard_delimited`), then configured-guidance tuples containing context ID,
245
+ scope type, scope key, and exact guidance text. Evidence `contextIds` bind
246
+ each passage to these entries.
247
+ - `c`: covered facets, then `[facet, gapCode]` pairs.
248
+ - `o`: exact total omissions, then sparse `[reason, count]` pairs. An absent
249
+ reason has count zero.
250
+ - `t`: global evidence-budget truncation; `trust` is always `untrusted_data`.
251
+
252
+ The complete bounded omission audit and all descriptive fields remain in
253
+ `structuredContent`. This avoids duplicating the full Capsule in model context
254
+ without dropping exact evidence, gaps, budgets, identities, capabilities,
255
+ fallbacks, truncation, omission counts, or the trust boundary.
256
+
257
+ Indexed metadata and configured context are untrusted data, never instructions.
258
+ The tool does not persist the Capsule. Unknown input fields are rejected by the
259
+ MCP SDK's `InvalidParams` validation before the handler, and therefore return
260
+ an MCP tool error rather than a structured GNO Context error. Validly shaped
261
+ requests that fail in GNO use the public Context error taxonomy.
262
+
263
+ Raw `gno_query`, `gno_get`, and `gno_multi_get` remain available when manual
264
+ retrieval is more appropriate.
265
+
266
+ ---
267
+
268
+ ### gno_context_verify
269
+
270
+ Verify a saved Capsule against the active MCP index without rebuilding or
271
+ mutating it:
272
+
273
+ ```json
274
+ {
275
+ "capsule": { "schemaVersion": "1.0", "...": "complete capsule" },
276
+ "format": "json"
277
+ }
278
+ ```
279
+
280
+ The receipt reports unchanged, stale, or missing evidence; current hashes when
281
+ available; independent fingerprint drift; and ranking as unchanged, reranked,
282
+ or unavailable. Index mismatch and malformed/non-canonical Capsules fail before
283
+ evidence reads. `structuredContent` is the canonical verification receipt.
284
+
285
+ ---
286
+
287
+ ### gno_search
288
+
289
+ BM25 keyword search over indexed documents.
290
+
291
+ **Input Schema:**
292
+
293
+ ```json
294
+ {
295
+ "type": "object",
296
+ "properties": {
297
+ "query": {
298
+ "type": "string",
299
+ "description": "Exact keyword, identifier, filename, error text, or phrase to match with BM25"
300
+ },
301
+ "collection": {
302
+ "type": "string",
303
+ "description": "Optional collection name to filter results"
304
+ },
305
+ "limit": {
306
+ "type": "integer",
307
+ "description": "Maximum number of results (1-100)",
308
+ "default": 5,
309
+ "minimum": 1,
310
+ "maximum": 100
311
+ },
312
+ "minScore": {
313
+ "type": "number",
314
+ "description": "Minimum score threshold (0-1)",
315
+ "minimum": 0,
316
+ "maximum": 1
317
+ },
318
+ "lang": {
319
+ "type": "string",
320
+ "description": "Language filter (BCP-47 code)"
321
+ },
322
+ "since": {
323
+ "type": "string",
324
+ "description": "Modified-at lower bound (ISO date/time or relative token)"
325
+ },
326
+ "until": {
327
+ "type": "string",
328
+ "description": "Modified-at upper bound (ISO date/time or relative token)"
329
+ },
330
+ "categories": {
331
+ "type": "array",
332
+ "items": { "type": "string" },
333
+ "description": "Only include docs matching any category/content type"
334
+ },
335
+ "author": {
336
+ "type": "string",
337
+ "description": "Only include docs where author contains this value"
338
+ },
339
+ "tagsAll": {
340
+ "type": "array",
341
+ "items": { "type": "string" },
342
+ "description": "Only include docs with ALL specified tags"
343
+ },
344
+ "tagsAny": {
345
+ "type": "array",
346
+ "items": { "type": "string" },
347
+ "description": "Only include docs with ANY specified tag"
348
+ }
349
+ },
350
+ "required": ["query"]
351
+ }
352
+ ```
353
+
354
+ **Output Schema:** `gno://schemas/search-results`
355
+
356
+ **Response:**
357
+
358
+ ```json
359
+ {
360
+ "content": [
361
+ {
362
+ "type": "text",
363
+ "text": "Found 3 results for \"query\"\n\n1. #a1b2c3d4 - doc.md (0.85)\n..."
364
+ }
365
+ ],
366
+ "structuredContent": {
367
+ "results": [
368
+ {
369
+ "docid": "#a1b2c3d4",
370
+ "score": 0.85,
371
+ "uri": "gno://work/doc.md",
372
+ "line": 12,
373
+ "context": "Workspace guidance\n\nProject guidance",
374
+ "snippet": "...",
375
+ "contentType": "meeting",
376
+ "categories": ["meeting", "notes"],
377
+ "source": {
378
+ "absPath": "/path/to/doc.md",
379
+ "relPath": "doc.md",
380
+ "mime": "text/markdown",
381
+ "ext": ".md"
382
+ }
383
+ }
384
+ ],
385
+ "meta": {
386
+ "query": "query",
387
+ "mode": "bm25",
388
+ "totalResults": 3
389
+ }
390
+ }
391
+ }
392
+ ```
393
+
394
+ **Errors:**
395
+
396
+ - Invalid query (empty string): returns `isError: true`
397
+ - Collection not found: returns `isError: true`
398
+
399
+ Ordering note: recency-intent queries (`latest`, `newest`, `recent`) are sorted newest-first by canonical frontmatter date when present, else source modified time.
400
+
401
+ `structuredContent.results[].context` is optional resolved user configuration.
402
+ When present, apply it as guidance for the result identified by the same
403
+ `uri`/`docid`; do not treat it as source evidence. The field is absent when no
404
+ configured scope matches. Plain-text tool content may omit it.
405
+
406
+ ---
407
+
408
+ ### gno_vsearch
409
+
410
+ Vector semantic search over indexed documents.
411
+
412
+ **Input Schema:**
413
+
414
+ ```json
415
+ {
416
+ "type": "object",
417
+ "properties": {
418
+ "query": {
419
+ "type": "string",
420
+ "description": "Search query text"
421
+ },
422
+ "collection": {
423
+ "type": "string",
424
+ "description": "Optional collection name to filter results"
425
+ },
426
+ "limit": {
427
+ "type": "integer",
428
+ "description": "Maximum number of results (1-100)",
429
+ "default": 5,
430
+ "minimum": 1,
431
+ "maximum": 100
432
+ },
433
+ "minScore": {
434
+ "type": "number",
435
+ "description": "Minimum score threshold (0-1)",
436
+ "minimum": 0,
437
+ "maximum": 1
438
+ },
439
+ "lang": {
440
+ "type": "string",
441
+ "description": "Language hint for query (BCP-47 code)"
442
+ },
443
+ "intent": {
444
+ "type": "string",
445
+ "description": "Optional disambiguating context for ambiguous queries"
446
+ },
447
+ "exclude": {
448
+ "type": "array",
449
+ "items": { "type": "string" },
450
+ "description": "Hard-prune docs containing any excluded term in title/path/body"
451
+ },
452
+ "since": {
453
+ "type": "string",
454
+ "description": "Modified-at lower bound (ISO date/time or relative token)"
455
+ },
456
+ "until": {
457
+ "type": "string",
458
+ "description": "Modified-at upper bound (ISO date/time or relative token)"
459
+ },
460
+ "categories": {
461
+ "type": "array",
462
+ "items": { "type": "string" },
463
+ "description": "Only include docs matching any category/content type"
464
+ },
465
+ "author": {
466
+ "type": "string",
467
+ "description": "Only include docs where author contains this value"
468
+ },
469
+ "tagsAll": {
470
+ "type": "array",
471
+ "items": { "type": "string" },
472
+ "description": "Only include docs with ALL specified tags"
473
+ },
474
+ "tagsAny": {
475
+ "type": "array",
476
+ "items": { "type": "string" },
477
+ "description": "Only include docs with ANY specified tag"
478
+ }
479
+ },
480
+ "required": ["query"]
481
+ }
482
+ ```
483
+
484
+ **Output Schema:** `gno://schemas/search-results`
485
+
486
+ **Errors:**
487
+
488
+ - Vectors not available: returns `isError: true` with message suggesting `gno index`
489
+
490
+ ---
491
+
492
+ ### gno_query
493
+
494
+ Hybrid search combining BM25 and vector retrieval with optional expansion and reranking. Recommended default for agent retrieval.
495
+
496
+ **Input Schema:**
497
+
498
+ ```json
499
+ {
500
+ "type": "object",
501
+ "properties": {
502
+ "query": {
503
+ "type": "string",
504
+ "description": "Primary user query; combine with intent or queryModes for ambiguous requests"
505
+ },
506
+ "collection": {
507
+ "type": "string",
508
+ "description": "Optional collection name to filter results"
509
+ },
510
+ "limit": {
511
+ "type": "integer",
512
+ "description": "Maximum number of results (1-100)",
513
+ "default": 5,
514
+ "minimum": 1,
515
+ "maximum": 100
516
+ },
517
+ "minScore": {
518
+ "type": "number",
519
+ "description": "Minimum score threshold (0-1)",
520
+ "minimum": 0,
521
+ "maximum": 1
522
+ },
523
+ "lang": {
524
+ "type": "string",
525
+ "description": "Language hint for query (BCP-47 code)"
526
+ },
527
+ "intent": {
528
+ "type": "string",
529
+ "description": "Disambiguating context; steers expansion, rerank, and snippet choice without being searched directly"
530
+ },
531
+ "candidateLimit": {
532
+ "type": "integer",
533
+ "description": "Maximum candidates sent to reranking (1-100); raise for recall, lower for latency",
534
+ "minimum": 1,
535
+ "maximum": 100
536
+ },
537
+ "exclude": {
538
+ "type": "array",
539
+ "items": { "type": "string" },
540
+ "description": "Hard-prune docs containing any excluded term in title/path/body"
541
+ },
542
+ "since": {
543
+ "type": "string",
544
+ "description": "Modified-at lower bound (ISO date/time or relative token)"
545
+ },
546
+ "until": {
547
+ "type": "string",
548
+ "description": "Modified-at upper bound (ISO date/time or relative token)"
549
+ },
550
+ "categories": {
551
+ "type": "array",
552
+ "items": { "type": "string" },
553
+ "description": "Only include docs matching any category/content type"
554
+ },
555
+ "author": {
556
+ "type": "string",
557
+ "description": "Only include docs where author contains this value"
558
+ },
559
+ "queryModes": {
560
+ "type": "array",
561
+ "description": "Typed retrieval entries: term anchors, intent disambiguation, and at most one hyde hypothetical document",
562
+ "items": {
563
+ "type": "object",
564
+ "properties": {
565
+ "mode": {
566
+ "type": "string",
567
+ "enum": ["term", "intent", "hyde"]
568
+ },
569
+ "text": {
570
+ "type": "string",
571
+ "minLength": 1
572
+ }
573
+ },
574
+ "required": ["mode", "text"]
575
+ }
576
+ },
577
+ "expand": {
578
+ "type": "boolean",
579
+ "description": "Enable query expansion (slower, better recall)",
580
+ "default": false
581
+ },
582
+ "rerank": {
583
+ "type": "boolean",
584
+ "description": "Enable cross-encoder reranking",
585
+ "default": true
586
+ },
587
+ "noGraph": {
588
+ "type": "boolean",
589
+ "description": "Compatibility no-op unless graph is also true",
590
+ "default": false
591
+ },
592
+ "graph": {
593
+ "type": "boolean",
594
+ "description": "Enable bounded one-hop graph neighbor expansion",
595
+ "default": false
596
+ },
597
+ "fast": {
598
+ "type": "boolean",
599
+ "description": "Fast mode: skip expansion and reranking (~0.7s)",
600
+ "default": false
601
+ },
602
+ "thorough": {
603
+ "type": "boolean",
604
+ "description": "Thorough mode: enable expansion for broad research or missed recall (~5-8s)",
605
+ "default": false
606
+ },
607
+ "tagsAll": {
608
+ "type": "array",
609
+ "items": { "type": "string" },
610
+ "description": "Only include docs with ALL specified tags"
611
+ },
612
+ "tagsAny": {
613
+ "type": "array",
614
+ "items": { "type": "string" },
615
+ "description": "Only include docs with ANY specified tag"
616
+ }
617
+ },
618
+ "required": ["query"]
619
+ }
620
+ ```
621
+
622
+ **Output Schema:** `gno://schemas/search-results`
623
+
624
+ Validation note: `queryModes[].text` is trimmed and must remain non-empty; only one `mode: "hyde"` entry is allowed.
625
+
626
+ Search result items include `contentType` when available and always include
627
+ `categories` as the category/content-type filter set. Text output remains
628
+ human-oriented; structured clients should read `structuredContent.results`.
629
+ Structured result items also preserve optional `context` guidance in
630
+ global-to-specific order without changing the result `uri` or `docid`.
631
+
632
+ Compatibility / migration notes:
633
+
634
+ - Existing `gno_query` tool calls remain valid without `queryModes`.
635
+ - `intent` is orthogonal to `queryModes`: intent steers scoring/prompting, while query modes inject caller-provided retrieval expansions.
636
+ - `candidateLimit` tunes rerank cost without changing retrieval contracts.
637
+ - `exclude` hard-prunes matching docs after retrieval using title/path/body text.
638
+ - `gno_query` does not use graph expansion by default. Set `graph: true` to add capped one-hop graph neighbors after initial retrieval. Explicit links receive stronger treatment than inferred, ambiguous, or similarity edges.
639
+ - `queryModes` is optional; use it only when clients need explicit retrieval intent control.
640
+ - When `queryModes` is present, generated expansion is skipped and provided entries are used directly.
641
+
642
+ **Response structuredContent includes:**
643
+
644
+ ```json
645
+ {
646
+ "results": [
647
+ {
648
+ "docid": "#a1b2c3d4",
649
+ "uri": "gno://work/doc.md",
650
+ "context": "Workspace guidance\n\nProject guidance",
651
+ "contentType": "meeting",
652
+ "categories": ["meeting", "notes"],
653
+ "score": 0.92
654
+ }
655
+ ],
656
+ "meta": {
657
+ "query": "query",
658
+ "mode": "hybrid",
659
+ "expanded": true,
660
+ "reranked": true,
661
+ "vectorsUsed": true,
662
+ "totalResults": 5
663
+ }
664
+ }
665
+ ```
666
+
667
+ **Graceful Degradation:**
668
+
669
+ - If vectors unavailable: `mode: "bm25_only"`, `vectorsUsed: false`
670
+ - If expansion model unavailable: `expanded: false`
671
+ - If rerank model unavailable: `reranked: false`
672
+
673
+ ---
674
+
675
+ ### gno_query_diagnose
676
+
677
+ Targeted retrieval diagnostics for one named document. This read-only tool wraps
678
+ `diagnoseQueryTarget()` and uses the same query/filter controls as `gno_query`
679
+ plus a required `target` reference.
680
+
681
+ **Input Schema:** same fields as `gno_query`, plus:
682
+
683
+ ```json
684
+ {
685
+ "target": "gno://notes/people/alice.md"
686
+ }
687
+ ```
688
+
689
+ - `target`: URI, `#docid`, or `collection/path` for the document to diagnose.
690
+ - `query`, filters, `queryModes`, `fast`/`thorough`, `graph`, and rerank/expand controls behave like `gno_query`.
691
+
692
+ **Output Schema:** `gno://schemas/query-diagnose@1.0`
693
+
694
+ Structured content includes `schemaVersion`, normalized `query`, `target`
695
+ metadata/status (`not_found`, `inactive`, `no_indexed_content`,
696
+ `filtered_out`, or `diagnosed`), `stages` for BM25/vector/fusion/graph/rerank,
697
+ the selected target `chunk`, and retrieval `meta`.
698
+
699
+ Use when an expected target is missing from `gno_query`, when filters may have
700
+ excluded it, or when an agent needs evidence before raising `candidateLimit`,
701
+ changing `queryModes`, enabling graph expansion, or fetching more context.
702
+ For low-latency or CPU-only diagnosis, `fast: true` keeps this MCP tool
703
+ BM25-only and avoids initializing embedding/rerank models.
704
+
705
+ ---
706
+
707
+ ### gno_get
708
+
709
+ Retrieve a single document by reference.
710
+
711
+ **Input Schema:**
712
+
713
+ ```json
714
+ {
715
+ "type": "object",
716
+ "properties": {
717
+ "ref": {
718
+ "type": "string",
719
+ "description": "Document reference: gno:// URI, collection/path, or #docid"
720
+ },
721
+ "fromLine": {
722
+ "type": "integer",
723
+ "description": "Start at line number (1-indexed); use search/query result line anchors",
724
+ "minimum": 1
725
+ },
726
+ "lineCount": {
727
+ "type": "integer",
728
+ "description": "Number of lines to return; prefer a small range before fetching full docs",
729
+ "minimum": 1
730
+ },
731
+ "lineNumbers": {
732
+ "type": "boolean",
733
+ "description": "Include line numbers in content",
734
+ "default": true
735
+ }
736
+ },
737
+ "required": ["ref"]
738
+ }
739
+ ```
740
+
741
+ **Output Schema:** `gno://schemas/get`
742
+
743
+ **Response:**
744
+
745
+ ```json
746
+ {
747
+ "content": [
748
+ {
749
+ "type": "text",
750
+ "text": "1: # Document Title\n2: \n3: Content here..."
751
+ }
752
+ ],
753
+ "structuredContent": {
754
+ "docid": "#a1b2c3d4",
755
+ "uri": "gno://work/doc.md",
756
+ "title": "Document Title",
757
+ "content": "# Document Title\n\nContent here...",
758
+ "totalLines": 150,
759
+ "returnedLines": { "start": 1, "end": 150 },
760
+ "source": {
761
+ "absPath": "/path/to/doc.md",
762
+ "relPath": "doc.md",
763
+ "mime": "text/markdown",
764
+ "ext": ".md",
765
+ "modifiedAt": "2025-12-23T10:00:00Z",
766
+ "sizeBytes": 4096
767
+ },
768
+ "capabilities": {
769
+ "editable": true,
770
+ "tagsEditable": true,
771
+ "tagsWriteback": true,
772
+ "canCreateEditableCopy": false,
773
+ "mode": "editable"
774
+ }
775
+ }
776
+ }
777
+ ```
778
+
779
+ **Errors:**
780
+
781
+ - Document not found: returns `isError: true`
782
+ - Invalid ref format: returns `isError: true`
783
+ - Indexed URI names a missing index: returns `isError: true` without creating it
784
+
785
+ For `gno://...?...index=<name>` refs, the tool reads the named index rather than
786
+ the MCP server's active index.
787
+
788
+ `<name>` follows the CLI index-name contract: 1–64 UTF-16 code units drawn from
789
+ Unicode letters, marks, numbers, internal ASCII spaces, `.`, `_`, or `-`; it
790
+ starts with a letter or number, cannot end with a space or `.`, and cannot
791
+ contain `..`. Invalid names are rejected before filesystem access. NFC/case-
792
+ folded equivalents share one logical identity. The canonical identity is
793
+ limited to 242 UTF-8 bytes so `index-<identity>.sqlite` stays within the portable
794
+ 255-byte filename-component limit.
795
+
796
+ ---
797
+
798
+ ### gno_multi_get
799
+
800
+ Retrieve multiple documents by pattern or list.
801
+
802
+ **Input Schema:**
803
+
804
+ ```json
805
+ {
806
+ "type": "object",
807
+ "properties": {
808
+ "refs": {
809
+ "type": "array",
810
+ "description": "Array of document references from search/query results (gno:// URIs or docids)",
811
+ "items": {
812
+ "type": "string"
813
+ }
814
+ },
815
+ "pattern": {
816
+ "type": "string",
817
+ "description": "Glob pattern to match documents (alternative to refs)"
818
+ },
819
+ "maxBytes": {
820
+ "type": "integer",
821
+ "description": "Maximum bytes per document before truncation; lower when batching many refs",
822
+ "default": 10240
823
+ },
824
+ "lineNumbers": {
825
+ "type": "boolean",
826
+ "description": "Include line numbers in content",
827
+ "default": true
828
+ }
829
+ }
830
+ }
831
+ ```
832
+
833
+ **Note:** Provide either `refs` or `pattern`, not both.
834
+
835
+ All refs in one request must resolve to one index. Explicit refs for different
836
+ indexes, or indexed refs mixed with unindexed refs from another active index,
837
+ return `isError: true`; callers must split the batch by index.
838
+
839
+ **Output Schema:** `gno://schemas/multi-get`
840
+
841
+ **Response:**
842
+
843
+ ```json
844
+ {
845
+ "content": [
846
+ {
847
+ "type": "text",
848
+ "text": "Retrieved 3 documents (1 skipped due to size limit)"
849
+ }
850
+ ],
851
+ "structuredContent": {
852
+ "documents": [...],
853
+ "skipped": [
854
+ {
855
+ "ref": "gno://work/large.pdf",
856
+ "reason": "exceeds maxBytes"
857
+ }
858
+ ],
859
+ "meta": {
860
+ "requested": 4,
861
+ "returned": 3,
862
+ "skipped": 1
863
+ }
864
+ }
865
+ }
866
+ ```
867
+
868
+ ---
869
+
870
+ ### gno_status
871
+
872
+ Get index status and health information.
873
+
874
+ `structuredContent.resident` uses
875
+ `gno://schemas/resident-status@1.0`. HTTP sessions observe the shared
876
+ serve/daemon runtime counters. Stdio remains a standalone lifecycle and reports
877
+ `mode:"stdio"`, `resident:false`, no listener, and zero resident transport
878
+ counters; it never claims attachment to another process.
879
+
880
+ **Input Schema:**
881
+
882
+ ```json
883
+ {
884
+ "type": "object",
885
+ "properties": {}
886
+ }
887
+ ```
888
+
889
+ **Output Schema:** `gno://schemas/status`
890
+
891
+ **Response:**
892
+
893
+ ```json
894
+ {
895
+ "content": [
896
+ {
897
+ "type": "text",
898
+ "text": "Index: default\nCollections: 2\nDocuments: 150\nChunks: 800\nEmbedding backlog: 0"
899
+ }
900
+ ],
901
+ "structuredContent": {
902
+ "indexName": "default",
903
+ "collections": [
904
+ {
905
+ "name": "work",
906
+ "documentCount": 100,
907
+ "chunkCount": 500,
908
+ "embeddedCount": 500
909
+ }
910
+ ],
911
+ "totalDocuments": 150,
912
+ "totalChunks": 800,
913
+ "embeddingBacklog": 0,
914
+ "healthy": true
915
+ }
916
+ }
917
+ ```
918
+
919
+ ---
920
+
921
+ ### gno_capture
922
+
923
+ Create a new document in a collection (write-enabled).
924
+
925
+ **Input Schema:**
926
+
927
+ ```json
928
+ {
929
+ "type": "object",
930
+ "properties": {
931
+ "collection": {
932
+ "type": "string",
933
+ "description": "Target collection name"
934
+ },
935
+ "content": {
936
+ "type": "string",
937
+ "description": "Document content (markdown). Optional when presetId provides the scaffold."
938
+ },
939
+ "title": {
940
+ "type": "string",
941
+ "description": "Optional title used for filename generation"
942
+ },
943
+ "path": {
944
+ "type": "string",
945
+ "description": "Optional relative path within the collection"
946
+ },
947
+ "folderPath": {
948
+ "type": "string",
949
+ "description": "Optional folder path within the collection"
950
+ },
951
+ "collisionPolicy": {
952
+ "type": "string",
953
+ "enum": ["error", "open_existing", "create_with_suffix"],
954
+ "description": "How to handle name collisions"
955
+ },
956
+ "presetId": {
957
+ "type": "string",
958
+ "enum": [
959
+ "blank",
960
+ "project-note",
961
+ "research-note",
962
+ "decision-note",
963
+ "prompt-pattern",
964
+ "source-summary",
965
+ "idea-original",
966
+ "person",
967
+ "company-project",
968
+ "meeting"
969
+ ],
970
+ "description": "Optional note preset scaffold"
971
+ },
972
+ "overwrite": {
973
+ "type": "boolean",
974
+ "description": "Overwrite existing file if true",
975
+ "default": false
976
+ },
977
+ "tags": {
978
+ "type": "array",
979
+ "items": { "type": "string" },
980
+ "description": "Tags to apply to the document"
981
+ },
982
+ "source": {
983
+ "type": "object",
984
+ "description": "Optional provenance metadata; written under structured source frontmatter",
985
+ "properties": {
986
+ "kind": {
987
+ "type": "string",
988
+ "enum": [
989
+ "direct",
990
+ "web",
991
+ "email",
992
+ "meeting",
993
+ "chat",
994
+ "file",
995
+ "api",
996
+ "unknown"
997
+ ]
998
+ },
999
+ "title": { "type": "string" },
1000
+ "url": { "type": "string", "format": "uri" },
1001
+ "uri": { "type": "string" },
1002
+ "docid": { "type": "string" },
1003
+ "mime": { "type": "string" },
1004
+ "ext": { "type": "string" },
1005
+ "author": { "type": "string" },
1006
+ "observedAt": { "type": "string", "format": "date-time" },
1007
+ "capturedAt": { "type": "string", "format": "date-time" },
1008
+ "externalId": { "type": "string" }
1009
+ }
1010
+ }
1011
+ },
1012
+ "required": ["collection"]
1013
+ }
1014
+ ```
1015
+
1016
+ **Notes:**
1017
+
1018
+ - Paths must be relative, no `..` escapes, no NUL bytes
1019
+ - Sensitive subpaths are rejected (`.ssh`, `.gnupg`, `.git`, `node_modules`, etc.)
1020
+ - If `path` is omitted, a `.md` filename is generated from the title or heading
1021
+ - `folderPath` lets clients create inside a specific subfolder
1022
+ - `collisionPolicy` supports `error`, `open_existing`, or `create_with_suffix`
1023
+ - Legacy `overwrite: true` overwrites an existing target path and returns
1024
+ `collisionPolicyResult: "overwritten"`; otherwise existing targets follow
1025
+ `collisionPolicy`
1026
+ - `presetId` applies a structured note scaffold before write
1027
+ - Content is required unless `presetId` can scaffold a non-empty note
1028
+ - Content must be text; NUL or binary-like control bytes are rejected
1029
+ - Default generated captures use `inbox/YYYY-MM-DD/capture-<body-hash>.md`
1030
+ - Collision checks include indexed documents and disk-only files
1031
+ - Non-overwrite captures fail instead of replacing a file that appears after
1032
+ planning
1033
+ - Capture writes structured `source:` frontmatter with `kind`, `capturedAt`,
1034
+ and optional `url`, `uri`, `docid`, `mime`, `ext`, `author`, `observedAt`,
1035
+ `externalId`, and `title`
1036
+ - Tags are validated and normalized to lowercase
1037
+ - For Markdown files, tags are added to frontmatter
1038
+ - For non-Markdown files, tags are stored as user-source in the database
1039
+ - Receipts distinguish write result from sync and embedding state; capture does
1040
+ not imply embedding unless `embed.status` is `completed`
1041
+ - Writes run under the MCP write lock and are only registered when the server
1042
+ starts with `--enable-write` or `GNO_MCP_ENABLE_WRITE=1`
1043
+
1044
+ **Output Schema:** `gno://schemas/mcp-capture-result@1.0`, compatible with the
1045
+ shared `gno://schemas/capture-receipt@1.0` contract.
1046
+
1047
+ ---
1048
+
1049
+ ### gno_list_tags
1050
+
1051
+ List all tags with document counts.
1052
+
1053
+ **Input Schema:**
1054
+
1055
+ ```json
1056
+ {
1057
+ "type": "object",
1058
+ "properties": {
1059
+ "collection": {
1060
+ "type": "string",
1061
+ "description": "Filter by collection name"
1062
+ },
1063
+ "prefix": {
1064
+ "type": "string",
1065
+ "description": "Filter by tag prefix (e.g., 'work/' matches 'work/project')"
1066
+ }
1067
+ }
1068
+ }
1069
+ ```
1070
+
1071
+ **Output Schema:** `gno://schemas/tags-list@1.0`
1072
+
1073
+ **Response:**
1074
+
1075
+ ```json
1076
+ {
1077
+ "content": [
1078
+ {
1079
+ "type": "text",
1080
+ "text": "Found 5 tags:\n\n work (10)\n personal (5)\n ..."
1081
+ }
1082
+ ],
1083
+ "structuredContent": {
1084
+ "tags": [
1085
+ { "tag": "work", "count": 10 },
1086
+ { "tag": "personal", "count": 5 }
1087
+ ],
1088
+ "meta": {
1089
+ "collection": null,
1090
+ "prefix": null,
1091
+ "totalTags": 5
1092
+ }
1093
+ }
1094
+ }
1095
+ ```
1096
+
1097
+ ---
1098
+
1099
+ ### gno_links
1100
+
1101
+ Get outgoing links from a document.
1102
+
1103
+ **Input Schema:**
1104
+
1105
+ ```json
1106
+ {
1107
+ "type": "object",
1108
+ "properties": {
1109
+ "ref": {
1110
+ "type": "string",
1111
+ "description": "Document reference: gno:// URI, collection/path, or #docid"
1112
+ },
1113
+ "type": {
1114
+ "type": "string",
1115
+ "enum": ["wiki", "markdown"],
1116
+ "description": "Filter by link type"
1117
+ }
1118
+ },
1119
+ "required": ["ref"]
1120
+ }
1121
+ ```
1122
+
1123
+ **Output Schema:** `gno://schemas/links@1.0`
1124
+
1125
+ **Response:**
1126
+
1127
+ ```json
1128
+ {
1129
+ "content": [
1130
+ {
1131
+ "type": "text",
1132
+ "text": "Found 3 outgoing links in gno://notes/index.md:\n\n [wiki] Target Note (line 5)\n ..."
1133
+ }
1134
+ ],
1135
+ "structuredContent": {
1136
+ "links": [
1137
+ {
1138
+ "targetRef": "Target Note",
1139
+ "targetAnchor": "section-1",
1140
+ "targetCollection": "notes",
1141
+ "linkType": "wiki",
1142
+ "linkText": "see target",
1143
+ "position": { "startLine": 5, "startCol": 10 }
1144
+ }
1145
+ ],
1146
+ "meta": {
1147
+ "docid": "#a1b2c3d4",
1148
+ "uri": "gno://notes/index.md",
1149
+ "title": "Index",
1150
+ "totalLinks": 3,
1151
+ "filterType": null
1152
+ }
1153
+ }
1154
+ }
1155
+ ```
1156
+
1157
+ **Errors:**
1158
+
1159
+ - Document not found: returns `isError: true`
1160
+ - Invalid ref format: returns `isError: true`
1161
+
1162
+ ---
1163
+
1164
+ ### gno_backlinks
1165
+
1166
+ Get documents linking TO a document.
1167
+
1168
+ **Input Schema:**
1169
+
1170
+ ```json
1171
+ {
1172
+ "type": "object",
1173
+ "properties": {
1174
+ "ref": {
1175
+ "type": "string",
1176
+ "description": "Document reference: gno:// URI, collection/path, or #docid"
1177
+ },
1178
+ "collection": {
1179
+ "type": "string",
1180
+ "description": "Filter source documents by collection"
1181
+ }
1182
+ },
1183
+ "required": ["ref"]
1184
+ }
1185
+ ```
1186
+
1187
+ **Output Schema:** `gno://schemas/backlinks@1.0`
1188
+
1189
+ **Response:**
1190
+
1191
+ ```json
1192
+ {
1193
+ "content": [
1194
+ {
1195
+ "type": "text",
1196
+ "text": "Found 2 backlinks to gno://notes/target.md:\n\n gno://notes/index.md \"Index\" (line 10)\n ..."
1197
+ }
1198
+ ],
1199
+ "structuredContent": {
1200
+ "backlinks": [
1201
+ {
1202
+ "sourceDocUri": "gno://notes/index.md",
1203
+ "sourceDocTitle": "Index",
1204
+ "linkText": "Target Note",
1205
+ "position": { "startLine": 10, "startCol": 5 }
1206
+ }
1207
+ ],
1208
+ "meta": {
1209
+ "docid": "#a1b2c3d4",
1210
+ "uri": "gno://notes/target.md",
1211
+ "title": "Target Note",
1212
+ "totalBacklinks": 2,
1213
+ "filterCollection": null
1214
+ }
1215
+ }
1216
+ }
1217
+ ```
1218
+
1219
+ **Errors:**
1220
+
1221
+ - Document not found: returns `isError: true`
1222
+ - Collection not found: returns `isError: true`
1223
+ - Invalid ref format: returns `isError: true`
1224
+
1225
+ ---
1226
+
1227
+ ### gno_similar
1228
+
1229
+ Find semantically similar documents using vector embeddings.
1230
+
1231
+ **Input Schema:**
1232
+
1233
+ ```json
1234
+ {
1235
+ "type": "object",
1236
+ "properties": {
1237
+ "ref": {
1238
+ "type": "string",
1239
+ "description": "Document reference: gno:// URI, collection/path, or #docid"
1240
+ },
1241
+ "limit": {
1242
+ "type": "integer",
1243
+ "description": "Maximum number of similar documents (1-50)",
1244
+ "default": 5,
1245
+ "minimum": 1,
1246
+ "maximum": 50
1247
+ },
1248
+ "threshold": {
1249
+ "type": "number",
1250
+ "description": "Minimum similarity score (0-1)",
1251
+ "minimum": 0,
1252
+ "maximum": 1
1253
+ },
1254
+ "crossCollection": {
1255
+ "type": "boolean",
1256
+ "description": "Include documents from other collections",
1257
+ "default": false
1258
+ }
1259
+ },
1260
+ "required": ["ref"]
1261
+ }
1262
+ ```
1263
+
1264
+ **Output Schema:** `gno://schemas/similar@1.0`
1265
+
1266
+ **Response:**
1267
+
1268
+ ```json
1269
+ {
1270
+ "content": [
1271
+ {
1272
+ "type": "text",
1273
+ "text": "Found 3 similar documents for gno://notes/readme.md:\n\n [#def5678] gno://notes/guide.md (0.85)\n ..."
1274
+ }
1275
+ ],
1276
+ "structuredContent": {
1277
+ "similar": [
1278
+ {
1279
+ "docid": "#def5678",
1280
+ "uri": "gno://notes/guide.md",
1281
+ "title": "Guide",
1282
+ "score": 0.85,
1283
+ "absPath": "/path/to/notes/guide.md"
1284
+ }
1285
+ ],
1286
+ "meta": {
1287
+ "docid": "#a1b2c3d4",
1288
+ "uri": "gno://notes/readme.md",
1289
+ "title": "README",
1290
+ "totalSimilar": 3,
1291
+ "threshold": null,
1292
+ "crossCollection": false
1293
+ }
1294
+ }
1295
+ }
1296
+ ```
1297
+
1298
+ **Algorithm:**
1299
+
1300
+ 1. Get all chunks for the source document
1301
+ 2. Retrieve embeddings for each chunk from content_vectors
1302
+ 3. Compute average embedding across all chunks
1303
+ 4. Search for nearest neighbors using sqlite-vec
1304
+ 5. Exclude self and filter by collection if not crossCollection
1305
+ 6. Return top N similar documents with scores
1306
+
1307
+ **Errors:**
1308
+
1309
+ - Document not found: returns `isError: true`
1310
+ - Document has no content: returns `isError: true`
1311
+ - Document has no embeddings: returns `isError: true`
1312
+ - Vector search unavailable (sqlite-vec not loaded): returns `isError: true`
1313
+ - Invalid ref format: returns `isError: true`
1314
+
1315
+ ---
1316
+
1317
+ ### gno_graph
1318
+
1319
+ Get knowledge graph of document connections plus graph-health report fields.
1320
+
1321
+ **Input Schema:**
1322
+
1323
+ ```json
1324
+ {
1325
+ "type": "object",
1326
+ "properties": {
1327
+ "collection": {
1328
+ "type": "string",
1329
+ "description": "Filter to single collection"
1330
+ },
1331
+ "limit": {
1332
+ "type": "integer",
1333
+ "description": "Maximum nodes (1-5000)",
1334
+ "default": 2000,
1335
+ "minimum": 1,
1336
+ "maximum": 5000
1337
+ },
1338
+ "edgeLimit": {
1339
+ "type": "integer",
1340
+ "description": "Maximum edges (1-50000)",
1341
+ "default": 10000,
1342
+ "minimum": 1,
1343
+ "maximum": 50000
1344
+ },
1345
+ "includeSimilar": {
1346
+ "type": "boolean",
1347
+ "description": "Include semantic similarity edges",
1348
+ "default": false
1349
+ },
1350
+ "threshold": {
1351
+ "type": "number",
1352
+ "description": "Similarity threshold (0-1)",
1353
+ "default": 0.7,
1354
+ "minimum": 0,
1355
+ "maximum": 1
1356
+ },
1357
+ "linkedOnly": {
1358
+ "type": "boolean",
1359
+ "description": "Exclude isolated nodes (no connections)",
1360
+ "default": true
1361
+ },
1362
+ "similarTopK": {
1363
+ "type": "integer",
1364
+ "description": "Similar documents per node (1-20)",
1365
+ "default": 5,
1366
+ "minimum": 1,
1367
+ "maximum": 20
1368
+ }
1369
+ },
1370
+ "required": []
1371
+ }
1372
+ ```
1373
+
1374
+ **Output Schema:** `gno://schemas/graph@1.0`
1375
+
1376
+ The structured response includes `report.hubs`, `report.bridgeCandidates`,
1377
+ `report.isolated`, `report.unresolvedLinks`, `report.edgeTypes`,
1378
+ `report.edgeConfidence`, `report.communities`, node `communityId` assignments,
1379
+ and per-edge `confidence` / `audit` metadata so agents can assess graph health,
1380
+ clusters, and trust before deeper traversal.
1381
+
1382
+ **Response:**
1383
+
1384
+ ```json
1385
+ {
1386
+ "content": [
1387
+ {
1388
+ "type": "text",
1389
+ "text": "Knowledge Graph: 150 nodes, 320 edges\n\nTop nodes by degree:\n [#abc123] gno://notes/readme.md \"README\" (degree: 12)\n ..."
1390
+ }
1391
+ ],
1392
+ "structuredContent": {
1393
+ "nodes": [
1394
+ {
1395
+ "id": "#abc123",
1396
+ "uri": "gno://notes/readme.md",
1397
+ "title": "README",
1398
+ "collection": "notes",
1399
+ "relPath": "readme.md",
1400
+ "degree": 12,
1401
+ "communityId": "c1"
1402
+ }
1403
+ ],
1404
+ "links": [
1405
+ {
1406
+ "source": "#abc123",
1407
+ "target": "#def456",
1408
+ "type": "wiki",
1409
+ "weight": 1
1410
+ }
1411
+ ],
1412
+ "meta": {
1413
+ "collection": null,
1414
+ "nodeLimit": 2000,
1415
+ "edgeLimit": 10000,
1416
+ "totalNodes": 150,
1417
+ "totalEdges": 320,
1418
+ "returnedNodes": 150,
1419
+ "returnedEdges": 320,
1420
+ "truncated": false,
1421
+ "linkedOnly": true,
1422
+ "includedSimilar": false
1423
+ }
1424
+ }
1425
+ }
1426
+ ```
1427
+
1428
+ **Edge Types:**
1429
+
1430
+ - `wiki`: Wiki link (`[[Target]]`)
1431
+ - `markdown`: Markdown link (`[text](path.md)`)
1432
+ - `similar`: Semantic similarity (only when `includeSimilar: true`)
1433
+
1434
+ **Errors:**
1435
+
1436
+ - Collection not found: returns `isError: true`
1437
+
1438
+ ---
1439
+
1440
+ ### gno_graph_query
1441
+
1442
+ Bounded typed-edge traversal over the `doc_edges` relationship layer. This
1443
+ read-only tool wraps the shared graph-query core.
1444
+
1445
+ **Input Schema:**
1446
+
1447
+ ```json
1448
+ {
1449
+ "ref": "gno://notes/people/alice.md",
1450
+ "direction": "both",
1451
+ "edgeType": "works_at",
1452
+ "maxDepth": 2,
1453
+ "maxNodes": 100,
1454
+ "frontierLimit": 100,
1455
+ "visitedLimit": 500
1456
+ }
1457
+ ```
1458
+
1459
+ - `ref`: root document ref (URI, `#docid`, or `collection/path`).
1460
+ - `direction`: `out`, `in`, or `both` (default `both`).
1461
+ - `edgeType`: optional semantic edge type filter.
1462
+ - `relation`: alias for `edgeType`; if both are set they must match.
1463
+ - `maxDepth`: 1-6, default 2.
1464
+ - `maxNodes`: 1-1000, default 100.
1465
+ - `frontierLimit`: 1-1000, default 100.
1466
+ - `visitedLimit`: 1-5000, default 500.
1467
+
1468
+ **Output Schema:** `gno://schemas/graph-query@1.0`
1469
+
1470
+ Structured content includes `schemaVersion`, resolved `root`, typed `nodes`
1471
+ with graph hints, typed `edges` with `edgeType`/`relationType`/`confidence`/
1472
+ `edgeSource`, and `meta` with direction, caps, returned counts, warnings, and
1473
+ `truncated`.
1474
+
1475
+ Use for explicit relationship questions over typed edges such as `works_at`,
1476
+ `attended`, or `mentions` after a seed ref is known. Use `gno_query` first if
1477
+ the seed document is unknown.
1478
+
1479
+ ---
1480
+
1481
+ ### gno_graph_neighbors
1482
+
1483
+ Find incoming and outgoing graph neighbors for one document/node.
1484
+
1485
+ **Input Schema:** same graph filter fields as `gno_graph`, plus:
1486
+
1487
+ ```json
1488
+ {
1489
+ "ref": "notes/readme.md",
1490
+ "direction": "both"
1491
+ }
1492
+ ```
1493
+
1494
+ - `ref`: URI, `#docid`, `collection/path`, `relPath`, or exact title.
1495
+ - `direction`: `both`, `out`, or `in` (default: `both`).
1496
+
1497
+ Use for relationship questions, missed related docs, and corpus navigation after
1498
+ `gno_query` finds a seed document. Follow with `gno_get` for evidence.
1499
+
1500
+ ---
1501
+
1502
+ ### gno_graph_path
1503
+
1504
+ Find the shortest relationship path between two documents/nodes.
1505
+
1506
+ **Input Schema:** same graph filter fields as `gno_graph`, plus:
1507
+
1508
+ ```json
1509
+ {
1510
+ "from": "notes/a.md",
1511
+ "to": "notes/b.md",
1512
+ "maxDepth": 6
1513
+ }
1514
+ ```
1515
+
1516
+ - `from`, `to`: URI, `#docid`, `collection/path`, `relPath`, or exact title.
1517
+ - `maxDepth`: maximum hops to search (1-12, default: 6).
1518
+
1519
+ Use for "how are X and Y connected?" prompts. Run `gno_query` first when either
1520
+ endpoint is unknown, then read path nodes with `gno_get`.
1521
+
1522
+ ---
1523
+
1524
+ ### gno_add_collection
1525
+
1526
+ Add a folder as a new collection and start indexing (write-enabled).
1527
+
1528
+ **Input Schema:**
1529
+
1530
+ ```json
1531
+ {
1532
+ "type": "object",
1533
+ "properties": {
1534
+ "path": {
1535
+ "type": "string",
1536
+ "description": "Absolute or ~-expanded folder path"
1537
+ },
1538
+ "name": {
1539
+ "type": "string",
1540
+ "description": "Optional collection name (defaults to folder name)"
1541
+ },
1542
+ "pattern": {
1543
+ "type": "string",
1544
+ "description": "Glob pattern (default: **/*.md)"
1545
+ },
1546
+ "include": {
1547
+ "type": "array",
1548
+ "items": { "type": "string" },
1549
+ "description": "Additional include patterns"
1550
+ },
1551
+ "exclude": {
1552
+ "type": "array",
1553
+ "items": { "type": "string" },
1554
+ "description": "Exclude patterns"
1555
+ },
1556
+ "gitPull": {
1557
+ "type": "boolean",
1558
+ "description": "Run git pull before indexing",
1559
+ "default": false
1560
+ }
1561
+ },
1562
+ "required": ["path"]
1563
+ }
1564
+ ```
1565
+
1566
+ **Output Schema:** `gno://schemas/mcp-add-collection-result@1.0`
1567
+
1568
+ ---
1569
+
1570
+ ### gno_create_folder
1571
+
1572
+ Create a folder inside an existing collection (write-enabled).
1573
+
1574
+ **Input Schema:**
1575
+
1576
+ ```json
1577
+ {
1578
+ "type": "object",
1579
+ "properties": {
1580
+ "collection": { "type": "string" },
1581
+ "name": { "type": "string" },
1582
+ "parentPath": { "type": "string" }
1583
+ },
1584
+ "required": ["collection", "name"]
1585
+ }
1586
+ ```
1587
+
1588
+ ---
1589
+
1590
+ ### gno_rename_note
1591
+
1592
+ Rename an editable note in place (write-enabled).
1593
+
1594
+ **Input Schema:**
1595
+
1596
+ ```json
1597
+ {
1598
+ "type": "object",
1599
+ "properties": {
1600
+ "ref": { "type": "string" },
1601
+ "name": { "type": "string" }
1602
+ },
1603
+ "required": ["ref", "name"]
1604
+ }
1605
+ ```
1606
+
1607
+ ---
1608
+
1609
+ ### gno_move_note
1610
+
1611
+ Move an editable note to another folder in the same collection (write-enabled).
1612
+
1613
+ **Input Schema:**
1614
+
1615
+ ```json
1616
+ {
1617
+ "type": "object",
1618
+ "properties": {
1619
+ "ref": { "type": "string" },
1620
+ "folderPath": { "type": "string" },
1621
+ "name": { "type": "string" }
1622
+ },
1623
+ "required": ["ref", "folderPath"]
1624
+ }
1625
+ ```
1626
+
1627
+ ---
1628
+
1629
+ ### gno_duplicate_note
1630
+
1631
+ Duplicate an editable note into the current or another folder (write-enabled).
1632
+
1633
+ **Input Schema:**
1634
+
1635
+ ```json
1636
+ {
1637
+ "type": "object",
1638
+ "properties": {
1639
+ "ref": { "type": "string" },
1640
+ "folderPath": { "type": "string" },
1641
+ "name": { "type": "string" }
1642
+ },
1643
+ "required": ["ref"]
1644
+ }
1645
+ ```
1646
+
1647
+ ---
1648
+
1649
+ ### gno_sync
1650
+
1651
+ Reindex one or all collections (write-enabled).
1652
+
1653
+ **Input Schema:**
1654
+
1655
+ ```json
1656
+ {
1657
+ "type": "object",
1658
+ "properties": {
1659
+ "collection": {
1660
+ "type": "string",
1661
+ "description": "Collection to sync (all if omitted)"
1662
+ },
1663
+ "gitPull": {
1664
+ "type": "boolean",
1665
+ "description": "Run git pull before indexing",
1666
+ "default": false
1667
+ },
1668
+ "runUpdateCmd": {
1669
+ "type": "boolean",
1670
+ "description": "Run updateCmd before indexing (default: false for MCP)",
1671
+ "default": false
1672
+ }
1673
+ }
1674
+ }
1675
+ ```
1676
+
1677
+ **Output Schema:** `gno://schemas/mcp-sync-result@1.0`
1678
+
1679
+ ---
1680
+
1681
+ ### gno_embed
1682
+
1683
+ Generate embeddings for unembedded chunks (write-enabled). Runs as background job.
1684
+
1685
+ **Input Schema:**
1686
+
1687
+ ```json
1688
+ {
1689
+ "type": "object",
1690
+ "properties": {
1691
+ "collection": {
1692
+ "type": "string",
1693
+ "description": "Optional collection name to embed"
1694
+ }
1695
+ }
1696
+ }
1697
+ ```
1698
+
1699
+ **Output Schema:** `gno://schemas/mcp-embed-result@1.0`
1700
+
1701
+ **Response:**
1702
+
1703
+ ```json
1704
+ {
1705
+ "content": [
1706
+ {
1707
+ "type": "text",
1708
+ "text": "Job: <uuid>\nStatus: started\nModel: <model-uri>"
1709
+ }
1710
+ ],
1711
+ "structuredContent": {
1712
+ "jobId": "<uuid>",
1713
+ "status": "started",
1714
+ "model": "<model-uri>"
1715
+ }
1716
+ }
1717
+ ```
1718
+
1719
+ **Notes:**
1720
+
1721
+ - Requires `--enable-write` flag
1722
+ - Fails fast if embedding model not cached (run `gno models pull embed` first)
1723
+ - Poll job status with `gno_job_status`
1724
+
1725
+ ---
1726
+
1727
+ ### gno_clear_collection_embeddings
1728
+
1729
+ Clear stale or all embeddings for one collection (write-enabled).
1730
+
1731
+ **Input Schema:**
1732
+
1733
+ ```json
1734
+ {
1735
+ "type": "object",
1736
+ "required": ["collection"],
1737
+ "properties": {
1738
+ "collection": {
1739
+ "type": "string",
1740
+ "description": "Collection name"
1741
+ },
1742
+ "mode": {
1743
+ "type": "string",
1744
+ "enum": ["stale", "all"],
1745
+ "default": "stale"
1746
+ }
1747
+ }
1748
+ }
1749
+ ```
1750
+
1751
+ ---
1752
+
1753
+ ### gno_index
1754
+
1755
+ Full index: sync files + generate embeddings (write-enabled). Runs as background job.
1756
+
1757
+ **Input Schema:**
1758
+
1759
+ ```json
1760
+ {
1761
+ "type": "object",
1762
+ "properties": {
1763
+ "collection": {
1764
+ "type": "string",
1765
+ "description": "Collection to index (all if omitted)"
1766
+ },
1767
+ "gitPull": {
1768
+ "type": "boolean",
1769
+ "description": "Run git pull before sync",
1770
+ "default": false
1771
+ }
1772
+ }
1773
+ }
1774
+ ```
1775
+
1776
+ **Output Schema:** `gno://schemas/mcp-index-result@1.0`
1777
+
1778
+ **Response:**
1779
+
1780
+ ```json
1781
+ {
1782
+ "content": [
1783
+ {
1784
+ "type": "text",
1785
+ "text": "Job: <uuid>\nStatus: started\nCollections: work, notes\nPhases: sync → embed"
1786
+ }
1787
+ ],
1788
+ "structuredContent": {
1789
+ "jobId": "<uuid>",
1790
+ "status": "started",
1791
+ "collections": ["work", "notes"],
1792
+ "phases": ["sync", "embed"],
1793
+ "options": {
1794
+ "gitPull": false,
1795
+ "runUpdateCmd": false
1796
+ }
1797
+ }
1798
+ }
1799
+ ```
1800
+
1801
+ **Notes:**
1802
+
1803
+ - Requires `--enable-write` flag
1804
+ - Runs sync phase first, then embed phase
1805
+ - `runUpdateCmd` is always false for MCP (security)
1806
+ - Fails fast if embedding model not cached
1807
+ - Poll job status with `gno_job_status`
1808
+
1809
+ ---
1810
+
1811
+ ### gno_remove_collection
1812
+
1813
+ Remove a collection from config (write-enabled). Indexed data is retained.
1814
+
1815
+ **Input Schema:**
1816
+
1817
+ ```json
1818
+ {
1819
+ "type": "object",
1820
+ "properties": {
1821
+ "collection": {
1822
+ "type": "string",
1823
+ "description": "Collection name to remove"
1824
+ }
1825
+ },
1826
+ "required": ["collection"]
1827
+ }
1828
+ ```
1829
+
1830
+ **Output Schema:** `gno://schemas/mcp-remove-result@1.0`
1831
+
1832
+ ---
1833
+
1834
+ ### gno_job_status
1835
+
1836
+ Get status of a background job.
1837
+
1838
+ **Input Schema:**
1839
+
1840
+ ```json
1841
+ {
1842
+ "type": "object",
1843
+ "properties": {
1844
+ "jobId": {
1845
+ "type": "string",
1846
+ "description": "Job identifier"
1847
+ }
1848
+ },
1849
+ "required": ["jobId"]
1850
+ }
1851
+ ```
1852
+
1853
+ **Output Schema:** `gno://schemas/mcp-job-status@1.0`
1854
+
1855
+ ---
1856
+
1857
+ ### gno_list_jobs
1858
+
1859
+ List active and recent jobs.
1860
+
1861
+ **Input Schema:**
1862
+
1863
+ ```json
1864
+ {
1865
+ "type": "object",
1866
+ "properties": {
1867
+ "limit": {
1868
+ "type": "integer",
1869
+ "description": "Max recent jobs to return",
1870
+ "default": 10
1871
+ }
1872
+ }
1873
+ }
1874
+ ```
1875
+
1876
+ **Output Schema:** `gno://schemas/mcp-job-list@1.0`
1877
+
1878
+ ---
1879
+
1880
+ ## Resources
1881
+
1882
+ ### gno://tags
1883
+
1884
+ List all tags with document counts. Supports query parameters for filtering.
1885
+
1886
+ **URI Pattern:** `gno://tags` or `gno://tags?collection=x&prefix=work/`
1887
+
1888
+ **Query Parameters:**
1889
+
1890
+ | Parameter | Description |
1891
+ | ------------ | ------------------------------------- |
1892
+ | `collection` | Filter tags by collection name |
1893
+ | `prefix` | Filter tags by prefix (e.g., `work/`) |
1894
+
1895
+ **Response:**
1896
+
1897
+ MIME type: `application/json`
1898
+
1899
+ ```json
1900
+ {
1901
+ "tags": [
1902
+ { "tag": "work", "count": 10 },
1903
+ { "tag": "personal", "count": 5 }
1904
+ ],
1905
+ "meta": {
1906
+ "collection": null,
1907
+ "prefix": null,
1908
+ "totalTags": 2
1909
+ }
1910
+ }
1911
+ ```
1912
+
1913
+ ---
1914
+
1915
+ ### gno://{collection}/{path}
1916
+
1917
+ Read document content by URI.
1918
+
1919
+ **URI Pattern:** `gno://{collection}/{relativePath}[?index={name}]`
1920
+
1921
+ **Examples:**
1922
+
1923
+ - `gno://work/contracts/nda.docx`
1924
+ - `gno://notes/2025/01/meeting.md`
1925
+ - `gno://notes/2025/01/meeting.md?index=research`
1926
+
1927
+ **Response:**
1928
+
1929
+ MIME type: `text/markdown`
1930
+
1931
+ Content includes optional header comment:
1932
+
1933
+ ```markdown
1934
+ <!-- gno://work/contracts/nda.docx
1935
+ docid: #a1b2c3d4
1936
+ source: /abs/path/to/nda.docx
1937
+ mime: application/vnd.openxmlformats-officedocument.wordprocessingml.document
1938
+ -->
1939
+
1940
+ 1: # Contract
1941
+ 2:
1942
+ 3: This Non-Disclosure Agreement...
1943
+ ```
1944
+
1945
+ **Header Fields:**
1946
+ | Field | Description |
1947
+ |-------|-------------|
1948
+ | URI | Full gno:// URI |
1949
+ | docid | Document ID |
1950
+ | source | Absolute path to source file |
1951
+ | mime | Source file MIME type |
1952
+ | language | Document language hint (if available) |
1953
+
1954
+ **Behavior:**
1955
+
1956
+ - Returns Markdown mirror content (converted from source)
1957
+ - Line numbers included by default for agent friendliness
1958
+ - Header is display-only, not part of indexed content
1959
+ - An `index` query opens that named database; a missing index errors without
1960
+ creating an empty database
1961
+
1962
+ **Errors:**
1963
+
1964
+ - Document not found: standard MCP resource error
1965
+ - Collection not found: standard MCP resource error
1966
+
1967
+ ---
1968
+
1969
+ ## URI Encoding
1970
+
1971
+ Special characters in URIs are URL-encoded per RFC 3986:
1972
+
1973
+ | Character | Encoded |
1974
+ | --------- | ------- |
1975
+ | Space | `%20` |
1976
+ | `#` | `%23` |
1977
+ | `?` | `%3F` |
1978
+ | `%` | `%25` |
1979
+
1980
+ Path separators (`/`) are preserved.
1981
+
1982
+ **Example:**
1983
+
1984
+ - File: `My Documents/file name.pdf`
1985
+ - URI: `gno://work/My%20Documents/file%20name.pdf`
1986
+
1987
+ ---
1988
+
1989
+ ## Error Handling
1990
+
1991
+ Tool errors return:
1992
+
1993
+ ```json
1994
+ {
1995
+ "isError": true,
1996
+ "content": [
1997
+ {
1998
+ "type": "text",
1999
+ "text": "Error: Document not found: #invalid"
2000
+ }
2001
+ ]
2002
+ }
2003
+ ```
2004
+
2005
+ Resource errors use standard MCP error responses.
2006
+
2007
+ ### MCP Error Codes (Write Tools)
2008
+
2009
+ - `NOT_FOUND` — Resource not found
2010
+ - `DUPLICATE` — Resource already exists
2011
+ - `CONFLICT` — Conflict with existing resource
2012
+ - `HAS_REFERENCES` — Collection referenced by contexts
2013
+ - `INVALID_PATH` — Path violates safety rules
2014
+ - `PATH_NOT_FOUND` — Path does not exist
2015
+ - `JOB_CONFLICT` — Another job is already running
2016
+ - `LOCKED` — Another MCP process holds the write lock
2017
+
2018
+ ---
2019
+
2020
+ ## Versioning
2021
+
2022
+ ### Tool Versioning
2023
+
2024
+ Tools are versioned via the server version. Breaking changes require major version bump.
2025
+
2026
+ **Compatibility Rules:**
2027
+
2028
+ - New optional input parameters: minor version
2029
+ - New output fields: minor version
2030
+ - Removing/renaming parameters: major version
2031
+ - Changing output structure: major version
2032
+
2033
+ ### Schema Versioning
2034
+
2035
+ Output schemas include version in `$id`:
2036
+
2037
+ - `gno://schemas/search-result@1.0`
2038
+ - `gno://schemas/capture-receipt@1.0`
2039
+
2040
+ Clients should check schema version for compatibility.
2041
+
2042
+ ---
2043
+
2044
+ ## Session Behavior
2045
+
2046
+ - DB connection kept open for server lifetime
2047
+ - No persistent state between tool calls
2048
+ - Each tool call is independent
2049
+ - Server handles concurrent requests sequentially
2050
+
2051
+ ---
2052
+
2053
+ ## CLI Commands
2054
+
2055
+ GNO provides CLI commands to manage MCP server installation.
2056
+
2057
+ ### gno mcp install
2058
+
2059
+ Install gno as an MCP server in client configurations.
2060
+
2061
+ **Synopsis:**
2062
+
2063
+ ```bash
2064
+ gno mcp install [options]
2065
+ ```
2066
+
2067
+ **Options:**
2068
+
2069
+ | Option | Description | Default |
2070
+ | ----------------------- | ------------------------------------------------------------------------------------------------------------- | --------------------------------- |
2071
+ | `-t, --target <target>` | One of the 10 supported automatic clients | `claude-desktop` |
2072
+ | `-s, --scope <scope>` | `user` or `project`; project is supported by Claude Code, Codex, Cursor, OpenCode, and project-only LibreChat | Target default (otherwise `user`) |
2073
+ | `-f, --force` | Overwrite existing configuration | `false` |
2074
+ | `--dry-run` | Show what would be done without changes | `false` |
2075
+ | `--enable-write` | Install config with `--enable-write` args | `false` |
2076
+ | `--json` | JSON output | `false` |
2077
+
2078
+ **Config Locations:**
2079
+
2080
+ | Target | Scope(s) | Config path |
2081
+ | ---------------- | ------------- | --------------------------------------------------------------------- |
2082
+ | `claude-desktop` | user | `~/Library/Application Support/Claude/claude_desktop_config.json` |
2083
+ | `claude-code` | user, project | `~/.claude.json`, `./.mcp.json` |
2084
+ | `codex` | user, project | `~/.codex/config.toml`, `./.codex/config.toml` |
2085
+ | `cursor` | user, project | `~/.cursor/mcp.json`, `./.cursor/mcp.json` |
2086
+ | `zed` | user | `~/.config/zed/settings.json`; Windows: `%APPDATA%\Zed\settings.json` |
2087
+ | `windsurf` | user | `~/.codeium/windsurf/mcp_config.json` |
2088
+ | `opencode` | user, project | `~/.config/opencode/opencode.json`, `./opencode.json` |
2089
+ | `amp` | user | `~/.config/amp/settings.json` |
2090
+ | `lmstudio` | user | `~/.lmstudio/mcp.json` |
2091
+ | `librechat` | project | `./librechat.yaml` |
2092
+
2093
+ **Example:**
2094
+
2095
+ ```bash
2096
+ # Install for Claude Desktop (default)
2097
+ gno mcp install
2098
+
2099
+ # Install for Claude Code (user scope)
2100
+ gno mcp install -t claude-code
2101
+
2102
+ # Install for Claude Code (project scope)
2103
+ gno mcp install -t claude-code -s project
2104
+
2105
+ # Install for project-only LibreChat
2106
+ gno mcp install -t librechat -s project
2107
+
2108
+ # Preview changes
2109
+ gno mcp install --dry-run
2110
+
2111
+ # Install with write tools enabled
2112
+ gno mcp install --enable-write
2113
+ ```
2114
+
2115
+ **Installed entry contract:** The command is the absolute current Bun
2116
+ executable. Arguments are `run`, the absolute `src/index.ts` from the currently
2117
+ installed GNO package, `--index <active>`, `--config <absolute>`, then `mcp`.
2118
+ `--enable-write`, when requested, follows `mcp`. The config path is the active
2119
+ explicit, environment-selected, or default config resolved to an absolute path.
2120
+ Entries also pin absolute `GNO_DATA_DIR` and `GNO_CACHE_DIR` values under `env`
2121
+ (`environment` for OpenCode). No other environment keys are accepted by status
2122
+ or activation verification, and all values must be absolute paths without
2123
+ control characters. Codex uses native `[mcp_servers.gno]` and nested
2124
+ `[mcp_servers.gno.env]` TOML tables; install, update, and uninstall preserve
2125
+ unrelated TOML and comments. The index, config, data, and cache identity are
2126
+ always pinned because GUI clients do not reliably inherit the installing
2127
+ shell's `PATH` or environment. Invalid or empty index names fail before the
2128
+ target client config is written.
2129
+ JSON/JSONC targets preserve comments, trailing commas, and unrelated layout;
2130
+ OpenCode and Amp reuse supported existing `.jsonc` alternates rather than
2131
+ creating duplicate `.json` configs. `--dry-run --json` returns normalized
2132
+ command, argument, and workspace values rather than a target-specific persisted
2133
+ wrapper. Previewing an existing entry requires `--force --dry-run --json`.
2134
+
2135
+ Standard JSON/YAML entries use this shape (with target-specific outer keys):
2136
+
2137
+ ```json
2138
+ {
2139
+ "command": "/absolute/path/to/bun",
2140
+ "args": [
2141
+ "run",
2142
+ "/absolute/path/to/@gmickel/gno/src/index.ts",
2143
+ "--index",
2144
+ "default",
2145
+ "--config",
2146
+ "/absolute/path/to/index.yml",
2147
+ "mcp"
2148
+ ],
2149
+ "env": {
2150
+ "GNO_DATA_DIR": "/absolute/path/to/data",
2151
+ "GNO_CACHE_DIR": "/absolute/path/to/cache"
2152
+ }
2153
+ }
2154
+ ```
2155
+
2156
+ OpenCode stores the same executable and arguments in its `command` array and
2157
+ uses `environment`, not `env`. Codex stores the equivalent native TOML:
2158
+
2159
+ ```toml
2160
+ [mcp_servers.gno]
2161
+ command = "/absolute/path/to/bun"
2162
+ args = ["run", "/absolute/path/to/@gmickel/gno/src/index.ts", "--index", "default", "--config", "/absolute/path/to/index.yml", "mcp"]
2163
+
2164
+ [mcp_servers.gno.env]
2165
+ GNO_DATA_DIR = "/absolute/path/to/data"
2166
+ GNO_CACHE_DIR = "/absolute/path/to/cache"
2167
+ ```
2168
+
2169
+ ### gno mcp uninstall
2170
+
2171
+ Remove gno MCP server from client configurations.
2172
+
2173
+ **Synopsis:**
2174
+
2175
+ ```bash
2176
+ gno mcp uninstall [options]
2177
+ ```
2178
+
2179
+ **Options:**
2180
+
2181
+ | Option | Description | Default |
2182
+ | ----------------------- | ------------------------------------ | ---------------- |
2183
+ | `-t, --target <target>` | Target client | `claude-desktop` |
2184
+ | `-s, --scope <scope>` | Scope; LibreChat defaults to project | Target default |
2185
+ | `--json` | JSON output | `false` |
2186
+
2187
+ ### gno mcp status
2188
+
2189
+ Show MCP server installation status across all targets.
2190
+
2191
+ **Synopsis:**
2192
+
2193
+ ```bash
2194
+ gno mcp status [options]
2195
+ ```
2196
+
2197
+ **Options:**
2198
+
2199
+ | Option | Description | Default |
2200
+ | ----------------------- | --------------------------- | ------- |
2201
+ | `-t, --target <target>` | Filter by target (or `all`) | `all` |
2202
+ | `-s, --scope <scope>` | Filter by scope (or `all`) | `all` |
2203
+ | `--json` | JSON output | `false` |
2204
+
2205
+ **Example Output (abbreviated; unfiltered status enumerates 14 target/scope
2206
+ pairs):**
2207
+
2208
+ ```text
2209
+ MCP Server Status
2210
+ ──────────────────────────────────────────────────
2211
+
2212
+ ✓ Claude Desktop: configured
2213
+ Command: /path/to/bun
2214
+ Args: run /path/to/@gmickel/gno/src/index.ts --index default --config /absolute/path/to/index.yml mcp
2215
+ Config: ~/Library/Application Support/Claude/claude_desktop_config.json
2216
+
2217
+ ✗ Claude Code: not configured
2218
+ Config: ~/.claude.json
2219
+
2220
+ 1/14 targets configured
2221
+ ```
2222
+
2223
+ ---
2224
+
2225
+ ## See Also
2226
+
2227
+ - [CLI Specification](./cli.md)
2228
+ - [Output Schemas](./output-schemas/)
2229
+ - [MCP Protocol Specification](https://modelcontextprotocol.io/specification/)