-
Notifications
You must be signed in to change notification settings - Fork 195
Open
Labels
apiItems related to the APIItems related to the APIauthenticationenhancementNew feature or requestNew feature or requestgoPull requests that update go codePull requests that update go code
Description
Overview
Implement validating webhook middleware that calls external HTTP services to approve or deny MCP requests. This middleware allows organizations to plug in external policy engines, approval workflows, or rate limiters.
RFC: https://github.com/stacklok/toolhive-rfcs/blob/main/rfcs/THV-0017-dynamic-webhook-middleware.md
Depends on: Phase 1 (Core webhook package)
Files to Create
| File | Purpose |
|---|---|
pkg/webhook/validating/middleware.go |
Validating webhook middleware implementation |
pkg/webhook/validating/config.go |
Configuration types and validation |
Files to Modify
| File | Changes |
|---|---|
pkg/runner/middleware.go |
Register validating-webhook in GetSupportedMiddlewareFactories() |
pkg/runner/config.go |
Add ValidatingWebhooks []webhook.WebhookConfig field to RunConfig |
Middleware Implementation
// pkg/webhook/validating/middleware.go
const MiddlewareType = "validating-webhook"
type MiddlewareParams struct {
Webhooks []webhook.WebhookConfig `json:"webhooks"`
}
type Middleware struct {
client *webhook.Client
webhooks []webhook.WebhookConfig
middleware types.MiddlewareFunction
}
func (m *Middleware) Handler() types.MiddlewareFunction {
return m.middleware
}
func (m *Middleware) Close() error {
return nil
}
func CreateMiddleware(config *types.MiddlewareConfig, runner types.MiddlewareRunner) error {
// Implementation
}Request/Response Format
Request to webhook (POST):
{
"version": "v0.1.0",
"uid": "unique-request-id",
"timestamp": "2025-01-22T10:30:00Z",
"principal": {
"sub": "user123",
"email": "user@example.com",
"groups": ["engineering"]
},
"mcp_request": {
"method": "tools/call",
"params": { "name": "database_query", "arguments": {...} }
},
"context": {
"server_name": "my-mcp-server",
"transport": "sse",
"source_ip": "192.0.2.1"
}
}Response (allowed):
{
"version": "v0.1.0",
"uid": "unique-request-id",
"allowed": true
}Response (denied):
{
"version": "v0.1.0",
"uid": "unique-request-id",
"allowed": false,
"code": 403,
"message": "Production writes require approval",
"reason": "RequiresApproval",
"details": {
"ticket_url": "https://tickets.example.com/PROD-1234"
}
}Middleware Chain Position
Validating webhooks should be placed after MCP Parser and before Authorization:
Auth -> Token Exchange -> Tool Filter -> MCP Parser ->
[Mutating Webhooks] -> [Validating Webhooks] -> Telemetry -> Authorization -> Audit -> Recovery
Failure Policies
| Policy | Behavior on webhook error |
|---|---|
fail (fail-closed) |
Deny request with 403 |
ignore (fail-open) |
Allow request to continue |
Error conditions:
- Network errors
- Timeout
- HTTP 5xx from webhook
- Invalid JSON response
- Missing required fields
Multiple Webhooks
When multiple validating webhooks are configured:
- Execute in configuration order
- If ANY webhook returns
allowed: false, deny the request - If ALL webhooks return
allowed: true, allow the request - Failure policy applies per-webhook
Tests
- Unit tests with mock webhook servers (use
httptest) - Tests for
allowed=trueandallowed=falseresponses - Tests for failure policies (fail-closed, fail-open)
- Tests for multiple webhooks in chain
- Tests for timeout handling
- Tests for invalid response handling
- Integration tests with middleware chain
Acceptance Criteria
- Middleware registered in
GetSupportedMiddlewareFactories() - Correctly calls webhooks with RFC-compliant request format
- Handles
allowed: true/falseresponses correctly - Implements both failure policies
- Supports multiple webhooks in chain
- Comprehensive unit tests with >80% coverage
- Integration test with full middleware chain
- Code passes
task lintandtask test
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
apiItems related to the APIItems related to the APIauthenticationenhancementNew feature or requestNew feature or requestgoPull requests that update go codePull requests that update go code