first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 21:52:23 +03:00
commit 880f412e2c
2662 changed files with 866266 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
.PHONY: build clean
build:
@echo "Building MCP-Only plugin..."
@mkdir -p build
@go build -buildmode=plugin -o build/mcp-only.so main.go
@echo "Plugin built successfully: build/mcp-only.so"
clean:
@echo "Cleaning build directory..."
@rm -rf build
@echo "Clean complete"

View File

@@ -0,0 +1,112 @@
# MCP-Only Plugin Example
This example demonstrates a plugin that only implements the `MCPPlugin` interface for Model Context Protocol governance.
## Features
- **PreMCPHook**: Intercepts MCP requests before execution
- Validates tool/resource calls
- Implements governance policies (blocking dangerous tools)
- Adds audit trails
- Can short-circuit calls with custom responses
- **PostMCPHook**: Intercepts MCP responses after execution
- Logs responses
- Transforms error messages
- Accesses audit trails from context
## Use Cases
- **Security & Governance**
- Block unauthorized tool calls
- Enforce access control policies
- Validate tool parameters
- **Observability**
- Log all MCP interactions
- Track tool usage
- Monitor resource access
- **Error Handling**
- Transform error messages
- Add retry logic
- Provide fallback responses
## Building
```bash
make build
```
This creates `build/mcp-only.so`
## Configuration
Add to your Bifrost config:
```json
{
"plugins": [
{
"path": "/path/to/mcp-only.so",
"name": "mcp-only",
"display_name": "MCP Tool Governance",
"enabled": true,
"type": "mcp",
"config": {
"blocked_tools": ["dangerous_tool", "risky_operation"],
"enable_audit": true,
"enable_logging": true,
"transform_errors": true,
"custom_error_message": "Tool is not allowed by security policy"
}
}
]
}
```
**Note:**
- `name` is the system identifier (from `GetName()`) and is **not editable**
- `display_name` is shown in the UI and is **editable** by users
### Configuration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `blocked_tools` | array of strings | `["dangerous_tool"]` | List of tool names to block |
| `enable_audit` | boolean | `true` | Enable audit trail logging |
| `enable_logging` | boolean | `true` | Enable detailed logging |
| `transform_errors` | boolean | `true` | Transform 404 errors to user-friendly messages |
| `custom_error_message` | string | `"Tool is not allowed..."` | Custom error message for blocked tools |
### Example Configurations
**Block multiple tools:**
```json
{
"config": {
"blocked_tools": ["delete_data", "modify_system", "unsafe_exec"],
"custom_error_message": "This tool is disabled for security reasons"
}
}
```
**Minimal logging:**
```json
{
"config": {
"enable_audit": false,
"enable_logging": false,
"transform_errors": false
}
}
```
**Allow all tools:**
```json
{
"config": {
"blocked_tools": []
}
}
```

View File

@@ -0,0 +1,32 @@
module github.com/maximhq/bifrost/examples/plugins/mcp-only
go 1.26.2
replace github.com/maximhq/bifrost/core => ../../../core
require github.com/maximhq/bifrost/core v0.0.0-00010101000000-000000000000
require (
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.2 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.15.0 // indirect
github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/klauspost/compress v1.18.2 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/mailru/easyjson v0.9.1 // indirect
github.com/mark3labs/mcp-go v0.43.2 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.68.0 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
golang.org/x/arch v0.23.0 // indirect
golang.org/x/sys v0.42.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -0,0 +1,80 @@
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=
github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mark3labs/mcp-go v0.43.2 h1:21PUSlWWiSbUPQwXIJ5WKlETixpFpq+WBpbMGDSVy/I=
github.com/mark3labs/mcp-go v0.43.2/go.mod h1:YnJfOL382MIWDx1kMY+2zsRHU/q78dBg9aFb8W6Thdw=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok=
github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,195 @@
package main
import (
"fmt"
"github.com/maximhq/bifrost/core/schemas"
)
// Plugin configuration
type PluginConfig struct {
BlockedTools []string `json:"blocked_tools"` // List of tool names to block
EnableAudit bool `json:"enable_audit"` // Enable audit trail logging
EnableLogging bool `json:"enable_logging"` // Enable detailed logging
TransformErrors bool `json:"transform_errors"` // Transform 404 errors to friendly messages
CustomErrorMessage string `json:"custom_error_message"` // Custom error message for blocked tools
}
var (
// Default configuration
pluginConfig = &PluginConfig{
BlockedTools: []string{"dangerous_tool"},
EnableAudit: true,
EnableLogging: true,
TransformErrors: true,
CustomErrorMessage: "Tool is not allowed by security policy",
}
)
// Init is called when the plugin is loaded (optional)
func Init(config any) error {
fmt.Println("[MCP-Only Plugin] Init called")
// Parse configuration
if configMap, ok := config.(map[string]interface{}); ok {
if blockedTools, ok := configMap["blocked_tools"].([]interface{}); ok {
pluginConfig.BlockedTools = []string{}
for _, tool := range blockedTools {
if toolName, ok := tool.(string); ok {
pluginConfig.BlockedTools = append(pluginConfig.BlockedTools, toolName)
}
}
fmt.Printf("[MCP-Only Plugin] Blocked tools: %v\n", pluginConfig.BlockedTools)
}
if enableAudit, ok := configMap["enable_audit"].(bool); ok {
pluginConfig.EnableAudit = enableAudit
fmt.Printf("[MCP-Only Plugin] Audit trail: %v\n", pluginConfig.EnableAudit)
}
if enableLogging, ok := configMap["enable_logging"].(bool); ok {
pluginConfig.EnableLogging = enableLogging
fmt.Printf("[MCP-Only Plugin] Logging enabled: %v\n", pluginConfig.EnableLogging)
}
if transformErrors, ok := configMap["transform_errors"].(bool); ok {
pluginConfig.TransformErrors = transformErrors
fmt.Printf("[MCP-Only Plugin] Error transformation: %v\n", pluginConfig.TransformErrors)
}
if customMsg, ok := configMap["custom_error_message"].(string); ok {
pluginConfig.CustomErrorMessage = customMsg
}
}
fmt.Printf("[MCP-Only Plugin] Configuration loaded: %+v\n", pluginConfig)
return nil
}
// GetName returns the name of the plugin (required)
// This is the system identifier - not editable by users
// Users can set a custom display_name in the config for the UI
func GetName() string {
return "mcp-only"
}
// PreMCPHook is called before MCP tool/resource calls are executed
// This example demonstrates request validation and governance
func PreMCPHook(ctx *schemas.BifrostContext, req *schemas.BifrostMCPRequest) (*schemas.BifrostMCPRequest, *schemas.MCPPluginShortCircuit, error) {
if pluginConfig.EnableLogging {
fmt.Println("[MCP-Only Plugin] PreMCPHook called")
fmt.Printf("[MCP-Only Plugin] Request type: %v\n", req.RequestType)
}
// Example: Governance - check tool calls (configurable)
if req.ChatAssistantMessageToolCall != nil {
toolName := ""
if req.ChatAssistantMessageToolCall.Function.Name != nil {
toolName = *req.ChatAssistantMessageToolCall.Function.Name
}
if pluginConfig.EnableLogging {
fmt.Printf("[MCP-Only Plugin] Tool call: %s\n", toolName)
}
// Check if tool is in blocked list
for _, blockedTool := range pluginConfig.BlockedTools {
if toolName == blockedTool {
fmt.Printf("[MCP-Only Plugin] Blocked tool call: %s\n", toolName)
// Return a short-circuit response to prevent the call
errorMsg := fmt.Sprintf("%s: %s", pluginConfig.CustomErrorMessage, toolName)
// Get the tool call ID to link the response back to the original call
toolCallID := req.ChatAssistantMessageToolCall.ID
return req, &schemas.MCPPluginShortCircuit{
Response: &schemas.BifrostMCPResponse{
// Chat API format - tool result message
ChatMessage: &schemas.ChatMessage{
Role: schemas.ChatMessageRoleTool,
ChatToolMessage: &schemas.ChatToolMessage{
ToolCallID: toolCallID,
},
Content: &schemas.ChatMessageContent{
ContentStr: &errorMsg,
},
},
// Responses API format - function_call_output
ResponsesMessage: &schemas.ResponsesMessage{
Type: schemas.Ptr(schemas.ResponsesMessageTypeFunctionCallOutput),
ResponsesToolMessage: &schemas.ResponsesToolMessage{
CallID: toolCallID,
Output: &schemas.ResponsesToolMessageOutputStruct{
ResponsesToolCallOutputStr: &errorMsg,
},
},
},
},
}, nil
}
}
}
// Example: Add audit trail to context (configurable)
if pluginConfig.EnableAudit {
auditMsg := fmt.Sprintf("MCP request processed at %v", ctx.Value(schemas.BifrostContextKey("request_id")))
ctx.SetValue(schemas.BifrostContextKey("mcp-audit-trail"), auditMsg)
if pluginConfig.EnableLogging {
fmt.Printf("[MCP-Only Plugin] Audit: %s\n", auditMsg)
}
}
// Return modified request, no short-circuit, no error
return req, nil, nil
}
// PostMCPHook is called after MCP tool/resource calls complete
// This example demonstrates response logging and error handling
func PostMCPHook(ctx *schemas.BifrostContext, resp *schemas.BifrostMCPResponse, bifrostErr *schemas.BifrostError) (*schemas.BifrostMCPResponse, *schemas.BifrostError, error) {
if pluginConfig.EnableLogging {
fmt.Println("[MCP-Only Plugin] PostMCPHook called")
}
// Retrieve audit trail from context (if enabled)
if pluginConfig.EnableAudit {
auditTrail := ctx.Value(schemas.BifrostContextKey("mcp-audit-trail"))
if pluginConfig.EnableLogging {
fmt.Printf("[MCP-Only Plugin] Audit trail: %v\n", auditTrail)
}
}
// Example: Log the response (configurable)
if pluginConfig.EnableLogging && resp != nil {
if resp.ChatMessage != nil {
fmt.Printf("[MCP-Only Plugin] Chat message response received\n")
}
if resp.ResponsesMessage != nil {
fmt.Printf("[MCP-Only Plugin] Responses message received\n")
}
}
// Example: Log errors if present
if bifrostErr != nil && bifrostErr.Error != nil {
fmt.Printf("[MCP-Only Plugin] Error occurred: %v\n", bifrostErr.Error.Message)
}
// Example: Transform error responses (configurable)
if pluginConfig.TransformErrors && bifrostErr != nil && bifrostErr.StatusCode != nil && *bifrostErr.StatusCode == 404 {
// Convert 404 to a more user-friendly error
if bifrostErr.Error != nil {
bifrostErr.Error.Message = "The requested MCP resource was not found. Please check your request."
if pluginConfig.EnableLogging {
fmt.Println("[MCP-Only Plugin] Error message transformed")
}
}
}
// Return modified response and error
return resp, bifrostErr, nil
}
// Cleanup is called when the plugin is unloaded (required)
func Cleanup() error {
if pluginConfig.EnableLogging {
fmt.Println("[MCP-Only Plugin] Cleanup called")
}
return nil
}