{"record":{"id":"d3abbefd98629c48","repo":"abhigyanpatwari/GitNexus","slug":"group-tools-are-unavailable-when-an-mcp-repository","errorCode":null,"errorMessage":"Group tools are unavailable when an MCP repository allowlist is set.","messagePattern":"Group tools are unavailable when an MCP repository allowlist is set\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/mcp/repository-policy.ts","lineNumber":263,"sourceCode":"        total,\n        limit,\n        offset,\n        returned,\n        hasMore,\n        ...(hasMore && { nextOffset: offset + returned }),\n      },\n    };\n  }\n\n  private async callTool(\n    backend: LocalBackend,\n    method: string,\n    params: Record<string, unknown> | undefined,\n  ): Promise<unknown> {\n    if (!this.configured) return backend.callTool(method, params);\n    if (method === 'list_repos') return this.listReposPage(backend, params);\n    if (this.restricted && method.startsWith('group_')) {\n      throw new Error('Group tools are unavailable when an MCP repository allowlist is set.');\n    }\n    return backend.callTool(method, this.normalizeToolArgs(params));\n  }\n\n  private async resolveRepo(\n    backend: LocalBackend,\n    repo?: string,\n    branch?: string,\n  ): Promise<Awaited<ReturnType<LocalBackend['resolveRepo']>>> {\n    if (!this.configured) return backend.resolveRepo(repo, branch);\n    if (!this.restricted) return backend.resolveRepo(repo ?? this.defaultRepo?.path, branch);\n    const selected = this.repoForArgs(repo === undefined ? undefined : { repo });\n    return backend.resolveRepo(selected?.path, branch);\n  }\n\n  private async selectToolRepository(\n    backend: LocalBackend,\n    repo?: string,","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/52924ef12c2290ceee4612526a828ec4cdf2047f/gitnexus/src/mcp/repository-policy.ts#L245-L281","documentation":"Thrown by McpRepositoryPolicy.callTool when a repository allowlist is configured and the requested backend method starts with 'group_' (the cross-repo group tools). Allowlist mode restricts the server to its explicitly allowed local repositories, so the entire group-tool surface is disabled at the dispatch wrapper, before the backend is reached.","triggerScenarios":"GITNEXUS_MCP_ALLOWED_REPOS set (restricted), then invoking any group_* tool through MCP — e.g. the group status/contracts tool surface — via callTool. Note list_repos stays available and is intercepted separately.","commonSituations":"A multi-repo group deployment is later locked down with an allowlist for security/compliance, but dashboards or scheduled jobs still poll the group status tools. Client code enumerating all tools via tools/list and probing each one hits this on the group entries.","solutions":["Replace group tool usage with per-repo calls against each allowed repository (query/context/impact per repo).","Use list_repos to enumerate what is available under the allowlist.","If group tools are required, remove GITNEXUS_MCP_ALLOWED_REPOS from the server environment."],"exampleFix":"# before (allowlist set)\nawait client.callTool({ name: 'group_status', arguments: { group: 'org' } });\n\n# after\nfor (const repo of ['frontend', 'backend']) {\n  await client.callTool({ name: 'query', arguments: { search_query: 'status', repo } });\n}","handlingStrategy":"validation","validationCode":"function assertToolAllowedUnderAllowlist(tool: string, allowlistConfigured: boolean) {\n  if (allowlistConfigured && tool.startsWith('group_')) {\n    throw new Error('Group tools are disabled by the repository allowlist on this endpoint.');\n  }\n}","typeGuard":"const isGroupTool = (tool: string): boolean => tool.startsWith('group_');","tryCatchPattern":"try {\n  return await client.callTool({ name, arguments });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Group tools are unavailable')) {\n    return runPerRepo(name, arguments, allowedRepos); // fan out manually\n  }\n  throw e;\n}","preventionTips":["Filter tool names through !name.startsWith('group_') when running against allowlisted servers.","Use list_repos to discover what the allowlist exposes instead of assuming group surfaces exist.","When adding an allowlist to a shared deployment, audit dashboards/jobs for group_* tool usage first."],"tags":["mcp","allowlist","group-tools","authorization","configuration"],"backgroundTag":"allowlist-restriction","analyzedSha":"52924ef12c2290ceee4612526a828ec4cdf2047f","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}