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

|
15 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 eight 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, and get_quality_findings only while Quality Lens is enabled — genuinely absent from the tool list while they're 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
get_quality_findings none, file + symbol?, or jira_key With no arguments, a workspace summary (totals by source and severity, how many files Sonar has results for, the 20 flows with the most issues). Otherwise SonarQube for IDE and Contour findings for a file, a member, or a whole flow, grouped like the Quality view, with the files nobody has analysed yet

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?"
  • get_quality_findings "What quality issues are there along the POST /orders flow, and which files haven't been analysed?"

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; VS Code offers completion and descriptions for every key while you edit that file.

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). Five checks, 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.
  • Concrete injection (new in 0.4.0, concrete-injection) a Spring bean injected as its concrete class (OrderServiceImpl) when it implements an interface in the same codebase (OrderService). A @Qualifier excuses it only when two or more beans implement that interface.
  • Direct API call in a React component (new in 0.4.0, frontend-direct-api-call) a fetch, axios or configured wrapper call made inside a component (function or class, including useEffect callbacks, handlers and componentDidMount) instead of in an API/service module or a use* hook. Files under api/, services/, hooks/, queries/, store/ and similar folders are allowed; add your own with solidLint.frontendApiAllowedIn.

All five default on once the setting is enabled; set solidLint.fieldInjection, solidLint.entityLeak, solidLint.concreteInjection and/or solidLint.frontendDirectApiCall 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.

Quality Lens

Off by default (contourMCP.qualityLens.enabled). Issue counts where you read code: SonarQube for IDE's findings plus Contour's own SOLID lint, on every Java / JS / JSX / TS / TSX file Contour indexes, and — what nothing else shows — summed along each traced flow.

  • File lens on line 1: Quality: 3 issues · part of 2 flows (backend) or Quality: 1 issue · calls 1 endpoint (frontend).
  • Member lens above each class, method, function or React component with findings: save() · 1 issue · used by 1 endpoint.
  • Flow count next to Contour's existing endpoint, job, event and call-site lenses: quality: 4 issues, counted across the whole chain (frontend → controller → service → repository), or quality: clean.
  • The Contour Quality view (Explorer, below the use-case view) lists the issues behind whichever lens you clicked: by member for a file, by chain layer for a flow. Filter… picks All / Sonar / Contour / the highest severity, Pin holds the current target, Copy for AI copies the issues (with the flow's AI context for a flow), and a file Sonar hasn't analysed yet gets Open to analyse. Clicking any row opens the file there.
  • All issues (the view's Show all issues button, or Contour MCP: Show All Quality Issues) opens an editor tab with every issue in the workspace, as three sortable tables: by flow (worst first), by file and by rule. The header says how much of the workspace Sonar has results for ("Sonar has results for 12 of 99 indexed files · open files to analyse more"). Click a flow to see it in the Quality view, or a file or finding to open the code. The tab updates itself as results change (at most every few seconds), and Show suppressed and baselined lists the findings that aren't counted.

A file SonarQube for IDE hasn't analysed (it usually analyses files as you open them) is always reported as not analysed, never as clean, and a flow touching one says how many files are missing. Contour's own findings count only while contourMCP.solidLint.enabled is on; the view and the lens tooltip say so when it isn't.

Settings

Setting Default
contourMCP.qualityLens.enabled false Master switch: lenses, the Quality view and the MCP quality data.
contourMCP.qualityLens.scope withFindings flows: only the flow counts. withFindings: also file and member lenses where there are findings. all: also clean members.
contourMCP.qualityLens.includeSonar true Read SonarQube for IDE diagnostics; off counts Contour's findings only.
contourMCP.qualityLens.showCriticalMarker false Append ⚠ when a count includes a High or Blocker finding (see the severity note below).
contourMCP.qualityLens.maxMemberLensesPerFile 40 Beyond this, one +N more members with findings lens opens the view.
contourMCP.qualityLens.sonarDiagnosticSources ["sonarqube", "sonarlint"] Diagnostic source values treated as Sonar.

In contour.config.json, qualityLens.excludeSonarRules (e.g. ["typescript:S1172"]) lists Sonar rules never to count, and qualityLens.baselineFile moves the baseline file (below).

Sonar, via connected mode. Contour reads only the diagnostics SonarQube for IDE has already published in VS Code. Bind SonarQube for IDE to your SonarQube Server or Cloud project (connected mode) and those diagnostics follow the team's quality profile and server-side issue status, so Contour's counts do too. Contour never runs an analysis and never talks to SonarQube. SonarQube for IDE publishes every issue at VS Code level Warning, so Contour reads Sonar's own impact severity (Blocker / High / Medium / Low / Info) from the diagnostic's data.impactSeverity, and Filter… → High+ lists High and Blocker issues. That field isn't a documented SonarQube for IDE API and could change in a release; an issue without it is shown at its VS Code level (Error / Warning / Info) and the output says the severity is approximate.

Closed files. SonarQube for IDE analyses files as you open them and clears their results when you close them. Contour keeps a closed file's last Sonar results, marked "as of ", until the file changes on disk or Sonar publishes for it again, so a file you looked at earlier doesn't drop back to "not analysed". Quality Lens never opens files in the background itself.

No network calls. Quality Lens makes no external calls of any kind: everything comes from VS Code's local diagnostics and Contour's own index.

Baseline. To adopt the lint on an existing codebase, run Contour MCP: Create Quality Baseline. It records every current Contour finding in contour.baseline.json (next to contour.config.json, or in the first workspace folder), and those findings stop counting, leave the Problems panel and the MCP tools, and move to a collapsed Baselined (N) group in the Quality view. New findings still show. A fingerprint survives lines added or removed elsewhere in the file; editing the flagged line itself, or moving it into another method, makes it a new finding. Commit the file to share it; Contour picks up changes to it (an edit, a git pull) without a re-index. Contour MCP: Clear Quality Baseline deletes it. Sonar findings are never baselined: SonarQube has its own new-code period.

Suppression. For a single accepted finding, add a comment on the line above it, or at the end of its line:

// contour-ignore field-injection: legacy singleton, removed in ORD-142
@Autowired
private LegacyHelper helper;

The same // syntax works in JS/TS. List several codes with commas (// contour-ignore concrete-injection, field-injection: …). A comment without a reason still suppresses, but shows an Information-level "add a reason" hint. Suppressed findings aren't counted or shown in the Problems panel; Filter… → Show suppressed lists them, with their reasons, in the Quality view. A contour-ignore never applies to Sonar findings; suppress those in SonarQube.

For AI agents. While Quality Lens is on, the extension writes .contour-mcp/quality.json next to the trace cache (at most every few seconds, removed when the feature is turned off). The MCP server then offers get_quality_findings and adds a one-line quality summary to get_trace and get_flow; every answer says when the snapshot was taken and which files weren't analysed. get_drift lists Contour findings the same way the Problems panel does: baselined and suppressed ones are counted in a single "not shown" line.

Restart your MCP client after turning Quality Lens on or off. The MCP server reads the setting once, when it starts, so get_quality_findings appears only after Claude Code (or your client) reconnects. Contour reminds you once when you turn it on. The same applies to Coupling Map.

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.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft