Entropic 2.3.8
Local-first agentic inference engine
Loading...
Searching...
No Matches
entropic_server.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
22
23#include <nlohmann/json.hpp>
24
25#include <algorithm>
26#include <chrono>
27#include <cstdlib>
28#include <string>
29#include <vector>
30
31static auto logger = entropic::log::get("mcp.entropic");
32
33namespace entropic {
34
35// ── TodoItem ────────────────────────────────────────────────────
36
42struct TodoItem {
43 std::string content;
44 std::string status;
45};
46
47// ── TodoTool ────────────────────────────────────────────────────
48
54class TodoTool : public ToolBase {
55public:
63 : ToolBase(std::move(def)) {}
64
72 ServerResponse execute(const std::string& args_json) override;
73
81 std::string anchor_key(
82 const std::string& args_json) const override;
83
84private:
95 void apply_todo_action(const std::string& action,
96 const nlohmann::json& args);
97
98 std::vector<TodoItem> items_;
99
106 std::string format_list() const;
107};
108
117 const std::string& /*args_json*/) const {
118 return "todo_state";
119}
120
127std::string TodoTool::format_list() const {
128 if (items_.empty()) {
129 return "(empty)";
130 }
131 std::string out;
132 for (size_t i = 0; i < items_.size(); ++i) {
133 out += std::to_string(i) + ". [" +
134 items_[i].status + "] " +
135 items_[i].content + "\n";
136 }
137 return out;
138}
139
154void TodoTool::apply_todo_action(const std::string& action,
155 const nlohmann::json& args) {
156 if (action == "add") {
157 std::string content = args.at("content").get<std::string>();
158 items_.push_back({content, "pending"});
159 logger->info("[todo] add: {}", content);
160 } else if (action == "update") {
161 auto idx = args.at("index").get<size_t>();
162 if (idx < items_.size()) {
163 items_[idx].status = args.value("status", items_[idx].status);
164 items_[idx].content = args.value("content", items_[idx].content);
165 logger->info("[todo] update #{}: {}", idx, items_[idx].status);
166 }
167 } else if (action == "remove") {
168 auto idx = args.at("index").get<size_t>();
169 if (idx < items_.size()) {
170 logger->info("[todo] remove #{}", idx);
171 items_.erase(items_.begin() + static_cast<ptrdiff_t>(idx));
172 }
173 }
174}
175
181ServerResponse TodoTool::execute(const std::string& args_json) {
182 auto args = nlohmann::json::parse(args_json);
183 std::string action = args.at("action").get<std::string>();
184
185 apply_todo_action(action, args);
186
187 nlohmann::json result;
188 result["todo_state"] = format_list();
189 result["action"] = action;
190
191 Directive anchor_d;
193 Directive notify_d;
195 return {result.dump(), {anchor_d, notify_d}};
196}
197
198// ── DelegateTool ────────────────────────────────────────────────
199
205class DelegateTool : public ToolBase {
206public:
215 const std::vector<std::string>& tier_names);
216
224 ServerResponse execute(const std::string& args_json) override;
225};
226
235 ToolDefinition def,
236 const std::vector<std::string>& tier_names)
237 : ToolBase(std::move(def)) {
238
239 auto schema = nlohmann::json::parse(definition_.input_schema);
240 schema["properties"]["target"]["enum"] = tier_names;
241 definition_.input_schema = schema.dump();
242
243 logger->info("[delegate] patched enum with {} tiers",
244 tier_names.size());
245}
246
254ServerResponse DelegateTool::execute(const std::string& args_json) {
255 auto args = nlohmann::json::parse(args_json);
256 std::string target = args.at("target").get<std::string>();
257 std::string task = args.at("task").get<std::string>();
258 int max_turns = args.value("max_turns", -1);
259
260 logger->info("[delegate] target='{}' task='{}' max_turns={}",
261 target, task, max_turns);
262
263 nlohmann::json result;
264 result["action"] = "delegate";
265 result["target"] = target;
266 result["task"] = task;
267 result["max_turns"] = max_turns;
268
269 Directive delegate_d;
271
272 Directive stop_d;
274
275 return {result.dump(), {delegate_d, stop_d}};
276}
277
278// ── PipelineTool ────────────────────────────────────────────────
279
285class PipelineTool : public ToolBase {
286public:
295 const std::vector<std::string>& tier_names);
296
304 ServerResponse execute(const std::string& args_json) override;
305};
306
315 ToolDefinition def,
316 const std::vector<std::string>& tier_names)
317 : ToolBase(std::move(def)) {
318
319 auto schema = nlohmann::json::parse(definition_.input_schema);
320 schema["properties"]["stages"]["items"]["enum"] = tier_names;
321 definition_.input_schema = schema.dump();
322
323 logger->info("[pipeline] patched enum with {} tiers",
324 tier_names.size());
325}
326
334ServerResponse PipelineTool::execute(const std::string& args_json) {
335 auto args = nlohmann::json::parse(args_json);
336 auto stages = args.at("stages").get<std::vector<std::string>>();
337 std::string task = args.at("task").get<std::string>();
338
339 if (stages.size() < 2) {
340 logger->warn("[pipeline] rejected: fewer than 2 stages");
341 return {"Error: pipeline requires at least 2 stages", {}};
342 }
343
344 logger->info("[pipeline] stages={} task='{}'",
345 stages.size(), task);
346
347 nlohmann::json result;
348 result["action"] = "pipeline";
349 result["stages"] = stages;
350 result["task"] = task;
351
352 Directive pipeline_d;
354
355 Directive stop_d;
357
358 return {result.dump(), {pipeline_d, stop_d}};
359}
360
361// ── CompleteTool ────────────────────────────────────────────────
362
368class CompleteTool : public ToolBase {
369public:
377 : ToolBase(std::move(def)) {}
378
386 ServerResponse execute(const std::string& args_json) override;
387};
388
405ServerResponse CompleteTool::execute(const std::string& args_json) {
406 auto args = nlohmann::json::parse(args_json);
407 std::string summary = args.at("summary").get<std::string>();
408 bool coverage_gap = args.value("coverage_gap", false);
409 std::string gap_description =
410 args.value("gap_description", std::string{});
411 std::vector<std::string> suggested;
412 if (args.contains("suggested_files")
413 && args["suggested_files"].is_array()) {
414 suggested = args["suggested_files"]
415 .get<std::vector<std::string>>();
416 }
417
418 // Issue #10 (v2.1.4): coverage_gap=true REQUIRES a non-empty
419 // gap_description so the lead context's [COVERAGE GAP] message
420 // tells the next specialist what to fill in. An empty
421 // gap_description with coverage_gap=true would be a bare
422 // "I don't know" with no signal — refuse it at the tool boundary.
423 if (coverage_gap && gap_description.empty()) {
424 nlohmann::json err;
425 err["error"] = "missing_gap_description";
426 err["message"] =
427 "coverage_gap=true requires a non-empty gap_description "
428 "(what's missing from this answer and why).";
429 return {err.dump(), {}};
430 }
431
432 logger->info("[complete] summary='{}' coverage_gap={} "
433 "gap_description_len={} suggested_files={}",
434 summary, coverage_gap,
435 gap_description.size(), suggested.size());
436
437 nlohmann::json result;
438 result["action"] = "complete";
439 result["summary"] = summary;
440 result["coverage_gap"] = coverage_gap;
441 result["gap_description"] = gap_description;
442 result["suggested_files"] = suggested;
443
444 Directive complete_d;
446
447 Directive stop_d;
449
450 return {result.dump(), {complete_d, stop_d}};
451}
452
453// ── PhaseChangeTool ─────────────────────────────────────────────
454
460class PhaseChangeTool : public ToolBase {
461public:
468
476 ServerResponse execute(const std::string& args_json) override;
477};
478
485 : ToolBase({"phase_change",
486 "Switch inference phase",
487 R"({"type":"object","properties":{"phase":{"type":"string"}},"required":["phase"]})"}) {}
488
497 const std::string& args_json) {
498 auto args = nlohmann::json::parse(args_json);
499 std::string phase = args.at("phase").get<std::string>();
500
501 logger->info("[phase_change] phase='{}'", phase);
502
503 nlohmann::json result;
504 result["action"] = "phase_change";
505 result["phase"] = phase;
506
507 Directive phase_d;
509
510 return {result.dump(), {phase_d}};
511}
512
513// ── PruneContextTool ────────────────────────────────────────────
514
521public:
528 : ToolBase(std::move(def)) {}
529
536 ServerResponse execute(const std::string& args_json) override;
537};
538
547 const std::string& args_json) {
548 auto args = nlohmann::json::parse(args_json);
549
550 static constexpr int default_keep = 2;
551 int keep_recent = args.value("keep_recent", default_keep);
552
553 logger->info("[prune_context] keep_recent={}", keep_recent);
554
555 nlohmann::json result;
556 result["action"] = "prune_context";
557 result["keep_recent"] = keep_recent;
558
559 Directive prune_d;
561
562 return {result.dump(), {prune_d}};
563}
564
565// ── DiagnoseTool ────────────────────────────────────────────────
566
572class DiagnoseTool : public ToolBase {
573public:
581 : ToolBase(std::move(def)) {}
582
590 ServerResponse execute(const std::string& args_json) override;
591
600 }
601
608 provider_ = p;
609 }
610
611private:
612 const entropic_state_provider_t* provider_ = nullptr;
613};
614
623static std::string call_provider(
624 char* (*fn)(void*), void* ud) {
625 if (fn == nullptr) {
626 return "{}";
627 }
628 char* raw = fn(ud);
629 if (raw == nullptr) {
630 return "{}";
631 }
632 std::string result(raw);
633 free(raw);
634 return result;
635}
636
646static std::string call_history_provider(
647 char* (*fn)(int, void*), int max_entries, void* ud) {
648 if (fn == nullptr) {
649 return "[]";
650 }
651 char* raw = fn(max_entries, ud);
652 if (raw == nullptr) {
653 return "[]";
654 }
655 std::string result(raw);
656 free(raw);
657 return result;
658}
659
669static std::string call_docs_provider(
670 char* (*fn)(const char*, void*),
671 const char* section, void* ud) {
672 if (fn == nullptr) {
673 return "";
674 }
675 char* raw = fn(section, ud);
676 if (raw == nullptr) {
677 return "";
678 }
679 std::string result(raw);
680 free(raw);
681 return result;
682}
683
693static nlohmann::json build_snapshot(
695 bool include_docs, int history_limit) {
696
697 nlohmann::json snap;
698
699 auto now = std::chrono::system_clock::now();
700 auto ms = std::chrono::duration_cast<
701 std::chrono::milliseconds>(
702 now.time_since_epoch()).count();
703 snap["snapshot_timestamp_ms"] = ms;
704
705 snap["engine"] = nlohmann::json::parse(
707 snap["config"] = nlohmann::json::parse(
709 snap["identities"] = nlohmann::json::parse(
711 snap["tools"] = nlohmann::json::parse(
713 snap["history"] = nlohmann::json::parse(
715 p.get_history, history_limit, p.user_data));
716 snap["metrics"] = nlohmann::json::parse(
718
719 if (include_docs) {
720 snap["docs"] = call_docs_provider(
721 p.get_docs, nullptr, p.user_data);
722 } else {
723 snap["docs"] = nullptr;
724 }
725 return snap;
726}
727
735ServerResponse DiagnoseTool::execute(const std::string& args_json) {
736 if (provider_ == nullptr) {
737 logger->error("[diagnose] no state provider set");
738 return {"Error: engine state provider not configured", {}};
739 }
740
741 auto args = nlohmann::json::parse(args_json);
742 bool include_docs = args.value("include_docs", false);
743 int history_limit = args.value("history_limit", 20);
744
745 logger->info("[diagnose] include_docs={} history_limit={}",
746 include_docs, history_limit);
747
748 auto snap = build_snapshot(
749 *provider_, include_docs, history_limit);
750 return {snap.dump(), {}};
751}
752
753// ── InspectTool ─────────────────────────────────────────────────
754
760class InspectTool : public ToolBase {
761public:
769 : ToolBase(std::move(def)) {}
770
778 ServerResponse execute(const std::string& args_json) override;
779
788 }
789
796 provider_ = p;
797 }
798
799private:
800 const entropic_state_provider_t* provider_ = nullptr;
801};
802
803// ── ContextInspectTool ──────────────────────────────────────────
804
815public:
823 : ToolBase(std::move(def)) {}
824
832 ServerResponse execute(const std::string& args_json) override;
833
842 }
843
850 provider_ = p;
851 }
852
853private:
854 const entropic_state_provider_t* provider_ = nullptr;
855};
856
864static std::vector<std::string> collect_object_keys(
865 const nlohmann::json& j) {
866 std::vector<std::string> keys;
867 for (auto it = j.begin(); it != j.end(); ++it) {
868 keys.push_back(it.key());
869 }
870 return keys;
871}
872
880static std::vector<std::string> collect_array_names(
881 const nlohmann::json& j) {
882 std::vector<std::string> names;
883 for (const auto& item : j) {
884 if (item.is_object() && item.contains("name")) {
885 names.push_back(item["name"].get<std::string>());
886 }
887 }
888 return names;
889}
890
898static std::string list_available_keys(const nlohmann::json& j) {
899 auto keys = j.is_object() ? collect_object_keys(j)
901 std::string result;
902 for (const auto& k : keys) {
903 if (!result.empty()) { result += ", "; }
904 result += k;
905 }
906 return result;
907}
908
918static std::string filter_json_by_key(
919 const std::string& json_str,
920 const std::string& key,
921 const std::string& label) {
922 auto j = nlohmann::json::parse(json_str);
923
924 if (j.is_object() && j.contains(key)) {
925 return j[key].dump();
926 }
927 if (j.is_array()) {
928 for (const auto& item : j) {
929 if (item.value("name", "") == key) {
930 return item.dump();
931 }
932 }
933 }
934 return "Error: " + label + " '" + key
935 + "' not found. Available: "
937}
938
949static std::string inspect_filterable(
950 char* (*fn)(void*), void* ud,
951 const std::string& key, const std::string& label) {
952 auto raw = call_provider(fn, ud);
953 if (key.empty()) {
954 return raw;
955 }
956 return filter_json_by_key(raw, key, label);
957}
958
971 const std::string& target,
972 const std::string& key,
973 std::string& result) {
974
975 if (target == "state") {
976 result = call_provider(p.get_state, p.user_data);
977 } else if (target == "metrics") {
978 result = call_provider(p.get_metrics, p.user_data);
979 } else if (target == "history") {
980 int limit = key.empty() ? 10 : std::atoi(key.c_str());
981 result = call_history_provider(
982 p.get_history, limit, p.user_data);
983 } else if (target == "docs") {
984 const char* sec = key.empty() ? nullptr : key.c_str();
985 result = call_docs_provider(p.get_docs, sec, p.user_data);
986 } else {
987 return false;
988 }
989 return true;
990}
991
1004 const std::string& target,
1005 const std::string& key,
1006 std::string& result) {
1007 if (target == "config") {
1008 result = inspect_filterable(
1009 p.get_config, p.user_data, key, "config section");
1010 } else if (target == "identity") {
1011 result = inspect_filterable(
1012 p.get_identities, p.user_data, key, "identity");
1013 } else if (target == "tool") {
1014 result = inspect_filterable(
1015 p.get_tools, p.user_data, key, "tool");
1016 } else {
1017 return false;
1018 }
1019 return true;
1020}
1021
1031static std::string dispatch_inspect(
1033 const std::string& target,
1034 const std::string& key) {
1035
1036 std::string result;
1037 if (dispatch_filterable_target(p, target, key, result)) {
1038 return result;
1039 }
1040 if (dispatch_simple_target(p, target, key, result)) {
1041 return result;
1042 }
1043 return "Error: unknown target '" + target
1044 + "'. Supported: config, identity, tool, state, "
1045 "metrics, history, docs";
1046}
1047
1072ServerResponse InspectTool::execute(const std::string& args_json) {
1073 if (provider_ == nullptr) {
1074 logger->error("[inspect] no state provider set");
1075 return {"Error: engine state provider not configured", {}};
1076 }
1077
1078 auto args = nlohmann::json::parse(args_json, nullptr, false);
1079 // gh#33 (v2.1.6): non-throwing parse returns a discarded value on
1080 // invalid/empty/null input; calling .value() on it throws
1081 // type_error.306 and crashes the engine. Coerce to an empty object
1082 // so a no-arg `entropic.inspect()` call falls through to the
1083 // full-state dump path.
1084 if (args.is_discarded() || !args.is_object()) {
1085 args = nlohmann::json::object();
1086 }
1087 std::string target = args.value("target", "");
1088 std::string key = args.value("key", "");
1089
1090 if (target.empty()) {
1091 logger->info("[inspect] full state dump (no target)");
1092 auto state = call_provider(provider_->get_state,
1093 provider_->user_data);
1094 return {state, {}};
1095 }
1096
1097 logger->info("[inspect] target='{}' key='{}'", target, key);
1098 auto result = dispatch_inspect(*provider_, target, key);
1099 return {result, {}};
1100}
1101
1114 const std::string& args_json) {
1115 if (provider_ == nullptr) {
1116 logger->error("[context_inspect] no state provider set");
1117 return {"Error: engine state provider not configured", {}};
1118 }
1119
1120 auto args = nlohmann::json::parse(args_json, nullptr, false);
1121 int max_messages = args.value("max_messages", 0);
1122
1123 logger->info("[context_inspect] max_messages={}", max_messages);
1124 auto result = call_history_provider(
1125 provider_->get_history, max_messages, provider_->user_data);
1126 return {result, {}};
1127}
1128
1129// ── FollowupTool (gh#32, v2.1.6) ────────────────────────────────
1130
1141class FollowupTool : public ToolBase {
1142public:
1150 : ToolBase(std::move(def)) {}
1151
1159 ServerResponse execute(const std::string& args_json) override;
1160
1168 return MCPAccessLevel::READ;
1169 }
1170
1177 provider_ = p;
1178 }
1179
1180private:
1181 const entropic_state_provider_t* provider_ = nullptr;
1182};
1183
1191ServerResponse FollowupTool::execute(const std::string& args_json) {
1192 auto args = nlohmann::json::parse(args_json, nullptr, false);
1193 std::string body;
1194 if (args.is_discarded() || !args.is_object()) {
1195 body = R"({"error":"invalid args: object with 'query' required"})";
1196 } else {
1197 std::string query = args.value("query", "");
1198 int max_results = args.value("max_results", 3);
1199 if (query.empty()) {
1200 body = R"({"error":"'query' is required and must be non-empty"})";
1201 } else if (provider_ == nullptr
1202 || provider_->search_delegations == nullptr) {
1203 logger->warn("[followup] no search_delegations provider "
1204 "configured");
1205 body = R"({"error":"delegation storage not available"})";
1206 } else {
1207 logger->info("[followup] query='{}' max_results={}",
1208 query, max_results);
1209 char* raw = provider_->search_delegations(
1210 query.c_str(), max_results, provider_->user_data);
1211 if (raw == nullptr) {
1212 body = R"({"results":[]})";
1213 } else {
1214 body = raw;
1215 std::free(raw);
1216 }
1217 }
1218 }
1219 return {body, {}};
1220}
1221
1222// ── ResumeDelegationTool (gh#32, v2.1.6) ────────────────────────
1223
1237public:
1245 : ToolBase(std::move(def)) {}
1246
1254 ServerResponse execute(const std::string& args_json) override;
1255};
1256
1264ServerResponse ResumeDelegationTool::execute(const std::string& args_json) {
1265 auto args = nlohmann::json::parse(args_json, nullptr, false);
1266 if (args.is_discarded() || !args.is_object()) {
1267 return {R"({"error":"invalid args: object required"})", {}};
1268 }
1269 std::string delegation_id = args.value("delegation_id", "");
1270 std::string task = args.value("task", "");
1271 int max_turns = args.value("max_turns", -1);
1272 if (delegation_id.empty() || task.empty()) {
1273 return {R"({"error":"'delegation_id' and 'task' are required"})",
1274 {}};
1275 }
1276 logger->info("[resume_delegation] id='{}' task='{}' max_turns={}",
1277 delegation_id, task, max_turns);
1278
1279 nlohmann::json result;
1280 result["action"] = "resume_delegation";
1281 result["delegation_id"] = delegation_id;
1282 result["task"] = task;
1283 result["max_turns"] = max_turns;
1284
1285 Directive delegate_d;
1286 delegate_d.type = ENTROPIC_DIRECTIVE_DELEGATE;
1287 Directive stop_d;
1289 return {result.dump(), {delegate_d, stop_d}};
1290}
1291
1292// ── EntropicServer ──────────────────────────────────────────────
1293
1301int EntropicServer::register_core_tools(
1302 const std::string& tools_dir) {
1303 auto todo_def = load_tool_definition(
1304 "todo", "entropic", tools_dir);
1305 todo_ = std::make_unique<TodoTool>(std::move(todo_def));
1306 register_tool(todo_.get());
1307
1308 auto complete_def = load_tool_definition(
1309 "complete", "entropic", tools_dir);
1310 complete_ = std::make_unique<CompleteTool>(
1311 std::move(complete_def));
1312 register_tool(complete_.get());
1313
1314 phase_change_ = std::make_unique<PhaseChangeTool>();
1315 register_tool(phase_change_.get());
1316
1317 auto prune_def = load_tool_definition(
1318 "prune_context", "entropic", tools_dir);
1319 prune_context_ = std::make_unique<PruneContextTool>(
1320 std::move(prune_def));
1321 register_tool(prune_context_.get());
1322
1323 return 4;
1324}
1325
1334int EntropicServer::register_delegation_tools(
1335 const std::string& tools_dir,
1336 const std::vector<std::string>& tier_names) {
1337 if (tier_names.size() <= 1) {
1338 return 0;
1339 }
1340 auto delegate_def = load_tool_definition(
1341 "delegate", "entropic", tools_dir);
1342 delegate_ = std::make_unique<DelegateTool>(
1343 std::move(delegate_def), tier_names);
1344 register_tool(delegate_.get());
1345
1346 auto pipeline_def = load_tool_definition(
1347 "pipeline", "entropic", tools_dir);
1348 pipeline_ = std::make_unique<PipelineTool>(
1349 std::move(pipeline_def), tier_names);
1350 register_tool(pipeline_.get());
1351
1352 // gh#32 (v2.1.6): resume_delegation lives alongside delegate
1353 // because it produces the same directive kind (resume-flavored).
1354 auto resume_def = load_tool_definition(
1355 "resume_delegation", "entropic", tools_dir);
1356 resume_delegation_ = std::make_unique<ResumeDelegationTool>(
1357 std::move(resume_def));
1358 register_tool(resume_delegation_.get());
1359
1360 return 3;
1361}
1362
1370int EntropicServer::register_introspection_tools(
1371 const std::string& tools_dir) {
1372 diagnose_ = std::make_unique<DiagnoseTool>(
1373 load_tool_definition("diagnose", "entropic", tools_dir));
1374 register_tool(diagnose_.get());
1375
1376 inspect_ = std::make_unique<InspectTool>(
1377 load_tool_definition("inspect", "entropic", tools_dir));
1378 register_tool(inspect_.get());
1379
1380 context_inspect_ = std::make_unique<ContextInspectTool>(
1381 load_tool_definition("context_inspect", "entropic", tools_dir));
1382 register_tool(context_inspect_.get());
1383
1384 // gh#32 (v2.1.6): followup is read-only and consumes the same
1385 // state_provider plumbing as diagnose/inspect.
1386 followup_ = std::make_unique<FollowupTool>(
1387 load_tool_definition("followup", "entropic", tools_dir));
1388 register_tool(followup_.get());
1389
1390 return 4;
1391}
1392
1400 const std::vector<std::string>& tier_names,
1401 const std::string& data_dir)
1402 : MCPServerBase("entropic") {
1403
1404 std::string tools_dir = data_dir + "/tools";
1405 int count = register_core_tools(tools_dir);
1406 count += register_delegation_tools(tools_dir, tier_names);
1407 count += register_introspection_tools(tools_dir);
1408
1409 logger->info("EntropicServer initialized with {} tools "
1410 "({} tiers)", count, tier_names.size());
1411}
1412
1418
1427 const std::string& tool_name) const {
1428 return tool_name == "delegate" || tool_name == "pipeline";
1429}
1430
1438 const entropic_state_provider_t& provider) {
1439 state_provider_ = provider;
1440 diagnose_->set_provider(&state_provider_);
1441 inspect_->set_provider(&state_provider_);
1442 context_inspect_->set_provider(&state_provider_);
1443 if (followup_) {
1444 followup_->set_provider(&state_provider_);
1445 }
1446 logger->info("State provider set for introspection tools");
1447}
1448
1449} // namespace entropic
Tool for signaling task completion.
CompleteTool(ToolDefinition def)
Construct from tool definition.
ServerResponse execute(const std::string &args_json) override
Execute completion signal.
Tool for inspecting the current context window contents.
ContextInspectTool(ToolDefinition def)
Construct from tool definition.
ServerResponse execute(const std::string &args_json) override
Return context window contents as a message array.
void set_provider(const entropic_state_provider_t *p)
Set state provider pointer.
MCPAccessLevel required_access_level() const override
Read-only tool requires only READ access.
Tool for delegating tasks to child inference loops.
ServerResponse execute(const std::string &args_json) override
Execute delegation.
DelegateTool(ToolDefinition def, const std::vector< std::string > &tier_names)
Construct and patch input schema with tier names.
Tool for full engine state snapshots.
ServerResponse execute(const std::string &args_json) override
Execute diagnostic snapshot.
void set_provider(const entropic_state_provider_t *p)
Set state provider pointer.
MCPAccessLevel required_access_level() const override
Read-only tool requires only READ access.
DiagnoseTool(ToolDefinition def)
Construct from tool definition.
void set_state_provider(const entropic_state_provider_t &provider)
Set the engine state provider for introspection tools.
~EntropicServer() override
Destructor.
EntropicServer(const std::vector< std::string > &tier_names, const std::string &data_dir)
Construct with tier names and data dir.
bool skip_duplicate_check(const std::string &tool_name) const override
delegate and pipeline skip duplicate check.
Recall prior delegation results via storage-backed search.
ServerResponse execute(const std::string &args_json) override
Execute a followup query.
MCPAccessLevel required_access_level() const override
Read-only — requires only READ access.
void set_provider(const entropic_state_provider_t *p)
Store provider pointer (non-owning).
FollowupTool(ToolDefinition def)
Construct from loaded tool definition.
Tool for targeted engine state queries.
ServerResponse execute(const std::string &args_json) override
Execute targeted inspection.
MCPAccessLevel required_access_level() const override
Read-only tool requires only READ access.
void set_provider(const entropic_state_provider_t *p)
Set state provider pointer.
InspectTool(ToolDefinition def)
Construct from tool definition.
Concrete base class for MCP servers (80% logic).
Definition server_base.h:66
void register_tool(ToolBase *tool)
Register a tool with this server.
Tool for switching inference phase.
PhaseChangeTool()
Construct with inline tool definition.
ServerResponse execute(const std::string &args_json) override
Execute phase change.
Tool for multi-stage delegation pipelines.
ServerResponse execute(const std::string &args_json) override
Execute pipeline setup.
PipelineTool(ToolDefinition def, const std::vector< std::string > &tier_names)
Construct and patch input schema with tier names.
Tool for pruning old messages from context.
PruneContextTool(ToolDefinition def)
Construct from tool definition.
ServerResponse execute(const std::string &args_json) override
Execute context pruning.
Resume a prior delegation with its conversation seeded.
ResumeDelegationTool(ToolDefinition def)
Construct from loaded tool definition.
ServerResponse execute(const std::string &args_json) override
Emit a resume-flavored delegate directive.
Tool for managing a persistent todo list.
std::string anchor_key(const std::string &args_json) const override
Anchor key for todo state replacement.
TodoTool(ToolDefinition def)
Construct from tool definition.
ServerResponse execute(const std::string &args_json) override
Execute todo action (add/update/remove).
Abstract base class for individual MCP tools.
Definition tool_base.h:45
ToolDefinition definition_
Tool definition.
Definition tool_base.h:102
Directive processing for tool-to-engine communication.
Entropic MCP server — engine-level tools including introspection.
@ ENTROPIC_DIRECTIVE_STOP_PROCESSING
Halt directive processing.
Definition enums.h:54
@ ENTROPIC_DIRECTIVE_PRUNE_MESSAGES
Prune old tool results.
Definition enums.h:61
@ ENTROPIC_DIRECTIVE_COMPLETE
Mark task complete.
Definition enums.h:58
@ ENTROPIC_DIRECTIVE_NOTIFY_PRESENTER
Generic UI notification passthrough.
Definition enums.h:64
@ ENTROPIC_DIRECTIVE_PHASE_CHANGE
Switch active inference phase.
Definition enums.h:63
@ ENTROPIC_DIRECTIVE_PIPELINE
Multi-stage sequential execution.
Definition enums.h:57
@ ENTROPIC_DIRECTIVE_CONTEXT_ANCHOR
Replace context anchor.
Definition enums.h:62
@ ENTROPIC_DIRECTIVE_DELEGATE
Route to another identity.
Definition enums.h:56
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 std::string call_provider(char *(*fn)(void *), void *ud)
Call a state provider callback and wrap result.
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:81
static std::string inspect_filterable(char *(*fn)(void *), void *ud, const std::string &key, const std::string &label)
Inspect a filterable target (config/identity/tool).
static std::vector< std::string > collect_object_keys(const nlohmann::json &j)
Collect keys from a JSON object into a vector.
static std::string filter_json_by_key(const std::string &json_str, const std::string &key, const std::string &label)
Filter a JSON value by key (object key or array name).
static std::string call_docs_provider(char *(*fn)(const char *, void *), const char *section, void *ud)
Call docs callback with section param.
static std::string call_history_provider(char *(*fn)(int, void *), int max_entries, void *ud)
Call history callback with max_entries param.
static bool dispatch_simple_target(const entropic_state_provider_t &p, const std::string &target, const std::string &key, std::string &result)
Dispatch inspect for simple (non-filterable) targets.
static nlohmann::json build_snapshot(const entropic_state_provider_t &p, bool include_docs, int history_limit)
Build the diagnose snapshot JSON.
MCPAccessLevel
MCP tool access level for per-identity authorization.
Definition config.h:38
@ READ
Read-only operations (e.g., read_file, list_directory)
static std::string dispatch_inspect(const entropic_state_provider_t &p, const std::string &target, const std::string &key)
Dispatch an inspect query to the appropriate provider.
static std::vector< std::string > collect_array_names(const nlohmann::json &j)
Collect "name" fields from a JSON array of objects.
static std::string list_available_keys(const nlohmann::json &j)
List available keys from a JSON value for error messages.
static bool dispatch_filterable_target(const entropic_state_provider_t &p, const std::string &target, const std::string &key, std::string &result)
Try filterable targets (config/identity/tool).
MCPServerBase concrete base class + ServerResponse.
Base directive — all directives carry a type tag.
Definition directives.h:36
entropic_directive_type_t type
Discriminant for dispatch.
Definition directives.h:37
Structured result from tool execution.
Definition server_base.h:33
Single todo entry.
std::string content
Item text.
std::string status
"pending", "in_progress", "done"
Parsed tool definition from JSON schema file.
Definition tool_base.h:27
std::string input_schema
JSON Schema for arguments (raw JSON string)
Definition tool_base.h:30
Read-only engine state provider for introspection tools.
char *(* get_tools)(void *user_data)
Get available tools as JSON array.
char *(* get_metrics)(void *user_data)
Get engine metrics as JSON.
void * user_data
Opaque user data passed to all callbacks.
char *(* search_delegations)(const char *query, int max_results, void *user_data)
Search prior delegation summaries (gh#32, v2.1.6).
char *(* get_identities)(void *user_data)
Get loaded identities as JSON array.
char *(* get_config)(void *user_data)
Get current engine configuration as JSON.
char *(* get_history)(int max_entries, void *user_data)
Get recent tool call history as JSON array.
char *(* get_docs)(const char *section, void *user_data)
Get bundled documentation as text.
char *(* get_state)(void *user_data)
Get engine state as JSON.
Abstract base class for individual MCP tools.