Model Context Protocol (MCP)
The Model Context Protocol (MCP) is an open standard that enables secure, controlled communication between AI applications and external tools and data sources. Developed by Anthropic, MCP provides a standardized way for language models to interact with tools, APIs, databases, file systems, and other external resources in a safe and predictable manner.
In this chapter, weβll explore how to use MCP with Genkit Go in two key scenarios:
- Building MCP clients that connect to existing MCP servers to access their tools
- Creating MCP servers that expose your custom tools to MCP-compatible clients
Prerequisites
Before diving into MCP, make sure you have the following prerequisites:
- A basic understanding of Genkit Go and how to create AI applications
- Familiarity with Go programming language
- An MCP server to connect to (weβll use the filesystem MCP server in our example)
- AWS Account with access to Bedrock (if using Bedrock plugin)
- Node.js installed for MCP inspector tool and MCP filesystem server
Understanding MCP
MCP acts as a bridge between AI applications and external systems, providing several key benefits:
- Standardization: A common protocol for tool integration across different AI platforms
- Security: Controlled access to external resources with proper boundaries
- Interoperability: Tools can be shared across different AI applications
- Flexibility: Support for various transport mechanisms (stdio, SSE)
MCP Architecture
The MCP architecture consists of three main components:
- MCP Clients: Applications that want to access tools (like your Genkit app)
- MCP Servers: Services that expose tools and capabilities
- Transport Layer: Communication mechanism between clients and servers
Connecting to MCP Servers (Client side)
Letβs start by building a Genkit application that connects to an existing MCP server. In our example, weβll connect to the filesystem MCP server to enable AI-powered file operations. The filesystem MCP server is an npm package that implements the Model Context Protocol. It is available at @modelcontextprotocol/server-filesystem
developed by Anthropic.
Setting Up the MCP Client
Our client application will connect to the filesystem MCP server and use its tools to perform file operations through AI assistance.
These are the main steps to set up the MCP client:
- Initialize Genkit with the AWS Bedrock plugin to handle AI interactions.
- Configure the MCP filesystem server to expose file system operations.
- Create an MCP manager that manages the MCP servers and retrieves available tools.
- Use the tools from the MCP server in a Genkit flow to perform file operations.
- Create a Genkit flow that uses the tools provided by the MCP server.
Hereβs the main structure of our AI Application that connects to the MCP server. It is important to note that we are reusing the flow OperatingSystemFlow
from the previous chapter but instead of using the tools directly, we are using the tools provided by the MCP server:
package main
import (
"context"
"log"
"mastering-genkit-go/example/chapter-09/mcp-client/internal/flows"
"mastering-genkit-go/example/chapter-09/mcp-client/internal/tools"
mcpinternal "mastering-genkit-go/example/chapter-09/mcp-client/internal/mcp"
"net/http"
"os"
"github.com/firebase/genkit/go/genkit"
"github.com/firebase/genkit/go/plugins/mcp"
"github.com/firebase/genkit/go/plugins/server"
bedrock "github.com/xavidop/genkit-aws-bedrock-go"
)
func main() {
ctx := context.Background()
bedrockPlugin := &bedrock.Bedrock{
Region: "us-east-1",
}
// Initialize Genkit with the AWS Bedrock plugin and Haiku model.
g := genkit.Init(ctx,
genkit.WithPlugins(bedrockPlugin),
genkit.WithDefaultModel("bedrock/anthropic.claude-3-haiku-20240307-v1:0"), // Set default model
)
bedrock.DefineCommonModels(bedrockPlugin, g)
// Get the MCPClient for file operations
mcpFileSystem := mcpinternal.NewFilesystemServerConfig("file-system", "./")
// Create the MCP manager with the file system server
manager, err := mcpinternal.NewMCPHostWrapper(g, "my-manager", "1.0.0", []mcp.MCPServerConfig{
mcpFileSystem,
})
if err != nil {
log.Fatalf("Failed to create MCP manager: %v", err)
}
// Get all active tools from the MCP manager
toolList, err := manager.GetActiveTools(ctx, g)
if err != nil {
log.Fatalf("Failed to get active tools: %v", err)
}
// Create a flow that uses MCP tools
operatingSystemFlow := flows.NewOperatingSystemFlow(g, tools.ConvertToolsToToolRefs(toolList))
// Set up HTTP server
mux := http.NewServeMux()
mux.HandleFunc("POST /operatingSystemFlow", genkit.Handler(operatingSystemFlow))
port := os.Getenv("PORT")
if port == "" {
port = "9090"
}
log.Printf("Starting server on 127.0.0.1:%s", port)
log.Fatal(server.Start(ctx, "0.0.0.0:"+port, mux))
}
The structure of our AI Application that connects to the MCP server is as follows:
main.go
βββ internal
β βββ flows
β β βββ os.go
β βββ mcp
β β βββ manager.go
β β βββ clients.go
β βββ tools
β βββ utils.go
βββ go.mod
understanding the structure:
main.go
: The entry point of the application that initializes Genkit, configures the MCP server, and starts the HTTP server.internal/flows/os.go
: Contains the Genkit flow that uses the MCP tools for file operations.internal/mcp/manager.go
: Contains the MCP manager wrapper that simplifies MCP server management.internal/mcp/clients.go
: Contains helper functions to configure MCP servers.internal/tools/utils.go
: Contains utility functions to convert GenkitTool
to GenkitToolRef
.
Letβs break down the helper functions and configurations used in this example in the following sections.
Creating MCP Manager Wrapper
To simplify MCP integration from the main application, we create a wrapper that handles server configuration:
package mcp
import (
"fmt"
"log"
"github.com/firebase/genkit/go/plugins/mcp"
)
func NewMCPHostWrapper(g *genkit.Genkit, name string, version string, mcpServers []mcp.MCPServerConfig) (*mcp.MCPHost, error) {
if version == "" {
version = "1.0.0"
}
options := mcp.MCPHostOptions{
Name: name,
Version: version,
MCPServers: mcpServers,
}
manager, err := mcp.NewMCPHost(g, options)
if err != nil {
return nil, fmt.Errorf("failed to create MCP manager: %w", err)
}
log.Printf("Successfully created MCP manager '%s' (v%s) with %d servers", name, version, len(mcpServers))
return manager, nil
}
As You can see above, we create a NewMCPHostWrapper
function that initializes the MCP host with the provided servers. This allows us to easily manage multiple MCP servers in our application. It needs:
- A name for the host
- A version string (defaulting to β1.0.0β)
- A list of MCP server configurations
Configuring MCP Servers
The filesystem server configuration is handled by a helper function:
package mcp
import (
"github.com/firebase/genkit/go/plugins/mcp"
)
func NewFilesystemServerConfig(name string, allowedDirs ...string) mcp.MCPServerConfig {
args := []string{"-y", "@modelcontextprotocol/server-filesystem"}
args = append(args, allowedDirs...)
return mcp.MCPServerConfig{
Name: name,
Config: mcp.MCPClientOptions{
Name: name,
Stdio: &mcp.StdioConfig{
Command: "npx",
Args: args,
},
},
}
}
This wrapper function creates a configuration for the filesystem MCP server, allowing us to specify which directories are accessible. It uses npx
to run the MCP filesystem server package. Make it easier and more flexible to configure the MCP server in our application.
Debugging the tools exposed by the MCP server
Thanks to the Genkit UI, you can easily inspect the tools exposed by the MCP server that we are connecting to. This allows you to see the available tools, their schemas, and test them interactively.
And you can go further and debug the tools from the MCP server directly from the UI. This is very useful to understand how the tools work, what parameters they accept, and how to use them effectively in your AI application.
Testing the AI Application
Once your AI application is running, you can invoke the Genkit flow and test it with various file operations:
# Read a file
curl -X POST http://127.0.0.1:9090/operatingSystemFlow \
-H "Content-Type: application/json" \
-d '{"data":"Read the contents of README.md and summarize it"}'
# Create a file
curl -X POST http://127.0.0.1:9090/operatingSystemFlow \
-H "Content-Type: application/json" \
-d '{"data":"Create a file called test.txt with the content Hello from MCP!"}'
# List directories
curl -X POST http://127.0.0.1:9090/operatingSystemFlow \
-H "Content-Type: application/json" \
-d '{"data":"List all directories in the current folder"}'
Or just using the Genkit CLI, to run the flow directly make sure you are running your AI Application in another terminal with genkit start -- go run .
:
cd src/examples/chapter-09/mcp-client
genkit flow:run operatingSystemFlow "\"give me all the directories with size in /\""
Here is a full example of how we can interact the flow and invoking the tools from the MCP server from the UI:
Building MCP Servers
Now letβs explore how to create your own MCP server that exposes custom tools to MCP clients. This is useful when you want to share your tools with other AI applications or when building specialized tool suites.
MCP Server Structure
Our MCP server will expose three main tools, we are going to reuse the tools from the previous chapter. Hereβs the main server implementation:
- Directory listing
- Directory creation
- System information gathering
The structure of this MCP server is as follows:
main.go
βββ internal
β βββ mcp
β β βββ server.go
β βββ tools
β βββ directories.go
βββ go.mod
Letβs break down each file and its purpose:
main.go
: The entry point of the application that initializes Genkit, creates the MCP server, and starts it.internal/mcp/server.go
: Contains the MCP server wrapper that simplifies MCP server creation.internal/tools/directories.go
: Contains the implementation of the tools that are going to be exposed by the MCP server.
package main
import (
"context"
"log"
mcpinternal "mastering-genkit-go/example/chapter-09/mcp-server/internal/mcp"
"mastering-genkit-go/example/chapter-09/mcp-server/internal/tools"
"github.com/firebase/genkit/go/ai"
"github.com/firebase/genkit/go/genkit"
)
func main() {
ctx := context.Background()
// Initialize Genkit
g := genkit.Init(ctx)
// Create MCP server with custom tools
mcpServer := mcpinternal.NewMCPServer(g, "file-system", "1.0.0", []ai.Tool{
tools.NewListDirectories(g),
tools.NewCreateDirectory(g),
tools.NewSystemInfo(g),
})
// Start the MCP server
log.Println("Starting MCP server...")
err := mcpServer.ServeStdio()
if err != nil {
log.Fatalf("could not start MCP server: %v", err)
}
}
Our MCP server uses the genkit.Init
function to initialize the Genkit application and then creates an MCP server with custom tools. The tools are defined in the tools
package, which we will implement next. In this cases we are exposing our MCP Server using the ServeStdio
method, which allows it to communicate over standard input/output. This is a common transport mechanism for MCP servers.
If you want to expose your MCP server over HTTP streaming using Server-Sent Events (SSE), you can use the ServeSSE
method instead:
err := mcpServer.ServeSSE(ctx, "http://localhost:3000/sse")
if err != nil {
log.Fatalf("could not start MCP server: %v", err)
}
MCP Server Wrapper
We create a simplified wrapper for MCP server creation:
package mcp
import (
"log"
"github.com/firebase/genkit/go/ai"
"github.com/firebase/genkit/go/genkit"
"github.com/firebase/genkit/go/plugins/mcp"
)
func NewMCPServer(g *genkit.Genkit, name string, version string, tools []ai.Tool) *mcp.GenkitMCPServer {
if version == "" {
version = "1.0.0"
}
options := mcp.MCPServerOptions{
Name: name,
Version: version,
}
// If specific tools are provided, set them in options
for _, tool := range tools {
log.Printf("Exposing tool: %s", tool.Name())
}
return mcp.NewMCPServer(g, options)
}
This simplifies the MCP server creation process by calling mcp.NewMCPServer
, allowing you to easily configure the server with a name, version, and list of tools.
Testing Your MCP Server
To test your MCP server, you can use the MCP inspector tool from Anthropic:
npx @modelcontextprotocol/inspector go run main.go
This opens a web interface where you can:
- View available tools and their schemas
- Test tool execution interactively
- Debug MCP protocol communications
- Validate server compliance
Integrating with MCP Clients
Once your server is running, you can integrate it with various MCP clients for example Claude Desktop or Gemini CLI.
Claude Desktop Integration
Add to your Claude Desktop configuration file (claude_desktop_config.json
):
{
"mcpServers": {
"genkit-file-system": {
"command": "go",
"args": ["run", "main.go"],
"cwd": "/path/to/your/mcp-server"
}
}
}
Gemini CLI Integration
Add to your Gemini CLI configuration file (~/.gemini/settings.json
):
{
"theme": "Default",
"selectedAuthType": "oauth-personal",
"mcpServers": {
"genkit-file-system": {
"command": "go",
"args": ["run", "main.go", "/path/to/your/mcp-server"],
}
}
}
Security Considerations
- Validate Inputs: Always validate tool inputs to prevent malicious usage
- Limit Access: Restrict file system access to specific directories
- Sanitize Paths: Prevent directory traversal attacks
- Rate Limiting: Implement rate limiting for resource-intensive operations
- Authentication: Use secure authentication mechanisms for MCP servers
Conclusion
The Model Context Protocol provides a standardized way to integrate AI applications with external tools and services. With Genkit Goβs MCP support, you can:
- Connect to existing MCP servers to leverage pre-built tools and capabilities
- Build custom MCP servers to expose your specialized tools to the MCP ecosystem
- Create secure, interoperable AI applications that can work with various MCP clients
Whether youβre building AI-powered development tools, automation systems, or custom integrations, MCP provides the foundation for safe, standardized tool sharing across the AI ecosystem.
In our examples, weβve seen how to create both MCP clients that consume tools from external servers and MCP servers that expose custom functionality. This dual capability makes Genkit Go applications both consumers and providers in the MCP ecosystem, enabling rich, interconnected AI tool networks.