-
Notifications
You must be signed in to change notification settings - Fork 4
Add Model Context Protocol (MCP) support to Serpent #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
70210af
to
06a74d8
Compare
mcp.go
Outdated
// JSONRPC2 message types | ||
type JSONRPC2Request struct { | ||
JSONRPC string `json:"jsonrpc"` | ||
ID interface{} `json:"id"` | ||
Method string `json:"method"` | ||
Params json.RawMessage `json:"params,omitempty"` | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason we shouldn't import the types from a different package apart from "a little copying is better than a little dependency"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no specific reason. I looked at the MCP spec and just carried the types over.
We could introduce a library for that, but if it doesn't provide any abstraction that makes it easier to work with (e.g., a layer for supporting different spec versions with polyfills), I considered the "copy and be done with it" approach more straightforward.
06a74d8
to
ac46dea
Compare
func MCPCommand() *Command { | ||
return &Command{ | ||
Use: "mcp [command]", | ||
Short: "Run a command as an MCP server", | ||
Long: `Run a command as a Model Context Protocol (MCP) server over stdio. | ||
|
||
This command allows any serpent command to be exposed as an MCP server, which can | ||
provide tools and resources to MCP clients. The server communicates using JSON-RPC 2.0 | ||
over stdin/stdout. | ||
|
||
If a command name is provided, that specific command will be run as an MCP server. | ||
Otherwise, the root command will be used. | ||
|
||
Commands with a Tool field set can be invoked as MCP tools. | ||
Commands with a Resource field set can be accessed as MCP resources. | ||
Commands with neither Tool nor Resource set will not be accessible via MCP.`, | ||
Handler: func(inv *Invocation) error { | ||
rootCmd := inv.Command | ||
if rootCmd.Parent != nil { | ||
// Find the root command | ||
for rootCmd.Parent != nil { | ||
rootCmd = rootCmd.Parent | ||
} | ||
} | ||
|
||
// If a command name is provided, use that as the root | ||
if len(inv.Args) > 0 { | ||
cmdName := strings.Join(inv.Args, " ") | ||
cmd := DefaultCommandFinder(rootCmd, cmdName) | ||
if cmd == nil { | ||
return xerrors.Errorf("command not found: %s", cmdName) | ||
} | ||
rootCmd = cmd | ||
} | ||
|
||
// Create and run the MCP server | ||
server := NewMCPServer(rootCmd, inv.Stdin, inv.Stdout, inv.Stderr) | ||
return server.Run(inv.Context()) | ||
}, | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this is a nice convenience function, isn't this simple enough to just plumb through as required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is, and this can just be put into the docs.
My goal here was to provide a straightforward way of taking another serpent binary and adding a single line to enable MCP support.
- Implement MCP server functionality in a new example application. - Create README.md to document MCP usage, lifecycle, and methods. - Develop main.go to define commands and expose them as MCP tools and resources. - Introduce mcp.go to handle MCP server logic, including JSON-RPC request processing. - Add tests for MCP server setup, command validation, and JSON schema generation. - Update help template to display MCP tool and resource information. Change-Id: I162ad9d8f1c8d4f5b5bdc37fefb1c99dce179bb2 Signed-off-by: Thomas Kosiewski <[email protected]>
ac46dea
to
2de8fee
Compare
command.go
Outdated
// Tool is the name of the MCP tool this command provides. | ||
// If set, the command can be invoked via MCP as a tool. | ||
// Tool and Resource are mutually exclusive. | ||
Tool string | ||
|
||
// Resource is the URI of the MCP resource this command provides. | ||
// If set, the command can be accessed via MCP as a resource. | ||
// Tool and Resource are mutually exclusive. | ||
Resource string | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest maybe either prefix these with MCP
or put them into a struct of MCP-related options.
This idea is so cool ❤️ |
Change-Id: I162ad9d8f1c8d4f5b5bdc37fefb1c99dce179bb2