Summary
Add a dedicated cmf.tools_list hook so a plugin can shape the tools/list response — omit or redact tools the subject isn't permitted to use, making restricted tools invisible.
Today cmf.tool_pre_invoke gates tools/call (per-tool, request-side). There's no clean interception point for the list response. The hook family is open (crates/cpex-core/src/hooks/types.rs), and a list response is a different shape and lifecycle than per-tool invoke — a dedicated cmf.tools_list hook (response-side) is the natural home, alongside cmf.tool_pre_invoke / cmf.tool_post_invoke. This needs body_access: read_write to mutate the response.
User Story 1
- As an: operator gating MCP tool access per agent
- I want: the
tools/list response filtered to only the tools the subject may call
- So that: restricted tools are invisible, not just blocked at call time
Acceptance Criteria
Scenario: Restricted tools omitted from tools/list
Given an agent permitted to call only tool A
When the agent issues tools/list returning [A, B, C]
Then the response contains only A
Scenario: Filtering is consistent with call gating
Given a tool is omitted from a subject's tools/list
When that subject attempts tools/call on it
Then the call is denied
Alternatives Considered
Reuse cmf.tool_post_invoke on the list response. Workable, but that hook is per-tool-invoke shaped; a dedicated cmf.tools_list keeps list-response shaping distinct and easier to reason about.
Additional Context
Scoped in the Praxis epic #678 alignment work (tool access policies, praxis sub-issue #682). Depends on the body_access: read_write body-mutation slice.
Summary
Add a dedicated
cmf.tools_listhook so a plugin can shape thetools/listresponse — omit or redact tools the subject isn't permitted to use, making restricted tools invisible.Today
cmf.tool_pre_invokegatestools/call(per-tool, request-side). There's no clean interception point for the list response. The hook family is open (crates/cpex-core/src/hooks/types.rs), and a list response is a different shape and lifecycle than per-tool invoke — a dedicatedcmf.tools_listhook (response-side) is the natural home, alongsidecmf.tool_pre_invoke/cmf.tool_post_invoke. This needsbody_access: read_writeto mutate the response.User Story 1
tools/listresponse filtered to only the tools the subject may callAcceptance Criteria
Alternatives Considered
Reuse
cmf.tool_post_invokeon the list response. Workable, but that hook is per-tool-invoke shaped; a dedicatedcmf.tools_listkeeps list-response shaping distinct and easier to reason about.Additional Context
Scoped in the Praxis epic #678 alignment work (tool access policies, praxis sub-issue #682). Depends on the
body_access: read_writebody-mutation slice.