diff --git a/packages/cloud-agents/src/server/fast-agent/__tests__/fast-agent-integration-broker.test.ts b/packages/cloud-agents/src/server/fast-agent/__tests__/fast-agent-integration-broker.test.ts index cc8b4513c..225f6ac60 100644 --- a/packages/cloud-agents/src/server/fast-agent/__tests__/fast-agent-integration-broker.test.ts +++ b/packages/cloud-agents/src/server/fast-agent/__tests__/fast-agent-integration-broker.test.ts @@ -90,6 +90,11 @@ describe('fast-agent integration broker', () => { it('exposes the deployment GitHub App through its read-only router MCP', async () => { mocks.findGithubInstallation.mockResolvedValue({ id: 42 }); + mocks.listMcpTools.mockResolvedValue([ + { name: 'actions_get', inputSchema: { type: 'object' } }, + { name: 'actions_list', inputSchema: { type: 'object' } }, + { name: 'get_job_logs', inputSchema: { type: 'object' } }, + ]); const integrations = await listFastAgentIntegrations({ userId: 'user-1', @@ -99,6 +104,11 @@ describe('fast-agent integration broker', () => { expect(integrations.map((integration) => integration.id)).toEqual([ 'github', ]); + expect(integrations[0]?.tools.map((tool) => tool.name)).toEqual([ + 'actions_get', + 'actions_list', + 'get_job_logs', + ]); expect(mocks.listMcpTools).toHaveBeenCalledWith({ url: 'https://api.example.com/api/mcp-routing/github', headers: { Authorization: 'Bearer control-plane-token' }, diff --git a/packages/cloud-agents/src/server/router/__tests__/router-helpers.test.ts b/packages/cloud-agents/src/server/router/__tests__/router-helpers.test.ts index 86756ab2e..cdfb3173e 100644 --- a/packages/cloud-agents/src/server/router/__tests__/router-helpers.test.ts +++ b/packages/cloud-agents/src/server/router/__tests__/router-helpers.test.ts @@ -435,6 +435,17 @@ describe('router helpers', () => { expect(isRouterMcpToolAllowed('roomote', allowedTools[0]!)).toBe(true); }); + it('exposes only read-only GitHub Actions inspection tools', () => { + expect(getAllowedRouterMcpToolNames('github')).toEqual( + expect.arrayContaining(['actions_get', 'actions_list', 'get_job_logs']), + ); + expect(getRouterMcpUpstreamConstraints('github')).toEqual({ + readonly: true, + toolsets: ['repos', 'pull_requests', 'issues', 'actions'], + }); + expect(isRouterMcpToolAllowed('github', 'actions_run_trigger')).toBe(false); + }); + it('includes Roomote lookup support for Slack and Discord permalink references', () => { expect( shouldIncludeRoomoteRouterLookup( diff --git a/packages/cloud-agents/src/server/router/mcp-policy.ts b/packages/cloud-agents/src/server/router/mcp-policy.ts index 320962cea..7fb6ddb58 100644 --- a/packages/cloud-agents/src/server/router/mcp-policy.ts +++ b/packages/cloud-agents/src/server/router/mcp-policy.ts @@ -90,6 +90,9 @@ const ROUTER_LINEAR_ALLOWED_TOOLS = [ ] as const; const ROUTER_GITHUB_ALLOWED_TOOLS = [ + 'actions_get', + 'actions_list', + 'get_job_logs', 'get_pull_request', 'pull_request_read', 'list_pull_requests', @@ -130,7 +133,7 @@ const ROUTER_MCP_SERVER_POLICIES: Record< requiredToolGroups: ['github-pr-context', 'github-issue-context'], upstreamConstraints: { readonly: true, - toolsets: ['repos', 'pull_requests', 'issues'], + toolsets: ['repos', 'pull_requests', 'issues', 'actions'], }, }, };