How the GitNexus MCP Server Manages Multi-Repo Configurations and Parameter Routing

The GitNexus MCP server uses a global registry file and an in-memory LocalBackend to dynamically route tool calls to the correct repository based on a deterministic ID resolution algorithm.

GitNexus is an open-source Model Context Protocol (MCP) server designed to index and query multiple local codebases simultaneously. Understanding how the GitNexus MCP server handles multi-repo configuration and parameter routing is essential for developers integrating AI agents like Cursor or Claude across several projects.

The Three-Component Architecture for Multi-Repo Support

The system achieves multi-repository awareness through three tightly-coupled components that work together to discover, identify, and route requests to the correct codebase.

Global Registry (~/.gitnexus/registry.json)

At the heart of the configuration lies a JSON registry located at ~/.gitnexus/registry.json. This file lists every repository indexed by GitNexus, storing metadata including the repository name, absolute path, storage location, and KuzuDB database path. The registry is automatically updated when running gitnexus analyze, allowing the MCP server to discover new repositories without requiring a restart.

LocalBackend (src/mcp/local/local-backend.ts)

The LocalBackend class serves as the routing engine. Located in gitnexus/src/mcp/local/local-backend.ts, this lightweight in-process backend reads the global registry during initialization and builds an in-memory map of RepoHandle objects. It manages lazy initialization of KuzuDB connections and contains the core resolution logic for determining which repository should handle each tool call.

MCP Server (src/mcp/server.ts)

The actual JSON-RPC server implementation resides in gitnexus/src/mcp/server.ts. This component registers available MCP tools and forwards incoming requests to LocalBackend.callTool(). By delegating all repository-specific logic to the backend, the server maintains a clean separation between transport concerns and business logic.

Repository Discovery and ID Resolution

When the MCP server starts, it initializes the backend which immediately begins the discovery process to map all available codebases.

Loading the Global Registry

The LocalBackend.init() method (lines 90-99 in local-backend.ts) orchestrates the loading sequence:

  1. Registry Reading: Calls listRegisteredRepos() from gitnexus/src/storage/repo-manager.ts to parse ~/.gitnexus/registry.json
  2. Handle Creation: For each entry, creates a RepoHandle containing the repository name, path, storage location, KuzuDB path, and basic statistics
  3. Cache Population: Fills two Maps: repos (mapping repoId to RepoHandle) and contextCache (mapping repoId to lightweight CodebaseContext)
  4. Stale Entry Pruning: Removes any repositories from memory that no longer exist in the registry

Generating Stable Repository IDs

To handle collisions where multiple repositories share the same folder name (e.g., multiple my-project directories), GitNexus generates deterministic unique identifiers. The repoId() method in local-backend.ts (lines 155-166) implements the following logic:

private repoId(name: string, repoPath: string): string {
  const base = name.toLowerCase();
  // Detect collision → hash the absolute path
  for (const [id, handle] of this.repos) {
    if (id === base && handle.repoPath !== path.resolve(repoPath)) {
      const hash = Buffer.from(repoPath).toString('base64url').slice(0, 6);
      return `${base}-${hash}`;
    }
  }
  return base;
}

This algorithm uses a base64url-encoded hash of the absolute path (first 6 characters) as a suffix when collisions are detected, ensuring that each repository receives a unique repoId used as the key for KuzuDB connection pooling.

Parameter Routing in Tool Calls

Every MCP tool invocation undergoes a resolution process to determine which repository context to use, followed by dispatch to the appropriate handler.

The Resolution Algorithm

The resolveRepo() and resolveRepoFromCache() methods in local-backend.ts implement a three-tier resolution strategy:

  1. Cache Lookup: Checks the in-memory repos map for:

    • Exact repoId match
    • Case-insensitive repository name
    • Absolute path match
    • Substring match against available repositories
  2. Registry Refresh on Miss: If the cache lookup fails, refreshRepos() is called once to reload the global registry and pick up newly-indexed repositories, then the lookup is retried.

  3. Error Handling: The system provides specific error messages for different failure modes:

    • No repositories indexed: No indexed repositories. Run: gitnexus analyze
    • Unknown repository specified: Repository "<value>" not found. Available: …
    • Ambiguous repository selection: Multiple repositories indexed. Specify which one with the "repo" parameter…

Tool Dispatch in callTool

The LocalBackend.callTool() method serves as the single entry point for all MCP tool executions (lines 89-122 in local-backend.ts). Its implementation uses a switch statement to route requests:

async callTool(method: string, params: any): Promise<any> {
  if (method === 'list_repos') return this.listRepos();

  // Resolve repo (may throw if ambiguous)
  const repo = await this.resolveRepo(params?.repo);

  switch (method) {
    case 'query':          return this.query(repo, params);
    case 'cypher':         return this.formatCypherAsMarkdown(await this.cypher(repo, params));
    case 'context':        return this.context(repo, params);
    case 'impact':         return this.impact(repo, params);
    case 'detect_changes': return this.detectChanges(repo, params);
    case 'rename':         return this.rename(repo, params);
    // legacy aliases …
    default: throw new Error(`Unknown tool: ${method}`);
  }
}

Key routing behaviors:

  • list_repos: Operates globally without requiring a repository parameter, returning the entire registry contents.
  • Repository-specific tools (query, cypher, context, impact, detect_changes, rename): All require repository resolution via resolveRepo(). If the repo parameter is omitted and exactly one repository is indexed, that repository is used automatically.
  • Legacy aliases: Tools like search, explore, and overview are mapped to their modern implementations while maintaining backward compatibility.

Transport Layer Abstraction

The routing logic remains consistent across different transport mechanisms because both share the same LocalBackend instance.

STDIO Transport

The command gitnexus mcp launches the STDIO-based server (implemented in gitnexus/src/cli/mcp.ts). This creates a LocalBackend, initializes it to load the global registry, and starts the JSON-RPC server over standard input/output streams.

HTTP Transport

For web-based integrations, mountMCPEndpoints in gitnexus/src/server/mcp-http.ts mounts the same MCP Server instance on an Express application under the /api/mcp endpoint. This allows AI agents to connect via HTTP while utilizing identical repository resolution and routing logic.

Practical Code Examples

Starting the MCP Server via STDIO

Launch the server to auto-discover all indexed repositories:

npx gitnexus mcp

Behind the scenes in gitnexus/src/cli/mcp.ts:

import { startMCPServer } from '../mcp/server.js';
import { LocalBackend } from '../mcp/local/local-backend.js';

const backend = new LocalBackend();
await backend.init();                 // loads the global registry
await startMCPServer(backend);        // STDIO JSON-RPC transport

Listing All Registered Repositories

Send a JSON-RPC request to enumerate available repositories:

{
  "jsonrpc": "2.0",
  "method": "call_tool",
  "params": { "name": "list_repos", "arguments": {} },
  "id": 1
}

Response excerpt:

{
  "jsonrpc": "2.0",
  "result": {
    "content": [{ "type": "text", "text": "[{\"name\":\"my-app\",\"path\":\"/home/me/projects/my-app\",\"indexedAt\":\"2026-03-08…\"}, …]" }]
  },
  "id": 1
}

Querying a Specific Repository

Force routing to a specific repository using the repo parameter:

{
  "jsonrpc": "2.0",
  "method": "call_tool",
  "params": {
    "name": "query",
    "arguments": {
      "repo": "my-app",
      "query": "authentication",
      "limit": 5
    }
  },
  "id": 2
}

LocalBackend.callTool resolves "my-app" to its internal repoId, lazily initializes the KuzuDB connection via ensureInitialized, executes the hybrid search, and returns grouped results.

Using the HTTP Endpoint

For integrations requiring HTTP transport:

curl -X POST http://localhost:3000/api/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"call_tool","params":{"name":"context","arguments":{"repo":"my-app","name":"AuthService"}},"id":3}'

The mountMCPEndpoints function in gitnexus/src/server/mcp-http.ts ensures the same routing logic applies regardless of transport protocol.

Summary

  • Global Registry: GitNexus maintains a central ~/.gitnexus/registry.json file that tracks all indexed repositories, enabling automatic discovery without server restarts.
  • Stable ID Generation: The LocalBackend generates deterministic repository IDs using base64url-encoded path hashes to handle naming collisions when multiple repositories share the same folder name.
  • Hierarchical Resolution: Parameter routing follows a three-tier strategy—cache lookup, registry refresh, and specific error handling—to resolve repository references from IDs, names, paths, or substrings.
  • Unified Dispatch: The callTool method in local-backend.ts serves as a single entry point that routes requests to repository-specific implementations or global tools like list_repos.
  • Transport Agnostic: Both STDIO (gitnexus mcp) and HTTP (/api/mcp) transports share the same LocalBackend instance, ensuring consistent multi-repo behavior across integration methods.

Frequently Asked Questions

How does GitNexus handle two repositories with the same name?

When the LocalBackend detects a naming collision during initialization, it appends a base64url-encoded hash of the absolute path (first 6 characters) to the repository name. For example, two directories named my-app located at /home/user/project-a/my-app and /home/user/project-b/my-app would receive IDs like my-app and my-app-a3f9b2, ensuring unique identification in the internal repos Map.

Can I query multiple repositories simultaneously in a single tool call?

No, the current architecture routes each tool call to exactly one repository context. The resolveRepo() method in local-backend.ts selects a single RepoHandle based on the repo parameter or defaults to the sole indexed repository when unambiguous. To query multiple repositories, clients must issue separate tool calls for each target repository and aggregate results externally.

What happens if I specify a repository that hasn't been indexed yet?

If the repo parameter does not match any cached repository, the LocalBackend triggers refreshRepos() once to reload the global registry from ~/.gitnexus/registry.json. If the repository still cannot be found after this refresh, the system returns a specific error message listing all available repositories: Repository "<value>" not found. Available: [list]. This ensures users immediately understand which repositories are accessible without restarting the server.

How does the server distinguish between global and repository-specific tools?

The callTool method in local-backend.ts checks the tool name before attempting repository resolution. Tools like list_repos execute immediately without requiring a repo parameter, returning the entire registry contents. For all other tools (query, cypher, context, impact, etc.), the method first calls resolveRepo(params?.repo) to obtain the target repository context before dispatching to the specific implementation. This branching logic ensures global operations remain available while maintaining strict repository scoping for code-specific queries.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →