Skip to content

feat: add MCP tool annotations, titles, and deterministic tool ordering - #402

Merged
AutomateIP merged 3 commits into
itential:develfrom
AutomateIP:feature/tool-annotations-titles-ordering
Aug 7, 2026
Merged

feat: add MCP tool annotations, titles, and deterministic tool ordering#402
AutomateIP merged 3 commits into
itential:develfrom
AutomateIP:feature/tool-annotations-titles-ordering

Conversation

@AutomateIP

Copy link
Copy Markdown
Collaborator

Summary

Adds MCP advisory tool annotations (readOnlyHint/destructiveHint/idempotentHint/
openWorldHint) and per-tool human-readable titles to every tool this server
exposes, and makes tool discovery/registration order deterministic. Closes the
compliance gap where clients received no behavioral hints and had to treat
every tool as unknown-risk, and stabilizes tools/list for client-side prompt
caching.

Changes

  • Add an annotate(...) decorator in utilities/tool.py, mirroring the
    existing tags() decorator, that attaches an mcp.types.ToolAnnotations
    instance (hints + title) to a tool function.
  • Apply @annotate(...) to all 76 static tools, classifying each read-only vs.
    write vs. destructive per a safety-first classification pass.
  • Make utilities/tool.py:itertools() yield annotations alongside tags and
    discover tools in a stable, name-sorted order (sorted module files +
    name-sorted members); consume the new value in server/server.py:__init_tools__.
  • Give dynamic bindings (bindings/__init__.py:bind_to_tool()) a conservative
    default (destructiveHint=True, readOnlyHint=False, openWorldHint=True) plus a
    humanized title, since a bound automation's real-world effects are unknown at
    registration time.
  • Add tests: annotate-decorator unit tests, a completeness guard that fails if
    any discovered tool is unannotated, safety-invariant tests (no tool both
    read-only and destructive; the destructive set is correctly classified),
    deterministic-ordering tests, and binding-default assertions.

No new dependencies (ToolAnnotations is from the already-pinned mcp SDK).

Testing

  • Unit tests pass (make ci: ruff, bandit, headers, 2710 tests, ~98% coverage, unchanged baseline)
  • Integration tests pass (live Itential Platform: verified tools/list
    annotations/titles across a read-only/write/destructive sample, stable
    ordering across two server instantiations, no schema regressions, a live
    dynamic-binding registration confirming the conservative default, and a
    live get_health call)
  • Manual testing completed

Related Issues

Implements roadmap Tier D1 items G7, G8, and G14 (MCP 2026-07-28 tool
annotations, per-tool titles, and deterministic tool ordering).

Adds MCP advisory tool annotations (readOnlyHint/destructiveHint/
idempotentHint/openWorldHint) and per-tool human-readable titles to every
tool this server exposes, and makes tool discovery/registration order
deterministic. Closes the compliance gap where clients received no
behavioral hints and had to treat every tool as unknown-risk, and
stabilizes tools/list for client-side prompt caching.

- Add an annotate(...) decorator in utilities/tool.py, mirroring the
  existing tags() decorator, that attaches an mcp.types.ToolAnnotations
  instance (hints + title) to a tool function.
- Apply @Annotate(...) to all 76 static tools, classifying each read-only
  vs. write vs. destructive.
- Make utilities/tool.py:itertools() yield annotations alongside tags and
  discover tools in a stable, name-sorted order (sorted module files +
  name-sorted members); consume the new value in
  server/server.py:__init_tools__.
- Give dynamic bindings (bindings/__init__.py:bind_to_tool()) a
  conservative default (destructiveHint=True, readOnlyHint=False,
  openWorldHint=True) plus a humanized title, since a bound automation's
  real-world effects are unknown at registration time.
- Add tests: annotate-decorator unit tests, a completeness guard that
  fails if any discovered tool is unannotated, safety-invariant tests (no
  tool both read-only and destructive; the destructive set is correctly
  classified), deterministic-ordering tests, and binding-default
  assertions.

No new dependencies (ToolAnnotations is from the already-pinned mcp SDK).

Testing: make ci passes (2710 tests, ~98% coverage, unchanged baseline);
verified live against a real Itential Platform (tools/list
annotations/titles, stable ordering across two server instantiations, no
schema regressions, live dynamic-binding conservative default, and a live
get_health call).

Implements roadmap Tier D1 items G7, G8, and G14.
@AutomateIP AutomateIP added the new feature A new feature that has been added to the application label Aug 7, 2026
start_adapter, stop_adapter, restart_adapter, start_application,
stop_application, and restart_application were classified as
destructive=False in the initial pass. All six cause a real service
disruption on the live platform (traffic through a stopped adapter fails;
an application is unavailable while stopped/restarting), so they should
carry destructiveHint=True like the rest of the operationally-disruptive
tool set.

Also extends the safety-invariant test's explicit destructive-set to
cover all six, so this classification is guarded against regression.

Flagged by user review of PR itential#402.
Update contributor guidelines so future tool PRs don't silently regress
the annotation coverage this PR establishes:

- AGENTS.md (symlinked from CLAUDE.md): add a required step to the "New
  Static Tool" workflow requiring every tool function be decorated with
  annotate(...), with read-only and destructive usage examples pulled
  from the real codebase. Notes that this is CI-enforced, not just style
  guidance, citing the completeness guard and safety-invariant tests in
  tests/utilities/test_tool.py. Also corrects the stale "56+ tools"
  figure to the current live count of 76+.
- CONTRIBUTING.md: add a checklist item to the "Before Submitting"
  section calling out tool-annotation classification for any PR that
  adds or changes a tool.

No application code changed.
@AutomateIP
AutomateIP merged commit cf000d4 into itential:devel Aug 7, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature A new feature that has been added to the application

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant