19#include <nlohmann/json.hpp>
31#include <unordered_map>
32#include <unordered_set>
34#include <sys/socket.h>
39using json = nlohmann::json;
51static std::mutex s_bound_sockets_mu;
52static std::unordered_set<std::string> s_bound_sockets;
56 const std::filesystem::path& project_dir);
68static std::string
rpc_ok(
const json&
id,
const json& result) {
69 json r = {{
"jsonrpc",
"2.0"}, {
"id",
id}, {
"result", result}};
82static std::string
rpc_err(
const json&
id,
int code,
83 const std::string& msg) {
84 json r = {{
"jsonrpc",
"2.0"}, {
"id",
id},
85 {
"error", {{
"code", code}, {
"message", msg}}}};
97 return {{
"content", json::array({{{
"type",
"text"}, {
"text", text}}})}};
110 {{
"name",
"entropic.ask"},
112 "Submit a prompt to the running entropic engine. "
113 "Set async=true to return immediately with a task_id; "
114 "the engine pushes a notification when done."},
118 {
"prompt", {{
"type",
"string"},
119 {
"description",
"User message"}}},
120 {
"async", {{
"type",
"boolean"},
121 {
"description",
"Run asynchronously"},
124 {
"required", json::array({
"prompt"})}
126 {{
"name",
"entropic.ask_status"},
128 "Check status of an async entropic.ask task."},
131 {
"properties", {{
"task_id", {
133 {
"description",
"Task ID from async ask"}
135 {
"required", json::array({
"task_id"})}
137 {{
"name",
"entropic.status"},
138 {
"description",
"Engine version and message count."},
139 {
"inputSchema", {{
"type",
"object"},
140 {
"properties", json::object()}}}},
141 {{
"name",
"entropic.context_clear"},
142 {
"description",
"Clear conversation history."},
143 {
"inputSchema", {{
"type",
"object"},
144 {
"properties", json::object()}}}},
145 {{
"name",
"entropic.context_count"},
146 {
"description",
"Return the message count."},
147 {
"inputSchema", {{
"type",
"object"},
148 {
"properties", json::object()}}}},
168 auto s = msg.dump() +
"\n";
169 ::write(fd, s.c_str(), s.size());
181 const std::string& progress_token) {
184 {
"method",
"notifications/progress"},
186 {
"progressToken", progress_token},
187 {
"progress", token_text}
206 char* msgs_json =
nullptr;
208 auto answer = facade_text::final_text_or_reason(msgs_json);
229 auto it = args.find(
"prompt");
230 if (it == args.end() || !it->is_string()) {
231 return tool_text(
"error: missing 'prompt' argument");
233 char* result_json =
nullptr;
234 auto err =
entropic_run(handle, it->get<std::string>().c_str(),
239 return tool_text(std::string(
"error: ") + (msg ? msg :
"unknown"));
242 auto answer = facade_text::final_text_or_reason(result_json);
265 int client_fd,
const std::string& call_id) {
266 auto it = args.find(
"prompt");
267 if (it == args.end() || !it->is_string()) {
268 return tool_text(
"error: missing 'prompt' argument");
270 std::string prompt = it->get<std::string>();
273 struct StreamCtx {
int fd; std::string token_id; };
274 StreamCtx sctx{client_fd, call_id};
275 auto on_token = [](
const char* tok,
size_t len,
void* ud) {
276 auto* ctx =
static_cast<StreamCtx*
>(ud);
277 send_progress(ctx->fd, std::string(tok, len), ctx->token_id);
280 handle, prompt.c_str(), on_token, &sctx,
nullptr);
283 return tool_text(std::string(
"error: ") + (msg ? msg :
"unknown"));
299 std::ostringstream os;
301 <<
"\nmessages: " <<
count;
303 char* mjson =
nullptr;
305 && mjson !=
nullptr) {
306 os <<
"\nmetrics: " << mjson;
369 s.
text = msg ? msg :
"cancelled";
371 s.
phase =
"cancelled";
374 s.
text = msg ? msg :
"unknown error";
382 s.
text = facade_text::final_text_or_reason(result_json);
401 if (task.status ==
"queued" || task.status ==
"running") {
402 task.status =
"cancelled";
403 task.phase =
"cancelling";
419 if (task.phase ==
"cancelling") {
return true; }
441 if (bridge ==
nullptr) {
return; }
445 std::this_thread::sleep_for(std::chrono::milliseconds(50));
469 return tool_text(
"conversation cleared");
499 auto tid = args.value(
"task_id", std::string{});
501 auto it = tasks_.find(tid);
502 if (it == tasks_.end()) {
503 return tool_text(
"error: unknown task_id");
505 json status = {{
"status", it->second.status},
506 {
"phase", it->second.phase}};
507 if (!it->second.result.empty()) {
508 auto key = (it->second.status ==
"error") ?
"error" :
"result";
509 status[key] = it->second.result;
523 static std::atomic<uint64_t> counter{0};
524 auto n = counter.fetch_add(1);
525 auto t = std::chrono::steady_clock::now().time_since_epoch().count();
526 std::ostringstream ss;
527 ss << std::hex << (t ^ (n * 2654435761ULL));
528 return "task-" + ss.str();
548 const std::string& call_id) {
549 if (args.value(
"async",
false)) {
552 args.value(
"prompt",
""), task_id, client_fd);
553 return tool_text(
"async task started: " + task_id);
556 return handle_ask(handle, args, client_fd, call_id);
579 const std::string& call_id) {
580 std::string name = params.value(
"name", std::string{});
581 json args = params.value(
"arguments", json::object());
582 if (name ==
"entropic.ask") {
583 return dispatch_ask(handle, bridge, args, client_fd, call_id);
587 else if (name ==
"entropic.status") { result =
handle_status(handle); }
588 else if (name ==
"entropic.context_clear") { result =
handle_clear(handle, bridge); }
589 else if (name ==
"entropic.context_count") { result =
handle_count(handle); }
590 else { result =
tool_text(
"error: unknown tool '" + name +
"'"); }
607 const std::filesystem::path& project_dir)
608 : handle_(handle), config_(config) {
636 std::filesystem::create_directories(parent, ec);
637 ::chmod(parent.c_str(), S_IRWXU);
655 if (::lstat(path.c_str(), &st) != 0) {
656 return errno == ENOENT;
658 bool is_symlink = S_ISLNK(st.st_mode);
659 bool is_socket = S_ISSOCK(st.st_mode);
660 if (!is_socket || is_symlink) {
662 "Refusing to bind: {} is a {} (expected unix socket)",
664 is_symlink ?
"symlink" :
"non-socket file");
666 return is_socket && !is_symlink;
678 auto s = path.string();
679 if (s.size() >=
sizeof(sockaddr_un::sun_path)) {
return false; }
680 struct sockaddr_un addr{};
681 addr.sun_family = AF_UNIX;
682 std::strncpy(addr.sun_path, s.c_str(),
683 sizeof(addr.sun_path) - 1);
684 if (bind(fd,
reinterpret_cast<sockaddr*
>(&addr),
sizeof(addr)) != 0) {
687 ::chmod(s.c_str(), S_IRUSR | S_IWUSR);
688 return listen(fd, 1) == 0;
709 std::filesystem::remove(path);
711 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
714 logger->error(
"Socket setup failed for {}: {}",
715 path.string(), std::strerror(errno));
716 if (fd >= 0) { ::close(fd); }
720 "External MCP bridge ready: project_dir(canonical)={} socket={}",
721 path.parent_path().parent_path().string(), path.string());
741 socklen_t len =
sizeof(cred);
742 if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED,
744 logger->warn(
"SO_PEERCRED failed on fd={}: {}",
745 client_fd, std::strerror(errno));
748 if (cred.uid != ::geteuid()) {
750 "Rejecting MCP client on fd={}: peer uid {} != engine uid {}",
751 client_fd, cred.uid, ::geteuid());
774 std::filesystem::weakly_canonical(socket_path_, ec).string();
775 if (ec) { canonical = socket_path_.string(); }
777 std::lock_guard lk(s_bound_sockets_mu);
778 if (!s_bound_sockets.insert(canonical).second) {
780 "External MCP bridge: socket {} already bound by another "
781 "handle in this process; declining to start. Set "
782 "external.socket_path to a distinct path per handle "
783 "if you need per-handle bridges.",
790 if (listen_fd_ < 0) {
791 std::lock_guard lk(s_bound_sockets_mu);
792 s_bound_sockets.erase(canonical);
795 bound_canonical_ = canonical;
797 running_.store(
true);
802 int log_id = handle_ ? handle_->
log_id : 0;
803 accept_thread_ = std::thread([
this, log_id]() {
808 logger->info(
"External MCP bridge listening on {}",
809 socket_path_.string());
834 running_.store(
false);
835 if (listen_fd_ >= 0) {
839 if (accept_thread_.joinable()) {
840 accept_thread_.join();
847 std::vector<std::unique_ptr<ClientThread>> drained;
849 std::lock_guard<std::mutex> lock(client_threads_mutex_);
850 drained = std::move(client_threads_);
851 client_threads_.clear();
853 for (
auto& ct : drained) {
854 if (ct->fd >= 0) { ::shutdown(ct->fd, SHUT_RDWR); }
856 for (
auto& ct : drained) {
857 if (ct->thread.joinable()) { ct->thread.join(); }
862 std::filesystem::remove(socket_path_, ec);
868 if (!bound_canonical_.empty()) {
869 std::lock_guard lk(s_bound_sockets_mu);
870 s_bound_sockets.erase(bound_canonical_);
871 bound_canonical_.clear();
887void ExternalBridge::reap_finished_clients_locked() {
888 auto it = client_threads_.begin();
889 while (it != client_threads_.end()) {
890 if ((*it)->finished.load()) {
891 if ((*it)->thread.joinable()) { (*it)->thread.join(); }
892 it = client_threads_.erase(it);
924void ExternalBridge::accept_loop() {
925 while (running_.load()) {
930 int rc = poll(&pfd, 1, 500);
931 if (rc <= 0) {
continue; }
933 int client_fd = accept(listen_fd_,
nullptr,
nullptr);
934 if (client_fd < 0) {
continue; }
941 logger->info(
"External MCP client connected (fd={})", client_fd);
943 auto ct = std::make_unique<ClientThread>();
947 ClientThread* raw = ct.get();
951 int log_id = handle_ ? handle_->
log_id : 0;
952 ct->thread = std::thread([
this, raw, log_id]() {
954 serve_client(raw->fd);
957 logger->info(
"External MCP client disconnected");
958 raw->finished.store(
true);
961 std::lock_guard<std::mutex> lock(client_threads_mutex_);
962 client_threads_.push_back(std::move(ct));
963 reap_finished_clients_locked();
978 ssize_t n = read(fd, &c, 1);
979 if (n <= 0) {
return {}; }
980 if (c ==
'\n') {
return line; }
995void ExternalBridge::serve_client(
int client_fd) {
998 ExternalBridge* self;
1000 ~Unsub() { self->unsubscribe(fd); }
1001 } guard{
this, client_fd};
1003 while (running_.load()) {
1005 if (line.empty()) {
break; }
1007 auto response = dispatch(line, client_fd);
1008 if (response.empty()) {
continue; }
1011 ssize_t written = write(client_fd, response.c_str(),
1013 if (written < 0) {
break; }
1025 {
"protocolVersion",
"2025-06-18"},
1026 {
"serverInfo", {{
"name",
"entropic"},
1028 {
"capabilities", {{
"tools", json::object()}}}
1045std::string ExternalBridge::dispatch(
1046 const std::string&
request,
int client_fd) {
1047 auto req = json::parse(
request,
nullptr,
false);
1049 if (req.is_discarded() || !req.contains(
"id")) {
1050 return req.is_discarded()
1051 ?
rpc_err(
nullptr, -32700,
"Parse error")
1055 json
id = req[
"id"];
1056 std::string method = req.value(
"method", std::string{});
1057 json params = req.value(
"params", json::object());
1061 else if (method ==
"tools/list") { result = {{
"tools",
tool_definitions()}}; }
1062 else if (method ==
"tools/call") {
1063 auto id_str =
id.is_string() ?
id.get<std::string>()
1065 result =
dispatch_tool(handle_,
this, params, client_fd, id_str);
1067 else if (method ==
"shutdown" || method ==
"exit") { result = json::object(); }
1068 else {
return rpc_err(
id, -32601,
"Unknown method: " + method); }
1069 return rpc_ok(
id, result);
1082 std::lock_guard<std::mutex> lock(self->tasks_mutex_);
1085 if (self->observer_call_is_stale()) {
return; }
1086 auto it = self->tasks_for_cancel().find(self->active_task_id_for_observer());
1087 if (it == self->tasks_for_cancel().end()) {
return; }
1090 it->second.phase = (it->second.phase ==
"validating"
1091 || it->second.phase ==
"revising")
1092 ?
"revising" :
"validating";
1109 active_task_id_ = task_id;
1110 attached_gen_ = ++observer_gen_;
1131 active_task_id_.clear();
1150 const std::string& prompt,
1151 const std::string& task_id,
1158 t.
created = std::chrono::steady_clock::now();
1159 tasks_[task_id] = std::move(t);
1164 int log_id = handle_ ? handle_->
log_id : 0;
1165 std::thread([
this, prompt, task_id, client_fd, log_id]() {
1170 char* result_json =
nullptr;
1171 auto err =
entropic_run(handle_, prompt.c_str(), &result_json);
1176 handle_, err, result_json);
1180 auto it = tasks_.find(task_id);
1181 if (it != tasks_.end()) {
1182 it->second.status = final_state.status;
1183 it->second.phase = final_state.phase;
1184 it->second.result = final_state.text;
1192 auto status = final_state.status;
1217 {
"method",
"notifications/progress"},
1219 {
"progressToken", task_id},
1228 logger->info(
"Async task {} completed: {}", task_id, status);
1244 std::lock_guard<std::mutex> lock(subscribers_mutex_);
1245 subscribers_.insert(fd);
1255 std::lock_guard<std::mutex> lock(subscribers_mutex_);
1256 subscribers_.erase(fd);
1293 auto payload = notif.dump() +
"\n";
1295 std::vector<int> snapshot;
1297 std::lock_guard<std::mutex> lock(subscribers_mutex_);
1298 snapshot.assign(subscribers_.begin(), subscribers_.end());
1301 std::vector<int> dead;
1302 for (
int fd : snapshot) {
1303 ssize_t rc = ::send(fd, payload.c_str(), payload.size(),
1304 MSG_DONTWAIT | MSG_NOSIGNAL);
1310 logger->warn(
"Subscriber fd {} send failed (errno={}) — dropping",
1313 }
else if (
static_cast<size_t>(rc) < payload.size()) {
1314 logger->warn(
"Subscriber fd {} partial send ({}/{}) — dropping",
1315 fd, rc, payload.size());
1320 if (!dead.empty()) {
1321 std::lock_guard<std::mutex> lock(subscribers_mutex_);
1322 for (
int fd : dead) { subscribers_.erase(fd); }
1335 const std::string& status,
1336 const std::string& phase) {
1338 auto it = tasks_.find(task_id);
1339 if (it == tasks_.end()) {
return; }
1340 it->second.status = status;
1341 it->second.phase = phase;
1357 auto cutoff = std::chrono::steady_clock::now()
1358 - std::chrono::minutes(15);
1361 for (
auto it = tasks_.begin(); it != tasks_.end(); ) {
1362 if (it->second.created < cutoff) {
1363 if (!sentinel_dir.empty()) {
1364 for (
const char* suffix :
1365 {
".done",
".failed",
".cancelled"}) {
1367 std::filesystem::remove(
1368 sentinel_dir / (it->first + suffix), ec);
1371 it = tasks_.erase(it);
1391 std::filesystem::path root = async_sentinel_root_override_;
1392 if (root.empty() && handle_ !=
nullptr) {
1395 return root.empty() ? std::filesystem::path{} : (root /
"async");
1404 const std::filesystem::path& root) {
1405 async_sentinel_root_override_ = root;
1414 const std::string& status) {
1415 const char* suffix =
".done";
1416 if (status ==
"error") {
1418 }
else if (status ==
"cancelled") {
1419 suffix =
".cancelled";
1440 const std::string& status) {
1442 if (dir.empty()) {
return; }
1444 std::filesystem::create_directories(dir, ec);
1446 logger->warn(
"write_sentinel: mkdir {} failed: {}",
1447 dir.string(), ec.message());
1451 std::ofstream out(path);
1452 if (!out.is_open()) {
1453 logger->warn(
"write_sentinel: open {} failed", path.string());
1456 out << status <<
'\n';
Unix socket MCP bridge for external client access.
bool start()
Start the background accept loop.
void cleanup_expired_tasks()
Remove tasks older than TTL from the registry.
void stop()
Stop the accept loop and close the socket.
void unsubscribe(int fd)
Remove an fd from the subscriber set.
std::mutex tasks_mutex_
Async task mutex (public for dispatch_tool access).
~ExternalBridge()
Destructor — stop if running.
void detach_phase_observer()
Clear the phase observer installed by attach_phase_observer.
nlohmann::json handle_ask_status(const nlohmann::json &args)
Handle entropic.ask_status — check async task state.
std::filesystem::path async_sentinel_dir() const
Sentinel directory (lazy: returns empty path until the engine's log_dir is configured).
ExternalBridge(entropic_handle_t handle, const ExternalMCPConfig &config, const std::filesystem::path &project_dir)
Construct with engine handle and config.
void run_async_ask(const std::string &prompt, const std::string &task_id, int client_fd)
Run an async entropic.ask in a detached background thread.
bool ask_streaming() const
Whether entropic.ask routes through entropic_run_streaming.
void broadcast_notification(const nlohmann::json ¬if)
Write a JSON-RPC notification to every subscribed fd.
void set_async_sentinel_root(const std::filesystem::path &root)
Override the async sentinel root directory.
void attach_phase_observer(const std::string &task_id)
Run an async entropic.ask in a background thread.
void write_sentinel(const std::string &task_id, const std::string &status)
Write the sentinel file for an async task completion.
void subscribe(int fd)
Add a connected fd to the subscriber set.
void update_task_phase(const std::string &task_id, const std::string &status, const std::string &phase)
Update status/phase for a tracked task atomically.
std::unordered_map< std::string, AsyncTask > & tasks_for_cancel()
Mutable accessor to the task registry.
gh#59 (v2.3.1): RAII guard — sets thread's current handle_id.
Private definition of the entropic_engine struct.
Public C API for the Entropic inference engine.
ENTROPIC_EXPORT entropic_error_t entropic_set_state_observer(entropic_handle_t handle, void(*observer)(int state, void *user_data), void *user_data)
Register an engine state-change observer.
ENTROPIC_EXPORT entropic_error_t entropic_context_count(entropic_handle_t handle, size_t *count)
Get the number of messages in the conversation.
ENTROPIC_EXPORT entropic_error_t entropic_context_clear(entropic_handle_t handle)
Clear conversation history, starting a new session.
ENTROPIC_EXPORT entropic_error_t entropic_run(entropic_handle_t handle, const char *input, char **result_json)
Synchronous agentic loop.
ENTROPIC_EXPORT entropic_error_t entropic_metrics_json(entropic_handle_t handle, char **out)
Get loop metrics from the most recent run as JSON.
ENTROPIC_EXPORT const char * entropic_version(void)
Get the library version string.
ENTROPIC_EXPORT entropic_error_t entropic_interrupt(entropic_handle_t handle)
Interrupt a running generation.
ENTROPIC_EXPORT void entropic_free(void *ptr)
Free memory allocated by the engine or entropic_alloc().
ENTROPIC_EXPORT entropic_error_t entropic_context_get(entropic_handle_t handle, char **messages_json)
Get the current conversation history as a JSON array.
ENTROPIC_EXPORT entropic_error_t entropic_run_streaming(entropic_handle_t handle, const char *input, void(*on_token)(const char *token, size_t len, void *user_data), void *user_data, int *cancel_flag)
Streaming agentic loop with token callback.
@ ENTROPIC_AGENT_STATE_VERIFYING
Post-generation verification.
entropic_error_t
Error codes returned by all C API functions.
@ ENTROPIC_ERROR_CANCELLED
Operation cancelled via cancel token.
@ ENTROPIC_ERROR_INTERRUPTED
Operation interrupted via entropic_interrupt (v1.8.9)
ENTROPIC_EXPORT const char * entropic_last_error(entropic_handle_t handle)
Get the last error message for a handle.
Unix socket MCP bridge — exposes a running engine to external clients.
Operator-visible final-text extraction for the external bridge.
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 std::string generate_task_id()
Generate a simple UUID-like task ID.
static bool any_cancelling_left(ExternalBridge *bridge)
True while any task is still phase=cancelling.
static json handle_ask(entropic_handle_t handle, const json &args, int client_fd, const std::string &call_id)
Handle entropic.ask — stream tokens then return final text.
std::filesystem::path compute_socket_path(const std::filesystem::path &project_dir)
Compute project-unique Unix socket path for self-detection.
static const char * sentinel_suffix_for_status(const std::string &status)
Map a terminal status string to a sentinel filename suffix.
static int create_listen_socket(const std::filesystem::path &path)
Create, bind, and listen on a unix domain socket.
@ ok
Tool dispatched, returned non-empty content.
static void phase_observer_cb(int state, void *ud)
State observer that projects VERIFYING onto task phase.
static bool socket_path_safe(const std::filesystem::path &path)
Reject a pre-existing path that is a symlink or non-socket.
static json handle_count(entropic_handle_t handle)
Handle entropic.context_count.
static bool peer_uid_matches(int client_fd)
Validate that the connecting peer shares the engine's UID.
static json tool_text(const std::string &text)
Wrap text in MCP tool result shape.
static bool mark_tasks_cancelling(ExternalBridge *bridge)
Mark every queued/running task as cancelling.
@ request
GenerationParams::grammar (COMMON_GRAMMAR_TYPE_USER)
@ count
Sentinel — MUST remain last.
static void send_progress(int fd, const std::string &token_text, const std::string &progress_token)
Send an MCP progress notification with a text token.
static bool bind_and_listen(int fd, const std::filesystem::path &path)
Bind+listen on an AF_UNIX socket, applying 0600 perms.
static json final_answer_from_context(entropic_handle_t handle)
Read the live conversation and render the operator-visible answer.
static json handle_clear(entropic_handle_t handle, ExternalBridge *bridge)
entropic.context_clear MCP tool handler.
static void cancel_inflight_async_tasks(entropic_handle_t handle, ExternalBridge *bridge)
Cancel any async tasks currently running on the bridge.
static AsyncFinalState derive_async_final_state(entropic_handle_t handle, entropic_error_t err, char *result_json)
Translate entropic_run's return code into a final task state.
static json tool_definitions()
MCP tool definitions exposed by the bridge.
static void write_json_line(int fd, const json &msg)
Write a JSON-RPC line to a socket fd.
static json handle_ask_plain(entropic_handle_t handle, const json &args)
Handle entropic.ask — non-streaming path (gh#115, v2.9.12).
static std::string rpc_err(const json &id, int code, const std::string &msg)
Build a JSON-RPC error response.
static json initialize_result()
Build the MCP initialize response payload.
static json dispatch_tool(entropic_handle_t handle, ExternalBridge *bridge, const json ¶ms, int client_fd, const std::string &call_id)
Dispatch a tools/call to the appropriate handler.
static std::string read_line(int fd)
Read one newline-delimited line from a socket fd.
static json handle_status(entropic_handle_t handle)
Handle entropic.status.
static json dispatch_ask(entropic_handle_t handle, ExternalBridge *bridge, const json &args, int client_fd, const std::string &call_id)
Route entropic.ask — sync (streaming) or async.
static std::string rpc_ok(const json &id, const json &result)
Build a JSON-RPC success response.
static void prepare_socket_dir(const std::filesystem::path &parent)
Prepare the socket containing directory with 0700 perms.
Handle entropic.context_clear.
std::string text
result or error message
std::string status
done | error | cancelled
std::string phase
done | failed | cancelled
Async task state for background entropic.ask runs.
std::string phase
queued, running, running:<tier>, done, failed, cancelled (P1-5)
std::chrono::steady_clock::time_point created
For TTL cleanup.
std::string status
queued | running | done | error | cancelled (2.0.6-rc16)
External MCP server configuration (Entropic-as-server).
std::optional< std::filesystem::path > socket_path
Socket path (nullopt = derived)
std::filesystem::path log_dir
Session log directory (session.log + session_model.log).
Engine handle struct — owns all subsystems.
int log_id
gh#59 (v2.3.1): unique handle id for per-handle log routing via entropic::log::HandleAwareSink.
entropic::ParsedConfig config
Parsed config.