ai-debug-local-mcp
Version:
🎯 ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh
291 lines (254 loc) • 8.12 kB
text/typescript
import { DebugState } from '../types';
export interface TidewaveCapabilities {
framework: 'phoenix' | 'rails' | 'unknown';
endpoint: string;
available_tools: string[];
version?: string;
}
export interface TidewaveRequest {
tool: string;
params?: Record<string, any>;
}
export interface TidewaveResponse {
success: boolean;
data?: any;
error?: string;
metadata?: {
execution_time?: number;
framework_version?: string;
};
}
export class TidewaveIntegration {
private capabilities: Map<string, TidewaveCapabilities> = new Map();
/**
* Detect if a URL has Tidewave enabled and what capabilities it has
*/
async detectTidewave(url: string): Promise<TidewaveCapabilities | null> {
try {
// Try Phoenix endpoint first
const phoenixEndpoint = `${url}/tidewave/mcp`;
let response = await this.testEndpoint(phoenixEndpoint);
if (response) {
const capabilities: TidewaveCapabilities = {
framework: 'phoenix',
endpoint: phoenixEndpoint,
available_tools: response.tools || [],
version: response.version
};
this.capabilities.set(url, capabilities);
return capabilities;
}
// Try Rails endpoint
const railsEndpoint = `${url}/tidewave`;
response = await this.testEndpoint(railsEndpoint);
if (response) {
const capabilities: TidewaveCapabilities = {
framework: 'rails',
endpoint: railsEndpoint,
available_tools: response.tools || [],
version: response.version
};
this.capabilities.set(url, capabilities);
return capabilities;
}
return null;
} catch (error) {
console.error('Tidewave detection failed:', error);
return null;
}
}
/**
* Test if a Tidewave endpoint is accessible
*/
private async testEndpoint(endpoint: string): Promise<any> {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
const response = await fetch(`${endpoint}/capabilities`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
signal: controller.signal
});
clearTimeout(timeoutId);
if (response.ok) {
return await response.json();
}
return null;
} catch {
return null;
}
}
/**
* Execute a Tidewave tool for enhanced debugging/testing
*/
async executeTool(url: string, request: TidewaveRequest): Promise<TidewaveResponse> {
const capabilities = this.capabilities.get(url);
if (!capabilities) {
return {
success: false,
error: 'Tidewave not available for this URL'
};
}
if (!capabilities.available_tools.includes(request.tool)) {
return {
success: false,
error: `Tool '${request.tool}' not available. Available: ${capabilities.available_tools.join(', ')}`
};
}
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000);
const response = await fetch(`${capabilities.endpoint}/${request.tool}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(request.params || {}),
signal: controller.signal
});
clearTimeout(timeoutId);
const data = await response.json();
return {
success: response.ok,
data: response.ok ? data : undefined,
error: response.ok ? undefined : data.error || `HTTP ${response.status}`,
metadata: {
execution_time: data.execution_time,
framework_version: capabilities.version
}
};
} catch (error) {
return {
success: false,
error: `Tidewave execution failed: ${error instanceof Error ? error.message : String(error)}`
};
}
}
/**
* Get application logs through Tidewave
*/
async getLogs(url: string, options: {
level?: string;
limit?: number;
since?: string;
} = {}): Promise<TidewaveResponse> {
return this.executeTool(url, {
tool: 'logs',
params: options
});
}
/**
* Execute custom code for testing/debugging
*/
async evaluateCode(url: string, code: string, context?: Record<string, any>): Promise<TidewaveResponse> {
return this.executeTool(url, {
tool: 'eval',
params: { code, context }
});
}
/**
* Get application documentation
*/
async getDocumentation(url: string, path?: string): Promise<TidewaveResponse> {
return this.executeTool(url, {
tool: 'documentation',
params: { path }
});
}
/**
* Execute SQL queries for database testing
*/
async executeSQL(url: string, query: string, params?: any[]): Promise<TidewaveResponse> {
return this.executeTool(url, {
tool: 'sql',
params: { query, params }
});
}
/**
* Trace application processes
*/
async traceProcesses(url: string, pattern?: string): Promise<TidewaveResponse> {
return this.executeTool(url, {
tool: 'processes',
params: { pattern }
});
}
/**
* Search project dependencies
*/
async searchDependencies(url: string, query: string): Promise<TidewaveResponse> {
return this.executeTool(url, {
tool: 'dependencies',
params: { query }
});
}
/**
* Enhanced debugging state with Tidewave data
*/
async enhanceDebugState(url: string, debugState: DebugState): Promise<DebugState> {
const capabilities = this.capabilities.get(url);
if (!capabilities) {
return debugState;
}
// Add Tidewave-specific information to debug state
const tidewaveData: any = {
framework: capabilities.framework,
available_tools: capabilities.available_tools,
version: capabilities.version
};
// Get logs if available
if (capabilities.available_tools.includes('logs')) {
const logs = await this.getLogs(url, { limit: 50 });
if (logs.success) {
tidewaveData.recent_logs = logs.data;
}
}
// Get process information if available
if (capabilities.available_tools.includes('processes')) {
const processes = await this.traceProcesses(url);
if (processes.success) {
tidewaveData.processes = processes.data;
}
}
return {
...debugState,
tidewave: tidewaveData
};
}
/**
* Generate test assertions using Tidewave application knowledge
*/
async generateTestAssertions(url: string, scenario: string): Promise<string[]> {
const capabilities = this.capabilities.get(url);
if (!capabilities) {
return [];
}
const framework = capabilities.framework;
const assertions: string[] = [];
// Framework-specific test generation
if (framework === 'phoenix') {
assertions.push(`# Phoenix LiveView test assertions for: ${scenario}`);
assertions.push('assert has_element?(view, "[data-testid=\\"#{scenario}\\"]")');
assertions.push('refute has_element?(view, ".error")');
} else if (framework === 'rails') {
assertions.push(`# Rails system test assertions for: ${scenario}`);
assertions.push(`expect(page).to have_content("${scenario}")`);
assertions.push('expect(page).not_to have_css(".error")');
}
// Use Tidewave documentation to enhance assertions
if (capabilities.available_tools.includes('documentation')) {
const docs = await this.getDocumentation(url, scenario);
if (docs.success && docs.data) {
// Parse documentation to generate more specific assertions
assertions.push(`# Generated from application documentation`);
// Add doc-based assertions here
}
}
return assertions;
}
}
export const tidewave = new TidewaveIntegration();