|  | 
|  | 1 | +# Serpent MCP Server Example | 
|  | 2 | + | 
|  | 3 | +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. | 
|  | 4 | + | 
|  | 5 | +## What is MCP? | 
|  | 6 | + | 
|  | 7 | +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. | 
|  | 8 | + | 
|  | 9 | +## How to Use | 
|  | 10 | + | 
|  | 11 | +### Running as a CLI Tool | 
|  | 12 | + | 
|  | 13 | +You can run the example as a normal CLI tool: | 
|  | 14 | + | 
|  | 15 | +```bash | 
|  | 16 | +# Echo a message | 
|  | 17 | +go run main.go echo "Hello, World!" | 
|  | 18 | + | 
|  | 19 | +# Get version information | 
|  | 20 | +go run main.go version | 
|  | 21 | + | 
|  | 22 | +# Show help | 
|  | 23 | +go run main.go --help | 
|  | 24 | +``` | 
|  | 25 | + | 
|  | 26 | +### Running as an MCP Server | 
|  | 27 | + | 
|  | 28 | +You can run the example as an MCP server using the `mcp` subcommand: | 
|  | 29 | + | 
|  | 30 | +```bash | 
|  | 31 | +go run main.go mcp | 
|  | 32 | +``` | 
|  | 33 | + | 
|  | 34 | +This will start an MCP server that listens on stdin/stdout for JSON-RPC 2.0 requests. | 
|  | 35 | + | 
|  | 36 | +## MCP Protocol | 
|  | 37 | + | 
|  | 38 | +### Lifecycle | 
|  | 39 | + | 
|  | 40 | +The MCP server follows the standard MCP lifecycle: | 
|  | 41 | + | 
|  | 42 | +1. The client sends an `initialize` request to the server | 
|  | 43 | +2. The server responds with its capabilities | 
|  | 44 | +3. The client sends an `initialized` notification | 
|  | 45 | +4. After this, normal message exchange can begin | 
|  | 46 | + | 
|  | 47 | +All MCP methods will return an error if called before the initialization process is complete. | 
|  | 48 | + | 
|  | 49 | +### Methods | 
|  | 50 | + | 
|  | 51 | +The MCP server implements the following JSON-RPC 2.0 methods: | 
|  | 52 | + | 
|  | 53 | +- `initialize`: Initializes the MCP server and returns its capabilities | 
|  | 54 | +- `notifications/initialized`: Notifies the server that initialization is complete | 
|  | 55 | +- `ping`: Simple ping method to check server availability | 
|  | 56 | +- `tools/list`: Lists all available tools | 
|  | 57 | +- `tools/call`: Invokes a tool with the given arguments | 
|  | 58 | +- `resources/list`: Lists all available resources | 
|  | 59 | +- `resources/templates/list`: Lists all available resource templates | 
|  | 60 | +- `resources/read`: Accesses a resource with the given URI | 
|  | 61 | + | 
|  | 62 | +### Example Requests | 
|  | 63 | + | 
|  | 64 | +Here are some example JSON-RPC 2.0 requests you can send to the MCP server: | 
|  | 65 | + | 
|  | 66 | +#### Initialize | 
|  | 67 | + | 
|  | 68 | +```json | 
|  | 69 | +{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"manual-test-client","version":"1.0.0"},"capabilities":{}}} | 
|  | 70 | +``` | 
|  | 71 | + | 
|  | 72 | +Response: | 
|  | 73 | +```json | 
|  | 74 | +{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"tools":true,"resources":true}}} | 
|  | 75 | +``` | 
|  | 76 | + | 
|  | 77 | +#### Initialized | 
|  | 78 | + | 
|  | 79 | +```json | 
|  | 80 | +{"jsonrpc":"2.0","id":2,"method":"notifications/initialized"} | 
|  | 81 | +``` | 
|  | 82 | + | 
|  | 83 | +#### List Tools | 
|  | 84 | + | 
|  | 85 | +```json | 
|  | 86 | +{"jsonrpc":"2.0","id":3,"method":"tools/list","params":{}} | 
|  | 87 | +``` | 
|  | 88 | + | 
|  | 89 | +#### List Resources | 
|  | 90 | + | 
|  | 91 | +```json | 
|  | 92 | +{"jsonrpc":"2.0","id":4,"method":"resources/list","params":{}} | 
|  | 93 | +``` | 
|  | 94 | + | 
|  | 95 | +#### Invoke Tool | 
|  | 96 | + | 
|  | 97 | +```json | 
|  | 98 | +{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"echo","arguments":{"_":"Hello from MCP!"}}} | 
|  | 99 | +``` | 
|  | 100 | + | 
|  | 101 | +#### Access Resource | 
|  | 102 | + | 
|  | 103 | +```json | 
|  | 104 | +{"jsonrpc":"2.0","id":6,"method":"resources/read","params":{"uri":"version"}} | 
|  | 105 | +``` | 
|  | 106 | + | 
|  | 107 | +### Complete Initialization Example | 
|  | 108 | + | 
|  | 109 | +Here's a complete example of the initialization process: | 
|  | 110 | + | 
|  | 111 | +```json | 
|  | 112 | +// Client sends initialize request | 
|  | 113 | +{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"manual-test-client","version":"1.0.0"},"capabilities":{}}} | 
|  | 114 | + | 
|  | 115 | +// Server responds with capabilities | 
|  | 116 | +{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"tools":true,"resources":true}}} | 
|  | 117 | + | 
|  | 118 | +// Client sends initialized notification | 
|  | 119 | +{"jsonrpc":"2.0","id":2,"method":"notifications/initialized"} | 
|  | 120 | + | 
|  | 121 | +// Server acknowledges (optional, since initialized is technically a notification) | 
|  | 122 | +{"jsonrpc":"2.0","id":2,"result":{}} | 
|  | 123 | + | 
|  | 124 | +// Now client can use MCP methods | 
|  | 125 | +{"jsonrpc":"2.0","id":3,"method":"tools/list","params":{}} | 
|  | 126 | +``` | 
|  | 127 | + | 
|  | 128 | +## How to Implement MCP in Your Own Commands | 
|  | 129 | + | 
|  | 130 | +To implement MCP in your own Serpent commands: | 
|  | 131 | + | 
|  | 132 | +1. Add the `Tool` field to commands that should be invokable as MCP tools | 
|  | 133 | +2. Add the `Resource` field to commands that should be accessible as MCP resources | 
|  | 134 | +3. Add the MCP command to your root command using `root.AddMCPCommand()` | 
|  | 135 | + | 
|  | 136 | +Example: | 
|  | 137 | + | 
|  | 138 | +```go | 
|  | 139 | +// Create a command that will be exposed as an MCP tool | 
|  | 140 | +echoCmd := &serpent.Command{ | 
|  | 141 | +    Use:   "echo [message]", | 
|  | 142 | +    Short: "Echo a message", | 
|  | 143 | +    Tool:  "echo", // This makes the command available as an MCP tool | 
|  | 144 | +    Handler: func(inv *serpent.Invocation) error { | 
|  | 145 | +        // Command implementation | 
|  | 146 | +    }, | 
|  | 147 | +} | 
|  | 148 | + | 
|  | 149 | +// Create a command that will be exposed as an MCP resource | 
|  | 150 | +versionCmd := &serpent.Command{ | 
|  | 151 | +    Use:      "version", | 
|  | 152 | +    Short:    "Get version information", | 
|  | 153 | +    Resource: "version", // This makes the command available as an MCP resource | 
|  | 154 | +    Handler: func(inv *serpent.Invocation) error { | 
|  | 155 | +        // Command implementation | 
|  | 156 | +    }, | 
|  | 157 | +} | 
|  | 158 | + | 
|  | 159 | +// Add the MCP command to the root command | 
|  | 160 | +root.AddSubcommands(serpent.MCPCommand()) | 
|  | 161 | +``` | 
|  | 162 | + | 
|  | 163 | +## Notes | 
|  | 164 | + | 
|  | 165 | +- A command can have either a `Tool` field or a `Resource` field, but not both | 
|  | 166 | +- Commands with neither `Tool` nor `Resource` set will not be accessible via MCP | 
|  | 167 | +- The MCP server communicates using JSON-RPC 2.0 over stdin/stdout | 
0 commit comments