Documentation
¶
Index ¶
- Constants
- Variables
- func SSRFSafeTransport(allowLoopback bool, keepAlive time.Duration, requestTimeout time.Duration) *http.Transport
- type LifecycleManager
- func (lm *LifecycleManager) AddPlugin(ctx context.Context, name string, cfg config.PluginConfig) error
- func (lm *LifecycleManager) AddTool(ctx context.Context, name string, cfg config.ToolConfig) error
- func (lm *LifecycleManager) DisableTool(ctx context.Context, name string) error
- func (lm *LifecycleManager) EnableTool(ctx context.Context, name string) error
- func (lm *LifecycleManager) ListPlugins() []PluginStatus
- func (lm *LifecycleManager) ListTools() []ServerStatus
- func (lm *LifecycleManager) RemovePlugin(ctx context.Context, name string) error
- func (lm *LifecycleManager) RemoveTool(ctx context.Context, name string) error
- func (lm *LifecycleManager) RestartTool(ctx context.Context, name string) error
- func (lm *LifecycleManager) ToolManager() *Manager
- func (lm *LifecycleManager) TrackPlugin(name string, cfg config.PluginConfig)
- func (lm *LifecycleManager) UpdateDisabledTools(serverName string, disabledTools []string) error
- func (lm *LifecycleManager) UpdateTool(ctx context.Context, name string, cfg config.ToolConfig) error
- type Manager
- func (m *Manager) AdoptFrom(source *Manager)
- func (m *Manager) CleanupOAuthToken(name string)
- func (m *Manager) Close() error
- func (m *Manager) Execute(ctx context.Context, call llm.ToolCall) (string, error)
- func (m *Manager) GetOAuthHandler(name string) oauthHandler
- func (m *Manager) IsIdempotent(toolName string) bool
- func (m *Manager) MarkDisabled(name string)
- func (m *Manager) RegisterDisabled(name string, cfg config.ToolConfig, reason string, isConfigError bool)
- func (m *Manager) RegisterPending(name string, cfg config.ToolConfig, lastErr string)
- func (m *Manager) RegisterServer(ctx context.Context, name string, cfg config.ToolConfig) error
- func (m *Manager) RegisterSession(ctx context.Context, name string, session *mcp.ClientSession) error
- func (m *Manager) RestartServer(ctx context.Context, name string) error
- func (m *Manager) ServerInfo(name string) (ServerStatus, bool)
- func (m *Manager) ServerNames() []string
- func (m *Manager) ServerResolvedURL(name string) string
- func (m *Manager) ServerToolConfig(name string) (config.ToolConfig, bool)
- func (m *Manager) ServerToolDefs(serverName string) ([]llm.ToolDef, bool)
- func (m *Manager) SetDisabledTools(serverName string, disabled []string) error
- func (m *Manager) SetOAuthSupport(o *OAuthSupport)
- func (m *Manager) StartHealthChecker(ctx context.Context, interval time.Duration)
- func (m *Manager) ToolDefs() []llm.ToolDef
- func (m *Manager) ToolDescription(toolName string) string
- func (m *Manager) ToolNames() []string
- func (m *Manager) ToolServer(toolName string) string
- func (m *Manager) UnregisterServer(name string) error
- type OAuthHandlerFactory
- type OAuthStatusInfo
- type OAuthSupport
- type PluginStatus
- type RejectionError
- type ServerStatus
- type TokenDeleter
Constants ¶
const DefaultMaxTools = 50
DefaultMaxTools is the combined limit for tools + plugins.
Variables ¶
var ErrAmbiguousTool = errors.New("ambiguous tool name")
ErrAmbiguousTool is returned when a bare tool name is advertised by more than one connected MCP server. Colliding tools are advertised — and must be called — under their server-qualified name, so resolving the bare name would mean guessing which server the caller meant. It never guesses. Callers classify with errors.Is, mirroring the ErrToolNotFound convention.
var ErrServerDraining = errors.New("server is shutting down")
ErrServerDraining is returned when a tool call targets an MCP server that is being torn down (removed, disabled, restarted, or reconfigured). The refusal is immediate — the model sees it and adapts, exactly as it does for an unknown tool. Callers classify with errors.Is, mirroring ErrToolNotFound.
var ErrToolNotFound = errors.New("tool not found")
ErrToolNotFound is returned when an operation targets a tool name that is not registered with the manager.
Functions ¶
func SSRFSafeTransport ¶ added in v0.16.5
func SSRFSafeTransport(allowLoopback bool, keepAlive time.Duration, requestTimeout time.Duration) *http.Transport
SSRFSafeTransport returns an *http.Transport that blocks connections to SSRF-sensitive IP addresses at TCP connect time via net.Dialer.Control. This prevents DNS-rebinding attacks where a hostname resolves to a blocked IP after passing the initial string-based URL validation. The keepAlive parameter sets the TCP keepalive interval for connections; use 0 for the default (15s).
Types ¶
type LifecycleManager ¶ added in v0.1.0
type LifecycleManager struct {
// contains filtered or unexported fields
}
LifecycleManager coordinates adding and removing MCP tools and plugins at runtime, persisting changes to the TOML config file.
func NewLifecycleManager ¶ added in v0.1.0
func NewLifecycleManager(toolMgr *Manager, configPath string, maxTools int, logger *slog.Logger) *LifecycleManager
NewLifecycleManager creates a lifecycle manager wrapping the given tool.Manager. configPath is the path to denkeeper.toml. maxTools is the combined limit (0 uses DefaultMaxTools).
func (*LifecycleManager) AddPlugin ¶ added in v0.1.0
func (lm *LifecycleManager) AddPlugin(ctx context.Context, name string, cfg config.PluginConfig) error
func (*LifecycleManager) AddTool ¶ added in v0.1.0
func (lm *LifecycleManager) AddTool(ctx context.Context, name string, cfg config.ToolConfig) error
AddTool validates the config, spawns the MCP server, registers it, and persists the [tools.<name>] section to denkeeper.toml.
func (*LifecycleManager) DisableTool ¶ added in v0.36.0
func (lm *LifecycleManager) DisableTool(ctx context.Context, name string) error
DisableTool stops the MCP server process, marks it as user-disabled, and persists enabled=false to TOML.
func (*LifecycleManager) EnableTool ¶ added in v0.36.0
func (lm *LifecycleManager) EnableTool(ctx context.Context, name string) error
EnableTool starts a previously disabled MCP server and persists enabled=true to TOML.
func (*LifecycleManager) ListPlugins ¶ added in v0.1.0
func (lm *LifecycleManager) ListPlugins() []PluginStatus
ListPlugins returns metadata for all registered plugins.
func (*LifecycleManager) ListTools ¶ added in v0.1.0
func (lm *LifecycleManager) ListTools() []ServerStatus
ListTools returns metadata for all registered MCP tool servers.
func (*LifecycleManager) RemovePlugin ¶ added in v0.1.0
func (lm *LifecycleManager) RemovePlugin(ctx context.Context, name string) error
RemovePlugin unregisters and removes [plugins.<name>] from denkeeper.toml.
func (*LifecycleManager) RemoveTool ¶ added in v0.1.0
func (lm *LifecycleManager) RemoveTool(ctx context.Context, name string) error
RemoveTool unregisters the MCP server and removes [tools.<name>] from denkeeper.toml.
func (*LifecycleManager) RestartTool ¶ added in v0.15.1
func (lm *LifecycleManager) RestartTool(ctx context.Context, name string) error
RestartTool stops and re-registers an MCP tool server, resetting its health state.
func (*LifecycleManager) ToolManager ¶ added in v0.1.0
func (lm *LifecycleManager) ToolManager() *Manager
ToolManager returns the underlying tool.Manager.
func (*LifecycleManager) TrackPlugin ¶ added in v0.1.0
func (lm *LifecycleManager) TrackPlugin(name string, cfg config.PluginConfig)
TrackPlugin registers a plugin that was loaded at startup so ListPlugins can report it. This avoids re-registering already-running plugins.
func (*LifecycleManager) UpdateDisabledTools ¶ added in v0.33.0
func (lm *LifecycleManager) UpdateDisabledTools(serverName string, disabledTools []string) error
UpdateDisabledTools updates the disabled tools for a server in memory and persists the change to TOML. No MCP reconnect is performed.
func (*LifecycleManager) UpdateTool ¶ added in v0.15.1
func (lm *LifecycleManager) UpdateTool(ctx context.Context, name string, cfg config.ToolConfig) error
UpdateTool replaces the configuration of an existing MCP tool server. It removes the old server and re-adds it with the new config atomically.
type Manager ¶
type Manager struct {
Auditor audit.Emitter // nil = no audit events
// contains filtered or unexported fields
}
Manager manages MCP tool server connections and tool execution.
toolMap, owners, localOf and toolDefs are one derived projection of the per-server tool lists, rebuilt as a whole by rebuildToolIndex on every registration change. Two servers may advertise the same tool name; when they do, neither is reachable under the bare name and both are advertised as "<server>__<tool>" instead (see toolindex.go).
func NewManager ¶
NewManager creates a manager with no servers registered.
func (*Manager) AdoptFrom ¶
AdoptFrom stores a reference to source as a parent manager. The child manager delegates tool lookups to the parent, so tools added to the parent at runtime (e.g. via the REST API) are immediately visible to all agents. Both managers share the same underlying *mcp.ClientSession pointers, which is safe for concurrent use.
func (*Manager) CleanupOAuthToken ¶ added in v0.16.0
CleanupOAuthToken removes the OAuth token for a tool, if any. Called during tool removal to avoid leaving orphaned tokens.
A live OAuth connection clears through its handler, which also drops the in-memory token source. Handlers exist only on live connections, though — a tool that is disabled, config-errored, pending, or not registered at all may still own a persisted row keyed by its name, so those fall through to a direct store delete. Otherwise the row survives and a tool later created with the same name silently adopts the stale token.
func (*Manager) Close ¶
Close shuts down all MCP server connections and OAuth handlers.
Shutdown follows the same two phases as UnregisterServer — every server is unpublished under one write lock, then drained and closed with no lock held. The drains run concurrently and under a short ceiling, so a process exit costs at most one drain window rather than one per server.
func (*Manager) Execute ¶
Execute runs a single tool call and returns the text result. If the tool is not found locally, it delegates to the parent manager.
The name the model called (bare when unique, "<server>__<tool>" when two servers share it) is resolved to its owning server; the server-local name is what goes on the wire. A bare name that two servers claim is never guessed at — it returns ErrAmbiguousTool naming the alternatives.
func (*Manager) GetOAuthHandler ¶ added in v0.16.0
GetOAuthHandler returns the OAuth handler for a tool, or nil.
func (*Manager) IsIdempotent ¶ added in v0.42.0
IsIdempotent reports whether toolName's results may be memoized within a single turn. In-process tools use the built-in allowlist; external MCP tools default to false unless their [tools.*] config opts in via idempotent / idempotent_tools, or via trust_annotations for tools the server marked readOnlyHint at discovery.
func (*Manager) MarkDisabled ¶ added in v0.29.0
MarkDisabled transitions a pending/connecting server to disabled state. Called when background init retries are exhausted.
func (*Manager) RegisterDisabled ¶ added in v0.36.0
func (m *Manager) RegisterDisabled(name string, cfg config.ToolConfig, reason string, isConfigError bool)
RegisterDisabled creates a placeholder entry for a tool that should not spawn a process. The tool appears in listings with its disabled reason. isConfigError distinguishes config validation failures from user-initiated disabling.
func (*Manager) RegisterPending ¶ added in v0.29.0
func (m *Manager) RegisterPending(name string, cfg config.ToolConfig, lastErr string)
RegisterPending creates a placeholder entry for a remote MCP server that is not yet connected. This makes the tool visible in the UI with "connecting" status while background retries are in progress. The placeholder is replaced by RegisterServer once the connection succeeds.
func (*Manager) RegisterServer ¶
RegisterServer connects to an MCP server (stdio subprocess or remote SSE) based on the transport field in cfg, and discovers its available tools.
func (*Manager) RegisterSession ¶
func (m *Manager) RegisterSession(ctx context.Context, name string, session *mcp.ClientSession) error
RegisterSession registers an already-connected MCP client session without spawning a subprocess. Use this for in-process servers (e.g. configmcp).
func (*Manager) RestartServer ¶ added in v0.15.1
RestartServer stops and re-registers an MCP server using its stored config. It resets the server's health state (disabled flag, error, restart count). If re-registration fails the server remains visible with status "error" so the user can retry or the health checker can pick it up.
func (*Manager) ServerInfo ¶ added in v0.1.0
func (m *Manager) ServerInfo(name string) (ServerStatus, bool)
ServerInfo returns metadata about a registered server. The second return value is false if the server is not registered. Checks the parent manager if the server is not found locally.
func (*Manager) ServerNames ¶ added in v0.1.0
ServerNames returns the names of all registered MCP servers, including those from the parent manager (if any).
func (*Manager) ServerResolvedURL ¶ added in v0.36.1
ServerResolvedURL returns the resolved (non-redacted) URL for a remote server. Returns empty string if the server is not found or has no URL.
func (*Manager) ServerToolConfig ¶ added in v0.15.1
func (m *Manager) ServerToolConfig(name string) (config.ToolConfig, bool)
ServerToolConfig returns the stored config.ToolConfig for a registered server. This is used to pre-populate edit forms. Returns false if not found.
func (*Manager) ServerToolDefs ¶ added in v0.16.0
ServerToolDefs returns tool definitions for a specific server, under the names they are advertised by (server-qualified when another server shares the tool name). Returns false if the server is not registered.
func (*Manager) SetDisabledTools ¶ added in v0.33.0
SetDisabledTools updates the in-memory disabled tool set for a server. No MCP reconnect is performed — changes take effect on the next ToolDefs() call.
func (*Manager) SetOAuthSupport ¶ added in v0.16.0
func (m *Manager) SetOAuthSupport(o *OAuthSupport)
SetOAuthSupport injects OAuth infrastructure into the Manager.
func (*Manager) StartHealthChecker ¶ added in v0.15.1
StartHealthChecker runs a background goroutine that periodically probes MCP servers and restarts crashed ones. It respects the mcp config settings: auto_restart, max_restart_attempts, and restart_cooldown.
func (*Manager) ToolDefs ¶
ToolDefs returns OpenAI-format tool definitions for all registered tools, including those from the parent manager (if any). Disabled tools are excluded from the result.
func (*Manager) ToolDescription ¶ added in v0.36.2
ToolDescription returns the MCP description for the named tool, or "" if the tool is not found or has no description.
Lookup is by *advertised* name, which is the surface the model calls: a bare name two servers claim has no definition of its own, so it returns "" rather than describing one of the two candidates.
func (*Manager) ToolNames ¶
ToolNames returns the names of all registered MCP tools, including those from the parent manager (if any).
func (*Manager) ToolServer ¶ added in v0.23.0
ToolServer returns the MCP server name that hosts the given tool. Returns an empty string if the tool is not found, or if a bare name is ambiguous — there is no single hosting server to name.
func (*Manager) UnregisterServer ¶ added in v0.1.0
UnregisterServer stops the MCP server for the given config name, removes its tools from the tool map, and closes the connection. Returns an error if the server is not registered.
Teardown is two-phase. Phase 1 runs under the write lock and only unpublishes the server: its tools leave the advertised set and new calls are refused with ErrServerDraining. Phase 2 runs with no manager lock held — it waits out the calls already executing, then closes the transport. The wait matters because the MCP SDK's ClientSession.Close blocks until outgoing calls retire; holding m.mu across it would freeze every other agent's tool calls, the health checker, and the tools API for the whole drain.
The two-phase shape follows the drain-before-withdraw teardown model in "A Programming Paradigm for Spatiotemporal Composability" (Shi, Zhang, Cui — https://github.com/cordiverse/paper/blob/main/paper.pdf): a component stops being offered before it is dismantled, and the effects already in flight are allowed to retire under a bounded window rather than being severed.
type OAuthHandlerFactory ¶ added in v0.16.0
type OAuthHandlerFactory func(name string, cfg config.ToolConfig, httpClient *http.Client) (oauthHandler, any, error)
OAuthHandlerFactory creates an OAuthHandler and its corresponding auth.OAuthHandler for use with StreamableClientTransport. The second return value is the transport-compatible handler.
func NewOAuthHandlerFactory ¶ added in v0.22.1
func NewOAuthHandlerFactory(store *oauth.TokenStore, pending *oauth.PendingManager, callbackURL string, logger *slog.Logger) OAuthHandlerFactory
NewOAuthHandlerFactory creates an OAuthHandlerFactory using the provided token store and pending manager. Called during wiring in main.go.
type OAuthStatusInfo ¶ added in v0.16.0
type OAuthStatusInfo struct {
HasToken bool `json:"has_token"`
NeedsReauth bool `json:"needs_reauth"`
}
OAuthStatusInfo is a non-sensitive view of OAuth state for API responses.
type OAuthSupport ¶ added in v0.16.0
type OAuthSupport struct {
// HandlerFactory creates an oauth.Handler for a tool. This is set from
// a build-tag-gated init in manager_oauth.go.
HandlerFactory OAuthHandlerFactory
CallbackURL string
// TokenStore deletes persisted tokens directly, independent of any live
// handler. Satisfied by *oauth.TokenStore. Needed because tokens are
// keyed by tool name alone: a tool that is disabled, config-errored, or
// pending at removal time has no oauthHandler, yet may still own a row.
TokenStore TokenDeleter
}
OAuthSupport holds OAuth infrastructure injected into the Manager.
type PluginStatus ¶ added in v0.1.0
type PluginStatus struct {
Name string `json:"name"`
Type string `json:"type"`
Command string `json:"command,omitempty"`
Image string `json:"image,omitempty"`
Args []string `json:"args,omitempty"`
Capabilities []string `json:"capabilities,omitempty"`
ToolNames []string `json:"tool_names"`
Status string `json:"status"`
}
PluginStatus exposes metadata about a registered plugin.
type RejectionError ¶ added in v0.38.2
type RejectionError struct {
Tool string // tool/function name that returned the error result
Text string // the error text the tool returned
}
RejectionError indicates a tool ran successfully at the transport level but returned an application-level error result (IsError=true) — typically because the model passed invalid or unusable arguments. It is distinct from genuine execution/transport failures (server unreachable, crashed, timed out), which surface as plain errors. Callers can use errors.As to classify a tool-call telemetry outcome as "rejected" (healthy tool, bad args) vs "failed".
func (*RejectionError) Error ¶ added in v0.38.2
func (e *RejectionError) Error() string
type ServerStatus ¶ added in v0.1.0
type ServerStatus struct {
Name string `json:"name"`
Command string `json:"command,omitempty"`
Args []string `json:"-"` // excluded from JSON (may contain secrets)
ArgsCount int `json:"args_count"` // safe count for display
ToolNames []string `json:"tool_names"`
Status string `json:"status"` // "connected", "connecting", "draining", "error", "disabled", "config_error", "pending_auth"
Transport string `json:"transport,omitempty"`
URL string `json:"url,omitempty"` // redacted
RestartCount int `json:"restart_count,omitempty"`
LastError string `json:"last_error,omitempty"`
UptimeSecs float64 `json:"uptime_secs,omitempty"`
AuthType string `json:"auth_type,omitempty"` // "oauth" or ""
OAuthStatus *OAuthStatusInfo `json:"oauth_status,omitempty"`
DisabledTools []string `json:"disabled_tools,omitempty"`
EnabledCount int `json:"enabled_count"`
TotalToolCount int `json:"total_tool_count"`
Enabled bool `json:"enabled"`
ConfigError string `json:"config_error,omitempty"`
}
ServerStatus exposes metadata about a registered MCP server.
type TokenDeleter ¶ added in v0.42.4
TokenDeleter removes a persisted OAuth token by tool name.