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