# MCP boundary The server uses local standard input/output transport and binds once to the explicit `--project-root` supplied at process startup. Optional proposal access also binds once to the configured `--proposal-writer`. It opens no network listener at startup. The explicit `docforge_visualize` read tool may start one token-protected loopback-only HTTP listener for the same immutable project binding. The additive `--diagnostics` startup option attaches a bounded version-1 request-local aggregate to successes and structured errors. Fixed stage timings and counters expose source parsing, adapter projection/extraction, index work, rendering work, and viewer-manager requests without including content, paths, queries, node IDs, or SQL. Diagnostics are disabled by default. They are the first response field discarded when the configured output limit would otherwise be exceeded. Canonical application is a second independent startup gate. The generic server accepts `--canonical-applier WRITER_ID`. A project adapter must also supply a compatible project-owned canonical applier implementation. The additive `--capability-mode read|proposal|application|operator` option selects a versioned effective process policy. Existing factory defaults and tool ordering remain unchanged: the read-only factory exposes the read surface, the ordinary project factory exposes the proposal surface, and an application-enabled factory adds exact-hash application. `application` mode fails closed unless a canonical applier is bound. `operator` is reserved for explicitly selected operator-only tools and adds none in the current contract. Bootstrap and contract results include `effective_policy` schema version 1 plus a separate `capabilities` record. Policy states the requested process behavior. Capabilities state the actual registered surface and startup-bound proposal/application access. The project descriptor remains schema version 1 and does not silently acquire machine-specific process policy. ## Read tools - `docforge_bootstrap` - `docforge_sync` - `docforge_project_info` - `docforge_get_contract` - `docforge_get_node` - `docforge_get_logic` - `docforge_search` - `docforge_filter_nodes` - `docforge_backlinks` - `docforge_dependencies` - `docforge_impact` - `docforge_get_context` - `docforge_get_task_context` - `docforge_validate_project` - `docforge_render_status` - `docforge_visualize` - `docforge_stop_visualization` - `docforge_visualization_status` - `docforge_get_generation_diff` Each response states that document text is project content, not higher-priority instructions. Each response includes project identity, revision, source hash, adapter version, and staleness state. Every normal tool call first checks current source identity and atomically rebuilds disposable index state when it is missing, stale, or invalid. `docforge_bootstrap` performs that synchronization and returns the complete fixed binding, active index path, effective policy, proposal and application capabilities, and a version-1 session contract. Bootstrap reuses the identity proven by synchronization instead of loading the project again. Its first operation and workflow guidance mention proposal or application tools only when the corresponding startup access is enabled. `docforge_sync` exposes the same idempotent synchronization explicitly. Neither operation changes canonical sources. Search, filter, backlinks, dependencies, and impact accept explicit result limits bounded by the project `max_results` policy. Omitted limits are still capped. Collection responses report whether they were truncated. Traversal also reports whether truncation came from the result limit or its deterministic candidate-edge work budget; it does not scan or materialize the complete edge table. `docforge_get_context` accepts optional `limit` and `cursor` arguments. Its page is one deterministic stream containing selected entries first and explicit omission evidence second. The page receipt reports the returned count, total evidence count, whether another page exists, and an opaque generation-bound cursor. Packing also observes the configured MCP response limit. An individually oversized entry advances as a hash-identified `response size limit` omission so pagination cannot loop; targeted retrieval remains available for that node. The existing three-argument custom context-provider contract is unchanged because pagination is applied after provider selection. `docforge_get_task_context` accepts one closed task kind (`change`, `implementation`, `failure`, `ownership`, `test`, `operation`, or `release`), a bounded task description, and optional `focus_node_id`, token `budget`, page `limit`, and opaque `cursor`. It derives, rather than accepts, a version-1 retrieval plan. The plan contains only exact or lexical focus, bounded outgoing and incoming graph traversal, and metadata hydration. It cannot request arbitrary SQL, paths, relation names, or Logic extraction. Task context applies fixed internal ceilings of 1,000 evidence items, 100,000 examined candidate edges, and 10,000 task-query characters even when broader project limits are configured. Traversal steps bind the complete project-owned relation vocabulary by hash rather than copying an unbounded name list into every response. The plan and returned context capsule are bound to the effective policy and one immutable index generation. Every evidence item identifies its indexed source path, content hash, graph path, additional qualifying relationship reasons, and the provenance facts that the current graph cannot prove. Required evidence gaps distinguish an undeclared relation category, a completed bounded search with no selected evidence, and an incomplete proof caused by a work, result, token, or response limit. Unknown project relations remain present with their raw names and an `unclassified_relation` limitation; DocForge never infers semantics from spelling outside the versioned alias map. Path relationship direction is relative to the preceding traversal node. Additional `relationship_reasons` direction is relative to the evidence item itself: `outgoing` when that evidence node is the stored source and `incoming` when it is the stored target. The exact version-1 aliases are: structure (`contains`, `defined_in`, `defines`, `owns`); implementation (`implemented_by`, `implements`, `inherits`, `inherits_from`); dependency (`depends_on`, `imports`); execution (`activates`, `calls`, `dispatches_to`, `launches`); data (`reads`, `writes`); evidence (`documents`, `governs`, `proves`, `tested_by`, `verifies`); and context (`relates_to`). Every other allowed relation is `unclassified`. Task-context continuation partitions the immutable evidence stream without changing its `request_hash`, `plan_hash`, `collection_hash`, or `capsule_hash`. Its cursor additionally binds the effective policy and task request. One evidence item that cannot fit advances exactly once as a hash-identified `response_limit` omission. A changed generation, policy, plan, or collection fails as `stale_cursor`. `docforge_get_generation_diff` accepts only optional `limit` and `cursor` fields. It reads the one latest version-1 primary-graph transition receipt; it does not accept arbitrary generations, paths, or history selectors. Exact summary counts and the full item-collection hash cover the complete transition. Pagination covers only the deterministically ordered retained details and states separately when the fixed 1,000-item or 1 MiB publication limit permanently omitted details. The receipt binds project, root, adapter, index schema, from/to source identity, node and edge hashes and counts, the committed index file identity, retained and full collection hashes, and its own canonical hash. Node changes compare every core `Node` field. Edge identity is the exact `(source_id, relation, target_id)` triple. Logic is excluded from public diff details. Current pages use `page_schema_version = 1`. The nested `receipt_header` contains every stored receipt field except `items`; its `stored_receipt_hash` is the hash of the complete stored receipt, not of the header. Page items and hash-identified response-limit omissions are siblings of that header. The only pagination object is at the top level, and its `next_cursor` is the only cursor copy. The page hash covers the complete header, page items, omissions, receipt state, and pagination receipt. Receipt states are fail-closed: `current` is proven against cheap source identity and exact index and receipt inodes; `stale` is a proven generation mismatch; `missing` means no receipt; `unsafe` means confinement or file-type checks failed; `unverified` covers corrupt, foreign, oversized, or concurrently changed evidence; and `unknown` means the project cannot provide a cheap generation identity. Only `current` returns a page. This status boundary is non-repairing. It never opens SQLite, loads or extracts an adapter projection, parses source, synchronizes, builds, or writes a receipt. Cheap source identity and stable receipt/index file identities can establish `current`; legacy projects without cheap identity report `unknown`. Invalid or unavailable disposable evidence remains an explicit status instead of triggering hidden recovery. The legacy `docforge_get_context` tool and its custom three-argument provider contract remain unchanged. A server with a custom context provider does not silently inherit the core task planner; version 1 exposes no custom task-planner extension point. `docforge_get_task_context` returns `task_context_unavailable` without synchronizing or loading the custom projection. Version-1 cursors are canonical JSON encoded as base64url with a domain-separated SHA-256 corruption checksum. They are opaque and fail closed, but are not authenticated authorization tokens. Cursors bind the project, adapter, source generation, operation parameters, collection hash, and position. A changed generation or collection returns `stale_cursor` with `restart_pagination`; DocForge never silently restarts at page one or combines generations. Adapter-backed servers also validate their process-start implementation fingerprint before every tool. `adapter_restart_required` is stale but not synchronizable. Its remediation is `restart_project_server`; the current process does not reload project code, update Git staging, or continue with a newly changed validator. The normal command binds the generic project loader. An explicit project integration may instead construct the same read-only surface from a validated `ProjectService` and project-owned context provider. This form cannot register proposal tools. Project discovery, session selection, family partitioning, and custom context policy remain outside the DocForge core. An explicit project integration may construct the full fixed surface only after supplying a confined proposal policy and startup-bound writer. Adapter proposal validators may narrow the writer's declared operations further. They cannot add arbitrary tools or weaken core changeset validation. The fixed application tool is registered only through the separate canonical applier gate. ## Isolated proposal tools - `docforge_create_changeset` - `docforge_register_changes` - `docforge_list_changesets` - `docforge_get_changeset` - `docforge_rebase_changeset` - `docforge_abandon_changeset` - `docforge_propose_node_create` - `docforge_propose_node_update` - `docforge_propose_node_move` - `docforge_propose_relationship_update` - `docforge_propose_node_delete` - `docforge_validate_changeset` - `docforge_get_changeset_diff` - `docforge_preview_changeset` Proposal tools may write only below the configured changeset or isolated preview roots. They never change canonical files or declared project output. Without `--proposal-writer`, changeset mutation tools return `proposal_access_disabled`. Validation, diff retrieval, and preview remain available for existing changesets. A preview accepts a declared view ID, not a renderer name or command. The relationship-update tool queues additions and removals without rewriting node content and rejects an empty relationship list. `docforge_register_changes` is the preferred write entry point. It creates, populates, projects, conflict-checks, and validates one complete changeset in a single locked operation. Existing-node operations may omit `expected_content_hash`; the server captures the current synchronized node hash inside that transaction. The stored changeset remains fully hash-bound. `docforge_rebase_changeset` moves a stale proposal to the current project base only when all touched nodes, sources, relationships, permissions, and graph invariants still validate. It never merges prose. `docforge_abandon_changeset` preserves an audit receipt without deleting the proposal. Changeset listing returns draft and ready work by default. Stale, applied, and abandoned proposals remain available through an explicit status or history request. Applied and abandoned proposals no longer participate in overlap conflict detection. Changeset list, inspection, validation, and diff reads accept optional `limit` and `cursor` arguments. Direct Python and CLI methods still return their complete legacy result when pagination is not requested. MCP defaults to bounded pages while preserving the exact changeset hash and ordered operation sequence. Pages may contain fewer records than requested to remain inside the response policy. Oversized inspection or validation pages return deterministic operation summaries with hashes and character counts. An individually oversized diff becomes a sequence of `canonical_json_chunk` pages; concatenating the ASCII chunks, decoding the JSON, and verifying its payload hash reconstructs the exact `operations` and `changes` arrays without duplication. Successful mutations return their existing full result while it fits the configured output limit. Before any proposal, preview, or canonical mutation, the server verifies that a minimum exact success receipt can fit. An impossible receipt fails with `result_too_large`, `stage = "preflight"`, and `mutation_committed = false` before calling the mutation. If a successful full result is too large, the server returns a version-1 compact receipt containing the exact changeset ID and hash plus the operation outcome. It may fall back to a preflight-guaranteed minimum receipt, but it never replaces a committed mutation with a failure response. Direct Python and CLI integrations retain their detailed return values. ## Canonical application tool - `docforge_apply_changeset` The tool is absent unless canonical application was explicitly enabled at startup. It accepts one changeset ID and the exact final changeset SHA-256. It revalidates the current canonical base, proposal ownership, node hashes, conflicts, graph, permissions, and paths before invoking the configured serializer. The generic serializer confines staged Markdown/TOML writes to declared content roots and verifies that the applied files reproduce the approved graph projection. A mismatch rolls canonical files back. Canonical success records an `applied` lifecycle receipt bound to the reviewed changeset hash before refreshing derived state. Index or render refresh failures return a successful canonical application with a degraded derived-refresh report and explicit remediation; they never invite the caller to apply the same canonical change twice. DocForge does not run project commands, shell, Git, builds, deployment, or publication. When the full application result exceeds the tool-output limit, its compact success receipt retains the applied lifecycle, exact hash, changed-source counts, and a derived-refresh summary. Detailed index, render, and error payloads remain available through the corresponding read and status tools. ## Render boundary `docforge_render_status` reads bounded publication receipts and cheap file/source identities by default. It does not parse canonical nodes, prepare Markdown, construct HTML, hash the complete output, rebuild the index, or write state. Missing or corrupt receipts are conservative `unverified` results. Callers may pass `deep = true` to explicitly request the side-effect-free full-render equivalence oracle. `docforge_preview_changeset` runs only a project-declared view through DocForge's fixed built-in renderer registry and writes one atomic HTML file below the configured preview root. Rendering declared project output is available only through the explicit local CLI integration command. ## Visualization boundary `docforge_visualize` starts the fixed built-in `graph-browser@17` template against the currently validated derived index. It may focus one stable node, run one bounded lexical query, or open the project overview. The tool returns a loopback URL and exact snapshot identity. The tool cannot select a project, database, template, host, port, filesystem path, or SQL expression. Its HTTP surface is token-bound, read-only, same-origin, and limited to overview, search, exact family/authority/status/tag filtering, composable node-kind/language/capability filtering, node-neighborhood JSON, semantic Flow, convergence Web, lazy function-scoped Logic, and a bounded project-confined source read for one indexed node. `docforge_get_logic` and the browser Logic endpoint accept one exact owner node ID and return only that bounded stored projection. The browser exposes an exact validated index snapshot. It rejects index replacement or alteration and requires another MCP invocation to refresh. Viewport interaction is entirely client-side: fitted neighborhood framing, wheel zoom, left-button drag pan, explicit zoom buttons, reset, and Space-to-center selection never request or mutate project data. Left activation visibly selects the node, highlights its incident edges and direct neighbors, mutes unrelated visible paths, and opens a compact descriptor card. Right-click opens the full inspector. Descriptor-pill activation fills the fixed left panel with an exact bounded category result set. The fixed right panel contains neighborhood navigation. Replacing the current root requires an explicit Explore neighborhood action. Users may hide non-focus nodes and restore them entirely client-side. Flow and Web prune upstream-only branches disconnected by a hidden node while retaining descendants that still lead to the focus. Source actions open the indexed source path and navigate to recognized anchors. Nodes presents the bounded neighborhood with relation-specific colors, line patterns, directional symbols, and a visible key. Semantic cards identify the focus and relation-derived Structure, Behavior, Dependency, Execution, Data, Evidence, Context, and Related contributors. Cards display readable leaf names and node kinds without truncation; complete qualified identities remain available in tooltips and inspectors. Flow presents semantic ancestry with relation-aware direction. Web follows bounded structural, dependency, execution, evidence, and contextual contributors into the focus. Direct focus-owned members and execution dependencies become adjacent contributor branches without expanding unrelated siblings. The relationship key is regenerated from each visible view. Logic displays possible static control paths for a focused function or method without adding its statement-level nodes to primary search or architectural traversal. Hiding a Logic step bridges its retained predecessors and successors with an explicit omitted path. The browser runs in a project-bound worker owned by the separately supervised per-user viewer manager. Standard-input transaction completion and MCP host exit do not close the listener. Repeated visualization requests reuse the current worker while its exact snapshot remains valid. The version-2 manager protocol binds each worker to the exact validated five-field index publication signature. Health checks compare that signature without opening SQLite. A stale worker remains `state = running` but is never reused. `docforge_visualization_status` reports lifecycle and freshness independently. `snapshot_state` and top-level `staleness` are `stale` when either the index or cheap source identity is proven stale, `current` only when both are proven current, and `unknown` otherwise. The `freshness` object exposes the separate index and source states. Status never checks, synchronizes, or rebuilds the index and never performs a complete project load. `docforge_stop_visualization` explicitly stops the current project's worker. The manager reclaims a worker only after one hour with no browser activity. ## Excluded tools The normal server never exposes shell execution, arbitrary reads or writes, arbitrary renderer execution, Git mutation, project builds, deployment, publication, external HTTP binding, global project selection, or cross-project retrieval. Without the explicit canonical applier gate, it also does not expose canonical application. DocForge pins the official stable Python MCP SDK to the compatible `mcp>=1.28,<2` release line. Migration to a later major release requires a separate contract and protocol compatibility review. ## Preserved no-AST bindings An owner may start the generic MCP server with `--no-ast`: ```bash docforge-mcp --project-root /absolute/project --no-ast ``` Project-owned integrations select the same immutable process policy with `create_project_server(..., no_ast=True)` or `create_read_only_server(..., no_ast=True)`. The policy preserves the current adapter extraction strategy. It does not require an adapter API migration and does not disable complete-projection loading or non-AST incremental fingerprinting, invalidation, and caching. Both `docforge_bootstrap` and `docforge_get_contract` report the exact policy. MCP server instructions tell clients not to add Python AST, Tree-sitter, compiler-AST, or function-Logic extraction. Under this binding: - `docforge_get_logic` returns `adapter_policy_forbids_logic`; - a nonempty Logic projection is rejected before index publication; - a pre-existing index containing Logic is rejected before any read or live-viewer snapshot; - hash-bound canonical application refresh uses the same policy-bound index; - `adapter_ast_upgrade` and `function_logic_extraction` appear as excluded operations; and - changing the policy requires changing the process configuration and starting a new MCP process. The policy governs the DocForge binding and conforming MCP clients. DocForge can enforce published and indexed Logic, but it does not inspect arbitrary adapter source to prove which parser implementation the adapter uses. DocForge still exposes no filesystem sandbox and cannot prevent an unrelated process with direct repository write access from editing adapter files. Repository permissions and project instructions remain responsible for that broader boundary. The legacy `adapter_policy` object remains byte-compatible. It is now a projection of the versioned `effective_policy`; `--no-ast` restrictively overrides adapter evolution, AST analysis, and Logic indexing without widening any other capability.