13#include <nlohmann/json.hpp>
44 static constexpr const char* unsafe_chars =
";&|`$<>\n\r\\\"'*?(){}[]";
45 if (cwd.find_first_of(unsafe_chars) != std::string::npos) {
49 return std::filesystem::is_directory(cwd, ec);
60 const std::string& full_cmd) {
62 FILE* pipe = popen(full_cmd.c_str(),
"r");
63 if (pipe ==
nullptr) {
64 return {
"Failed to open process", -1};
68 std::array<char, 4096> buf{};
69 while (fgets(buf.data(), buf.size(), pipe) !=
nullptr) {
73 int status = pclose(pipe);
74 int exit_code = WEXITSTATUS(status);
75 return {output, exit_code};
95 :
ToolBase(std::move(def)), server_(server) {}
127 auto args = nlohmann::json::parse(args_json);
128 std::string command = args.at(
"command").get<std::string>();
130 std::string cwd = args.value(
133 logger->info(
"[bash.execute] cmd='{}' cwd='{}'", command, cwd);
136 logger->warn(
"Rejected unsafe working_dir: '{}'", cwd);
137 return {
"Error: working_dir is not an existing directory or "
138 "contains shell metacharacters", {}};
141 std::string full_cmd =
142 "cd " + cwd +
" && " + command +
" 2>&1";
144 auto [output, exit_code] =
run_popen(full_cmd);
145 logger->info(
"Bash: exit={}, stdout={} chars, cmd='{}'",
146 exit_code, output.size(), command);
148 nlohmann::json result;
149 result[
"exit_code"] = exit_code;
150 result[
"output"] = output;
151 return {result.dump(), {}};
165 const std::filesystem::path& working_dir,
166 const std::string& data_dir,
169 , working_dir_(working_dir)
170 , timeout_(timeout) {
173 "execute",
"bash", data_dir +
"/tools");
175 execute_tool_ = std::make_unique<ExecuteTool>(
176 std::move(def), *
this);
180 logger->info(
"BashServer initialized: cwd='{}' timeout={}s",
181 working_dir_.string(), timeout_);
202 std::string base_cmd =
"unknown";
204 auto args = nlohmann::json::parse(args_json);
205 std::string cmd = args.at(
"command").get<std::string>();
206 auto space = cmd.find(
' ');
207 base_cmd = (space != std::string::npos)
208 ? cmd.substr(0, space) : cmd;
209 }
catch (
const std::exception& e) {
210 logger->warn(
"Failed to parse command for permission: {}",
213 return tool_name +
":" + base_cmd +
" *";
225 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.