Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,13 @@ private CLIOptions buildCLIOptions(AgentTaskRequest request) {
// Set include partial messages (Claude Agent SDK v0.1.0)
builder.includePartialMessages(options.isIncludePartialMessages());

// Register MCP servers (stdio + http)
if (options.getMcpServers() != null && !options.getMcpServers().isEmpty()) {
builder.mcpServers(options.getMcpServers());
}

builder.strictMcpConfig(options.isStrictMcpConfig());

return builder.build();
}

Expand Down Expand Up @@ -499,4 +506,4 @@ private static void createProjectClaudeSettings(Path workspace) {
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@

package org.springaicommunity.agents.claude;

import org.springaicommunity.agents.claude.sdk.config.McpServerConfig;
import org.springaicommunity.agents.model.AgentOptions;

import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.LinkedHashMap;

/**
* Configuration options for Claude Code Agent Model implementations.
Expand Down Expand Up @@ -87,6 +89,13 @@ public class ClaudeAgentOptions implements AgentOptions {
*/
private Map<String, AgentDefinition> agents = Map.of();

/**
* MCP servers that should be registered with the Claude CLI instance.
*/
private Map<String, McpServerConfig> mcpServers = Map.of();

private boolean strictMcpConfig = false;

/**
* When true, resumed sessions will fork to a new session ID rather than continuing
* the previous session.
Expand Down Expand Up @@ -177,6 +186,22 @@ public void setAgents(Map<String, AgentDefinition> agents) {
this.agents = agents != null ? agents : Map.of();
}

public Map<String, McpServerConfig> getMcpServers() {
return mcpServers;
}

public void setMcpServers(Map<String, McpServerConfig> mcpServers) {
this.mcpServers = mcpServers != null ? Map.copyOf(mcpServers) : Map.of();
}

public boolean isStrictMcpConfig() {
return strictMcpConfig;
}

public void setStrictMcpConfig(boolean strictMcpConfig) {
this.strictMcpConfig = strictMcpConfig;
}

public boolean isForkSession() {
return forkSession;
}
Expand Down Expand Up @@ -271,6 +296,23 @@ public Builder agents(Map<String, AgentDefinition> agents) {
return this;
}

public Builder mcpServers(Map<String, McpServerConfig> mcpServers) {
options.setMcpServers(mcpServers);
return this;
}

public Builder addMcpServer(String name, McpServerConfig config) {
Map<String, McpServerConfig> updated = new LinkedHashMap<>(options.getMcpServers());
updated.put(name, config);
options.setMcpServers(updated);
return this;
}

public Builder strictMcpConfig(boolean strictMcpConfig) {
options.setStrictMcpConfig(strictMcpConfig);
return this;
}

public Builder forkSession(boolean forkSession) {
options.setForkSession(forkSession);
return this;
Expand All @@ -292,4 +334,4 @@ public ClaudeAgentOptions build() {

}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2024 Spring AI Community
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springaicommunity.agents.claude.sdk.config;

import java.util.List;
import java.util.Map;

/**
* Model Context Protocol (MCP) server configuration describing either a streamable HTTP
* transport or a local STDIO command. This class lives in the Claude SDK package so the
* SDK can expose MCP configuration without depending on higher-level modules.
*/
public record McpServerConfig(String type, String url, String command, List<String> args, Map<String, String> env,
Map<String, String> headers) {

public McpServerConfig {
if (command != null && command.isBlank()) {
throw new IllegalArgumentException("MCP server command must not be blank when provided");
}
if (type != null && type.isBlank()) {
throw new IllegalArgumentException("MCP server type must not be blank when provided");
}
args = args != null ? List.copyOf(args) : List.of();
env = env != null ? Map.copyOf(env) : Map.of();
headers = headers != null ? Map.copyOf(headers) : Map.of();
}

public static McpServerConfig http(String url) {
return http(url, Map.of());
}

public static McpServerConfig http(String url, Map<String, String> headers) {
if (url == null || url.isBlank()) {
throw new IllegalArgumentException("MCP server url must not be null or blank");
}
return new McpServerConfig("http", url, null, List.of(), Map.of(), headers);
}

public static McpServerConfig stdio(String command, List<String> args, Map<String, String> env) {
if (command == null || command.isBlank()) {
throw new IllegalArgumentException("MCP stdio server command must not be null or blank");
}
return new McpServerConfig(null, null, command, args, env, Map.of());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

Expand All @@ -30,8 +31,9 @@
*/
public record SDKConfiguration(String model, String systemPrompt, String appendSystemPrompt, Integer maxTokens,
Integer maxThinkingTokens, Duration timeout, Path workingDirectory, List<String> allowedTools,
List<String> disallowedTools, PermissionMode permissionMode, boolean continueConversation,
String resumeFromSession, Integer maxTurns, Map<String, Object> additionalSettings) {
List<String> disallowedTools, Map<String, McpServerConfig> mcpServers, boolean strictMcpConfig,
PermissionMode permissionMode, boolean continueConversation, String resumeFromSession, Integer maxTurns,
Map<String, Object> additionalSettings) {

public SDKConfiguration {
// Validation and defaults
Expand All @@ -47,6 +49,12 @@ public record SDKConfiguration(String model, String systemPrompt, String appendS
if (disallowedTools == null) {
disallowedTools = List.of();
}
if (mcpServers == null) {
mcpServers = Map.of();
}
else {
mcpServers = Map.copyOf(mcpServers);
}
if (permissionMode == null) {
permissionMode = PermissionMode.DEFAULT;
}
Expand All @@ -69,6 +77,8 @@ public CLIOptions toCliOptions() {
.timeout(timeout)
.allowedTools(allowedTools)
.disallowedTools(disallowedTools)
.mcpServers(mcpServers)
.strictMcpConfig(strictMcpConfig)
.build();
}

Expand All @@ -94,8 +104,8 @@ public static Builder builder() {

public static SDKConfiguration defaultConfiguration() {
return new SDKConfiguration(null, null, null, null, 8000, Duration.ofMinutes(2),
Paths.get(System.getProperty("user.dir")), List.of(), List.of(), PermissionMode.BYPASS_PERMISSIONS,
false, null, null, Map.of());
Paths.get(System.getProperty("user.dir")), List.of(), List.of(), Map.of(), false,
PermissionMode.BYPASS_PERMISSIONS, false, null, null, Map.of());
}

// Convenience getters
Expand Down Expand Up @@ -131,6 +141,10 @@ public List<String> getDisallowedTools() {
return disallowedTools;
}

public Map<String, McpServerConfig> getMcpServers() {
return mcpServers;
}

public PermissionMode getPermissionMode() {
return permissionMode;
}
Expand Down Expand Up @@ -171,6 +185,10 @@ public static class Builder {

private List<String> disallowedTools = List.of();

private Map<String, McpServerConfig> mcpServers = Map.of();

private boolean strictMcpConfig = false;

private PermissionMode permissionMode = PermissionMode.BYPASS_PERMISSIONS;

private boolean continueConversation = false;
Expand Down Expand Up @@ -226,6 +244,23 @@ public Builder disallowedTools(List<String> disallowedTools) {
return this;
}

public Builder mcpServers(Map<String, McpServerConfig> mcpServers) {
this.mcpServers = mcpServers != null ? Map.copyOf(mcpServers) : Map.of();
return this;
}

public Builder addMcpServer(String name, McpServerConfig config) {
Map<String, McpServerConfig> updated = new LinkedHashMap<>(this.mcpServers);
updated.put(name, config);
this.mcpServers = Map.copyOf(updated);
return this;
}

public Builder strictMcpConfig(boolean strictMcpConfig) {
this.strictMcpConfig = strictMcpConfig;
return this;
}

public Builder permissionMode(PermissionMode permissionMode) {
this.permissionMode = permissionMode;
return this;
Expand Down Expand Up @@ -253,9 +288,9 @@ public Builder additionalSettings(Map<String, Object> additionalSettings) {

public SDKConfiguration build() {
return new SDKConfiguration(model, systemPrompt, appendSystemPrompt, maxTokens, maxThinkingTokens, timeout,
workingDirectory, allowedTools, disallowedTools, permissionMode, continueConversation,
resumeFromSession, maxTurns, additionalSettings);
workingDirectory, allowedTools, disallowedTools, mcpServers, strictMcpConfig, permissionMode,
continueConversation, resumeFromSession, maxTurns, additionalSettings);
}

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
import org.springaicommunity.agents.claude.sdk.config.PermissionMode;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.LinkedHashMap;

import org.springaicommunity.agents.claude.sdk.config.McpServerConfig;

/**
* Configuration options for Claude CLI commands. Corresponds to ClaudeAgentOptions in
Expand All @@ -28,7 +32,7 @@
public record CLIOptions(String model, String systemPrompt, Integer maxTokens, Duration timeout,
List<String> allowedTools, List<String> disallowedTools, PermissionMode permissionMode, boolean interactive,
OutputFormat outputFormat, List<String> settingSources, String agents, boolean forkSession,
boolean includePartialMessages) {
boolean includePartialMessages, Map<String, McpServerConfig> mcpServers, boolean strictMcpConfig) {

public CLIOptions {
// Validation
Expand All @@ -50,6 +54,12 @@ public record CLIOptions(String model, String systemPrompt, Integer maxTokens, D
if (settingSources == null) {
settingSources = List.of(); // Default: no filesystem settings loaded
}
if (mcpServers == null) {
mcpServers = Map.of();
}
else {
mcpServers = Map.copyOf(mcpServers);
}
}

public static Builder builder() {
Expand All @@ -58,7 +68,8 @@ public static Builder builder() {

public static CLIOptions defaultOptions() {
return new CLIOptions(null, null, null, Duration.ofMinutes(2), List.of(), List.of(),
PermissionMode.DANGEROUSLY_SKIP_PERMISSIONS, false, OutputFormat.JSON, List.of(), null, false, false);
PermissionMode.DANGEROUSLY_SKIP_PERMISSIONS, false, OutputFormat.JSON, List.of(), null, false, false,
Map.of(), false);
}

// Convenience getters
Expand Down Expand Up @@ -114,6 +125,14 @@ public boolean isIncludePartialMessages() {
return includePartialMessages;
}

public Map<String, McpServerConfig> getMcpServers() {
return mcpServers;
}

public boolean isStrictMcpConfig() {
return strictMcpConfig;
}

public static class Builder {

private String model;
Expand Down Expand Up @@ -142,6 +161,10 @@ public static class Builder {

private boolean includePartialMessages = false;

private Map<String, McpServerConfig> mcpServers = Map.of();

private boolean strictMcpConfig = false;

public Builder model(String model) {
this.model = model;
return this;
Expand Down Expand Up @@ -207,11 +230,34 @@ public Builder includePartialMessages(boolean includePartialMessages) {
return this;
}

public Builder mcpServers(Map<String, McpServerConfig> mcpServers) {
this.mcpServers = mcpServers != null ? Map.copyOf(mcpServers) : Map.of();
return this;
}

public Builder addMcpServer(String name, McpServerConfig server) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("MCP server name must not be null or blank");
}
if (server == null) {
throw new IllegalArgumentException("MCP server configuration must not be null");
}
Map<String, McpServerConfig> updated = new LinkedHashMap<>(this.mcpServers);
updated.put(name, server);
this.mcpServers = Map.copyOf(updated);
return this;
}

public Builder strictMcpConfig(boolean strictMcpConfig) {
this.strictMcpConfig = strictMcpConfig;
return this;
}

public CLIOptions build() {
return new CLIOptions(model, systemPrompt, maxTokens, timeout, allowedTools, disallowedTools,
permissionMode, interactive, outputFormat, settingSources, agents, forkSession,
includePartialMessages);
includePartialMessages, mcpServers, strictMcpConfig);
}

}
}
}
Loading