MCP surface

August 5, 2026 · View on GitHub

Gortex exposes a knowledge-graph query surface over the Model Context Protocol: 100+ tools, 18 resources, 3 prompts. Agents call the same surface from stdio, the daemon Unix socket, or the MCP 2026 Streamable HTTP endpoint.

Compact MCP surface

The compact surface consolidates the legacy catalogue into 21 domain tools with compact, stable schemas. Every MCP connection with a non-empty clientInfo.name selects it automatically in hide mode unless a higher-precedence forwarded, operator, or instruction-profile policy overrides it. Empty and pre-initialize sessions retain the server default. Select it explicitly with the neutral compact preset alias:

GORTEX_TOOLS=compact gortex mcp

With that surface, the client receives all 21 names in its first tools/list: explore, search, read, relations, trace, analyze, ask, change, review, pr, recall, workspace, response, capabilities, edit, refactor, remember, workspace_admin, overlay, session, and publish_review. They are static for the session—there is no tools_search promotion or tools/list_changed dependency. capabilities discovers operation schemas, not additional tool names. Session-lifetime controls use session, for example {"operation":"subscribe","channel":"diagnostics"}; durable workspace changes remain under workspace_admin.

// Read a source file.
{"name":"read","arguments":{"target":{"file":"internal/mcp/server.go"}}}

// Preview a file edit; omit dry_run (or set false) to apply it.
{"name":"edit","arguments":{"target":{"file":"internal/mcp/server.go"},"match":"old text","replacement":"new text","dry_run":true}}

// Fetch the exact schema for a read operation.
{"name":"capabilities","arguments":{"domain":"read","operation":"file","detail":"schema"}}

The compact surface delegates to the existing handlers. Existing agent, core, full, and specialist presets retain their legacy schemas; the CLI, HTTP routes, and legacy MCP names remain compatible. Names shared by both surfaces (such as explore, analyze, and review) advertise the compact schema only in a compact session. See the compact MCP surface specification for effects, schemas, migration, and acceptance gates.

Authorization follows observable effects. analyze is strictly read-only: blame, coverage enrichment, SQL rebuild, and Temporal verification are exposed through workspace_admin; model-assisted concepts/search and lazy graph enrichment are fixed off on local read operations. Stateful nav is exposed as session(operation="cursor"). change.contract cannot acknowledge risk; durable acknowledgement is explicit through remember(operation="risk_ack").

Tool discovery (lazy mode)

The fallback server default is a curated core preset in defer mode (see presets): ~34 dev-cycle workhorse tools are published eagerly in the initial tools/list, and the rest of the ~180-tool catalogue is deferred—fetched on demand through tools_search. Named MCP clients instead default to the static compact surface described above. Opt into the full eager surface with preset full (GORTEX_TOOLS=full).

tools_search returns each deferred tool's schema inline (in a <functions>{…}</functions> block) and promotes it into tools/list, firing notifications/tools/list_changed. Clients that honour that notification (or read the inline schema) reach deferred tools transparently. GORTEX_LAZY_TOOLS=1 is the older, all-or-nothing switch that defers everything except a hard-coded hot set regardless of preset; the core/defer default supersedes it for the common case.

// With GORTEX_LAZY_TOOLS=1 set:
// Browse — list deferred tool names without schemas.
{"name":"tools_search","arguments":{}}

// Fetch schemas for specific tools by name (auto-promotes them into tools/list).
{"name":"tools_search","arguments":{"query":"select:flow_between,taint_paths,find_clones"}}

// Keyword search with required-token filter, ranked, capped at max_results.
{"name":"tools_search","arguments":{"query":"+overlay drop","max_results":5}}

// Fuzzy keyword match across name + description.
{"name":"tools_search","arguments":{"query":"memories invariants"}}

Returned tools are auto-promoted (promote:false opts out) and the server fires notifications/tools/list_changed. The tool_profile tool reports the active surface — which tools are live vs. deferred, their scopes and categories, the active preset (below), and (with a tool argument) a single tool's enabled status.

Two front doors over one set of handlers. The same tool handlers back both the MCP transport and the gortex CLI. The daemon routes a tool call by name over its socket, independent of which tools a given client eagerly published in tools/list — so the CLI reaches the full surface, including tools that are deferred under the core preset, with no tools_search round-trip. gortex call <tool> invokes any tool by name, and the dedicated CLI verb groups (gortex edit …, gortex memory …, gortex analyze, gortex flow / taint / clones / feedback) are ergonomic front-ends over the most-used tools. Driving Gortex through those verbs mounts no tool schemas into the model's context — see cli.md and the consumption-path trade-off in cli.md.

Restricting the tool surface (presets)

The full ~180-tool surface is more than many agents need. A tool preset picks what the server publishes — the basis both for the lean shipped default and for a minimal, headless editing harness (an agent on a trusted box driving a remote daemon through a small, fixed tool set).

Eight built-in presets:

PresetSurface
facade-v1 (default for named MCP clients)Stable config identifier for the 21 static, effect-homogeneous domain tools; operation schemas are discovered through capabilities, with no tool promotion. Aliases: compact, facade, agent-v2
agentLegacy lean coding-agent working set (~20 tools): explore (the one-shot localization verb) + search/navigate + read (incl. batch_symbols) + orient + edit/verify. Parameter descriptions are compacted (the full prose is one tools_search / full hop away). Aliases: coding-agent
core (fallback server default)the curated dev-cycle set (~35 tools): orient (incl. explore) + search/navigate + read + edit + verify/test + analyze + review + the memory workflow. Aliases: default, classic
fullevery tool (the pre-core behaviour — opt back in here)
readonlyeverything except the mutating tools (edit_file, write_file, index_repository, …)
editthe minimal headless editing set — orient + navigate + mutate + verify (smart_context, search_symbols, find_files, edit_file, verify_change, get_test_targets, …)
navread-only navigation / exploration; no editors
localizationthe diet "where is the code that does X" set (~10 tools, read-only, compacted descriptions): smart_context + search + trace + read. The eager list is sourced from the instruction-profile table, so this surface and the localization profile's instructions body cannot drift. Aliases: locate, find

For legacy presets, tool_profile and tools_search are always kept. The compact surface uses capabilities instead and is closed: it always contains exactly its 21 public tools, so allow / deny deltas are ignored for facade-v1. Select a legacy or custom surface when per-tool deltas are required.

Client-aware default. With no higher-precedence selection, every connection with a non-empty MCP clientInfo.name gets the compact 21-tool surface in hide mode. Empty and pre-initialize sessions retain the server default. Client identity and wire format are separate: an unknown named client still gets the compact tools but remains on JSON unless it is independently GCX-capable. GORTEX_TOOLS always overrides. The gortex mcp proxy forwards its GORTEX_TOOLS / --tools to the daemon in the handshake, so a client's preset applies over the shared daemon (it can both narrow and widen the surface, not just subtract).

Instruction profiles. The machine's active instruction profile (gortex instructions switch <core|localization|full> — see cli.md) can carry a tool preset; sessions pick it up between the forwarded spec and the client-aware default. Full precedence: forwarded spec (GORTEX_TOOLS / --tools) > operator-pinned mcp.tools config > active instruction profile > client-aware default > server default. The shipped core profile carries no preset, so nothing changes until a machine explicitly switches; profile changes apply to new sessions only.

Two modes (mode):

  • defer (the default mode for core) — non-allowed tools are kept out of the cold tools/list but stay reachable through tools_search, which returns their schema inline and promotes them (firing notifications/tools/list_changed). The lean-but-complete surface: nothing is lost, the rare tool is one discovery call away.
  • hide (the default for facade-v1 and the explicit edit / nav / readonly harness presets) — non-allowed tools are removed from tools/list and calls to them are hard-blocked. The locked-down surface; works identically on every client.

Select a preset three ways (precedence: env > flag > config > default):

# .gortex.yaml — config file
mcp:
  tools:
    preset: full          # compact | agent | core (default) | full | readonly | edit | nav
    mode: defer           # defer | hide
    allow: [find_files]   # add tools on top of the preset
    deny: [write_file]    # remove tools from the preset
# env (overrides config) — spec is "preset,+add,-remove"
export GORTEX_TOOLS="edit,+analyze,-write_file"
export GORTEX_TOOLS_MODE=hide

# CLI flags (override config; env still wins)
gortex mcp --tools edit --tools-mode hide
gortex daemon start --tools readonly      # propagates to the detached child

Per-connection (client-driven) scoping. The selectors above applied to gortex daemon start narrow the whole daemon for every client. To let one client pick its own surface while the daemon keeps serving the full set to everyone else, set --tools / GORTEX_TOOLS on that client's gortex mcp invocation — the stdio proxy filters just that connection's tools/list and blocks calls to tools outside the set. Because the filter applies from the first tools/list, it works on every MCP client (no tools/list_changed dependency).

// An MCP client config giving this client a minimal editing surface,
// against a daemon that still serves the full catalogue to others:
{
  "command": "gortex",
  "args": ["mcp", "--tools", "search_symbols,find_files,edit_file,verify_change"],
  // or: "args": ["mcp", "--tools", "edit"]  (a named preset)
  // or: "env": { "GORTEX_TOOLS": "readonly" }
}

A spec whose first token isn't a known preset (search_symbols,find_files,…) is an explicit allow list — exactly those tools — for experts who want to hand-pick the surface. A known preset followed by names (edit,find_files) keeps preset semantics plus the extra tools.

tool_profile reports the active preset / preset_mode, the narrowed live set, and a categories{} map grouping every tool into a functional family (nav / read / edit / analysis / review / pr / memory / overlay / subscription / enrich / workspace / admin) for prefix-style filtering.

Prompt-injection screening. Every tool call is screened by middleware that scans arguments and result text for injection patterns. On a hit it attaches a non-blocking _meta.gortex_security advisory — the call still succeeds and the result body is never mutated. Disable with GORTEX_MCP_SANITIZE=0.

Core navigation

ToolDescription
graph_statsNode/edge counts by kind, language, per-repo stats, session token savings, and an edge_identity_revisions counter (edges re-keyed when their provenance changed)
search_symbolsFind symbols by name (replaces Grep). Inline kind:/flavor:/lang:/path: field clauses (also a top-level flavor param) + query_class / max_per_file tuning; accepts repo, project, ref, scope params. flavor: filters type nodes by their structural shape — class/struct/enum/interface/trait/protocol/object/record/type_alias/newtype/message/service/table/view/module/… — with flavor:component spanning every UI component (React / Vue / Svelte / SwiftUI / Compose / Flutter / Angular / …); a kind:class-style value that is only a flavor routes to this filter automatically. corpus: code|docs|all selects the corpus (docs has its own retrieval channel + prose-tuned ranking); vocab_anchored: true constrains LLM expansion to the repo's own vocabulary; a zero-result identifier query is auto-decomposed into leaf terms (decomposed: true)
search_textTrigram-accelerated literal (or regexp: true) code search across the repo — the alt grep backbone. Returns file/line/text rows, each carrying the enclosing symbol (symbol_id / symbol_name)
find_filesFind source files by name — the file-name counterpart of search_symbols. query (basename/path substring, ranked exact > prefix > substring) and/or glob (e.g. internal/**/*_test.go), with optional fuzzy subsequence matching and path / repo scoping. File nodes are excluded from the symbol index, so search_symbols kind:file cannot return them — use this
winnow_symbolsStructured constraint-chain retrieval — kind, language, community, path_prefix, min_fan_in, min_fan_out, min_churn, text_match with per-axis score contributions
get_symbolSymbol location and signature (replaces Read). Accepts repo, project, ref params
get_file_summaryAll symbols and imports in a file. Accepts repo, project, ref, max_bytes / max_tokens budget caps
get_editing_contextPrimary pre-edit tool — symbols, signatures, callers, callees. Accepts max_bytes / max_tokens budget caps; compress_bodies stubs bodies, and fidelity_globs (e.g. internal/**:full,*_test.go:omit,vendor/**:compress) sets a per-glob full/compress/omit tier
get_repo_outlineNarrative single-call repo overview — top languages, communities, hotspots, most-imported files, entry points
plan_turnOpening-move router — returns ranked next calls with pre-filled args for a task description (~200 tokens)

Graph traversal

ToolDescription
get_dependenciesWhat a symbol depends on
get_dependentsWhat depends on a symbol (blast radius)
get_call_chainForward call graph. Accepts max_bytes / max_tokens budget caps
get_callersReverse call graph. Carries a caveat whenever the answer must not be taken at face value, so a pre-edit safety check isn't silently disarmed: likely_unused (indexed, nothing uses it), possible_extraction_gap (no edges at all — the extractor probably missed it), or coverage_incomplete (the only evidence is import-level, unresolved same-name candidates, or callers matched by name alone — including a populated caller list where every row is a name-only match)
find_usagesEvery reference to a symbol. Each usage carries its reference context (parameter_type / return_type / field / value / type / attribute / call); pass context: to filter (e.g. "where is this type used as a parameter?"). flavor: filters by where a usage originates — a type flavor resolves the usage's enclosing owner type ("usages from inside a struct"), and flavor:component keeps usages originating inside a UI component; each usage surfaces the resolved from_type_flavor / from_ui_component. Accepts max_bytes / max_tokens budget caps. Carries the same caveat as get_callerslikely_unused / possible_extraction_gap / coverage_incomplete, the last also on a populated result whose every usage is a name-only match
find_implementationsTypes implementing an interface
find_overridesMethods that override (children) or are overridden by (parents) a method — backed by EdgeOverrides
get_class_hierarchyMulti-hop inheritance subgraph around a type, interface, or method. Walks EdgeExtends + EdgeImplements + EdgeComposes (type nodes) and EdgeOverrides (method nodes); direction ∈ up / down / both, include_methods pulls members + their override chain
get_clusterBidirectional neighborhood

Search & traversal extensions

ToolDescription
find_declarationUse-site → declaration resolver. Accepts a literal substring or (with regex: true) a regex matching a use site like fooBar(; returns the declaration node plus the matching use locations. Trigram-prefiltered. Optional path_prefix / kind filters
walk_graphToken-budgeted free-form graph traversal — walks arbitrary edge_kinds (CSV) outward / inward / both from a starting symbol; auto-stops at token_budget. Surfaces budget_hit / stopped_at_depth on the response. community (ID or label) confines the walk to a detected community
context_closureDependency-closure context selection — given a set of seed files / symbols, walks the transitive import / dependency closure and packs it under one token_budget (reusing the graded-manifest tiers), ranked by graph distance from the nearest seed or, with rank: "proximity", by seeded random-walk proximity
graph_queryAd-hoc graph-query escape hatch — small read-only DSL with nodes / traverse / filter stages joined by |, e.g. nodes kind=interface name~Handler | traverse implements in | filter path=internal/mcp/. Bounded by limit and a five-stage cap
navPer-session symbol cursor — verb-dispatched via action: goto / into (a callee) / up (a caller) / sibling / back / where / read. Adjacency preview rides on every response; the cursor lives in session state and resets on disconnect

Dataflow (CPG-lite)

ToolDescription
flow_betweenRanked dataflow paths between two symbols — walks value_flow / arg_of / returns_to edges
taint_pathsPattern-driven source→sink dataflow sweep for security and architecture audits
ToolDescription
search_astCross-language structural search by AST shape — raw tree-sitter S-expression pattern or a bundled detector (e.g. sql-string-concat, weak-crypto, hardcoded-secret)

Diagnostics & code actions

Wired across every running language server (gopls, tsserver, pyright, rust-analyzer, …). Server-driven capability registration via client/registerCapability / client/unregisterCapability is honoured live, so servers (jdtls, tsserver, rust-analyzer) that announce features after initialize no longer return empty results.

ToolDescription
subscribe_diagnosticsOpt the session into push notifications/diagnostics; initial state replays immediately, deltas thereafter. Filter by min_severity / path_prefix
unsubscribe_diagnosticsOpt back out — idempotent, fires automatically on session disconnect
get_diagnosticsLatest stored diagnostics for a file; wait: true blocks on the first publish
get_code_actionsLSP code actions (quickfix / organizeImports / refactor / source) at a file location
apply_code_actionApply a single code action to disk — atomic temp+rename
fix_all_in_fileLoop codeAction → apply → re-collect until convergence over the whole file

Proactive notifications

Four additional push channels modeled on subscribe_diagnostics — per-session opt-in, delta-filtered, initial replay, auto-cleanup on disconnect.

ToolDescription
subscribe_workspace_readinessnotifications/workspace_readiness — daemon warmup phase transitions (snapshot_loaded → parallel_parse → deferred_passes_all → global_resolve → end_batch → watcher_started → ready). Last-known phase replayed to late subscribers. A graph tool called during warmup does not need this subscription to cope: it returns an in-band warming block plus best-effort partial results instead of blocking or erroring
unsubscribe_workspace_readinessOpt back out — idempotent
subscribe_daemon_healthnotifications/daemon_health — periodic ticker (default 15 s, interval_ms clamped to 1 s..5 min) snapshots uptime, alloc/sys/heap, num_goroutine, num_gc, tracked_repos, sessions, lsp_alive, graph nodes/edges. Ticker only runs while ≥1 subscriber is attached
unsubscribe_daemon_healthOpt back out — idempotent
subscribe_stale_refsnotifications/stale_refs — per-session intersect of watcher symbol-change events against the session's viewed/modified working set. Fires only when a change actually touches what this session has consumed
unsubscribe_stale_refsOpt back out — idempotent
subscribe_graph_invalidatednotifications/graph_invalidated — coarse "the graph was rebuilt, drop cached results" signal. {node_count, edge_count, reason, ts}; unfiltered
unsubscribe_graph_invalidatedOpt back out — idempotent

Request lifetime (why a call can never hang)

Every tool call and resource read is bounded, on every transport. A request that overruns its budget is abandoned: the client gets a terminal, structured error and the transport's slot is released immediately, so the session keeps serving later requests. The handler itself keeps running — it may be inside a store call that cannot be interrupted — so treat any side effect of an abandoned call as unknown and re-read before assuming it did or did not land.

LayerBoundKnob
Tool call / resource read / prompt fetch (all transports)60 sGORTEX_MCP_TOOL_TIMEOUT (Go duration; off disables)
Daemon socket, per JSON-RPC request60 s, terminal -32001
Daemon socket, concurrent dispatches8GORTEX_MCP_MAX_CONCURRENT_DISPATCHES (max 64)
Control RPC (daemon status / search_symbols / proxy)30 s, terminal timeout
Control RPC (shutdown)unbounded on the daemon — the store flush precedes the ack. gortex daemon stop bounds its own wait at 30 s, then watches the process for up to 2 min and force-kills
Control RPC (track / untrack / reload / enrich_*)unbounded by design

The handler bound fires just inside any deadline the transport already imposed, so the client receives the tool-shaped diagnosis rather than an opaque transport timeout. Track / reload / enrichment are deliberately left unbounded: they are long by design, and a user who starts one is waiting on purpose. Shutdown is unbounded for a different reason — abandoning a half-done store flush is worse than a slow stop — so the command carries the bound instead.

GORTEX_MCP_TOOL_TIMEOUT is read in the server's own process, so set it in the daemon's environment (or the gortex mcp process for the embedded server), not in the MCP client's config. It can always tighten the bound. It can only raise it past 60 s on the embedded stdio server and Streamable HTTP — on the daemon socket the per-request lifetime is a hard 60 s that clamps it. Raise it when a tool legitimately runs longer than a minute: a first index_repository over a very large tree, or ask against a slow local model.

Coding workflow

ToolDescription
get_symbol_sourceSource code of a single symbol (80% fewer tokens than Read). Returns tokens_saved per call. compress_bodies stubs bodies (with an optional keep subset); max_lines salience-truncates to a control-flow skeleton
batch_symbolsMultiple symbols with source/callers/callees in one call
find_import_pathCorrect import path for a symbol
explain_change_impactRisk-tiered blast radius with affected processes. A zero-edge target carries the same per-symbol likely_unused / possible_extraction_gap / coverage_incomplete caveat as get_callers
get_recent_changesFiles/symbols changed since timestamp. Rows are clamped to the session workspace and narrowed further by repo/project/scope; each multi-repo row names its repo
edit_symbolEdit a symbol's source directly by ID — no Read needed. Line-ending tolerant: an LF-authored old_source matches a CRLF file (and vice versa) and the replacement adopts the file's endings (eol_normalized: true rides on the response). Optional base_sha content-hash guard refuses the write when the on-disk SHA has drifted; every success carries new_sha so the next edit can pipeline without re-reading
edit_fileEdit any file (markdown, config, spec, template, source) by exact string replacement — accepts absolute paths or repo-rooted paths. Line-ending tolerant: an LF-authored old_string matches a CRLF file (and vice versa) and the replacement is written with the file's own endings (eol_normalized: true rides on the response). Same base_sha / new_sha drift guard. Kills Read-before-Edit for files not in the graph
write_fileCreate or overwrite any file — atomic temp+rename, re-indexes on write. Same base_sha / new_sha drift guard
rename_symbolCoordinated multi-file rename with all references — definition, graph usages, receiver lines, and test names that embed the old identifier. Replacement is whole-identifier, so renaming Get leaves GetUser intact. Every target line is re-verified against disk and every affected file is parse-gated before anything is written, so the rename lands completely or is refused; dry_run: true returns the identical edit list without writing. Response carries status (applied / would_apply / no_edits) plus per-file bytes_written / new_sha / reindexed
move_symbolRelocate a function / method / type / variable / const to another file. Cross-package moves rewrite every qualified reference, drop the source import, add the target import, synthesise the target file if missing. Go for now
inline_symbolReplace every callsite of a trivial single-statement / single-expression callee with the body — refuses cleanly on defer, spawn, close-over-scope, multi-return, or side-effecting arg. delete_after: true removes the declaration. Go for now
safe_delete_symbolAtomic dead-code removal with a graph-aware safety gate. A cascade parameter (off / preview / apply) drives a fixed-point orphan-propagation pass; cross-workspace and out-of-closure callers (and, by default, test-only callers) disqualify a candidate
set_planning_modeSwitch the session between a guaranteed no-writes planning phase and editing mode
workflowDrive a phase-enforcement state machine (explore → implement → verify) — editing tools are gated until the implement phase

Agent-optimized (token efficiency)

ToolDescription
exploreOne-shot localization: free task/bug text in, the ranked neighborhood out — likely symbols with source + call paths (1-hop callers/callees), a file map, and a completeness cue, packed under a token_budget (default 9000; bodies demote to signatures past it, truncation reported honestly). The opening move for any task-shaped request — folds the whole search/read/callers exploration phase into one call
smart_contextTask-aware minimal context — replaces 5-10 exploration calls. The working set is ranked through the full rerank pipeline. Always emits a blast_radius block (callers grouped by file + covering tests + a no covering tests found warning) and a file-clustered working_set; seed count and token_budget scale with graph size when unset. fidelity: "graded" returns a graph-distance-tiered context_manifest (large interchangeable symbol families are skeletonized to one representative) under one token_budget; estimate: true projects token cost without fetching; if_none_match dedups an unchanged pack to not_modified
get_edit_planDependency-ordered edit sequence for multi-file refactors
get_test_targetsMaps changed symbols to test files and run commands
get_untested_symbolsInverse of get_test_targets — functions/methods not reached from any test file, ranked by fan-in
suggest_patternExtracts code pattern from an example — source, registration, tests
export_contextPortable markdown/JSON context briefing for sharing outside MCP
feedbackaction: "record": report useful/missing symbols. action: "query": aggregated stats — most useful, most missed, accuracy metrics
askOptional in-process LLM research agent (-tags llama + llm.model) — navigates the graph and returns a synthesized answer; chain: true for cross-repo call-chain tracing

Response re-cutting

Gortex captures every large tool response into a bounded per-session ring; these tools re-cut a captured response without re-issuing the original query.

ToolDescription
ctx_statsList the session's buffered responses — handles, tools, line / byte / token counts
ctx_grep / grep_resultsRegex (or literal) search over a buffered response — structured matches[] plus a grep-style block with -A/-B/-C context
ctx_sliceAn explicit line range of a buffered response
ctx_peekHead + tail preview of a buffered response
head_resultsThe first N lines of a buffered response

Analysis

ToolDescription
get_communitiesFunctional clusters (Louvain). Without id: list all. With id: members and cohesion for one community. Members and files are clamped to the session workspace; the partition is global, so a repo/project/scope narrowing is widened to the workspace and the response discloses it
get_processesDiscovered execution flows. Without id: list all. With id: step-by-step trace. Clamped to the session workspace and narrowed further by repo/project/scope — out-of-scope steps are excised by subtree so the surviving chain keeps its real call shape
detect_changesGit diff mapped to affected symbols
index_repositoryIndex or re-index a repository path
reindex_repositoryIncrementally re-index a tracked repository — whole-root, or scoped to an optional paths subset. Multi-repo aware
contractsAPI contracts. action: "list" (default): detected HTTP/gRPC/GraphQL/topics/WebSocket/env/OpenAPI. action: "check": orphan providers/consumers
find_co_changing_symbolsRanked git co-change neighbours for a symbol — over the mined cosine-weighted co_change edge layer
search_artifactsFull-text search over the context-artifacts manifest — DB schemas, API specs, infra configs, ADRs registered via .gortex.yaml::artifacts
get_artifactFetch one context artifact by id, with its content and the symbols it references

Proactive safety

ToolDescription
verify_changeCheck proposed signature changes against all callers and interface implementors
check_guardsEvaluate project guard rules (.gortex.yaml) against changed symbols
audit_agent_configScan CLAUDE.md / AGENTS.md / .cursor/rules / .github/copilot-instructions.md / .windsurf/rules / .antigravity/rules for stale symbol references, dead file paths, and bloat — validated against the live graph

Code quality

ToolDescription
analyzeUnified graph analysis dispatcher. kinddead_code, hotspots, cycles, would_create_cycle, connectivity_health, todos, blame, coverage, coverage_gaps, coverage_summary, stale_code, stale_flags, ownership, releases, cgo_users, wasm_users, orphan_tables, unreferenced_tables, channel_ops, goroutine_spawns, field_writers, race_writes, unclosed_channels, unsafe_patterns, health_score, impact, annotation_users, config_readers, env_var_users, sql_call_sites, fixes_history, edge_audit, domain, named, tests_as_edges, clusters, event_emitters, pubsub, string_emitters, error_surface, log_events, sql_rebuild, external_calls, routes, models, components, k8s_resources, images, kustomize, cross_repo, dbt_models, synthesizers, resolution_outcomes. clusters takes an algorithm arg (leiden / louvain / spectral). impact takes an optional target ({symbol} or {file}): with one it ranks that target's blast radius — the target row first, then its transitive dependents with their depth — and reports the closure width plus whether it is exact; without one it ranks the whole repo. An unresolvable target is a structured error, never a silent fall-back to the repo-wide ranking. synthesizers rolls up every framework-dispatch-synthesized edge by the pass that produced it; resolution_outcomes classifies unresolved call/reference edges by why the resolver gave up (ambiguous_multi_match / candidate_out_of_scope / cross_language_only / stub_only / no_definition)
find_clonesNear-duplicate function/method clusters from the MinHash + LSH similar_to layer; dead_only: true finds dead duplicates of live code
index_healthHealth score, parse failures, stale files, language coverage, tracked-repo path liveness (tracked_repo_paths_ok + missing_repo_paths — a repo whose directory was deleted still holds its registration and silently drops out of workspace-wide answers), per-(repo, provider) semantic-enrichment lifecycle (semantic_enrichment: running / completed / partial / abandoned / failed with edge counts, plus a semantic_enrichment_ok rollup) — a green file count with a partial enrichment state means LSP-tier edges are incomplete. path_liveness asks the same question one level down, per file: it stats the paths the graph itself claims and reports how many indexed files no longer exist on disk (orphan_files / orphan_rate / orphans_by_repo, sampled with truncated: true past 20k files). stale_files only covers files the daemon still tracks, so a deletion it never witnessed shows up here and nowhere else; a non-zero orphan_files caps health_score
get_symbol_historySymbols modified this session with counts; flags churning (3+ edits)

The in-graph coverage tools above (analyze kind=coverage*, index_health language coverage) have an offline, whole-corpus counterpart for regression testing: the gortex eval parity CLI benchmarks per-language resolved cross-file-dependent coverage against a frozen baseline and is CI-fenced three ways — a per-language coverage floor, a frozen at-or-beyond-parity language count, and per-feature extraction goldens. See features.md.

Code generation

ToolDescription
scaffoldGenerate code, registration wiring, and test stubs from an example symbol
batch_editApply multiple edits in dependency order, re-index between steps
diff_contextGit diff enriched with callers, callees, community, processes, per-file risk
prefetch_contextPredict needed symbols from task description and recent activity. Accepts max_bytes / max_tokens budget caps

PR review

A graph-grounded pull-request review surface. The forge-data tools self-serve PR data via the daemon's own forge client (needs GH_TOKEN / GITHUB_TOKEN in the daemon environment), or accept caller-supplied data to skip the network; all are read-only. The review gate is AST-grounded — the deterministic correctness rulepack runs over the changeset and a graph-grounding pass drops false positives, with an opt-in LLM fold-in. The CLI exposes the same surface as gortex prs / gortex review (cli.md).

ToolDescription
list_prsList a repo's PRs with a one-shot review-state classification — a state label (DRAFT / BASE_MISMATCH / CHANGES_REQUESTED / APPROVED / STALE / READY), a normalized CI rollup (NONE / FAILURE / PENDING / SUCCESS), and merge blockers. Pass prs to classify an already-fetched set with no network call
get_pr_impactGraph-joined blast radius + risk score for one PR — maps the PR's changed files to symbols, scores five risk axes (blast-radius flow, caller fan-in, coverage gap, security keywords, community span), groups the affected surface by community and caller/test file. receipt: true emits a privacy-safe review receipt
triage_prsRank a repo's open PRs by graph-derived review priority — get_pr_impact per PR ordered by composite risk (deterministic; use_llm re-ranks with one compact LLM pass + per-PR rationale). Decides which PR to review first
pr_riskPR-level composite risk score for a set of changed symbols — five 0-100 axes into one score + a LOW/MEDIUM/HIGH/CRITICAL level and an ordered review_priorities list. Pass ids (mapped symbol IDs) or base (a git ref — changed set from the diff)
conflicts_prsSurface merge-order conflict risk — maps each open PR to the graph communities it touches and reports the communities touched by more than one PR, with colliding PR numbers, a suggested safe merge order, and a conflict-risk score. Plan a merge train that minimises rebases
suggest_reviewersRank the people / teams best placed to review a changeset — blends CODEOWNERS matches, recent authorship of the changed symbols, and co-change experts into one ranked list with per-reviewer reasons. Pass ids, base, or number
suggested_review_questionsPrioritised, symbol-anchored review questions mapping the changeset to graph anomalies — bridge / hub_risk / surprising / thin_community / untested_hotspot — each tied to a symbol id + file + line with a HIGH/MEDIUM/LOW severity
pr_review_contextDeterministic, LLM-free PR-review rollup in one call — composes diff_context, verify_change, simulate_chain (gated on an explicit overlay session), and audit_agent_config into a composite PASS / WARN / BLOCK verdict. The cheap counterpart to review_pack
sibling_diff_contextRaw unified diff of the OTHER changed files in a changeset — the sibling changes a per-symbol / per-file review view filters out, ranked by relatedness to the focus (shared community/process → co-change → directory proximity)
reviewReview a changeset → line-anchored inline comments + a BLOCK/REVIEW/APPROVE verdict. Runs the deterministic correctness rulepack (graph-grounded to drop false positives) over the changeset (base / scope, or a pasted diff); use_llm folds in LLM findings relocated to exact lines
review_packThe single AST-grounded PR-review entrypoint — folds the graph-grounded review, per-symbol semantic classification, per-file risk, contract-impact + guard/architecture checks, and impacted test targets into one envelope, with a derived verification_command and a privacy-safe receipt
critique_reviewSecond, adversarial self-critique pass over a prior review's findings — asks the LLM (grounded in the diff) which findings are genuine vs false positives, returns the kept set, the dropped set each with a reason, and a revised verdict. Conservative: a disabled LLM keeps everything
post_reviewPost review findings as inline comments on a GitHub PR / GitLab MR — each anchored to its file + line, batched into one review. Every body is secret-redacted before any payload is built; public / fork PRs require confirm_public: true; dry_run: true returns the would-post payloads with no network call
suppress_findingDurably silence a review finding as a false positive (or list / remove) for the current repo — keyed over rule / category / symbol / file / source text so it survives the finding shifting lines. A permanent per-repo never-flag-again list (sidecar-backed)

analyze also takes kind: "review" — the idiomatic/correctness rulepack (NPE / thread-safety check-then-act / N+1 / logic-error, Go + Python) with the same graph-grounded false-positive-reduction post-pass that backs the review tool.

Multi-repo management

ToolDescription
track_repositoryAdd a repo at runtime — indexes immediately, persists to global config
untrack_repositoryRemove a repo — evicts nodes/edges, persists to global config
set_active_projectSwitch active project scope for all subsequent queries
get_active_projectReturn current project name and its member repositories
list_reposList every project/repo in the active workspace
workspace_infoWorkspace identity — bind mode, root directory, marker contents, discovered member set
query_projectSearch symbols in another project or repo without a set_active_project switch — read-only cross-project lookup
save_scopeSave a named, reusable set of repository prefixes — accepted by search_symbols / smart_context via scope
list_scopesList every saved repository scope
delete_scopeDelete a saved repository scope by name

Live editor buffers (overlay sessions)

Editor extensions push in-flight (unsaved) buffers as overlays. Gortex composes a per-request shadow view on top of the immutable base graph and threads it through the tool dispatch context — every subsequent tools/call from the same MCP session reads through the shadow. Graph-walking tools (find_usages, get_call_chain, analyze, …) and source-reading tools (get_symbol_source, get_editing_context, …) all see the editor-buffer state without per-tool changes.

Base is never mutated by overlay flow. Concurrent sessions each see their own view; the file watcher's reindex passes don't race with overlay queries; cross-file edges from non-overlaid files into overlaid symbols are preserved.

ToolDescription
overlay_registerBind an overlay session to the current MCP session ID (idempotent)
overlay_pushPush (or update) a single file overlay; base_sha enables drift detection, deleted: true previews a delete
overlay_listList every overlay attached to the session — path / size / deleted / base_sha
overlay_deleteRemove one overlay from the session
overlay_dropTear down the session and discard every overlay
overlay_keepaliveRefresh the session's idle timer without re-pushing buffer content; cheap option for debugger / wizard pauses
compare_with_overlayRun find_usages / get_callers / get_call_chain / get_dependencies / get_dependents against base AND overlay; returns added / removed / common ID sets

Branching — N parallel speculative sessions off one baseline. Each overlay session carries an active-branch pointer plus a branches map; every legacy overlay tool operates on the active branch, so callers that never touch branches see exactly one implicit main branch and behave unchanged. With branches, an agent can hold strategy A and strategy B simultaneously off the same baseline, evaluate each, and merge the winner.

ToolDescription
overlay_forkClone the active (or named) branch into a new branch; optional activate: true flips the session pointer
overlay_branchesList every branch with active flag, file count, base_sha anchor count, parent, and created_at
overlay_switchFlip the session's active branch
overlay_mergeFold one branch into another (default target: main) or write the branch to disk through the same atomic-write + base_sha drift guard as edit_file. Same-path divergent content is refused without force: true; force resolves last-writer-wins
overlay_drop_branchDelete a named branch — refuses to drop the active branch or the implicit main
compare_branchesRun find_usages / get_callers / get_call_chain / get_dependencies / get_dependents against two branches and report each side plus the delta

HTTP transport mirrors the surface at /v1/overlay/sessions/*; the /v1/tools/<name> entry point reads the overlay session from Mcp-Session-Id (preferred), X-Gortex-Overlay-Session, or ?session_id=. Overlays are bound to their MCP session — when the session ends the overlay is dropped synchronously. Idle TTL is a fail-safe (default 30 m, configurable via GORTEX_OVERLAY_IDLE_TTL); every tool call against a live overlay refreshes it.

Speculative execution

Built on the same shadow-graph substrate, preview_edit and simulate_chain answer "what would change if I applied this WorkspaceEdit?" without ever touching disk or mutating the base graph. The input is a standard LSP WorkspaceEdit (changes / documentChanges), so any agent that already produces WorkspaceEdits for code actions can speculate on them directly. Per-step impact: touched files, added / removed / renamed symbols (non-trivial-signature rename heuristic), broken callers, broken interface implementors, blast-radius rollup, suggested test targets, and (when an LSP is configured) round-trip diagnostics restored to the on-disk state at simulation end.

ToolDescription
preview_editSingle-shot WorkspaceEdit → impact report. Optional diagnostics: false skips the LSP round-trip. inherit_overlay: true layers on top of the caller's current overlay
simulate_chainOrdered sequence of WorkspaceEdits applied in order with per-step impact + cumulative rollup + per-step diagnostics delta. stop_on_error: true (default) aborts on the first new ERROR-severity diagnostic. keep: true promotes the final simulated state into a real overlay session bound to the caller

MCP resources (18)

Read-only, URI-addressable, no args. Clients that speak resources can resources/subscribe once and receive notifications/resources/updated after each graph re-warm — no polling.

ResourceDescription
gortex://sessionCurrent session state and activity
gortex://statsGraph statistics (node/edge counts)
gortex://schemaGraph schema reference
gortex://guideOn-demand reference: LLM-provider matrix, capabilities, token-economy, analyze/search_ast catalogs, workflow
gortex://guide/{topic}One guide section by topic (providers, capabilities, tokens, analyze, search_ast, resources, workflow)
gortex://index-healthHealth score, parse failures, stale files
gortex://workspaceWorkspace identity and discovered member set
gortex://reposTracked repo / project list
gortex://active-projectActive project name and member repos
gortex://communitiesCommunity list with cohesion scores
gortex://community/{id}Single community detail
gortex://processesExecution flow list
gortex://process/{id}Single process trace
gortex://reportHigh-level orientation — graph size, top languages/kinds, hotspot / dead-code / todo counts
gortex://god-nodesTop 20 hotspots
gortex://surprisesCycles + dead code + cross-community call hubs
gortex://auditaudit_agent_config with discovery defaults
gortex://questionsTODO / FIXME / XXX / HACK / QUESTION rollup grouped by tag and assignee

MCP prompts (3)

PromptDescription
pre_commitReview uncommitted changes — shows changed symbols, blast radius, risk level, affected tests
orientationOrient in an unfamiliar codebase — graph stats, communities, execution flows, key symbols
safe_to_changeAnalyze whether it's safe to change specific symbols — blast radius, edit plan, affected tests