Documentation
¶
Overview ¶
Package mcp connects to Model Context Protocol servers and exposes their tools through the unchanged Tool port (change 0015, ADR-0010).
This package is the only place in OpenPlus that knows the MCP wire protocol: JSON-RPC 2.0 over either a stdio subprocess or streamable HTTP. Everything it hands the rest of the system is a neutral tool.Tool. It is written against the standard library only, so the build stays cgo-free.
Security: an MCP server is arbitrary code (stdio) or an arbitrary endpoint (http) chosen by the user's config. Its tools are registered like any other, so they pass the PolicyGate — that gate is the only in-process mitigation, and MCP tools must never bypass it.
Index ¶
- Constants
- func DecodeFrame(r *bufio.Reader, msg any) error
- func EncodeFrame(w io.Writer, msg any) error
- func NewHTTP(cfg HTTPConfig) *httpTransport
- func NewStdio(ctx context.Context, cfg StdioConfig) (*stdioTransport, error)
- type Client
- func (c *Client) CallTool(ctx context.Context, name string, args json.RawMessage) (string, error)
- func (c *Client) Close() error
- func (c *Client) Initialize(ctx context.Context) error
- func (c *Client) ListTools(ctx context.Context) ([]ToolDesc, error)
- func (c *Client) ServerInfo() string
- func (c *Client) Tools(ctx context.Context) ([]tool.Tool, error)
- type Error
- type HTTPConfig
- type Request
- type Response
- type StdioConfig
- type ToolDesc
- type Transport
Constants ¶
const ClientName = "openplus"
ClientName identifies OpenPlus to servers in the handshake.
const DefaultHTTPTimeout = 2 * time.Minute
DefaultHTTPTimeout bounds one streamable-HTTP call when the caller's context carries no deadline. A server that never answers must not hold a turn open forever.
const ProtocolVersion = "2025-06-18"
ProtocolVersion is the MCP revision this client speaks. A server that needs a different one says so in its initialize result; we report the mismatch rather than guessing at compatibility.
const ToolNameSeparator = "."
ToolNameSeparator joins a server name and its tool name. Namespacing is not cosmetic: two servers may both expose "search", and a permission rule is written against the full name.
const Version = "2.0"
Version is the JSON-RPC version every message carries.
Variables ¶
This section is empty.
Functions ¶
func DecodeFrame ¶
DecodeFrame reads one newline-delimited JSON message into msg, skipping blank padding lines. A malformed frame is an error rather than a zero value, so a caller cannot mistake garbage for an empty result.
func EncodeFrame ¶
EncodeFrame writes one newline-delimited JSON message. Compact encoding matters: an embedded newline would split one message across two frames.
func NewHTTP ¶
func NewHTTP(cfg HTTPConfig) *httpTransport
NewHTTP returns a Transport for a streamable-HTTP endpoint. Nothing is contacted until the first call, so this cannot fail.
func NewStdio ¶
func NewStdio(ctx context.Context, cfg StdioConfig) (*stdioTransport, error)
NewStdio starts the configured subprocess and returns a Transport for it. A command that cannot start is an error here rather than at the first call, so a typo in config surfaces at assemble time.
The subprocess is stopped when ctx is cancelled or Close is called — whichever happens first. Cancelling ctx is the session-teardown path.
Types ¶
type Client ¶
type Client struct {
// Name is the config key for this server; it prefixes its tool names and
// appears in every error, so a failure points at a specific server.
Name string
// contains filtered or unexported fields
}
Client speaks MCP to one server.
func NewClient ¶
NewClient wraps a Transport as an MCP client. The caller must call Initialize before listing or calling tools.
func (*Client) CallTool ¶
CallTool invokes one tool and returns its content as text.
Two distinct failures both surface as errors: a JSON-RPC error (the call did not happen) and an isError result (the tool ran and reported failure). The loop needs to see both as failures, so neither is returned as ordinary output.
func (*Client) Initialize ¶
Initialize performs the MCP handshake: an initialize request, then the initialized notification. The notification is only sent once the server has answered — sending it early would announce readiness we do not have.
func (*Client) ListTools ¶
ListTools returns the server's advertised tools. It refuses before the handshake rather than sending a request the server is entitled to reject.
func (*Client) ServerInfo ¶
ServerInfo reports the server's self-reported name and version, empty before the handshake.
func (*Client) Tools ¶
Tools lists the server's tools and adapts them to the Tool port.
A tool whose schema cannot be translated fails the whole listing rather than being dropped: a user who configured a server expects its tools, and silently serving a subset hides the problem until the model tries to call the missing one.
type Error ¶
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
Data json.RawMessage `json:"data,omitempty"`
}
Error is a JSON-RPC 2.0 error object.
type HTTPConfig ¶
type HTTPConfig struct {
// URL is the MCP endpoint. Required.
URL string
// Headers are sent on every request (an Authorization header, typically).
Headers map[string]string
// Client overrides the HTTP client. Nil uses a default one.
Client *http.Client
}
HTTPConfig describes a streamable-HTTP MCP endpoint.
type Request ¶
type Request struct {
JSONRPC string `json:"jsonrpc"`
ID json.RawMessage `json:"id,omitempty"`
Method string `json:"method"`
Params json.RawMessage `json:"params,omitempty"`
}
Request is a JSON-RPC 2.0 request or notification. A notification has no ID and expects no response.
type Response ¶
type Response struct {
JSONRPC string `json:"jsonrpc"`
ID json.RawMessage `json:"id,omitempty"`
Result json.RawMessage `json:"result,omitempty"`
Error *Error `json:"error,omitempty"`
}
Response is a JSON-RPC 2.0 response. Exactly one of Result and Error is set.
type StdioConfig ¶
type StdioConfig struct {
// Command is the executable to run. Required.
Command string
// Args are its arguments.
Args []string
// Env is added to the child's environment as "K=V" entries. The parent's
// environment is inherited, because these servers routinely need PATH, HOME
// and a credential the user already exported.
Env []string
// Dir is the child's working directory. Empty means the parent's.
Dir string
}
StdioConfig describes a subprocess MCP server.
type ToolDesc ¶
type ToolDesc struct {
Name string `json:"name"`
Description string `json:"description"`
InputSchema json.RawMessage `json:"inputSchema"`
}
ToolDesc is one tool a server advertises.
type Transport ¶
type Transport interface {
Call(ctx context.Context, method string, params json.RawMessage) (json.RawMessage, error)
Notify(ctx context.Context, method string, params json.RawMessage) error
Close() error
}
Transport carries JSON-RPC to one server. Call awaits a response; Notify sends a message that expects none. Both honor ctx: a hung server must never block a turn indefinitely.
Implementations: stdioTransport (subprocess) and httpTransport (streamable HTTP). The Client is transport-agnostic.