Documentation
¶
Overview ¶
Package mcp implements a Model Context Protocol server that exposes Locorum's daemon as a curated set of tools to local AI agents.
Locorum's MCP server is a thin shim: every tool call is forwarded to the daemon over the IPC socket. The daemon enforces the security boundary (per-site mutex, profile gating, scope checks). This package owns the protocol surface and the tool catalogue.
MCP (https://spec.modelcontextprotocol.io) is JSON-RPC 2.0 over either stdio (one frame per line) or Streamable HTTP. Locorum ships stdio first; HTTP is a follow-on (see AGENTS-SUPPORT.md P2).
Index ¶
Constants ¶
const MCPProtocolVersion = "2024-11-05"
MCPProtocolVersion is the version we declare in the initialize response. Negotiated against the client's claimed version; we accept any version for now and just echo back our own.
const ServerName = "locorum"
ServerName is the user-visible name reported in initialize. Pinned so MCP clients (Claude Code, Cursor, Continue) can recognise the server in their config UIs.
const TokenByteLen = 32
TokenByteLen is the size of the random source for an MCP HTTP auth token, before base64-encoding. 32 bytes (256 bits) gives a 43-char URL-safe string — well above the brute-force horizon for any local-network attacker.
const TokenFilename = "mcp_token"
TokenFilename is the basename of the auth token file under ~/.locorum/state/. The full path is built by TokenPath.
Variables ¶
This section is empty.
Functions ¶
func CompareTokens ¶
CompareTokens performs a constant-time comparison so a length-leak or timing-leak doesn't reveal token characters to an attacker. The strings package's == would be timing-leaky.
func LoadOrCreateToken ¶
LoadOrCreateToken returns the auth token, creating a new random one on first use. The file is written 0600 so peer users on the host cannot lift the token from disk. Idempotent across processes — the HTTP server reads the same value the user pastes into their MCP client.
func RotateToken ¶
RotateToken regenerates the token, replacing whatever was there. The old token stops working immediately; existing MCP clients must pick up the new value from disk (or be re-pasted into the IDE config).
Types ¶
type HTTPOptions ¶
type HTTPOptions struct {
Bind string
Token string
Server *Server // typically constructed via NewServer with stdio I/O wired to nil
Logger *slog.Logger
}
HTTPOptions configures the HTTP server. Bind is the listen address (e.g. "127.0.0.1:2484"); Token is the bearer secret clients send in `Authorization: Bearer <token>`.
type HTTPServer ¶
type HTTPServer struct {
// contains filtered or unexported fields
}
HTTPServer is the HTTP frontend for an existing daemon client. One goroutine handles the listen + accept loop; per-request dispatch is stateless so the http.Handler is goroutine-safe by construction.
func NewHTTPServer ¶
func NewHTTPServer(opts HTTPOptions) (*HTTPServer, error)
NewHTTPServer wires an HTTP listener around the existing dispatch engine. The caller must have already initialised opts.Server with a daemon Client; we don't second-guess the trust profile.
Refuses non-loopback binds unless the caller explicitly asks for one by passing a pre-resolved IP — we only check the literal Bind string against a safe-list. The CLI shows a clear error before reaching this point so the message reads naturally.
func (*HTTPServer) Addr ¶
func (h *HTTPServer) Addr() string
Addr returns the bound listen address. Useful in tests that pass :0 to pick a free port.
func (*HTTPServer) Serve ¶
func (h *HTTPServer) Serve(ctx context.Context) error
Serve binds the listener and serves requests until ctx cancels or Shutdown is called.
func (*HTTPServer) ServeHTTP ¶
func (h *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP is the http.Handler entry point — delegates to the mux. Defined explicitly so the type satisfies http.Handler with a custom error / log path.
func (*HTTPServer) Shutdown ¶
func (h *HTTPServer) Shutdown() error
Shutdown stops accepting new connections and waits up to 5s for in-flight requests to finish.
type Options ¶
type Options struct {
In io.Reader // defaults to os.Stdin
Out io.Writer // defaults to os.Stdout
Client *daemon.Client
Logger *slog.Logger
Scope string // optional MCP scope (LOCORUM_MCP_SCOPE)
Profile string // "full" or "readonly"
Version string // locorum binary version
}
Options configures a new MCP server.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the MCP stdio server. Constructed once per process; the caller wires stdin/stdout and a daemon client, then calls Serve.
Server is safe to use from a single goroutine only. MCP stdio is one-frame-at-a-time, no pipelining required by the spec.
func NewServer ¶
NewServer constructs a Server. The caller is responsible for opening the daemon client and passing it in: this package never auto-spawns a daemon (the parent process — `locorum mcp serve` — does that before instantiating us).