Entropic 2.14.3
Local-first agentic inference engine
Loading...
Searching...
No Matches
entropic_mcp.cpp File Reference

C API implementation for external MCP server management. More...

#include "engine_handle.h"
#include <entropic/entropic.h>
#include <entropic/config/loader.h>
#include <entropic/mcp/mcp_json_discovery.h>
#include <entropic/prompts/manager.h>
#include <entropic/types/logging.h>
#include "json_serializers.h"
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iterator>
#include <optional>
#include <memory>
#include <string>
#include <vector>
Include dependency graph for entropic_mcp.cpp:

Go to the source code of this file.

Functions

static entropic_error_t check_server_mgr (entropic_handle_t h)
 Check handle prerequisites for MCP server APIs.
 
static entropic::MCPConfig confined_mcp_config (entropic_handle_t h)
 The handle's MCP config, confined to a workspace root (gh#166).
 
static entropic::OutsideRootVerdict outside_root_thunk (const entropic::OutsideRootRequest &req, void *ud)
 FilesystemServer → consumer bridge for outside-root approval.
 
static bool discovered_server_thunk (const std::string &context_json, void *ud)
 Ask the handle's PRE_MCP_SERVER_CONNECT hooks about one server.
 
ENTROPIC_EXPORT entropic_error_t entropic_set_path_approval_callback (entropic_handle_t handle, ent_path_approval_cb cb, void *user_data)
 Register the outside-root path approver — see entropic.h.
 
static std::unique_ptr< EntropicWorkspace > build_workspace (entropic_handle_t h, const std::string &name, const std::filesystem::path &dir)
 Build a workspace's own server instances rooted at dir.
 
static entropic_error_t validate_workspace_args (entropic_handle_t handle, const char *name, const char *dir, std::filesystem::path &root)
 Validate workspace-create arguments and resolve the root.
 
static entropic_error_t load_workspace_context_file (entropic_handle_t handle, const std::filesystem::path &root, std::optional< std::string > &text)
 Read a new workspace's opt-in app_context file (gh#179).
 
ENTROPIC_EXPORT entropic_error_t entropic_workspace_create (entropic_handle_t handle, const char *name, const char *dir)
 Create a named workspace (gh#166) — see entropic.h.
 
ENTROPIC_EXPORT entropic_error_t entropic_session_bind_workspace (entropic_handle_t handle, const char *session_key, const char *name)
 Bind a session to a workspace (gh#166) — see entropic.h.
 
ENTROPIC_EXPORT entropic_error_t entropic_workspace_set_app_context (entropic_handle_t handle, const char *name, const char *app_context)
 Give a workspace its own app_context (gh#179) — see entropic.h .
 
static entropic::ExternalServerConfig parse_external_server_spec (const char *name, const nlohmann::json &j)
 Register an external MCP server at runtime.
 
static entropic::ServerManager * resolve_registration_target (entropic_handle_t handle, const std::string &ws_name, entropic::ExternalServerConfig &spec)
 Servers a registration targets, and the cwd it spawns in (gh#166).
 
ENTROPIC_EXPORT entropic_error_t entropic_register_mcp_server (entropic_handle_t handle, const char *name, const char *config_json)
 Register an external MCP server from JSON config (C ABI).
 
ENTROPIC_EXPORT entropic_error_t entropic_deregister_mcp_server (entropic_handle_t handle, const char *name)
 Deregister an external MCP server.
 
ENTROPIC_EXPORT char * entropic_list_mcp_servers (entropic_handle_t handle)
 List all MCP servers as JSON array.
 

Variables

static auto logger = entropic::log::get("facade.mcp")
 

Detailed Description

C API implementation for external MCP server management.

Implements entropic_register_mcp_server, entropic_deregister_mcp_server, and entropic_list_mcp_servers from entropic.h.

Version
2.0.0

Definition in file entropic_mcp.cpp.

Function Documentation

◆ build_workspace()

static std::unique_ptr< EntropicWorkspace > build_workspace ( entropic_handle_t  h,
const std::string &  name,
const std::filesystem::path &  dir 
)
static

Build a workspace's own server instances rooted at dir.

The built-in servers take their root at CONSTRUCTION and hold one working directory each, so a second repository needs a second set — this is the whole reason a workspace is an object and not a path. Plugins get the root through set_working_dir at load. Weights, tiers and identity are untouched: nothing here reloads a model.

Parameters
hEngine handle (configured).
nameWorkspace name.
dirAbsolute workspace root.
Returns
Owned workspace with connected servers. @utility @req REQ-MCP-027
Version
2.14.1

Definition at line 380 of file entropic_mcp.cpp.

◆ check_server_mgr()

static entropic_error_t check_server_mgr ( entropic_handle_t  h)
static

Check handle prerequisites for MCP server APIs.

Parameters
hEngine handle.
Returns
ENTROPIC_OK if valid, error code otherwise. @req REQ-API-005
Version
2.0.0

Definition at line 40 of file entropic_mcp.cpp.

◆ confined_mcp_config()

static entropic::MCPConfig confined_mcp_config ( entropic_handle_t  h)
static

The handle's MCP config, confined to a workspace root (gh#166).

mcp.filesystem.allow_outside_root is a SINGLE-PROJECT setting: one repository, the operator's own machine, "let the agent read /etc/os-release". Until v2.13.0 data/default_config.yaml shipped it TRUE; it is now optional (ask the host's path approver).

The moment one handle serves several repositories that setting stops meaning what the operator agreed to. "Outside my root" no longer means "somewhere on the disk"; it means "inside ANOTHER WORKSPACE" — and a workspace's whole contract (REQ-MCP-027) is that a bound session cannot reach the other repository's files. The v2.13.0 gh#166 model gate proved the gap end to end: both repositories held a PROJECT.md, the model asked for the sibling by absolute path, and the bound session's own server served it without a single "Path escape blocked".

So a workspace's servers are confined unconditionally: refuse, an EMPTY outside_root_allow, and no approver is ever installed on them (only the default set is wired, in init_mcp_servers). Neither an allow-list entry nor "just approve it" can reach a sibling workspace. outside_root_deny is kept — it can only narrow. The handle's DEFAULT set — every consumer that binds no workspace — is untouched and honours the host's setting verbatim.

Parameters
hEngine handle (configured).
Returns
A copy of h->config.mcp with root confinement forced on. @utility @req REQ-MCP-027 @req REQ-MCP-021
Version
2.13.0 [reviewed]

Definition at line 80 of file entropic_mcp.cpp.

◆ discovered_server_thunk()

static bool discovered_server_thunk ( const std::string &  context_json,
void *  ud 
)
static

Ask the handle's PRE_MCP_SERVER_CONNECT hooks about one server.

Fail-closed (gh#199): with no handler registered the answer is "no" — the inverse of every other pre-hook, and the reason this point exists.

Parameters
context_jsonServer description (see hooks.h).
udThe engine handle.
Returns
true to connect. @dg_internal
Version
2.14.0

Definition at line 158 of file entropic_mcp.cpp.

◆ entropic_deregister_mcp_server()

ENTROPIC_EXPORT entropic_error_t entropic_deregister_mcp_server ( entropic_handle_t  handle,
const char *  name 
)

Deregister an external MCP server.

Parameters
handleEngine handle returned by entropic_create.
nameMCP server name (must be unique).
Returns
ENTROPIC_OK or error code. @req REQ-MCP-025 @req REQ-API-005 @req REQ-ABI-002
Version
2.0.0

Definition at line 776 of file entropic_mcp.cpp.

◆ entropic_list_mcp_servers()

ENTROPIC_EXPORT char * entropic_list_mcp_servers ( entropic_handle_t  handle)

List all MCP servers as JSON array.

List all MCP servers with status information.

Parameters
handleEngine handle returned by entropic_create.
Returns
JSON string (caller frees), or NULL. @req REQ-MCP-025 @req REQ-API-008 @req REQ-API-005 @req REQ-ABI-002
Version
2.0.0

Definition at line 807 of file entropic_mcp.cpp.

◆ entropic_register_mcp_server()

ENTROPIC_EXPORT entropic_error_t entropic_register_mcp_server ( entropic_handle_t  handle,
const char *  name,
const char *  config_json 
)

Register an external MCP server from JSON config (C ABI).

Register an external MCP server at runtime.

Returns
ENTROPIC_OK on success; the check_server_mgr code for a bad handle/state, INVALID_ARGUMENT for NULL name/config_json, CONNECTION_FAILED when the transport connect throws. @req REQ-MCP-025 @req REQ-API-005 @req REQ-ABI-002
Version
2.13.0

Definition at line 730 of file entropic_mcp.cpp.

◆ entropic_session_bind_workspace()

ENTROPIC_EXPORT entropic_error_t entropic_session_bind_workspace ( entropic_handle_t  handle,
const char *  session_key,
const char *  name 
)

Bind a session to a workspace (gh#166) — see entropic.h.

Bind a session to a workspace (gh#166).

Parameters
handleEngine handle.
session_keySession key (NULL or "" = default session).
nameWorkspace name.
Returns
ENTROPIC_OK, or a typed argument/state error. @req REQ-API-005 @req REQ-MCP-027 @req REQ-ABI-002
Version
2.14.0

Definition at line 549 of file entropic_mcp.cpp.

◆ entropic_set_path_approval_callback()

ENTROPIC_EXPORT entropic_error_t entropic_set_path_approval_callback ( entropic_handle_t  handle,
ent_path_approval_cb  cb,
void *  user_data 
)

Register the outside-root path approver — see entropic.h.

Register the approver for filesystem access outside the root.

Only the slot changes: the default set's filesystem server already holds the thunk (wired at configure), which reads the slot per call. Workspaces are never wired, so this can never reach them.

Parameters
handleEngine handle.
cbApprover (NULL clears).
user_dataForwarded to cb.
Returns
ENTROPIC_OK, or ENTROPIC_ERROR_INVALID_HANDLE. @req REQ-MCP-021 @req REQ-API-010 @req REQ-API-005
Version
2.13.0

Definition at line 348 of file entropic_mcp.cpp.

◆ entropic_workspace_create()

ENTROPIC_EXPORT entropic_error_t entropic_workspace_create ( entropic_handle_t  handle,
const char *  name,
const char *  dir 
)

Create a named workspace (gh#166) — see entropic.h.

Create a named workspace rooted at a directory (gh#166).

Parameters
handleEngine handle.
nameWorkspace name.
dirWorkspace root directory.
Returns
ENTROPIC_OK, or a typed argument/state error. @req REQ-API-005 @req REQ-MCP-027 @req REQ-ABI-002
Version
2.14.0

Definition at line 508 of file entropic_mcp.cpp.

◆ entropic_workspace_set_app_context()

ENTROPIC_EXPORT entropic_error_t entropic_workspace_set_app_context ( entropic_handle_t  handle,
const char *  name,
const char *  app_context 
)

Give a workspace its own app_context (gh#179) — see entropic.h .

Give a workspace its own app_context (gh#179).

Parameters
handleEngine handle.
nameWorkspace name.
app_contextText; "" = none; NULL = the handle's.
Returns
ENTROPIC_OK, or a typed argument/state error. @req REQ-API-005 @req REQ-MCP-027 @req REQ-ABI-002
Version
2.14.0

Definition at line 595 of file entropic_mcp.cpp.

◆ load_workspace_context_file()

static entropic_error_t load_workspace_context_file ( entropic_handle_t  handle,
const std::filesystem::path &  root,
std::optional< std::string > &  text 
)
static

Read a new workspace's opt-in app_context file (gh#179).

workspace_app_context unset, or the file absent from this root: no override — the workspace's sessions keep the handle's app_context. The file is repository-supplied text going into a system prompt, so it must resolve INSIDE the root: a symlink out of the repository is refused rather than followed.

Parameters
handleEngine handle.
rootWorkspace root (absolute).
[out]textThe file's text, when one was loaded.
Returns
ENTROPIC_OK, ENTROPIC_ERROR_IO (unreadable) or ENTROPIC_ERROR_INVALID_ARGUMENT (escapes the root), with last_error set. @dg_internal
Version
2.14.0

Definition at line 469 of file entropic_mcp.cpp.

◆ outside_root_thunk()

static entropic::OutsideRootVerdict outside_root_thunk ( const entropic::OutsideRootRequest &  req,
void *  ud 
)
static

FilesystemServer → consumer bridge for outside-root approval.

Reads the handle's slot per call (so a later registration takes effect with no server rewiring), copies it under the slot mutex, and calls the consumer OUTSIDE the lock — the consumer may block on a person.

Parameters
reqThe pending access.
udEngine handle.
Returns
The consumer's verdict, or no_approver when the slot is empty. @callback @req REQ-MCP-021
Version
2.13.0

Definition at line 109 of file entropic_mcp.cpp.

◆ parse_external_server_spec()

static entropic::ExternalServerConfig parse_external_server_spec ( const char *  name,
const nlohmann::json &  j 
)
static

Register an external MCP server at runtime.

Issue #9 (v2.1.4): parses the FULL ExternalServerConfig from config_json — including env and explicit transport. Pre-2.1.4 the runtime path silently dropped env, leaving spawned children with an empty environment (not even PATH). Env keys are filtered through the same blocklist used by .mcp.json discovery (is_blocked_env_var) so PATH/LD_PRELOAD/etc. cannot be injected.

Recognized config_json fields:

  • "command" (string) — stdio executable
  • "args" (string[]) — stdio arguments
  • "env" (object) — stdio environment (block-filtered)
  • "url" (string) — SSE endpoint (mutually exclusive)
  • "transport" (string) — "stdio" | "sse"; auto-inferred if absent
Parameters
handleEngine handle returned by entropic_create.
nameMCP server name (must be unique).
config_jsonJSON-serialized MCP server configuration.
Returns
ENTROPIC_OK or error code. @dg_internal
Version
2.1.4

Parse an ExternalServerConfig from register-mcp JSON.

Extracted from entropic_register_mcp_server to keep it knots-clean. Env vars on the block list are skipped with a warning.

Parameters
nameServer name.
jParsed config JSON.
Returns
Populated ExternalServerConfig. @utility
Version
2.3.7

Definition at line 665 of file entropic_mcp.cpp.

◆ resolve_registration_target()

static entropic::ServerManager * resolve_registration_target ( entropic_handle_t  handle,
const std::string &  ws_name,
entropic::ExternalServerConfig &  spec 
)
static

Servers a registration targets, and the cwd it spawns in (gh#166).

A "workspace" field registers the server on that workspace's set and spawns it with cwd = the workspace root — which is what a repo-scoped server like clew-mcp --repo . actually needs when one handle serves several repositories.

Parameters
handleEngine handle.
ws_nameWorkspace name ("" = the handle's default set).
[in,out]specSpec whose working_dir is filled for a workspace.
Returns
Target manager, or nullptr when the workspace is unknown. @utility @req REQ-MCP-027
Version
2.13.0

Definition at line 707 of file entropic_mcp.cpp.

◆ validate_workspace_args()

static entropic_error_t validate_workspace_args ( entropic_handle_t  handle,
const char *  name,
const char *  dir,
std::filesystem::path &  root 
)
static

Validate workspace-create arguments and resolve the root.

Extracted to keep entropic_workspace_create inside the knots returns gate; sets handle->last_error with the specific reason.

Parameters
handleEngine handle.
nameRequested workspace name.
dirRequested root directory.
[out]rootAbsolute, validated root on success.
Returns
ENTROPIC_OK, or ENTROPIC_ERROR_INVALID_ARGUMENT with a reason. @utility @req REQ-MCP-027
Version
2.13.0

Definition at line 426 of file entropic_mcp.cpp.

Variable Documentation

◆ logger

auto logger = entropic::log::get("facade.mcp")
static

Definition at line 31 of file entropic_mcp.cpp.