This example demonstrates how to use the Model Context Protocol (MCP) functionality in Serpent to create a command-line tool that can also be used as an MCP server.
The Model Context Protocol (MCP) is a protocol for communication between AI models and external tools or resources. It allows AI models to invoke tools and access resources provided by MCP servers.
You can run the example as a normal CLI tool:
# Echo a message
go run main.go echo "Hello, World!"
# Get version information
go run main.go version
# Show help
go run main.go --help
You can run the example as an MCP server using the mcp
subcommand:
go run main.go mcp
This will start an MCP server that listens on stdin/stdout for JSON-RPC 2.0 requests.
The MCP server follows the standard MCP lifecycle:
- The client sends an
initialize
request to the server - The server responds with its capabilities
- The client sends an
initialized
notification - After this, normal message exchange can begin
All MCP methods will return an error if called before the initialization process is complete.
The MCP server implements the following JSON-RPC 2.0 methods:
initialize
: Initializes the MCP server and returns its capabilitiesnotifications/initialized
: Notifies the server that initialization is completeping
: Simple ping method to check server availabilitytools/list
: Lists all available toolstools/call
: Invokes a tool with the given argumentsresources/list
: Lists all available resourcesresources/templates/list
: Lists all available resource templatesresources/read
: Accesses a resource with the given URI
Here are some example JSON-RPC 2.0 requests you can send to the MCP server:
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"manual-test-client","version":"1.0.0"},"capabilities":{}}}
Response:
{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"tools":true,"resources":true}}}
{"jsonrpc":"2.0","id":2,"method":"notifications/initialized"}
{"jsonrpc":"2.0","id":3,"method":"tools/list","params":{}}
{"jsonrpc":"2.0","id":4,"method":"resources/list","params":{}}
{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"echo","arguments":{"_":"Hello from MCP!"}}}
{"jsonrpc":"2.0","id":6,"method":"resources/read","params":{"uri":"version"}}
Here's a complete example of the initialization process:
// Client sends initialize request
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"manual-test-client","version":"1.0.0"},"capabilities":{}}}
// Server responds with capabilities
{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"tools":true,"resources":true}}}
// Client sends initialized notification
{"jsonrpc":"2.0","id":2,"method":"notifications/initialized"}
// Server acknowledges (optional, since initialized is technically a notification)
{"jsonrpc":"2.0","id":2,"result":{}}
// Now client can use MCP methods
{"jsonrpc":"2.0","id":3,"method":"tools/list","params":{}}
To implement MCP in your own Serpent commands:
- Add the
Tool
field to commands that should be invokable as MCP tools - Add the
Resource
field to commands that should be accessible as MCP resources - Add the MCP command to your root command using
root.AddMCPCommand()
Example:
// Create a command that will be exposed as an MCP tool
echoCmd := &serpent.Command{
Use: "echo [message]",
Short: "Echo a message",
Tool: "echo", // This makes the command available as an MCP tool
Handler: func(inv *serpent.Invocation) error {
// Command implementation
},
}
// Create a command that will be exposed as an MCP resource
versionCmd := &serpent.Command{
Use: "version",
Short: "Get version information",
Resource: "version", // This makes the command available as an MCP resource
Handler: func(inv *serpent.Invocation) error {
// Command implementation
},
}
// Add the MCP command to the root command
root.AddSubcommands(serpent.MCPCommand())
- A command can have either a
Tool
field or aResource
field, but not both - Commands with neither
Tool
norResource
set will not be accessible via MCP - The MCP server communicates using JSON-RPC 2.0 over stdin/stdout