MCP Server Integration
This guide covers how to integrate Model Context Protocol (MCP) servers with vibe-check to extend agent capabilities with custom tools. You’ll learn how to configure, secure, and use MCP servers in tests and workflows.
What are MCP Servers?
Section titled “What are MCP Servers?”MCP servers provide agents with specialized tools for specific domains:
- Filesystem - Read/write files with path restrictions
- Database - SQL operations (PostgreSQL, MySQL, etc.)
- Docker - Container management
- Git - Version control operations
- Custom - Build your own tools
Without MCP servers, agents are limited to built-in Claude Code tools (Read, Write, Edit, Bash, etc.). MCP servers expand this toolkit.
Basic Configuration
Section titled “Basic Configuration”MCP servers are configured in the mcpServers
field of RunAgentOptions
or AgentConfig
:
import { vibeTest } from '@dao/vibe-check';
vibeTest('use filesystem MCP', async ({ runAgent, expect }) => { const result = await runAgent({ prompt: 'List all TypeScript files in src/', mcpServers: { filesystem: { command: 'npx', args: ['@modelcontextprotocol/server-filesystem', './src'], allowedTools: ['read_file', 'list_directory'] } } });
expect(result).toHaveUsedTool('list_directory');});
MCPServerConfig
Section titled “MCPServerConfig”Each MCP server has this structure:
interface MCPServerConfig { /** Server command (e.g., 'node', 'python', 'npx') */ command: string;
/** Command arguments */ args?: string[];
/** Environment variables */ env?: Record<string, string>;
/** Allowed tool names (whitelist) */ allowedTools?: string[];}
Common MCP Servers
Section titled “Common MCP Servers”Filesystem Server
Section titled “Filesystem Server”Provides safe file operations with path restrictions:
vibeTest('filesystem operations', async ({ runAgent, expect }) => { const result = await runAgent({ prompt: 'Read all package.json files and summarize dependencies', mcpServers: { filesystem: { command: 'npx', args: [ '@modelcontextprotocol/server-filesystem', './src', // Restrict to src/ directory './package.json' ], allowedTools: [ 'read_file', 'list_directory' // Note: 'write_file' not allowed (read-only access) ] } } });
expect(result).toHaveUsedTool('read_file'); expect(result.files.changed().length).toBe(0); // No writes});
Database Server
Section titled “Database Server”Execute SQL operations on PostgreSQL databases:
import { defineAgent } from '@dao/vibe-check';
const dbAgent = defineAgent({ name: 'database-admin', mcpServers: { database: { command: 'npx', args: ['@modelcontextprotocol/server-postgres'], env: { POSTGRES_URL: process.env.DATABASE_URL // Pass connection string via env }, allowedTools: [ 'query', // Execute SELECT queries 'schema', // Read schema information 'migrate' // Run migrations ] } }});
vibeTest('database migration', async ({ runAgent, expect }) => { const result = await runAgent({ agent: dbAgent, prompt: 'Add a created_at column to the users table' });
expect(result).toHaveUsedTool('migrate');});
Docker Server
Section titled “Docker Server”Manage Docker containers and images:
vibeTest('container deployment', async ({ runAgent, expect }) => { const result = await runAgent({ prompt: 'Build and start the app container', mcpServers: { docker: { command: 'npx', args: ['@modelcontextprotocol/server-docker'], allowedTools: [ 'build_image', 'start_container', 'list_containers' ] } } });
expect(result).toHaveUsedTool('build_image'); expect(result).toHaveUsedTool('start_container');});
Git Server
Section titled “Git Server”Version control operations:
vibeTest('git operations', async ({ runAgent, expect }) => { const result = await runAgent({ prompt: 'Create a feature branch and commit changes', mcpServers: { git: { command: 'npx', args: ['@modelcontextprotocol/server-git', './'], allowedTools: [ 'branch', 'commit', 'status' // Note: 'push' not allowed for safety ] } } });
expect(result).toHaveUsedTool('branch'); expect(result).toHaveUsedTool('commit');});
Multiple MCP Servers
Section titled “Multiple MCP Servers”Use multiple servers for different capabilities:
vibeTest('full-stack deployment', async ({ runAgent }) => { const result = await runAgent({ prompt: 'Deploy the application', mcpServers: { // Database operations database: { command: 'npx', args: ['@mcp/postgres'], env: { POSTGRES_URL: process.env.DATABASE_URL }, allowedTools: ['migrate', 'query'] },
// Docker operations docker: { command: 'npx', args: ['@mcp/docker'], allowedTools: ['build_image', 'start_container'] },
// Git operations git: { command: 'npx', args: ['@mcp/git', './'], allowedTools: ['status', 'commit'] } } });
// Agent can use tools from all servers console.log('Tools used:', result.tools.all().map(t => t.name));});
Workflow Integration
Section titled “Workflow Integration”Agent-Level Configuration
Section titled “Agent-Level Configuration”Configure MCP servers once for reuse:
import { defineAgent, vibeWorkflow } from '@dao/vibe-check';
const deploymentAgent = defineAgent({ name: 'deployer', mcpServers: { docker: { command: 'npx', args: ['@mcp/docker'], allowedTools: ['build_image', 'start_container', 'stop_container'] }, database: { command: 'npx', args: ['@mcp/postgres'], env: { POSTGRES_URL: process.env.DATABASE_URL }, allowedTools: ['migrate', 'query'] } }});
vibeWorkflow('deployment pipeline', async (wf) => { // All stages use the same MCP servers await wf.stage('run migrations', { agent: deploymentAgent, prompt: '/migrate-database' });
await wf.stage('deploy app', { agent: deploymentAgent, prompt: '/deploy-containers' });});
Per-Stage Configuration
Section titled “Per-Stage Configuration”Override MCP servers for specific stages:
vibeWorkflow('multi-stage deployment', async (wf) => { // Stage 1: Database only await wf.stage('migrate database', { prompt: '/migrate', mcpServers: { database: { command: 'npx', args: ['@mcp/postgres'], env: { POSTGRES_URL: process.env.DATABASE_URL } } } });
// Stage 2: Docker only await wf.stage('deploy containers', { prompt: '/deploy', mcpServers: { docker: { command: 'npx', args: ['@mcp/docker'] } } });
// Stage 3: Both servers await wf.stage('finalize deployment', { prompt: '/finalize', mcpServers: { database: { command: 'npx', args: ['@mcp/postgres'] }, docker: { command: 'npx', args: ['@mcp/docker'] } } });});
Security Best Practices
Section titled “Security Best Practices”1. Use allowedTools Whitelist
Section titled “1. Use allowedTools Whitelist”Always restrict tools to the minimum necessary:
// ✅ Good: Whitelist only necessary toolsmcpServers: { filesystem: { command: 'npx', args: ['@mcp/filesystem', './src'], allowedTools: ['read_file', 'list_directory'] // Read-only }}
// ❌ Bad: No restriction (all tools allowed)mcpServers: { filesystem: { command: 'npx', args: ['@mcp/filesystem', './'] // Missing allowedTools - allows delete_file, write_file, etc. }}
2. Use Environment Variables for Secrets
Section titled “2. Use Environment Variables for Secrets”Never hardcode credentials:
// ✅ Good: Use environment variablesmcpServers: { database: { command: 'npx', args: ['@mcp/postgres'], env: { POSTGRES_URL: process.env.DATABASE_URL, DB_PASSWORD: process.env.DB_PASSWORD } }}
// ❌ Bad: Hardcoded credentialsmcpServers: { database: { command: 'npx', args: ['@mcp/postgres'], env: { POSTGRES_URL: 'postgresql://user:password@localhost/db' // Exposed! } }}
3. Restrict Filesystem Access
Section titled “3. Restrict Filesystem Access”Limit access to specific directories:
// ✅ Good: Specific directoryargs: ['@mcp/filesystem', './src', './tests']
// ❌ Bad: Unrestricted accessargs: ['@mcp/filesystem', '/']
4. Avoid Dangerous Tools in Production
Section titled “4. Avoid Dangerous Tools in Production”Be careful with destructive operations:
// ✅ Good: Safe tools for productionallowedTools: ['query', 'schema'] // Read-only
// ⚠️ Dangerous: Allow with cautionallowedTools: ['migrate', 'drop_table', 'delete'] // Destructive
Custom MCP Servers
Section titled “Custom MCP Servers”Build custom MCP servers for specialized needs:
Example: Custom API Server
Section titled “Example: Custom API Server”import { MCPServer } from '@modelcontextprotocol/sdk';
const server = new MCPServer({ name: 'custom-api', tools: [ { name: 'fetch_user', description: 'Fetch user data from API', parameters: { userId: { type: 'string', required: true } }, handler: async ({ userId }) => { const response = await fetch(`https://api.example.com/users/${userId}`); return response.json(); } } ]});
server.start();
Use Custom Server
Section titled “Use Custom Server”vibeTest('use custom API', async ({ runAgent, expect }) => { const result = await runAgent({ prompt: 'Get user data for user ID 123', mcpServers: { customApi: { command: 'node', args: ['./custom-api-server.js'], allowedTools: ['fetch_user'] } } });
expect(result).toHaveUsedTool('fetch_user');});
Troubleshooting
Section titled “Troubleshooting”Server Not Starting
Section titled “Server Not Starting”Check that the server command and args are correct:
// Debug server configurationvibeTest('debug MCP server', async ({ runAgent }) => { try { const result = await runAgent({ prompt: 'Test MCP', mcpServers: { test: { command: 'npx', args: ['@mcp/filesystem', './'], allowedTools: ['read_file'] } } }); } catch (error) { console.error('Server failed to start:', error); // Check: Is the package installed? // Check: Are args correct? // Check: Does the server work standalone? }});
Tool Not Found
Section titled “Tool Not Found”Verify tool names match server’s exposed tools:
// ✅ Good: Correct tool nameallowedTools: ['read_file'] // Matches server tool name
// ❌ Bad: Incorrect tool nameallowedTools: ['readFile'] // Server uses 'read_file', not 'readFile'
Permission Errors
Section titled “Permission Errors”Check filesystem paths and environment variables:
// Ensure paths are accessiblemcpServers: { filesystem: { command: 'npx', args: ['@mcp/filesystem', './src'], // Must exist allowedTools: ['read_file'] }}
Testing MCP Server Integration
Section titled “Testing MCP Server Integration”Verify Tool Usage
Section titled “Verify Tool Usage”vibeTest('verify MCP tools', async ({ runAgent, expect }) => { const result = await runAgent({ prompt: 'List files and read package.json', mcpServers: { filesystem: { command: 'npx', args: ['@mcp/filesystem', './'], allowedTools: ['list_directory', 'read_file'] } } });
// Verify tools were used expect(result).toHaveUsedTool('list_directory'); expect(result).toHaveUsedTool('read_file');
// Check tool call details const listCalls = result.tools.filter('list_directory'); expect(listCalls.length).toBeGreaterThan(0);
const readCalls = result.tools.filter('read_file'); expect(readCalls.some(t => t.input?.includes('package.json'))).toBe(true);});
Test Error Handling
Section titled “Test Error Handling”vibeTest('MCP error handling', async ({ runAgent }) => { const result = await runAgent({ prompt: 'Read non-existent file', mcpServers: { filesystem: { command: 'npx', args: ['@mcp/filesystem', './'], allowedTools: ['read_file'] } } });
// Check if error was handled const readCalls = result.tools.filter('read_file'); const failedReads = readCalls.filter(t => t.result?.includes('ERROR'));
expect(failedReads.length).toBeGreaterThan(0);});
Performance Considerations
Section titled “Performance Considerations”Server Startup Time
Section titled “Server Startup Time”MCP servers add startup overhead:
// Measure server startup impactvibeTest('MCP startup performance', async ({ runAgent }) => { const start = Date.now();
const result = await runAgent({ prompt: 'Quick task', mcpServers: { filesystem: { command: 'npx', args: ['@mcp/filesystem', './'] } } });
const duration = Date.now() - start; console.log('Total time (with MCP):', duration, 'ms');
// Server startup typically adds 1-3 seconds});
Reuse Agents to Avoid Restarts
Section titled “Reuse Agents to Avoid Restarts”// ✅ Good: Define once, reuseconst dbAgent = defineAgent({ name: 'db', mcpServers: { database: { /* config */ } }});
vibeTest('test 1', async ({ runAgent }) => { await runAgent({ agent: dbAgent, prompt: '/task1' });});
vibeTest('test 2', async ({ runAgent }) => { await runAgent({ agent: dbAgent, prompt: '/task2' });});
Best Practices Summary
Section titled “Best Practices Summary”- Whitelist Tools - Use
allowedTools
to restrict access - Use Environment Variables - Never hardcode secrets
- Restrict Paths - Limit filesystem access to specific directories
- Test Locally First - Verify MCP servers work standalone before integrating
- Handle Errors - Check
result.tools
for failed tool calls - Reuse Configurations - Use
defineAgent
for common MCP setups - Document Tool Usage - Comment which tools each task needs
- Monitor Performance - Track server startup overhead
Available MCP Servers
Section titled “Available MCP Servers”Server | Package | Tools | Use Case |
---|---|---|---|
Filesystem | @modelcontextprotocol/server-filesystem | read_file, write_file, list_directory, delete_file | File operations with path restrictions |
PostgreSQL | @modelcontextprotocol/server-postgres | query, schema, migrate | SQL database operations |
Docker | @modelcontextprotocol/server-docker | build_image, start_container, stop_container, list_containers | Container management |
Git | @modelcontextprotocol/server-git | branch, commit, status, diff, log | Version control operations |
What’s Next?
Section titled “What’s Next?”Now that you understand MCP servers, explore:
- Cost Optimization → - Reduce costs when using MCP tools
- Building Workflows → - Use MCP in workflows
- defineAgent() → - Configure agents with MCP servers
Or learn more about MCP:
- MCP Specification - Official MCP documentation
- MCPServerConfig → - Type reference