Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions _context/wiki/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,9 @@ RuntimePluginConfigDocument
cpex: CpexConfig
```

Supported: `cmf.tool_pre_invoke`, `cmf.tool_post_invoke` only.
Rejected: routing-based selection, plugin dirs, global policies, other hook types.
Supported: `cmf.tool_pre_invoke`, `cmf.tool_post_invoke`, `cmf.prompt_pre_fetch`, `cmf.prompt_post_fetch` only.
Rejected: routing-based selection, plugin dirs, global policies, resource and LLM hooks, plugin conditions.
Config validation and `CmfPluginFactory` registration must agree on that list: a hook accepted by validation but not registered leaves the plugin loaded and silently inert.
Reload watcher: 10-minute interval. Invalid reload → runtime marked failed.

### Tool Call Hook Behavior
Expand All @@ -168,6 +169,22 @@ After the upstream backend returns, the post hook can leave the result unchanged

Plugin execution must not poison shared gateway state. A plugin denial becomes an MCP error. Soft plugin errors are logged. Unsupported plugin configuration fails validation before the runtime is accepted.

### Prompt Fetch Hook Behavior

For `get_prompt`, the pre hook runs after backend routing, so the plugin sees the backend-local prompt name and the owning backend separately rather than the gateway-prefixed identifier. It can leave the arguments unchanged, replace them, or deny the fetch before the backend renders anything.

The post hook receives the rendered prompt as one CMF message per rendered MCP message, each carrying its role and its content block: text, image, audio, embedded resource, or resource link. A plugin can inspect or rewrite any of them, so a policy can act on a file interpolated into a prompt rather than only on the surrounding text.

Writing plugin edits back follows three rules:

- A message the plugin left unchanged is returned exactly as the backend sent it, so annotations, `_meta`, and binary resource blobs survive untouched.
- A message the plugin changed is rebuilt from CMF. CMF does not model MCP annotations or `_meta`, so an edited message loses them.
- Edits that cannot be applied faithfully fail the call rather than falling back to the backend's original. A changed message count, anything other than exactly one prompt result in the payload, a role MCP prompts cannot express, or a resource whose text the plugin removed all return an error. Silently restoring the backend's content would undo a redaction.

MCP prompt results carry no error flag, so a plugin setting `is_error` on the CMF prompt result is rejecting the prompt rather than describing it. The gateway turns that into an MCP error carrying the plugin's `error_message`, and the rendered content never reaches the client. This differs from tools, where `is_error` is a field on `CallToolResult` and is forwarded as a successful response.

Binary resource blobs reach plugins by URI and MIME type but not by content: CMF stores decoded bytes while MCP sends base64. A plugin can deny such a message; editing one fails the write-back.

### Demo Plugin Workflow

The optional `test-plugins` feature compiles demo factories from the `cpex-plugins-rs` repository. Redis configuration activates factories already present in the binary; it never loads new Rust code into a running process.
Expand Down
2 changes: 1 addition & 1 deletion _context/wiki/project.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ flowchart LR
direction TB
MW["Middleware stack\nvirtual host · JWT · session · user config"]
RT["MCP Routing\nfan-out · prefix namespace\nlist merge · capability merge"]
PL["Plugin hooks\ncmf.tool_pre_invoke\ncmf.tool_post_invoke"]
PL["Plugin hooks\ncmf.tool_pre_invoke\ncmf.tool_post_invoke\ncmf.prompt_pre_fetch\ncmf.prompt_post_fetch"]
MW --> RT --> PL
end

Expand Down
2 changes: 1 addition & 1 deletion _context/wiki/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ If RMCP rejects the delete, local state is untouched.
| `call_tool` | Targeted | Resolves alias → single/multi-backend fallback. Runs pre/post plugin hooks. Forwards downstream cancellation to backend. Tracks backend progress tokens: RMCP assigns a new token per backend request; the gateway maps each backend token to the downstream token. Request enqueue and mapping publication are serialized against progress lookup so an immediate backend notification cannot overtake registration. When the notification matches an in-flight token, the gateway restores the downstream token and forwards it to the client. |
| `read_resource` | Targeted | Single-backend: URI unchanged. Multi-backend: strips prefix. |
| `subscribe` / `unsubscribe` | Targeted | Same resource-URI routing; forwards/stops resource-update notifications. |
| `get_prompt` | Targeted | Single-backend: name unchanged. Multi-backend: strips prefix. |
| `get_prompt` | Targeted | Single-backend: name unchanged. Multi-backend: strips prefix. Runs pre/post prompt hooks around the backend call: the pre hook may rewrite arguments or deny, the post hook may rewrite or reject the rendered messages. |
| `complete` | Targeted | Routes on prompt name or resource URI inside `ref`. |
| `ping` | Local | Returns success; no backend fanout. |
| `DELETE` | Session | RMCP handles first; on success `session_id_layer` removes local session + backend transports. |
2 changes: 1 addition & 1 deletion _context/wiki/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Protocol tests and fixtures should target MCP `2026-07-28`, use `server/discover
| `gateway_list_tools.rs` | List fanout, prefixing, and merged output. |
| `gateway_prompts.rs` | Prompt listing and prefixed `get_prompt` routing. |
| `gateway_resource_templates.rs` | Template fanout with prefixed names and URI templates, plus `read_resource` round-trips. |
| `gateway_plugins.rs` | CPEX pre/post tool hooks around `call_tool` and stream events. |
| `gateway_plugins.rs` | CPEX pre/post tool hooks around `call_tool` and stream events, and prompt hooks around `get_prompt`. |

These run in `cargo nextest run` with no Docker dependencies.

Expand Down
Loading
Loading