Skip to content

Commit 8bc04da

Browse files
Merge pull request #1528 from yusmer96-maker:fix/mcp-stdio-from-config
PiperOrigin-RevId: 999202068
2 parents 923d602 + 28810cd commit 8bc04da

2 files changed

Lines changed: 108 additions & 0 deletions

File tree

‎core/src/main/java/com/google/adk/tools/mcp/McpToolset.java‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,22 @@ public class McpToolset implements BaseToolset {
6161
private static final long RETRY_DELAY_MILLIS = 100;
6262
protected static final Class<? extends McpToolsetConfig> CONFIG_TYPE = McpToolsetConfig.class;
6363

64+
/**
65+
* System property that lets an operator forbid agent configs from declaring local (stdio) MCP
66+
* servers without modifying the embedding application. Setting it to {@code false} turns the
67+
* rejection on; any other value leaves the historical behaviour in place.
68+
*/
69+
private static final String ALLOW_CONFIG_STDIO_PROPERTY = "adk.mcp.allowConfigStdioServers";
70+
71+
/**
72+
* Whether an agent config may declare a local (stdio) MCP server. Defaults to {@code true}, the
73+
* behaviour restored in #1360, so existing configs keep working unchanged. It can be turned off
74+
* at launch with {@code -Dadk.mcp.allowConfigStdioServers=false}, or at runtime with {@link
75+
* #setAllowConfigStdioServers}.
76+
*/
77+
private static volatile boolean allowConfigStdioServers =
78+
!"false".equalsIgnoreCase(System.getProperty(ALLOW_CONFIG_STDIO_PROPERTY));
79+
6480
/**
6581
* Initializes the McpToolset with SSE server parameters.
6682
*
@@ -416,6 +432,19 @@ public static McpToolset fromConfig(BaseTool.ToolConfig config, String configAbs
416432
+ " for McpToolset");
417433
}
418434

435+
if ((mcpToolsetConfig.stdioServerParams() != null
436+
|| mcpToolsetConfig.stdioConnectionParams() != null)
437+
&& !allowConfigStdioServers) {
438+
throw new ConfigurationException(
439+
"Refusing to start a local MCP server declared in an agent config."
440+
+ " stdioServerParams and stdioConnectionParams launch the config-supplied command"
441+
+ " as a local process while tools are resolved, before the model is contacted."
442+
+ " This rejection is disabled by default; it is active because"
443+
+ " McpToolset.setAllowConfigStdioServers(false) was called or -D"
444+
+ ALLOW_CONFIG_STDIO_PROPERTY
445+
+ "=false was set. Remote transports (sseServerParams) are unaffected.");
446+
}
447+
419448
List<String> toolNames = mcpToolsetConfig.toolFilter();
420449
Object connectionParameters = resolveConnectionParameters(mcpToolsetConfig);
421450

@@ -430,6 +459,16 @@ public static McpToolset fromConfig(BaseTool.ToolConfig config, String configAbs
430459
}
431460
}
432461

462+
/**
463+
* Allows or forbids agent configs to declare local (stdio) MCP servers. The default is {@code
464+
* true}, matching the behaviour of previous releases. Applications that load agent configs they
465+
* did not author call this with {@code false} at startup, so a config cannot launch a local
466+
* process during tool resolution.
467+
*/
468+
public static void setAllowConfigStdioServers(boolean allow) {
469+
allowConfigStdioServers = allow;
470+
}
471+
433472
/**
434473
* Resolves the single connection-parameters object from an already-validated config. {@code
435474
* stdioServerParams} is converted to the MCP SDK {@link ServerParameters}, the type {@link

‎core/src/test/java/com/google/adk/tools/mcp/McpToolsetTest.java‎

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import io.modelcontextprotocol.json.McpJsonMapper;
3737
import io.modelcontextprotocol.spec.McpSchema;
3838
import java.util.List;
39+
import org.junit.After;
3940
import org.junit.Rule;
4041
import org.junit.Test;
4142
import org.junit.runner.RunWith;
@@ -52,6 +53,11 @@ public class McpToolsetTest {
5253
@Mock private McpSyncClient mockMcpSyncClient;
5354
@Mock private ReadonlyContext mockReadonlyContext;
5455

56+
@After
57+
public void restoreConfigStdioDefault() {
58+
McpToolset.setAllowConfigStdioServers(true);
59+
}
60+
5561
private static final McpJsonMapper jsonMapper = McpJsonDefaults.getMapper();
5662

5763
private static final ImmutableMap<String, Object> STDIO_SERVER_PARAMS =
@@ -211,6 +217,69 @@ public void testFromConfig_validStdioConnectionParams_createsToolset()
211217
assertThat(toolset).isNotNull();
212218
}
213219

220+
@Test
221+
public void testFromConfig_stdioServerParams_rejectedWhenDisallowed() {
222+
McpToolset.setAllowConfigStdioServers(false);
223+
BaseTool.ToolArgsConfig args = new BaseTool.ToolArgsConfig();
224+
args.put("stdioServerParams", STDIO_SERVER_PARAMS);
225+
226+
BaseTool.ToolConfig config = new BaseTool.ToolConfig("mcp_toolset", args);
227+
String configPath = "/path/to/config.yaml";
228+
229+
ConfigurationException exception =
230+
assertThrows(ConfigurationException.class, () -> McpToolset.fromConfig(config, configPath));
231+
232+
assertThat(exception)
233+
.hasMessageThat()
234+
.contains("Refusing to start a local MCP server declared in an agent config");
235+
}
236+
237+
@Test
238+
public void testFromConfig_stdioConnectionParams_rejectedWhenDisallowed() {
239+
McpToolset.setAllowConfigStdioServers(false);
240+
BaseTool.ToolArgsConfig args = new BaseTool.ToolArgsConfig();
241+
args.put(
242+
"stdioConnectionParams",
243+
ImmutableMap.of("timeout", 10f, "serverParams", STDIO_SERVER_PARAMS));
244+
245+
BaseTool.ToolConfig config = new BaseTool.ToolConfig("mcp_toolset", args);
246+
String configPath = "/path/to/config.yaml";
247+
248+
ConfigurationException exception =
249+
assertThrows(ConfigurationException.class, () -> McpToolset.fromConfig(config, configPath));
250+
251+
assertThat(exception)
252+
.hasMessageThat()
253+
.contains("Refusing to start a local MCP server declared in an agent config");
254+
}
255+
256+
@Test
257+
public void testFromConfig_stdioServerParams_allowedByDefault() throws ConfigurationException {
258+
BaseTool.ToolArgsConfig args = new BaseTool.ToolArgsConfig();
259+
args.put("stdioServerParams", STDIO_SERVER_PARAMS);
260+
261+
BaseTool.ToolConfig config = new BaseTool.ToolConfig("mcp_toolset", args);
262+
String configPath = "/path/to/config.yaml";
263+
264+
McpToolset toolset = McpToolset.fromConfig(config, configPath);
265+
266+
assertThat(toolset).isNotNull();
267+
}
268+
269+
@Test
270+
public void testFromConfig_sseParams_unaffectedByStdioRejection() throws ConfigurationException {
271+
McpToolset.setAllowConfigStdioServers(false);
272+
BaseTool.ToolArgsConfig args = new BaseTool.ToolArgsConfig();
273+
args.put("sseServerParams", ImmutableMap.of("url", "http://localhost:8080"));
274+
275+
BaseTool.ToolConfig config = new BaseTool.ToolConfig("mcp_toolset", args);
276+
String configPath = "/path/to/config.yaml";
277+
278+
McpToolset toolset = McpToolset.fromConfig(config, configPath);
279+
280+
assertThat(toolset).isNotNull();
281+
}
282+
214283
@Test
215284
public void testFromConfig_onlySseParams_doesNotUseStdioBranch() throws ConfigurationException {
216285
// This test ensures that when only SSE params are provided, the SSE branch is taken

0 commit comments

Comments
 (0)