Entropic 2.11.1
Local-first agentic inference engine
Loading...
Searching...
No Matches
bash.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
12
13#include <nlohmann/json.hpp>
14
15#include <array>
16#include <cstdio>
17#include <filesystem>
18#include <stdexcept>
19#include <string>
20
21static auto logger = entropic::log::get("mcp.bash");
22
23namespace entropic {
24
25// ── working_dir validation ──────────────────────────────────────
26
45static bool is_safe_cwd(const std::string& cwd) {
46 static constexpr const char* unsafe_chars = ";&|`$<>\n\r\\\"'*?(){}[]";
47 if (cwd.find_first_of(unsafe_chars) != std::string::npos) {
48 return false;
49 }
50 std::error_code ec;
51 return std::filesystem::is_directory(cwd, ec);
52}
53
65static std::pair<std::string, int> run_popen(
66 const std::string& full_cmd) {
67
68 FILE* pipe = popen(full_cmd.c_str(), "r"); // NOLINT
69 if (pipe == nullptr) {
70 return {"Failed to open process", -1};
71 }
72
73 std::string output;
74 std::array<char, 4096> buf{};
75 while (fgets(buf.data(), buf.size(), pipe) != nullptr) {
76 output += buf.data();
77 }
78
79 int status = pclose(pipe); // NOLINT
80 int exit_code = WEXITSTATUS(status);
81 return {output, exit_code};
82}
83
84// ── ExecuteTool ─────────────────────────────────────────────────
85
91class ExecuteTool : public ToolBase {
92public:
101 : ToolBase(std::move(def)), server_(server) {}
102
110 ServerResponse execute(const std::string& args_json) override;
111
112private:
113 BashServer& server_;
114};
115
136ServerResponse ExecuteTool::execute(const std::string& args_json) {
137 auto args = nlohmann::json::parse(args_json);
138 std::string command = args.at("command").get<std::string>();
139
140 std::string cwd = args.value(
141 "working_dir", server_.working_dir().string());
142
143 logger->info("[bash.execute] cmd='{}' cwd='{}'", command, cwd);
144
145 if (!is_safe_cwd(cwd)) {
146 logger->warn("Rejected unsafe working_dir: '{}'", cwd);
147 return {"Error: working_dir is not an existing directory or "
148 "contains shell metacharacters", {}};
149 }
150
151 std::string full_cmd =
152 "cd " + cwd + " && " + command + " 2>&1";
153
154 auto [output, exit_code] = run_popen(full_cmd);
155 logger->info("Bash: exit={}, stdout={} chars, cmd='{}'",
156 exit_code, output.size(), command);
157
158 nlohmann::json result;
159 result["exit_code"] = exit_code;
160 result["output"] = output;
161 return {result.dump(), {}};
162}
163
164// ── BashServer ──────────────────────────────────────────────────
165
180 const std::filesystem::path& working_dir,
181 const std::string& data_dir,
182 int timeout)
183 : MCPServerBase("bash")
184 , working_dir_(working_dir)
185 , timeout_(timeout) {
186
187 auto def = load_tool_definition(
188 "execute", "bash", data_dir + "/tools");
189
190 execute_tool_ = std::make_unique<ExecuteTool>(
191 std::move(def), *this);
192
193 register_tool(execute_tool_.get());
194
195 logger->info("BashServer initialized: cwd='{}' timeout={}s",
196 working_dir_.string(), timeout_);
197}
198
204BashServer::~BashServer() = default;
205
222std::string
223BashServer::get_permission_pattern(const std::string& tool_name, const std::string& args_json) const {
224
225 std::string base_cmd = "unknown";
226 try {
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: {}",
234 e.what());
235 }
236 return tool_name + ":" + base_cmd + " *";
237}
238
251bool BashServer::set_working_dir(const std::string& path) {
252 working_dir_ = path;
253 logger->info("Working directory set to: {}", path);
254 return true;
255}
256
264const std::filesystem::path& BashServer::working_dir() const {
265 return working_dir_;
266}
267
275 return timeout_;
276}
277
278} // namespace entropic
Bash MCP server — shell command execution.
Bash MCP server for shell command execution.
Definition bash.h:28
int timeout() const
Get command timeout.
Definition bash.cpp:274
const std::filesystem::path & working_dir() const
Get the working directory.
Definition bash.cpp:264
bool set_working_dir(const std::string &path) override
Set working directory.
Definition bash.cpp:251
~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.
Definition bash.cpp:179
std::string get_permission_pattern(const std::string &tool_name, const std::string &args_json) const override
Permission pattern: "execute:{base_cmd} *".
Definition bash.cpp:223
Tool for executing shell commands.
Definition bash.cpp:91
ServerResponse execute(const std::string &args_json) override
Execute a shell command.
Definition bash.cpp:136
ExecuteTool(ToolDefinition def, BashServer &server)
Construct from tool definition with server ref.
Definition bash.cpp:100
Concrete base class for MCP servers (80% logic).
Definition server_base.h:66
void register_tool(ToolBase *tool)
Register a tool with this server.
Abstract base class for individual MCP tools.
Definition tool_base.h:45
spdlog initialization and logger access.
ENTROPIC_EXPORT std::shared_ptr< spdlog::logger > get(const std::string &name)
Get or create a named logger.
Definition logging.cpp:211
Activate model on GPU (WARM → ACTIVE).
static bool is_safe_cwd(const std::string &cwd)
Reject working_dir values that would smuggle shell syntax.
Definition bash.cpp:45
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.
Definition tool_base.cpp:99
static std::pair< std::string, int > run_popen(const std::string &full_cmd)
Run a shell command and capture output.
Definition bash.cpp:65
MCPServerBase concrete base class + ServerResponse.
Structured result from tool execution.
Definition server_base.h:33
Parsed tool definition from JSON schema file.
Definition tool_base.h:27
Abstract base class for individual MCP tools.