diff --git a/AGENTS.md b/AGENTS.md index 266b097..34186b7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -117,6 +117,7 @@ referenced by name in these specs, not redefined here. ├── taxonomy.md # Vocabulary baseline, v1 capability matrix, MCP-vs-gRPC boundary, non-goals ├── resources.md # Per-family resource URIs (honua:// grammar), inspection fields, lifecycle, relationship graph ├── planning.md # Clarification, elicitation, planning, and handoff semantics + ├── transport.md # Session/streaming transport: protocol revision, Mcp-Session-Id, SSE, progress + list_changed notifications ├── corpus.md # Canonical dataset corpus, fixture conventions, scenario-pack taxonomy ├── conformance.md # Conformance fixtures, evaluation rubric, pass/fail, runtime portability └── schemas/ # JSON Schemas + index.json vocabulary map diff --git a/README.md b/README.md index 8b78cb2..f5bd45e 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ conformance strategy established; downstream consumer work tracked in | [Taxonomy, Capability Matrix, and Non-Goals](spec/taxonomy.md) | Vocabulary baseline, v1 coverage matrix, MCP vs gRPC boundary, explicit non-goals | | [MCP Resource Contracts](spec/resources.md) | Per-family resource URIs, inspection fields, lifecycle visibility, relationship graph for results, maps, apps, styles, themes, templates, and promotion surfaces | | [Clarification, Elicitation, Planning, and Handoff Semantics](spec/planning.md) | Clarification and elicitation semantics, assumption policies, per-family planning step kinds, boundary-crossing handoff contract | +| [Session and Streaming Transport](spec/transport.md) | Supported protocol revision (2025-06-18), `Mcp-Session-Id` sessions, streamable-HTTP/SSE and stdio transports, `notifications/progress`, and `notifications/*/list_changed` capability notifications | | [Canonical Dataset Corpus and Scenario Packs](spec/corpus.md) | Corpus layout, fixture descriptor conventions, canonical synthetic pack, publishing source packs, protocol mirrors, dirty-data packs, expected scenario shapes, and scenario-pack taxonomy | | [JSON Schemas](spec/schemas/README.md) | Machine-readable JSON Schema (draft 2020-12) bindings for each core tool `inputSchema` and resource payload shape, plus a `index.json` vocabulary map. Makes the prose implementable. | | [Conformance Fixtures and Evaluation](spec/conformance.md) | Fixture layout, operator-workflow scenario model, pass/fail rubric, runtime portability guidance, scenario coverage matrix | diff --git a/conformance/fixtures/tools/list_capabilities/list_capabilities.json b/conformance/fixtures/tools/list_capabilities/list_capabilities.json new file mode 100644 index 0000000..ecbeb97 --- /dev/null +++ b/conformance/fixtures/tools/list_capabilities/list_capabilities.json @@ -0,0 +1,13 @@ +{ + "id": "list_capabilities.analyze", + "schemaRef": "tools/list_capabilities.schema.json", + "validates": "inputs", + "inputs": { + "workflowFamilies": ["Analyze"], + "includeResources": true, + "includeGrounding": true, + "includeAnnotations": true + }, + "expected": "tool_result", + "canonicalRefs": ["CapabilityCatalog"] +} diff --git a/conformance/fixtures/tools/resolve_entity/resolve_entity.json b/conformance/fixtures/tools/resolve_entity/resolve_entity.json new file mode 100644 index 0000000..cf2b551 --- /dev/null +++ b/conformance/fixtures/tools/resolve_entity/resolve_entity.json @@ -0,0 +1,16 @@ +{ + "id": "resolve_entity.layer", + "schemaRef": "tools/resolve_entity.schema.json", + "validates": "inputs", + "inputs": { + "text": "the parcels layer for the county", + "entityKinds": ["Layer", "Dataset"], + "limit": 5, + "context": { + "areaOfInterest": "named:Travis County", + "spatialReferenceId": 4326 + } + }, + "expected": "tool_result", + "canonicalRefs": ["CapabilityCatalog", "DatasetRef", "LayerRef"] +} diff --git a/spec/conformance.md b/spec/conformance.md index a1ca590..125c94f 100644 --- a/spec/conformance.md +++ b/spec/conformance.md @@ -129,7 +129,7 @@ A fixture's `expected` value is exactly one of: |---|---|---| | `emit_plan` | Tool fixtures for the named plan-emitting MCP tool: `plan_analysis` (Analyze). Publish Data, Build App, and Automate / Deploy do not define a plan-emitting MCP tool in [taxonomy.md §MCP Tools to Workflow Family Mapping](taxonomy.md#mcp-tools-to-workflow-family-mapping); their plans surface to MCP through `validate_plan` or direct family tools (per [planning.md §2](planning.md#2-clarification-and-elicitation-semantics) and the per-family handoff model in [planning.md §5](planning.md#5-plan-handoff-semantics)) until upstream names additional plan-emitting MCP tools. A harness MUST NOT fixture an `emit_plan` shape under a tool name the taxonomy does not define; if upstream later names per-family plan-emitting tools, this row and the taxonomy tool matrix update together | Canonical `AnalysisPlan`, expected `outputs` declaration per [planning.md §5.4](planning.md#54-boundary-crossing-fields); no validation verdict is asserted | | `plan_validation` | Tool fixtures for `validate_plan` (Analyze, Publish Data, Build App per [taxonomy.md §MCP Tools to Workflow Family Mapping](taxonomy.md#mcp-tools-to-workflow-family-mapping)) | Canonical `PlanValidationResult` (upstream: [`DETERMINISTIC_OPERATOR_WORKFLOW_RESULTS.md` §Stage Model](https://github.com/honua-io/honua-server/blob/main/docs/developer/DETERMINISTIC_OPERATOR_WORKFLOW_RESULTS.md#stage-model) and [`AI_OPERATOR_CONTRACT.md` §ValidatePlan](https://github.com/honua-io/honua-server/blob/main/docs/developer/AI_OPERATOR_CONTRACT.md)) asserted over a supplied canonical plan; the fixture asserts structural validation, capability preview, authorization preview, and policy preview outcomes without emitting a new plan | -| `tool_result` | Tool fixtures that return a canonical resource object or assert a boundary-crossing handoff rather than a plan (`ground_candidates` → a candidate grounding result over `CapabilityCatalog` plus a draft family intent (`AnalysisIntent` / `PublishingIntent`), emitting an `emit_clarification` shape instead when grounding surfaces ambiguity; `get_style` → a read-only `StyleRef` projection (canonical per [resources.md §`honua://styles/{style_id}`](resources.md#honuastylesstyle_id)); `execute_plan` → `ExecutionJob` reference for Analyze; Publish Data `execute_plan` asserts submission into `PipelineService` with **no MCP-owned return object** — `PipelineService`-owned internal execution state (publication-state read, refresh-run identity) is not an MCP resource in this version of the standard and cannot be expressed through `resource_projection`; post-handoff promotion-surface state (`PublishedService`, `Deployment`) is expressed through a `resource_projection` fixture against the stable `honua://` URI and responsibility-level projection per §3.1/§3.2, and only truly non-constructible reserved routes (for example `PublishingResultPackage`) remain deferred-shape fixtures per §2.4; `create_app_package` → `AppPackage`; `preview_app_package` → preview `ArtifactRef` (canonical per [resources.md §`honua://apps/{app_package_id}`](resources.md#honuaappsapp_package_id) and the Reserved `honua://results/{id}` — Builder Result section in the same document); `create_map_package`, `refine_map_package`, `apply_style_preset`, `compose_mixed_protocol_map` → `MapPackage`; `preview_map_package` → preview `ArtifactRef`; `publish_result` → canonical promotion-surface target per family: `PublishedService` for Analyze and Publish Data and `Deployment` for Build App, bound at the stable `honua://services/{published_service_id}` / `honua://deployments/{deployment_id}` URI and responsibility-level projection defined in [resources.md §Promotion-Surface Resources](resources.md#promotion-surface-resources); only concrete field spellings still finalizing in `honua-server#730` / `honua-server#732` stay deferred per §2.4) | For canonical-return tools, the returned object by name and, where defined in [resources.md](resources.md), the `honua://` URI under which it is addressable. For `ground_candidates`, the bound `CapabilityCatalog`-derived candidate set and the draft family intent (`AnalysisIntent` / `PublishingIntent`); no plan or handoff is asserted, and an ambiguous grounding uses an `emit_clarification` fixture instead. For `get_style`, the resolved `StyleRef` bound at its `honua://styles/{style_id}` URI. For Publish Data `execute_plan`, the fixture binds the handoff boundary only: it MUST NOT invent a local job/result noun, and the enclosing scenario MUST NOT require a `resource_projection` fixture against a non-MCP `PipelineService`-owned surface. Any post-handoff state the scenario wants to assert against a promotion-surface resource (`PublishedService`, `Deployment`) is expressed as a `resource_projection` fixture bound to the stable `honua://` URI and responsibility-level projection from [resources.md](resources.md); only assertions that depend on the still-finalizing concrete field set follow §2.4. Truly non-constructible reserved routes (for example `PublishingResultPackage`, which has no stable shared identifier yet) remain name-only deferred shapes per §2.4 | +| `tool_result` | Tool fixtures that return a canonical resource object or assert a boundary-crossing handoff rather than a plan (`list_capabilities` → an MCP-owned capability-surface projection per [taxonomy.md §Grounding-as-Tools](taxonomy.md#grounding-as-tools), validated against [`tools/list_capabilities.output.schema.json`](schemas/tools/list_capabilities.output.schema.json) and asserting no plan or handoff; `resolve_entity` → ranked, evidence-backed canonical entity references over `CapabilityCatalog` / `honua://catalog/features` (validated against [`tools/resolve_entity.output.schema.json`](schemas/tools/resolve_entity.output.schema.json)), emitting an `emit_clarification` shape instead when the text is too ambiguous to ground; `ground_candidates` → a candidate grounding result over `CapabilityCatalog` plus a draft family intent (`AnalysisIntent` / `PublishingIntent`), emitting an `emit_clarification` shape instead when grounding surfaces ambiguity; `get_style` → a read-only `StyleRef` projection (canonical per [resources.md §`honua://styles/{style_id}`](resources.md#honuastylesstyle_id)); `execute_plan` → `ExecutionJob` reference for Analyze; Publish Data `execute_plan` asserts submission into `PipelineService` with **no MCP-owned return object** — `PipelineService`-owned internal execution state (publication-state read, refresh-run identity) is not an MCP resource in this version of the standard and cannot be expressed through `resource_projection`; post-handoff promotion-surface state (`PublishedService`, `Deployment`) is expressed through a `resource_projection` fixture against the stable `honua://` URI and responsibility-level projection per §3.1/§3.2, and only truly non-constructible reserved routes (for example `PublishingResultPackage`) remain deferred-shape fixtures per §2.4; `create_app_package` → `AppPackage`; `preview_app_package` → preview `ArtifactRef` (canonical per [resources.md §`honua://apps/{app_package_id}`](resources.md#honuaappsapp_package_id) and the Reserved `honua://results/{id}` — Builder Result section in the same document); `create_map_package`, `refine_map_package`, `apply_style_preset`, `compose_mixed_protocol_map` → `MapPackage`; `preview_map_package` → preview `ArtifactRef`; `publish_result` → canonical promotion-surface target per family: `PublishedService` for Analyze and Publish Data and `Deployment` for Build App, bound at the stable `honua://services/{published_service_id}` / `honua://deployments/{deployment_id}` URI and responsibility-level projection defined in [resources.md §Promotion-Surface Resources](resources.md#promotion-surface-resources); only concrete field spellings still finalizing in `honua-server#730` / `honua-server#732` stay deferred per §2.4) | For canonical-return tools, the returned object by name and, where defined in [resources.md](resources.md), the `honua://` URI under which it is addressable. For `ground_candidates`, the bound `CapabilityCatalog`-derived candidate set and the draft family intent (`AnalysisIntent` / `PublishingIntent`); no plan or handoff is asserted, and an ambiguous grounding uses an `emit_clarification` fixture instead. For `get_style`, the resolved `StyleRef` bound at its `honua://styles/{style_id}` URI. For Publish Data `execute_plan`, the fixture binds the handoff boundary only: it MUST NOT invent a local job/result noun, and the enclosing scenario MUST NOT require a `resource_projection` fixture against a non-MCP `PipelineService`-owned surface. Any post-handoff state the scenario wants to assert against a promotion-surface resource (`PublishedService`, `Deployment`) is expressed as a `resource_projection` fixture bound to the stable `honua://` URI and responsibility-level projection from [resources.md](resources.md); only assertions that depend on the still-finalizing concrete field set follow §2.4. Truly non-constructible reserved routes (for example `PublishingResultPackage`, which has no stable shared identifier yet) remain name-only deferred shapes per §2.4 | | `emit_clarification` | Tool fixtures for `clarify_intent` and any planning or validation tool when reason codes apply | `ClarificationRequest` with `reasonCodes[]` (subset of [planning.md §2.1](planning.md#21-trigger-conditions)) and per-question `kind` drawn from `ClarificationQuestionKind` | | `resource_projection` | Resource fixtures | For resource families defined in [resources.md §Resource URI Conventions](resources.md#resource-uri-conventions): a `honua://` URI matching that section and the per-family responsibilities enumerated in the same document. For planning-stage families referenced by [planning.md §3](planning.md#3-planning-stage-resources) that use the open-core data-access surface (per §2.1): the open-core `honua://services/{encodedServiceId}/layers/{layerId}` route per [MCP_SERVER.md](https://github.com/honua-io/honua-server/blob/main/docs/developer/MCP_SERVER.md) for dataset/layer reads, or a canonical-object-name binding (`CapabilityCatalog`, `ProcessDefinition`) for families that do not carry an MCP inspection URI in this version of the standard. A fixture MUST NOT mint a new `honua://` route for a family this spec does not define | | `metadata_cache_state` | Metadata-read fixtures that assert the cache-state projection on a metadata-oriented surface (catalog, dataset/layer, process, style, theme, template, or published-service metadata) | The `cache` projection defined in [resources.md §Metadata Cache State](resources.md#metadata-cache-state): `cache.state` drawn from the upstream five-state set (`hit`, `miss`, `stale`, `refreshed`, `bypass`) and the visibility fields available to the read (`keyFingerprint`, `age`, `ttl`, `revalidatedAt`, `validators`, `invalidationReason`, `refreshErrorId`). A failed revalidation pairs a `stale`-or-last-known `cache.state` carrying a `refreshErrorId` with a `geoprocessing_error` fixture whose canonical `GeoprocessingError` correlates to that id; the fixture MUST NOT mint an MCP-local cache state or cache error code. Feature/query/non-materialized result fixtures MUST NOT carry this projection (per [resources.md §Feature, Query, and Result Reads Are Not Default-Cached](resources.md#feature-query-and-result-reads-are-not-default-cached)) | diff --git a/spec/planning.md b/spec/planning.md index 19d8af8..bf7ab21 100644 --- a/spec/planning.md +++ b/spec/planning.md @@ -199,7 +199,10 @@ indefinitely. prompts render the questions to the client and carry answers back as `ClarificationResponse`. The transport does not own the reason codes, the question kinds, or the assumption policy; those are protocol semantics defined -here and reused from the upstream contract. +here and reused from the upstream contract. The concrete transport realization +of this mapping — MCP-native `elicitation/create` on revision `2025-06-18`, with +the `emit_clarification` tool-result fallback on older revisions — is specified +in [transport.md §Elicitation Transport](transport.md#6-elicitation-transport). A transport MAY batch multiple `ClarificationRequest`s before returning to the planner, but MUST preserve `questionId` identity so answers remain bindable. diff --git a/spec/schemas/README.md b/spec/schemas/README.md index c044672..ffb79bf 100644 --- a/spec/schemas/README.md +++ b/spec/schemas/README.md @@ -87,4 +87,18 @@ prefix-independent. | [`index.json`](index.json) | Machine-readable tool/resource → schema map | | [`common/geoprocessing-error.schema.json`](common/geoprocessing-error.schema.json) | The canonical `GeoprocessingError` envelope (shared) | | [`tools/*.schema.json`](tools/) | One `inputSchema` per core tool family | +| [`tools/*.output.schema.json`](tools/) | The MCP 2025-06-18 `outputSchema` for the tools whose return shape the standard owns (`list_capabilities`, `resolve_entity`) | | [`resources/*.schema.json`](resources/) | One payload schema per core resource family | + +## Output schemas + +MCP revision `2025-06-18` (see [transport.md](../transport.md)) adds tool +`outputSchema` / `structuredContent`. The standard publishes an `outputSchema` +only where it **owns** the return shape — currently the capability-surface +introspection tools `list_capabilities` and `resolve_entity`, whose outputs are +MCP-owned projections rather than upstream canonical objects. Every other tool's +return is asserted through the conformance expected-behavior shapes +([`spec/conformance.md` §2.3](../conformance.md#23-expected-behavior-shapes)), +which bind the canonical object by name; that pattern is unchanged. Where an +`outputSchema` exists, `index.json` records it on the tool entry under +`outputSchema`, and the tool's safety annotations under `annotations`. diff --git a/spec/schemas/index.json b/spec/schemas/index.json index 27920c5..5a871a8 100644 --- a/spec/schemas/index.json +++ b/spec/schemas/index.json @@ -1,8 +1,8 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "geospatial-mcp JSON Schema index", - "description": "Machine-readable map from standard MCP tool names and resource family URIs to their JSON Schema (draft 2020-12) files. 'standardName' is the bare taxonomy.md tool name; 'referenceToolName' is the name the reference implementation (Honua /mcp) advertises. 'implementationStatus' records whether the reference implementation ships the tool/resource family today (implemented) or whether it is a standard family not yet served by the reference (known-gap). For resources, 'implementationStatus' gates the FULL conformance level the same way it does for tools: a manifest must advertise every 'implemented' resource family to reach FULL; 'known-gap' families are reported as informational notes only.", - "date": "2026-06-21", + "description": "Machine-readable map from standard MCP tool names and resource family URIs to their JSON Schema (draft 2020-12) files. 'standardName' is the bare taxonomy.md tool name; 'referenceToolName' is the name the reference implementation (Honua /mcp) advertises. 'schema' points to the tool inputSchema; 'outputSchema' (optional) points to the tool's MCP 2025-06-18 outputSchema where the standard owns the return shape. 'annotations' (optional) records the tool's MCP safety hints (see taxonomy.md §Tool Safety Annotations). 'implementationStatus' records whether the reference implementation ships the tool/resource family today (implemented) or whether it is a standard family not yet served by the reference (known-gap). For resources, 'implementationStatus' gates the FULL conformance level the same way it does for tools: a manifest must advertise every 'implemented' resource family to reach FULL; 'known-gap' families are reported as informational notes only.", + "date": "2026-06-29", "dialect": "https://json-schema.org/draft/2020-12/schema", "tools": [ { @@ -34,6 +34,26 @@ "implementationStatus": "implemented", "notes": "Reference also advertises honua_dry_run_plan with the same partial-plan input schema." }, + { + "standardName": "list_capabilities", + "referenceToolName": null, + "family": "Capability and grounding", + "schema": "tools/list_capabilities.schema.json", + "outputSchema": "tools/list_capabilities.output.schema.json", + "annotations": { "readOnlyHint": true, "destructiveHint": false, "idempotentHint": true, "openWorldHint": false }, + "implementationStatus": "known-gap", + "notes": "Client-LLM self-description tool: enumerates the composable tool/resource surface plus grounding-as-tools and grounding resources. Tracked for the reference in honua-io/honua-server#1949." + }, + { + "standardName": "resolve_entity", + "referenceToolName": null, + "family": "Capability and grounding", + "schema": "tools/resolve_entity.schema.json", + "outputSchema": "tools/resolve_entity.output.schema.json", + "annotations": { "readOnlyHint": true, "destructiveHint": false, "idempotentHint": true, "openWorldHint": true }, + "implementationStatus": "known-gap", + "notes": "Grounds natural-language text to ranked, evidence-backed entity references over honua://catalog/features. Tracked for the reference in honua-io/honua-server#1949." + }, { "standardName": "execute_plan", "referenceToolName": "honua_execute_plan", diff --git a/spec/schemas/tools/list_capabilities.output.schema.json b/spec/schemas/tools/list_capabilities.output.schema.json new file mode 100644 index 0000000..159ff7a --- /dev/null +++ b/spec/schemas/tools/list_capabilities.output.schema.json @@ -0,0 +1,168 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://geospatial-mcp.honua.io/spec/schemas/tools/list_capabilities.output.schema.json", + "title": "list_capabilities outputSchema", + "description": "Structured-content shape returned by the list_capabilities tool (MCP 2025-06-18 outputSchema / structuredContent). This is an MCP-owned introspection projection of the server's composable surface (analogous to the conformance manifest), not an upstream canonical object. It lets a cold client LLM discover every tool, resource family, and grounding affordance it can compose, with the safety annotations needed to call them correctly. See taxonomy.md §Capability and grounding, §Grounding-as-Tools, and §Tool Safety Annotations.", + "type": "object", + "required": ["tools"], + "additionalProperties": false, + "properties": { + "protocolVersion": { + "type": "string", + "description": "The negotiated MCP protocol revision the surface speaks (e.g. '2025-06-18'). See transport.md §Supported Protocol Revision." + }, + "specVersion": { + "type": "string", + "description": "The geospatial-mcp SPEC_VERSION (taxonomy.md §Standard Version) this surface targets, e.g. '1.0'." + }, + "tools": { + "type": "array", + "description": "The composable tool surface. Each entry is a capability descriptor sufficient for a model with no Honua-specific knowledge to call the tool.", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["standardName", "name"], + "properties": { + "standardName": { + "type": "string", + "description": "The bare taxonomy.md tool name (e.g. 'resolve_entity'). Maps to a tools[].standardName in spec/schemas/index.json." + }, + "name": { + "type": "string", + "description": "The name the server advertises on tools/list (MAY be vendor-prefixed, e.g. 'honua_resolve_entity')." + }, + "family": { + "type": "string", + "description": "Tool family from taxonomy.md §Tools." + }, + "workflowFamilies": { + "type": "array", + "description": "Workflow families this tool participates in.", + "items": { + "type": "string", + "enum": ["Analyze", "PublishData", "BuildApp", "AutomateDeploy"] + } + }, + "description": { + "type": "string", + "description": "LLM-grade description of what the tool does and when to call it." + }, + "inputSchemaRef": { + "type": "string", + "description": "Pointer to the tool's inputSchema (an index.json schema path or a resolvable $id)." + }, + "outputSchemaRef": { + "type": "string", + "description": "Pointer to the tool's outputSchema, when the tool declares one (MCP 2025-06-18)." + }, + "annotations": { + "$ref": "#/$defs/toolAnnotations" + }, + "example": { + "type": "object", + "additionalProperties": true, + "description": "Optional worked example (representative arguments) to anchor correct tool-calling." + } + } + } + }, + "resources": { + "type": "array", + "description": "Advertised resource families the client can read for context. Present when includeResources is not false.", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["family", "uriForm"], + "properties": { + "family": { + "type": "string", + "description": "Resource family name (resources.md / index.json)." + }, + "uriForm": { + "type": "string", + "description": "The honua:// URI template for the family.", + "pattern": "^honua://" + }, + "description": { + "type": "string", + "description": "What the resource family exposes." + } + } + } + }, + "grounding": { + "type": "object", + "additionalProperties": false, + "description": "The grounding affordances a client LLM uses to ground entities, disambiguate, and self-check. Present when includeGrounding is not false.", + "properties": { + "tools": { + "type": "array", + "description": "Standard names of the grounding-as-tools set (e.g. list_capabilities, resolve_entity, ground_candidates, clarify_intent, validate_plan).", + "items": { "type": "string", "minLength": 1 } + }, + "resources": { + "type": "array", + "description": "Grounding resources the model can read as evidence (e.g. honua://catalog/features). These may be catalog-family resources whose per-family inspection contract is deferred to the open-core/upstream surface in this version of the standard.", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["uriForm"], + "properties": { + "family": { "type": "string" }, + "uriForm": { + "type": "string", + "description": "honua:// URI of the grounding resource.", + "pattern": "^honua://" + }, + "description": { "type": "string" } + } + } + } + } + }, + "prompts": { + "type": "array", + "description": "Optional reusable workflow entry points the server advertises (taxonomy.md §Prompts).", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { "type": "string", "minLength": 1 }, + "family": { "type": "string" }, + "description": { "type": "string" } + } + } + }, + "nextCursor": { + "type": "string", + "description": "Opaque cursor for the next page when the surface is paginated, per the collection-read pagination contract in resources.md." + } + }, + "$defs": { + "toolAnnotations": { + "type": "object", + "additionalProperties": false, + "description": "MCP tool safety annotations (advisory hints), aligned with the MCP base Tool.annotations. See taxonomy.md §Tool Safety Annotations.", + "properties": { + "title": { "type": "string", "description": "Human-readable tool title." }, + "readOnlyHint": { + "type": "boolean", + "description": "True if the tool does not mutate server state. Every read-only inspection/grounding tool in this standard sets true." + }, + "destructiveHint": { + "type": "boolean", + "description": "True if the tool may perform destructive updates. Read-only tools set false." + }, + "idempotentHint": { + "type": "boolean", + "description": "True if repeated calls with the same arguments have no additional effect." + }, + "openWorldHint": { + "type": "boolean", + "description": "True if the tool interacts with an open, evolving world (e.g. a live catalog) rather than a closed set." + } + } + } + } +} diff --git a/spec/schemas/tools/list_capabilities.schema.json b/spec/schemas/tools/list_capabilities.schema.json new file mode 100644 index 0000000..dceed20 --- /dev/null +++ b/spec/schemas/tools/list_capabilities.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://geospatial-mcp.honua.io/spec/schemas/tools/list_capabilities.schema.json", + "title": "list_capabilities inputSchema", + "description": "Argument shape for the list_capabilities tool (Capability and grounding family). Enumerates the composable capability surface a client LLM can plan over: the standard tools/operations the server advertises (with descriptions and safety annotations), the resource families it exposes, and the grounding tools and grounding resources (e.g. honua://catalog/features) the model uses to ground entities, disambiguate, and self-check. Read-only self-description; it does not plan, execute, or mutate. All arguments are optional filters. See taxonomy.md §Capability and grounding and §Grounding-as-Tools; output shape in list_capabilities.output.schema.json.", + "type": "object", + "additionalProperties": false, + "properties": { + "workflowFamilies": { + "type": "array", + "description": "Optional filter restricting returned capabilities to those usable in the listed workflow families.", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "enum": ["Analyze", "PublishData", "BuildApp", "AutomateDeploy"] + } + }, + "families": { + "type": "array", + "description": "Optional filter restricting returned tools to the listed tool families (taxonomy.md §Tools), e.g. 'Intent and planning', 'Capability and grounding'.", + "minItems": 1, + "uniqueItems": true, + "items": { "type": "string", "minLength": 1 } + }, + "includeResources": { + "type": "boolean", + "description": "Include the advertised resource families in the result. Defaults to true." + }, + "includeGrounding": { + "type": "boolean", + "description": "Include the grounding-as-tools set and grounding resources in the result. Defaults to true." + }, + "includeAnnotations": { + "type": "boolean", + "description": "Include per-tool safety annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint). Defaults to true." + }, + "cursor": { + "type": "string", + "minLength": 1, + "description": "Opaque pagination cursor from a prior call's nextCursor, following the collection-read pagination contract in resources.md." + } + } +} diff --git a/spec/schemas/tools/resolve_entity.output.schema.json b/spec/schemas/tools/resolve_entity.output.schema.json new file mode 100644 index 0000000..a826792 --- /dev/null +++ b/spec/schemas/tools/resolve_entity.output.schema.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://geospatial-mcp.honua.io/spec/schemas/tools/resolve_entity.output.schema.json", + "title": "resolve_entity outputSchema", + "description": "Structured-content shape returned by the resolve_entity tool (MCP 2025-06-18 outputSchema / structuredContent). Carries ranked, evidence-backed candidate references to canonical geospatial entities, or a clarification pointer when the text is too ambiguous to ground. Each candidate references a canonical object by its upstream id and, where the entity is addressable as an MCP resource, its stable honua:// URI; this schema does not redefine those canonical object field shapes. See taxonomy.md §Capability and grounding and resources.md §Resource URI Conventions.", + "type": "object", + "required": ["text", "candidates"], + "additionalProperties": false, + "properties": { + "text": { + "type": "string", + "description": "The resolution text echoed back from the request." + }, + "groundingResource": { + "type": "string", + "description": "The grounding resource the resolution was evidenced against (typically honua://catalog/features). Candidates not present in a grounding resource MUST NOT be returned.", + "pattern": "^honua://" + }, + "candidates": { + "type": "array", + "description": "Ranked candidate entity references, most-confident first. MAY be empty when nothing grounds.", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["kind", "ref", "score"], + "properties": { + "kind": { + "type": "string", + "description": "Resolved entity kind.", + "enum": ["Dataset", "Layer", "Feature", "Service", "Style", "Theme", "Template", "Process"] + }, + "ref": { + "type": "string", + "minLength": 1, + "description": "Canonical reference identifier for the entity (e.g. a DatasetRef/LayerRef id, feature id, ProcessDefinition id, or StyleRef style_id). Identifier spellings are upstream-owned; this field carries the id, not a redefined shape." + }, + "uri": { + "type": "string", + "description": "Stable honua:// URI under which the entity is addressable as an MCP resource, when one exists for its family per resources.md. Omitted for families with no MCP inspection URI in this version of the standard.", + "pattern": "^honua://" + }, + "title": { + "type": "string", + "description": "Human-readable title for disambiguation in the client UI/LLM." + }, + "description": { + "type": "string", + "description": "Optional short description carried from the catalog entry." + }, + "score": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Server-computed grounding confidence in [0,1]. Deterministic for identical catalog state." + }, + "evidence": { + "type": "object", + "additionalProperties": true, + "description": "Why this candidate matched: the grounding signals (e.g. matched name/alias/field, catalog entry id) that ground the candidate. Shape is advisory and server-extensible; it MUST NOT assert a capability absent from the grounding resource." + } + } + } + }, + "clarification": { + "type": "object", + "additionalProperties": true, + "description": "Present instead of (or alongside) a thin candidate set when the text is too ambiguous to ground safely. Carries a canonical ClarificationRequest (planning.md §2); the standard does not redefine its shape here. When present, the tool result SHOULD be surfaced as the emit_clarification expected-behavior shape (conformance.md §2.3).", + "properties": { + "intentId": { + "type": "string", + "description": "Intent identifier the clarification is bound to, for answer rebinding via clarify_intent." + } + } + } + } +} diff --git a/spec/schemas/tools/resolve_entity.schema.json b/spec/schemas/tools/resolve_entity.schema.json new file mode 100644 index 0000000..a35ad92 --- /dev/null +++ b/spec/schemas/tools/resolve_entity.schema.json @@ -0,0 +1,64 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://geospatial-mcp.honua.io/spec/schemas/tools/resolve_entity.schema.json", + "title": "resolve_entity inputSchema", + "description": "Argument shape for the resolve_entity tool (Capability and grounding family). Resolves a natural-language or partial-text reference into ranked, evidence-backed geospatial entity references (dataset, layer, feature, service, style, theme, template, or process) over the server CapabilityCatalog and feature catalog grounding resource (honua://catalog/features). Read-only grounding primitive: it does not plan, execute, or mutate. A cold client LLM uses it to bind freeform names to canonical references before composing a plan. See taxonomy.md §Capability and grounding and §Grounding-as-Tools; output shape in resolve_entity.output.schema.json.", + "type": "object", + "required": ["text"], + "additionalProperties": false, + "properties": { + "text": { + "type": "string", + "minLength": 1, + "description": "Freeform natural-language or partial text naming the entity to resolve (e.g. 'flood zones', 'the parcels layer', 'travel-time service area process')." + }, + "entityKinds": { + "type": "array", + "description": "Optional filter restricting the kinds of entity to resolve to. When omitted, the server resolves across every kind it can ground.", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "enum": ["Dataset", "Layer", "Feature", "Service", "Style", "Theme", "Template", "Process"] + } + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 50, + "description": "Maximum number of ranked candidates to return (server caps to its own ceiling). Defaults to a small server-chosen value." + }, + "intentId": { + "type": "string", + "minLength": 1, + "description": "Optional intent identifier correlating this resolution with an in-flight grounding/planning session (see ground_candidates)." + }, + "context": { + "type": "object", + "additionalProperties": false, + "description": "Optional scoping context that narrows resolution.", + "properties": { + "workspaceId": { + "type": "string", + "description": "Workspace whose visible catalog scopes resolution." + }, + "datasetId": { + "type": "string", + "description": "Dataset to scope Layer/Feature resolution within." + }, + "layerId": { + "type": "string", + "description": "Layer to scope Feature resolution within (required for stable Feature resolution)." + }, + "areaOfInterest": { + "type": "string", + "description": "AOI as WKT, bounding-box string, or named area, used to bias Feature/Service candidates." + }, + "spatialReferenceId": { + "type": "integer", + "description": "EPSG SRID for the AOI (defaults to 4326)." + } + } + } + } +} diff --git a/spec/taxonomy.md b/spec/taxonomy.md index 39c604c..f510660 100644 --- a/spec/taxonomy.md +++ b/spec/taxonomy.md @@ -145,6 +145,10 @@ protocol-specific level. Geospatial tool families: +- **Capability and grounding** -- `list_capabilities`, `resolve_entity` + (client-LLM self-description and natural-language entity grounding; the + composable-surface entry points a cold client LLM calls first; see + [§Grounding-as-Tools](#grounding-as-tools)) - **Intent and planning** -- `plan_analysis`, `ground_candidates`, `clarify_intent`, `validate_plan` - **Execution** -- `execute_plan` @@ -160,6 +164,67 @@ Geospatial tool families: - **App composition** -- `create_app_package`, `preview_app_package` - **Publishing** -- `publish_result` +### Grounding-as-Tools + +The standard blesses a **grounding-as-tools** pattern: a cold client LLM (one +with no Honua-specific system prompt) must be able to compose a correct +geospatial workflow using only the advertised tool surface. To make that +possible, the server exposes — as ordinary read-only MCP tools — the affordances +the model needs to *discover*, *ground*, *disambiguate*, and *self-check*, +rather than assuming the model already knows the catalog. + +The grounding-as-tools set is: + +| Tool | Role for the client LLM | +|---|---| +| `list_capabilities` | Self-describe the composable surface: which tools/operations exist, which resource families are readable, and which grounding tools and grounding resources are available — with per-tool descriptions and [safety annotations](#tool-safety-annotations) — so the model can plan over the real surface | +| `resolve_entity` | Ground freeform natural-language text to ranked, evidence-backed canonical entity references (dataset, layer, feature, service, style, theme, template, process) before they are used as plan inputs | +| `ground_candidates` | Ground a whole goal to a workflow family, ranked candidates, and a draft intent ([planning.md §2](planning.md#2-clarification-and-elicitation-semantics)) | +| `clarify_intent` | Resolve ambiguity through typed clarification questions ([planning.md §2](planning.md#2-clarification-and-elicitation-semantics)) | +| `validate_plan` | Self-check a composed (possibly partial) plan before execution ([planning.md §5](planning.md#5-plan-handoff-semantics)) | + +Two invariants make grounding-as-tools trustworthy: + +1. **Evidence-backed grounding.** `list_capabilities` and `resolve_entity` + ground against the server `CapabilityCatalog` and the **feature catalog + grounding resource** `honua://catalog/features` (a catalog-family grounding + resource; its per-family inspection contract remains deferred to the + open-core/upstream surface in this version of the standard, per + [resources.md](resources.md)). A capability or entity that has no evidence in + a grounding resource MUST NOT be returned. This keeps a client LLM from + composing hallucinated capabilities. +2. **Correctness is server-owned, not model-owned.** Grounding and validation + are deterministic over the catalog and independent of the model. Invalid + compositions return a structured, actionable + [`GeoprocessingError`](resources.md#error-model) (or a `ClarificationRequest`) + the model can recover from — never a silent bad result. + +`list_capabilities` and `resolve_entity` are read-only and do not advance a +workflow; they describe and ground it. Grounding resources are MCP **resources** +(read-only context) surfaced *as* tool-accessible grounding so a client whose +host does not auto-attach resources can still reach them. + +### Tool Safety Annotations + +Each advertised MCP tool SHOULD carry the MCP base `Tool.annotations` safety +hints so a client LLM and its host can reason about a tool before calling it. +These hints are **advisory** (a security-conscious host MUST NOT treat them as a +trust boundary); they describe intent: + +| Annotation | Meaning | +|---|---| +| `readOnlyHint` | The tool does not mutate server state. Every inspection and grounding tool in this standard sets `true`; per the [boundary rules](#boundary-rules) MCP tools never mutate server state directly, so a tool that would imply mutation is out of scope | +| `destructiveHint` | The tool may perform destructive updates. Read-only tools set `false` | +| `idempotentHint` | Repeated calls with the same arguments have no additional effect beyond the first | +| `openWorldHint` | The tool interacts with an open, evolving world (e.g. a live catalog) rather than a closed, fixed set | + +`list_capabilities` exposes each tool's annotations so the model can discover +them at runtime. The machine-readable values for standard tools that the +standard owns are recorded in the `annotations` field of +[`spec/schemas/index.json`](schemas/index.json) (for example `resolve_entity` +is `readOnlyHint: true`, `openWorldHint: true`; `list_capabilities` is +`readOnlyHint: true`, `openWorldHint: false`). + ### Prompts Prompts are reusable workflow entry points that encode domain-specific patterns. @@ -189,6 +254,17 @@ Elicitation triggers: See `spec/planning.md` §2 for the full clarification protocol: reason codes, question kinds, assumption policies, and answer binding semantics. +### Transport, Sessions, and Streaming + +The four primitives above are carried over an MCP transport. The session, +streaming, and protocol-revision contract — the supported protocol revision +(`2025-06-18`), the `Mcp-Session-Id` session model, streamable-HTTP/SSE and +stdio transports, `notifications/progress` for long-running jobs, and the +`notifications/*/list_changed` capability notifications — is owned by +[MCP Session and Streaming Transport](transport.md). That document is the +authoritative transport contract; this section only points to it so the +primitive vocabulary and the transport contract stay single-sourced. + ## Workflow Families The geospatial MCP standard covers four operator workflow families. A fifth @@ -251,6 +327,19 @@ surfaces. Canonical definitions are spread across three upstream documents: This section lists the objects for reference; the upstream documents are authoritative. +Two capability-surface tool outputs are **MCP-owned introspection +projections** rather than upstream canonical objects: `list_capabilities` +returns a self-description of the composable surface, and `resolve_entity` +returns ranked references to the canonical entities below. Their shapes are +defined by their JSON Schemas +([`tools/list_capabilities.output.schema.json`](schemas/tools/list_capabilities.output.schema.json), +[`tools/resolve_entity.output.schema.json`](schemas/tools/resolve_entity.output.schema.json)), +in the same spirit as the MCP-owned +[`ConformanceManifest`](schemas/conformance/manifest.schema.json); they +reference the canonical objects (e.g. `DatasetRef`, `LayerRef`, +`ProcessDefinition`, `StyleRef`) by id and `honua://` URI and do not redefine +their field shapes. + ### Discovery and Context | Object | Role | @@ -333,6 +422,7 @@ not read `v1` in this matrix as "available in the reference". | Capability | Analyze | Publish Data | Build App | Automate / Deploy | |---|---|---|---|---| +| Capability discovery and grounding | v1 | v1 | v1 | deferred | | Intent capture | v1 | v1 | v1 | deferred | | Clarification / elicitation | v1 | v1 | v1 | deferred | | Plan validation | v1 | v1 | v1 | deferred | @@ -349,7 +439,7 @@ not read `v1` in this matrix as "available in the reference". | Primitive | v1 Coverage | |---|---| | Resources | Catalog, dataset, process definition, style, theme, map template, app template, result package, map, app, published service, deployment, workspace ([per-family contracts](resources.md) for result through workspace families) | -| Tools | Intent/planning, execution, map composition, app composition, publishing | +| Tools | Capability and grounding, intent/planning, execution, map composition, app composition, publishing | | Prompts | Analysis workflows (site selection, hazard assessment, service coverage), review workflows, builder workflows | | Elicitation | Clarification reason codes defined in `spec/planning.md` §2.1 (seven codes across all workflow families) | @@ -357,6 +447,8 @@ not read `v1` in this matrix as "available in the reference". | Tool | Analyze | Publish Data | Build App | |---|---|---|---| +| `list_capabilities` | v1 | v1 | v1 | +| `resolve_entity` | v1 | v1 | v1 | | `plan_analysis` | v1 | -- | -- | | `ground_candidates` | v1 | v1 | -- | | `clarify_intent` | v1 | v1 | v1 | diff --git a/spec/transport.md b/spec/transport.md new file mode 100644 index 0000000..9054f87 --- /dev/null +++ b/spec/transport.md @@ -0,0 +1,293 @@ +# MCP Session and Streaming Transport + +**Status:** Draft +**Date:** 2026-06-29 +**Scope:** Session, transport, protocol-revision, and streaming contract for the +geospatial MCP standard + +This document defines the **transport contract** the geospatial MCP standard +expects: the supported MCP protocol revision, the session model, the streamable +transports, and the streaming notifications that let long-running geospatial +jobs push progress instead of forcing the client to poll. It extends +[Taxonomy, Capability Matrix, and Non-Goals](taxonomy.md) and does not redefine +vocabulary, the capability matrix, resource URIs, or the planning/handoff +contract; it specifies *how* the four MCP primitives +([taxonomy.md §MCP Primitives](taxonomy.md#mcp-primitives)) are carried over the +wire so server and client implementers share one authoritative contract. + +Upstream references (authoritative for object field shapes): + +- [AI Operator Contract](https://github.com/honua-io/honua-server/blob/main/docs/developer/AI_OPERATOR_CONTRACT.md) +- [AI-First Operator Architecture](https://github.com/honua-io/honua-server/blob/main/docs/contributor/AI_OPERATOR_ARCHITECTURE.md) + +Protocol references (authoritative for transport framing and message shapes): + +- [Model Context Protocol specification, revision 2025-06-18](https://modelcontextprotocol.io/specification/2025-06-18) +- [MCP Basic Transports](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports) + +This repository owns the geospatial transport contract (which revision, which +transports, which notifications a conformant geospatial MCP surface MUST honor). +The base MCP protocol semantics — JSON-RPC framing, the `initialize` handshake, +and message shapes — are owned by the MCP specification and referenced here, not +restated. + +## 1. Scope and Relationship to Existing Documents + +The other spec documents fix *what* the surface offers; this document fixes +*how a client connects to and streams from it*: + +- `spec/taxonomy.md` fixes the primitives, the capability matrix, and the tool + vocabulary (including the [grounding-as-tools](taxonomy.md#grounding-as-tools) + set and [tool safety annotations](taxonomy.md#tool-safety-annotations)). This + document carries those primitives; it does not add to the vocabulary. +- `spec/planning.md` fixes the clarification, elicitation, and handoff + semantics. This document maps the clarification envelope onto MCP-native + elicitation (§6) and the long-running-job status model onto progress + notifications (§5.1); it does not redefine reason codes, question kinds, or + the post-handoff ownership model. +- `spec/resources.md` fixes the `honua://` URI grammar and the metadata cache / + freshness-token model. This document defers polling-cadence and freshness + semantics to that document and adds the push (notification) complement. + +A conformant geospatial MCP surface MUST satisfy this contract on every +transport it exposes. The contract is transport-symmetric: the same tool and +resource catalog is presented over every supported transport (§3). + +## 2. Supported Protocol Revision + +- A conformant server MUST support MCP protocol revision **`2025-06-18`** and + MUST negotiate it through the standard `initialize` handshake: the client + sends its `protocolVersion`; the server responds with the revision it will + use for the connection. +- Revision `2025-06-18` is REQUIRED because the geospatial standard depends on + capabilities it introduces or stabilizes — native **elicitation** (§6), + tool **`outputSchema`** / `structuredContent` (used by `resolve_entity` and + `list_capabilities`; see + [taxonomy.md §Grounding-as-Tools](taxonomy.md#grounding-as-tools)), and tool + **annotations** + ([taxonomy.md §Tool Safety Annotations](taxonomy.md#tool-safety-annotations)). +- A server MAY additionally accept an older revision (for example `2025-03-26`) + for backward compatibility. When it negotiates an older revision it MUST + degrade gracefully: features that require `2025-06-18` (native elicitation, + output schemas) are unavailable, and the server MUST fall back to the + equivalent in-band shapes — the clarification envelope is carried as a tool + result rather than an `elicitation/create` request, and structured tool + output is carried as content rather than `structuredContent`. A server MUST + NOT silently behave as if a feature is present on a revision that does not + support it. +- On HTTP transports, after initialization the client MUST send the negotiated + revision on each request via the `MCP-Protocol-Version` header, per the base + protocol. +- The negotiated revision is observable: `list_capabilities` echoes it in its + `protocolVersion` output field + ([taxonomy.md §Grounding-as-Tools](taxonomy.md#grounding-as-tools)). + +## 3. Transports + +The geospatial standard recognizes two transports. A server MAY expose either +or both, but the tool/resource catalog it advertises MUST be identical across +them (transport symmetry): a client MUST NOT see a different surface depending +on how it connected. + +### 3.1 Streamable HTTP and SSE + +- The primary networked transport is **streamable HTTP** with Server-Sent + Events (SSE), per the base MCP transport specification: a single HTTP + endpoint accepts JSON-RPC `POST` requests and MAY return either a single JSON + response or an SSE stream, and supports an SSE `GET` channel for + server-initiated messages (notifications and elicitation). +- Streaming is REQUIRED for long-running tools (geoprocessing, publishing): the + server streams `notifications/progress` (§5.1) on the response stream rather + than blocking until completion. +- The server MUST be able to deliver server-initiated messages + (`notifications/*`, `elicitation/create`) to a connected client over this + transport. + +### 3.2 stdio + +- A server MAY expose the same surface over the **stdio** transport (newline- + delimited JSON-RPC over stdin/stdout), for local hosts and SDK-embedded use. +- The stdio surface MUST present the same catalog as the HTTP surface + (transport symmetry). Notifications and elicitation are delivered as + JSON-RPC messages on the stdout stream. + +## 4. Sessions (`Mcp-Session-Id`) + +Sessions let a server correlate a sequence of requests (and the server-initiated +messages it pushes back) with one logical client connection. + +- On the HTTP transport, a server that maintains session state MUST assign a + session identifier and return it in the **`Mcp-Session-Id`** response header + on the `initialize` response. +- Once assigned, the client MUST include `Mcp-Session-Id` on every subsequent + request (including the SSE `GET` channel) for the life of the session. +- If a server receives a request that requires a session and the + `Mcp-Session-Id` is missing or no longer valid, it MUST reject the request — + HTTP `400` for a missing required id, HTTP `404` for an expired/unknown + session. On `404` the client MUST treat the session as gone and re-run + `initialize` to obtain a new session. +- A client MAY end a session explicitly by issuing an HTTP `DELETE` with the + `Mcp-Session-Id`; the server SHOULD release session-scoped state. +- The session identifier is opaque, MUST be unguessable, and MUST NOT encode + privileged data. It is a correlation handle, not an authorization token; + authorization is out of scope (§8). +- A server MAY operate **statelessly** (no `Mcp-Session-Id`) when it keeps no + per-connection state. A stateless server still satisfies §5 by streaming + progress on the originating request's response stream, but it cannot deliver + unsolicited server-initiated notifications between requests; clients fall back + to the conditional-read polling contract in + [planning.md §5.3](planning.md#53-post-handoff-execution--orchestration-plane). + +## 5. Streaming and Notifications + +### 5.1 Progress (`notifications/progress`) + +Long-running geospatial jobs (geoprocessing via `execute_plan`, publishing, +large renders) MUST stream progress rather than require fixed-interval polling. + +- A client opts in by attaching a `progressToken` to a request's `_meta` per + the base protocol. The server then emits `notifications/progress` referencing + that token, carrying `progress`, optional `total`, and an optional human- + readable `message`. +- Progress notifications are advisory UI/telemetry signals; they are **not** a + parallel job-state model. The authoritative post-handoff state model is + unchanged: Analyze jobs remain `ExecutionJob` owned by `ProcessService`, and + Publish Data execution state remains `PipelineService`-owned, per + [planning.md §5.3](planning.md#53-post-handoff-execution--orchestration-plane). + A progress `message` MAY mirror an `ExecutionJob.status` transition but MUST + NOT redefine the status vocabulary. +- Progress streaming is the standard replacement for tight polling of a job + resource. Polling remains a valid fallback (e.g. for stateless servers or + reconnecting clients) under the conditional-read + capped-backoff contract in + [planning.md §5.3](planning.md#53-post-handoff-execution--orchestration-plane) + and the freshness-token model in + [resources.md §Metadata Cache State](resources.md#metadata-cache-state); a + client MUST NOT present stale progress as current. +- Errors that end a streamed job use the canonical + [`GeoprocessingError`](resources.md#error-model) envelope; the transport does + not define a local error vocabulary. + +### 5.2 List-Changed Notifications + +When the advertised surface changes during a session, the server MUST notify +subscribed clients so a client LLM re-reads the surface instead of planning +against a stale catalog. The capability flags for these notifications MUST be +honest: a server advertises a `listChanged` capability only if it actually +emits the corresponding notification. + +| Notification | Fires when | +|---|---| +| `notifications/tools/list_changed` | The tool catalog changes — for example a governed workflow is registered or retired as a first-class tool, or a tool becomes available after authentication or tier change | +| `notifications/resources/list_changed` | The resource-family/template catalog changes | +| `notifications/prompts/list_changed` | The prompt catalog changes | + +A client that receives a `list_changed` notification SHOULD re-list the affected +primitive (and MAY re-call [`list_capabilities`](taxonomy.md#grounding-as-tools) +to refresh its composable view). + +### 5.3 Resource Subscriptions (Optional) + +A server MAY support per-resource subscriptions (`resources/subscribe` / +`notifications/resources/updated`) for read-only `honua://` resources. When +offered it MUST advertise the `resources.subscribe` capability. Subscriptions +are read-only change signals over the projections defined in +[resources.md](resources.md); they MUST NOT expose mutation paths or server +internals. + +## 6. Elicitation Transport + +Clarification is a planning-plane concept (`ClarificationRequest` / +`ClarificationResponse`, [planning.md §2](planning.md#2-clarification-and-elicitation-semantics)); +this section fixes only how it is carried. + +- On `2025-06-18`, a server SHOULD carry clarification over MCP-native + **elicitation**: it issues an `elicitation/create` request to the client and + receives the client's structured response. This is the transport realization + of the mapping in + [planning.md §2.6](planning.md#26-mcp-elicitation-mapping). +- The mapping is shape-only. The transport MUST NOT own reason codes, question + kinds, or assumption policy (those remain planning-plane semantics), and it + MUST preserve `questionId` identity end-to-end so answers rebind correctly, + per [planning.md §2.6](planning.md#26-mcp-elicitation-mapping). Only the typed + question/answer fields cross; conversational phrasing does not. +- A `ClarificationRequest` whose questions do not fit MCP-native elicitation's + flat structured-field model (for example multi-question batches that exceed + what the elicitation primitive expresses) is carried as a tool result using + the `emit_clarification` expected-behavior shape + ([conformance.md §2.3](conformance.md#23-expected-behavior-shapes)) instead; + the choice of carrier MUST NOT change the typed payload. +- On a negotiated older revision without native elicitation, the server MUST + carry every clarification as the `emit_clarification` tool-result shape (§2). + +## 7. Capability Negotiation + +At `initialize`, server and client advertise capabilities; the geospatial +contract pins the meaning of the ones it depends on: + +- A server that streams progress, fires `list_changed`, supports resource + subscriptions, or elicits MUST advertise the matching capability + (`tools.listChanged`, `resources.listChanged`, `resources.subscribe`, + `prompts.listChanged`, and the client-side `elicitation` capability the + server relies on). A flag advertised MUST be backed by actual behavior — no + inert flags. +- A client SHOULD advertise the `elicitation` capability when it can render + clarification; a server that needs clarification from a client lacking it + MUST fall back to the `emit_clarification` tool-result shape (§6). +- `list_capabilities` ([taxonomy.md §Grounding-as-Tools](taxonomy.md#grounding-as-tools)) + is the application-level, model-facing view of the surface; the `initialize` + capabilities object is the protocol-level view. The two MUST be consistent. + +## 8. Non-Goals + +- **gRPC execution transport.** This document covers the MCP interaction-plane + transport only. Typed deterministic execution transport remains in + `geospatial-grpc` and the post-handoff execution plane + ([planning.md §5.3](planning.md#53-post-handoff-execution--orchestration-plane)); + this document does not redefine it. +- **Authentication and authorization.** Auth (including HTTP/OAuth flows) is + governed by the base MCP specification and the deployment; the + `Mcp-Session-Id` is a correlation handle, not an authorization token (§4). +- **Message framing and JSON-RPC semantics.** Owned by the base MCP + specification; referenced, not restated. +- **Server internals.** Worker routing, queue management, and storage backends + remain private ([taxonomy.md §MCP Role in the Architecture](taxonomy.md#mcp-role-in-the-architecture)); + the transport contract never exposes them. +- **Parallel taxonomy, URI scheme, or error envelope.** This document reuses + the `honua://` scheme and the canonical + [`GeoprocessingError`](resources.md#error-model); it mints none of its own. + +## 9. Observable Signals + +Implementations should emit signals that allow transport-conformance tracking: + +- **Revision coverage** — negotiated protocol revision per connection, and the + rate of fallback to an older revision. +- **Session health** — session establishment, expiry (`404`) and re-initialize + rate, and stateless-vs-stateful operation. +- **Streaming coverage** — fraction of long-running tool calls that stream + `notifications/progress` versus complete synchronously, and progress-to- + terminal-state correlation. +- **Notification fidelity** — `list_changed` emissions versus advertised + `listChanged` capability flags (to detect inert flags). +- **Elicitation carrier** — share of clarifications carried as native + `elicitation/create` versus the `emit_clarification` tool-result fallback. + +These extend the taxonomy-, planning-, corpus-, and conformance-plane signals +defined in the sibling documents +([taxonomy.md §Observable Signals](taxonomy.md#observable-signals), +[planning.md §7](planning.md#7-observable-signals)). + +## Downstream Coordination + +This contract is implemented and exercised downstream, not in this repository: + +- `honua-io/honua-server` — the reference `/mcp` surface implements the session, + streaming, and revision contract (epic `honua-io/honua-server#1948`; sessions + & streaming `honua-io/honua-server#1954`; transport-symmetric surface + `honua-io/honua-server#1950`). +- `honua-io/honua-sdk-js` — the stdio surface and client integrations bind the + same contract. + +The reference implementation MAY vendor a slightly different shape ahead of this +document; where it diverges, this document defines the canonical contract and +the divergence is tracked in the referenced tickets.