docs: update README for plugin system, add proxyModel tests
- Rewrite README Content Pipeline section as Plugin System section documenting built-in plugins (default, gate, content-pipeline), plugin hooks, and the relationship between gating and proxyModel - Update all README examples to use --proxy-model instead of --gated - Add unit tests: proxyModel normalization in JSON/YAML output (4 tests), Plugin Config section in describe output (2 tests) - Add smoke tests: yaml/json output shows resolved proxyModel without gated field, round-trip compatibility (4 tests) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
113
README.md
113
README.md
@@ -21,7 +21,7 @@ Claude Code <--STDIO--> mcplocal (local proxy) <--HTTP--> mcpd (daemon) <--
|
||||
```
|
||||
|
||||
- **mcpd** — the daemon. Runs on a server, manages MCP server containers (Docker/Podman), stores configuration in PostgreSQL.
|
||||
- **mcplocal** — local proxy. Runs on your machine, presents a single MCP endpoint to Claude that merges tools from all your servers. Handles namespacing (`grafana/search_dashboards`), gated sessions, and prompt delivery.
|
||||
- **mcplocal** — local proxy. Runs on your machine, presents a single MCP endpoint to Claude that merges tools from all your servers. Handles namespacing (`grafana/search_dashboards`), plugin execution (gating, content pipelines), and prompt delivery.
|
||||
- **mcpctl** — the CLI. Talks to mcpd (via mcplocal or directly) to manage everything.
|
||||
|
||||
## Quick Start
|
||||
@@ -88,7 +88,7 @@ A project groups servers together and configures how Claude interacts with them.
|
||||
mcpctl create project monitoring \
|
||||
--description "Grafana dashboards and alerting" \
|
||||
--server grafana \
|
||||
--no-gated
|
||||
--proxy-model content-pipeline
|
||||
```
|
||||
|
||||
### 6. Connect Claude Code
|
||||
@@ -133,7 +133,7 @@ servers:
|
||||
projects:
|
||||
- name: monitoring
|
||||
description: "Infrastructure monitoring"
|
||||
gated: false
|
||||
proxyModel: content-pipeline
|
||||
servers:
|
||||
- grafana
|
||||
```
|
||||
@@ -150,55 +150,80 @@ mcpctl get all --project monitoring -o yaml > backup.yaml
|
||||
mcpctl apply -f backup.yaml
|
||||
```
|
||||
|
||||
## Content Pipeline (ProxyModel)
|
||||
## Plugin System (ProxyModel)
|
||||
|
||||
ProxyModel defines a **content transformation pipeline** that runs between upstream MCP servers and the client (e.g., Claude). It processes tool results, prompts, and resources through ordered stages before delivering them.
|
||||
ProxyModel is mcpctl's plugin system. Each project is assigned a **plugin** that controls how Claude interacts with its servers. Plugins are composed from two layers: **TypeScript plugins** (MCP middleware hooks) and **YAML pipelines** (content transformation stages).
|
||||
|
||||
### Built-in Models
|
||||
### Built-in Plugins
|
||||
|
||||
| Model | Stages | Use case |
|
||||
|-------|--------|----------|
|
||||
| Plugin | Includes gating | Content pipeline | Description |
|
||||
|--------|:-:|:-:|---|
|
||||
| **default** | Yes | Yes | Gate + content pipeline. The default for all projects. |
|
||||
| **gate** | Yes | No | Gating only — `begin_session` gate with prompt delivery. |
|
||||
| **content-pipeline** | No | No | Content transformation only — no gating. |
|
||||
|
||||
**Gating** means Claude initially sees only a `begin_session` tool. After calling it with a task description, relevant prompts are delivered and the full tool list is revealed. This keeps Claude's context focused.
|
||||
|
||||
```bash
|
||||
# Create a gated project (default behavior)
|
||||
mcpctl create project home --server my-ha --proxy-model default
|
||||
|
||||
# Create an ungated project (direct tool access, no gate)
|
||||
mcpctl create project tools --server grafana --proxy-model content-pipeline
|
||||
```
|
||||
|
||||
### Plugin Hooks
|
||||
|
||||
TypeScript plugins intercept MCP requests/responses at specific lifecycle points:
|
||||
|
||||
| Hook | When it fires |
|
||||
|------|--------------|
|
||||
| `onSessionCreate` | New MCP session established |
|
||||
| `onSessionDestroy` | Session ends |
|
||||
| `onInitialize` | MCP `initialize` request — can inject instructions |
|
||||
| `onToolsList` | `tools/list` — can filter/modify tool list |
|
||||
| `onToolCallBefore` | Before forwarding a tool call — can intercept |
|
||||
| `onToolCallAfter` | After receiving tool result — can transform |
|
||||
| `onResourcesList` | `resources/list` — can filter resources |
|
||||
| `onResourceRead` | `resources/read` — can intercept resource reads |
|
||||
| `onPromptsList` | `prompts/list` — can filter prompts |
|
||||
| `onPromptGet` | `prompts/get` — can intercept prompt reads |
|
||||
|
||||
Plugins compose via `extends` — the `default` plugin extends both `gate` and `content-pipeline`, inheriting all their hooks.
|
||||
|
||||
### Content Pipelines
|
||||
|
||||
Content pipelines transform tool results through ordered stages before delivering to Claude:
|
||||
|
||||
| Pipeline | Stages | Use case |
|
||||
|----------|--------|----------|
|
||||
| **default** | `passthrough` → `paginate` (8KB pages) | Safe pass-through with pagination for large responses |
|
||||
| **subindex** | `section-split` → `summarize-tree` | Splits large content into sections, returns a summary index. Client drills down with `_resultId`/`_section` params |
|
||||
| **subindex** | `section-split` → `summarize-tree` | Splits large content into sections, returns a summary index |
|
||||
|
||||
### How `subindex` Works
|
||||
#### How `subindex` Works
|
||||
|
||||
1. Upstream returns a large tool result (e.g., 50KB of device states)
|
||||
2. `section-split` divides content into logical sections (2KB–15KB each)
|
||||
2. `section-split` divides content into logical sections (2KB-15KB each)
|
||||
3. `summarize-tree` generates a compact index with section summaries (~200 tokens each)
|
||||
4. Client receives the index and can request specific sections via `_section` parameter
|
||||
|
||||
### Configuration
|
||||
|
||||
Set per-project (all servers use the same model):
|
||||
Set per-project:
|
||||
|
||||
```yaml
|
||||
kind: Project
|
||||
metadata:
|
||||
name: home-automation
|
||||
spec:
|
||||
servers: [home-assistant, node-red]
|
||||
proxyModel: subindex
|
||||
```
|
||||
|
||||
Override per-server within a project:
|
||||
|
||||
```yaml
|
||||
kind: Project
|
||||
metadata:
|
||||
name: monitoring
|
||||
spec:
|
||||
servers: [grafana, prometheus]
|
||||
proxyModel: default
|
||||
serverOverrides:
|
||||
grafana:
|
||||
proxyModel: subindex
|
||||
kind: project
|
||||
name: home-automation
|
||||
proxyModel: default
|
||||
servers:
|
||||
- home-assistant
|
||||
- node-red
|
||||
```
|
||||
|
||||
Via CLI:
|
||||
|
||||
```bash
|
||||
mcpctl create project monitoring --server grafana --server prometheus --proxy-model subindex
|
||||
mcpctl create project monitoring --server grafana --proxy-model content-pipeline
|
||||
```
|
||||
|
||||
### Custom ProxyModels
|
||||
@@ -223,7 +248,13 @@ spec:
|
||||
cacheable: true
|
||||
```
|
||||
|
||||
Inspect available models: `mcpctl get proxymodels` / `mcpctl describe proxymodel subindex`
|
||||
Inspect available plugins and pipelines:
|
||||
|
||||
```bash
|
||||
mcpctl get proxymodels # List all plugins and pipelines
|
||||
mcpctl describe proxymodel default # Pipeline details (stages, controller)
|
||||
mcpctl describe proxymodel gate # Plugin details (hooks, extends)
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
@@ -255,12 +286,12 @@ mcpctl describe project monitoring
|
||||
# Create resources
|
||||
mcpctl create server <name> [flags]
|
||||
mcpctl create secret <name> --data KEY=value
|
||||
mcpctl create project <name> --server <srv> [--gated]
|
||||
mcpctl create project <name> --server <srv> [--proxy-model <plugin>]
|
||||
mcpctl create prompt <name> --project <proj> --content "..."
|
||||
|
||||
# Modify resources
|
||||
mcpctl edit server grafana # Opens in $EDITOR
|
||||
mcpctl patch project myproj gated=true
|
||||
mcpctl patch project myproj proxyModel=default
|
||||
mcpctl apply -f config.yaml # Declarative create/update
|
||||
|
||||
# Delete resources
|
||||
@@ -300,7 +331,7 @@ mcpctl create server my-ha \
|
||||
|
||||
## Gated Sessions
|
||||
|
||||
Projects are **gated** by default. When Claude connects to a gated project:
|
||||
Projects using the `default` or `gate` plugin are **gated**. When Claude connects to a gated project:
|
||||
|
||||
1. Claude sees only a `begin_session` tool initially
|
||||
2. Claude calls `begin_session` with a description of its task
|
||||
@@ -310,9 +341,11 @@ Projects are **gated** by default. When Claude connects to a gated project:
|
||||
This keeps Claude's context focused — instead of dumping 100+ tools and pages of docs upfront, only the relevant ones are delivered based on the task at hand.
|
||||
|
||||
```bash
|
||||
# Enable/disable gating
|
||||
mcpctl patch project monitoring gated=true
|
||||
mcpctl patch project monitoring gated=false
|
||||
# Gated (default)
|
||||
mcpctl create project monitoring --server grafana --proxy-model default
|
||||
|
||||
# Ungated (direct tool access)
|
||||
mcpctl create project tools --server grafana --proxy-model content-pipeline
|
||||
```
|
||||
|
||||
## Prompts
|
||||
|
||||
Reference in New Issue
Block a user