mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-02-06 05:23:08 +00:00
feat: add environment-aware debugging to diagnostic tools
Enhanced health check and diagnostic tools with environment-specific troubleshooting guidance based on telemetry analysis of 632K events from 5,308 users. Key improvements: - Environment-aware debugging suggestions for http/stdio modes - Docker-specific troubleshooting when IS_DOCKER=true - Cloud platform detection (Railway, Render, Fly, Heroku, AWS, K8s, GCP, Azure) - Platform-specific configuration paths (macOS, Windows, Linux) - MCP_MODE and platform tracking in telemetry events - Comprehensive integration tests for environment detection Addresses 59% session abandonment by providing actionable, context-specific next steps based on user's deployment environment. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -39,12 +39,28 @@ describe('Integration: handleDiagnostic', () => {
|
||||
expect(data).toHaveProperty('environment');
|
||||
expect(data).toHaveProperty('apiConfiguration');
|
||||
expect(data).toHaveProperty('toolsAvailability');
|
||||
expect(data).toHaveProperty('troubleshooting');
|
||||
expect(data).toHaveProperty('versionInfo');
|
||||
expect(data).toHaveProperty('performance');
|
||||
|
||||
// Verify timestamp format
|
||||
expect(typeof data.timestamp).toBe('string');
|
||||
const timestamp = new Date(data.timestamp);
|
||||
expect(timestamp.toString()).not.toBe('Invalid Date');
|
||||
|
||||
// Verify version info
|
||||
expect(data.versionInfo).toBeDefined();
|
||||
if (data.versionInfo) {
|
||||
expect(data.versionInfo).toHaveProperty('current');
|
||||
expect(data.versionInfo).toHaveProperty('upToDate');
|
||||
expect(typeof data.versionInfo.upToDate).toBe('boolean');
|
||||
}
|
||||
|
||||
// Verify performance metrics
|
||||
expect(data.performance).toBeDefined();
|
||||
if (data.performance) {
|
||||
expect(data.performance).toHaveProperty('diagnosticResponseTimeMs');
|
||||
expect(typeof data.performance.diagnosticResponseTimeMs).toBe('number');
|
||||
}
|
||||
});
|
||||
|
||||
it('should include environment variables', async () => {
|
||||
@@ -60,11 +76,20 @@ describe('Integration: handleDiagnostic', () => {
|
||||
expect(data.environment).toHaveProperty('N8N_API_KEY');
|
||||
expect(data.environment).toHaveProperty('NODE_ENV');
|
||||
expect(data.environment).toHaveProperty('MCP_MODE');
|
||||
expect(data.environment).toHaveProperty('isDocker');
|
||||
expect(data.environment).toHaveProperty('cloudPlatform');
|
||||
expect(data.environment).toHaveProperty('nodeVersion');
|
||||
expect(data.environment).toHaveProperty('platform');
|
||||
|
||||
// API key should be masked
|
||||
if (data.environment.N8N_API_KEY) {
|
||||
expect(data.environment.N8N_API_KEY).toBe('***configured***');
|
||||
}
|
||||
|
||||
// Environment detection types
|
||||
expect(typeof data.environment.isDocker).toBe('boolean');
|
||||
expect(typeof data.environment.nodeVersion).toBe('string');
|
||||
expect(typeof data.environment.platform).toBe('string');
|
||||
});
|
||||
|
||||
it('should check API configuration and connectivity', async () => {
|
||||
@@ -147,17 +172,118 @@ describe('Integration: handleDiagnostic', () => {
|
||||
|
||||
const data = response.data as DiagnosticResponse;
|
||||
|
||||
expect(data.troubleshooting).toBeDefined();
|
||||
expect(data.troubleshooting).toHaveProperty('steps');
|
||||
expect(data.troubleshooting).toHaveProperty('documentation');
|
||||
// Should have either nextSteps (if API connected) or setupGuide (if not configured)
|
||||
const hasGuidance = data.nextSteps || data.setupGuide || data.troubleshooting;
|
||||
expect(hasGuidance).toBeDefined();
|
||||
|
||||
// Troubleshooting steps should be an array
|
||||
expect(Array.isArray(data.troubleshooting.steps)).toBe(true);
|
||||
expect(data.troubleshooting.steps.length).toBeGreaterThan(0);
|
||||
if (data.nextSteps) {
|
||||
expect(data.nextSteps).toHaveProperty('message');
|
||||
expect(data.nextSteps).toHaveProperty('recommended');
|
||||
expect(Array.isArray(data.nextSteps.recommended)).toBe(true);
|
||||
}
|
||||
|
||||
// Documentation link should be present
|
||||
expect(typeof data.troubleshooting.documentation).toBe('string');
|
||||
expect(data.troubleshooting.documentation).toContain('https://');
|
||||
if (data.setupGuide) {
|
||||
expect(data.setupGuide).toHaveProperty('message');
|
||||
expect(data.setupGuide).toHaveProperty('whatYouCanDoNow');
|
||||
expect(data.setupGuide).toHaveProperty('whatYouCannotDo');
|
||||
expect(data.setupGuide).toHaveProperty('howToEnable');
|
||||
}
|
||||
|
||||
if (data.troubleshooting) {
|
||||
expect(data.troubleshooting).toHaveProperty('issue');
|
||||
expect(data.troubleshooting).toHaveProperty('steps');
|
||||
expect(Array.isArray(data.troubleshooting.steps)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ======================================================================
|
||||
// Environment Detection
|
||||
// ======================================================================
|
||||
|
||||
describe('Environment Detection', () => {
|
||||
it('should provide mode-specific debugging suggestions', async () => {
|
||||
const response = await handleDiagnostic(
|
||||
{ params: { arguments: {} } },
|
||||
mcpContext
|
||||
);
|
||||
|
||||
const data = response.data as DiagnosticResponse;
|
||||
|
||||
// Mode-specific debug should always be present
|
||||
expect(data).toHaveProperty('modeSpecificDebug');
|
||||
expect(data.modeSpecificDebug).toBeDefined();
|
||||
expect(data.modeSpecificDebug).toHaveProperty('mode');
|
||||
expect(data.modeSpecificDebug).toHaveProperty('troubleshooting');
|
||||
expect(data.modeSpecificDebug).toHaveProperty('commonIssues');
|
||||
|
||||
// Verify troubleshooting is an array with content
|
||||
expect(Array.isArray(data.modeSpecificDebug.troubleshooting)).toBe(true);
|
||||
expect(data.modeSpecificDebug.troubleshooting.length).toBeGreaterThan(0);
|
||||
|
||||
// Verify common issues is an array with content
|
||||
expect(Array.isArray(data.modeSpecificDebug.commonIssues)).toBe(true);
|
||||
expect(data.modeSpecificDebug.commonIssues.length).toBeGreaterThan(0);
|
||||
|
||||
// Mode should be either 'HTTP Server' or 'Standard I/O (Claude Desktop)'
|
||||
expect(['HTTP Server', 'Standard I/O (Claude Desktop)']).toContain(data.modeSpecificDebug.mode);
|
||||
});
|
||||
|
||||
it('should include Docker debugging if IS_DOCKER is true', async () => {
|
||||
// Save original value
|
||||
const originalIsDocker = process.env.IS_DOCKER;
|
||||
|
||||
try {
|
||||
// Set IS_DOCKER for this test
|
||||
process.env.IS_DOCKER = 'true';
|
||||
|
||||
const response = await handleDiagnostic(
|
||||
{ params: { arguments: {} } },
|
||||
mcpContext
|
||||
);
|
||||
|
||||
const data = response.data as DiagnosticResponse;
|
||||
|
||||
// Should have Docker debug section
|
||||
expect(data).toHaveProperty('dockerDebug');
|
||||
expect(data.dockerDebug).toBeDefined();
|
||||
expect(data.dockerDebug?.containerDetected).toBe(true);
|
||||
expect(data.dockerDebug?.troubleshooting).toBeDefined();
|
||||
expect(Array.isArray(data.dockerDebug?.troubleshooting)).toBe(true);
|
||||
expect(data.dockerDebug?.commonIssues).toBeDefined();
|
||||
} finally {
|
||||
// Restore original value
|
||||
if (originalIsDocker) {
|
||||
process.env.IS_DOCKER = originalIsDocker;
|
||||
} else {
|
||||
delete process.env.IS_DOCKER;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should not include Docker debugging if IS_DOCKER is false', async () => {
|
||||
// Save original value
|
||||
const originalIsDocker = process.env.IS_DOCKER;
|
||||
|
||||
try {
|
||||
// Unset IS_DOCKER for this test
|
||||
delete process.env.IS_DOCKER;
|
||||
|
||||
const response = await handleDiagnostic(
|
||||
{ params: { arguments: {} } },
|
||||
mcpContext
|
||||
);
|
||||
|
||||
const data = response.data as DiagnosticResponse;
|
||||
|
||||
// Should not have Docker debug section
|
||||
expect(data.dockerDebug).toBeUndefined();
|
||||
} finally {
|
||||
// Restore original value
|
||||
if (originalIsDocker) {
|
||||
process.env.IS_DOCKER = originalIsDocker;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -245,13 +371,14 @@ describe('Integration: handleDiagnostic', () => {
|
||||
|
||||
const data = response.data as DiagnosticResponse;
|
||||
|
||||
// Verify all required fields
|
||||
// Verify all required fields (always present)
|
||||
const requiredFields = [
|
||||
'timestamp',
|
||||
'environment',
|
||||
'apiConfiguration',
|
||||
'toolsAvailability',
|
||||
'troubleshooting'
|
||||
'versionInfo',
|
||||
'performance'
|
||||
];
|
||||
|
||||
requiredFields.forEach(field => {
|
||||
@@ -259,12 +386,17 @@ describe('Integration: handleDiagnostic', () => {
|
||||
expect(data[field]).toBeDefined();
|
||||
});
|
||||
|
||||
// Context-specific fields (at least one should be present)
|
||||
const hasContextualGuidance = data.nextSteps || data.setupGuide || data.troubleshooting;
|
||||
expect(hasContextualGuidance).toBeDefined();
|
||||
|
||||
// Verify data types
|
||||
expect(typeof data.timestamp).toBe('string');
|
||||
expect(typeof data.environment).toBe('object');
|
||||
expect(typeof data.apiConfiguration).toBe('object');
|
||||
expect(typeof data.toolsAvailability).toBe('object');
|
||||
expect(typeof data.troubleshooting).toBe('object');
|
||||
expect(typeof data.versionInfo).toBe('object');
|
||||
expect(typeof data.performance).toBe('object');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,6 +35,9 @@ describe('Integration: handleHealthCheck', () => {
|
||||
expect(data).toHaveProperty('status');
|
||||
expect(data).toHaveProperty('apiUrl');
|
||||
expect(data).toHaveProperty('mcpVersion');
|
||||
expect(data).toHaveProperty('versionCheck');
|
||||
expect(data).toHaveProperty('performance');
|
||||
expect(data).toHaveProperty('nextSteps');
|
||||
|
||||
// Status should be a string (e.g., "ok", "healthy")
|
||||
if (data.status) {
|
||||
@@ -48,6 +51,22 @@ describe('Integration: handleHealthCheck', () => {
|
||||
// MCP version should be defined
|
||||
expect(data.mcpVersion).toBeDefined();
|
||||
expect(typeof data.mcpVersion).toBe('string');
|
||||
|
||||
// Version check should be present
|
||||
expect(data.versionCheck).toBeDefined();
|
||||
expect(data.versionCheck).toHaveProperty('current');
|
||||
expect(data.versionCheck).toHaveProperty('upToDate');
|
||||
expect(typeof data.versionCheck.upToDate).toBe('boolean');
|
||||
|
||||
// Performance metrics should be present
|
||||
expect(data.performance).toBeDefined();
|
||||
expect(data.performance).toHaveProperty('responseTimeMs');
|
||||
expect(typeof data.performance.responseTimeMs).toBe('number');
|
||||
expect(data.performance.responseTimeMs).toBeGreaterThan(0);
|
||||
|
||||
// Next steps should be present
|
||||
expect(data.nextSteps).toBeDefined();
|
||||
expect(Array.isArray(data.nextSteps)).toBe(true);
|
||||
});
|
||||
|
||||
it('should include feature availability information', async () => {
|
||||
|
||||
@@ -77,6 +77,10 @@ export interface DiagnosticResponse {
|
||||
N8N_API_KEY: string | null;
|
||||
NODE_ENV: string;
|
||||
MCP_MODE: string;
|
||||
isDocker: boolean;
|
||||
cloudPlatform: string | null;
|
||||
nodeVersion: string;
|
||||
platform: string;
|
||||
};
|
||||
apiConfiguration: {
|
||||
configured: boolean;
|
||||
@@ -88,10 +92,43 @@ export interface DiagnosticResponse {
|
||||
} | null;
|
||||
};
|
||||
toolsAvailability: ToolsAvailability;
|
||||
troubleshooting: {
|
||||
versionInfo?: {
|
||||
current: string;
|
||||
latest: string | null;
|
||||
upToDate: boolean;
|
||||
message: string;
|
||||
updateCommand?: string;
|
||||
};
|
||||
performance?: {
|
||||
diagnosticResponseTimeMs: number;
|
||||
cacheHitRate: string;
|
||||
cachedInstances: number;
|
||||
};
|
||||
modeSpecificDebug: {
|
||||
mode: string;
|
||||
troubleshooting: string[];
|
||||
commonIssues: string[];
|
||||
[key: string]: any; // For mode-specific fields like port, configLocation, etc.
|
||||
};
|
||||
dockerDebug?: {
|
||||
containerDetected: boolean;
|
||||
troubleshooting: string[];
|
||||
commonIssues: string[];
|
||||
};
|
||||
cloudPlatformDebug?: {
|
||||
name: string;
|
||||
troubleshooting: string[];
|
||||
};
|
||||
troubleshooting?: {
|
||||
issue?: string;
|
||||
error?: string;
|
||||
steps: string[];
|
||||
commonIssues?: string[];
|
||||
documentation: string;
|
||||
};
|
||||
nextSteps?: any;
|
||||
setupGuide?: any;
|
||||
updateWarning?: any;
|
||||
debug?: DebugInfo;
|
||||
[key: string]: any; // Allow dynamic property access for optional field checks
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user