Both agents can be handed more than one repository. Here is how to wire cross-repo blast radius into each, and the exact point where Claude Code’s clone and Cursor’s index each stop.


You have Claude Code open in one repository and Cursor in another. The task in front of you is small, and the kind agents are good at. Bump a base image. Tighten a variable on a Terraform module. Delete a CI job that you are fairly sure three other projects still call. Before you let the agent run, you want the one thing it cannot tell you. Who else breaks.

Both tools have a multi-repo story now, so it feels like a question they should be able to answer. Claude Code has /add-dir and native MCP. Cursor indexes an entire workspace and answers questions about it. You could be forgiven for assuming that somewhere in there is a feature that knows your orders-service consumes the contract you are about to change. There is not, and the reason is different for each tool. Knowing exactly where each one stops is the difference between wiring up something that genuinely helps and trusting something that quietly does not.

So here is the claim this post runs on. Claude Code and Cursor can both be handed more than one repository, and neither can tell you which repositories a base-image or Terraform change will break, because Claude Code only sees the repositories you checked out, and Cursor’s index answers similarity, not dependency. The edge that breaks the other repo was declared in a manifest, and a manifest is neither a file the agent cloned nor a chunk that embeds near your query.

I have covered the Copilot version of this question before, and made the case for why this blindness is structural rather than a gap a smarter model closes. This is the Claude Code and Cursor version, and it gets concrete about the wiring.

What each agent can actually see today

What Claude Code and Cursor can see across repositories is different for each, and it moves fast enough that this section carries a date. As of June 2026, here is the precise shape of it.

Claude Code sees the directory you started it in, recursing up the tree to load any CLAUDE.md it finds along the way. You extend that reach with the --add-dir flag, the /add-dir command, or permissions.additionalDirectories in .claude/settings.json. It reads and greps those trees, and it can edit them. What it does not do is hold a dependency graph of them. Add MCP servers and it gains tools that can. So Claude Code’s cross-repo reach is exactly the set of directories you granted, navigated by reading and search, plus whatever tools you wired in.

Cursor’s reach is its index. It chunks your code, embeds each chunk with a custom model, and stores the vectors in a remote vector database, and @Codebase answers by finding the chunks whose embeddings sit nearest your query. Multi-root workspaces are supported, so several repositories can be indexed at once and Agent can reach all of them. The index covers the repositories you opened, it trails your local checkout by a sync interval, and it answers by similarity.

Notice what both give you. A way to put more repositories in front of the agent. Notice what neither gives you. A way to turn “the agent can see these repositories” into “the agent knows how these repositories depend on each other.” That second thing is the whole post, and the rest of it is, first, why each tool stops short of it, and then how to hand each tool the part it is missing.

Claude Code: widen the window, write the map, add the tool

Claude Code gives you three ways to reach across repositories, and they map cleanly onto the three families every agent’s users reach for, in roughly this order. Each one solves a real problem. The first two stop at the same wall, and the third is where the fix actually goes.

Widen the window with /add-dir

The first instinct is access, and Claude Code makes it cheap. --add-dir at launch, /add-dir mid-session, or permissions.additionalDirectories in .claude/settings.json to make a set of sibling directories part of the project so everyone working in that area gets them.

# At launch
claude --add-dir ../orders-service --add-dir ../platform-charts

# Or permanently, in .claude/settings.json
{
  "permissions": {
    "additionalDirectories": ["../orders-service", "../platform-charts"]
  }
}

Each granted directory becomes readable and editable, and as of a recent version Claude Code can even load the CLAUDE.md from an added directory if you set CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1. At a handful of tightly coupled repositories, with someone keeping the set current, this is the right first move and it costs an afternoon. I want to be fair to it before drawing the line.

The line is that access is not selection. You chose which directories to add, and you chose them from memory. Nothing in /add-dir tells you that a third repository consumes the same contract and is not in the set at all. And the failure that bites hardest is quieter still. You can only add a directory that exists on your disk, so the repository you forgot to clone is not a directory you can add. Widening the window shows the agent more rooms. It does not hand it the floor plan, and I made the longer version of that argument in Repo access was never the hard part.

Write the map in CLAUDE.md

The second instinct is to write the structure down. This is the CLAUDE.md and AGENTS.md layer, files loaded at session start and held for the whole session, layered enterprise, user, and project deep, that describe how the system fits together. More than sixty thousand repositories now carry one. The most documented version is Mabl’s, an 850-line coordination graph their agents query at planning time, and it works.

It also decays, and the engineers writing these files know it. The sharpest line I have read on the pattern comes from someone who layered the files org, team, and repo deep and concluded: “I learned not to list repos here. Lists go stale. Instead, tell Claude where to look.” That is the trap in one sentence. A hand-written map has to be kept current by humans at the same throughput the agents are changing the repositories, and an agent navigating by a stale map does not feel stale. It feels fast, right up until the change lands. A map that decays is the developer-portal catalogue problem wearing new clothes, and it loses to the same thing: a graph parsed from the source, which cannot drift from the source because it is read from it.

Add the graph as an MCP tool

The third instinct is the right one. Stop describing the structure and hand the agent a tool that holds it. MCP is native to Claude Code, configured with claude mcp add or a committable .mcp.json at the project root, and this is where the fix belongs.

The catch is what is usually on the other end of that tool call. Most of what you can plug in here is a symbol index or a semantic one. It follows imports and call edges, or it embeds your code and retrieves by similarity. Both stop at the language boundary, the same wall I walked in the flagship: a symbol graph answers “who calls this function” and never sees a FROM line. The tool Claude Code actually needs at this layer is one that resolves a Dockerfile FROM to the repository that builds the image, and a Terraform source to the repository that owns the module. That tool is an artifact graph, and wiring it in is the back half of this post.

Cursor: the index is the context model, and it answers the wrong question

Cursor’s cross-repo story is its index, and the index is built to answer a different question than the one a breaking change asks. This is the part worth slowing down on, because Cursor is genuinely good and the distinction is easy to miss.

Here is how it works. Cursor splits your code into chunks, runs each chunk through an embedding model to get a vector, and stores those vectors in a database. When you ask @Codebase something, your question becomes a vector too, and Cursor returns the chunks whose vectors sit nearest yours. The official description is exact about this: it returns the most semantically similar code chunks, even when they do not contain the keywords you used. Across a multi-root workspace it does that over every repository you opened.

I want to be generous here, because it deserves it. Semantic search is a real improvement over grep. Cursor’s own research puts it at around 12.5% more accurate on large codebases, and the gain grows with size. Open web-app, orders-service, and the shared contract repo in one workspace, and “how does the orders service validate a token” becomes one question instead of four context switches. For understanding a system, exploring it, finding where a concept lives, it is excellent, and nothing below is a knock on that.

The line is that semantic search returns the code most similar to your query, and a declared dependency is not a similarity relationship. A Dockerfile line that reads FROM platform/base-go:1.21 and the repository that builds platform/base-go share almost no tokens, no structure, nothing an embedding would place near the other. The edge between them is not latent in the text, waiting to be retrieved. It was declared once, in a manifest, and it is either parsed from that manifest or it is not found.

Ask @Codebase “what depends on this base image” and you get the files that mention base images, which is a different set from the repositories that inherit this one. This is the difference between inferred context and a dependency graph, and it is sharpest exactly where Cursor is most loved.

Two more limits sit underneath that one, and they bite even if you set the similarity question aside. The index covers only the repositories you opened in the workspace, and someone chose that .code-workspace folder list, from memory, which is the catalogue trap again in a different file. And the index trails your checkout by a sync interval, so by Cursor’s own guidance it is reliable for stable architecture and unreliable for recent change. The repository you did not open and the push from this morning are both outside it. Cursor indexes what looks related. The thing you need before a base-image bump is what is declared dependent, and those are different graphs built by different machinery.

The wall both share, and the graph that clears it

Widen the window, write the map, or index the workspace, and you reach the same wall from three directions. /add-dir gives access without selection. CLAUDE.md gives structure that decays. The index gives similarity, never the declared edge. None of them resolves a source block to the repository that owns the module, because that was never the job any of them was built to do.

How it reaches across reposWhat that answers wellWhat it can’t resolve
Claude CodeReads the directories you grant it with /add-dirAnything inside a repository you checked outWhich repositories a change affects, and any repo you didn’t clone
CursorSemantic search over an embedding index of the open workspace”Where does this live”, “how does this work”A declared dependency, which is not a similarity relationship

But the edges that matter are already written down. The contract package in a package.json dependency or a go.mod require. The module in a Terraform source block. The image in a Dockerfile FROM. The chart in a Chart.yaml dependency. The template in a GitLab CI include:project or a reusable Actions uses:. I spent a whole series walking those edges one ecosystem at a time. They are deterministic. Parsed, not inferred. The graph that should be answering the agent’s question already exists in your organisation’s manifests, unassembled. The rest of this post is assembling it once and wiring it into Claude Code and Cursor.

Wiring the graph into Claude Code and Cursor

A cross-repo dependency graph helps either agent in three ways, and they are worth doing in order, because each one is more setup and more enforcement than the last. The graph here is Riftmap, which parses these edges across your whole GitHub or GitLab organisation from one read-only token and serves the result over an HTTP API. You can adopt the same architecture with any parsed graph, including one you build yourself.

Tier one: let the agent call the API

The simplest version works today and installs nothing. Tell the agent the graph is an HTTP call, and let it make the call with the shell tool it already has. The recommended pattern is three endpoints. Resolve the working tree to a node, hydrate that node’s context in one round-trip, and ask for the transitive cascade when the change actually warrants it.

# Resolve the working tree's clone URL to a Riftmap repo
REPO_ID=$(curl -s \
  "https://api.riftmap.dev/api/v1/repositories/lookup?url=https://github.com/myorg/platform-charts" \
  -H "X-API-Key: $RIFTMAP_API_KEY" | jq -r '.id')

# One round-trip: the repo, its dependencies, its dependents, its artifacts
curl -s "https://api.riftmap.dev/api/v1/repositories/$REPO_ID/context" \
  -H "X-API-Key: $RIFTMAP_API_KEY"

# When the change is breaking, ask for the transitive blast radius
curl -s "https://api.riftmap.dev/api/v1/repositories/$REPO_ID/impact?max_depth=3" \
  -H "X-API-Key: $RIFTMAP_API_KEY"

To make the agent reach for this without being reminded each time, put the instruction where it already reads. A line in CLAUDE.md for Claude Code, a rule in .cursor/rules/ for Cursor, along the lines of: before planning any change to a shared artifact (a base image, a Terraform module, a Helm chart, a CI template), call the Riftmap API to list the dependents and fold them into the plan.

This is not theoretical. I have a recorded Claude Code session doing exactly this. The prompt asks it to delete a helm:deploy job, because Helm deploys are moving into an umbrella chart, and to flag who would break. The agent calls the API on its own, surfaces 51 consumers across the organisation, and sorts them by whether they pin a release tag, and so have a grace period, or float on main, and so break on the next pipeline run. It can make that distinction because every dependency edge the API returns carries the version_constraint the consumer declared. It also flags, unprompted, a caveat about the granularity of one edge, which is the kind of self-correction you want from an agent about to make a breaking change. That is the entire point. The structural account is in front of the agent before the first edit, not discovered in CI twenty minutes later.

Tier two: make it a first-class tool with MCP

Calling curl from a prompt works, but the agent has to be told the shape of the call each time, and the call lives in your instructions rather than in the tool list where it belongs. Both Claude Code and Cursor are MCP clients, so the more native option is to expose the graph as MCP tools.

Riftmap does not ship its own MCP server yet, and the reason is worth stating plainly, because it is the honest one: the endpoints are the load-bearing piece, packaging them is mechanical, and the build is being held until real users ask for it rather than designed for a workflow nobody has yet. If you want it, the roadmap page takes an issue, and concrete demand is what unblocks it.

Until it lands, you bridge in about five minutes, because Riftmap publishes a static OpenAPI schema and there are mature generators that turn any OpenAPI spec into an MCP server. Point one at the schema, give it the API base and your key, and the three endpoints become tools the agent sees in its tool list. For Claude Code, in a committable .mcp.json at the project root:

{
  "mcpServers": {
    "riftmap": {
      "command": "npx",
      "args": ["-y", "@ivotoby/openapi-mcp-server"],
      "env": {
        "OPENAPI_SPEC_PATH": "https://app.riftmap.dev/openapi.json",
        "API_BASE_URL": "https://api.riftmap.dev/api/v1",
        "API_HEADERS": "X-API-Key:${RIFTMAP_API_KEY}"
      }
    }
  }
}

Cursor reads the same mcpServers format, so the identical block drops into .cursor/mcp.json, with one change: Cursor’s variable syntax for the key is ${env:RIFTMAP_API_KEY} rather than ${RIFTMAP_API_KEY}. One detail in that config is worth knowing rather than copying blind. The schema is served from app.riftmap.dev and the API answers on api.riftmap.dev, which is why the base URL is set explicitly instead of being inferred from the spec’s own host.

With either client, “who depends on platform-charts” is now a tool call the agent makes itself, in Plan Mode or mid-task, with no HTTP in the prompt. When Riftmap’s own server ships it will be a thinner version of the same thing, pipx install riftmap-mcp against the same endpoints, and switching to it is a config line.

Tier three: make it a gate, not a hint

A tool the agent may call is still a tool the agent may skip. For the highest-stakes changes you want the graph between the change and main, not merely available to the planner. There are two honest places to put it.

In the loop, Claude Code runs hooks, skills, and subagents. A skill the agent invokes during planning, or a check on the commit boundary, can make the dependents query a step that has to happen before a change to a shared artifact proceeds. This keeps the graph in the agent’s path rather than its discretion, though it depends on the agent’s cooperation to fire.

At review, the cleaner gate is CI, because it does not depend on the agent cooperating at all. On any pull request that touches a shared component, the pipeline calls /impact, posts the consumer list as a comment, and the human reviewing the agent’s change is checking it against the same structural account the agent planned with, not against memory. This is the architecture I think the whole category lands on, and it is the one Mabl built by hand before running agents across a hundred repositories on top of it.

What makes either gate trustworthy rather than confidently wrong is the freshness contract. Every repository the API returns carries last_scanned_at and last_activity_at, and the single rule is that if the repository has been pushed to since Riftmap last scanned it, the graph is treated as stale. For an interactive agent that means warn and proceed with the caveat. For a CI gate it means trigger a rescan and re-poll before the merge. A gate that knows when it is out of date is the opposite of the stale CLAUDE.md that feels fast right up until it is wrong.

The graph you hand the agent, not the one it tries to infer

Give Claude Code every repository you have checked out, and give Cursor an index of all of them, and you have given each agent a faster way to read what is already in front of it. You have not given either one the list of repositories that break when it bumps the base image. That list was never a file Claude Code could clone or a chunk Cursor could embed. It was a set of FROM lines and source blocks sitting in repositories the agent never opened, declared once and never assembled. The agent cannot infer it, because it was never there to infer. It has to be parsed and handed over.

Riftmap is that graph, built from one read-only token across your GitHub or GitLab organisation. It parses the manifests that already declare these edges across twelve ecosystems and resolves each reference to the repository that owns the artifact, then serves the result two ways. An interactive blast-radius view for the engineer who owns the estate and holds the pager, and an HTTP API, three endpoints with an OpenAPI schema and a freshness field on every response, that Claude Code and Cursor can call during planning, or that you can put in front of a merge. Auto-discovered, never declared. Parsed, not inferred. The MCP server is coming when enough people ask. Until then it is one config block away.

Questions teams ask

The same questions come up whenever I help someone wire this in, so here they are, answered straight.

How do I give Claude Code cross-repo dependency awareness? Claude Code sees the repositories you grant it with /add-dir and reads them, but it does not build a dependency graph across them. To give it cross-repo blast radius, expose a parsed dependency graph as a tool it calls during planning. Either let it call the Riftmap HTTP API from its shell, resolving the repo with lookup and then asking for context or impact, or wrap the OpenAPI schema as an MCP server in .mcp.json. Then it can ask which repositories a change affects before it edits.

Does Cursor’s @Codebase understand cross-repo dependencies? Not in the sense a breaking change needs. @Codebase is semantic search over an embedding index, so it returns the code most similar to your query, which is a different set from the repositories that declare a dependency on what you are changing. A Dockerfile FROM line and the repository that builds that base image are not similar text, so no embedding search reliably connects them. Cross-repo dependency edges have to be parsed from manifests, not retrieved by similarity.

Can I add a Riftmap MCP server to Claude Code or Cursor? Not a first-party one yet. It is on the roadmap, deferred until there is real demand. Today you bridge in a few minutes: Riftmap publishes a static OpenAPI schema at app.riftmap.dev/openapi.json, and a generic OpenAPI-to-MCP server turns it into MCP tools you register in .mcp.json for Claude Code or .cursor/mcp.json for Cursor. Or skip MCP entirely and have the agent call the three HTTP endpoints directly.

Why doesn’t /add-dir tell Claude Code which repositories a change affects? Because /add-dir grants access, not selection. It makes the directories you name readable and editable, but you chose those directories from memory, and it cannot add a repository that is not checked out on your disk. Knowing which repositories to add is the cross-repo dependency question itself, and that answer comes from a parsed graph, not from a wider window.