Skip to content

Commit 5d7cfca

Browse files
committed
Add plugins README
Signed-off-by: Mihai Criveti <[email protected]>
1 parent a70a485 commit 5d7cfca

File tree

1 file changed

+395
-1
lines changed

1 file changed

+395
-1
lines changed

plugins/README.md

Lines changed: 395 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,395 @@
1-
Plugins go here..
1+
# MCP Context Forge Plugin Framework
2+
3+
The MCP Context Forge Plugin Framework provides a powerful, production-ready system for AI safety middleware, content security, policy enforcement, and operational excellence. Plugins run as middleware components that can intercept and transform requests and responses at various points in the gateway lifecycle.
4+
5+
## Quick Start
6+
7+
### Enable Plugins
8+
9+
1. Set environment variables in `.env`:
10+
```bash
11+
PLUGINS_ENABLED=true
12+
PLUGIN_CONFIG_FILE=plugins/config.yaml
13+
PLUGINS_CLI_COMPLETION=false
14+
PLUGINS_CLI_MARKUP_MODE=rich
15+
```
16+
17+
2. Configure plugins in `plugins/config.yaml` (see [Configuration](#configuration) section)
18+
19+
3. Restart the gateway: `make dev`
20+
21+
## Plugin Architecture
22+
23+
The framework supports two types of plugins:
24+
25+
### 1. Self-Contained Plugins
26+
- Written in Python and run directly in the gateway process
27+
- Sub-millisecond latency (<1ms)
28+
- Perfect for high-frequency operations like PII filtering and regex transformations
29+
- Examples: `pii_filter`, `regex_filter`, `deny_filter`, `resource_filter`
30+
31+
### 2. External Service Plugins
32+
- Call external AI safety services via HTTP/MCP
33+
- Support microservice integrations with authentication
34+
- 10-100ms latency depending on service
35+
- Examples: LlamaGuard, OpenAI Moderation, custom safety services
36+
37+
## Available Hooks
38+
39+
Plugins can implement hooks at these lifecycle points:
40+
41+
| Hook | Description | Payload Type | Use Cases |
42+
|------|-------------|--------------|-----------|
43+
| `prompt_pre_fetch` | Before prompt template retrieval | `PromptPrehookPayload` | Input validation, access control |
44+
| `prompt_post_fetch` | After prompt template retrieval | `PromptPosthookPayload` | Content filtering, transformation |
45+
| `tool_pre_invoke` | Before tool execution | `ToolPreInvokePayload` | Parameter validation, safety checks |
46+
| `tool_post_invoke` | After tool execution | `ToolPostInvokeResult` | Result filtering, audit logging |
47+
| `resource_pre_fetch` | Before resource retrieval | `ResourcePreFetchPayload` | Protocol/domain validation |
48+
| `resource_post_fetch` | After resource retrieval | `ResourcePostFetchResult` | Content scanning, size limits |
49+
50+
Future hooks (in development):
51+
- `server_pre_register` / `server_post_register` - Virtual server verification
52+
- `auth_pre_check` / `auth_post_check` - Custom authentication logic
53+
- `federation_pre_sync` / `federation_post_sync` - Gateway federation
54+
55+
## Configuration
56+
57+
### Main Configuration File (`plugins/config.yaml`)
58+
59+
```yaml
60+
plugins:
61+
- name: "PIIFilterPlugin"
62+
kind: "plugins.pii_filter.pii_filter.PIIFilterPlugin"
63+
description: "Detects and masks Personally Identifiable Information"
64+
version: "0.1.0"
65+
author: "Your Name"
66+
hooks: ["prompt_pre_fetch", "tool_pre_invoke"]
67+
tags: ["security", "pii", "compliance"]
68+
mode: "enforce" # enforce | permissive | disabled
69+
priority: 50 # Lower number = higher priority (runs first)
70+
conditions:
71+
- prompts: [] # Empty = apply to all prompts
72+
server_ids: [] # Apply to specific servers
73+
tenant_ids: [] # Apply to specific tenants
74+
config:
75+
detect_ssn: true
76+
detect_email: true
77+
default_mask_strategy: "partial"
78+
79+
# Global settings
80+
plugin_settings:
81+
parallel_execution_within_band: true
82+
plugin_timeout: 30
83+
fail_on_plugin_error: false
84+
plugin_health_check_interval: 60
85+
```
86+
87+
### Plugin Modes
88+
89+
- **`enforce`**: Blocks violations and prevents request processing
90+
- **`permissive`**: Logs violations but allows request to continue
91+
- **`disabled`**: Plugin is not executed (useful for temporary disabling)
92+
93+
### Plugin Priority
94+
95+
Lower priority numbers run first (higher priority). Recommended ranges:
96+
- **1-50**: Critical security plugins (PII, access control)
97+
- **51-100**: Content filtering and validation
98+
- **101-200**: Transformations and enhancements
99+
- **201+**: Logging and monitoring
100+
101+
## Built-in Plugins
102+
103+
### PII Filter Plugin
104+
Detects and masks Personally Identifiable Information (PII):
105+
106+
```yaml
107+
- name: "PIIFilterPlugin"
108+
kind: "plugins.pii_filter.pii_filter.PIIFilterPlugin"
109+
config:
110+
detect_ssn: true
111+
detect_credit_card: true
112+
detect_email: true
113+
detect_phone: true
114+
detect_aws_keys: true
115+
default_mask_strategy: "partial" # redact | partial | hash | tokenize
116+
block_on_detection: false
117+
whitelist_patterns:
118+
119+
```
120+
121+
### Regex Filter Plugin
122+
Find and replace text patterns:
123+
124+
```yaml
125+
- name: "ReplaceBadWordsPlugin"
126+
kind: "plugins.regex_filter.search_replace.SearchReplacePlugin"
127+
config:
128+
words:
129+
- search: "inappropriate_word"
130+
replace: "[FILTERED]"
131+
```
132+
133+
### Deny List Plugin
134+
Block requests containing specific terms:
135+
136+
```yaml
137+
- name: "DenyListPlugin"
138+
kind: "plugins.deny_filter.deny.DenyListPlugin"
139+
config:
140+
words:
141+
- "blocked_term"
142+
- "another_blocked_term"
143+
```
144+
145+
### Resource Filter Plugin
146+
Validate and filter resource requests:
147+
148+
```yaml
149+
- name: "ResourceFilterExample"
150+
kind: "plugins.resource_filter.resource_filter.ResourceFilterPlugin"
151+
config:
152+
max_content_size: 1048576 # 1MB
153+
allowed_protocols: ["http", "https"]
154+
blocked_domains: ["malicious.example.com"]
155+
content_filters:
156+
- pattern: "password\\s*[:=]\\s*\\S+"
157+
replacement: "password: [REDACTED]"
158+
```
159+
160+
## Writing Custom Plugins
161+
162+
### 1. Plugin Structure
163+
164+
Create a new directory under `plugins/`:
165+
166+
```
167+
plugins/my_plugin/
168+
├── __init__.py
169+
├── plugin-manifest.yaml
170+
├── my_plugin.py
171+
└── README.md
172+
```
173+
174+
### 2. Plugin Manifest (`plugin-manifest.yaml`)
175+
176+
```yaml
177+
description: "My custom plugin"
178+
author: "Your Name"
179+
version: "1.0.0"
180+
available_hooks:
181+
- "tool_pre_invoke"
182+
- "tool_post_invoke"
183+
default_configs:
184+
my_setting: true
185+
threshold: 0.8
186+
```
187+
188+
### 3. Plugin Implementation
189+
190+
```python
191+
# my_plugin.py
192+
from mcpgateway.plugins.framework.base import Plugin
193+
from mcpgateway.plugins.framework.models import (
194+
ToolPreInvokePayload,
195+
ToolPreInvokeResult,
196+
PluginResult
197+
)
198+
199+
class MyPlugin(Plugin):
200+
"""Custom plugin implementation."""
201+
202+
async def tool_pre_invoke(self, payload: ToolPreInvokePayload) -> ToolPreInvokeResult:
203+
"""Process tool invocation before execution."""
204+
205+
# Get plugin configuration
206+
my_setting = self.config.get("my_setting", False)
207+
threshold = self.config.get("threshold", 0.5)
208+
209+
# Implement your logic
210+
if my_setting and self._should_block(payload):
211+
return ToolPreInvokeResult(
212+
result=PluginResult.BLOCK,
213+
message="Request blocked by custom logic",
214+
modified_payload=payload
215+
)
216+
217+
# Modify payload if needed
218+
modified_payload = self._transform_payload(payload)
219+
220+
return ToolPreInvokeResult(
221+
result=PluginResult.CONTINUE,
222+
modified_payload=modified_payload
223+
)
224+
225+
def _should_block(self, payload: ToolPreInvokePayload) -> bool:
226+
"""Custom blocking logic."""
227+
# Implement your validation logic here
228+
return False
229+
230+
def _transform_payload(self, payload: ToolPreInvokePayload) -> ToolPreInvokePayload:
231+
"""Transform payload if needed."""
232+
return payload
233+
```
234+
235+
### 4. Register Your Plugin
236+
237+
Add to `plugins/config.yaml`:
238+
239+
```yaml
240+
plugins:
241+
- name: "MyCustomPlugin"
242+
kind: "plugins.my_plugin.my_plugin.MyPlugin"
243+
description: "My custom plugin description"
244+
version: "1.0.0"
245+
author: "Your Name"
246+
hooks: ["tool_pre_invoke"]
247+
mode: "enforce"
248+
priority: 100
249+
config:
250+
my_setting: true
251+
threshold: 0.8
252+
```
253+
254+
## Plugin Development Best Practices
255+
256+
### Error Handling
257+
```python
258+
async def tool_pre_invoke(self, payload: ToolPreInvokePayload) -> ToolPreInvokeResult:
259+
try:
260+
# Your plugin logic
261+
result = await self._process_payload(payload)
262+
return ToolPreInvokeResult(result=PluginResult.CONTINUE)
263+
except Exception as e:
264+
self.logger.error(f"Plugin error: {e}")
265+
if self.mode == PluginMode.ENFORCE:
266+
return ToolPreInvokeResult(
267+
result=PluginResult.BLOCK,
268+
message=f"Plugin failed: {e}"
269+
)
270+
return ToolPreInvokeResult(result=PluginResult.CONTINUE)
271+
```
272+
273+
### Logging and Monitoring
274+
```python
275+
def __init__(self, config: PluginConfig):
276+
super().__init__(config)
277+
self.logger.info(f"Initialized {self.name} v{self.version}")
278+
279+
async def tool_pre_invoke(self, payload: ToolPreInvokePayload) -> ToolPreInvokeResult:
280+
self.logger.debug(f"Processing tool: {payload.tool_name}")
281+
# ... plugin logic
282+
self.metrics.increment("requests_processed")
283+
```
284+
285+
### Configuration Validation
286+
```python
287+
def validate_config(self) -> None:
288+
"""Validate plugin configuration."""
289+
required_keys = ["threshold", "api_key"]
290+
for key in required_keys:
291+
if key not in self.config:
292+
raise ValueError(f"Missing required config key: {key}")
293+
294+
if not 0 <= self.config["threshold"] <= 1:
295+
raise ValueError("threshold must be between 0 and 1")
296+
```
297+
298+
## Performance Considerations
299+
300+
### Latency Guidelines
301+
- **Self-contained plugins**: <1ms target
302+
- **External service plugins**: <100ms target
303+
- Use async/await for I/O operations
304+
- Implement timeouts for external calls
305+
306+
### Resource Management
307+
```python
308+
class MyPlugin(Plugin):
309+
def __init__(self, config: PluginConfig):
310+
super().__init__(config)
311+
self._session = None
312+
313+
async def __aenter__(self):
314+
self._session = aiohttp.ClientSession()
315+
return self
316+
317+
async def __aexit__(self, exc_type, exc_val, exc_tb):
318+
if self._session:
319+
await self._session.close()
320+
```
321+
322+
## Testing Plugins
323+
324+
### Unit Testing
325+
```python
326+
import pytest
327+
from mcpgateway.plugins.framework.models import ToolPreInvokePayload, PluginConfig
328+
from plugins.my_plugin.my_plugin import MyPlugin
329+
330+
@pytest.fixture
331+
def plugin():
332+
config = PluginConfig(
333+
name="test_plugin",
334+
config={"my_setting": True}
335+
)
336+
return MyPlugin(config)
337+
338+
async def test_tool_pre_invoke(plugin):
339+
payload = ToolPreInvokePayload(
340+
tool_name="test_tool",
341+
arguments={"arg1": "value1"}
342+
)
343+
344+
result = await plugin.tool_pre_invoke(payload)
345+
assert result.result == PluginResult.CONTINUE
346+
```
347+
348+
### Integration Testing
349+
```bash
350+
# Test with live gateway
351+
make dev
352+
curl -X POST http://localhost:4444/tools/invoke \
353+
-H "Content-Type: application/json" \
354+
-d '{"name": "test_tool", "arguments": {}}'
355+
```
356+
357+
## Troubleshooting
358+
359+
### Common Issues
360+
361+
1. **Plugin not loading**: Check `plugin_dirs` in config and Python import paths
362+
2. **Configuration errors**: Validate YAML syntax and required fields
363+
3. **Performance issues**: Profile plugin execution time and optimize bottlenecks
364+
4. **Hook not triggering**: Verify hook name matches available hooks in manifest
365+
366+
### Debug Mode
367+
```bash
368+
LOG_LEVEL=DEBUG make serve # port 4444
369+
# Or with reloading dev server:
370+
LOG_LEVEL=DEBUG make dev # port 8000
371+
```
372+
373+
## Documentation Links
374+
375+
- **Plugin Usage Guide**: https://ibm.github.io/mcp-context-forge/using/plugins/
376+
- **Plugin Lifecycle**: https://ibm.github.io/mcp-context-forge/using/plugins/lifecycle/
377+
- **API Reference**: Generated from code docstrings
378+
- **Examples**: See `plugins/` directory for complete implementations
379+
380+
## Performance Metrics
381+
382+
The framework supports high-performance operations:
383+
- **1,000+ requests/second** with 5 active plugins
384+
- **Sub-millisecond latency** for self-contained plugins
385+
- **Parallel execution** within priority bands
386+
- **Resource isolation** and timeout protection
387+
388+
## Security Features
389+
390+
- Input validation and sanitization
391+
- Timeout protection for external calls
392+
- Resource limits and quota enforcement
393+
- Error isolation between plugins
394+
- Comprehensive audit logging
395+
- Plugin configuration validation

0 commit comments

Comments
 (0)