mcpmgr

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 4, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RenderSecret

func RenderSecret(format string, kv map[string]string) ([]byte, error)

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
  • "yaml" — KEY: VALUE mapping

Keys are sorted so output is deterministic.

func ThreadServerID

func ThreadServerID(integrationID, threadID string) string

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"`

	// 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|yaml. Only consulted when
	// SecretRef is set.
	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
	Client *client.Client
	Tools  []MCPTool
	// 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) Close

func (m *Manager) Close()

Close disconnects all MCP servers (project + thread scope).

func (*Manager) GetThreadTools

func (m *Manager) GetThreadTools(threadID string) []MCPTool

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) GetTools

func (m *Manager) GetTools() []MCPTool

GetTools returns all tools from all connected MCP servers.

func (*Manager) ServerStarted

func (m *Manager) ServerStarted(id string) bool

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) 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

func (m *Manager) StartServer(ctx context.Context, cfg MCPConfig) error

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

func (m *Manager) StopServer(id string)

StopServer disconnects and kills an MCP server.

func (*Manager) StopThreadServers

func (m *Manager) StopThreadServers(threadID string)

StopThreadServers tears down every thread-scoped MCP server bound to threadID, across all integrations. Called on thread close.

func (*Manager) ThreadServerStarted

func (m *Manager) ThreadServerStarted(integrationID, threadID string) bool

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 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.

Jump to

Keyboard shortcuts

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