abhigyanpatwari/GitNexus · error

Group routing is not available in GitNexus MCP read-only mod

Error message

Group routing is not available in GitNexus MCP read-only mode.

What it means

Thrown by assertMcpReadOnlyToolCall when read-only mode is active and a tool call passes a repo argument that starts with '@' (the cross-repo group routing syntax, e.g. repo: '@my-group'). Read-only mode deliberately disables group routing, so any @-prefixed repo is rejected at dispatch before the backend sees it.

Source

Thrown at gitnexus/src/mcp/read-only-policy.ts:40

export function resolveMcpReadOnlyMode(env: NodeJS.ProcessEnv = process.env): boolean {
  const value = env.GITNEXUS_MCP_READ_ONLY?.trim();
  if (value === undefined || value === '' || value === '0') return false;
  if (value === '1') return true;
  throw new Error('GITNEXUS_MCP_READ_ONLY must be 0 or 1.');
}

export function assertMcpReadOnlyToolCall(
  toolName: string,
  args: Record<string, unknown> | undefined,
  readOnly: boolean,
): void {
  if (!readOnly) return;
  if (!MCP_READ_ONLY_TOOLS.has(toolName) && !MCP_READ_ONLY_ALIASES.has(toolName)) {
    throw new Error(`Tool "${toolName}" is not available in GitNexus MCP read-only mode.`);
  }
  if (typeof args?.repo === 'string' && args.repo.trim().startsWith('@')) {
    throw new Error('Group routing is not available in GitNexus MCP read-only mode.');
  }
  // crossDepth/subgroup only do anything on the @group path rejected above,
  // but rejecting them here keeps the advertised schema and the dispatch
  // contract in agreement.
  for (const groupOnlyArg of ['crossDepth', 'subgroup']) {
    if (args?.[groupOnlyArg] !== undefined) {
      throw new Error(
        `Parameter "${groupOnlyArg}" is not available in GitNexus MCP read-only mode.`,
      );
    }
  }
}

export function readOnlyResourceTemplateAllowed(uriTemplate: string, readOnly: boolean): boolean {
  return !readOnly || !/^gitnexus:\/\/group\//iu.test(uriTemplate);
}

export function assertMcpReadOnlyResource(uri: string, readOnly: boolean): void {

View on GitHub (pinned to aac7515d2a)

Solutions

  1. Pass a concrete repository name or path instead of the @group specifier, e.g. repo: 'GitNexus' or repo: '/srv/repos/frontend'.
  2. Issue one call per repository instead of one group-routed call.
  3. If group routing is required, disable read-only mode (unset GITNEXUS_MCP_READ_ONLY or set it to 0) after reviewing the security trade-off.

Example fix

// before
await client.callTool({ name: 'query', arguments: { search_query: 'auth', repo: '@my-group' } });

// after
for (const repo of ['frontend', 'backend']) {
  await client.callTool({ name: 'query', arguments: { search_query: 'auth', repo } });
}
Defensive patterns

Strategy: validation

Validate before calling

function assertRepoArg(repo: unknown, readOnly: boolean) {
  if (readOnly && typeof repo === 'string' && repo.trim().startsWith('@')) {
    throw new Error('Group routing (@repo) is disabled on this read-only endpoint; pass a concrete repo.');
  }
}

Type guard

const isConcreteRepo = (repo: unknown): repo is string =>
  typeof repo === 'string' && repo.trim().length > 0 && !repo.trim().startsWith('@');

Try / catch

try {
  await client.callTool({ name: 'query', arguments: { search_query: q, repo } });
} catch (e) {
  if (e instanceof Error && e.message.includes('Group routing is not available')) {
    // fall back to per-repo calls over a known list
    for (const r of repoList) await runQuery(q, r);
  } else throw e;
}

Prevention

When it happens

Trigger: With GITNEXUS_MCP_READ_ONLY=1, calling any allowlisted tool with arguments like { repo: '@frontend' } or { repo: ' @backend' } (leading whitespace is trimmed, so it still triggers).

Common situations: A client reuses group-routing arguments from a read-write deployment ('repo: "@org"') against a hardened read-only endpoint. Agents copy the @group example from cross-repo docs without noticing the deployment difference.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20). Data as JSON: /api/errors/0e164e2b9eb1346e. Report an issue: GitHub.