Skip to main content

What is MCP?

The Model Context Protocol (MCP) is a standard for AI agents to interact with external tools, data sources, and services. MCP servers expose:
  • Tools - Functions the agent can call
  • Prompts - Reusable prompt templates
  • Resources - Files, data, and context
Craft Agents uses the official @modelcontextprotocol/sdk for MCP connections.

Transport Types

MCP servers support three transport mechanisms:
Remote servers accessed via HTTP endpoints. Most common for cloud-hosted MCP servers.
{
  "transport": "http",
  "url": "https://mcp.linear.app/mcp",
  "authType": "oauth"
}
URL normalization:
  • Trailing slashes are removed
  • /mcp suffix is added automatically
  • Example: https://api.example.comhttps://api.example.com/mcp
HTTP-based streaming transport for real-time updates.
{
  "transport": "sse",
  "url": "https://mcp.example.com/sse",
  "authType": "bearer"
}
URLs ending in /sse are automatically detected as SSE transport.
Spawns a local process and communicates via stdin/stdout. Used for local MCP servers.
{
  "transport": "stdio",
  "command": "npx",
  "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/data"],
  "env": {
    "NODE_ENV": "production"
  }
}
Stdio sources are filtered out when “Local MCP Servers” is disabled in workspace settings.

Authentication Types

MCP sources support three authentication methods:

OAuth Authentication

Full OAuth 2.0 flow with automatic token refresh:
{
  "type": "mcp",
  "mcp": {
    "transport": "http",
    "url": "https://mcp.linear.app",
    "authType": "oauth",
    "clientId": "your-client-id"
  }
}
Credentials stored in encrypted format:
  • Access token
  • Refresh token
  • Expiration time
  • Client ID
1

Trigger OAuth Flow

Agent calls source_oauth_trigger tool or user initiates from UI
2

Browser Authentication

User authenticates in browser and authorizes the connection
3

Token Storage

Tokens encrypted and saved to ~/.craft-agent/credentials.enc
4

Automatic Refresh

Tokens refreshed automatically when they expire

Bearer Token Authentication

Static API tokens passed in Authorization header:
{
  "type": "mcp",
  "mcp": {
    "transport": "http",
    "url": "https://api.example.com/mcp",
    "authType": "bearer"
  }
}
Tokens stored securely and injected automatically:
Authorization: Bearer your-token-here

No Authentication

Public MCP servers that don’t require authentication:
{
  "type": "mcp",
  "mcp": {
    "transport": "http",
    "url": "https://public-api.example.com/mcp",
    "authType": "none"
  }
}

MCP Client Implementation

Craft Agents uses CraftMcpClient to manage connections:
import { CraftMcpClient } from '@craft-agent/shared/mcp';

const client = new CraftMcpClient({
  transport: 'http',
  url: 'https://mcp.linear.app/mcp',
  headers: {
    'Authorization': 'Bearer token123'
  }
});

// Connect and verify
await client.connect();

// List available tools
const tools = await client.listTools();

// Call a tool
const result = await client.callTool('createIssue', {
  title: 'Bug in login',
  teamId: 'ENG'
});
Sensitive environment variables (ANTHROPIC_API_KEY, AWS_ACCESS_KEY_ID, etc.) are automatically filtered out when spawning stdio processes.

McpClientPool

All MCP connections are managed by a centralized pool in the main Electron process:
import { McpClientPool } from '@craft-agent/shared/mcp';

const pool = new McpClientPool({
  debug: (msg) => console.log(msg),
  workspaceRootPath: '~/.craft-agent/workspaces/xxx',
  sessionPath: '~/.craft-agent/workspaces/xxx/sessions/yyy'
});

// Connect sources
await pool.sync({
  'linear': {
    type: 'http',
    url: 'https://mcp.linear.app/mcp',
    headers: { 'Authorization': 'Bearer token' }
  },
  'github': {
    type: 'http',
    url: 'https://mcp.github.com/mcp',
    headers: { 'Authorization': 'Bearer ghtoken' }
  }
});

// Get available tools
const slugs = pool.getConnectedSlugs(); // ['linear', 'github']
const linearTools = pool.getTools('linear');

// Execute tool via proxy
const result = await pool.callTool('mcp__linear__createIssue', {
  title: 'Bug report',
  teamId: 'ENG'
});

Proxy Tool Naming

The pool creates namespaced proxy tools:
mcp__{sourceSlug}__{originalToolName}
Examples:
  • mcp__linear__createIssue
  • mcp__github__createPullRequest
  • mcp__notion__searchPages
This prevents name collisions when multiple sources expose similar tools.

Connecting MCP Servers

1

Create Source Configuration

import { createSource } from '@craft-agent/shared/sources';

await createSource(workspaceRootPath, {
  name: 'Linear',
  provider: 'linear',
  type: 'mcp',
  mcp: {
    transport: 'http',
    url: 'https://mcp.linear.app',
    authType: 'oauth'
  },
  icon: '🔵',
  enabled: true
});
2

Authenticate Source

For OAuth sources, trigger authentication flow:
import { SourceCredentialManager } from '@craft-agent/shared/sources';

const credManager = new SourceCredentialManager();
const result = await credManager.authenticate(source, {
  onStatus: (msg) => console.log(msg),
  onError: (err) => console.error(err)
});
3

Build Server Config

import { SourceServerBuilder } from '@craft-agent/shared/sources';

const builder = new SourceServerBuilder();
const token = await credManager.getToken(source);
const mcpConfig = builder.buildMcpServer(source, token);
4

Connect to Pool

await pool.connect('linear', mcpConfig);

Runtime Source Switching

Sources can be enabled/disabled without restarting the session:
// Enable a new source
const newSources = {
  ...existingServers,
  'notion': {
    type: 'http',
    url: 'https://mcp.notion.com/mcp',
    headers: { 'Authorization': 'Bearer token' }
  }
};

// Sync pool (connects new, keeps existing, disconnects removed)
await pool.sync(newSources);

// Notify agents of tool changes
pool.onToolsChanged?.();
The pool automatically connects new sources, disconnects removed ones, and keeps existing connections alive.

Large Response Handling

MCP tool results are automatically guarded against large responses:
const result = await pool.callTool('mcp__linear__searchIssues', {
  query: 'status:open'
});

// If response > 25KB or binary:
// 1. Binary files saved to session downloads/
// 2. Large text responses summarized or saved
// 3. User receives file path or summary

Error Handling

try {
  await client.connect();
} catch (error) {
  // Connection failed during health check
  console.error('MCP connection failed:', error.message);
}

Best Practices

OAuth tokens are scoped to the authenticated user, ensuring proper access control. Use bearer tokens only for service accounts.
The tagline helps agents understand when to use this source:
"tagline": "Issue tracking, bugs, tasks, sprints"
Provide context about data structure, query patterns, and team conventions so agents use the source effectively.
Use source_test tool or connection status checks to verify sources remain connected.

Next Steps

API Sources

Learn about REST API sources with automatic authentication

Local Files

Connect to local filesystems and Obsidian vaults

Build docs developers (and LLMs) love