1- import { parseAgentFileTools } from "@continuedev/config-yaml " ;
1+ import { ALL_BUILT_IN_TOOLS } from "src/tools/allBuiltIns.js " ;
22
33import { ensurePermissionsYamlExists } from "../permissions/permissionsYamlLoader.js" ;
44import { resolvePermissionPrecedence } from "../permissions/precedenceResolver.js" ;
@@ -11,7 +11,11 @@ import { logger } from "../util/logger.js";
1111
1212import { BaseService , ServiceWithDependencies } from "./BaseService.js" ;
1313import { serviceContainer } from "./ServiceContainer.js" ;
14- import { AgentFileServiceState , SERVICE_NAMES } from "./types.js" ;
14+ import {
15+ AgentFileServiceState ,
16+ MCPServiceState ,
17+ SERVICE_NAMES ,
18+ } from "./types.js" ;
1519
1620export interface InitializeToolServiceOverrides {
1721 allow ?: string [ ] ;
@@ -61,55 +65,94 @@ export class ToolPermissionService
6165 * Declare dependencies on other services
6266 */
6367 getDependencies ( ) : string [ ] {
64- return [ SERVICE_NAMES . AGENT_FILE ] ;
68+ return [ SERVICE_NAMES . AGENT_FILE , SERVICE_NAMES . MCP ] ;
6569 }
6670
6771 /**
6872 * Generate agent-file-specific policies if an agent file is active
6973 */
70- private generateAgentFilePolicies (
74+ generateAgentFilePolicies (
7175 agentFileServiceState ?: AgentFileServiceState ,
76+ mcpServiceState ?: MCPServiceState ,
7277 ) : undefined | ToolPermissionPolicy [ ] {
73- if ( ! agentFileServiceState ?. agentFile ?. tools ) {
78+ const parsedTools = agentFileServiceState ?. parsedTools ;
79+ if ( ! parsedTools ?. tools . length ) {
7480 return undefined ;
7581 }
7682
77- const parsedTools = parseAgentFileTools (
78- agentFileServiceState . agentFile . tools ,
79- ) ;
80- if ( parsedTools . tools . length === 0 ) {
81- return undefined ;
82- }
8383 const policies : ToolPermissionPolicy [ ] = [ ] ;
84+ const servers = Array . from ( mcpServiceState ?. connections ?. values ( ) ?? [ ] ) ;
85+ for ( const mcpServer of parsedTools . mcpServers ) {
86+ const server = servers ?. find (
87+ ( s ) => s . config ?. sourceSlug && s . config . sourceSlug === mcpServer ,
88+ ) ;
89+ if ( ! server ) {
90+ logger . warn ( "No connected MCP server found " ) ;
91+ continue ;
92+ }
93+
94+ const specificTools = parsedTools . tools . filter (
95+ ( t ) => t . mcpServer && t . toolName && t . mcpServer === mcpServer ,
96+ ) ;
8497
85- for ( const toolRef of parsedTools . tools ) {
86- if ( toolRef . mcpServer ) {
87- if ( toolRef . toolName ) {
88- policies . push ( {
89- tool : `mcp:${ toolRef . mcpServer } :${ toolRef . toolName } ` ,
90- permission : "allow" ,
91- } ) ;
92- } else {
93- policies . push ( {
94- tool : `mcp:${ toolRef . mcpServer } :*` ,
95- permission : "allow" ,
96- } ) ;
97- }
98- } else if ( toolRef . toolName ) {
99- policies . push ( { tool : toolRef . toolName , permission : "allow" } ) ;
98+ // In the `tools` key of an agent is comma separated strings like
99+ // mcp/server, mcp/server2:tool_name, Bash
100+ // - this would give the agent access to ALL tools from mcp/server, ONLY tool_name from mcp/server2, and Bash, and no other tools
101+ // mcp/server
102+ // - this would give the agent access to ALL tools from mcp/server, and no built-ins
103+ // built_in, mcp/server
104+ // - "built_in" keyword can be used to include all built ins
105+ // Blank = all built-in tools
106+
107+ // Handle MCP first
108+ // If ANY mcp tools are specifically called out, only allow those ones for that mcp server
109+ // Otherwise the blanket allow will cover all MCP tools
110+ if ( specificTools . length ) {
111+ const specificSet = new Set ( specificTools . map ( ( t ) => t . toolName ) ) ;
112+ const notMentioned = server . tools . filter (
113+ ( t ) => ! specificSet . has ( t . name ) ,
114+ ) ;
115+ const allowed : ToolPermissionPolicy [ ] = specificTools . map ( ( t ) => ( {
116+ tool : t . toolName ! ,
117+ permission : "allow" ,
118+ } ) ) ;
119+ policies . push ( ...allowed ) ;
120+ const disallowed : ToolPermissionPolicy [ ] = notMentioned . map ( ( t ) => ( {
121+ tool : t . name ,
122+ permission : "exclude" ,
123+ } ) ) ;
124+ policies . push ( ...disallowed ) ;
100125 }
101126 }
102127
103- if ( policies . length > 0 ) {
104- logger . debug ( `Generated ${ policies . length } agent file tool policies` ) ;
128+ // If mcp servers or specific built-in tools are specified
129+ // then we only inclue listed built-in tools and exclude all others
130+ const hasMcp = ! ! parsedTools . mcpServers ?. length ;
131+ const specificBuiltIns = parsedTools . tools
132+ . filter ( ( t ) => ! t . mcpServer )
133+ . map ( ( t ) => t . toolName ! ) ;
134+ if ( specificBuiltIns . length || ( hasMcp && ! parsedTools . allBuiltIn ) ) {
135+ const allowed : ToolPermissionPolicy [ ] = specificBuiltIns . map ( ( tool ) => ( {
136+ tool,
137+ permission : "allow" ,
138+ } ) ) ;
139+ policies . push ( ...allowed ) ;
140+ const specificBuiltInSet = new Set ( specificBuiltIns ) ;
141+ const notMentioned = ALL_BUILT_IN_TOOLS . map ( ( t ) => t . name ) . filter (
142+ ( name ) => ! specificBuiltInSet . has ( name ) ,
143+ ) ;
144+ const disallowed : ToolPermissionPolicy [ ] = notMentioned . map ( ( tool ) => ( {
145+ tool,
146+ permission : "exclude" ,
147+ } ) ) ;
148+ policies . push ( ...disallowed ) ;
105149 }
106150
107- if ( parsedTools . allBuiltIn ) {
108- policies . push ( { tool : "*" , permission : "allow" } ) ;
109- policies . push ( { tool : "mcp:*" , permission : "exclude" } ) ;
110- } else {
111- policies . push ( { tool : "*" , permission : "exclude" } ) ;
112- }
151+ // Allow all other tools
152+ policies . push ( {
153+ tool : "*" ,
154+ permission : "allow" ,
155+ } ) ;
113156
114157 return policies ;
115158 }
@@ -165,6 +208,7 @@ export class ToolPermissionService
165208 initializeSync (
166209 runtimeOverrides ?: InitializeToolServiceOverrides ,
167210 agentFileServiceState ?: AgentFileServiceState ,
211+ mcpServiceState ?: MCPServiceState ,
168212 ) : ToolPermissionServiceState {
169213 logger . debug ( "Synchronously initializing ToolPermissionService" ) ;
170214
@@ -180,6 +224,7 @@ export class ToolPermissionService
180224
181225 const agentFilePolicies = this . generateAgentFilePolicies (
182226 agentFileServiceState ,
227+ mcpServiceState ,
183228 ) ;
184229 const modePolicies = this . generateModePolicies ( ) ;
185230
@@ -224,11 +269,16 @@ export class ToolPermissionService
224269 async doInitialize (
225270 runtimeOverrides ?: InitializeToolServiceOverrides ,
226271 agentFileServiceState ?: AgentFileServiceState ,
272+ mcpServiceState ?: MCPServiceState ,
227273 ) : Promise < ToolPermissionServiceState > {
228274 await ensurePermissionsYamlExists ( ) ;
229275
230276 // Use the synchronous version after ensuring the file exists
231- return this . initializeSync ( runtimeOverrides , agentFileServiceState ) ;
277+ return this . initializeSync (
278+ runtimeOverrides ,
279+ agentFileServiceState ,
280+ mcpServiceState ,
281+ ) ;
232282 }
233283
234284 /**
0 commit comments