Documentation
¶
Overview ¶
Package mcpserver exposes spectackle over the Model Context Protocol.
Transport is stdio: only JSON-RPC 2.0 frames go to stdout, all logging goes to stderr (SPX-ARC-001). The server is the single source of truth for the spec lifecycle: every write to the versioned .spectackle/ folders happens here — the LLM never touches those files directly.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var Version = "dev"
Version is stamped into the MCP handshake and the CLI. Pre-1.0: anything may break between versions; the schema stamp and cache generation rotate with it instead of migrating. A var, not a const: release builds inject the tag via -ldflags "-X .../internal/mcpserver.Version=v0.x.y". When no ldflags ran (go install, plain go build), ResolvedVersion falls back to the module version Go embeds automatically — a go-installed v0.3.1 must never introduce itself as an old dev build (B-01KYJ66TWW: the hardcoded default made `go install ...@latest` look like it ignored the tag).
Functions ¶
func Manifest ¶ added in v0.2.0
func Manifest() string
manifest composes the full server-instructions manifest handed to the MCP initialize handshake: the static lifecycle instructions plus the defect-reporting paragraph, whose URL is only known at runtime. Manifest exposes the composed manifest for surfaces outside the MCP handshake: the CLI `manifest` subcommand prints it for humans and for the bench harness, which meters it as its own once-per-session line — its cost multiplier is sessions, not calls, and 7238 bytes stayed invisible through eleven benchmarked landings for lack of exactly this metering (T-01KYE3).
func ResolvedVersion ¶ added in v0.4.0
func ResolvedVersion() string
ResolvedVersion returns the version every surface should print: the ldflags-injected tag when a release build set one, else the module version from the embedded build info (present for `go install` and any module-aware build; "(devel)" for in-tree builds), else the dev default.
Types ¶
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server bundles the MCP server with its workspace, cache, graph and swarm coordination.
func New ¶
New detects the workspace starting at root, resolves the MAIN repo (a linked worktree resolves to its parent), scaffolds, opens the local index cache and the shared coordination DB, and registers all tools.
func (*Server) AgentName ¶ added in v0.2.0
AgentName is this server's swarm identity. The self-restart exec must carry it explicitly in the environment: defers never run across syscall.Exec, so Deregister is skipped, and a replacement that re-generates a random name (coord.GenName when SPECTACKLE_AGENT is unset) turns its own pre-swap leases into a dead foreign holder that blocks it and every sibling until the TTL sweep — found by adversarial review of the first self-restart draft (T-01KYEH). With the identity carried over, the skipped Deregister is harmless: the same row upserts and the leases are legitimately retained.
func (*Server) Busy ¶ added in v0.2.0
Busy reports whether any tool call is currently in flight — the self-restart watcher defers swaps until quiet (B-01KYF7).
func (*Server) Close ¶
Close releases the cache and coordination handles. The agent deregisters (leases + registry row) so short-lived sessions leave a clean swarm view; an open worktree's record survives in coord.db for reclaim.
func (*Server) SelfRestartEligible ¶ added in v0.2.0
SelfRestartEligible reports whether the committed-only self-restart watcher may operate on this serving root: a development build serving its own module (the issue #29 guard). Dropping this gate let the watcher act on ANY tree with a resolvable HEAD — flap-forever on foreign repos, and structurally able to build and exec foreign code over the server (adversarial cross-verification of ADR-01KYF5). Checked once at serve start; the answer is static for the process lifetime.
func (*Server) ServedDir ¶ added in v0.2.0
ServedDir is the root of the tree currently being served — the tree BinaryStale judges, and therefore the tree the self-restart rebuild must compile. The distinction from s.main.Dir is load-bearing: a server started inside a linked git worktree resolves main to the PRIMARY checkout, whose sources may lack exactly the changes that made the binary stale — the first live swap rebuilt there and exec'd a replacement that did not know its own flags.
func (*Server) SetSelfRestart ¶ added in v0.2.0
func (s *Server) SetSelfRestart()
SetSelfRestart marks the committed-only watcher active (see selfRestartOn).
type TypedPassState ¶ added in v0.2.0
type TypedPassState struct {
Added int // edges added; meaningful only when Cause == ""
// Cause is "" when the pass is healthy: it either completed over real
// packages, or found no Go module at root to check at all
// (index.ResolveTypedCalls treats that as nothing to do, not a
// failure) — either way there is nothing to warn about. Cause is
// non-empty exactly when the pass failed, and Cause == "" is
// deliberately the ONLY signal typedPassFinding trusts (not a separate
// "did it run" bool): the zero-value TypedPassState — before the very
// first reindex completes, or after a reindex whose IndexAll itself
// failed and left the previous state untouched — must read as healthy,
// not as a phantom degradation with an empty cause.
Cause string
// Packages is the number of packages that individually failed to
// load/type-check; 0 when Cause == "", or when the whole-module load
// failed before any per-package breakdown existed.
Packages int
}
TypedPassState is the go/types call-edge upgrade pass's outcome on the last successful reindex — whether it ran, and if not, why and how many packages were affected (issue 28). BuildGraph (server.go) returns this instead of a bare edge count precisely so the failure survives past the log line reindex's stderr always got: a resident server driven over stdio/HTTP, or the `call` subcommand, never sees stderr, so `state` answering "ok graph nodes=N edges=M" with no mention of the degradation left an agent trusting a `get depth` impact radius that had silently lost every cross-package typed call edge. Both stateGraphSection (state.go) and check() (tools.go) read it via typedPassFinding below, so the record's wording and gate live in exactly one place.
func BuildGraph ¶
func BuildGraph(ctx context.Context, ws workspace.Root, blobs store.Store) (graph.Graph, index.Stats, TypedPassState, error)
BuildGraph runs the full index pipeline — hand-written parsers first, then every langspec-registered language (adding a language is one data file in internal/langspec, no wiring), a syntactic pass over root, then the M3 go/types-resolved call-edge upgrade pass — and returns the resulting graph, the syntactic Stats, and the typed-call pass's outcome.
Exported and factored out of (*Server).reindex so the `spectackle reindex` CLI subcommand (main.go) can rebuild the exact same symbol graph drift classification depends on, instead of silently only resyncing the spec/doc cache while claiming to "reindex" (DEFECT 3 / contract-pending T-0107): the two call sites sharing this one function is what keeps them from drifting onto two different parser/resolver lists.