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
4 changes: 2 additions & 2 deletions .secrets.baseline

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ clap = { version = "4.5.60", features = ["derive", "env"] }
thiserror = "2.0.18"
rmp-serde = "1.3.1"
async-trait = "0.1.89"
base64 = "0.22.1"
reqwest = "0.13"
jsonwebtoken = { version = "11.0.0", features = ["aws_lc_rs"] }
rustls = { version = "0.23", features = ["ring"] }
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ Activation requires all three pieces:
- Runtime flag: `--runtime-plugins-enabled true`
- Redis config key: `ContextForgeGatewayRuntimePluginConfig`

The plugin kind is `validator/secrets-detection`. The data plane currently
wires only `cmf.tool_pre_invoke` and `cmf.tool_post_invoke`.
The plugin kind is `validator/secrets-detection`. The dataplane wires CMF hooks
for tool calls, prompt fetches, and resource reads.

Example run command:

Expand Down
6 changes: 3 additions & 3 deletions _context/wiki/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ The binary sets `tikv_jemallocator` as the global allocator. jemalloc holds up b
- `initialize` opens one backend transport per configured backend concurrently (`futures::future::join_all`); a failed backend degrades that backend only.
- List methods fan out to all connected backends concurrently and merge.
- Targeted calls (except `call_tool`) resolve exactly one backend service handle from `BackendTransports`.
- `call_tool` creates a fresh per-request backend connection via `connect_backend_for_request`, runs pre/post plugin hooks, executes the call, then explicitly closes the connection before returning.
- Targeted tool, prompt, and resource calls run configured pre/post plugin hooks after backend routing. `call_tool` creates a fresh per-request backend connection via `connect_backend_for_request`, then explicitly closes it before returning.
- `call_tool` watches the downstream cancellation token and forwards a cancel to the backend if the client gives up first; backend progress notifications are forwarded downstream while the call is in flight.

## Startup And Response Flow
Expand All @@ -180,7 +180,7 @@ Response unwind order (Tower layers execute outside-in, so unwind is inside-out)

```text
backend response
-> response plugin hooks (call_tool only)
-> response plugin hooks (tool, prompt, and resource calls)
-> merge / namespace / pass through
-> virtual_host_config_layer response side
-> user_config_store_layer response side
Expand Down Expand Up @@ -227,7 +227,7 @@ Do not bury transport security decisions inside MCP method handlers. They belong

## Plugin Hook Expansion Requirements

Current supported hooks are intentionally narrow (`cmf.tool_pre_invoke`, `cmf.tool_post_invoke`). Before adding any new hook point, define all of the following:
Current supported hooks cover tool, prompt, and resource pre/post lifecycles. Before adding any new hook point, define all of the following:

| Requirement | Why |
| --- | --- |
Expand Down
10 changes: 7 additions & 3 deletions _context/wiki/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ RuntimePluginConfigDocument
cpex: CpexConfig
```

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.
Supported: tool, prompt, and resource pre/post CMF hooks.
Rejected: routing-based selection, plugin dirs, global policies, 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.

Expand All @@ -201,7 +201,11 @@ Writing plugin edits back follows three rules:

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.
Binary resource blobs reach plugins as decoded CMF bytes and are encoded back to MCP base64 after an edit. Unchanged blobs retain the backend's exact wire representation.

### Resource Read Hook Behavior

For `resources/read`, the pre hook runs after routing and may allow or deny the canonical backend-local URI, but cannot change it. The post hook can redact text resources, transform binary resources, or deny the response. URI, MIME type, item count, CMF schema version, and channel must remain stable; unsupported or lossy edits and invalid hook lifecycle state fail closed.

### Demo Plugin Workflow

Expand Down
1 change: 1 addition & 0 deletions crates/contextforge-data-plane-cpex/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ doctest = false
[dependencies]
arc-swap = "1.7"
async-trait.workspace = true
base64.workspace = true
contextforge-data-plane-apis.workspace = true
cpex.workspace = true
redis.workspace = true
Expand Down
Loading