|
| 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