Documentation
¶
Index ¶
- func RenderSecret(format string, kv map[string]string) ([]byte, error)
- func ThreadServerID(integrationID, threadID string) string
- type MCPConfig
- type MCPServer
- type MCPTool
- type Manager
- func (m *Manager) CallTool(ctx context.Context, serverID string, toolName string, ...) (string, error)
- func (m *Manager) Capabilities(serverID string) map[string]any
- func (m *Manager) Close()
- func (m *Manager) GetPrompt(ctx context.Context, serverID, name string, args map[string]string) (string, []PromptMessage, error)
- func (m *Manager) GetThreadTools(threadID string) []MCPTool
- func (m *Manager) GetTools() []MCPTool
- func (m *Manager) ListPrompts(ctx context.Context, serverID string) ([]Prompt, error)
- func (m *Manager) ListResources(ctx context.Context, serverID string) ([]Resource, error)
- func (m *Manager) ReadResource(ctx context.Context, serverID, uri string) ([]ResourceContent, error)
- func (m *Manager) ServerReady(id string) bool
- func (m *Manager) ServerReadyErr(id string) error
- func (m *Manager) ServerStarted(id string) bool
- func (m *Manager) ServerStderr(id string) string
- func (m *Manager) SetNotificationHandler(fn func(Notification))
- func (m *Manager) SetSecretResolver(resolver SecretResolver)
- func (m *Manager) StartServer(ctx context.Context, cfg MCPConfig) error
- func (m *Manager) StartThreadServer(ctx context.Context, integrationID, threadID string, cfg MCPConfig) error
- func (m *Manager) StopServer(id string)
- func (m *Manager) StopThreadServers(threadID string)
- func (m *Manager) ThreadServerStarted(integrationID, threadID string) bool
- type Notification
- type Prompt
- type PromptArgument
- type PromptMessage
- type Resource
- type ResourceContent
- type SecretResolver
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RenderSecret ¶
RenderSecret serialises a flat key/value secret map into one of the supported on-disk formats. The rendered bytes are written to a per-spawn 0600 temp file whose path replaces the {{secret_file}} placeholder in an MCP server's Args/Env (see manager.spawn).
Supported formats:
- "env" — KEY=VALUE lines (gmcpail consumes this)
- "properties" — key=value lines (java .properties variant)
- "json" — a JSON object
There was a "yaml" format. It was removed with gopkg.in/yaml.v3, which was 11k LOC carried for that one branch and had no users — every config in the field renders env. A server that genuinely needs YAML can take "json" instead: YAML 1.2 is a superset of JSON, so a JSON object IS a YAML document, and any conforming parser reads it unchanged.
Keys are sorted so output is deterministic.
func ThreadServerID ¶
ThreadServerID is the convention for cfg.ID on thread-scoped servers — composite so the scheduler's resolveTool can recover the (integration, thread) pair from a single ServerID field and so tool names stay collision-free across threads.
Types ¶
type MCPConfig ¶
type MCPConfig struct {
ID string `json:"id"`
Name string `json:"name"`
Command string `json:"command"`
Args []string `json:"args"`
Env []string `json:"env"`
Timeout int `json:"timeout"` // seconds, default 30
Scope string `json:"scope"`
// URL selects the Streamable HTTP transport (http.go) instead of
// spawning Command. Headers go on every request — Authorization: Bearer
// <token> for a server that takes a static token.
URL string `json:"url,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
// SecretRef names a row in the secrets table whose plaintext is a
// JSON object (a flat KV map). When set, spawn() resolves it,
// renders it to SecretFormat, writes it to a per-server 0600 temp
// file, and substitutes the literal token {{secret_file}} in every
// Args and Env entry with that file's path. No secret value ever
// lands in Args/Env directly — only the file path. Carried in the
// integration's JSONB config (type=mcp); no schema column.
SecretRef string `json:"secret_ref"`
// SecretFormat selects the on-disk encoding of the rendered secret
// file: one of env|properties|json. Only consulted when SecretRef is
// set. A server wanting YAML takes json — YAML 1.2 is a superset of
// JSON.
SecretFormat string `json:"secret_format"`
}
MCPConfig holds the configuration for an MCP server connection.
Scope:
- "" — backwards-compat default, treated as "project"
- "project" — one shared instance per integration, spawned at daemon boot. Use when the MCP server is workspace- agnostic OR indexes the project's canonical repo_path.
- "thread" — one instance per (integration, thread) pair, spawned lazily when the thread's first dev session activates. The Args list may contain "{{worktree_root}}" placeholders that the scheduler substitutes with the thread's worktree directory at spawn time. Tears down on thread close. Use for per-workspace indexers (poly-lsp-mcp and friends) where each thread has its own worktree.
type MCPServer ¶
type MCPServer struct {
Config MCPConfig
Tools []MCPTool
// Capabilities is what the server declared in the handshake: "tools",
// "resources", "prompts", … — a client asks only for what is there.
Capabilities map[string]any
// contains filtered or unexported fields
}
MCPServer wraps a connected MCP client.
type MCPTool ¶
type MCPTool struct {
Name string `json:"name"`
Description string `json:"description"`
InputSchema map[string]interface{} `json:"inputSchema"`
ServerID string `json:"server_id"`
}
MCPTool represents a tool discovered from an MCP server.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager manages MCP server connections, both project-scoped (one shared instance per integration) and thread-scoped (one instance per (integration, thread) pair). Thread-scoped servers are what makes a per-thread editor MCP work: each thread's poly-lsp-mcp indexes only that thread's worktree, so file operations stay single-threaded within a thread (the dev-lane lock already serializes Turns) while threads make progress independently.
Key conventions:
- servers[cfg.ID] — project-scoped, started at boot
- threadServers[integID][threadID] — thread-scoped, started lazily when the first dev session for the thread activates
func NewManager ¶
func NewManager() *Manager
func NewManagerWithSecrets ¶
func NewManagerWithSecrets(resolver SecretResolver) *Manager
NewManagerWithSecrets builds a Manager wired to a SecretResolver. MCP configs that carry a non-empty SecretRef will have the resolved secret rendered to a 0600 temp file and substituted into Args/Env via the {{secret_file}} placeholder at spawn time.
func (*Manager) CallTool ¶
func (m *Manager) CallTool(ctx context.Context, serverID string, toolName string, args map[string]interface{}) (string, error)
CallTool invokes a tool on the appropriate MCP server. ServerID may be a project-scoped id (one of m.servers' keys) OR a thread-scoped id produced by ThreadServerID(integID, threadID). Routes to whichever map holds it.
func (*Manager) Capabilities ¶ added in v0.7.0
Capabilities returns what a connected server declared in the handshake, or nil when it is not connected.
func (*Manager) Close ¶
func (m *Manager) Close()
Close disconnects all MCP servers (project + thread scope).
func (*Manager) GetPrompt ¶ added in v0.7.0
func (m *Manager) GetPrompt(ctx context.Context, serverID, name string, args map[string]string) (string, []PromptMessage, error)
GetPrompt renders one prompt with its arguments (prompts/get): the prompt's description and its messages.
func (*Manager) GetThreadTools ¶
GetThreadTools returns the tool set the scheduler should expose to developers on threadID: project-scoped tools (unchanged behaviour) PLUS any thread-scoped tools bound to this thread. Callers that don't care about thread scoping can continue using GetTools.
func (*Manager) ListPrompts ¶ added in v0.7.0
ListPrompts returns a server's prompts (prompts/list).
func (*Manager) ListResources ¶ added in v0.7.0
ListResources returns a server's resources (resources/list).
func (*Manager) ReadResource ¶ added in v0.7.0
func (m *Manager) ReadResource(ctx context.Context, serverID, uri string) ([]ResourceContent, error)
ReadResource returns the contents of one resource (resources/read).
func (*Manager) ServerReady ¶ added in v0.7.0
ServerReady reports whether a project-scoped server finished tool discovery without error. A server with no tools at all (one that serves only resources or prompts) is ready once discovery returns empty.
func (*Manager) ServerReadyErr ¶ added in v0.4.0
ServerReadyErr reports why a project-scoped server discovered no tools, once discovery has finished. Returns nil while still discovering, when discovery succeeded, or when no such server is running — a caller that wants to distinguish those cases should pair it with ServerStarted.
This exists because tool discovery is async: StartServer returning nil only means the handshake worked, and a server that then fails ListTools otherwise shows up as a silent absence of tools.
func (*Manager) ServerStarted ¶
ServerStarted reports whether a project-scoped MCP server is already running for id (== cfg.ID). The Phase 3 decision executor uses this to spawn the write server (gmail-exec) on demand WITHOUT the idempotent-restart of StartServer tearing down a live process on every apply (which would kill in-flight IMAP/SMTP calls).
func (*Manager) ServerStderr ¶ added in v0.4.0
ServerStderr returns the tail of what a running server wrote to stderr, or "" when it has said nothing (or is not running). Useful for reporting a server that started but is misbehaving, where no error was ever returned.
func (*Manager) SetNotificationHandler ¶ added in v0.4.0
func (m *Manager) SetNotificationHandler(fn func(Notification))
SetNotificationHandler registers the sink for server→client notifications. Call before StartServer: handlers are attached at spawn, so a server already running keeps whatever sink it was spawned with.
func (*Manager) SetSecretResolver ¶
func (m *Manager) SetSecretResolver(resolver SecretResolver)
SetSecretResolver attaches (or replaces) the resolver after construction. Used when the Manager is built before the secrets store is available.
func (*Manager) StartServer ¶
StartServer spawns a project-scoped MCP server process and connects to it. Idempotent: calling twice for the same cfg.ID closes the previous instance.
func (*Manager) StartThreadServer ¶
func (m *Manager) StartThreadServer(ctx context.Context, integrationID, threadID string, cfg MCPConfig) error
StartThreadServer spawns a thread-scoped MCP server. integrationID identifies the project_integration the config came from; (integrationID, threadID) is the spawn key — a second call for the same pair restarts the server. Tool dispatch uses cfg.ID for routing, so callers should set cfg.ID = threadServerID(integID, threadID) before calling.
func (*Manager) StopServer ¶
StopServer disconnects and kills an MCP server.
func (*Manager) StopThreadServers ¶
StopThreadServers tears down every thread-scoped MCP server bound to threadID, across all integrations. Called on thread close.
func (*Manager) ThreadServerStarted ¶
ThreadServerStarted reports whether a thread-scoped MCP server is already running for (integrationID, threadID). The scheduler's per-Turn collectTools uses this to avoid re-spawning on every tool list.
type Notification ¶ added in v0.4.0
Notification is one server→client message, flattened to what a consumer acts on. The raw params are kept because MCP's notification methods are open-ended and this package should not become a bottleneck on which ones mean something.
func (Notification) Level ¶ added in v0.4.0
func (n Notification) Level() string
Level is the notifications/message severity, when the method carries one. Empty for methods that do not.
func (Notification) Text ¶ added in v0.4.0
func (n Notification) Text() string
Text renders the human-readable payload of a notifications/message, which is where servers put anything they want a person or a model to read. `data` is by spec any JSON value; a bare string is the common case and is returned as-is rather than re-encoded with quotes around it.
type Prompt ¶ added in v0.7.0
type Prompt struct {
Name string `json:"name"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Arguments []PromptArgument `json:"arguments,omitempty"`
}
Prompt is one entry of prompts/list.
type PromptArgument ¶ added in v0.7.0
type PromptMessage ¶ added in v0.7.0
PromptMessage is one message of a prompts/get reply. Content is one content block, kept raw for the same reason tool results are; Text renders it.
func (PromptMessage) Text ¶ added in v0.7.0
func (p PromptMessage) Text() string
type Resource ¶ added in v0.7.0
type Resource struct {
URI string `json:"uri"`
Name string `json:"name"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
MimeType string `json:"mimeType,omitempty"`
}
Resource is one entry of resources/list.
type ResourceContent ¶ added in v0.7.0
type ResourceContent struct {
URI string `json:"uri"`
MimeType string `json:"mimeType,omitempty"`
Text string `json:"text,omitempty"`
Blob string `json:"blob,omitempty"`
}
ResourceContent is one entry of a resources/read reply: Text for a text resource, Blob (base64) for a binary one.
type SecretResolver ¶
type SecretResolver interface {
Resolve(ctx context.Context, ref string) (map[string]string, error)
}
SecretResolver resolves a secret reference (a row key in the secrets table) into a flat key/value map. The concrete implementation lives server-side (internal/server/secret_resolver.go) and wraps the at-rest *secrets.Store; mcpmgr intentionally does NOT import internal/secrets so it stays free of the crypto/db dependency edge and unit-testable with a fake resolver.