// Copyright 2025 Chris Bunting
// Brief: Main entry point for the dependency analysis MCP server
// Scope: Initializes the server and registers all tools
import { PackageManager, SeverityLevel } from '@mcp-code-analysis/shared-types';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
import { DependencyAnalyzer } from './services/DependencyAnalyzer.js';
import { DependencyGraphBuilder } from './services/DependencyGraphBuilder.js';
import { PackageManagerDetector } from './services/PackageManagerDetector.js';
import { SecurityAuditor } from './services/SecurityAuditor.js';
import { VersionResolver } from './services/VersionResolver.js';
import { Logger } from './utils/Logger.js';
// Create logger
const logger = new Logger('DependencyAnalysisServer' as any);
// Create service instances
const packageManagerDetector = new PackageManagerDetector(logger);
const dependencyGraphBuilder = new DependencyGraphBuilder(logger);
const versionResolver = new VersionResolver(logger);
const securityAuditor = new SecurityAuditor(logger);
const dependencyAnalyzer = new DependencyAnalyzer(
packageManagerDetector,
dependencyGraphBuilder,
versionResolver,
securityAuditor,
logger
);
// Create server instance
const server = new Server(
{
name: 'dependency-analysis-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// Tool schemas
const AnalyzeDependenciesSchema = z.object({
projectPath: z.string().describe('Path to the project directory'),
includeDev: z.boolean().optional().default(true).describe('Include development dependencies'),
includePeer: z.boolean().optional().default(true).describe('Include peer dependencies'),
includeOptional: z.boolean().optional().default(true).describe('Include optional dependencies'),
});
const CheckUpdatesSchema = z.object({
projectPath: z.string().describe('Path to the project directory'),
packageName: z.string().optional().describe('Specific package name to check updates for'),
});
const FindConflictsSchema = z.object({
projectPath: z.string().describe('Path to the project directory'),
});
const SuggestAlternativesSchema = z.object({
packageName: z.string().describe('Name of the package to find alternatives for'),
packageManager: z.nativeEnum(PackageManager).optional().describe('Package manager to use'),
});
const SecurityAuditSchema = z.object({
projectPath: z.string().describe('Path to the project directory'),
severity: z.nativeEnum(SeverityLevel).optional().default(SeverityLevel.WARNING).describe('Minimum severity level to report'),
});
// Register tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'analyze_dependencies',
description: 'Analyze project dependencies and generate comprehensive report',
inputSchema: zodToJsonSchema(AnalyzeDependenciesSchema),
},
{
name: 'check_updates',
description: 'Check for available updates for project dependencies',
inputSchema: zodToJsonSchema(CheckUpdatesSchema),
},
{
name: 'find_conflicts',
description: 'Find version conflicts in project dependencies',
inputSchema: zodToJsonSchema(FindConflictsSchema),
},
{
name: 'suggest_alternatives',
description: 'Suggest alternative packages for a given package',
inputSchema: zodToJsonSchema(SuggestAlternativesSchema),
},
{
name: 'security_audit',
description: 'Perform security audit of project dependencies',
inputSchema: zodToJsonSchema(SecurityAuditSchema),
},
],
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'analyze_dependencies': {
const { projectPath, includeDev, includePeer, includeOptional } =
AnalyzeDependenciesSchema.parse(args);
logger.info(`Analyzing dependencies for project: ${projectPath}`);
const result = await dependencyAnalyzer.analyzeProject(projectPath, {
includeDev,
includePeer,
includeOptional,
});
// Format the result for output
const formattedResult = {
summary: {
projectPath: result.projectPath,
packageManager: result.packageManager,
totalDependencies: result.metrics.totalDependencies,
directDependencies: result.metrics.directDependencies,
transitiveDependencies: result.metrics.transitiveDependencies,
maxDepth: result.metrics.maxDepth,
analysisTime: `${result.analysisTime}ms`,
},
metrics: result.metrics,
security: {
vulnerablePackages: result.securityAudits.filter(a => a.vulnerabilities.length > 0).length,
totalVulnerabilities: result.securityAudits.reduce((sum, audit) => sum + audit.vulnerabilities.length, 0),
averageSecurityScore: result.securityAudits.reduce((sum, audit) => sum + audit.score, 0) / result.securityAudits.length || 100,
},
updates: {
outdatedPackages: result.outdatedPackages.length,
majorUpdates: result.outdatedPackages.filter(p => p.semverDiff === 'major').length,
minorUpdates: result.outdatedPackages.filter(p => p.semverDiff === 'minor').length,
},
issues: {
conflicts: result.conflicts.length,
circularDependencies: result.circularDependencies.length,
},
recommendations: result.recommendations,
vulnerablePackages: result.securityAudits
.filter(audit => audit.vulnerabilities.length > 0)
.map(audit => ({
package: audit.package,
version: audit.version,
vulnerabilities: audit.vulnerabilities,
score: audit.score,
})),
outdatedPackages: result.outdatedPackages,
conflicts: result.conflicts,
circularDependencies: result.circularDependencies,
};
return {
content: [
{
type: 'text',
text: JSON.stringify(formattedResult, null, 2),
},
],
};
}
case 'check_updates': {
const { projectPath, packageName } = CheckUpdatesSchema.parse(args);
logger.info(`Checking updates for project: ${projectPath}${packageName ? ` (package: ${packageName})` : ''}`);
const updates = await dependencyAnalyzer.checkUpdates(projectPath, packageName);
const formattedUpdates = {
projectPath,
packageName: packageName || 'all',
updatesFound: updates.length,
updates: updates.map(update => ({
package: update.package,
currentVersion: update.current,
latestVersion: update.latest,
semverDiff: update.semverDiff,
isBreaking: update.semverDiff === 'major',
releaseDate: undefined, // Not available in UpdateInfo
})),
};
return {
content: [
{
type: 'text',
text: JSON.stringify(formattedUpdates, null, 2),
},
],
};
}
case 'find_conflicts': {
const { projectPath } = FindConflictsSchema.parse(args);
logger.info(`Finding conflicts for project: ${projectPath}`);
const conflicts = await dependencyAnalyzer.findConflicts(projectPath);
const formattedConflicts = {
projectPath,
conflictsFound: conflicts.length,
conflicts: conflicts.map(conflict => ({
package: conflict.package,
versions: conflict.versions,
constraints: conflict.constraints,
resolution: conflict.resolution,
})),
};
return {
content: [
{
type: 'text',
text: JSON.stringify(formattedConflicts, null, 2),
},
],
};
}
case 'suggest_alternatives': {
const { packageName, packageManager } = SuggestAlternativesSchema.parse(args);
logger.info(`Suggesting alternatives for package: ${packageName}`);
const alternatives = await dependencyAnalyzer.suggestAlternatives(packageName, packageManager);
const formattedAlternatives = {
packageName,
packageManager: packageManager || 'auto-detected',
alternativesFound: alternatives.length,
alternatives: alternatives.map(alt => ({
name: alt.name,
description: alt.description,
packageManager: alt.packageManager,
downloads: alt.downloads,
stars: alt.stars,
lastUpdated: alt.lastUpdated,
score: alt.score,
features: alt.features,
})),
};
return {
content: [
{
type: 'text',
text: JSON.stringify(formattedAlternatives, null, 2),
},
],
};
}
case 'security_audit': {
const { projectPath, severity } = SecurityAuditSchema.parse(args);
logger.info(`Performing security audit for project: ${projectPath} (severity: ${severity})`);
const auditResult = await dependencyAnalyzer.securityAudit(projectPath, severity);
const formattedAudit = {
projectPath,
severity,
audits: auditResult.audits.map((audit: any) => ({
package: audit.package,
version: audit.version,
score: audit.score,
vulnerabilities: audit.vulnerabilities,
})),
report: auditResult.report,
};
return {
content: [
{
type: 'text',
text: JSON.stringify(formattedAudit, null, 2),
},
],
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
logger.error(`Error executing tool ${name}:`, error);
return {
content: [
{
type: 'text',
text: JSON.stringify({
error: error instanceof Error ? error.message : 'Unknown error',
tool: name,
}),
},
],
isError: true,
};
}
});
// Start the server
async function main() {
logger.info('Starting Dependency Analysis MCP Server...');
const transport = new StdioServerTransport();
await server.connect(transport);
logger.info('Dependency Analysis MCP Server started successfully');
}
main().catch((error) => {
logger.error('Failed to start server:', error);
process.exit(1);
});