Go SDK for developing mcpd middleware plugins.
This SDK provides Go types and gRPC interfaces for building middleware plugins that integrate with mcpd
.
Plugins can process HTTP requests and responses, implementing capabilities like authentication, rate limiting,
content transformation, and observability.
go get github.com/mozilla-ai/mcpd-plugins-sdk-go@latest
Then in your project:
go mod tidy
The SDK provides two approaches: with helpers (recommended) for convenience, and explicit for full control.
The SDK provides the Serve()
helper and BasePlugin
struct to minimize boilerplate:
package main
import (
"context"
"log"
pluginv1 "github.com/mozilla-ai/mcpd-plugins-sdk-go/pkg/plugins/v1/plugins"
"google.golang.org/protobuf/types/known/emptypb"
)
type MyPlugin struct {
pluginv1.BasePlugin // Provides sensible defaults for all methods.
}
func (p *MyPlugin) GetMetadata(ctx context.Context, _ *emptypb.Empty) (*pluginv1.Metadata, error) {
return &pluginv1.Metadata{
Name: "my-plugin",
Version: "1.0.0",
Description: "Example plugin that does something useful",
}, nil
}
func (p *MyPlugin) GetCapabilities(ctx context.Context, _ *emptypb.Empty) (*pluginv1.Capabilities, error) {
return &pluginv1.Capabilities{
Flows: []pluginv1.Flow{pluginv1.FlowRequest},
}, nil
}
func (p *MyPlugin) HandleRequest(ctx context.Context, req *pluginv1.HTTPRequest) (*pluginv1.HTTPResponse, error) {
// Custom request processing logic here.
return &pluginv1.HTTPResponse{
Continue: true,
StatusCode: 0,
Headers: req.Headers,
Body: req.Body,
}, nil
}
func main() {
if err := pluginv1.Serve(&MyPlugin{}); err != nil {
log.Fatal(err)
}
}
What BasePlugin
provides:
CheckHealth()
- returns OKCheckReady()
- returns OKConfigure()
,Stop()
- no-opsHandleRequest()
,HandleResponse()
- pass through unchanged
Override only the methods you need!
For full control over the server lifecycle:
package main
import (
"context"
"flag"
"log"
"net"
"os"
pluginv1 "github.com/mozilla-ai/mcpd-plugins-sdk-go/pkg/plugins/v1/plugins"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
)
type MyPlugin struct {
pluginv1.UnimplementedPluginServer
}
func (p *MyPlugin) GetMetadata(ctx context.Context, _ *emptypb.Empty) (*pluginv1.Metadata, error) {
return &pluginv1.Metadata{
Name: "my-plugin",
Version: "1.0.0",
Description: "Example plugin",
}, nil
}
func (p *MyPlugin) GetCapabilities(ctx context.Context, _ *emptypb.Empty) (*pluginv1.Capabilities, error) {
return &pluginv1.Capabilities{
Flows: []pluginv1.Flow{pluginv1.FlowRequest},
}, nil
}
func (p *MyPlugin) CheckHealth(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) {
return &emptypb.Empty{}, nil
}
func (p *MyPlugin) CheckReady(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) {
return &emptypb.Empty{}, nil
}
func (p *MyPlugin) Configure(ctx context.Context, cfg *pluginv1.PluginConfig) (*emptypb.Empty, error) {
return &emptypb.Empty{}, nil
}
func (p *MyPlugin) Stop(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) {
return &emptypb.Empty{}, nil
}
func (p *MyPlugin) HandleRequest(ctx context.Context, req *pluginv1.HTTPRequest) (*pluginv1.HTTPResponse, error) {
return &pluginv1.HTTPResponse{
Continue: true,
Headers: req.Headers,
Body: req.Body,
}, nil
}
func (p *MyPlugin) HandleResponse(ctx context.Context, resp *pluginv1.HTTPResponse) (*pluginv1.HTTPResponse, error) {
return &pluginv1.HTTPResponse{
Continue: true,
StatusCode: resp.StatusCode,
Headers: resp.Headers,
Body: resp.Body,
}, nil
}
func main() {
var address, network string
flag.StringVar(&address, "address", "", "gRPC address (socket path for unix, host:port for tcp)")
flag.StringVar(&network, "network", "unix", "Network type (unix or tcp)")
flag.Parse()
if address == "" {
log.Fatal("--address flag is required")
}
lis, err := net.Listen(network, address)
if err != nil {
log.Fatalf("failed to listen on %s %s: %v", network, address, err)
}
if network == "unix" {
defer func() { _ = os.Remove(address) }()
}
grpcServer := grpc.NewServer()
pluginv1.RegisterPluginServer(grpcServer, &MyPlugin{})
log.Printf("Plugin server listening on %s %s", network, address)
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
Following Kubernetes conventions (e.g., corev1
, appsv1
), this SDK uses pluginv1
as the recommended import alias:
import pluginv1 "github.com/mozilla-ai/mcpd-plugins-sdk-go/pkg/plugins/v1/plugins"
The SDK follows the versioning of mcpd-proto:
- API Version:
plugins/v1/
(in proto repo) maps topkg/plugins/v1/plugins/
(in SDK) - Release Version: Proto repo tags like
v0.0.1
,v0.0.2
, etc. - SDK Version: This repo's tags track SDK releases and may differ from proto versions
Current proto version: v0.0.2
mcpd-plugins-sdk-go/
├── README.md # This file.
├── LICENSE # Apache 2.0 license.
├── Makefile # Proto fetching and code generation.
├── go.mod # Go module definition.
├── go.sum # Dependency checksums.
├── .gitignore # Ignores tmp/ directory.
├── tmp/ # Downloaded protos (gitignored).
└── pkg/
└── plugins/
└── v1/
└── plugins/
├── base.go # BasePlugin helper.
├── server.go # Serve() helper.
├── plugin.pb.go # Generated protobuf types.
└── plugin_grpc.pb.go # Generated gRPC service.
- Go 1.25.1 or later
- protoc (Protocol Buffer Compiler)
- protoc-gen-go and protoc-gen-go-grpc plugins
Install protoc plugins:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
The SDK uses a Makefile-based approach to fetch proto definitions and generate Go code:
Fetch protos and generate:
make all
go mod tidy
Update to a new proto version:
- Edit
PROTO_VERSION
in the Makefile (e.g.,v0.0.2
) - Run
make clean all
- Run
go mod tidy
- Commit the updated generated files
Run linter:
make lint
Clean generated files:
make clean
Note: The clean
target only removes generated .pb.go
files, preserving helper files like base.go
and server.go
.
The SDK follows gRPC Health Checking Protocol conventions:
rpc CheckHealth(google.protobuf.Empty) returns (google.protobuf.Empty);
rpc CheckReady(google.protobuf.Empty) returns (google.protobuf.Empty);
Apache 2.0 - See LICENSE file for details.
This is an early PoC. Contribution guidelines coming soon.