omnigraph/docs/user/queries/index.md
Ragnor Comerford c06343362a
docs(mcp): document the MCP surface, authoring controls, and skill (v0.8.0)
Document the per-graph MCP surface (POST /graphs/{id}/mcp, shipped in the
preceding commits and landing under v0.8.0) and the `.gq` authoring controls
that shape stored-query tools.

- New docs/user/operations/mcp.md: the client-facing guide — transport, tool
  catalog (built-ins + stored queries), projection modes, structured output,
  authorization (call-authoritative + list-relaxation), Host/Origin policy, the
  protocol-version contract.
- docs/user/operations/server.md: the /mcp endpoint + an "MCP surface" section;
  docs/user/index.md: a "Connect an MCP agent" pointer.
- docs/user/queries/index.md: an Annotations section — query @description /
  @instruction / @mcp(expose, tool_name) and per-parameter @description.
- AGENTS.md: topic-table row + MCP note on the HTTP-server capability row.
- docs/dev/testing.md: the omnigraph-mcp crate + server tests/mcp.rs.
- docs/dev/rfc-005 §D5: retire the "cluster = everything exposed" bridge —
  cluster mode honors source `@mcp(expose: …)`; presentation vs authorization
  split made explicit.
- skills/omnigraph: server-policy.md MCP section; stored-queries.md corrected
  (per-query controls now ship via @mcp, not "planned"); SKILL.md MCP triggers,
  Deep Dives row, version → 0.8.0.
- docs/releases/v0.8.0.md: the MCP surface + authoring-controls release notes.

Crate version manifests are deliberately NOT bumped — that is the v0.8.0
release-cut step; this lands on the feature branch.
2026-06-17 16:04:29 +02:00

90 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Query Language (`.gq`)
## Query declarations
```
query <name>(@description("…") $p1: T1, $p2: T2?, …)
@description("…") @instruction("…") @mcp(tool_name: "…", expose: true) {
}
```
Two body shapes:
- **Read**: `match { … } return { … } [order { … }] [limit N]` — covered on this page.
- **Mutation**: one or more of `insert | update | delete` statements — see [mutations](../mutations/index.md).
Multi-modal search functions (`nearest`, `bm25`, `rrf`, …) used inside `match`,
`return`, and `order` are documented on the [search](../search/index.md) page.
Param types reuse all schema scalars; trailing `?` makes a param optional. The compiler reserves `$__nanograph_now` for `now()`.
### Annotations
Annotations after the param list are optional and order-independent:
- `@description("…")` — human-readable summary of the query (shown in the
stored-query catalog and as the MCP tool description).
- `@instruction("…")` — agent-facing *how/when to use* guidance. It is folded
into the [MCP](../operations/mcp.md) tool description (appended after
`@description`), so an agent reading `tools/list` sees it.
- `@mcp(...)`**MCP-presentation** controls for when the query is served as an
agent tool (see [mcp.md](../operations/mcp.md)). Both keys are optional:
- `tool_name: "<name>"` — the tool id to expose the query under (default: the
query name). Must be unique across exposed queries and must not shadow a
built-in tool, or the server refuses to boot.
- `expose: <bool>` — whether the query appears on the agent tool surface
(default `true`). `expose: false` keeps the query HTTP/service-callable by
name but hides it from `tools/list` and the catalog. This is **presentation
only** — not an authorization control (Cedar `invoke_query` governs who may
call it).
A **per-parameter** `@description("…")` (written before the variable, e.g.
`@description("the user's slug") $slug: String`) documents that argument; it is
surfaced into the parameter's JSON-Schema `description` in the catalog and the
MCP tool input schema.
## MATCH clauses
- **Binding**: `$x: NodeType { prop: <literal | $param | now()>, … }`
- **Traversal**: `$src EDGE_NAME { min, max? } $dst` — variable-length paths via hop bounds; default 1..1 if bounds omitted.
- **Filter**: `<expr> <op> <expr>` with operators `>=`, `<=`, `!=`, `>`, `<`, `=`, and string `contains`.
- **Negation**: `not { clause+ }` — desugars to anti-join over the inner pipeline.
## RETURN clause
`return { <expr> [as <alias>], … }` with expressions:
- Variable / property access: `$x`, `$x.prop`
- Literals: string, int, float, bool, list
- `now()`
- Aggregates: `count`, `sum`, `avg`, `min`, `max`
- [Search functions](../search/index.md) (so you can return a score column)
- `AliasRef` — re-use a previous projection alias
## ORDER & LIMIT
- `order { <expr> [asc|desc], … }` — supports plain expressions and `nearest(...)`.
- `limit <integer>` — required when there is a `nearest(...)` ordering.
- **Total, deterministic order.** Rows with equal user-sort keys are broken by the bound entities' key columns (`<var>.id`, ascending) appended as a final tie-break, so the result is a *total* order — reproducible across runs, and `order … limit N` returns a deterministic top-N even when ties straddle the cutoff. (Aggregate results have no entity-key columns; their group rows are already distinct on the projected group keys.)
- **NULL placement** is *nulls-first ascending, nulls-last descending* (i.e. `nulls_first = !descending`): a NULL sorts as if smaller than any value.
Write statements (`insert` / `update` / `delete`) are documented on the
[mutations](../mutations/index.md) page.
## Traversal execution
Variable-length traversals (`Expand`) are executed one of two ways, chosen per-expand by a cost model over cheap manifest counts (frontier size, edge count, source-vertex count, hops) plus index coverage: selective traversals (small frontier relative to the source set) resolve neighbors from the persisted `src`/`dst` BTREE (one indexed scan per hop); dense / deep / large-frontier traversals — or those whose BTREE coverage is degraded so a full scan would be paid per hop — use an in-memory CSR adjacency index. Both produce identical results. The `OMNIGRAPH_EXPAND_INDEXED_MAX_FRONTIER` / `OMNIGRAPH_EXPAND_INDEXED_MAX_HOPS` ceilings bound the *initial dispatch* frontier/hops (beyond them CSR is always used); the cost model estimates total indexed work as ~`hops × frontier × fanout` and prices dense fan-out toward CSR — they are not a hard per-hop bound. `OMNIGRAPH_TRAVERSAL_MODE=indexed|csr` forces a mode (see [constants](../reference/constants.md)).
## Linting & validation
Codes seen so far:
- **Q000** (Error): parse error
- **L201** (Warning): nullable property never set by any UPDATE — "{type}.{prop} exists in schema but no update query sets it"
- (Warning): mutation declares no params — hardcoded mutations are easy to miss
- Plus all type errors from type checking (undefined types, mismatched operators, undefined edges, etc.)
Lint output reports an overall status, per-query results (name, kind, status, any error and warnings), and structured findings (severity, code, message, and the type/property/query they apply to).
CLI exits non-zero only on `status = Error`.