Help us shape the future of Go! Take the 2026 Go Developer Survey

mcp

package
v2.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 9, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

cmd/celeste/tools/mcp/adapter.go

cmd/celeste/tools/mcp/client.go

cmd/celeste/tools/mcp/config.go

cmd/celeste/tools/mcp/discovery.go

cmd/celeste/tools/mcp/jsonrpc.go

cmd/celeste/tools/mcp/manager.go

cmd/celeste/tools/mcp/sse.go

cmd/celeste/tools/mcp/stdio.go

cmd/celeste/tools/mcp/transport.go

Index

Constants

This section is empty.

Variables

View Source
var ErrWorkspaceConfigSkipped = errors.New("skipped workspace MCP config")

ErrWorkspaceConfigSkipped matches (errors.Is) the error of a workspace MCP config that did not load and was skipped: a warning, not a failure, since the other configs' servers still start.

Functions

func DiscoverConfigPaths

func DiscoverConfigPaths(cwd, home string) []string

DiscoverConfigPaths returns the MCP config files that exist on disk, ordered from lowest to highest precedence. Later paths override earlier ones on server-name collision (see LoadMerged). Non-existent candidates, and any that is not a regular file, are skipped, so an empty slice means no MCP config anywhere.

func GlobalConfigPaths

func GlobalConfigPaths(home string) []string

GlobalConfigPaths returns the home-level MCP config candidates, in the precedence order DiscoverConfigPaths uses, whether or not they exist. Any other path DiscoverConfigPaths returns came from the workspace.

func IsGlobalConfig

func IsGlobalConfig(home, path string) bool

IsGlobalConfig reports whether path is one of home's GlobalConfigPaths: the user's own config, whose servers start without approval. With no home, nothing is.

func SetServerEnabled

func SetServerEnabled(path, name string, enabled bool) error

SetServerEnabled flips the `enabled` flag of one server in the config file at path and writes it back, preserving all other fields. Errors if the file or the named server does not exist.

func SplitGlobal

func SplitGlobal(paths []string, home string) (global, workspace []string)

SplitGlobal splits paths (as DiscoverConfigPaths returns them) into the home-level configs every mode loads and the workspace configs only the interactive chat loads, each in its original order. With no home every path is a workspace one.

func ToolName

func ToolName(server, tool string) string

ToolName returns the registry name for an MCP server's tool, mcp__<server>__<tool>. MCP tools used to register under the server's bare name, which let a server replace a built-in tool (and inherit its allow rules) or clobber another server's tool (#187).

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a high-level MCP client that handles the protocol handshake, tool discovery, and tool execution over a Transport.

func NewClient

func NewClient(transport Transport, clientName, clientVersion string) *Client

NewClient creates a new MCP client over the given transport.

func (*Client) CallTool

func (c *Client) CallTool(ctx context.Context, name string, arguments map[string]any) (string, error)

CallTool executes a tool on the MCP server and returns the text result. Multiple text content blocks are joined with newlines. A result the server marked isError comes back as a *ToolError carrying that text.

func (*Client) Close

func (c *Client) Close() error

Close shuts down the client and its transport.

func (*Client) Initialize

func (c *Client) Initialize(ctx context.Context) error

Initialize performs the MCP initialize handshake. Sends initialize request, validates the server's protocol version, then sends notifications/initialized.

func (*Client) ListTools

func (c *Client) ListTools(ctx context.Context) ([]MCPToolDef, error)

ListTools discovers available tools from the MCP server.

func (*Client) ProtocolVersion

func (c *Client) ProtocolVersion() string

ProtocolVersion returns the MCP protocol version negotiated with the server during Initialize. Empty until Initialize succeeds.

type ContentBlock

type ContentBlock struct {
	Type string `json:"type"`
	Text string `json:"text"`
}

ContentBlock is a single content item in a tool call response.

type ErrorObject

type ErrorObject struct {
	Code    int             `json:"code"`
	Message string          `json:"message"`
	Data    json.RawMessage `json:"data,omitempty"`
}

ErrorObject is the error payload in a JSON-RPC 2.0 response.

func (*ErrorObject) Error

func (e *ErrorObject) Error() string

Error implements the error interface.

type HTTPTransport

type HTTPTransport struct {
	// contains filtered or unexported fields
}

HTTPTransport speaks MCP over Streamable HTTP: each outbound message is POSTed to the endpoint; the response is either a single JSON object or an SSE stream of JSON-RPC messages. Decoded responses are queued for Receive to drain.

func NewHTTPTransport

func NewHTTPTransport(url string) (*HTTPTransport, error)

NewHTTPTransport creates a Streamable-HTTP transport for the given endpoint.

func (*HTTPTransport) Close

func (t *HTTPTransport) Close() error

Close is a no-op for the stateless HTTP transport.

func (*HTTPTransport) Receive

func (t *HTTPTransport) Receive() (*Response, error)

Receive drains the next queued response.

func (*HTTPTransport) Send

func (t *HTTPTransport) Send(req *Request) error

Send POSTs a request and queues the resulting response(s).

func (*HTTPTransport) SendContext

func (t *HTTPTransport) SendContext(ctx context.Context, req *Request) error

SendContext is Send, abandoned when ctx is done. The server answers inside the POST, so this bounds the whole call.

func (*HTTPTransport) SendNotification

func (t *HTTPTransport) SendNotification(notif *Notification) error

SendNotification POSTs a notification; no response is queued.

func (*HTTPTransport) SendNotificationContext

func (t *HTTPTransport) SendNotificationContext(ctx context.Context, notif *Notification) error

SendNotificationContext is SendNotification, abandoned when ctx is done.

func (*HTTPTransport) SetProtocolVersion

func (t *HTTPTransport) SetProtocolVersion(v string)

SetProtocolVersion sets the value sent as the MCP-Protocol-Version header.

type MCPConfig

type MCPConfig struct {
	Servers map[string]ServerConfig `json:"mcpServers"`
}

MCPConfig is the top-level configuration for MCP servers. Loaded from ~/.celeste/mcp.json.

func LoadConfig

func LoadConfig(path string) (*MCPConfig, error)

LoadConfig reads and parses the MCP configuration from a JSON file. If the file does not exist, returns an empty config (not an error). This allows celeste to start without any MCP servers configured.

Only a regular file of at most maxConfigBytes is read: a workspace's .mcp.json is untrusted, and a FIFO or a link to a device would block or never end (Aikido 806869859).

func LoadMerged

func LoadMerged(paths []string) (*MCPConfig, error)

LoadMerged folds every config in paths into a single MCPConfig. paths must be ordered lowest-to-highest precedence (as DiscoverConfigPaths returns them): a server name present in a later path replaces the earlier definition wholesale. Each surviving server is stamped with the Origin path it came from. A missing file is skipped (LoadConfig already treats os.ErrNotExist as empty).

func LoadMergedLenient

func LoadMergedLenient(paths []string, home string) (cfg *MCPConfig, skipped []error, err error)

LoadMergedLenient is LoadMerged, except that a workspace config (a path not among home's GlobalConfigPaths) that fails to load or parse is skipped, its error returned in skipped, instead of failing the whole load: a broken repository .mcp.json must not turn off the user's own servers (Aikido 806869709). A home config that fails still fails it.

type MCPTool

type MCPTool struct {
	// contains filtered or unexported fields
}

MCPTool wraps an MCP tool definition and implements the tools.Tool interface. It delegates execution to the MCP Client, bridging external MCP servers into celeste's unified tool system.

func NewMCPTool

func NewMCPTool(def MCPToolDef, client *Client, serverName string) *MCPTool

NewMCPTool creates a new MCPTool adapter for the given MCP tool definition.

func (*MCPTool) Description

func (m *MCPTool) Description() string

func (*MCPTool) Execute

func (m *MCPTool) Execute(ctx context.Context, input map[string]any, progress chan<- tools.ProgressEvent) (tools.ToolResult, error)

Execute calls the MCP tool via the client and returns the result. Server-side errors are returned as ToolResult with Error=true so the LLM can see and react to the error message.

func (*MCPTool) InterruptBehavior

func (m *MCPTool) InterruptBehavior() tools.InterruptBehavior

func (*MCPTool) IsConcurrencySafe

func (m *MCPTool) IsConcurrencySafe(input map[string]any) bool

IsConcurrencySafe returns false because MCP tool calls go over a shared transport and we cannot guarantee the server handles concurrent calls safely.

func (*MCPTool) IsReadOnly

func (m *MCPTool) IsReadOnly() bool

IsReadOnly is the tool's readOnlyHint when the user marked its server "trusted": true; otherwise false. The hint is the server's own unverified claim, and IsReadOnly auto-approves in default mode, so an untrusted server's hint is ignored.

func (*MCPTool) Name

func (m *MCPTool) Name() string

func (*MCPTool) Parameters

func (m *MCPTool) Parameters() json.RawMessage

func (*MCPTool) ValidateInput

func (m *MCPTool) ValidateInput(input map[string]any) error

ValidateInput returns nil -- validation is delegated to the MCP server.

type MCPToolDef

type MCPToolDef struct {
	Name        string           `json:"name"`
	Description string           `json:"description"`
	InputSchema json.RawMessage  `json:"inputSchema"`
	Annotations *ToolAnnotations `json:"annotations,omitempty"`
}

MCPToolDef is a tool definition returned by the MCP server.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager handles the lifecycle of all MCP server connections. It loads configuration, connects to servers, discovers tools, and registers them in the tool registry.

func NewManager

func NewManager(configPath string, registry *tools.Registry) *Manager

NewManager creates a new MCP Manager. configPath is the path to the MCP configuration file (e.g., ~/.celeste/mcp.json). registry is the tool registry where discovered MCP tools will be registered.

func NewManagerMulti

func NewManagerMulti(paths []string, registry *tools.Registry) *Manager

NewManagerMulti creates a Manager that merges MCP config from multiple discovered paths (see DiscoverConfigPaths / LoadMerged) instead of a single file. Lower-index paths are overridden by higher-index paths on name clash.

func (*Manager) Connect

func (m *Manager) Connect(ctx context.Context, name string, cfg ServerConfig) error

Connect builds a transport from cfg, connects, and registers the server's tools at runtime. Safe to call after Start (lazy connect). A server that is already connected is a no-op. The connect is in flight, and a Disconnect or Stop revokes it, from before the transport is built (Aikido review of #424).

func (*Manager) Disconnect

func (m *Manager) Disconnect(name string) error

Disconnect closes a server's client and removes exactly the tools it added. No-op if the server is not connected.

func (*Manager) IsConnected

func (m *Manager) IsConnected(name string) bool

IsConnected reports whether a server currently has a live client.

func (*Manager) RegisterGlobalInto

func (m *Manager) RegisterGlobalInto(dst *tools.Registry, home string) int

RegisterGlobalInto is RegisterInto for a non-interactive run nested under the chat (2.0 F2e): only servers configured in one of home's global files (GlobalConfigPaths) are mirrored. A server from the workspace's .mcp.json or .celeste/mcp.json, or of unknown origin, stays the chat's: non-interactive runs never run a repo's MCP servers.

func (*Manager) RegisterInto

func (m *Manager) RegisterInto(dst *tools.Registry) int

RegisterInto registers every connected server's tools in dst, hidden until find_tools activates them (as discoverAndRegister does), and returns how many it added. A nested run's registry uses it to share this manager's clients instead of starting its own servers. Tools a server adds later are not mirrored; the nested registry is a snapshot.

func (*Manager) ServerStatus

func (m *Manager) ServerStatus() []ServerInfo

ServerStatus returns health information for all connected MCP servers.

func (*Manager) SetAdmit

func (m *Manager) SetAdmit(admit func(name string, cfg ServerConfig) bool)

SetAdmit makes Start skip an enabled server admit refuses (a workspace server nobody approved). Connect is unaffected: the /mcp panel connects a server only when the person asks. Call it before Start.

func (*Manager) Start

func (m *Manager) Start(ctx context.Context) error

Start loads the MCP configuration, connects to all configured servers, discovers their tools, and registers them in the tool registry. If the config file does not exist, it returns nil (no MCP configured). If a server fails to connect, it logs a warning and continues with others.

func (*Manager) Stop

func (m *Manager) Stop() error

Stop gracefully disconnects from all MCP servers.

type Notification

type Notification struct {
	JSONRPC string          `json:"jsonrpc"`
	Method  string          `json:"method"`
	Params  json.RawMessage `json:"params,omitempty"`
}

Notification is a JSON-RPC 2.0 notification (no ID, no response expected).

func NewNotification

func NewNotification(method string) *Notification

NewNotification creates a JSON-RPC 2.0 notification (no ID).

type Request

type Request struct {
	JSONRPC string          `json:"jsonrpc"`
	ID      int64           `json:"id"`
	Method  string          `json:"method"`
	Params  json.RawMessage `json:"params,omitempty"`
}

Request is a JSON-RPC 2.0 request.

func NewRequest

func NewRequest(method string, params any) (*Request, error)

NewRequest creates a new JSON-RPC 2.0 request with an auto-incremented ID. params is marshaled to JSON. Pass nil for no params.

type Response

type Response struct {
	JSONRPC string          `json:"jsonrpc"`
	ID      json.Number     `json:"id,omitempty"`
	Result  json.RawMessage `json:"result,omitempty"`
	Error   *ErrorObject    `json:"error,omitempty"`
}

Response is a JSON-RPC 2.0 response.

type SSETransport

type SSETransport struct {
	// contains filtered or unexported fields
}

SSETransport communicates with an MCP server over HTTP Server-Sent Events. Requests are sent via POST to the server's endpoint URL. Responses are received via an SSE event stream from a GET connection.

func NewSSETransport

func NewSSETransport(url string) (*SSETransport, error)

NewSSETransport connects to an MCP server's SSE endpoint. url is the base URL of the MCP server (e.g., "http://localhost:3000/sse").

func (*SSETransport) Close

func (t *SSETransport) Close() error

Close shuts down the SSE connection.

func (*SSETransport) Receive

func (t *SSETransport) Receive() (*Response, error)

Receive reads the next JSON-RPC response from the SSE event stream. It also returns once the transport is closed, so a Receive the client left running for a cancelled call doesn't outlive Close. It returns the stream's error once the stream has ended and every response it delivered has been read.

func (*SSETransport) Send

func (t *SSETransport) Send(req *Request) error

Send sends a JSON-RPC request via HTTP POST to the server's endpoint.

func (*SSETransport) SendContext

func (t *SSETransport) SendContext(ctx context.Context, req *Request) error

SendContext is Send, abandoned when ctx is done.

func (*SSETransport) SendNotification

func (t *SSETransport) SendNotification(notif *Notification) error

SendNotification sends a JSON-RPC notification via HTTP POST.

func (*SSETransport) SendNotificationContext

func (t *SSETransport) SendNotificationContext(ctx context.Context, notif *Notification) error

SendNotificationContext is SendNotification, abandoned when ctx is done.

type ServerConfig

type ServerConfig struct {
	// Enabled opts the server into auto-connection at startup. It is false by
	// default: a server is only connected if it explicitly sets
	// "enabled": true. This keeps configured-but-unused servers (e.g. an X
	// bridge awaiting its first OAuth login) from delaying startup.
	Enabled bool `json:"enabled,omitempty"`

	// Transport is "stdio" or "sse". Defaults to "stdio" if not set.
	Transport string `json:"transport"`

	// Command is the executable to spawn (stdio transport only).
	Command string `json:"command,omitempty"`

	// Args are command-line arguments (stdio transport only).
	Args []string `json:"args,omitempty"`

	// URL is the SSE endpoint URL (sse transport only).
	URL string `json:"url,omitempty"`

	// Env is a map of environment variables passed to the child process.
	// Values support ${VAR} expansion from the host environment.
	Env map[string]string `json:"env,omitempty"`

	// Trusted honours this server's readOnlyHint, which auto-approves the
	// tools it marks read-only. Set it only for servers you control. It is
	// read only from the home-level configs (GlobalConfigPaths): a
	// repository's .mcp.json cannot vouch for its own server (2.0 W4).
	Trusted bool `json:"trusted,omitempty"`

	// Dir is the working directory a stdio server starts in; empty is
	// celeste's own. Set by the caller (an ACP session uses its folder),
	// never parsed from JSON.
	Dir string `json:"-"`

	// Origin is the absolute path of the config file this server was loaded
	// from. Set by the discovery/merge layer, never parsed from JSON. Used by
	// the /mcp panel to show provenance.
	Origin string `json:"-"`
}

ServerConfig defines how to connect to a single MCP server.

func (ServerConfig) TrustHash

func (c ServerConfig) TrustHash() string

TrustHash is what a workspace server's approval is pinned to: its transport, command, args, env (names and values as written, before ${VAR} expansion) and url. Any edit to them asks again; "enabled", "trusted" and the file it sits in do not count.

func (ServerConfig) TrustSummary

func (c ServerConfig) TrustSummary() string

TrustSummary describes the server for an approval prompt: what it runs or connects to. Strings from the file are Go-quoted so they cannot act on the terminal; env values, a URL's password and its query are left out, since they often hold secrets.

type ServerInfo

type ServerInfo struct {
	Name      string
	Transport string
	Connected bool
	ToolCount int
}

ServerInfo contains health/status information for a connected MCP server.

type SkippedConfigError

type SkippedConfigError struct {
	Path string
	Err  error // names Path too (LoadConfig's errors do)
}

SkippedConfigError is one workspace MCP config LoadMergedLenient skipped.

func SkippedConfigs

func SkippedConfigs(err error) []*SkippedConfigError

SkippedConfigs returns the skipped configs err carries (Start's error).

func (*SkippedConfigError) Error

func (e *SkippedConfigError) Error() string

func (*SkippedConfigError) Is

func (e *SkippedConfigError) Is(target error) bool

Is makes every SkippedConfigError match ErrWorkspaceConfigSkipped.

func (*SkippedConfigError) Unwrap

func (e *SkippedConfigError) Unwrap() error

type StdioTransport

type StdioTransport struct {
	// contains filtered or unexported fields
}

StdioTransport communicates with an MCP server via a child process's stdin and stdout. Each JSON-RPC message is a single line of JSON.

func (*StdioTransport) Close

func (t *StdioTransport) Close() error

Close shuts down the child process and closes pipes.

func (*StdioTransport) Receive

func (t *StdioTransport) Receive() (*Response, error)

Receive reads the next JSON line from stdout and parses it as a Response. A line over maxResponseBytes fails and closes the transport: the rest of it cannot be skipped reliably (Aikido 806869944).

func (*StdioTransport) Send

func (t *StdioTransport) Send(req *Request) error

Send sends a JSON-RPC request as a single JSON line to the child process stdin.

func (*StdioTransport) SendNotification

func (t *StdioTransport) SendNotification(notif *Notification) error

SendNotification sends a JSON-RPC notification as a single JSON line.

type ToolAnnotations

type ToolAnnotations struct {
	ReadOnlyHint    *bool `json:"readOnlyHint,omitempty"`
	DestructiveHint *bool `json:"destructiveHint,omitempty"`
	IdempotentHint  *bool `json:"idempotentHint,omitempty"`
	OpenWorldHint   *bool `json:"openWorldHint,omitempty"`
}

ToolAnnotations are a tool's hints from tools/list. They are the server's own claims: only readOnlyHint is used, and only for a server the user marked "trusted": true (2.0 W4). The others are kept, unused.

type ToolCallResult

type ToolCallResult struct {
	Content []ContentBlock `json:"content"`
	IsError bool           `json:"isError,omitempty"`
}

ToolCallResult is the server's response to tools/call. IsError marks a call the tool itself reported as failed (MCP's isError).

type ToolError

type ToolError struct {
	Text string
}

ToolError is CallTool's error for a result the server marked isError. Text is the result's own text, which the model gets as the failure.

func (*ToolError) Error

func (e *ToolError) Error() string

type Transport

type Transport interface {
	// Send sends a JSON-RPC request to the server.
	// For requests, the caller should subsequently call Receive to get the response.
	Send(req *Request) error

	// SendNotification sends a JSON-RPC notification (no response expected).
	SendNotification(notif *Notification) error

	// Receive reads the next JSON-RPC response from the server.
	// Blocks until a response is available or the transport is closed.
	Receive() (*Response, error)

	// Close shuts down the transport, releasing all resources.
	Close() error
}

Transport is the abstraction over MCP communication channels. Implementations handle the physical sending and receiving of JSON-RPC messages over stdio (child process) or SSE (HTTP).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL