# How MCP Server Integration Handles Security and Isolation in Craft Agents

> Learn how Craft Agents secures MCP server integration with defense-in-depth: validation, workspace guards, credential isolation, and permission policies prevent unauthorized access and data leaks.

- Repository: [Craft Docs/craft-agents-oss](https://github.com/lukilabs/craft-agents-oss)
- Tags: internals
- Published: 2026-04-18

---

**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](https://github.com/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`](https://github.com/lukilabs/craft-agents-oss/blob/main/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:

```typescript
// 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:

1. **Static headers** defined in the source configuration
2. **Credential-store headers** retrieved from the encrypted credential manager
3. **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:

```typescript
// 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`](https://github.com/lukilabs/craft-agents-oss/blob/main/packages/shared/src/workspaces/storage.ts).

### The Resolution Chain for Local MCP Permissions

The guard evaluates three levels of configuration in strict priority order:

```typescript
// 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;
}

```

1. **Environment variable** `CRAFT_LOCAL_MCP_ENABLED` – enables enterprise-wide lockdowns
2. **Workspace configuration** (`localMcpServers.enabled`) – allows per-project policies
3. **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_process` with 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:

```typescript
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 `isLocalMcpEnabled` provide 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`](https://github.com/lukilabs/craft-agents-oss/blob/main/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`](https://github.com/lukilabs/craft-agents-oss/blob/main/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`](https://github.com/lukilabs/craft-agents-oss/blob/main/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.