Documentation
¶
Overview ¶
Package mcp connects chatz to MCP servers (stdio + http) and imports their configuration. Servers live in the DB (admin-managed via the UI) or are imported from a Claude-style .mcp.json here. Header + env secrets are sealed at rest via internal/pkg/secrets — plaintext never touches the DB.
Index ¶
- Variables
- func ParseMCPJSON(raw []byte, box *secrets.Box, createdBy *uuid.UUID) ([]*models.MCPServer, error)
- func ServerToAPI(m *models.MCPServer, st Status) api.MCPServer
- func ToolToAPI(t Tool) api.MCPTool
- type Client
- type Manager
- func (m *Manager) Add(ctx context.Context, srv *models.MCPServer) error
- func (m *Manager) Call(ctx context.Context, qualifiedName string, args map[string]any) (*ToolResult, error)
- func (m *Manager) Close() error
- func (m *Manager) ConnectAsync(ctx context.Context, srv *models.MCPServer)
- func (m *Manager) Disable(ctx context.Context, name string)
- func (m *Manager) Remove(ctx context.Context, name string)
- func (m *Manager) ServerTools(ctx context.Context, name string) ([]Tool, error)
- func (m *Manager) Status(name string) Status
- func (m *Manager) Tools(ctx context.Context) []Tool
- type Reason
- type ServerStore
- func (s *ServerStore) Create(ctx context.Context, srv *models.MCPServer) error
- func (s *ServerStore) Delete(ctx context.Context, id uuid.UUID) error
- func (s *ServerStore) Get(ctx context.Context, id uuid.UUID) (*models.MCPServer, error)
- func (s *ServerStore) List(ctx context.Context) ([]*models.MCPServer, error)
- func (s *ServerStore) Save(ctx context.Context, srv *models.MCPServer) error
- type State
- type Status
- type Tool
- type ToolResult
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNoServers is returned when a .mcp.json has no mcpServers entries. ErrNoServers = errors.New("mcp: no servers in file") // ErrInvalidServer is returned for an entry that is neither a stdio server // (has command) nor an http server (has url). ErrInvalidServer = errors.New("mcp: server has neither command nor url") // ErrUnsupportedTransport is returned when a server's transport is neither // stdio nor http. ErrUnsupportedTransport = errors.New("mcp: unsupported transport") // ErrInvalidToolName is returned when a qualified tool name is not of the // form <server>__<tool>. ErrInvalidToolName = errors.New("mcp: invalid qualified tool name") // ErrServerNotFound is returned when a call targets a server not registered // with the manager. ErrServerNotFound = errors.New("mcp: server not found") )
MCP errors. Declared with errors.New so they stay comparable across ctxerrors.Wrap layers via errors.Is.
Functions ¶
func ParseMCPJSON ¶
func ParseMCPJSON( raw []byte, box *secrets.Box, createdBy *uuid.UUID, ) ([]*models.MCPServer, error)
ParseMCPJSON parses raw .mcp.json bytes into MCPServer rows ready to persist, sealing env + header secrets with box. createdBy stamps the importing admin (nil is allowed). Servers come back sorted by name for deterministic output.
func ServerToAPI ¶
ServerToAPI projects a stored MCP server row plus its live status to the wire shape. Optional command/url/error/reason/toolCount fields stay nil unless the row or status actually carries them.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a live connection to one MCP server.
func Connect ¶
Connect dials the server described by srv, decrypting its env/header secrets with box, and completes the MCP initialize handshake. ctx bounds the connect; the session outlives it (a stdio subprocess is torn down by Close, not ctx).
func (*Client) CallTool ¶
func (c *Client) CallTool( ctx context.Context, tool string, args map[string]any, ) (*ToolResult, error)
CallTool invokes tool (the raw server-side name) with args. A protocol failure returns an error; a tool-level failure returns a result with IsError set.
func (*Client) ListTools ¶
ListTools enumerates the server's tools (auto-paginated), namespaced to this server.
func (*Client) Wait ¶
Wait blocks until the underlying session closes, deliberately or not, and returns the terminal error (nil on a clean close). A caller uses this to detect the connection dying in the background outside any specific RPC — see WasIntentionalClose to tell that apart from an expected shutdown.
func (*Client) WasIntentionalClose ¶
WasIntentionalClose reports whether Close has been called on this client — distinguishes a deliberate shutdown from an unexpected async death for a Wait caller.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager holds live connections to multiple MCP servers, aggregates their tools (namespaced <server>__<tool>), and routes a qualified tool call back to the owning server. Safe for concurrent use.
func NewManager ¶
NewManager builds an empty manager. box decrypts each server's env/header secrets at connect time.
func (*Manager) Add ¶
Add connects to srv synchronously, registering it under its name (replacing and closing any existing client) and recording its status. A failure is recorded as a failed status AND returned so callers that want the error get it. Prefer ConnectAsync from request handlers so a slow server can't block.
Bumps the server's generation first so this attempt always wins the status write over any in-flight ConnectAsync goroutine for the same name — Add is synchronous and its result is authoritative for its caller.
func (*Manager) Call ¶
func (m *Manager) Call( ctx context.Context, qualifiedName string, args map[string]any, ) (*ToolResult, error)
Call routes a qualified tool name (<server>__<tool>) to its server and invokes it with args. Logs start/end with timing, and heartbeats every heartbeat.Interval while the call is still in flight — CallTool is a single blocking round trip to (potentially) a slow external server with no intermediate progress signal, so without a heartbeat a slow tool looks identical to a hang in the logs.
Only argument KEY NAMES are logged, never values — tool-call arguments can carry arbitrary (and potentially sensitive) caller-supplied data.
func (*Manager) Close ¶
Close disconnects every registered server, joining any close errors, and stops every pending auto-retry timer so nothing fires after the manager (and the DB/process it's part of) is torn down.
func (*Manager) ConnectAsync ¶
ConnectAsync marks the server connecting and connects in the background, so the caller's request returns immediately and the recorded status settles to connected/failed on its own.
The status is GUARANTEED to leave StateConnecting within connectTimeout + connectOuterGrace, regardless of how the inner connect attempt behaves. connectAndStore's own ctx (bounded by connectTimeout) relies on the MCP SDK selecting on ctx.Done() for its I/O, which it does for the read/response path — but ctx-UNBOUND work can still follow (e.g. Close() waiting out a stuck subprocess's SIGTERM grace period). Rather than trust every layer to honor ctx, this races connectAndStore against cctx's own deadline with an explicit select: if the deadline wins, the status is force-settled to failed/not_responding immediately and the inner attempt is left to finish (or leak) in the background, logged as a WARN.
A monotonic per-server generation number, bumped here before the goroutine starts, guards against a late result from an abandoned attempt clobbering a newer one: if a Reconnect (or another ConnectAsync/Add) supersedes this attempt before it finishes, its result — early or late — is discarded.
func (*Manager) Disable ¶
Disable disconnects a server and records it as disabled — used when a server is edited to enabled=false (the row stays, its tools go away).
Bumps the generation first for the same reason as Remove: a superseded in-flight connect attempt must not overwrite the disabled status. Also cancels any pending auto-retry — a disabled server must never reconnect on its own.
func (*Manager) Remove ¶
Remove disconnects a server (if connected) and forgets its status entirely — used when a server is deleted.
Bumps the generation first so a ConnectAsync attempt already in flight for this name (started before the delete) can never resurrect a status after removal, early or late. Also cancels any pending auto-retry — a deleted server must never reconnect on its own.
func (*Manager) ServerTools ¶
ServerTools lists a connected server's tools (unqualified names + description + input schema). A server that is not connected yields nil, nil — the admin UI shows tools for a live server only.
type Reason ¶
type Reason string
Reason classifies WHY a connect failed so the UI can say "unreachable" / "access denied" / "not responding" instead of a raw error blob.
type ServerStore ¶
type ServerStore struct {
// contains filtered or unexported fields
}
ServerStore is the persistence core for MCP server rows. The HTTP handlers drive it instead of reaching into the repositories directly, so the wire layer never knows about gorm — a missing row surfaces as commerr.ErrNotFound, which the handlers map to a 404. The live connection lifecycle stays in Manager; this type owns only the DB side.
func NewServerStore ¶
func NewServerStore(q *repositories.Query) *ServerStore
NewServerStore builds a ServerStore over the given query handle.
func (*ServerStore) Delete ¶
Delete removes the server row with id. Deleting a row that isn't there is a no-op (callers Get first to produce a 404), so this never reports not-found.
type Status ¶
type Status struct {
State State
Reason Reason
Error string
ToolCount int
LastConnectionAttemptAt time.Time
LastSuccessfulConnectionAt time.Time
LastConnectionFailureAt time.Time
LastConnectionLatency time.Duration
LastError string
}
Status is a server's last-known connection status (keyed by server name).
type Tool ¶
type Tool struct {
Server string `json:"server"`
Name string `json:"name"`
Description string `json:"description"`
InputSchema map[string]any `json:"inputSchema,omitempty"`
}
Tool is a tool discovered on an MCP server.
func (Tool) QualifiedName ¶
QualifiedName is the server-namespaced name exposed to the LLM.
type ToolResult ¶
ToolResult is the outcome of a tool call. IsError is the tool-level error flag (the call itself succeeded at the protocol level); Text is the joined text content.