How MCP Server Integration Handles Security and Isolation in Craft Agents
Craft Agents implements a defense-in-depth strategy for MCP server integration that combines configuration validation, workspace-level guards, credential isolation, and runtime permission policies to securely connect external tools while preventing unauthorized subprocess execution and data leakage.
The MCP (Modular Compute Protocol) server integration in the lukilabs/craft-agents-oss repository is architected as a two-layer security system. The first layer validates and sanitizes server configurations before any network connection or subprocess is created, while the second layer enforces runtime isolation through workspace policies and permission-based access controls. This approach ensures that MCP server integration remains both flexible for developers and secure for enterprise deployments.
Defense-in-Depth Architecture for MCP Security
The security model separates configuration building from execution. All MCP server definitions flow through SourceServerBuilder in packages/shared/src/sources/server-builder.ts, which constructs a validated, credential-scrubbed configuration object. Only after this validation phase does the system determine whether to initiate an HTTP/SSE connection or spawn a local stdio subprocess, with the latter gated by strict workspace-level permissions.
Building Secure MCP Server Configurations with SourceServerBuilder
The SourceServerBuilder class serves as the single entry point for transforming raw source definitions into executable server configurations. It enforces type safety, validates transport mechanisms, and implements a hierarchical credential merging strategy that prevents secret leakage.
Validating Source Types and Transport Methods
The buildMcpServer method (lines 85-101) immediately filters out non-MCP sources and validates transport-specific requirements:
// packages/shared/src/sources/server-builder.ts
// https://github.com/lukilabs/craft-agents-oss/blob/main/packages/shared/src/sources/server-builder.ts#L85-L101
if (source.config.type !== 'mcp' || !source.config.mcp) {
return null;
}
if (mcp.transport === 'stdio') {
// Requires explicit command and workspace permission
}
if (!mcp.url) {
// Remote endpoints must specify a valid URL
}
For stdio transport, the builder requires both a valid mcp.command and explicit workspace permission. For HTTP/SSE transport, it normalizes URLs and prepares header configurations without executing any network requests.
Header Merging and Credential Priority
The system implements a strict precedence for authentication headers to ensure secrets always override static values:
- Static headers defined in the source configuration
- Credential-store headers retrieved from the encrypted credential manager
- OAuth bearer tokens injected at request time
This merging order guarantees that OAuth tokens take highest precedence, preventing accidental credential leakage through static configuration files.
Handling Missing Authentication
When a source claims authentication but lacks valid tokens, the builder logs a debug message using SERVER_BUILD_ERRORS constants and returns null, effectively excluding the source from the session:
// If authenticated but no token available, omit the source
debug(SERVER_BUILD_ERRORS.MISSING_CREDENTIALS, source.name);
return null;
Workspace-Level Guards for Local MCP Server Isolation
Local stdio MCP servers execute as separate OS processes, creating potential security boundaries. Craft Agents controls this capability through the isLocalMcpEnabled helper in packages/shared/src/workspaces/storage.ts.
The Resolution Chain for Local MCP Permissions
The guard evaluates three levels of configuration in strict priority order:
// packages/shared/src/workspaces/storage.ts
// https://github.com/lukilabs/craft-agents-oss/blob/main/packages/shared/src/workspaces/storage.ts#L79-L94
export function isLocalMcpEnabled(rootPath: string): boolean {
const envValue = process.env.CRAFT_LOCAL_MCP_ENABLED;
if (envValue !== undefined) {
return envValue.toLowerCase() === 'true';
}
const config = loadWorkspaceConfig(rootPath);
if (config?.localMcpServers?.enabled !== undefined) {
return config.localMcpServers.enabled;
}
return true;
}
- Environment variable
CRAFT_LOCAL_MCP_ENABLED– enables enterprise-wide lockdowns - Workspace configuration (
localMcpServers.enabled) – allows per-project policies - Default fallback – enabled (
true) for backward compatibility
When this guard returns false, SourceServerBuilder never creates a stdio configuration, preventing any local subprocess execution.
Runtime Isolation and Permission Policies
The execution layer separates remote HTTP/SSE connections from local subprocesses and applies additional sandboxing through the Claude Agent SDK.
HTTP/SSE vs. Local Stdio Subprocesses
- Remote HTTP/SSE endpoints operate within the network stack isolation, sending only the pre-validated headers constructed during the configuration phase.
- Local stdio subprocesses spawn via
child_processwith a dedicated environment (mcp.env) and no inherited file descriptors, limiting their ability to access parent process resources.
Per-Tool Permission Policies in the Claude Agent SDK
Version 0.2.111 of the SDK introduced permission_policy support for tools registered via createSdkMcpServer. When the SDK receives the server map from mcp_set_servers, it enforces:
- Read-only vs. read-write restrictions per tool
- Sandboxing that denies filesystem, network, or privileged API access to specific tools
This ensures that even if an MCP server is configured and enabled, individual tools within that server respect granular permission boundaries.
Credential Hygiene and Secret Management
The system maintains strict separation between configuration data and secrets:
- Static headers remain in plain configuration files for non-sensitive metadata
- Credential-store headers retrieved from the encrypted credential manager via
isMultiHeaderCredential - OAuth tokens injected at request time through
Authorization: Bearer <token>headers, never persisted to disk
The hierarchical merging ensures that sensitive authentication data always overrides static values, eliminating the risk of accidental credential leakage through configuration files.
Practical Implementation: Building a Secure MCP Server Map
The following pattern demonstrates how to combine workspace guards with the builder to create a secure MCP server configuration:
import { SourceServerBuilder } from '@craft/shared/src/sources/server-builder';
import { isLocalMcpEnabled } from '@craft/shared/src/workspaces/storage';
const builder = new SourceServerBuilder();
async function buildServers(rootPath: string, sourcesWithCreds) {
// Respect workspace-level isolation flag
const allowLocal = isLocalMcpEnabled(rootPath);
const filtered = sourcesWithCreds.map(sc => ({
...sc,
// If local stdio is disabled, strip the token to force a null result
token: allowLocal ? sc.token : null,
}));
const { mcpServers, apiServers, errors } = await builder.buildAll(filtered);
// Pass `mcpServers` to the SDK via mcp_set_servers(...)
return { mcpServers, apiServers, errors };
}
This implementation checks the CRAFT_LOCAL_MCP_ENABLED environment variable and workspace configuration before allowing local subprocess creation, merges credentials according to the secure hierarchy, and returns a sanitized server map ready for the Claude Agent SDK's permission policy enforcement.
Summary
- SourceServerBuilder validates all MCP configurations and enforces a strict credential merging order that prioritizes OAuth tokens over static values.
- Workspace-level guards via
isLocalMcpEnabledprovide three-tier control (environment variable, config file, default) to disable local stdio subprocesses. - Runtime isolation separates HTTP/SSE network connections from local subprocesses, with the latter spawning in isolated environments without inherited file descriptors.
- Permission policies in the Claude Agent SDK enforce read-only or sandboxed restrictions on individual tools after server configuration.
- Credential hygiene ensures secrets never persist in configuration files, with encrypted credential stores and runtime token injection.
Frequently Asked Questions
How does Craft Agents prevent unauthorized local MCP server execution?
Craft Agents implements a workspace-level guard through the isLocalMcpEnabled function in packages/shared/src/workspaces/storage.ts. This function checks the CRAFT_LOCAL_MCP_ENABLED environment variable first, then the workspace configuration's localMcpServers.enabled setting, defaulting to true only if neither is specified. If the guard returns false, SourceServerBuilder refuses to create stdio transport configurations, preventing any local subprocess from spawning.
What happens when an MCP server configuration lacks valid authentication credentials?
When SourceServerBuilder encounters a source claiming authentication but missing valid tokens, it logs a debug message using the SERVER_BUILD_ERRORS.MISSING_CREDENTIALS constant and returns null for that server. This causes the source to be omitted entirely from the session rather than attempting to connect with invalid credentials. The builder enforces this validation in the buildMcpServer method within packages/shared/src/sources/server-builder.ts.
How are OAuth tokens and API secrets protected from exposure in configuration files?
The system implements credential hygiene through a hierarchical header merging strategy. Static headers defined in configuration files cannot override sensitive values retrieved from the encrypted credential store or OAuth tokens injected at request time. Specifically, the merging order prioritizes Authorization: Bearer tokens highest, followed by credential-store headers, then static headers. This ensures that secrets never persist in plain-text configuration files and always take precedence over any accidental static values.
Can enterprise administrators enforce a global ban on local MCP servers across all workspaces?
Yes, administrators can enforce a global ban by setting the CRAFT_LOCAL_MCP_ENABLED environment variable to false. This environment variable takes precedence over individual workspace configurations in the isLocalMcpEnabled resolution chain defined in packages/shared/src/workspaces/storage.ts. When this variable is set to false, all workspaces will be prevented from launching local stdio MCP servers regardless of their individual localMcpServers.enabled settings, providing a centralized security control for enterprise deployments.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →