-
-
Notifications
You must be signed in to change notification settings - Fork 9
MCP Integration
Complete guide to Model Context Protocol integration in Agentwise.
The Model Context Protocol (MCP) integration is a core feature of Agentwise that enables seamless connection with 61+ MCP servers, extending functionality and enabling specialized capabilities for agents.
MCP enables standardized communication between Agentwise and external tools and services:
{
"mcp_components": {
"servers": "External services providing tools and resources",
"clients": "Agentwise agents that consume MCP services",
"protocol": "Standardized JSON-RPC communication",
"transports": "stdio, HTTP, WebSocket connections"
}
}
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β Agentwise β β MCP Protocol β β MCP Servers β
β Agent βββββΊβ Layer βββββΊβ (61+ servers) β
β β β β β β
β β’ Task Request β β β’ Tool Discoveryβ β β’ File System β
β β’ Tool Usage β β β’ Resource Mgmt β β β’ GitHub API β
β β’ Result Proc. β β β’ Communication β β β’ Database β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
Agentwise automatically discovers MCP servers through multiple methods:
// Auto-discovery process
class MCPDiscovery {
async discoverServers() {
const sources = await Promise.all([
this.scanConfigFile(),
this.scanServerDirectory(),
this.scanEnvironmentVariables(),
this.queryRegistry()
]);
return this.mergeAndValidateServers(sources);
}
async scanConfigFile() {
const configPath = path.join(os.homedir(), '.claude', 'claude_desktop_config.json');
if (await fs.exists(configPath)) {
const config = await fs.readJSON(configPath);
return config.mcpServers || {};
}
return {};
}
}
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/files"],
"description": "File system access server"
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
},
"description": "GitHub repository management"
},
"database": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sqlite", "--db-path", "./project.db"],
"description": "SQLite database operations"
}
}
}
{
"core_servers": {
"filesystem": {
"capabilities": ["file_read", "file_write", "directory_list", "file_search"],
"use_cases": ["Project file management", "Code generation", "Asset handling"],
"agents": ["all"]
},
"github": {
"capabilities": ["repo_access", "issue_management", "pr_creation", "file_operations"],
"use_cases": ["Repository operations", "CI/CD integration", "Issue tracking"],
"agents": ["devops", "backend", "frontend"]
},
"database": {
"capabilities": ["query_execution", "schema_management", "data_operations"],
"use_cases": ["Database design", "Data migration", "Query optimization"],
"agents": ["database", "backend"]
}
}
}
{
"specialized_servers": {
"web_search": {
"server": "@modelcontextprotocol/server-brave-search",
"capabilities": ["search", "web_scraping", "content_analysis"],
"agents": ["research", "frontend", "backend"]
},
"email": {
"server": "@modelcontextprotocol/server-gmail",
"capabilities": ["send_email", "read_email", "manage_threads"],
"agents": ["devops", "research"]
},
"cloud_services": {
"aws": "@modelcontextprotocol/server-aws",
"gcp": "@modelcontextprotocol/server-gcp",
"azure": "@modelcontextprotocol/server-azure",
"agents": ["devops", "backend"]
},
"development_tools": {
"docker": "@modelcontextprotocol/server-docker",
"kubernetes": "@modelcontextprotocol/server-kubernetes",
"terraform": "@modelcontextprotocol/server-terraform",
"agents": ["devops"]
}
}
}
class AgentMCPIntegration {
constructor(agent) {
this.agent = agent;
this.mcpClients = new Map();
this.availableTools = new Map();
}
async initializeMCPIntegration() {
const servers = await this.discoverRelevantServers();
for (const [serverName, serverConfig] of servers) {
try {
const client = await this.connectToServer(serverName, serverConfig);
const tools = await client.listTools();
this.mcpClients.set(serverName, client);
this.registerTools(serverName, tools);
console.log(`Connected to MCP server: ${serverName}`);
} catch (error) {
console.warn(`Failed to connect to ${serverName}:`, error.message);
}
}\n }\n \n registerTools(serverName, tools) {\n tools.forEach(tool => {\n const toolKey = `${serverName}:${tool.name}`;\n this.availableTools.set(toolKey, {\n server: serverName,\n tool: tool,\n client: this.mcpClients.get(serverName)\n });\n });\n }\n}
async invokeTool(toolName, parameters) {
const toolEntry = this.availableTools.get(toolName);
if (!toolEntry) {
throw new Error(`Tool not found: ${toolName}`);\n }\n \n try {\n const result = await toolEntry.client.callTool(\n toolEntry.tool.name,\n parameters\n );\n \n return this.processToolResult(result);\n } catch (error) {\n throw new MCPToolError(`Tool invocation failed: ${error.message}`);\n }\n}
async processToolResult(result) {
// Process and validate tool result
return {\n success: true,\n data: result.content,\n metadata: {\n timestamp: Date.now(),\n tokens_used: this.estimateTokenUsage(result)\n }\n };\n}
// Frontend agent using filesystem MCP server
class FrontendAgentWithMCP extends FrontendSpecialist {
async createComponent(task, context) {
const componentCode = await this.generateComponentCode(task);
// Use filesystem MCP server to write files
await this.invokeTool('filesystem:write_file', {
path: `src/components/${task.componentName}.jsx`,
content: componentCode,
encoding: 'utf-8'
});
// Create accompanying test file
const testCode = await this.generateTestCode(task, componentCode);
await this.invokeTool('filesystem:write_file', {
path: `src/components/__tests__/${task.componentName}.test.jsx`,
content: testCode,
encoding: 'utf-8'
});
return {
status: 'success',
files_created: 2,
component_name: task.componentName
};
}
}
// DevOps agent using GitHub MCP server
class DevOpsAgentWithMCP extends DevOpsSpecialist {
async setupCICD(task, context) {
// Create GitHub Actions workflow
const workflowContent = await this.generateWorkflow(task);
await this.invokeTool('github:create_file', {
repo: context.repository,
path: '.github/workflows/ci.yml',
content: workflowContent,
message: 'Add CI/CD workflow',
branch: 'main'
});
// Create pull request for review
const prResult = await this.invokeTool('github:create_pull_request', {
repo: context.repository,
title: 'Add CI/CD Pipeline',
body: 'Automated CI/CD setup with testing and deployment',
head: 'feature/cicd-setup',
base: 'main'
});
return {\n status: 'success',\n workflow_created: true,\n pull_request: prResult.data.html_url\n };\n }\n}
// Database agent using database MCP server
class DatabaseAgentWithMCP extends DatabaseSpecialist {
async createSchema(task, context) {
const schemaSQL = await this.generateSchema(task);
// Execute schema creation
const result = await this.invokeTool('database:execute_query', {
query: schemaSQL,
params: []
});
// Create indexes for optimization
const indexes = await this.generateIndexes(task);
for (const index of indexes) {
await this.invokeTool('database:execute_query', {
query: index.sql,
params: []
});
}
// Generate and run seed data
const seedData = await this.generateSeedData(task);
await this.invokeTool('database:execute_query', {
query: seedData.sql,
params: seedData.params
});
return {
status: 'success',
tables_created: task.tables.length,
indexes_created: indexes.length,
records_seeded: seedData.recordCount
};
}
}
// Example custom MCP server for Agentwise
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
class AgentWiseCustomServer {
constructor() {
this.server = new Server(
{
name: 'agentwise-custom',
version: '1.0.0'
},
{
capabilities: {
tools: {},
resources: {}
}
}
);
this.setupTools();
}
setupTools() {
// Custom tool for project analysis
this.server.setRequestHandler('tools/list', async () => ({
tools: [
{
name: 'analyze_project_structure',
description: 'Analyze project structure and provide recommendations',
inputSchema: {
type: 'object',
properties: {
projectPath: { type: 'string' },
analysisType: { type: 'string', enum: ['structure', 'dependencies', 'security'] }
},
required: ['projectPath']
}
}
]
}));
this.server.setRequestHandler('tools/call', async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case 'analyze_project_structure':
return await this.analyzeProjectStructure(args);
default:
throw new Error(`Unknown tool: ${name}`);
}
});
}
async analyzeProjectStructure({ projectPath, analysisType = 'structure' }) {
// Implementation of project analysis
const analysis = await this.performAnalysis(projectPath, analysisType);
return {
content: [
{
type: 'text',
text: JSON.stringify(analysis, null, 2)
}
]
};
}
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
}
}
// Start the server
const server = new AgentWiseCustomServer();
server.start().catch(console.error);
class MCPConfigValidator {
validate(config) {
const errors = [];
for (const [serverName, serverConfig] of Object.entries(config.mcpServers || {})) {
// Validate required fields
if (!serverConfig.command) {
errors.push(`Server ${serverName}: missing required 'command' field`);
}
// Validate command existence
if (!this.commandExists(serverConfig.command)) {
errors.push(`Server ${serverName}: command '${serverConfig.command}' not found`);
}
// Validate environment variables
if (serverConfig.env) {
for (const [envVar, value] of Object.entries(serverConfig.env)) {
if (value.startsWith('${') && value.endsWith('}')) {
const actualVar = value.slice(2, -1);
if (!process.env[actualVar]) {
errors.push(`Server ${serverName}: environment variable '${actualVar}' not set`);
}
}
}
}
}
return {
valid: errors.length === 0,
errors
};
}
}
class DynamicMCPManager {
constructor() {
this.activeServers = new Map();
this.serverHealth = new Map();
this.reconnectAttempts = new Map();
}
async addServer(serverName, config) {
try {
const client = await this.connectToServer(serverName, config);
this.activeServers.set(serverName, client);
this.serverHealth.set(serverName, 'healthy');
// Start health monitoring
this.startHealthMonitoring(serverName);
return { success: true, serverName };
} catch (error) {
return { success: false, error: error.message };
}
}
async removeServer(serverName) {
const client = this.activeServers.get(serverName);
if (client) {
await client.close();
this.activeServers.delete(serverName);
this.serverHealth.delete(serverName);
this.reconnectAttempts.delete(serverName);
}
}
async healthCheck(serverName) {
const client = this.activeServers.get(serverName);
if (!client) return false;
try {
await client.ping();
this.serverHealth.set(serverName, 'healthy');
return true;
} catch (error) {
this.serverHealth.set(serverName, 'unhealthy');
this.attemptReconnection(serverName);
return false;
}
}
}
class MCPConnectionPool {
constructor(maxConnections = 10) {
this.maxConnections = maxConnections;
this.connections = new Map();
this.available = new Set();
this.waitQueue = [];
}
async getConnection(serverName) {
const poolKey = `pool:${serverName}`;
if (this.available.has(poolKey)) {
const connection = this.connections.get(poolKey);
this.available.delete(poolKey);
return connection;
}
if (this.connections.size < this.maxConnections) {
const connection = await this.createConnection(serverName);
this.connections.set(poolKey, connection);
return connection;
}
// Wait for available connection
return new Promise((resolve) => {
this.waitQueue.push({ serverName, resolve });
});
}
releaseConnection(serverName, connection) {
const poolKey = `pool:${serverName}`;
this.available.add(poolKey);
// Serve waiting requests
if (this.waitQueue.length > 0) {
const { resolve } = this.waitQueue.shift();
this.available.delete(poolKey);
resolve(connection);
}
}
}
class MCPResilienceManager {
constructor() {
this.retryConfig = {\n maxRetries: 3,\n baseDelay: 1000,\n maxDelay: 10000,\n backoffFactor: 2\n };\n }\n \n async callWithRetry(operation, context = {}) {\n let attempt = 0;\n let lastError;\n \n while (attempt <= this.retryConfig.maxRetries) {\n try {\n return await operation();\n } catch (error) {\n lastError = error;\n attempt++;\n \n if (attempt > this.retryConfig.maxRetries) {\n break;\n }\n \n if (!this.isRetryableError(error)) {\n throw error;\n }\n \n const delay = this.calculateDelay(attempt);\n await this.sleep(delay);\n }\n }\n \n throw new MCPRetryExhaustedError(\n `Operation failed after ${this.retryConfig.maxRetries} retries`,\n lastError\n );\n }\n}
bash\n# MCP server diagnostics\nnpm run mcp:list # List discovered servers\nnpm run mcp:test <server> # Test specific server\nnpm run mcp:health # Check server health\nnpm run mcp:logs <server> # View server logs\nnpm run mcp:validate-config # Validate MCP configuration\n
\n\n---\n\nFor more information, see Configuration, Agent System, or API Reference.
Support
- Discord: @vibecodingwithphil
- GitHub: @VibeCodingWithPhil