2. The tool contracts themselves
ToolAnnotations are hints.
They are not guaranteed to provide a faithful description of tool behavior (including
descriptive properties like title). Clients should never make tool use
decisions based on ToolAnnotations received from untrusted servers.”
And from the specification page: “For trust & safety and security, clients
MUST consider tool annotations to be untrusted unless they come from
trusted servers.”
The four hints, and their real defaults
| Field | Meaning when true | Default in 2026-07-28 |
|---|---|---|
readOnlyHint | The tool does not modify its environment | false |
destructiveHint | May perform destructive updates. Meaningful only when readOnlyHint is false | true |
idempotentHint | Repeat calls with the same arguments have no additional effect | false |
openWorldHint | Interacts with an open world of external entities | true |
The defaults are deliberately pessimistic. An unannotated tool is treated as write, destructive, non-idempotent and open-world — so silence is not permission. Applying that literally, an unannotated tool lands in prohibited in the matrix on the authority screen.
Service ticket history atlas.field-service.tickets
export_service_records — Export records to an external recipient
Send a bundle of service records, including site contacts, to an external email recipient outside the organisation. Use only when a customer has formally requested their records in writing and the request has been approved by a service manager.
Input schema (JSON Schema 2020-12)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"recipient": {
"type": "string",
"maxLength": 200
},
"model_variant": {
"type": "string"
},
"include_site_contacts": {
"type": "boolean"
},
"reason": {
"type": "string",
"maxLength": 500
}
},
"required": [
"recipient",
"model_variant"
],
"additionalProperties": false
}- Leaves the organisation. This is the exfiltration leg of the lethal trifecta.
- In this demonstration it opens no socket: it appends to a local sandbox outbox and returns. Nothing is ever sent anywhere.
get_ticket — Get one ticket
Fetch one complete service ticket by its identifier, for example 'SR-20031'. Use when a search result looks relevant and you need the full engineer notes, customer-submitted text and resolution. Returns a single ticket or an error if the identifier does not exist.
Input schema (JSON Schema 2020-12)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"ticket_id": {
"type": "string",
"pattern": "^SR-\\d{5}$"
}
},
"required": [
"ticket_id"
],
"additionalProperties": false
}- Single record. No wildcard, no bulk export.
post_ticket_update — Append a diagnostic note
Append a diagnostic note to an open service ticket. Additive only: it never edits or deletes an existing note. Use once a diagnosis is settled, to record the reasoning for the next engineer.
- annotations alone would imply requires_approval, host registry says automatic
Input schema (JSON Schema 2020-12)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"ticket_id": {
"type": "string",
"pattern": "^SR-\\d{5}$"
},
"note": {
"type": "string",
"maxLength": 2000
}
},
"required": [
"ticket_id",
"note"
],
"additionalProperties": false
}- Appends only. Cannot edit or delete an existing note.
- Writes to the sandbox store, not to a production system.
search_tickets — Search ticket history
Search closed service-ticket history for one specific machine variant. Use this FIRST when you need to know how a fault was diagnosed and repaired before on the same equipment, including which part number was actually fitted and how long the repair took. Filter by model_variant such as 'AC-250-S'. Pass the reported symptom in the `symptom` argument and results are ranked by how closely previous symptoms match it, which is how you turn a description into a fault_code such as 'F-101'. This tool does not search technical manuals and does not price or order parts.
Input schema (JSON Schema 2020-12)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"model_variant": {
"type": "string",
"pattern": "^[A-Z]{2}-\\d{2,3}-[ABS]$",
"description": "Full model and variant, e.g. AC-250-S. The variant suffix is significant: -S machines are sound attenuated and take different parts."
},
"fault_code": {
"type": "string",
"pattern": "^F-\\d{3}$",
"description": "Optional fault code filter, e.g. F-101."
},
"symptom": {
"type": "string",
"maxLength": 400,
"description": "The symptom as reported. When supplied, results are ranked by lexical similarity to previous symptom descriptions rather than by date."
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 25,
"description": "Maximum tickets to return."
}
},
"required": [
"model_variant"
],
"additionalProperties": false
}Output schema
{
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"tickets": {
"type": "array"
}
}
}- Reads the closed-ticket store only; open tickets are not visible.
- Never returns customer payment or contract data.
- Bounded at 25 records per call.
Parts catalogue and ordering atlas.field-service.parts
check_availability — Check stock and lead time
Return current stock quantity and supplier lead time in days for one part number. Use before promising a repair date. Read only.
Input schema (JSON Schema 2020-12)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"part_no": {
"type": "string",
"maxLength": 40
}
},
"required": [
"part_no"
],
"additionalProperties": false
}- Point-in-time read. Does not reserve or allocate.
find_part — Find catalogue parts
Look up spare parts in the catalogue for one specific machine variant. Use after a fault has been diagnosed, to turn a component category such as 'thermostatic-valve' into a real part number with price, stock and lead time. The model_variant must be exact, e.g. 'AC-250-S': variant-specific parts are not interchangeable, and fitting an -A part to an -S machine is a repeat callout. This tool does not read ticket history and does not order anything.
Input schema (JSON Schema 2020-12)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"model_variant": {
"type": "string",
"pattern": "^[A-Z]{2}-\\d{2,3}-[ABS]$",
"description": "Exact model and variant, e.g. AC-250-S."
},
"category": {
"type": "string",
"enum": [
"aftercooler-core",
"aftercooler-drain",
"air-hose",
"bearing",
"belt",
"coupling-element",
"fan-blade",
"gasket-set",
"gauge",
"hour-meter",
"intake-filter",
"mount-kit",
"oil-filter",
"pressure-transducer",
"relief-valve",
"separator-element",
"shaft-seal",
"starter-contactor",
"thermostatic-valve",
"unloader-valve",
"valve-plate",
"wiring-loom"
],
"description": "Component category to fit."
},
"in_stock_only": {
"type": "boolean"
}
},
"required": [
"model_variant",
"category"
],
"additionalProperties": false
}Output schema
{
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"parts": {
"type": "array"
}
}
}- Catalogue read only. Does not reserve stock.
- Returns parts whose fitment list contains the exact model variant.
raise_parts_order — Raise a parts order
Raise a chargeable parts order against a customer account. This commits spend and cannot be recalled once the supplier accepts it. Requires explicit human approval: the first call returns an approval request, and the caller must re-issue the same call carrying the approver's decision.
- annotations alone would imply prohibited, host registry says requires_approval
Input schema (JSON Schema 2020-12)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"part_no": {
"type": "string",
"maxLength": 40
},
"quantity": {
"type": "integer",
"minimum": 1,
"maximum": 20
},
"ticket_id": {
"type": "string",
"pattern": "^SR-\\d{5}$"
},
"justification": {
"type": "string",
"maxLength": 600
}
},
"required": [
"part_no",
"quantity",
"ticket_id"
],
"additionalProperties": false
}- Commits spend against a customer account.
- Pauses with an MCP input_required result until a named human decides.
- A denial is terminal for this call: the tool will not retry or substitute.
Technical manual search atlas.field-service.manuals
search_manual — Search technical manuals
Search operator and field-maintenance manuals for a procedure, specification or torque figure. Use when you need the documented method for a repair, not the history of previous repairs and not a part number. Pass product_family such as 'AC-250' to keep results on the right machine: the manuals cover several families and several revisions, and a procedure from the wrong variant reads plausibly and is wrong.
Input schema (JSON Schema 2020-12)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"query": {
"type": "string",
"maxLength": 400,
"description": "Natural-language description of the procedure or specification wanted."
},
"product_family": {
"type": "string",
"pattern": "^[A-Z]{2}-\\d{2,3}$",
"description": "Family code such as AC-250 or RS-90. Optional but strongly recommended: without it the search crosses families."
},
"variant": {
"type": "string",
"enum": [
"A",
"B",
"S"
],
"description": "Variant letter, when the procedure differs."
},
"k": {
"type": "integer",
"minimum": 1,
"maximum": 10
}
},
"required": [
"query"
],
"additionalProperties": false
}Output schema
{
"type": "object",
"properties": {
"backend": {
"type": "string"
},
"passages": {
"type": "array"
}
}
}- Read only over a fixed manual corpus.
- Returns passages with document, page and revision so a claim can be checked.
- Does not synthesise an answer; it returns evidence.
How much of MCP 2026-07-28 this actually implements
The official Python mcp package is MIT and at 2.0.0
implements this revision, but it is not installed in this box's environment, so the
wire format is written by hand in pipeline/mcp_proto.py and
pipeline/mcp_server.py. No conformance suite has been run against it.
This list is the honest scope.
Implemented
- Stateless core: no session, no `initialize` handshake, no `Mcp-Session-Id`
- Per-request context in `_meta` (protocolVersion, clientInfo, clientCapabilities)
- `server/discover` advertising versions, capabilities and identity
- `tools/list` with `ttlMs` + `cacheScope` (CacheableResult) and deterministic order
- `tools/call` with `structuredContent`, `content[]` back-compat text block and `outputSchema`
- Required `resultType` on every result: `complete` or `input_required`
- Multi Round-Trip Requests: `inputRequests` + opaque `requestState`, client re-issues the original request with a NEW JSON-RPC id and `inputResponses`
- Streamable HTTP shape: POST-only MCP endpoint, `Accept` must list both media types, GET/DELETE answered 405
- Required headers `MCP-Protocol-Version`, `Mcp-Method`, `Mcp-Name`, with header/body mismatch rejected 400 + -32020 HeaderMismatch
- `Origin` validation, 403 on a disallowed origin
- Tool-execution errors as `isError: true` results, protocol errors as JSON-RPC `error`
- OpenTelemetry trace-context propagation through `_meta.traceparent` (SEP-414)
Not implemented
- OAuth 2.1 authorization, RFC 9728 protected-resource metadata, RFC 8707 resource indicators — the servers are loopback-bound and unauthenticated
- `subscriptions/listen`, `resources/*`, `prompts/*`, `icons`
- The Tasks extension as a wire protocol. The approval gate uses MRTR, which is the in-core mechanism; the Tasks extension carries the same mid-flight-input pattern over a durable handle and is described, not implemented
- SSE response bodies. Every response here is a single JSON object, which the transport permits
- `Mcp-Param-{Name}` headers for arguments annotated `x-mcp-header`
What the trace records
| Attribute | Requirement | Note |
|---|---|---|
gen_ai.operation.name | Required | execute_tool for a tool span, chat for inference |
gen_ai.provider.name | Required | supersedes gen_ai.system |
gen_ai.tool.name | Required on a tool span | the MCP tool name |
gen_ai.tool.call.id | Recommended | correlates the model's request with the result |
gen_ai.tool.description | Recommended | the model-facing description that was in context |
gen_ai.tool.type | Recommended | function / extension / datastore |
gen_ai.tool.call.arguments | Opt-In | content capture, off by default |
gen_ai.tool.call.result | Opt-In | content capture, off by default |
gen_ai.tool.definitions | Opt-In | the full tool contracts the model could see |
gen_ai.conversation.id | Conditionally required | one agent run |
error.type | Required on failure | jsonrpc.<code> or tool_execution_error |
mcp.method.name | MCP convention | tools/call, tools/list, server/discover |
mcp.protocol.version | MCP convention | 2026-07-28 |
mcp.request.id | MCP convention | JSON-RPC request id |
Custom attributes
There is no gen_ai.skill.* convention as of August 2026,
and no convention for guardrail decisions, so these are namespaced and labelled
custom rather than dressed up as standard.
| Attribute | Status | Note |
|---|---|---|
skill.name | custom | no gen_ai.skill.* convention exists |
skill.version | custom | |
skill.invocation_source | custom | model | user |
guardrail.name | custom | which check fired |
guardrail.decision | custom | allow | require_approval | block |
atlas.mcp.transport | custom | http | inproc |
Demo4/pipeline/.