Entropic 2.11.1
Local-first agentic inference engine
Loading...
Searching...
No Matches
server_manager.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
18
19#include <nlohmann/json.hpp>
20
21static auto logger = entropic::log::get("mcp.server_manager");
22
23namespace entropic {
24
33 const PermissionsConfig& permissions,
34 const std::filesystem::path& project_dir)
35 : permissions_(permissions.allow, permissions.deny),
36 project_dir_(project_dir) {}
37
55 const MCPConfig& mcp,
56 const std::vector<std::string>& tier_names,
57 const std::string& data_dir) {
58 if (mcp.enable_entropic) {
59 register_server(std::make_unique<EntropicServer>(
60 tier_names, data_dir));
61 }
62 if (mcp.enable_filesystem) {
63 register_server(std::make_unique<FilesystemServer>(
64 project_dir_, mcp.filesystem, data_dir));
65 }
66 if (mcp.enable_bash) {
67 register_server(std::make_unique<BashServer>(
68 project_dir_, data_dir));
69 }
70 if (mcp.enable_git) {
71 register_server(std::make_unique<GitServer>(
72 project_dir_, data_dir));
73 }
74 if (mcp.enable_diagnostics) {
75 register_server(std::make_unique<DiagnosticsServer>(
76 project_dir_, data_dir));
77 }
78 if (mcp.enable_web) {
79 register_server(std::make_unique<WebServer>(data_dir));
80 }
81}
82
104 auto first_error = ENTROPIC_OK;
105
106 for (const auto& path : mcp.plugins) {
107 std::unique_ptr<PluginServer> plugin;
108 auto rc = PluginServer::load(path, plugin);
109 if (rc != ENTROPIC_OK) {
110 // Keep going: attempting every path diagnoses a whole broken
111 // config in one run instead of one entry per restart.
112 first_error = (first_error == ENTROPIC_OK) ? rc : first_error;
113 continue;
114 }
115
116 auto name = plugin->name();
117 if (servers_.count(name) > 0 || external_clients_.count(name) > 0 ||
118 plugin_servers_.count(name) > 0) {
119 logger->error("Plugin '{}' from {} collides with an already "
120 "registered server of the same name — refusing to "
121 "shadow it", name, path.string());
122 first_error = (first_error == ENTROPIC_OK)
124 : first_error;
125 continue;
126 }
127
128 plugin->set_working_dir(project_dir_.string());
129 plugin_servers_[name] = std::move(plugin);
130 logger->info("Registered plugin server: {}", name);
131 }
132
133 return first_error;
134}
135
143 std::unique_ptr<MCPServerBase> server) {
144 auto name = server->name();
145 if (servers_.count(name) > 0) {
146 logger->warn("Server '{}' already registered — replacing",
147 name);
148 }
149 servers_[name] = std::move(server);
150 logger->info("Registered server: {}", name);
151}
152
159 logger->info("Initializing {} in-process MCP servers",
160 servers_.size());
161 for (auto& [name, server] : servers_) {
162 logger->info("Server '{}' ready", name);
163 }
164
165 initialize_external_servers();
166}
167
181static void append_prefixed_tools(const std::string& tools_json,
182 const std::string& prefix,
183 nlohmann::json& all) {
184 auto tools = nlohmann::json::parse(tools_json);
185 for (auto& tool : tools) {
186 std::string orig_name = tool.at("name");
187 tool["name"] = prefix + "." + orig_name;
188 all.push_back(std::move(tool));
189 }
190}
191
204void ServerManager::append_plugin_tools(nlohmann::json& all) const {
205 for (const auto& [name, plugin] : plugin_servers_) {
206 try {
207 append_prefixed_tools(plugin->list_tools(), name, all);
208 } catch (const std::exception& e) {
209 logger->error("Plugin '{}' returned an unusable tool list: {} — "
210 "its tools are unavailable this session", name,
211 e.what());
212 }
213 }
214}
215
226void ServerManager::append_external_tools(nlohmann::json& all) const {
227 for (const auto& [name, client] : external_clients_) {
228 if (!client->is_connected()) {
229 continue;
230 }
231 auto tools = nlohmann::json::parse(client->list_tools());
232 for (auto& tool : tools) {
233 all.push_back(std::move(tool));
234 }
235 }
236}
237
251std::string ServerManager::list_tools() const {
252 auto all = nlohmann::json::array();
253
254 // In-process servers
255 for (const auto& [name, server] : servers_) {
256 append_prefixed_tools(server->list_tools(), name, all);
257 }
258
259 append_plugin_tools(all); // gh#133 (v2.10.1)
260 append_external_tools(all);
261
262 logger->info("Tool list: {} tools from {} server(s) + {} plugin(s) + "
263 "{} external", all.size(), servers_.size(),
264 plugin_servers_.size(), external_clients_.size());
265 return all.dump();
266}
267
288 const std::string& tool_name,
289 const std::string& args_json) {
290
291 // Check permissions first
292 auto pattern = tool_name + ":" + args_to_pattern(args_json);
293 if (permissions_.is_denied(tool_name, pattern)) {
294 logger->warn("Permission denied: {}", tool_name);
295 nlohmann::json resp;
296 resp["result"] = "Error: Permission denied for " + tool_name;
297 resp["directives"] = nlohmann::json::array();
298 return resp.dump();
299 }
300
301 return route_tool_call(tool_name, args_json);
302}
303
313MCPServerBase* ServerManager::get_server(const std::string& name) const {
314 auto it = servers_.find(name);
315 return (it != servers_.end()) ? it->second.get() : nullptr;
316}
317
326std::vector<std::string> ServerManager::server_names() const {
327 std::vector<std::string> names;
328 for (const auto& [name, _] : servers_) {
329 names.push_back(name);
330 }
331 for (const auto& [name, _] : plugin_servers_) { // gh#133 (v2.10.1)
332 names.push_back(name);
333 }
334 for (const auto& [name, _] : external_clients_) {
335 names.push_back(name);
336 }
337 return names;
338}
339
356 const std::string& tool_name) const {
357 auto prefix = extract_prefix(tool_name);
358 auto local_name = extract_local_name(tool_name);
359 auto it = servers_.find(prefix);
360 if (it != servers_.end()) {
361 auto* tool = it->second->registry().get_tool(local_name);
362 return (tool != nullptr) ? tool->definition().input_schema
363 : std::string{};
364 }
365 // gh#133 (v2.10.1): without this a plugin's declared inputSchema would be
366 // invisible to ToolExecutor::check_schema, which skips validation on an
367 // empty schema — plugin tools would silently forgo the argument checking
368 // every built-in server's tools get.
369 return plugin_tool_schema(prefix, local_name);
370}
371
387std::string ServerManager::plugin_tool_schema(
388 const std::string& prefix,
389 const std::string& local_name) const {
390
391 auto it = plugin_servers_.find(prefix);
392 if (it == plugin_servers_.end()) {
393 return "";
394 }
395 try {
396 auto tools = nlohmann::json::parse(it->second->list_tools());
397 for (const auto& tool : tools) {
398 if (tool.value("name", std::string{}) == local_name) {
399 return tool.value("inputSchema",
400 nlohmann::json::object()).dump();
401 }
402 }
403 } catch (const std::exception& e) {
404 logger->error("Plugin '{}': cannot read schema for tool '{}': {}",
405 prefix, local_name, e.what());
406 }
407 return "";
408}
409
427std::string ServerManager::route_tool_call(
428 const std::string& tool_name,
429 const std::string& args_json) {
430
431 auto prefix = extract_prefix(tool_name);
432 auto local_name = extract_local_name(tool_name);
433
434 // Try in-process server first
435 auto it = servers_.find(prefix);
436 if (it != servers_.end()) {
437 return it->second->execute(local_name, args_json);
438 }
439
440 // gh#133 (v2.10.1): try a loaded plugin
441 auto plug_it = plugin_servers_.find(prefix);
442 if (plug_it != plugin_servers_.end()) {
443 return route_plugin_call(*plug_it->second, local_name, args_json);
444 }
445
446 return route_external_or_unknown(prefix, tool_name, local_name, args_json);
447}
448
466std::string ServerManager::route_external_or_unknown(
467 const std::string& prefix,
468 const std::string& tool_name,
469 const std::string& local_name,
470 const std::string& args_json) {
471
472 auto ext_it = external_clients_.find(prefix);
473 if (ext_it != external_clients_.end()) {
474 return route_external_call(
475 ext_it->second.get(), tool_name, local_name, args_json);
476 }
477
478 logger->warn("Unknown server: {}", prefix);
479 nlohmann::json resp;
480 resp["result"] = "Error: Unknown server '" + prefix + "'";
481 resp["directives"] = nlohmann::json::array();
482 return resp.dump();
483}
484
498std::string ServerManager::route_external_call(
499 ExternalMCPClient* client,
500 const std::string& tool_name,
501 const std::string& local_name,
502 const std::string& args_json) {
503
504 if (!client->is_connected()) {
505 auto prefix = extract_prefix(tool_name);
506 return disconnected_error(tool_name, prefix);
507 }
508 return client->execute(local_name, args_json);
509}
510
529std::string ServerManager::route_plugin_call(
530 PluginServer& plugin,
531 const std::string& local_name,
532 const std::string& args_json) {
533
534 std::string raw;
535 try {
536 raw = plugin.execute(local_name, args_json);
537 auto parsed = nlohmann::json::parse(raw);
538 if (!parsed.is_object() || !parsed.contains("result")) {
539 throw std::runtime_error("missing 'result' field");
540 }
541 return raw;
542 } catch (const std::exception& e) {
543 logger->error("Plugin '{}' returned an invalid response for tool "
544 "'{}': {} — raw response: {}",
545 plugin.name(), local_name, e.what(), raw);
546 nlohmann::json resp;
547 resp["result"] = "Error: plugin '" + plugin.name() +
548 "' returned an invalid response: " + e.what();
549 resp["directives"] = nlohmann::json::array();
550 return resp.dump();
551 }
552}
553
569 const std::string& tool_name,
570 const std::string& args_json) const {
571 auto pattern = tool_name + ":" + args_to_pattern(args_json);
572 return permissions_.is_allowed(tool_name, pattern);
573}
574
591 const std::string& tool_name,
592 const std::string& args_json) const {
593 auto prefix = extract_prefix(tool_name);
594 auto it = servers_.find(prefix);
595 if (it != servers_.end()) {
596 return it->second->get_permission_pattern(
597 tool_name, args_json);
598 }
599 return tool_name;
600}
601
612 const std::string& tool_name) const {
613 auto prefix = extract_prefix(tool_name);
614 auto it = servers_.find(prefix);
615 if (it != servers_.end()) {
616 auto local = extract_local_name(tool_name);
617 return it->second->skip_duplicate_check(local);
618 }
619 return false;
620}
621
635 const std::string& tool_name) const {
636 auto prefix = extract_prefix(tool_name);
637 auto it = servers_.find(prefix);
638 if (it != servers_.end()) {
639 auto local = extract_local_name(tool_name);
640 auto* tool = it->second->registry().get_tool(local);
641 if (tool != nullptr) {
642 return tool->required_access_level();
643 }
644 }
645 return MCPAccessLevel::WRITE; // Safe default
646}
647
661 const std::string& pattern, bool allow) {
662 permissions_.add_permission(pattern, allow);
663}
664
678 // Stop health monitor first
679 if (health_monitor_) {
680 health_monitor_->stop();
681 }
682
683 // Disconnect external clients
684 for (auto& [name, client] : external_clients_) {
685 client->disconnect();
686 }
687 external_clients_.clear();
688
689 // gh#133 (v2.10.1): destroy plugin instances and dlclose their libraries
690 // before the in-process servers, so nothing can call into an unmapped .so.
691 logger->info("Unloading {} MCP plugin(s)", plugin_servers_.size());
692 plugin_servers_.clear();
693
694 // Destroy in-process servers
695 logger->info("Shutting down {} MCP servers", servers_.size());
696 servers_.clear();
697 server_info_.clear();
698}
699
709std::string ServerManager::extract_prefix(
710 const std::string& tool_name) {
711 auto dot = tool_name.find('.');
712 if (dot == std::string::npos) {
713 return tool_name;
714 }
715 return tool_name.substr(0, dot);
716}
717
727std::string ServerManager::extract_local_name(
728 const std::string& tool_name) {
729 auto dot = tool_name.find('.');
730 if (dot == std::string::npos) {
731 return tool_name;
732 }
733 return tool_name.substr(dot + 1);
734}
735
749std::string ServerManager::args_to_pattern(
750 const std::string& args_json) {
751 if (args_json.empty() || args_json == "{}") {
752 return "*";
753 }
754 try {
755 auto j = nlohmann::json::parse(args_json);
756 if (j.is_object() && !j.empty()) {
757 auto first = j.begin();
758 if (first->is_string()) {
759 return first->get<std::string>();
760 }
761 }
762 } catch (...) {
763 // Parse failure — treat as wildcard
764 }
765 return "*";
766}
767
768// ── v1.8.7: External server methods ─────────────────────
769
777 mcp_config_ = config;
778}
779
791 for (auto& [_, client] : external_clients_) {
792 if (client) { client->interrupt(); }
793 }
794}
795
807void ServerManager::initialize_external_servers() {
808 // Create .mcp.json discovery
809 mcp_json_discovery_ = std::make_unique<MCPJsonDiscovery>(
810 project_dir_);
811
812 // YAML config external_servers
813 for (const auto& [name, entry] : mcp_config_.external_servers) {
814 auto client = create_external_client(name, entry);
815 connect_and_register_external(name, std::move(client),
816 "config", entry.url,
817 entry.command);
818 }
819
820 // .mcp.json discovery
821 std::set<std::string> existing;
822 for (const auto& [name, _] : servers_) {
823 existing.insert(name);
824 }
825 for (const auto& [name, _] : external_clients_) {
826 existing.insert(name);
827 }
828
829 auto discovered = mcp_json_discovery_->discover(existing);
830 for (const auto& cfg : discovered) {
831 auto client = create_external_client(cfg);
832 connect_and_register_external(cfg.name, std::move(client),
833 "mcp_json", cfg.url,
834 cfg.command);
835 }
836
837 // Start health monitor
838 health_monitor_ = std::make_unique<HealthMonitor>(
839 ReconnectPolicy(mcp_config_.reconnect),
840 mcp_config_.health_check_interval_ms);
841
842 for (auto& [name, client] : external_clients_) {
843 health_monitor_->watch(name, client.get());
844 }
845 if (!external_clients_.empty()) {
846 health_monitor_->start();
847 }
848
849 logger->info("External MCP: {} servers connected",
850 external_clients_.size());
851}
852
863void ServerManager::connect_and_register_external(
864 const std::string& name,
865 std::unique_ptr<ExternalMCPClient> client,
866 const std::string& source,
867 const std::string& url,
868 const std::string& command) {
869
870 ServerInfo info;
871 info.name = name;
872 info.transport = url.empty() ? "stdio" : "sse";
873 info.url = url;
874 info.command = command;
875 info.source = source;
876 info.status = "disconnected";
877
878 bool ok = client->connect();
879 if (ok) {
880 info.status = "connected";
881 info.connected_at = std::chrono::system_clock::now();
882 } else {
883 info.status = "error";
884 logger->error("Failed to connect external server '{}'", name);
885 }
886
887 server_info_[name] = info;
888 external_clients_[name] = std::move(client);
889}
890
907std::unique_ptr<Transport> ServerManager::make_transport(
908 const ExternalServerConfig& spec) {
909 bool prefer_sse = (spec.transport == "sse")
910 || (!spec.url.empty() && spec.command.empty());
911 if (prefer_sse) {
912 return std::make_unique<SSETransport>(spec.url);
913 }
914 // gh#19 (v2.1.5): pass the registered server name as the display
915 // label so child stderr lines and lifecycle logs identify the
916 // server, not the resolved spawn command (which collides when
917 // multiple servers share an entrypoint like /usr/bin/env python).
918 return std::make_unique<StdioTransport>(
919 spec.name, spec.command, spec.args, spec.env,
920 /*default_timeout_ms=*/30000U);
921}
922
939 const ExternalServerConfig& spec) {
940
941 if (servers_.count(spec.name) > 0 ||
942 external_clients_.count(spec.name) > 0) {
943 logger->warn("Server '{}' already registered", spec.name);
944 return {};
945 }
946
947 auto client = std::make_unique<ExternalMCPClient>(
948 spec.name, make_transport(spec));
949
950 connect_and_register_external(spec.name, std::move(client),
951 "runtime", spec.url, spec.command);
952
953 auto& registered = external_clients_[spec.name];
954 if (health_monitor_) {
955 health_monitor_->watch(spec.name, registered.get());
956 }
957
958 // Parse tool names from cached list
959 std::vector<std::string> tool_names;
960 auto tools_json = registered->list_tools();
961 try {
962 auto tools = nlohmann::json::parse(tools_json);
963 for (const auto& t : tools) {
964 tool_names.push_back(t["name"].get<std::string>());
965 }
966 } catch (...) {}
967
968 return tool_names;
969}
970
981 const std::string& name,
982 const std::string& command,
983 const std::vector<std::string>& args,
984 const std::string& url) {
986 spec.name = name;
987 spec.command = command;
988 spec.args = args;
989 spec.url = url;
990 return connect_external_server(spec);
991}
992
1000 const std::string& name) {
1001
1002 auto it = external_clients_.find(name);
1003 if (it == external_clients_.end()) {
1004 logger->warn("External server '{}' not found", name);
1005 return;
1006 }
1007
1008 if (health_monitor_) {
1009 health_monitor_->unwatch(name);
1010 }
1011
1012 it->second->disconnect();
1013 external_clients_.erase(it);
1014 server_info_.erase(name);
1015
1016 logger->info("External server '{}' disconnected", name);
1017}
1018
1029std::map<std::string, ServerInfo>
1031 auto result = server_info_;
1032
1033 // Add in-process servers
1034 for (const auto& [name, _] : servers_) {
1035 if (result.count(name) == 0) {
1036 ServerInfo info;
1037 info.name = name;
1038 info.transport = "in_process";
1039 info.status = "connected";
1040 info.source = "builtin";
1041 result[name] = info;
1042 }
1043 }
1044
1045 // gh#133 (v2.10.1): plugins report their own transport so `entropic
1046 // inspect` distinguishes them from builtins and external processes.
1047 for (const auto& [name, plugin] : plugin_servers_) {
1048 if (result.count(name) == 0) {
1049 ServerInfo info;
1050 info.name = name;
1051 info.transport = "plugin";
1052 info.status = "connected";
1053 info.source = "plugin";
1054 info.command = plugin->path().string();
1055 result[name] = info;
1056 }
1057 }
1058 return result;
1059}
1060
1071 if (health_monitor_) {
1072 health_monitor_->process_events();
1073 }
1074}
1075
1090std::unique_ptr<ExternalMCPClient>
1091ServerManager::create_external_client(
1092 const std::string& name,
1093 const ExternalServerEntry& entry) {
1095 spec.name = name;
1096 spec.command = entry.command;
1097 spec.args = entry.args;
1098 spec.env = std::map<std::string, std::string>(
1099 entry.env.begin(), entry.env.end());
1100 spec.url = entry.url;
1101 spec.transport = entry.url.empty() ? "stdio" : "sse";
1102 return std::make_unique<ExternalMCPClient>(
1103 name, make_transport(spec));
1104}
1105
1116std::unique_ptr<ExternalMCPClient>
1117ServerManager::create_external_client(
1118 const ExternalServerConfig& config) {
1119 return std::make_unique<ExternalMCPClient>(
1120 config.name, make_transport(config));
1121}
1122
1137std::string ServerManager::disconnected_error(
1138 const std::string& tool_name,
1139 const std::string& server_name) {
1140
1141 nlohmann::json resp;
1142 resp["result"] = "Server '" + server_name +
1143 "' is disconnected. Tool '" + tool_name +
1144 "' is unavailable. Use a different approach "
1145 "or try again later.";
1146 resp["directives"] = nlohmann::json::array();
1147 resp["is_error"] = true;
1148 return resp.dump();
1149}
1150
1151} // namespace entropic
Bash MCP server — shell command execution.
Concrete base class for MCP servers (80% logic).
Definition server_base.h:66
bool is_denied(const std::string &tool_name, const std::string &pattern) const
Check if a tool call is explicitly denied.
void add_permission(const std::string &pattern, bool allow)
Add a permission pattern at runtime.
bool is_allowed(const std::string &tool_name, const std::string &pattern) const
Check if a tool call is explicitly allowed (skip prompting).
static entropic_error_t load(const std::filesystem::path &path, std::unique_ptr< PluginServer > &out)
Load a plugin .so and construct its server instance.
std::vector< std::string > connect_external_server(const ExternalServerConfig &spec)
Connect to an external MCP server at runtime (canonical spec-based API).
MCPAccessLevel get_required_access_level(const std::string &tool_name) const
Get the required access level for a tool.
bool is_explicitly_allowed(const std::string &tool_name, const std::string &args_json) const
Check if tool is explicitly allowed (skip prompting).
std::map< std::string, ServerInfo > list_server_info() const
Get snapshot of all servers with current status.
std::vector< std::string > server_names() const
List registered server names (in-process + external).
void process_health_events()
Process pending health events (call from engine loop).
MCPServerBase * get_server(const std::string &name) const
Get a registered in-process server by name.
std::string list_tools() const
List all tools from all connected servers.
ServerManager(const PermissionsConfig &permissions, const std::filesystem::path &project_dir)
Construct with permission config and project directory.
void register_server(std::unique_ptr< MCPServerBase > server)
Register a built-in server (in-process, ownership transferred).
void add_permission(const std::string &pattern, bool allow)
Add a runtime permission pattern.
entropic_error_t load_plugins(const MCPConfig &mcp_config)
Load the dlopen plugins listed in mcp.plugins (gh#133).
bool skip_duplicate_check(const std::string &tool_name) const
Check if tool should skip duplicate detection.
void init_builtins(const MCPConfig &mcp_config, const std::vector< std::string > &tier_names, const std::string &data_dir)
Register built-in servers based on config flags.
std::string get_permission_pattern(const std::string &tool_name, const std::string &args_json) const
Generate permission pattern via server class delegation.
std::string get_tool_schema(const std::string &tool_name) const
Get the JSON Schema for a tool's input parameters.
void interrupt_external_tools()
Abort in-flight tool calls across every external MCP client.
std::string execute(const std::string &tool_name, const std::string &args_json)
Execute a tool call via the appropriate server.
void set_mcp_config(const MCPConfig &config)
Set MCP config for external server initialization.
void shutdown()
Shutdown all servers (in-process + external).
void disconnect_external_server(const std::string &name)
Disconnect and remove an external server.
void initialize()
Initialize all registered servers.
Diagnostics MCP server — LSP client for code diagnostics.
Entropic MCP server — engine-level tools including introspection.
entropic_error_t
Error codes returned by all C API functions.
Definition error.h:37
@ ENTROPIC_OK
Success.
Definition error.h:38
@ ENTROPIC_ERROR_PLUGIN_LOAD_FAILED
dlopen/dlsym failed on plugin .so
Definition error.h:48
Filesystem MCP server — read/write/edit/glob/grep/list_directory.
Git MCP server — version control operations.
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).
@ ok
Tool dispatched, returned non-empty content.
static void append_prefixed_tools(const std::string &tools_json, const std::string &prefix, nlohmann::json &all)
Append a server's tools to an array, prefixing each name.
MCPAccessLevel
MCP tool access level for per-identity authorization.
Definition config.h:38
@ WRITE
Read + write operations (e.g., write_file, execute)
MCP server lifecycle management and tool routing.
Parsed server entry from .mcp.json.
std::map< std::string, std::string > env
Stdio env vars.
std::string command
Stdio command (empty for SSE)
std::string transport
"stdio" | "sse"
std::vector< std::string > args
Stdio command args.
std::string url
SSE URL (empty for stdio)
Configuration for a single external MCP server entry.
Definition config.h:667
std::string command
Stdio command (empty for SSE)
Definition config.h:668
std::vector< std::string > args
Stdio command arguments.
Definition config.h:669
std::string url
SSE endpoint URL (empty for stdio)
Definition config.h:671
std::unordered_map< std::string, std::string > env
Stdio environment variables.
Definition config.h:670
MCP server configuration.
Definition config.h:678
ReconnectConfig reconnect
Reconnection backoff policy.
Definition config.h:703
bool enable_entropic
Enable entropic internal server (handoff, delegate, pipeline)
Definition config.h:679
std::vector< std::filesystem::path > plugins
In-process MCP server plugin .so paths (gh#133, v2.10.1).
Definition config.h:699
FilesystemConfig filesystem
Filesystem server config.
Definition config.h:685
bool enable_filesystem
Enable filesystem server.
Definition config.h:680
bool enable_git
Enable git server.
Definition config.h:682
bool enable_diagnostics
Enable diagnostics server.
Definition config.h:683
uint32_t health_check_interval_ms
Ping interval (0 = disabled)
Definition config.h:704
bool enable_bash
Enable bash server.
Definition config.h:681
bool enable_web
Enable web server.
Definition config.h:684
Tool permission configuration.
Definition config.h:619
Runtime state of a connected MCP server.
SSE transport for external MCP servers.
Stdio transport for external MCP servers.
Web MCP server — web_fetch + web_search.