13#include <nlohmann/json.hpp>
46 static constexpr const char* unsafe_chars =
";&|`$<>\n\r\\\"'*?(){}[]";
47 if (cwd.find_first_of(unsafe_chars) != std::string::npos) {
51 return std::filesystem::is_directory(cwd, ec);
66 const std::string& full_cmd) {
68 FILE* pipe = popen(full_cmd.c_str(),
"r");
69 if (pipe ==
nullptr) {
70 return {
"Failed to open process", -1};
74 std::array<char, 4096> buf{};
75 while (fgets(buf.data(), buf.size(), pipe) !=
nullptr) {
79 int status = pclose(pipe);
80 int exit_code = WEXITSTATUS(status);
81 return {output, exit_code};
101 :
ToolBase(std::move(def)), server_(server) {}
137 auto args = nlohmann::json::parse(args_json);
138 std::string command = args.at(
"command").get<std::string>();
140 std::string cwd = args.value(
143 logger->info(
"[bash.execute] cmd='{}' cwd='{}'", command, cwd);
146 logger->warn(
"Rejected unsafe working_dir: '{}'", cwd);
147 return {
"Error: working_dir is not an existing directory or "
148 "contains shell metacharacters", {}};
151 std::string full_cmd =
152 "cd " + cwd +
" && " + command +
" 2>&1";
154 auto [output, exit_code] =
run_popen(full_cmd);
155 logger->info(
"Bash: exit={}, stdout={} chars, cmd='{}'",
156 exit_code, output.size(), command);
158 nlohmann::json result;
159 result[
"exit_code"] = exit_code;
160 result[
"output"] = output;
161 return {result.dump(), {}};
180 const std::filesystem::path& working_dir,
181 const std::string& data_dir,
184 , working_dir_(working_dir)
185 , timeout_(timeout) {
188 "execute",
"bash", data_dir +
"/tools");
190 execute_tool_ = std::make_unique<ExecuteTool>(
191 std::move(def), *
this);
195 logger->info(
"BashServer initialized: cwd='{}' timeout={}s",
196 working_dir_.string(), timeout_);
225 std::string base_cmd =
"unknown";
227 auto args = nlohmann::json::parse(args_json);
228 std::string cmd = args.at(
"command").get<std::string>();
229 auto space = cmd.find(
' ');
230 base_cmd = (space != std::string::npos)
231 ? cmd.substr(0, space) : cmd;
232 }
catch (
const std::exception& e) {
233 logger->warn(
"Failed to parse command for permission: {}",
236 return tool_name +
":" + base_cmd +
" *";
253 logger->info(
"Working directory set to: {}", path);
Bash MCP server — shell command execution.
Bash MCP server for shell command execution.
int timeout() const
Get command timeout.
const std::filesystem::path & working_dir() const
Get the working directory.
bool set_working_dir(const std::string &path) override
Set working directory.
~BashServer() override
Destructor.
BashServer(const std::filesystem::path &working_dir, const std::string &data_dir, int timeout=30)
Construct with working directory and data dir.
std::string get_permission_pattern(const std::string &tool_name, const std::string &args_json) const override
Permission pattern: "execute:{base_cmd} *".
Concrete base class for MCP servers (80% logic).
void register_tool(ToolBase *tool)
Register a tool with this server.
spdlog initialization and logger access.
ENTROPIC_EXPORT std::shared_ptr< spdlog::logger > get(const std::string &name)
Get or create a named logger.
Activate model on GPU (WARM → ACTIVE).
static bool is_safe_cwd(const std::string &cwd)
Reject working_dir values that would smuggle shell syntax.
ToolDefinition load_tool_definition(const std::string &tool_name, const std::string &server_prefix, const std::string &data_dir)
Load a tool definition from a JSON file.
static std::pair< std::string, int > run_popen(const std::string &full_cmd)
Run a shell command and capture output.
MCPServerBase concrete base class + ServerResponse.
Structured result from tool execution.