Skip to content
| Marketplace
Sign in
Visual Studio Code>Other>Contour MCPNew to Visual Studio Code? Get it now.
Contour MCP

Contour MCP

Saad Zarook

|
13 installs
| (0) | Free
The shape of your system, traced end to end — for developers and AI coding agents alike, via a bundled local MCP server.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Contour MCP

Everything Contour does endpoint↔consumer mapping, full chain tracing (UI → API → service → database), drift detection, SOLID structural lint, test coverage overlay, scheduled-task / service-to-service / event-driven flow tracking, use-case tagging with Jira linkage, and the shareable visual report plus a bundled local, read-only MCP server that exposes the same trace index to Claude Code, Claude Desktop, and other MCP clients.

This extension installs side by side with plain Contour you don't need to choose one. If you don't need the MCP server, install Contour instead; this is a superset of it.

How this differs from vanilla Contour

  • The same trace engine, packaged for this extension. The indexing, matching, drift detection, coverage overlay, and use-case tagging live in @contour/trace-engine-mcp, a workspace package inside this repo.
  • Plus an MCP server. On first activation this extension writes a local, git-ignored MCP server registration (.mcp.json for Claude Code, .vscode/mcp.json for VS Code's own MCP support) pointing at a bundled Node script no separate install step, and each client's own trust prompt still applies (this extension never auto-approves itself).
  • The trace index becomes a cache file, not just in-memory extension state. Every time Contour finishes a reindex (full or incremental never a separate timer), it atomically rewrites .contour-mcp/cache.json in your workspace. The MCP server reads that file fresh on every tool call, so an agent's answer always reflects your latest edit, or clearly tells you to wait for Contour to finish reindexing rather than guessing.

Install

Install from the Marketplace (search "Contour MCP"), or build and install a .vsix locally:

npm install
npm run compile
npx vsce package
code --install-extension api-contract-tracer-mcp-*.vsix

Open a workspace with at least one backend (Java/Spring) and one frontend (React/JS) folder. Contour MCP indexes automatically on activation; watch the "Contour MCP" output channel for progress. Once the first index completes, the bundled MCP server is registered automatically Claude Code or VS Code will prompt you to trust it the first time it's discovered, same as adding any other MCP server by hand.

The seven tools

All read-only. None writes a file, runs a command, or calls any external API including Jira: if a flow is tagged with a Jira key, only what's already stored locally is surfaced, never a live lookup. get_coupling and list_hotspots are only registered while Coupling Map is enabled — genuinely absent from the tool list while it's off, not just hidden.

Tool Input Returns
list_flows entity Flow summaries (endpoints, scheduled tasks, service calls, events) touching a file, service, or table
get_trace file, symbol? The scoped call chain for a file UI/consumer → API → service → database
get_flow jira_key The flow tagged to that ticket description, tags, test coverage, as stored locally
get_drift scope? Where the traced architecture and the real code have diverged, optionally scoped
export_context scope, task A minimal, packaged context bundle sized for a specific task
get_coupling target Whether a DTO, service method, or entry point is shared across unrelated parts of the system, and how risky (good/warning/critical/excluded/unknown)
list_hotspots minSeverity?, limit? The ranked list of shared-contract/high-fan-in coupling findings, worst first

Example prompts, once the server is connected:

  • list_flows "What flows touch OrderController?"
  • get_trace "Trace OrderController.java end to end, from the API down to the database."
  • get_flow "What's the flow tagged to ORD-123, and is it tested?"
  • get_drift "Has anything drifted between the frontend and backend in the orders service?"
  • export_context "I need to add a cancel-order endpoint give me the minimal context for that."
  • get_coupling "Is OrderSummaryDto safely shared, or is it hidden coupling?"
  • list_hotspots "What are the worst coupling hotspots in this codebase right now?"

The chain tracer understands real Spring DI now

Interfaces with more than one real implementation used to be invisible to the tracer. Now every hop in a chain resolves through a priority chain — an explicit chains.beanOverrides config hint, then @Primary, then @Qualifier/bean-name matching, then (the common case) the single remaining candidate — and when none of those disambiguate it, the chain says so explicitly instead of guessing:

-> JdbcOwnerRepositoryImpl.findById() [repository, ambiguous]
   [note: 2 candidate implementations, none disambiguated — see chains.beanOverrides | @Profile("jdbc")]

Every hop also carries a confidence (certain / resolved / ambiguous) and, where relevant, a note disclosing an @Transactional/@Async/@Cacheable AOP annotation, a @Profile/@ConditionalOnProperty conditional-wiring guard, or a @PreAuthorize/@Secured/custom authorization annotation on that hop — verbatim, including arguments, never evaluated or assumed. DDD/hexagonal (ports-and-adapters) layouts are supported too: a domain-layer repository interface with no Spring Data ancestry, implemented by an @Repository-annotated infrastructure adapter, resolves correctly — a record or enum in that adapter role (not just a class) resolves the same way. All of this is configurable per project under chains in contour.config.json — see docs/indexing-engine-upgrades.md for the full config reference and the evidence behind each of these claims (real bugs found on real open-source codebases, not just synthetic fixtures).

Controllers built contract-first from an OpenAPI spec are indexed too

A Controller that implements an OpenAPI-codegen-generated interface (the interfaceOnly: true pattern common with openapi-generator-maven-plugin) puts its real @RequestMapping/@GetMapping/etc. annotations on the generated interface's methods, not on the @Overrides in your own source the generated interface itself lives under target/, which the indexer correctly never treats as project source. The endpoint indexer now resolves those @Override methods back to their generated interface's own mapping annotation, scoped per backend service so it can't cross-resolve across unrelated services in a monorepo. Confirmed against real spring-petclinic-reactjs: previously 7 of its 8 controllers indexed to zero endpoints; now all 8 resolve correctly. Works out of the box for the common Maven default output path (target/generated-sources/openapi/**); add more locations via backend.generatedApiGlobs in contour.config.json for a Gradle build or a customized <output>.

SOLID structural lint

Advisory-only Problems-panel warnings, off by default (contourMCP.solidLint.enabled). Three checks today, each independently toggleable via the solidLint namespace in contour.config.json:

  • Controller → Repository layering a Controller calling a Repository directly, skipping the Service layer.
  • Field injection @Autowired on a field instead of a constructor. Applies everywhere, not just Controllers; never fires inside src/test/**, where field-level @Autowired/@MockBean is normal and still recommended by Spring itself.
  • Entity leak a @Entity-annotated class returned from, or accepted via @RequestBody into, a Controller endpoint coupling the API shape directly to the database schema.

All three default on once the setting is enabled; set solidLint.fieldInjection and/or solidLint.entityLeak to false in contour.config.json to turn a specific one off without disabling the rest. Every check was validated against real Spring Boot source before shipping, not just synthetic fixtures the field-injection detector, for example, was proven against a real 2-true/9-false-if-naive split in production code, where a naive "flag any @Autowired" regex would have wrongly caught 9 real constructor/method injections alongside the 2 real field-injection violations.

Coupling Map

Off by default (contourMCP.couplingMap.enabled). A different question than the SOLID lint above: instead of "is this one class well-formed," it asks "is this DTO or service method being reused by two or more unrelated parts of the system in a way that creates hidden coupling" — built on the same traced call graph, plus two lightweight signals (cognitive complexity, integration fan-out). A bare-minimum, defensible signal, not a full architecture-governance suite.

Tuning lives under a couplingMap namespace in contour.config.json: exclude (built-in defaults plus your own package/name-pattern exclusions), markedShared (targets a human has reviewed and accepted as intentionally shared), relatedness (how flows get clustered), and theme (webview colors). Marking, unmarking, or including an excluded target back into analysis is always a human decision made in the editor — never something an MCP client can do; get_coupling and list_hotspots (see the tools table above) are read-only, same guarantee as the rest of the server.

Two commands:

  • Contour MCP: Open Coupling Map — a webview with three views: Overview (every cluster, sized by scope, ringed by risk), Detail (one flow's full internal chain plus any bridge into another flow), and Matrix (every pair of flows at once). Click through to mark/unmark a target as intentionally shared, or include an excluded target back into analysis — both write straight to contour.config.json.
  • Contour MCP: Export Coupling Map — the same map as a single, self-contained HTML file for sharing with someone who doesn't have the extension installed. Pan/zoom, search, and hover all still work standalone; marking and click-to-navigate need the live webview.

Staleness

The MCP server never trusts an in-memory copy it re-reads .contour-mcp/cache.json from disk on every call. If the cache is missing, or a file your query touches was edited after the cache was last built, or the cache itself has passed its backstop max age (contourMCP.cacheStalenessThresholdMinutes, default 360 minutes), you get a clear message to let Contour finish reindexing never stale or fabricated data.

Further reading

  • docs/indexing-engine-upgrades.md DI resolution, DDD/hexagonal support, the confidence/disclosure model, targeted invalidation, and the optional tree-sitter AST parsing path, each claim backed by a test or a real-repo validation run.
  • docs/mcp-technical-writeup.md why handing an already-computed trace to an AI agent beats letting it re-derive the same call graph by reading files, with token-count evidence from a real trace.
  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft