mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-06-12 01:45:14 +02:00
Add GET /queries stored-query catalog endpoint
List a graph's mcp.expose stored queries as a typed tool catalog so a client
(the MCP server) can register them as tools without fetching .gq source.
Each entry carries name, MCP tool_name, description/instruction, a
read/mutate flag, and decomposed typed params (kind enum: string|bool|int|
bigint|float|date|datetime|blob|vector|list, plus item_kind for lists and
vector_dim) — so the consumer builds an input schema with a closed match and
never re-parses omnigraph type spelling. I64/U64 are bigint (string on the
wire): a JSON number loses precision past 2^53 and the engine already accepts
decimal strings.
Read-gated (works in default-deny; the catalog is graph-wide, authorized
against main). NOT Cedar-filtered per query yet — a reader can list a query
whose invoke_query they lack (documented gap until per-query authz lands);
invocation stays invoke_query-gated + deny==404.
- api: QueriesCatalogOutput / QueryCatalogEntry / ParamDescriptor / ParamKind
+ query_catalog_entry (reuses PropType::from_param_type_name; scalar_kind is
exhaustive, so a new ScalarType is a compile error here until catalogued).
- GET /queries route in per_graph_protected (→ /graphs/{id}/queries in multi
mode); OpenAPI regenerated; path allowlists updated.
- Tests: projection unit (every kind, list, vector, nullable, mutation,
empty) + handler (exposed-only filter, read-gate probe-oracle, empty
registry).
This commit is contained in:
parent
6cad21cb6a
commit
a087ac4476
6 changed files with 481 additions and 1 deletions
161
openapi.json
161
openapi.json
|
|
@ -829,6 +829,53 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/queries": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"queries"
|
||||
],
|
||||
"summary": "List the graph's exposed stored queries as a typed tool catalog.",
|
||||
"description": "Returns the `mcp.expose == true` subset of the `queries:` registry, each\nwith its MCP tool name, read/mutate flag, description/instruction, and\ntyped parameters — enough for a client to register them as tools without\nfetching `.gq` source. Read-gated; the catalog is graph-wide (branch\nindependent — `read` is authorized against `main`). **Not** Cedar-filtered\nper query yet, so it can list a query whose `invoke_query` the caller\nlacks (a known gap until per-query authorization lands).",
|
||||
"operationId": "list_queries",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Stored-query catalog (the mcp.expose subset, with typed params)",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/QueriesCatalogOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorOutput"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer_token": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/queries/{name}": {
|
||||
"post": {
|
||||
"tags": [
|
||||
|
|
@ -1840,6 +1887,120 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"ParamDescriptor": {
|
||||
"type": "object",
|
||||
"description": "One declared parameter of a stored query, projected for the catalog.",
|
||||
"required": [
|
||||
"name",
|
||||
"kind",
|
||||
"nullable"
|
||||
],
|
||||
"properties": {
|
||||
"item_kind": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ParamKind",
|
||||
"description": "Element kind when `kind == list` (always a scalar — the grammar\nforbids lists of vectors or nested lists)."
|
||||
}
|
||||
]
|
||||
},
|
||||
"kind": {
|
||||
"$ref": "#/components/schemas/ParamKind"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"nullable": {
|
||||
"type": "boolean",
|
||||
"description": "`false` → the caller must supply it; `true` → optional."
|
||||
},
|
||||
"vector_dim": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int32",
|
||||
"description": "Dimension when `kind == vector`.",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"ParamKind": {
|
||||
"type": "string",
|
||||
"description": "The kind of a stored-query parameter, decomposed so a client (e.g. an\nMCP server) can build a typed input schema with a closed `match` and\nnever re-parse omnigraph's type spelling. `bigint`/`date`/`datetime`/\n`blob` are carried as JSON strings on the wire: a 64-bit integer past\n2^53 loses precision as a JSON number, and Date/DateTime are ISO\nstrings, Blob a blob-URI string.",
|
||||
"enum": [
|
||||
"string",
|
||||
"bool",
|
||||
"int",
|
||||
"bigint",
|
||||
"float",
|
||||
"date",
|
||||
"datetime",
|
||||
"blob",
|
||||
"vector",
|
||||
"list"
|
||||
]
|
||||
},
|
||||
"QueriesCatalogOutput": {
|
||||
"type": "object",
|
||||
"description": "Response for `GET /queries`: the `mcp.expose` subset of a graph's\nstored-query registry, each with typed parameters.",
|
||||
"required": [
|
||||
"queries"
|
||||
],
|
||||
"properties": {
|
||||
"queries": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/QueryCatalogEntry"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"QueryCatalogEntry": {
|
||||
"type": "object",
|
||||
"description": "One entry in the stored-query catalog (`GET /queries`).",
|
||||
"required": [
|
||||
"name",
|
||||
"tool_name",
|
||||
"mutation",
|
||||
"params"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"instruction": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"mutation": {
|
||||
"type": "boolean",
|
||||
"description": "`true` for a stored mutation → an MCP read-only hint of `false`."
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Registry key / invoke path segment (`POST /queries/{name}`)."
|
||||
},
|
||||
"params": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ParamDescriptor"
|
||||
}
|
||||
},
|
||||
"tool_name": {
|
||||
"type": "string",
|
||||
"description": "MCP tool id (the `tool_name` override, else `name`)."
|
||||
}
|
||||
}
|
||||
},
|
||||
"QueryRequest": {
|
||||
"type": "object",
|
||||
"description": "Inline read-query request for `POST /query`.\n\nFriendlier-named alternative to [`ReadRequest`] for ad-hoc reads and\nAI-agent integration. Mutations are rejected with 400 — use `POST\n/mutate` (or its deprecated alias `POST /change`) for write queries.\nField names are deliberately short (`query`, `name`) to match the GQ\nkeyword and the CLI `-e` flag.",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue