MCP is a Go implementation of the Model Context Protocol — a standardized way for applications to communicate with AI models. It allows developers to seamlessly bridge applications and AI models using a lightweight, JSON-RPC–based protocol.
Official Model Context Protocol Specification
MCP (Model Context Protocol) is designed to provide a standardized communication layer between applications and AI models. The protocol simplifies the integration of AI capabilities into applications by offering a consistent interface for resource access, prompt management, model interaction, and tool invocation.
Key features:
- JSON-RPC 2.0 based communication
- Support for multiple transport protocols (HTTP/SSE, stdio)
- Server Side Features
- Resource management capabilities
- Model prompting and completion
- Tool invocation
- Subscriptions for resource updates
- Logging
- Progress reporting
- Request cancellation
- Client Side Features
- Roots
- Sampling
MCP is built around the following components:
- Protocol: Defines the communication format and methods
- Server: Handles incoming requests and dispatches to implementers
- Client: Makes requests to MCP-compatible servers
- Implementer: Provides the actual functionality behind each protocol method
go get github.com/viant/mcp
To create an MCP server, you need to provide an implementer that handles the protocol methods:
package main
import (
"context"
"github.com/viant/mcp/example/fs"
"github.com/viant/mcp/protocol/server"
"github.com/viant/mcp/schema"
"embed"
"log"
)
//go:embed data/*
var embedFs embed.FS
func main() {
// Create a configuration for the filesystem implementer (for this exmple we use go embed file system)
config := &fs.Config{
BaseURL: "embed://data",
}
// Create a new implementer
newImplementer := fs.New(config)
// Configure server options
options := []server.Option{
server.WithNewImplementer(newImplementer),
server.WithImplementation(schema.Implementation{"My MCP Server", "1.0"}),
server.WithCapabilities(schema.ServerCapabilities{
Resources: &schema.ServerCapabilitiesResources{},
}),
}
// Create and start the server
srv, err := server.New(options...)
if err != nil {
log.Fatalf("Failed to create server: %v", err)
}
// Start an HTTP server
ctx := context.Background()
endpoint := srv.HTTP(ctx, ":4981")
log.Fatal(endpoint.ListenAndServe())
}
To connect to an MCP server:
package main
import (
"context"
"fmt"
"github.com/viant/jsonrpc/transport/client/http/sse"
"github.com/viant/mcp/protocol/client"
"github.com/viant/mcp/schema"
"log"
)
func main() {
ctx := context.Background()
// Create a transport (HTTP/SSE in this example)
transport, err := sse.New(ctx, "http://localhost:4981/sse")
if err != nil {
log.Fatalf("Failed to create transport: %v", err)
}
// Create the client
mcpClient := client.New(
"MyClient",
"1.0",
transport,
client.WithCapabilities(schema.ClientCapabilities{}),
)
// Initialize the client
result, err := mcpClient.Initialize(ctx)
if err != nil {
log.Fatalf("Failed to initialize: %v", err)
}
fmt.Printf("Connected to %s %s\n", result.ServerInfo.Name, result.ServerInfo.Version)
// List resources
resources, err := mcpClient.ListResources(ctx, nil)
if err != nil {
log.Fatalf("Failed to list resources: %v", err)
}
for _, resource := range resources.Resources {
fmt.Printf("Resource: %s (%s)\n", resource.Name, resource.Uri)
}
}
Implementers provide the actual functionality behind the MCP protocol. Here's a simple example of creating a custom implementer:
package myimplementer
import (
"context"
"encoding/base64"
"github.com/viant/afs"
_ "github.com/viant/afs/embed"
"mime"
"github.com/viant/jsonrpc"
"github.com/viant/jsonrpc/transport"
"github.com/viant/mcp/implementer"
"github.com/viant/mcp/logger"
"github.com/viant/mcp/protocol/client"
"github.com/viant/mcp/protocol/server"
"github.com/viant/mcp/schema"
)
type MyImplementer struct {
*implementer.Base
fs afs.Service
// Add your custom fields here
}
func (i *MyImplementer) ListResources(ctx context.Context, request *schema.ListResourcesRequest) (*schema.ListResourcesResult, *jsonrpc.Error) {
objects, err := i.fs.List(ctx, i.config.BaseURL, i.config.Options...)
if err != nil {
return nil, jsonrpc.NewInternalError(err.Error(), nil)
}
var resources []schema.Resource
for _, object := range objects {
if object.IsDir() {
continue
}
name := object.Name()
ext := filepath.Ext(name)
mimeType := mime.TypeByExtension(ext)
if mimeType == "" {
mimeType = "application/octet-stream"
}
resource := &schema.Resource{
Name: name,
MimeType: &mimeType,
Uri: object.URL(),
}
resources = append(resources, *resource)
}
return &schema.ListResourcesResult{Resources: resources}, nil
}
func (i *MyImplementer) ReadResource(ctx context.Context, request *schema.ReadResourceRequest) (*schema.ReadResourceResult, *jsonrpc.Error) {
object, err := i.fs.Object(ctx, request.Params.Uri)
if err != nil {
return nil, jsonrpc.NewInternalError(err.Error(), nil)
}
data, err := i.fs.DownloadWithURL(ctx, request.Params.Uri, i.config.Options...)
if err != nil {
return nil, jsonrpc.NewInternalError(err.Error(), nil)
}
name := object.Name()
ext := filepath.Ext(name)
mimeType := mime.TypeByExtension(ext)
var text string
var blob string
if isBinary(data) {
blob = base64.StdEncoding.EncodeToString(data)
} else {
text = string(data)
}
result := schema.ReadResourceResult{}
content := schema.ReadResourceResultContentsElem{
MimeType: &mimeType,
Uri: object.URL(),
Blob: blob,
Text: text,
}
result.Contents = append(result.Contents, content)
return &result, nil
}
func (i *MyImplementer) Implements(method string) bool {
switch method {
case schema.MethodResourcesList, schema.MethodResourcesRead, schema.MethodSubscribe, schema.MethodUnsubscribe:
return true
}
return false
}
func New() server.NewImplementer {
return func(ctx context.Context, notifier transport.Notifier, logger logger.Logger, client client.Operations) server.Implementer {
base := implementer.New(notifier, logger, client)
return &MyImplementer{Base: base, fs: afs.New()}
}
}
The project includes a filesystem implementer example that exposes go embed files through the MCP protocol. This example demonstrates how to create a custom implementer that allows browsing and reading files.
Usage:
config := &fs.Config{
BaseURL: "embed:///resources",
Options: []storage.Option{
resourceFS, // An embedded filesystem
},
}
// Create the implementer
newImplementer := fs.New(config)
// Use with server
srv, _ := server.New(server.WithNewImplementer(newImplementer))
MCP supports the following Server Side methods:
initialize
- Initialize the connectionping
- Check server statusresources/list
- List available resourcesresources/read
- Read resource contentsresources/templates/list
- List resource templatesresources/subscribe
- Subscribe to resource updatesresources/unsubscribe
- Unsubscribe from resource updatesprompts/list
- List available promptsprompts/get
- Get prompt detailstools/list
- List available toolstools/call
- Call a specific toolcomplete
- Get completions from the modellogging/setLevel
- Set logging level
MCP supports the following Client Side methods:
roots/list
- List available rootssampling/createMessage
- A standardized way for servers to request LLM sampling (“completions” or “generations”) from language models via clients.
Work in progress
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the Apache License 2.0.
Author: Adrian Witas
This project is maintained by Viant.