This is page 2 of 3. Use http://codebase.md/thealchemist6/codecompass-mcp?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .dockerignore
├── .env.example
├── .gitignore
├── config
│ ├── .eslintrc.json
│ ├── .prettierignore
│ ├── .prettierrc
│ ├── README.md
│ └── tsconfig.dev.json
├── CONTRIBUTING.md
├── docker
│ ├── docker-compose.dev.yml
│ ├── docker-compose.yml
│ ├── Dockerfile.dev
│ └── README.md
├── Dockerfile
├── docs
│ ├── API.md
│ ├── DOCKER.md
│ ├── legacy-tools
│ │ ├── chat.ts
│ │ ├── extract.ts
│ │ ├── files.ts
│ │ ├── README.md
│ │ ├── refactor.ts
│ │ ├── repository.ts
│ │ ├── template.ts
│ │ └── transform.ts
│ ├── MONITORING.md
│ ├── README.md
│ └── SETUP.md
├── examples
│ ├── basic-usage.js
│ └── basic-usage.md
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── scripts
│ ├── docker-build.sh
│ ├── docker-logs.sh
│ ├── docker-run.sh
│ ├── monitor.js
│ └── start-mcp.sh
├── src
│ ├── index.ts
│ ├── services
│ │ ├── github.ts
│ │ ├── openai.ts
│ │ └── refactor.ts
│ ├── tools
│ │ └── consolidated.ts
│ ├── types
│ │ ├── index.ts
│ │ └── responses.ts
│ └── utils
│ ├── config.ts
│ ├── file-processor.ts
│ ├── logger.ts
│ ├── monitoring.ts
│ ├── security.ts
│ └── validation.ts
├── tests
│ └── verify-installation.sh
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/src/utils/validation.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { ValidationResult } from '../types/index.js';
2 |
3 | export class ValidationService {
4 |
5 | static validateGitHubUrl(url: string): ValidationResult {
6 | const errors: string[] = [];
7 | const warnings: string[] = [];
8 |
9 | if (!url) {
10 | errors.push('URL is required');
11 | return { isValid: false, errors, warnings };
12 | }
13 |
14 | // Check if it's a valid GitHub URL
15 | const githubRegex = /^https:\/\/github\.com\/[\w\-\.]+\/[\w\-\.]+\/?$/;
16 | if (!githubRegex.test(url)) {
17 | errors.push('Invalid GitHub URL format. Expected: https://github.com/owner/repo');
18 | }
19 |
20 | // Check for common issues
21 | if (url.includes('/tree/') || url.includes('/blob/')) {
22 | warnings.push('URL appears to point to a specific file or branch. Consider using the repository root URL.');
23 | }
24 |
25 | if (url.endsWith('.git')) {
26 | warnings.push('URL ends with .git. This may cause issues with the GitHub API.');
27 | }
28 |
29 | return {
30 | isValid: errors.length === 0,
31 | errors,
32 | warnings,
33 | };
34 | }
35 |
36 | static validateFilePath(filePath: string): ValidationResult {
37 | const errors: string[] = [];
38 | const warnings: string[] = [];
39 |
40 | if (!filePath) {
41 | errors.push('File path is required');
42 | return { isValid: false, errors, warnings };
43 | }
44 |
45 | // Check for security issues
46 | if (filePath.includes('..')) {
47 | errors.push('File path contains directory traversal sequences (..)');
48 | }
49 |
50 | if (filePath.startsWith('/')) {
51 | errors.push('File path should not start with /');
52 | }
53 |
54 | // Check for common issues
55 | if (filePath.includes('\\')) {
56 | warnings.push('File path contains backslashes. Use forward slashes instead.');
57 | }
58 |
59 | return {
60 | isValid: errors.length === 0,
61 | errors,
62 | warnings,
63 | };
64 | }
65 |
66 | static validateRefactorOptions(options: any): ValidationResult {
67 | const errors: string[] = [];
68 | const warnings: string[] = [];
69 |
70 | if (!options) {
71 | return { isValid: true, errors, warnings };
72 | }
73 |
74 | // Validate naming convention
75 | if (options.namingConvention) {
76 | const validConventions = ['camelCase', 'snake_case', 'kebab-case', 'PascalCase'];
77 | if (!validConventions.includes(options.namingConvention)) {
78 | errors.push(`Invalid naming convention. Must be one of: ${validConventions.join(', ')}`);
79 | }
80 | }
81 |
82 | // Validate modernization level
83 | if (options.modernizationLevel) {
84 | const validLevels = ['minimal', 'moderate', 'aggressive'];
85 | if (!validLevels.includes(options.modernizationLevel)) {
86 | errors.push(`Invalid modernization level. Must be one of: ${validLevels.join(', ')}`);
87 | }
88 | }
89 |
90 | // Validate target framework
91 | if (options.targetFramework) {
92 | const supportedFrameworks = ['react', 'vue', 'angular', 'express', 'fastify', 'koa'];
93 | if (!supportedFrameworks.includes(options.targetFramework.toLowerCase())) {
94 | warnings.push(`Framework '${options.targetFramework}' may not be fully supported. Supported frameworks: ${supportedFrameworks.join(', ')}`);
95 | }
96 | }
97 |
98 | return {
99 | isValid: errors.length === 0,
100 | errors,
101 | warnings,
102 | };
103 | }
104 |
105 | static validateComponentTypes(types: string[]): ValidationResult {
106 | const errors: string[] = [];
107 | const warnings: string[] = [];
108 |
109 | if (!types || types.length === 0) {
110 | return { isValid: true, errors, warnings };
111 | }
112 |
113 | const validTypes = ['ui-components', 'hooks', 'utilities', 'services', 'models', 'types'];
114 |
115 | for (const type of types) {
116 | if (!validTypes.includes(type)) {
117 | errors.push(`Invalid component type: ${type}. Valid types: ${validTypes.join(', ')}`);
118 | }
119 | }
120 |
121 | return {
122 | isValid: errors.length === 0,
123 | errors,
124 | warnings,
125 | };
126 | }
127 |
128 | static validateLanguage(language: string): ValidationResult {
129 | const errors: string[] = [];
130 | const warnings: string[] = [];
131 |
132 | if (!language) {
133 | errors.push('Language is required');
134 | return { isValid: false, errors, warnings };
135 | }
136 |
137 | const supportedLanguages = [
138 | 'javascript',
139 | 'typescript',
140 | 'python',
141 | 'java',
142 | 'cpp',
143 | 'c',
144 | 'go',
145 | 'rust',
146 | 'php',
147 | 'ruby',
148 | 'swift',
149 | 'kotlin',
150 | 'dart',
151 | ];
152 |
153 | if (!supportedLanguages.includes(language.toLowerCase())) {
154 | warnings.push(`Language '${language}' may not be fully supported. Supported languages: ${supportedLanguages.join(', ')}`);
155 | }
156 |
157 | return {
158 | isValid: errors.length === 0,
159 | errors,
160 | warnings,
161 | };
162 | }
163 |
164 | static validateTemplateOptions(options: any): ValidationResult {
165 | const errors: string[] = [];
166 | const warnings: string[] = [];
167 |
168 | if (!options) {
169 | return { isValid: true, errors, warnings };
170 | }
171 |
172 | // Validate template type
173 | if (options.templateType) {
174 | const validTypes = ['starter', 'component-library', 'microservice', 'fullstack', 'cli-tool', 'library'];
175 | if (!validTypes.includes(options.templateType)) {
176 | errors.push(`Invalid template type: ${options.templateType}. Valid types: ${validTypes.join(', ')}`);
177 | }
178 | }
179 |
180 | // Validate package manager
181 | if (options.packageManager) {
182 | const validManagers = ['npm', 'yarn', 'pnpm', 'bun'];
183 | if (!validManagers.includes(options.packageManager)) {
184 | errors.push(`Invalid package manager: ${options.packageManager}. Valid managers: ${validManagers.join(', ')}`);
185 | }
186 | }
187 |
188 | // Validate name
189 | if (options.name && !/^[a-z0-9-]+$/.test(options.name)) {
190 | errors.push('Template name must contain only lowercase letters, numbers, and hyphens');
191 | }
192 |
193 | return {
194 | isValid: errors.length === 0,
195 | errors,
196 | warnings,
197 | };
198 | }
199 |
200 | static validateSearchQuery(query: string): ValidationResult {
201 | const errors: string[] = [];
202 | const warnings: string[] = [];
203 |
204 | if (!query) {
205 | errors.push('Search query is required');
206 | return { isValid: false, errors, warnings };
207 | }
208 |
209 | if (query.length < 2) {
210 | errors.push('Search query must be at least 2 characters long');
211 | }
212 |
213 | if (query.length > 1000) {
214 | errors.push('Search query is too long (max 1000 characters)');
215 | }
216 |
217 | // Check for potentially problematic regex patterns
218 | try {
219 | new RegExp(query);
220 | } catch (e) {
221 | warnings.push('Search query may contain invalid regex patterns');
222 | }
223 |
224 | return {
225 | isValid: errors.length === 0,
226 | errors,
227 | warnings,
228 | };
229 | }
230 |
231 | static validateCodeInput(code: string): ValidationResult {
232 | const errors: string[] = [];
233 | const warnings: string[] = [];
234 |
235 | if (!code) {
236 | errors.push('Code input is required');
237 | return { isValid: false, errors, warnings };
238 | }
239 |
240 | if (code.length > 1000000) { // 1MB limit
241 | errors.push('Code input is too large (max 1MB)');
242 | }
243 |
244 | // Check for potentially malicious patterns
245 | const maliciousPatterns = [
246 | /eval\s*\(/,
247 | /Function\s*\(/,
248 | /document\.write/,
249 | /innerHTML\s*=/,
250 | /dangerouslySetInnerHTML/,
251 | ];
252 |
253 | for (const pattern of maliciousPatterns) {
254 | if (pattern.test(code)) {
255 | warnings.push('Code contains potentially unsafe patterns');
256 | break;
257 | }
258 | }
259 |
260 | return {
261 | isValid: errors.length === 0,
262 | errors,
263 | warnings,
264 | };
265 | }
266 |
267 | static validateDependencyMappings(mappings: Record<string, string>): ValidationResult {
268 | const errors: string[] = [];
269 | const warnings: string[] = [];
270 |
271 | if (!mappings) {
272 | return { isValid: true, errors, warnings };
273 | }
274 |
275 | for (const [oldDep, newDep] of Object.entries(mappings)) {
276 | if (!oldDep || !newDep) {
277 | errors.push('Dependency mappings must have both old and new values');
278 | continue;
279 | }
280 |
281 | // Check for valid package names
282 | const packageNameRegex = /^[@a-z0-9-~][a-z0-9-._~]*\/[a-z0-9-._~]*$|^[a-z0-9-~][a-z0-9-._~]*$/;
283 |
284 | if (!packageNameRegex.test(oldDep)) {
285 | warnings.push(`Old dependency name '${oldDep}' may not be valid`);
286 | }
287 |
288 | if (!packageNameRegex.test(newDep)) {
289 | warnings.push(`New dependency name '${newDep}' may not be valid`);
290 | }
291 | }
292 |
293 | return {
294 | isValid: errors.length === 0,
295 | errors,
296 | warnings,
297 | };
298 | }
299 |
300 | static validateFileExtensions(extensions: string[]): ValidationResult {
301 | const errors: string[] = [];
302 | const warnings: string[] = [];
303 |
304 | if (!extensions || extensions.length === 0) {
305 | return { isValid: true, errors, warnings };
306 | }
307 |
308 | for (const ext of extensions) {
309 | if (!ext.startsWith('.')) {
310 | errors.push(`File extension '${ext}' must start with a dot`);
311 | }
312 |
313 | if (ext.length < 2) {
314 | errors.push(`File extension '${ext}' is too short`);
315 | }
316 |
317 | if (!/^\.[\w]+$/.test(ext)) {
318 | errors.push(`File extension '${ext}' contains invalid characters`);
319 | }
320 | }
321 |
322 | return {
323 | isValid: errors.length === 0,
324 | errors,
325 | warnings,
326 | };
327 | }
328 | }
```
--------------------------------------------------------------------------------
/docs/legacy-tools/extract.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
2 |
3 | export const extractTools: Tool[] = [
4 | {
5 | name: 'extract_components',
6 | description: 'Extract UI components from a codebase for reuse',
7 | inputSchema: {
8 | type: 'object',
9 | properties: {
10 | url: {
11 | type: 'string',
12 | description: 'GitHub repository URL',
13 | },
14 | componentPaths: {
15 | type: 'array',
16 | items: { type: 'string' },
17 | description: 'Specific component paths to extract (optional)',
18 | },
19 | framework: {
20 | type: 'string',
21 | description: 'UI framework (e.g., "react", "vue", "angular")',
22 | },
23 | extractionOptions: {
24 | type: 'object',
25 | properties: {
26 | includeStyles: {
27 | type: 'boolean',
28 | description: 'Include component styles',
29 | default: true,
30 | },
31 | includeTests: {
32 | type: 'boolean',
33 | description: 'Include component tests',
34 | default: true,
35 | },
36 | includeDocs: {
37 | type: 'boolean',
38 | description: 'Include component documentation',
39 | default: true,
40 | },
41 | minComplexity: {
42 | type: 'number',
43 | description: 'Minimum complexity score for extraction',
44 | default: 3,
45 | },
46 | maxDependencies: {
47 | type: 'number',
48 | description: 'Maximum number of dependencies',
49 | default: 5,
50 | },
51 | },
52 | },
53 | },
54 | required: ['url', 'framework'],
55 | },
56 | },
57 | {
58 | name: 'extract_utilities',
59 | description: 'Extract utility functions and helpers from a codebase',
60 | inputSchema: {
61 | type: 'object',
62 | properties: {
63 | url: {
64 | type: 'string',
65 | description: 'GitHub repository URL',
66 | },
67 | utilityTypes: {
68 | type: 'array',
69 | items: {
70 | type: 'string',
71 | enum: ['validation', 'formatting', 'data-processing', 'math', 'date', 'string', 'array', 'object'],
72 | },
73 | description: 'Types of utilities to extract',
74 | },
75 | extractionCriteria: {
76 | type: 'object',
77 | properties: {
78 | minReusability: {
79 | type: 'number',
80 | description: 'Minimum reusability score (0-100)',
81 | default: 70,
82 | },
83 | maxComplexity: {
84 | type: 'number',
85 | description: 'Maximum complexity score',
86 | default: 10,
87 | },
88 | requireTests: {
89 | type: 'boolean',
90 | description: 'Only extract utilities with tests',
91 | default: false,
92 | },
93 | requireDocumentation: {
94 | type: 'boolean',
95 | description: 'Only extract utilities with documentation',
96 | default: false,
97 | },
98 | },
99 | },
100 | },
101 | required: ['url'],
102 | },
103 | },
104 | {
105 | name: 'extract_hooks',
106 | description: 'Extract React hooks or similar patterns from a codebase',
107 | inputSchema: {
108 | type: 'object',
109 | properties: {
110 | url: {
111 | type: 'string',
112 | description: 'GitHub repository URL',
113 | },
114 | hookTypes: {
115 | type: 'array',
116 | items: {
117 | type: 'string',
118 | enum: ['state', 'effect', 'context', 'custom', 'data-fetching', 'form'],
119 | },
120 | description: 'Types of hooks to extract',
121 | },
122 | framework: {
123 | type: 'string',
124 | description: 'Framework (e.g., "react", "vue-composition")',
125 | },
126 | extractionOptions: {
127 | type: 'object',
128 | properties: {
129 | includeTypes: {
130 | type: 'boolean',
131 | description: 'Include TypeScript types',
132 | default: true,
133 | },
134 | includeExamples: {
135 | type: 'boolean',
136 | description: 'Include usage examples',
137 | default: true,
138 | },
139 | removeDependencies: {
140 | type: 'boolean',
141 | description: 'Remove external dependencies',
142 | default: true,
143 | },
144 | },
145 | },
146 | },
147 | required: ['url', 'framework'],
148 | },
149 | },
150 | {
151 | name: 'extract_types',
152 | description: 'Extract TypeScript types and interfaces from a codebase',
153 | inputSchema: {
154 | type: 'object',
155 | properties: {
156 | url: {
157 | type: 'string',
158 | description: 'GitHub repository URL',
159 | },
160 | typeCategories: {
161 | type: 'array',
162 | items: {
163 | type: 'string',
164 | enum: ['api', 'domain', 'ui', 'utility', 'config', 'generic'],
165 | },
166 | description: 'Categories of types to extract',
167 | },
168 | extractionOptions: {
169 | type: 'object',
170 | properties: {
171 | includeGenerics: {
172 | type: 'boolean',
173 | description: 'Include generic types',
174 | default: true,
175 | },
176 | includeUnions: {
177 | type: 'boolean',
178 | description: 'Include union types',
179 | default: true,
180 | },
181 | includeComments: {
182 | type: 'boolean',
183 | description: 'Include type comments/documentation',
184 | default: true,
185 | },
186 | resolveImports: {
187 | type: 'boolean',
188 | description: 'Resolve imported types',
189 | default: true,
190 | },
191 | },
192 | },
193 | },
194 | required: ['url'],
195 | },
196 | },
197 | {
198 | name: 'extract_api_definitions',
199 | description: 'Extract API definitions and schemas from a codebase',
200 | inputSchema: {
201 | type: 'object',
202 | properties: {
203 | url: {
204 | type: 'string',
205 | description: 'GitHub repository URL',
206 | },
207 | apiType: {
208 | type: 'string',
209 | enum: ['rest', 'graphql', 'rpc', 'websocket'],
210 | description: 'Type of API',
211 | },
212 | extractionOptions: {
213 | type: 'object',
214 | properties: {
215 | includeSchemas: {
216 | type: 'boolean',
217 | description: 'Include request/response schemas',
218 | default: true,
219 | },
220 | includeValidation: {
221 | type: 'boolean',
222 | description: 'Include validation rules',
223 | default: true,
224 | },
225 | includeDocumentation: {
226 | type: 'boolean',
227 | description: 'Include API documentation',
228 | default: true,
229 | },
230 | generateOpenAPI: {
231 | type: 'boolean',
232 | description: 'Generate OpenAPI specification',
233 | default: false,
234 | },
235 | },
236 | },
237 | },
238 | required: ['url', 'apiType'],
239 | },
240 | },
241 | {
242 | name: 'extract_configuration',
243 | description: 'Extract configuration patterns and settings from a codebase',
244 | inputSchema: {
245 | type: 'object',
246 | properties: {
247 | url: {
248 | type: 'string',
249 | description: 'GitHub repository URL',
250 | },
251 | configTypes: {
252 | type: 'array',
253 | items: {
254 | type: 'string',
255 | enum: ['env', 'build', 'runtime', 'database', 'api', 'feature-flags'],
256 | },
257 | description: 'Types of configuration to extract',
258 | },
259 | extractionOptions: {
260 | type: 'object',
261 | properties: {
262 | includeDefaults: {
263 | type: 'boolean',
264 | description: 'Include default values',
265 | default: true,
266 | },
267 | includeValidation: {
268 | type: 'boolean',
269 | description: 'Include configuration validation',
270 | default: true,
271 | },
272 | includeDocumentation: {
273 | type: 'boolean',
274 | description: 'Include configuration documentation',
275 | default: true,
276 | },
277 | createSchema: {
278 | type: 'boolean',
279 | description: 'Create configuration schema',
280 | default: true,
281 | },
282 | },
283 | },
284 | },
285 | required: ['url'],
286 | },
287 | },
288 | {
289 | name: 'extract_patterns',
290 | description: 'Extract design patterns and architectural patterns from a codebase',
291 | inputSchema: {
292 | type: 'object',
293 | properties: {
294 | url: {
295 | type: 'string',
296 | description: 'GitHub repository URL',
297 | },
298 | patternTypes: {
299 | type: 'array',
300 | items: {
301 | type: 'string',
302 | enum: ['mvc', 'mvvm', 'observer', 'factory', 'singleton', 'strategy', 'decorator', 'adapter'],
303 | },
304 | description: 'Types of patterns to extract',
305 | },
306 | extractionOptions: {
307 | type: 'object',
308 | properties: {
309 | includeExamples: {
310 | type: 'boolean',
311 | description: 'Include usage examples',
312 | default: true,
313 | },
314 | includeTests: {
315 | type: 'boolean',
316 | description: 'Include pattern tests',
317 | default: true,
318 | },
319 | includeDocumentation: {
320 | type: 'boolean',
321 | description: 'Include pattern documentation',
322 | default: true,
323 | },
324 | createGeneric: {
325 | type: 'boolean',
326 | description: 'Create generic implementation',
327 | default: true,
328 | },
329 | },
330 | },
331 | },
332 | required: ['url'],
333 | },
334 | },
335 | ];
```
--------------------------------------------------------------------------------
/docs/DOCKER.md:
--------------------------------------------------------------------------------
```markdown
1 | # Docker Deployment Guide
2 |
3 | This guide covers how to deploy CodeCompass MCP using Docker containers, including development and production configurations.
4 |
5 | ## Quick Start
6 |
7 | ### Building the Image
8 |
9 | ```bash
10 | # Build production image
11 | ./scripts/docker-build.sh
12 |
13 | # Build development image
14 | ./scripts/docker-build.sh --dev
15 |
16 | # Build with custom tag
17 | ./scripts/docker-build.sh -t v1.0.0
18 | ```
19 |
20 | ### Running the Container
21 |
22 | ```bash
23 | # Run with defaults
24 | ./scripts/docker-run.sh
25 |
26 | # Run with environment variables
27 | ./scripts/docker-run.sh -e GITHUB_TOKEN=your_token -e OPENROUTER_API_KEY=your_key
28 |
29 | # Run with environment file
30 | ./scripts/docker-run.sh --env-file .env
31 |
32 | # Run interactively
33 | ./scripts/docker-run.sh --interactive
34 | ```
35 |
36 | ### Viewing Logs
37 |
38 | ```bash
39 | # View last 100 lines
40 | ./scripts/docker-logs.sh
41 |
42 | # Follow logs
43 | ./scripts/docker-logs.sh -f
44 |
45 | # View all logs with timestamps
46 | ./scripts/docker-logs.sh --all --timestamps
47 | ```
48 |
49 | ## Docker Images
50 |
51 | ### Production Image (`Dockerfile`)
52 |
53 | The production image is optimized for:
54 | - Minimal size using Alpine Linux
55 | - Non-root user for security
56 | - Multi-stage build for efficiency
57 | - Health checks for monitoring
58 | - Proper signal handling
59 |
60 | Key features:
61 | - Based on `node:18-alpine`
62 | - Runs as non-root user `mcpuser`
63 | - Includes health check endpoint
64 | - Optimized for container environments
65 |
66 | ### Development Image (`Dockerfile.dev`)
67 |
68 | The development image includes:
69 | - Source code mounted as volume
70 | - Development dependencies
71 | - Hot reloading support
72 | - Debug tools
73 | - Interactive shell access
74 |
75 | ## Environment Variables
76 |
77 | ### Required Variables
78 |
79 | - `GITHUB_TOKEN`: GitHub personal access token for API access
80 | - `OPENROUTER_API_KEY`: OpenRouter API key for AI features
81 |
82 | ### Optional Variables
83 |
84 | - `NODE_ENV`: Environment (development/production)
85 | - `LOG_LEVEL`: Logging level (debug/info/warn/error)
86 | - `MAX_RESPONSE_TOKENS`: Maximum response size
87 | - `MAX_FILE_CONTENT_LENGTH`: Maximum file content size
88 | - `RATE_LIMIT_REQUESTS`: Rate limit for requests
89 | - `RATE_LIMIT_WINDOW`: Rate limit window in seconds
90 |
91 | ### Configuration Example
92 |
93 | Create a `.env` file:
94 |
95 | ```bash
96 | # Required
97 | GITHUB_TOKEN=ghp_your_github_token_here
98 | OPENROUTER_API_KEY=sk-or-your_openrouter_key_here
99 |
100 | # Optional
101 | NODE_ENV=production
102 | LOG_LEVEL=info
103 | MAX_RESPONSE_TOKENS=25000
104 | MAX_FILE_CONTENT_LENGTH=5000
105 | RATE_LIMIT_REQUESTS=100
106 | RATE_LIMIT_WINDOW=3600
107 | ```
108 |
109 | ## Docker Compose
110 |
111 | Use Docker Compose for more complex deployments:
112 |
113 | ```bash
114 | # Start services
115 | docker-compose up -d
116 |
117 | # View logs
118 | docker-compose logs -f
119 |
120 | # Stop services
121 | docker-compose down
122 | ```
123 |
124 | The `docker-compose.yml` includes:
125 | - CodeCompass MCP service
126 | - Environment configuration
127 | - Volume mounts
128 | - Health checks
129 | - Resource limits
130 | - Logging configuration
131 |
132 | ### Production Docker Compose
133 |
134 | For production with external logging:
135 |
136 | ```yaml
137 | version: '3.8'
138 |
139 | services:
140 | codecompass-mcp:
141 | image: codecompass-mcp:latest
142 | container_name: codecompass-mcp-prod
143 | restart: unless-stopped
144 | env_file: .env.production
145 | environment:
146 | - NODE_ENV=production
147 | - LOG_LEVEL=info
148 | healthcheck:
149 | test: ["CMD", "node", "-e", "console.log('Health check')"]
150 | interval: 30s
151 | timeout: 10s
152 | retries: 3
153 | start_period: 10s
154 | resources:
155 | limits:
156 | memory: 512M
157 | cpus: '0.5'
158 | reservations:
159 | memory: 256M
160 | cpus: '0.25'
161 | logging:
162 | driver: json-file
163 | options:
164 | max-size: 10m
165 | max-file: 3
166 | networks:
167 | - codecompass-network
168 |
169 | networks:
170 | codecompass-network:
171 | driver: bridge
172 | ```
173 |
174 | ## Scripts Overview
175 |
176 | ### `docker-build.sh`
177 |
178 | Builds Docker images with various options:
179 |
180 | ```bash
181 | # Available options
182 | ./scripts/docker-build.sh --help
183 |
184 | # Examples
185 | ./scripts/docker-build.sh # Default build
186 | ./scripts/docker-build.sh --dev # Development build
187 | ./scripts/docker-build.sh -t v1.0.0 --push # Build and push
188 | ./scripts/docker-build.sh --build-arg NODE_ENV=production
189 | ```
190 |
191 | ### `docker-run.sh`
192 |
193 | Runs containers with flexible configuration:
194 |
195 | ```bash
196 | # Available options
197 | ./scripts/docker-run.sh --help
198 |
199 | # Examples
200 | ./scripts/docker-run.sh # Default run
201 | ./scripts/docker-run.sh --interactive # Interactive mode
202 | ./scripts/docker-run.sh --foreground # Foreground mode
203 | ./scripts/docker-run.sh --remove-existing # Remove existing container
204 | ./scripts/docker-run.sh -v /host/data:/app/data # Mount volume
205 | ```
206 |
207 | ### `docker-logs.sh`
208 |
209 | Views container logs with various options:
210 |
211 | ```bash
212 | # Available options
213 | ./scripts/docker-logs.sh --help
214 |
215 | # Examples
216 | ./scripts/docker-logs.sh # Last 100 lines
217 | ./scripts/docker-logs.sh -f # Follow logs
218 | ./scripts/docker-logs.sh --timestamps # Show timestamps
219 | ./scripts/docker-logs.sh --all # Show all logs
220 | ```
221 |
222 | ## Development Workflow
223 |
224 | ### 1. Development Setup
225 |
226 | ```bash
227 | # Build development image
228 | ./scripts/docker-build.sh --dev
229 |
230 | # Run in development mode
231 | ./scripts/docker-run.sh --interactive -v $(pwd):/app
232 |
233 | # Or use docker-compose
234 | docker-compose -f docker-compose.dev.yml up
235 | ```
236 |
237 | ### 2. Testing Changes
238 |
239 | ```bash
240 | # Run tests in container
241 | docker exec -it codecompass-mcp npm test
242 |
243 | # Run linting
244 | docker exec -it codecompass-mcp npm run lint
245 |
246 | # Run type checking
247 | docker exec -it codecompass-mcp npm run type-check
248 | ```
249 |
250 | ### 3. Production Deployment
251 |
252 | ```bash
253 | # Build production image
254 | ./scripts/docker-build.sh -t production
255 |
256 | # Run with production configuration
257 | ./scripts/docker-run.sh -i codecompass-mcp:production --env-file .env.production
258 |
259 | # Or use docker-compose
260 | docker-compose -f docker-compose.prod.yml up -d
261 | ```
262 |
263 | ## Security Best Practices
264 |
265 | ### 1. Non-Root User
266 |
267 | Both images run as non-root user `mcpuser` (UID 1000):
268 |
269 | ```dockerfile
270 | # Create non-root user
271 | RUN addgroup -g 1000 mcpuser && \
272 | adduser -D -s /bin/sh -u 1000 -G mcpuser mcpuser
273 |
274 | # Switch to non-root user
275 | USER mcpuser
276 | ```
277 |
278 | ### 2. Environment Variables
279 |
280 | - Never commit secrets to version control
281 | - Use `.env` files for local development
282 | - Use container orchestration secrets for production
283 | - Rotate API keys regularly
284 |
285 | ### 3. Network Security
286 |
287 | - Use custom networks for container isolation
288 | - Expose only necessary ports
289 | - Use reverse proxy for external access
290 | - Enable TLS termination at load balancer
291 |
292 | ### 4. Resource Limits
293 |
294 | Always set resource limits:
295 |
296 | ```yaml
297 | resources:
298 | limits:
299 | memory: 512M
300 | cpus: '0.5'
301 | reservations:
302 | memory: 256M
303 | cpus: '0.25'
304 | ```
305 |
306 | ## Monitoring and Observability
307 |
308 | ### Health Checks
309 |
310 | Health checks are built into the Docker images:
311 |
312 | ```bash
313 | # Manual health check
314 | docker exec codecompass-mcp node -e "console.log('Health check')"
315 |
316 | # Docker health status
317 | docker inspect codecompass-mcp | grep -A 10 Health
318 | ```
319 |
320 | ### Logging
321 |
322 | Structured logging is configured for container environments:
323 |
324 | ```bash
325 | # View structured logs
326 | docker logs codecompass-mcp | jq .
327 |
328 | # Filter by log level
329 | docker logs codecompass-mcp | jq 'select(.level == "ERROR")'
330 |
331 | # Follow logs with timestamps
332 | docker logs -f --timestamps codecompass-mcp
333 | ```
334 |
335 | ### Metrics
336 |
337 | The logger provides basic metrics:
338 |
339 | ```bash
340 | # Get server stats (if exposed)
341 | docker exec codecompass-mcp node -e "
342 | import('./build/utils/logger.js').then(m =>
343 | console.log(JSON.stringify(m.log.getStats(), null, 2))
344 | )
345 | "
346 | ```
347 |
348 | ## Troubleshooting
349 |
350 | ### Common Issues
351 |
352 | 1. **Container won't start**
353 | ```bash
354 | # Check logs
355 | ./scripts/docker-logs.sh
356 |
357 | # Check container status
358 | docker ps -a
359 |
360 | # Inspect container
361 | docker inspect codecompass-mcp
362 | ```
363 |
364 | 2. **Environment variables not loaded**
365 | ```bash
366 | # Check environment in container
367 | docker exec codecompass-mcp env | grep -E "(GITHUB|OPENROUTER)"
368 |
369 | # Check .env file format
370 | cat .env
371 | ```
372 |
373 | 3. **Permission errors**
374 | ```bash
375 | # Check file permissions
376 | ls -la
377 |
378 | # Fix script permissions
379 | chmod +x scripts/*.sh
380 | ```
381 |
382 | 4. **Memory issues**
383 | ```bash
384 | # Check memory usage
385 | docker stats codecompass-mcp
386 |
387 | # Adjust memory limits
388 | docker update --memory=1g codecompass-mcp
389 | ```
390 |
391 | ### Debug Mode
392 |
393 | Run container in debug mode:
394 |
395 | ```bash
396 | # Interactive debug session
397 | ./scripts/docker-run.sh --interactive
398 |
399 | # Or with debug logging
400 | ./scripts/docker-run.sh -e LOG_LEVEL=debug
401 | ```
402 |
403 | ## Advanced Configuration
404 |
405 | ### Multi-Stage Builds
406 |
407 | The production Dockerfile uses multi-stage builds:
408 |
409 | ```dockerfile
410 | # Build stage
411 | FROM node:18-alpine AS builder
412 | WORKDIR /app
413 | COPY package*.json ./
414 | RUN npm ci --only=production
415 |
416 | # Runtime stage
417 | FROM node:18-alpine AS runtime
418 | WORKDIR /app
419 | COPY --from=builder /app/node_modules ./node_modules
420 | COPY . .
421 | ```
422 |
423 | ### Container Orchestration
424 |
425 | For Kubernetes deployment:
426 |
427 | ```yaml
428 | apiVersion: apps/v1
429 | kind: Deployment
430 | metadata:
431 | name: codecompass-mcp
432 | spec:
433 | replicas: 2
434 | selector:
435 | matchLabels:
436 | app: codecompass-mcp
437 | template:
438 | metadata:
439 | labels:
440 | app: codecompass-mcp
441 | spec:
442 | containers:
443 | - name: codecompass-mcp
444 | image: codecompass-mcp:latest
445 | ports:
446 | - containerPort: 3000
447 | env:
448 | - name: GITHUB_TOKEN
449 | valueFrom:
450 | secretKeyRef:
451 | name: codecompass-secrets
452 | key: github-token
453 | - name: OPENROUTER_API_KEY
454 | valueFrom:
455 | secretKeyRef:
456 | name: codecompass-secrets
457 | key: openrouter-api-key
458 | resources:
459 | requests:
460 | memory: "256Mi"
461 | cpu: "250m"
462 | limits:
463 | memory: "512Mi"
464 | cpu: "500m"
465 | ```
466 |
467 | ## References
468 |
469 | - [Docker Best Practices](https://docs.docker.com/develop/dev-best-practices/)
470 | - [Docker Compose Documentation](https://docs.docker.com/compose/)
471 | - [Node.js Docker Guide](https://nodejs.org/en/docs/guides/nodejs-docker-webapp/)
472 | - [Container Security Guide](https://docs.docker.com/engine/security/)
```
--------------------------------------------------------------------------------
/docs/legacy-tools/chat.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
2 |
3 | export const chatTools: Tool[] = [
4 | {
5 | name: 'chat_with_repository',
6 | description: 'Interactive chat about repository code, patterns, and refactoring opportunities',
7 | inputSchema: {
8 | type: 'object',
9 | properties: {
10 | url: {
11 | type: 'string',
12 | description: 'GitHub repository URL',
13 | },
14 | message: {
15 | type: 'string',
16 | description: 'Message or question about the repository',
17 | },
18 | context: {
19 | type: 'object',
20 | properties: {
21 | currentFile: {
22 | type: 'string',
23 | description: 'Currently focused file path',
24 | },
25 | selectedCode: {
26 | type: 'string',
27 | description: 'Selected code snippet',
28 | },
29 | refactoringGoals: {
30 | type: 'array',
31 | items: { type: 'string' },
32 | description: 'Current refactoring goals',
33 | },
34 | previousMessages: {
35 | type: 'array',
36 | items: {
37 | type: 'object',
38 | properties: {
39 | role: { type: 'string' },
40 | content: { type: 'string' },
41 | timestamp: { type: 'string' },
42 | },
43 | required: ['role', 'content'],
44 | },
45 | description: 'Previous conversation messages',
46 | },
47 | },
48 | },
49 | options: {
50 | type: 'object',
51 | properties: {
52 | includeCodeSuggestions: {
53 | type: 'boolean',
54 | description: 'Include code suggestions in response',
55 | default: true,
56 | },
57 | includePatternAnalysis: {
58 | type: 'boolean',
59 | description: 'Include pattern analysis',
60 | default: true,
61 | },
62 | includeRefactoringTips: {
63 | type: 'boolean',
64 | description: 'Include refactoring tips',
65 | default: true,
66 | },
67 | focusArea: {
68 | type: 'string',
69 | enum: ['architecture', 'performance', 'maintainability', 'testing', 'security'],
70 | description: 'Area to focus the discussion on',
71 | },
72 | },
73 | },
74 | },
75 | required: ['url', 'message'],
76 | },
77 | },
78 | {
79 | name: 'suggest_refactoring_plan',
80 | description: 'Generate AI-powered refactoring plan based on repository analysis',
81 | inputSchema: {
82 | type: 'object',
83 | properties: {
84 | url: {
85 | type: 'string',
86 | description: 'GitHub repository URL',
87 | },
88 | targetProject: {
89 | type: 'object',
90 | properties: {
91 | framework: {
92 | type: 'string',
93 | description: 'Target framework',
94 | },
95 | language: {
96 | type: 'string',
97 | description: 'Target language',
98 | },
99 | constraints: {
100 | type: 'array',
101 | items: { type: 'string' },
102 | description: 'Project constraints',
103 | },
104 | timeline: {
105 | type: 'string',
106 | description: 'Timeline for refactoring (e.g., "2 weeks", "1 month")',
107 | },
108 | },
109 | required: ['framework'],
110 | },
111 | goals: {
112 | type: 'array',
113 | items: {
114 | type: 'string',
115 | enum: ['modernize', 'extract-components', 'improve-performance', 'add-types', 'improve-testing', 'reduce-complexity'],
116 | },
117 | description: 'Refactoring goals',
118 | },
119 | options: {
120 | type: 'object',
121 | properties: {
122 | includeRisks: {
123 | type: 'boolean',
124 | description: 'Include risk analysis',
125 | default: true,
126 | },
127 | includeTimeline: {
128 | type: 'boolean',
129 | description: 'Include timeline estimates',
130 | default: true,
131 | },
132 | includePriorities: {
133 | type: 'boolean',
134 | description: 'Include priority recommendations',
135 | default: true,
136 | },
137 | includeResources: {
138 | type: 'boolean',
139 | description: 'Include resource recommendations',
140 | default: true,
141 | },
142 | },
143 | },
144 | },
145 | required: ['url', 'targetProject'],
146 | },
147 | },
148 | {
149 | name: 'explain_architecture',
150 | description: 'Generate detailed explanation of repository architecture and patterns',
151 | inputSchema: {
152 | type: 'object',
153 | properties: {
154 | url: {
155 | type: 'string',
156 | description: 'GitHub repository URL',
157 | },
158 | focusAreas: {
159 | type: 'array',
160 | items: {
161 | type: 'string',
162 | enum: ['overall', 'frontend', 'backend', 'database', 'api', 'testing', 'deployment', 'security'],
163 | },
164 | description: 'Areas to focus the explanation on',
165 | },
166 | audienceLevel: {
167 | type: 'string',
168 | enum: ['beginner', 'intermediate', 'advanced'],
169 | description: 'Target audience level',
170 | default: 'intermediate',
171 | },
172 | explanationOptions: {
173 | type: 'object',
174 | properties: {
175 | includeCodeExamples: {
176 | type: 'boolean',
177 | description: 'Include code examples',
178 | default: true,
179 | },
180 | includeDiagrams: {
181 | type: 'boolean',
182 | description: 'Include ASCII diagrams',
183 | default: true,
184 | },
185 | includePatterns: {
186 | type: 'boolean',
187 | description: 'Include pattern explanations',
188 | default: true,
189 | },
190 | includeRecommendations: {
191 | type: 'boolean',
192 | description: 'Include improvement recommendations',
193 | default: true,
194 | },
195 | includeComparisons: {
196 | type: 'boolean',
197 | description: 'Include comparisons with alternatives',
198 | default: false,
199 | },
200 | },
201 | },
202 | },
203 | required: ['url'],
204 | },
205 | },
206 | {
207 | name: 'generate_code_review',
208 | description: 'Generate comprehensive code review with refactoring suggestions',
209 | inputSchema: {
210 | type: 'object',
211 | properties: {
212 | url: {
213 | type: 'string',
214 | description: 'GitHub repository URL',
215 | },
216 | filePaths: {
217 | type: 'array',
218 | items: { type: 'string' },
219 | description: 'Specific files to review (optional - reviews all if not specified)',
220 | },
221 | reviewCriteria: {
222 | type: 'object',
223 | properties: {
224 | checkComplexity: {
225 | type: 'boolean',
226 | description: 'Check code complexity',
227 | default: true,
228 | },
229 | checkPatterns: {
230 | type: 'boolean',
231 | description: 'Check for anti-patterns',
232 | default: true,
233 | },
234 | checkSecurity: {
235 | type: 'boolean',
236 | description: 'Check for security issues',
237 | default: true,
238 | },
239 | checkPerformance: {
240 | type: 'boolean',
241 | description: 'Check for performance issues',
242 | default: true,
243 | },
244 | checkMaintainability: {
245 | type: 'boolean',
246 | description: 'Check maintainability',
247 | default: true,
248 | },
249 | checkTestability: {
250 | type: 'boolean',
251 | description: 'Check testability',
252 | default: true,
253 | },
254 | },
255 | },
256 | outputFormat: {
257 | type: 'string',
258 | enum: ['detailed', 'summary', 'checklist', 'markdown'],
259 | description: 'Output format for review',
260 | default: 'detailed',
261 | },
262 | },
263 | required: ['url'],
264 | },
265 | },
266 | {
267 | name: 'ask_about_code',
268 | description: 'Ask specific questions about code functionality, patterns, or implementation',
269 | inputSchema: {
270 | type: 'object',
271 | properties: {
272 | url: {
273 | type: 'string',
274 | description: 'GitHub repository URL',
275 | },
276 | question: {
277 | type: 'string',
278 | description: 'Specific question about the code',
279 | },
280 | scope: {
281 | type: 'object',
282 | properties: {
283 | filePath: {
284 | type: 'string',
285 | description: 'Specific file to focus on',
286 | },
287 | functionName: {
288 | type: 'string',
289 | description: 'Specific function to focus on',
290 | },
291 | lineRange: {
292 | type: 'object',
293 | properties: {
294 | start: { type: 'number' },
295 | end: { type: 'number' },
296 | },
297 | description: 'Line range to focus on',
298 | },
299 | },
300 | },
301 | responseStyle: {
302 | type: 'string',
303 | enum: ['explanation', 'tutorial', 'reference', 'troubleshooting'],
304 | description: 'Style of response',
305 | default: 'explanation',
306 | },
307 | },
308 | required: ['url', 'question'],
309 | },
310 | },
311 | {
312 | name: 'compare_implementations',
313 | description: 'Compare different implementations and suggest the best approach',
314 | inputSchema: {
315 | type: 'object',
316 | properties: {
317 | implementations: {
318 | type: 'array',
319 | items: {
320 | type: 'object',
321 | properties: {
322 | name: { type: 'string' },
323 | url: { type: 'string' },
324 | filePath: { type: 'string' },
325 | description: { type: 'string' },
326 | },
327 | required: ['name', 'url', 'filePath'],
328 | },
329 | description: 'Different implementations to compare',
330 | },
331 | comparisonCriteria: {
332 | type: 'array',
333 | items: {
334 | type: 'string',
335 | enum: ['performance', 'maintainability', 'readability', 'testability', 'security', 'scalability'],
336 | },
337 | description: 'Criteria for comparison',
338 | },
339 | targetUseCase: {
340 | type: 'string',
341 | description: 'Target use case for the implementation',
342 | },
343 | outputFormat: {
344 | type: 'string',
345 | enum: ['table', 'detailed', 'summary', 'recommendations'],
346 | description: 'Format for comparison output',
347 | default: 'detailed',
348 | },
349 | },
350 | required: ['implementations'],
351 | },
352 | },
353 | ];
```
--------------------------------------------------------------------------------
/src/utils/monitoring.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { log } from './logger.js';
2 | import { getConfig } from './config.js';
3 |
4 | // Monitoring metrics interface
5 | export interface ServerMetrics {
6 | uptime: number;
7 | memory: NodeJS.MemoryUsage;
8 | requestCount: number;
9 | errorCount: number;
10 | responseTime: {
11 | average: number;
12 | min: number;
13 | max: number;
14 | percentile95: number;
15 | };
16 | toolUsage: Record<string, number>;
17 | lastRequestTime: number;
18 | startTime: number;
19 | }
20 |
21 | // Request metrics tracking
22 | interface RequestMetric {
23 | timestamp: number;
24 | duration: number;
25 | tool: string;
26 | success: boolean;
27 | error?: string;
28 | }
29 |
30 | class MonitoringService {
31 | private metrics: ServerMetrics;
32 | private requestMetrics: RequestMetric[] = [];
33 | private maxRequestMetrics = 1000;
34 | private requestIdCounter = 0;
35 | private config = getConfig();
36 |
37 | constructor() {
38 | this.metrics = {
39 | uptime: 0,
40 | memory: process.memoryUsage(),
41 | requestCount: 0,
42 | errorCount: 0,
43 | responseTime: {
44 | average: 0,
45 | min: 0,
46 | max: 0,
47 | percentile95: 0,
48 | },
49 | toolUsage: {},
50 | lastRequestTime: 0,
51 | startTime: Date.now(),
52 | };
53 |
54 | // Update metrics periodically
55 | setInterval(() => this.updateMetrics(), 60000); // Every minute
56 | }
57 |
58 | // Generate unique request ID
59 | generateRequestId(): string {
60 | return `req-${Date.now()}-${++this.requestIdCounter}`;
61 | }
62 |
63 | // Track request start
64 | startRequest(tool: string, requestId?: string): string {
65 | const id = requestId || this.generateRequestId();
66 | this.metrics.requestCount++;
67 | this.metrics.lastRequestTime = Date.now();
68 |
69 | if (!this.metrics.toolUsage[tool]) {
70 | this.metrics.toolUsage[tool] = 0;
71 | }
72 | this.metrics.toolUsage[tool]++;
73 |
74 | log.info(`Request started: ${tool}`, { tool, requestId: id });
75 | return id;
76 | }
77 |
78 | // Track request completion
79 | completeRequest(tool: string, startTime: number, success: boolean, error?: string, requestId?: string): void {
80 | const duration = Date.now() - startTime;
81 |
82 | // Add to request metrics
83 | this.requestMetrics.push({
84 | timestamp: Date.now(),
85 | duration,
86 | tool,
87 | success,
88 | error,
89 | });
90 |
91 | // Keep only recent metrics
92 | if (this.requestMetrics.length > this.maxRequestMetrics) {
93 | this.requestMetrics.shift();
94 | }
95 |
96 | // Update error count
97 | if (!success) {
98 | this.metrics.errorCount++;
99 | }
100 |
101 | // Update response time metrics
102 | this.updateResponseTimeMetrics();
103 |
104 | log.info(`Request completed: ${tool}`, {
105 | tool,
106 | duration,
107 | success,
108 | error,
109 | requestId,
110 | });
111 | }
112 |
113 | // Update response time metrics
114 | private updateResponseTimeMetrics(): void {
115 | if (this.requestMetrics.length === 0) return;
116 |
117 | const durations = this.requestMetrics.map(m => m.duration).sort((a, b) => a - b);
118 | const sum = durations.reduce((a, b) => a + b, 0);
119 |
120 | this.metrics.responseTime = {
121 | average: Math.round(sum / durations.length),
122 | min: durations[0],
123 | max: durations[durations.length - 1],
124 | percentile95: durations[Math.floor(durations.length * 0.95)],
125 | };
126 | }
127 |
128 | // Update general metrics
129 | private updateMetrics(): void {
130 | this.metrics.uptime = Date.now() - this.metrics.startTime;
131 | this.metrics.memory = process.memoryUsage();
132 |
133 | log.debug('Metrics updated', {
134 | uptime: this.metrics.uptime,
135 | memory: this.metrics.memory,
136 | requestCount: this.metrics.requestCount,
137 | errorCount: this.metrics.errorCount,
138 | });
139 | }
140 |
141 | // Get current metrics
142 | getMetrics(): ServerMetrics {
143 | this.updateMetrics();
144 | return { ...this.metrics };
145 | }
146 |
147 | // Get health status
148 | getHealthStatus(): {
149 | status: 'healthy' | 'degraded' | 'unhealthy';
150 | checks: Record<string, any>;
151 | metrics: ServerMetrics;
152 | } {
153 | const metrics = this.getMetrics();
154 | const memoryUsage = metrics.memory.heapUsed / metrics.memory.heapTotal;
155 | const errorRate = metrics.requestCount > 0 ? metrics.errorCount / metrics.requestCount : 0;
156 | const recentErrors = this.requestMetrics
157 | .filter(m => m.timestamp > Date.now() - 300000 && !m.success) // Last 5 minutes
158 | .length;
159 |
160 | const checks = {
161 | memory: {
162 | status: memoryUsage < 0.8 ? 'healthy' : memoryUsage < 0.9 ? 'degraded' : 'unhealthy',
163 | usage: Math.round(memoryUsage * 100),
164 | limit: 90,
165 | },
166 | errorRate: {
167 | status: errorRate < 0.05 ? 'healthy' : errorRate < 0.1 ? 'degraded' : 'unhealthy',
168 | rate: Math.round(errorRate * 100),
169 | limit: 10,
170 | },
171 | recentErrors: {
172 | status: recentErrors < 5 ? 'healthy' : recentErrors < 10 ? 'degraded' : 'unhealthy',
173 | count: recentErrors,
174 | limit: 10,
175 | },
176 | responseTime: {
177 | status: metrics.responseTime.average < 5000 ? 'healthy' : metrics.responseTime.average < 10000 ? 'degraded' : 'unhealthy',
178 | average: metrics.responseTime.average,
179 | limit: 10000,
180 | },
181 | };
182 |
183 | // Determine overall status
184 | const statuses = Object.values(checks).map(check => check.status);
185 | const overallStatus = statuses.includes('unhealthy') ? 'unhealthy' :
186 | statuses.includes('degraded') ? 'degraded' : 'healthy';
187 |
188 | return {
189 | status: overallStatus,
190 | checks,
191 | metrics,
192 | };
193 | }
194 |
195 | // Get recent request metrics
196 | getRecentRequests(limit: number = 50): RequestMetric[] {
197 | return this.requestMetrics
198 | .slice(-limit)
199 | .sort((a, b) => b.timestamp - a.timestamp);
200 | }
201 |
202 | // Get tool usage statistics
203 | getToolUsageStats(): Array<{
204 | tool: string;
205 | count: number;
206 | percentage: number;
207 | averageResponseTime: number;
208 | errorRate: number;
209 | }> {
210 | const totalRequests = this.metrics.requestCount;
211 |
212 | return Object.entries(this.metrics.toolUsage).map(([tool, count]) => {
213 | const toolMetrics = this.requestMetrics.filter(m => m.tool === tool);
214 | const toolErrors = toolMetrics.filter(m => !m.success).length;
215 | const avgResponseTime = toolMetrics.length > 0
216 | ? toolMetrics.reduce((sum, m) => sum + m.duration, 0) / toolMetrics.length
217 | : 0;
218 |
219 | return {
220 | tool,
221 | count,
222 | percentage: Math.round((count / totalRequests) * 100),
223 | averageResponseTime: Math.round(avgResponseTime),
224 | errorRate: Math.round((toolErrors / count) * 100),
225 | };
226 | }).sort((a, b) => b.count - a.count);
227 | }
228 |
229 | // Get performance insights
230 | getPerformanceInsights(): {
231 | slowestTools: Array<{ tool: string; avgTime: number }>;
232 | mostErrorProneTools: Array<{ tool: string; errorRate: number }>;
233 | peakUsageHours: Array<{ hour: number; requestCount: number }>;
234 | recommendations: string[];
235 | } {
236 | const toolStats = this.getToolUsageStats();
237 | const slowestTools = toolStats
238 | .sort((a, b) => b.averageResponseTime - a.averageResponseTime)
239 | .slice(0, 5)
240 | .map(t => ({ tool: t.tool, avgTime: t.averageResponseTime }));
241 |
242 | const mostErrorProneTools = toolStats
243 | .filter(t => t.errorRate > 0)
244 | .sort((a, b) => b.errorRate - a.errorRate)
245 | .slice(0, 5)
246 | .map(t => ({ tool: t.tool, errorRate: t.errorRate }));
247 |
248 | // Calculate peak usage hours
249 | const hourlyUsage = new Array(24).fill(0);
250 | this.requestMetrics.forEach(metric => {
251 | const hour = new Date(metric.timestamp).getHours();
252 | hourlyUsage[hour]++;
253 | });
254 |
255 | const peakUsageHours = hourlyUsage
256 | .map((count, hour) => ({ hour, requestCount: count }))
257 | .filter(h => h.requestCount > 0)
258 | .sort((a, b) => b.requestCount - a.requestCount)
259 | .slice(0, 5);
260 |
261 | // Generate recommendations
262 | const recommendations: string[] = [];
263 | const metrics = this.getMetrics();
264 |
265 | if (metrics.responseTime.average > 5000) {
266 | recommendations.push('Consider implementing response caching for frequently accessed data');
267 | }
268 |
269 | if (metrics.errorCount / metrics.requestCount > 0.05) {
270 | recommendations.push('High error rate detected - review error handling and API limits');
271 | }
272 |
273 | if (metrics.memory.heapUsed / metrics.memory.heapTotal > 0.8) {
274 | recommendations.push('Memory usage is high - consider implementing garbage collection optimization');
275 | }
276 |
277 | if (slowestTools.length > 0 && slowestTools[0].avgTime > 10000) {
278 | recommendations.push(`${slowestTools[0].tool} is slow - consider implementing chunking or pagination`);
279 | }
280 |
281 | if (mostErrorProneTools.length > 0 && mostErrorProneTools[0].errorRate > 20) {
282 | recommendations.push(`${mostErrorProneTools[0].tool} has high error rate - review implementation`);
283 | }
284 |
285 | return {
286 | slowestTools,
287 | mostErrorProneTools,
288 | peakUsageHours,
289 | recommendations,
290 | };
291 | }
292 |
293 | // Export metrics to JSON
294 | exportMetrics(): string {
295 | return JSON.stringify({
296 | timestamp: new Date().toISOString(),
297 | metrics: this.getMetrics(),
298 | health: this.getHealthStatus(),
299 | toolUsage: this.getToolUsageStats(),
300 | recentRequests: this.getRecentRequests(100),
301 | insights: this.getPerformanceInsights(),
302 | }, null, 2);
303 | }
304 |
305 | // Reset metrics (for testing or maintenance)
306 | resetMetrics(): void {
307 | this.metrics = {
308 | uptime: 0,
309 | memory: process.memoryUsage(),
310 | requestCount: 0,
311 | errorCount: 0,
312 | responseTime: {
313 | average: 0,
314 | min: 0,
315 | max: 0,
316 | percentile95: 0,
317 | },
318 | toolUsage: {},
319 | lastRequestTime: 0,
320 | startTime: Date.now(),
321 | };
322 | this.requestMetrics = [];
323 | this.requestIdCounter = 0;
324 |
325 | log.info('Metrics reset');
326 | }
327 | }
328 |
329 | // Global monitoring instance
330 | export const monitoring = new MonitoringService();
331 |
332 | // Helper function to wrap tool handlers with monitoring
333 | export function monitorTool<T extends (...args: any[]) => any>(
334 | toolName: string,
335 | handler: T
336 | ): T {
337 | return ((...args: any[]) => {
338 | const requestId = monitoring.generateRequestId();
339 | const startTime = Date.now();
340 | monitoring.startRequest(toolName, requestId);
341 |
342 | try {
343 | const result = handler(...args);
344 |
345 | // Handle both sync and async results
346 | if (result && typeof result.then === 'function') {
347 | return result
348 | .then((data: any) => {
349 | monitoring.completeRequest(toolName, startTime, true, undefined, requestId);
350 | return data;
351 | })
352 | .catch((error: any) => {
353 | monitoring.completeRequest(toolName, startTime, false, error.message, requestId);
354 | throw error;
355 | });
356 | } else {
357 | monitoring.completeRequest(toolName, startTime, true, undefined, requestId);
358 | return result;
359 | }
360 | } catch (error: any) {
361 | monitoring.completeRequest(toolName, startTime, false, error.message, requestId);
362 | throw error;
363 | }
364 | }) as T;
365 | }
366 |
367 | // Export utilities
368 | export default monitoring;
```
--------------------------------------------------------------------------------
/docs/legacy-tools/template.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
2 |
3 | export const templateTools: Tool[] = [
4 | {
5 | name: 'generate_boilerplate',
6 | description: 'Generate project boilerplate based on extracted patterns from a repository',
7 | inputSchema: {
8 | type: 'object',
9 | properties: {
10 | url: {
11 | type: 'string',
12 | description: 'GitHub repository URL to use as template source',
13 | },
14 | templateType: {
15 | type: 'string',
16 | enum: ['starter', 'component-library', 'microservice', 'fullstack', 'cli-tool', 'library'],
17 | description: 'Type of template to generate',
18 | },
19 | options: {
20 | type: 'object',
21 | properties: {
22 | name: {
23 | type: 'string',
24 | description: 'Name for the generated project',
25 | },
26 | description: {
27 | type: 'string',
28 | description: 'Description for the generated project',
29 | },
30 | framework: {
31 | type: 'string',
32 | description: 'Target framework',
33 | },
34 | language: {
35 | type: 'string',
36 | description: 'Target programming language',
37 | },
38 | includeTests: {
39 | type: 'boolean',
40 | description: 'Include test setup and examples',
41 | default: true,
42 | },
43 | includeDocs: {
44 | type: 'boolean',
45 | description: 'Include documentation templates',
46 | default: true,
47 | },
48 | includeCI: {
49 | type: 'boolean',
50 | description: 'Include CI/CD configuration',
51 | default: true,
52 | },
53 | includeDocker: {
54 | type: 'boolean',
55 | description: 'Include Docker configuration',
56 | default: false,
57 | },
58 | packageManager: {
59 | type: 'string',
60 | enum: ['npm', 'yarn', 'pnpm', 'bun'],
61 | description: 'Package manager to use',
62 | default: 'npm',
63 | },
64 | },
65 | },
66 | },
67 | required: ['url', 'templateType'],
68 | },
69 | },
70 | {
71 | name: 'create_component_library',
72 | description: 'Create a standalone component library from extracted components',
73 | inputSchema: {
74 | type: 'object',
75 | properties: {
76 | url: {
77 | type: 'string',
78 | description: 'GitHub repository URL',
79 | },
80 | componentPaths: {
81 | type: 'array',
82 | items: { type: 'string' },
83 | description: 'Paths to components to include in library',
84 | },
85 | libraryOptions: {
86 | type: 'object',
87 | properties: {
88 | name: {
89 | type: 'string',
90 | description: 'Library name',
91 | },
92 | version: {
93 | type: 'string',
94 | description: 'Initial version',
95 | default: '1.0.0',
96 | },
97 | bundler: {
98 | type: 'string',
99 | enum: ['rollup', 'webpack', 'vite', 'parcel'],
100 | description: 'Bundler to use',
101 | default: 'rollup',
102 | },
103 | outputFormats: {
104 | type: 'array',
105 | items: {
106 | type: 'string',
107 | enum: ['esm', 'cjs', 'umd', 'iife'],
108 | },
109 | description: 'Output formats',
110 | default: ['esm', 'cjs'],
111 | },
112 | includeStorybook: {
113 | type: 'boolean',
114 | description: 'Include Storybook setup',
115 | default: true,
116 | },
117 | includeJest: {
118 | type: 'boolean',
119 | description: 'Include Jest testing setup',
120 | default: true,
121 | },
122 | includeTSDoc: {
123 | type: 'boolean',
124 | description: 'Include TypeScript documentation',
125 | default: true,
126 | },
127 | },
128 | },
129 | },
130 | required: ['url'],
131 | },
132 | },
133 | {
134 | name: 'scaffold_project_structure',
135 | description: 'Generate project structure based on analyzed repository patterns',
136 | inputSchema: {
137 | type: 'object',
138 | properties: {
139 | url: {
140 | type: 'string',
141 | description: 'GitHub repository URL to analyze for structure',
142 | },
143 | projectType: {
144 | type: 'string',
145 | enum: ['web-app', 'api', 'library', 'cli', 'desktop', 'mobile', 'monorepo'],
146 | description: 'Type of project to scaffold',
147 | },
148 | structureOptions: {
149 | type: 'object',
150 | properties: {
151 | preserveStructure: {
152 | type: 'boolean',
153 | description: 'Preserve original folder structure',
154 | default: false,
155 | },
156 | modernizeStructure: {
157 | type: 'boolean',
158 | description: 'Apply modern project structure patterns',
159 | default: true,
160 | },
161 | includeConfig: {
162 | type: 'boolean',
163 | description: 'Include configuration files',
164 | default: true,
165 | },
166 | includeBuild: {
167 | type: 'boolean',
168 | description: 'Include build configuration',
169 | default: true,
170 | },
171 | includeScripts: {
172 | type: 'boolean',
173 | description: 'Include package scripts',
174 | default: true,
175 | },
176 | createReadme: {
177 | type: 'boolean',
178 | description: 'Create README with project information',
179 | default: true,
180 | },
181 | },
182 | },
183 | },
184 | required: ['url', 'projectType'],
185 | },
186 | },
187 | {
188 | name: 'generate_integration_code',
189 | description: 'Generate integration code and adapters for using extracted components',
190 | inputSchema: {
191 | type: 'object',
192 | properties: {
193 | sourceUrl: {
194 | type: 'string',
195 | description: 'GitHub repository URL of source code',
196 | },
197 | targetProject: {
198 | type: 'object',
199 | properties: {
200 | framework: {
201 | type: 'string',
202 | description: 'Target framework',
203 | },
204 | language: {
205 | type: 'string',
206 | description: 'Target language',
207 | },
208 | structure: {
209 | type: 'object',
210 | description: 'Target project structure',
211 | },
212 | },
213 | required: ['framework'],
214 | },
215 | integrationOptions: {
216 | type: 'object',
217 | properties: {
218 | adapterType: {
219 | type: 'string',
220 | enum: ['direct', 'wrapper', 'facade', 'bridge'],
221 | description: 'Type of adapter to generate',
222 | default: 'wrapper',
223 | },
224 | includeTypes: {
225 | type: 'boolean',
226 | description: 'Include TypeScript type definitions',
227 | default: true,
228 | },
229 | includeExamples: {
230 | type: 'boolean',
231 | description: 'Include usage examples',
232 | default: true,
233 | },
234 | includeTests: {
235 | type: 'boolean',
236 | description: 'Include integration tests',
237 | default: true,
238 | },
239 | generateDocs: {
240 | type: 'boolean',
241 | description: 'Generate integration documentation',
242 | default: true,
243 | },
244 | },
245 | },
246 | },
247 | required: ['sourceUrl', 'targetProject'],
248 | },
249 | },
250 | {
251 | name: 'create_migration_guide',
252 | description: 'Create step-by-step migration guide for integrating extracted code',
253 | inputSchema: {
254 | type: 'object',
255 | properties: {
256 | sourceUrl: {
257 | type: 'string',
258 | description: 'GitHub repository URL of source code',
259 | },
260 | targetProject: {
261 | type: 'object',
262 | properties: {
263 | framework: {
264 | type: 'string',
265 | description: 'Target framework',
266 | },
267 | currentVersion: {
268 | type: 'string',
269 | description: 'Current version of target project',
270 | },
271 | constraints: {
272 | type: 'array',
273 | items: { type: 'string' },
274 | description: 'Migration constraints (e.g., no breaking changes)',
275 | },
276 | },
277 | required: ['framework'],
278 | },
279 | migrationOptions: {
280 | type: 'object',
281 | properties: {
282 | includeBackup: {
283 | type: 'boolean',
284 | description: 'Include backup steps',
285 | default: true,
286 | },
287 | includeRollback: {
288 | type: 'boolean',
289 | description: 'Include rollback instructions',
290 | default: true,
291 | },
292 | includeTesting: {
293 | type: 'boolean',
294 | description: 'Include testing steps',
295 | default: true,
296 | },
297 | includeValidation: {
298 | type: 'boolean',
299 | description: 'Include validation steps',
300 | default: true,
301 | },
302 | estimateTime: {
303 | type: 'boolean',
304 | description: 'Include time estimates',
305 | default: true,
306 | },
307 | },
308 | },
309 | },
310 | required: ['sourceUrl', 'targetProject'],
311 | },
312 | },
313 | {
314 | name: 'generate_starter_template',
315 | description: 'Generate a complete starter template based on repository analysis',
316 | inputSchema: {
317 | type: 'object',
318 | properties: {
319 | url: {
320 | type: 'string',
321 | description: 'GitHub repository URL to use as base',
322 | },
323 | templateName: {
324 | type: 'string',
325 | description: 'Name for the starter template',
326 | },
327 | templateOptions: {
328 | type: 'object',
329 | properties: {
330 | extractBestPractices: {
331 | type: 'boolean',
332 | description: 'Extract and apply best practices',
333 | default: true,
334 | },
335 | modernizeCode: {
336 | type: 'boolean',
337 | description: 'Modernize code patterns',
338 | default: true,
339 | },
340 | addTemplateVars: {
341 | type: 'boolean',
342 | description: 'Add template variables for customization',
343 | default: true,
344 | },
345 | includeExamples: {
346 | type: 'boolean',
347 | description: 'Include usage examples',
348 | default: true,
349 | },
350 | createCLI: {
351 | type: 'boolean',
352 | description: 'Create CLI tool for template generation',
353 | default: false,
354 | },
355 | supportedPlatforms: {
356 | type: 'array',
357 | items: { type: 'string' },
358 | description: 'Supported platforms (e.g., ["web", "mobile", "desktop"])',
359 | },
360 | },
361 | },
362 | },
363 | required: ['url', 'templateName'],
364 | },
365 | },
366 | ];
```
--------------------------------------------------------------------------------
/src/utils/security.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { SecurityCheck, SecurityIssue } from '../types/index.js';
2 |
3 | export class SecurityService {
4 |
5 | static checkCode(code: string, language: string): SecurityCheck {
6 | const issues: SecurityIssue[] = [];
7 |
8 | // Common security patterns across languages
9 | const commonChecks = [
10 | this.checkHardcodedSecrets(code),
11 | this.checkSqlInjection(code),
12 | this.checkXssVulnerabilities(code),
13 | this.checkCommandInjection(code),
14 | this.checkInsecureRandomness(code),
15 | ];
16 |
17 | // Language-specific checks
18 | switch (language.toLowerCase()) {
19 | case 'javascript':
20 | case 'typescript':
21 | commonChecks.push(
22 | this.checkEvalUsage(code),
23 | this.checkInnerHtml(code),
24 | this.checkUnsafeRegex(code)
25 | );
26 | break;
27 | case 'python':
28 | commonChecks.push(
29 | this.checkPythonEval(code),
30 | this.checkPickleUsage(code),
31 | this.checkShellCommand(code)
32 | );
33 | break;
34 | case 'java':
35 | commonChecks.push(
36 | this.checkJavaDeserialization(code),
37 | this.checkRuntimeExec(code)
38 | );
39 | break;
40 | }
41 |
42 | // Flatten and filter issues
43 | const allIssues = commonChecks.flat().filter(Boolean) as SecurityIssue[];
44 |
45 | return {
46 | passed: allIssues.length === 0,
47 | issues: allIssues,
48 | recommendations: this.generateRecommendations(allIssues),
49 | };
50 | }
51 |
52 | static sanitizeCode(code: string): string {
53 | // Remove or mask sensitive information
54 | let sanitized = code;
55 |
56 | // Remove API keys and tokens
57 | sanitized = sanitized.replace(/['"](sk-[a-zA-Z0-9]{32,})['"]/g, '"<API_KEY_MASKED>"');
58 | sanitized = sanitized.replace(/['"](ghp_[a-zA-Z0-9]{36})['"]/g, '"<GITHUB_TOKEN_MASKED>"');
59 | sanitized = sanitized.replace(/['"](xoxb-[a-zA-Z0-9-]{51,})['"]/g, '"<SLACK_TOKEN_MASKED>"');
60 |
61 | // Remove database connection strings
62 | sanitized = sanitized.replace(/['"](postgresql:\/\/[^'"]+)['"]/g, '"<DATABASE_URL_MASKED>"');
63 | sanitized = sanitized.replace(/['"](mysql:\/\/[^'"]+)['"]/g, '"<DATABASE_URL_MASKED>"');
64 | sanitized = sanitized.replace(/['"](mongodb:\/\/[^'"]+)['"]/g, '"<DATABASE_URL_MASKED>"');
65 |
66 | // Remove email addresses
67 | sanitized = sanitized.replace(/['"]([\w\.-]+@[\w\.-]+\.\w+)['"]/g, '"<EMAIL_MASKED>"');
68 |
69 | // Remove potential passwords
70 | sanitized = sanitized.replace(/password\s*[:=]\s*['"](.*?)['"]/gi, 'password: "<PASSWORD_MASKED>"');
71 | sanitized = sanitized.replace(/secret\s*[:=]\s*['"](.*?)['"]/gi, 'secret: "<SECRET_MASKED>"');
72 |
73 | return sanitized;
74 | }
75 |
76 | static isPathSafe(path: string): boolean {
77 | // Check for directory traversal
78 | if (path.includes('..')) return false;
79 |
80 | // Check for absolute paths
81 | if (path.startsWith('/')) return false;
82 |
83 | // Check for dangerous characters
84 | if (/[<>:"|?*]/.test(path)) return false;
85 |
86 | // Check for null bytes
87 | if (path.includes('\0')) return false;
88 |
89 | return true;
90 | }
91 |
92 | static validateApiAccess(url: string): boolean {
93 | // Only allow GitHub API access
94 | return url.startsWith('https://api.github.com/') || url.startsWith('https://github.com/');
95 | }
96 |
97 | static rateLimit(key: string, limit: number = 100, window: number = 60000): boolean {
98 | // Simple in-memory rate limiting
99 | const now = Date.now();
100 | const windowStart = now - window;
101 |
102 | if (!this.rateLimitStore) {
103 | this.rateLimitStore = new Map();
104 | }
105 |
106 | const requests = this.rateLimitStore.get(key) || [];
107 | const validRequests = requests.filter((time: number) => time > windowStart);
108 |
109 | if (validRequests.length >= limit) {
110 | return false;
111 | }
112 |
113 | validRequests.push(now);
114 | this.rateLimitStore.set(key, validRequests);
115 |
116 | return true;
117 | }
118 |
119 | private static rateLimitStore: Map<string, number[]>;
120 |
121 | private static checkHardcodedSecrets(code: string): SecurityIssue[] {
122 | const issues: SecurityIssue[] = [];
123 |
124 | const patterns = [
125 | { pattern: /['"](sk-[a-zA-Z0-9]{32,})['"]/, name: 'OpenAI API Key' },
126 | { pattern: /['"](ghp_[a-zA-Z0-9]{36})['"]/, name: 'GitHub Personal Access Token' },
127 | { pattern: /['"](xoxb-[a-zA-Z0-9-]{51,})['"]/, name: 'Slack Bot Token' },
128 | { pattern: /['"](AIza[0-9A-Za-z-_]{35})['"]/, name: 'Google API Key' },
129 | { pattern: /['"](AKIA[0-9A-Z]{16})['"]/, name: 'AWS Access Key' },
130 | { pattern: /['"](ya29\.[0-9A-Za-z\-_]+)['"]/, name: 'Google OAuth Token' },
131 | ];
132 |
133 | for (const { pattern, name } of patterns) {
134 | const matches = code.match(pattern);
135 | if (matches) {
136 | issues.push({
137 | type: 'high',
138 | description: `Hardcoded ${name} found in code`,
139 | file: 'current',
140 | suggestion: `Store ${name} in environment variables or secure configuration`,
141 | });
142 | }
143 | }
144 |
145 | return issues;
146 | }
147 |
148 | private static checkSqlInjection(code: string): SecurityIssue[] {
149 | const issues: SecurityIssue[] = [];
150 |
151 | const patterns = [
152 | /query\s*\(\s*['"]\s*SELECT\s.*?\+.*?['"]\s*\)/i,
153 | /execute\s*\(\s*['"]\s*SELECT\s.*?\+.*?['"]\s*\)/i,
154 | /sql\s*=\s*['"]\s*SELECT\s.*?\+.*?['"]/i,
155 | ];
156 |
157 | for (const pattern of patterns) {
158 | if (pattern.test(code)) {
159 | issues.push({
160 | type: 'high',
161 | description: 'Potential SQL injection vulnerability',
162 | file: 'current',
163 | suggestion: 'Use parameterized queries or prepared statements',
164 | });
165 | }
166 | }
167 |
168 | return issues;
169 | }
170 |
171 | private static checkXssVulnerabilities(code: string): SecurityIssue[] {
172 | const issues: SecurityIssue[] = [];
173 |
174 | const patterns = [
175 | /dangerouslySetInnerHTML/,
176 | /innerHTML\s*=\s*.*?\+/,
177 | /document\.write\s*\(/,
178 | /eval\s*\(/,
179 | ];
180 |
181 | for (const pattern of patterns) {
182 | if (pattern.test(code)) {
183 | issues.push({
184 | type: 'medium',
185 | description: 'Potential XSS vulnerability',
186 | file: 'current',
187 | suggestion: 'Sanitize user input and use safe DOM manipulation methods',
188 | });
189 | }
190 | }
191 |
192 | return issues;
193 | }
194 |
195 | private static checkCommandInjection(code: string): SecurityIssue[] {
196 | const issues: SecurityIssue[] = [];
197 |
198 | const patterns = [
199 | /exec\s*\(\s*.*?\+.*?\)/,
200 | /system\s*\(\s*.*?\+.*?\)/,
201 | /spawn\s*\(\s*.*?\+.*?\)/,
202 | /execSync\s*\(\s*.*?\+.*?\)/,
203 | ];
204 |
205 | for (const pattern of patterns) {
206 | if (pattern.test(code)) {
207 | issues.push({
208 | type: 'high',
209 | description: 'Potential command injection vulnerability',
210 | file: 'current',
211 | suggestion: 'Validate and sanitize input before passing to system commands',
212 | });
213 | }
214 | }
215 |
216 | return issues;
217 | }
218 |
219 | private static checkInsecureRandomness(code: string): SecurityIssue[] {
220 | const issues: SecurityIssue[] = [];
221 |
222 | const patterns = [
223 | /Math\.random\(\)/,
224 | /random\.random\(\)/,
225 | /rand\(\)/,
226 | ];
227 |
228 | for (const pattern of patterns) {
229 | if (pattern.test(code)) {
230 | issues.push({
231 | type: 'medium',
232 | description: 'Insecure random number generation',
233 | file: 'current',
234 | suggestion: 'Use cryptographically secure random number generators',
235 | });
236 | }
237 | }
238 |
239 | return issues;
240 | }
241 |
242 | private static checkEvalUsage(code: string): SecurityIssue[] {
243 | const issues: SecurityIssue[] = [];
244 |
245 | if (/eval\s*\(/.test(code)) {
246 | issues.push({
247 | type: 'high',
248 | description: 'Use of eval() function',
249 | file: 'current',
250 | suggestion: 'Avoid eval() and use safer alternatives like JSON.parse() or Function constructor',
251 | });
252 | }
253 |
254 | return issues;
255 | }
256 |
257 | private static checkInnerHtml(code: string): SecurityIssue[] {
258 | const issues: SecurityIssue[] = [];
259 |
260 | if (/innerHTML\s*=/.test(code)) {
261 | issues.push({
262 | type: 'medium',
263 | description: 'Direct use of innerHTML',
264 | file: 'current',
265 | suggestion: 'Use textContent or sanitize HTML content',
266 | });
267 | }
268 |
269 | return issues;
270 | }
271 |
272 | private static checkUnsafeRegex(code: string): SecurityIssue[] {
273 | const issues: SecurityIssue[] = [];
274 |
275 | // Check for potential ReDoS patterns
276 | const redosPatterns = [
277 | /\(\.\*\)\+/,
278 | /\(\.\+\)\+/,
279 | /\(\.\*\)\*/,
280 | /\(\.\+\)\*/,
281 | ];
282 |
283 | for (const pattern of redosPatterns) {
284 | if (pattern.test(code)) {
285 | issues.push({
286 | type: 'medium',
287 | description: 'Potential ReDoS (Regular Expression Denial of Service) vulnerability',
288 | file: 'current',
289 | suggestion: 'Review regex patterns for potential catastrophic backtracking',
290 | });
291 | }
292 | }
293 |
294 | return issues;
295 | }
296 |
297 | private static checkPythonEval(code: string): SecurityIssue[] {
298 | const issues: SecurityIssue[] = [];
299 |
300 | const patterns = [
301 | /eval\s*\(/,
302 | /exec\s*\(/,
303 | /compile\s*\(/,
304 | ];
305 |
306 | for (const pattern of patterns) {
307 | if (pattern.test(code)) {
308 | issues.push({
309 | type: 'high',
310 | description: 'Use of dangerous Python eval/exec functions',
311 | file: 'current',
312 | suggestion: 'Use safer alternatives like ast.literal_eval() or avoid dynamic code execution',
313 | });
314 | }
315 | }
316 |
317 | return issues;
318 | }
319 |
320 | private static checkPickleUsage(code: string): SecurityIssue[] {
321 | const issues: SecurityIssue[] = [];
322 |
323 | if (/pickle\.loads?\s*\(/.test(code)) {
324 | issues.push({
325 | type: 'high',
326 | description: 'Use of pickle.load() or pickle.loads()',
327 | file: 'current',
328 | suggestion: 'Avoid pickle for untrusted data, use JSON or other safe serialization formats',
329 | });
330 | }
331 |
332 | return issues;
333 | }
334 |
335 | private static checkShellCommand(code: string): SecurityIssue[] {
336 | const issues: SecurityIssue[] = [];
337 |
338 | const patterns = [
339 | /subprocess\.call\s*\(\s*.*shell\s*=\s*True/,
340 | /os\.system\s*\(/,
341 | /subprocess\.run\s*\(\s*.*shell\s*=\s*True/,
342 | ];
343 |
344 | for (const pattern of patterns) {
345 | if (pattern.test(code)) {
346 | issues.push({
347 | type: 'high',
348 | description: 'Use of shell commands with potential injection risk',
349 | file: 'current',
350 | suggestion: 'Use subprocess without shell=True or validate input thoroughly',
351 | });
352 | }
353 | }
354 |
355 | return issues;
356 | }
357 |
358 | private static checkJavaDeserialization(code: string): SecurityIssue[] {
359 | const issues: SecurityIssue[] = [];
360 |
361 | if (/ObjectInputStream|readObject\s*\(/.test(code)) {
362 | issues.push({
363 | type: 'high',
364 | description: 'Java deserialization vulnerability',
365 | file: 'current',
366 | suggestion: 'Validate serialized data or use safer serialization methods',
367 | });
368 | }
369 |
370 | return issues;
371 | }
372 |
373 | private static checkRuntimeExec(code: string): SecurityIssue[] {
374 | const issues: SecurityIssue[] = [];
375 |
376 | if (/Runtime\.getRuntime\(\)\.exec\s*\(/.test(code)) {
377 | issues.push({
378 | type: 'high',
379 | description: 'Use of Runtime.exec() for command execution',
380 | file: 'current',
381 | suggestion: 'Use ProcessBuilder with proper input validation',
382 | });
383 | }
384 |
385 | return issues;
386 | }
387 |
388 | private static generateRecommendations(issues: SecurityIssue[]): string[] {
389 | const recommendations: string[] = [];
390 |
391 | if (issues.length === 0) {
392 | recommendations.push('No security issues detected');
393 | return recommendations;
394 | }
395 |
396 | const highIssues = issues.filter(issue => issue.type === 'high');
397 | const mediumIssues = issues.filter(issue => issue.type === 'medium');
398 | const lowIssues = issues.filter(issue => issue.type === 'low');
399 |
400 | if (highIssues.length > 0) {
401 | recommendations.push(`Address ${highIssues.length} high-priority security issues immediately`);
402 | }
403 |
404 | if (mediumIssues.length > 0) {
405 | recommendations.push(`Review ${mediumIssues.length} medium-priority security issues`);
406 | }
407 |
408 | if (lowIssues.length > 0) {
409 | recommendations.push(`Consider addressing ${lowIssues.length} low-priority security issues`);
410 | }
411 |
412 | recommendations.push('Implement security linting in your CI/CD pipeline');
413 | recommendations.push('Regular security audits and dependency updates');
414 | recommendations.push('Use environment variables for sensitive configuration');
415 |
416 | return recommendations;
417 | }
418 | }
```
--------------------------------------------------------------------------------
/src/utils/file-processor.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { z } from 'zod';
2 | import { getConfig } from './config.js';
3 |
4 | // File processing result interface
5 | export interface FileProcessingResult {
6 | success: boolean;
7 | filePath: string;
8 | content?: string;
9 | metadata?: FileMetadata;
10 | error?: {
11 | code: string;
12 | message: string;
13 | details?: any;
14 | };
15 | }
16 |
17 | // File metadata interface
18 | export interface FileMetadata {
19 | name: string;
20 | extension: string;
21 | size: number;
22 | type: 'text' | 'binary' | 'image' | 'archive' | 'unknown';
23 | mimeType?: string;
24 | encoding?: string;
25 | lineCount?: number;
26 | language?: string;
27 | lastModified?: string;
28 | checksum?: string;
29 | }
30 |
31 | // File validation schema
32 | const FilePathSchema = z.string().refine(
33 | (path) => {
34 | // Security validation: prevent path traversal
35 | const normalizedPath = path.replace(/\\/g, '/');
36 | return !normalizedPath.includes('../') &&
37 | !normalizedPath.includes('..\\') &&
38 | !normalizedPath.startsWith('/') &&
39 | !normalizedPath.includes('//');
40 | },
41 | { message: 'Invalid file path: potential security risk detected' }
42 | );
43 |
44 | // Batch processing options
45 | export interface BatchProcessingOptions {
46 | maxConcurrent: number;
47 | continueOnError: boolean;
48 | validatePaths: boolean;
49 | includeMetadata: boolean;
50 | maxFileSize: number;
51 | allowedExtensions?: string[];
52 | excludePatterns?: string[];
53 | }
54 |
55 | // File type detection based on extension
56 | const FILE_TYPE_MAP: Record<string, string> = {
57 | // Text files
58 | '.txt': 'text/plain',
59 | '.md': 'text/markdown',
60 | '.json': 'application/json',
61 | '.yaml': 'application/yaml',
62 | '.yml': 'application/yaml',
63 | '.xml': 'application/xml',
64 | '.csv': 'text/csv',
65 | '.log': 'text/plain',
66 |
67 | // Code files
68 | '.js': 'text/javascript',
69 | '.jsx': 'text/javascript',
70 | '.ts': 'text/typescript',
71 | '.tsx': 'text/typescript',
72 | '.py': 'text/python',
73 | '.java': 'text/java',
74 | '.cpp': 'text/cpp',
75 | '.c': 'text/c',
76 | '.h': 'text/c',
77 | '.go': 'text/go',
78 | '.rs': 'text/rust',
79 | '.php': 'text/php',
80 | '.rb': 'text/ruby',
81 | '.swift': 'text/swift',
82 | '.kt': 'text/kotlin',
83 | '.scala': 'text/scala',
84 | '.clj': 'text/clojure',
85 | '.html': 'text/html',
86 | '.css': 'text/css',
87 | '.scss': 'text/scss',
88 | '.sass': 'text/sass',
89 | '.less': 'text/less',
90 | '.sql': 'text/sql',
91 | '.sh': 'text/shell',
92 | '.bash': 'text/shell',
93 | '.zsh': 'text/shell',
94 | '.fish': 'text/shell',
95 | '.ps1': 'text/powershell',
96 | '.r': 'text/r',
97 | '.m': 'text/matlab',
98 | '.pl': 'text/perl',
99 | '.lua': 'text/lua',
100 | '.dart': 'text/dart',
101 | '.elm': 'text/elm',
102 | '.ex': 'text/elixir',
103 | '.exs': 'text/elixir',
104 | '.erl': 'text/erlang',
105 | '.hrl': 'text/erlang',
106 | '.fs': 'text/fsharp',
107 | '.fsx': 'text/fsharp',
108 | '.ml': 'text/ocaml',
109 | '.mli': 'text/ocaml',
110 | '.hs': 'text/haskell',
111 | '.lhs': 'text/haskell',
112 | '.jl': 'text/julia',
113 | '.nim': 'text/nim',
114 | '.nims': 'text/nim',
115 | '.cr': 'text/crystal',
116 | '.d': 'text/d',
117 | '.zig': 'text/zig',
118 | '.v': 'text/v',
119 | '.vsh': 'text/v',
120 |
121 | // Configuration files
122 | '.toml': 'application/toml',
123 | '.ini': 'text/plain',
124 | '.cfg': 'text/plain',
125 | '.conf': 'text/plain',
126 | '.env': 'text/plain',
127 | '.properties': 'text/plain',
128 |
129 | // Documentation
130 | '.rst': 'text/restructuredtext',
131 | '.adoc': 'text/asciidoc',
132 | '.tex': 'text/latex',
133 |
134 | // Images
135 | '.jpg': 'image/jpeg',
136 | '.jpeg': 'image/jpeg',
137 | '.png': 'image/png',
138 | '.gif': 'image/gif',
139 | '.bmp': 'image/bmp',
140 | '.svg': 'image/svg+xml',
141 | '.webp': 'image/webp',
142 | '.ico': 'image/x-icon',
143 |
144 | // Archives
145 | '.zip': 'application/zip',
146 | '.tar': 'application/tar',
147 | '.gz': 'application/gzip',
148 | '.7z': 'application/x-7z-compressed',
149 | '.rar': 'application/vnd.rar',
150 | '.bz2': 'application/bzip2',
151 | '.xz': 'application/xz',
152 |
153 | // Binary
154 | '.exe': 'application/octet-stream',
155 | '.dll': 'application/octet-stream',
156 | '.so': 'application/octet-stream',
157 | '.dylib': 'application/octet-stream',
158 | '.bin': 'application/octet-stream',
159 | };
160 |
161 | // Language detection based on file extension
162 | const LANGUAGE_MAP: Record<string, string> = {
163 | '.js': 'javascript',
164 | '.jsx': 'javascript',
165 | '.ts': 'typescript',
166 | '.tsx': 'typescript',
167 | '.py': 'python',
168 | '.java': 'java',
169 | '.cpp': 'cpp',
170 | '.c': 'c',
171 | '.h': 'c',
172 | '.go': 'go',
173 | '.rs': 'rust',
174 | '.php': 'php',
175 | '.rb': 'ruby',
176 | '.swift': 'swift',
177 | '.kt': 'kotlin',
178 | '.scala': 'scala',
179 | '.clj': 'clojure',
180 | '.html': 'html',
181 | '.css': 'css',
182 | '.scss': 'scss',
183 | '.sass': 'sass',
184 | '.less': 'less',
185 | '.sql': 'sql',
186 | '.sh': 'shell',
187 | '.bash': 'shell',
188 | '.zsh': 'shell',
189 | '.fish': 'shell',
190 | '.ps1': 'powershell',
191 | '.r': 'r',
192 | '.m': 'matlab',
193 | '.pl': 'perl',
194 | '.lua': 'lua',
195 | '.dart': 'dart',
196 | '.elm': 'elm',
197 | '.ex': 'elixir',
198 | '.exs': 'elixir',
199 | '.erl': 'erlang',
200 | '.hrl': 'erlang',
201 | '.fs': 'fsharp',
202 | '.fsx': 'fsharp',
203 | '.ml': 'ocaml',
204 | '.mli': 'ocaml',
205 | '.hs': 'haskell',
206 | '.lhs': 'haskell',
207 | '.jl': 'julia',
208 | '.nim': 'nim',
209 | '.nims': 'nim',
210 | '.cr': 'crystal',
211 | '.d': 'd',
212 | '.zig': 'zig',
213 | '.v': 'v',
214 | '.vsh': 'v',
215 | };
216 |
217 | // File type categorization
218 | function categorizeFileType(mimeType: string): 'text' | 'binary' | 'image' | 'archive' | 'unknown' {
219 | if (mimeType.startsWith('text/') || mimeType.includes('json') || mimeType.includes('xml') || mimeType.includes('yaml')) {
220 | return 'text';
221 | }
222 | if (mimeType.startsWith('image/')) {
223 | return 'image';
224 | }
225 | if (mimeType.includes('zip') || mimeType.includes('tar') || mimeType.includes('compress')) {
226 | return 'archive';
227 | }
228 | if (mimeType.includes('octet-stream') || mimeType.includes('binary')) {
229 | return 'binary';
230 | }
231 | return 'unknown';
232 | }
233 |
234 | // Validate file path for security
235 | export function validateFilePath(filePath: string): { valid: boolean; error?: string } {
236 | try {
237 | FilePathSchema.parse(filePath);
238 | return { valid: true };
239 | } catch (error) {
240 | return {
241 | valid: false,
242 | error: error instanceof z.ZodError ? error.errors[0].message : 'Invalid file path'
243 | };
244 | }
245 | }
246 |
247 | // Extract file metadata
248 | export function extractFileMetadata(filePath: string, content?: string): FileMetadata {
249 | const name = filePath.split('/').pop() || filePath;
250 | const extensionMatch = name.match(/\.[^.]+$/);
251 | const extension = extensionMatch ? extensionMatch[0].toLowerCase() : '';
252 |
253 | const mimeType = FILE_TYPE_MAP[extension] || 'application/octet-stream';
254 | const type = categorizeFileType(mimeType);
255 | const language = LANGUAGE_MAP[extension];
256 |
257 | const metadata: FileMetadata = {
258 | name,
259 | extension,
260 | size: content ? Buffer.byteLength(content, 'utf8') : 0,
261 | type,
262 | mimeType,
263 | language,
264 | };
265 |
266 | // Add line count for text files
267 | if (type === 'text' && content) {
268 | metadata.lineCount = content.split('\n').length;
269 | metadata.encoding = 'utf8';
270 | }
271 |
272 | return metadata;
273 | }
274 |
275 | // Process a single file with error handling
276 | export async function processSingleFile(
277 | filePath: string,
278 | content: string,
279 | options: Partial<BatchProcessingOptions> = {}
280 | ): Promise<FileProcessingResult> {
281 | const config = getConfig();
282 |
283 | try {
284 | // Validate file path if requested
285 | if (options.validatePaths !== false) {
286 | const validation = validateFilePath(filePath);
287 | if (!validation.valid) {
288 | return {
289 | success: false,
290 | filePath,
291 | error: {
292 | code: 'INVALID_FILE_PATH',
293 | message: validation.error || 'Invalid file path',
294 | },
295 | };
296 | }
297 | }
298 |
299 | // Check file size
300 | const fileSize = Buffer.byteLength(content, 'utf8');
301 | const maxSize = options.maxFileSize || config.limits.maxFileSize;
302 |
303 | if (fileSize > maxSize) {
304 | return {
305 | success: false,
306 | filePath,
307 | error: {
308 | code: 'FILE_TOO_LARGE',
309 | message: `File size (${fileSize} bytes) exceeds maximum allowed size (${maxSize} bytes)`,
310 | details: { fileSize, maxSize },
311 | },
312 | };
313 | }
314 |
315 | // Check allowed extensions
316 | if (options.allowedExtensions && options.allowedExtensions.length > 0) {
317 | const metadata = extractFileMetadata(filePath);
318 | if (!options.allowedExtensions.includes(metadata.extension)) {
319 | return {
320 | success: false,
321 | filePath,
322 | error: {
323 | code: 'EXTENSION_NOT_ALLOWED',
324 | message: `File extension '${metadata.extension}' is not allowed`,
325 | details: { extension: metadata.extension, allowedExtensions: options.allowedExtensions },
326 | },
327 | };
328 | }
329 | }
330 |
331 | // Check exclude patterns
332 | if (options.excludePatterns && options.excludePatterns.length > 0) {
333 | const shouldExclude = options.excludePatterns.some(pattern => {
334 | const regex = new RegExp(pattern);
335 | return regex.test(filePath);
336 | });
337 |
338 | if (shouldExclude) {
339 | return {
340 | success: false,
341 | filePath,
342 | error: {
343 | code: 'FILE_EXCLUDED',
344 | message: `File matches exclude pattern`,
345 | details: { filePath, excludePatterns: options.excludePatterns },
346 | },
347 | };
348 | }
349 | }
350 |
351 | // Extract metadata if requested
352 | const metadata = options.includeMetadata ? extractFileMetadata(filePath, content) : undefined;
353 |
354 | return {
355 | success: true,
356 | filePath,
357 | content,
358 | metadata,
359 | };
360 |
361 | } catch (error) {
362 | return {
363 | success: false,
364 | filePath,
365 | error: {
366 | code: 'PROCESSING_ERROR',
367 | message: error instanceof Error ? error.message : 'Unknown processing error',
368 | details: error,
369 | },
370 | };
371 | }
372 | }
373 |
374 | // Batch process multiple files
375 | export async function batchProcessFiles(
376 | files: Array<{ path: string; content: string }>,
377 | options: Partial<BatchProcessingOptions> = {}
378 | ): Promise<{
379 | results: FileProcessingResult[];
380 | summary: {
381 | total: number;
382 | successful: number;
383 | failed: number;
384 | errors: Array<{ filePath: string; error: string }>;
385 | };
386 | }> {
387 | const config = getConfig();
388 | const maxConcurrent = options.maxConcurrent || config.limits.maxConcurrentRequests;
389 | const continueOnError = options.continueOnError !== false;
390 |
391 | const results: FileProcessingResult[] = [];
392 | const errors: Array<{ filePath: string; error: string }> = [];
393 |
394 | // Process files in batches to respect concurrency limits
395 | for (let i = 0; i < files.length; i += maxConcurrent) {
396 | const batch = files.slice(i, i + maxConcurrent);
397 |
398 | const batchPromises = batch.map(async (file) => {
399 | try {
400 | const result = await processSingleFile(file.path, file.content, options);
401 |
402 | if (!result.success && result.error) {
403 | errors.push({
404 | filePath: file.path,
405 | error: result.error.message,
406 | });
407 |
408 | if (!continueOnError) {
409 | throw new Error(`Processing failed for ${file.path}: ${result.error.message}`);
410 | }
411 | }
412 |
413 | return result;
414 | } catch (error) {
415 | const errorResult: FileProcessingResult = {
416 | success: false,
417 | filePath: file.path,
418 | error: {
419 | code: 'BATCH_PROCESSING_ERROR',
420 | message: error instanceof Error ? error.message : 'Unknown batch processing error',
421 | details: error,
422 | },
423 | };
424 |
425 | errors.push({
426 | filePath: file.path,
427 | error: errorResult.error!.message,
428 | });
429 |
430 | if (!continueOnError) {
431 | throw error;
432 | }
433 |
434 | return errorResult;
435 | }
436 | });
437 |
438 | const batchResults = await Promise.all(batchPromises);
439 | results.push(...batchResults);
440 | }
441 |
442 | const successful = results.filter(r => r.success).length;
443 | const failed = results.filter(r => !r.success).length;
444 |
445 | return {
446 | results,
447 | summary: {
448 | total: files.length,
449 | successful,
450 | failed,
451 | errors,
452 | },
453 | };
454 | }
455 |
456 | // Helper function to filter files by extension
457 | export function filterFilesByExtension(
458 | files: Array<{ path: string; content: string }>,
459 | allowedExtensions: string[]
460 | ): Array<{ path: string; content: string }> {
461 | return files.filter(file => {
462 | const metadata = extractFileMetadata(file.path);
463 | return allowedExtensions.includes(metadata.extension);
464 | });
465 | }
466 |
467 | // Helper function to get file statistics
468 | export function getFileStatistics(results: FileProcessingResult[]): {
469 | totalSize: number;
470 | totalLines: number;
471 | languageDistribution: Record<string, number>;
472 | typeDistribution: Record<string, number>;
473 | } {
474 | let totalSize = 0;
475 | let totalLines = 0;
476 | const languageDistribution: Record<string, number> = {};
477 | const typeDistribution: Record<string, number> = {};
478 |
479 | results.forEach(result => {
480 | if (result.success && result.metadata) {
481 | totalSize += result.metadata.size;
482 | totalLines += result.metadata.lineCount || 0;
483 |
484 | if (result.metadata.language) {
485 | languageDistribution[result.metadata.language] = (languageDistribution[result.metadata.language] || 0) + 1;
486 | }
487 |
488 | typeDistribution[result.metadata.type] = (typeDistribution[result.metadata.type] || 0) + 1;
489 | }
490 | });
491 |
492 | return {
493 | totalSize,
494 | totalLines,
495 | languageDistribution,
496 | typeDistribution,
497 | };
498 | }
```
--------------------------------------------------------------------------------
/docs/SETUP.md:
--------------------------------------------------------------------------------
```markdown
1 | # CodeCompass MCP Setup Guide
2 |
3 | ## 🚀 **Quick Start**
4 |
5 | ### **Option 1: Docker Deployment (Recommended)**
6 | ```bash
7 | # Clone the repository
8 | git clone https://github.com/your-org/codecompass-mcp.git
9 | cd codecompass-mcp
10 |
11 | # Set up environment variables
12 | cp .env.example .env
13 | # Edit .env with your API keys
14 |
15 | # Build and run with Docker
16 | ./scripts/docker-build.sh
17 | ./scripts/docker-run.sh --env-file .env
18 | ```
19 |
20 | ### **Option 2: Local Development**
21 | ```bash
22 | # Install dependencies
23 | npm install
24 |
25 | # Set up environment
26 | export GITHUB_TOKEN=your_github_token
27 | export OPENROUTER_API_KEY=your_openrouter_key
28 |
29 | # Build and run
30 | npm run build
31 | npm run dev
32 | ```
33 |
34 | ### **Option 3: Global Installation**
35 | ```bash
36 | # Install globally for system-wide access
37 | npm install -g codecompass-mcp
38 |
39 | # Run from anywhere
40 | codecompass-mcp --help
41 | ```
42 |
43 | ## 🔧 **Configuration**
44 |
45 | ### **Required Environment Variables**
46 | ```bash
47 | # GitHub API access (required for repository analysis)
48 | GITHUB_TOKEN=ghp_your_github_token_here
49 |
50 | # OpenRouter API access (required for AI-powered tools)
51 | OPENROUTER_API_KEY=sk-or-v1-your_openrouter_key_here
52 | ```
53 |
54 | ### **Optional Configuration**
55 | ```bash
56 | # AI Model Configuration
57 | OPENAI_MODEL=anthropic/claude-3.5-sonnet # Default AI model
58 | OPENROUTER_API_URL=https://openrouter.ai/api/v1 # Custom API endpoint
59 |
60 | # Response Management
61 | MAX_RESPONSE_TOKENS=25000 # Maximum response size
62 | MAX_FILE_CONTENT_LENGTH=5000 # Maximum file content per response
63 | CHUNK_SIZE=medium # Chunking strategy (small/medium/large)
64 |
65 | # Performance Tuning
66 | MAX_CONCURRENT_REQUESTS=10 # Concurrent processing limit
67 | MAX_FILE_SIZE=10485760 # Maximum file size (10MB)
68 | RATE_LIMIT_REQUESTS=1000 # Rate limit per window
69 | RATE_LIMIT_WINDOW=3600000 # Rate limit window (1 hour)
70 |
71 | # Logging Configuration
72 | LOG_LEVEL=info # Logging level (debug/info/warn/error)
73 | NODE_ENV=production # Environment mode
74 | ```
75 |
76 | ### **Configuration File (Optional)**
77 | Create a `config.json` file for persistent configuration:
78 | ```json
79 | {
80 | "github": {
81 | "token": "your_github_token",
82 | "apiUrl": "https://api.github.com"
83 | },
84 | "openrouter": {
85 | "apiKey": "your_openrouter_key",
86 | "defaultModel": "anthropic/claude-3.5-sonnet"
87 | },
88 | "response": {
89 | "maxTokens": 25000,
90 | "maxFileContentLength": 5000,
91 | "chunkSizes": {
92 | "small": { "filesPerChunk": 5, "fileContent": 1000 },
93 | "medium": { "filesPerChunk": 10, "fileContent": 2000 },
94 | "large": { "filesPerChunk": 20, "fileContent": 5000 }
95 | }
96 | },
97 | "logging": {
98 | "level": "info",
99 | "enableTimestamps": true,
100 | "enableColors": true
101 | }
102 | }
103 | ```
104 |
105 | ## 🔑 **API Key Setup**
106 |
107 | ### **GitHub Token**
108 | 1. Go to [GitHub Settings → Developer settings → Personal access tokens](https://github.com/settings/tokens)
109 | 2. Click "Generate new token (classic)"
110 | 3. Set expiration and select scopes:
111 | - `repo` (for private repositories)
112 | - `public_repo` (for public repositories)
113 | - `read:org` (for organization repositories)
114 | 4. Copy the token and set as `GITHUB_TOKEN`
115 |
116 | **Rate Limits:**
117 | - **Authenticated**: 5,000 requests/hour
118 | - **Unauthenticated**: 60 requests/hour
119 |
120 | ### **OpenRouter API Key**
121 | 1. Go to [OpenRouter](https://openrouter.ai/)
122 | 2. Sign up/login and go to [API Keys](https://openrouter.ai/keys)
123 | 3. Create a new API key
124 | 4. Copy the key and set as `OPENROUTER_API_KEY`
125 |
126 | **Available Models:**
127 | - `anthropic/claude-3.5-sonnet` (recommended)
128 | - `openai/gpt-4`
129 | - `google/gemini-pro`
130 | - `switchpoint/router` (automatic model selection)
131 |
132 | ## 🐳 **Docker Deployment**
133 |
134 | ### **Docker Compose (Recommended)**
135 | ```yaml
136 | # docker-compose.yml
137 | version: '3.8'
138 | services:
139 | codecompass-mcp:
140 | build: .
141 | container_name: codecompass-mcp
142 | restart: unless-stopped
143 | environment:
144 | - GITHUB_TOKEN=${GITHUB_TOKEN}
145 | - OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
146 | - NODE_ENV=production
147 | - LOG_LEVEL=info
148 | ports:
149 | - "3000:3000"
150 | volumes:
151 | - ./data:/app/data:ro
152 | healthcheck:
153 | test: ["CMD", "node", "-e", "console.log('Health check')"]
154 | interval: 30s
155 | timeout: 10s
156 | retries: 3
157 | ```
158 |
159 | ### **Docker Build Options**
160 | ```bash
161 | # Production build
162 | ./scripts/docker-build.sh
163 |
164 | # Development build with hot reload
165 | ./scripts/docker-build.sh --dev
166 |
167 | # Custom tag and registry
168 | ./scripts/docker-build.sh -t v1.0.0 -r your-registry.com --push
169 |
170 | # With build arguments
171 | ./scripts/docker-build.sh --build-arg NODE_ENV=production
172 | ```
173 |
174 | ### **Docker Run Options**
175 | ```bash
176 | # Basic run
177 | ./scripts/docker-run.sh
178 |
179 | # With environment file
180 | ./scripts/docker-run.sh --env-file .env
181 |
182 | # Interactive mode
183 | ./scripts/docker-run.sh --interactive
184 |
185 | # With custom configuration
186 | ./scripts/docker-run.sh \
187 | -e GITHUB_TOKEN=your_token \
188 | -e OPENROUTER_API_KEY=your_key \
189 | -e LOG_LEVEL=debug
190 | ```
191 |
192 | ## 🖥️ **MCP Client Integration**
193 |
194 | ### **Claude Desktop Integration**
195 | Add to your Claude Desktop configuration:
196 | ```json
197 | {
198 | "mcpServers": {
199 | "codecompass": {
200 | "command": "docker",
201 | "args": [
202 | "exec", "-i", "codecompass-mcp",
203 | "node", "build/index.js"
204 | ],
205 | "env": {
206 | "GITHUB_TOKEN": "your_github_token",
207 | "OPENROUTER_API_KEY": "your_openrouter_key"
208 | }
209 | }
210 | }
211 | }
212 | ```
213 |
214 | ### **Claude Code Integration**
215 | ```bash
216 | # Add MCP server to Claude Code
217 | claude mcp add codecompass-docker -s user -- \
218 | docker exec -i codecompass-mcp node build/index.js
219 | ```
220 |
221 | ### **Other MCP Clients**
222 | The server is compatible with any MCP client that supports the JSON-RPC protocol:
223 | - **Cline**: VS Code extension
224 | - **Continue**: VS Code/JetBrains extension
225 | - **Custom clients**: Using the MCP SDK
226 |
227 | ## 🔍 **Verification**
228 |
229 | ### **Health Check**
230 | ```bash
231 | # Test server health
232 | curl -X POST http://localhost:3000/health \
233 | -H "Content-Type: application/json" \
234 | -d '{"name": "health_check", "arguments": {"options": {"include_metrics": true}}}'
235 |
236 | # Or using the monitoring script
237 | ./scripts/monitor.js
238 | ```
239 |
240 | ### **Tool Testing**
241 | ```bash
242 | # Test repository analysis
243 | echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "fetch_repository_data", "arguments": {"url": "https://github.com/microsoft/typescript"}}}' | \
244 | docker exec -i codecompass-mcp node build/index.js
245 | ```
246 |
247 | ### **Performance Testing**
248 | ```bash
249 | # Run performance tests
250 | npm run test:performance
251 |
252 | # Monitor resource usage
253 | docker stats codecompass-mcp
254 |
255 | # Check logs
256 | ./scripts/docker-logs.sh -f --timestamps
257 | ```
258 |
259 | ## 📊 **Monitoring Setup**
260 |
261 | ### **Real-time Dashboard**
262 | ```bash
263 | # Start monitoring dashboard
264 | ./scripts/monitor.js --watch
265 |
266 | # Export metrics
267 | ./scripts/monitor.js --export > metrics.json
268 |
269 | # View specific metrics
270 | ./scripts/monitor.js --export | jq '.metrics.toolUsage'
271 | ```
272 |
273 | ### **Health Monitoring**
274 | ```bash
275 | # Comprehensive health check
276 | {
277 | "name": "health_check",
278 | "arguments": {
279 | "checks": ["api-limits", "monitoring", "configuration"],
280 | "options": {
281 | "include_metrics": true,
282 | "include_insights": true,
283 | "include_logs": true
284 | }
285 | }
286 | }
287 | ```
288 |
289 | ### **Log Analysis**
290 | ```bash
291 | # View structured logs
292 | docker logs codecompass-mcp | jq .
293 |
294 | # Filter by log level
295 | docker logs codecompass-mcp | jq 'select(.level == "ERROR")'
296 |
297 | # Search for specific events
298 | docker logs codecompass-mcp | jq 'select(.message | contains("Request started"))'
299 | ```
300 |
301 | ## 🛠️ **Development Setup**
302 |
303 | ### **Local Development**
304 | ```bash
305 | # Clone and setup
306 | git clone https://github.com/your-org/codecompass-mcp.git
307 | cd codecompass-mcp
308 |
309 | # Install dependencies
310 | npm install
311 |
312 | # Set up environment
313 | cp .env.example .env
314 | # Edit .env with your configuration
315 |
316 | # Start development server
317 | npm run dev
318 |
319 | # Run tests
320 | npm run test
321 |
322 | # Type checking
323 | npm run type-check
324 |
325 | # Linting
326 | npm run lint
327 | ```
328 |
329 | ### **Hot Reload Development**
330 | ```bash
331 | # Start with hot reload
332 | npm run dev:watch
333 |
334 | # Or with Docker
335 | ./scripts/docker-build.sh --dev
336 | ./scripts/docker-run.sh --interactive -v $(pwd):/app
337 | ```
338 |
339 | ### **Testing**
340 | ```bash
341 | # Run all tests
342 | npm test
343 |
344 | # Run specific test suites
345 | npm run test:unit
346 | npm run test:integration
347 | npm run test:performance
348 |
349 | # Test coverage
350 | npm run test:coverage
351 | ```
352 |
353 | ## 🔧 **Troubleshooting**
354 |
355 | ### **Common Error Messages & Solutions**
356 |
357 | #### **1. "API key missing" or "Neither GITHUB_TOKEN nor OPENROUTER_API_KEY is set"**
358 | **Cause**: Environment variables not properly configured.
359 |
360 | **Solution**:
361 | ```bash
362 | # Check if your .env file exists and has content
363 | cat .env
364 |
365 | # Verify tokens are set correctly
366 | echo $GITHUB_TOKEN | cut -c1-10 # Should show: ghp_xxxxxx
367 | echo $OPENROUTER_API_KEY | cut -c1-10 # Should show: sk-or-v1-x
368 |
369 | # Fix: Edit your .env file with real tokens
370 | nano .env
371 | ```
372 |
373 | **Expected .env content:**
374 | ```bash
375 | GITHUB_TOKEN=ghp_your_actual_40_character_token_here
376 | OPENROUTER_API_KEY=sk-or-v1-your_actual_key_here
377 | ```
378 |
379 | #### **2. "repository 'https://github.com/...' not found" (404 Error)**
380 | **Cause**: Repository is private or doesn't exist.
381 |
382 | **Solution**:
383 | ```bash
384 | # Test GitHub token permissions
385 | curl -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/user
386 |
387 | # Expected response: Your GitHub user info
388 | # If error: Check token has 'repo' scope at github.com/settings/tokens
389 | ```
390 |
391 | #### **3. "Container codecompass-mcp not found"**
392 | **Cause**: Docker container not running.
393 |
394 | **Solution**:
395 | ```bash
396 | # Check container status
397 | docker ps -a
398 |
399 | # If not running, start it
400 | ./scripts/docker-run.sh --env-file .env
401 |
402 | # If failed to start, check logs
403 | docker logs codecompass-mcp
404 | ```
405 |
406 | #### **4. "Permission denied" or "EACCES" errors**
407 | **Cause**: File permissions issue.
408 |
409 | **Solution**:
410 | ```bash
411 | # Fix script permissions
412 | chmod +x scripts/*.sh
413 |
414 | # Fix Docker permissions (Linux)
415 | sudo usermod -aG docker $USER
416 | newgrp docker
417 | ```
418 |
419 | #### **5. "Port already in use" or "EADDRINUSE"**
420 | **Cause**: Port 3000 already occupied.
421 |
422 | **Solution**:
423 | ```bash
424 | # Check what's using port 3000
425 | lsof -i :3000
426 |
427 | # Kill the process or use different port
428 | export MCP_PORT=3001
429 | ./scripts/docker-run.sh --env-file .env
430 | ```
431 |
432 | #### **6. "Request timeout" errors**
433 | **Cause**: Large repository or slow network.
434 |
435 | **Solution**:
436 | ```bash
437 | # Increase timeouts in .env
438 | echo "REQUEST_TIMEOUT=60000" >> .env
439 |
440 | # Use chunking for large repos
441 | echo "CHUNK_MODE=true" >> .env
442 | ```
443 |
444 | #### **7. "Docker build failed" or "npm install failed"**
445 | **Cause**: Network issues or missing dependencies.
446 |
447 | **Solution**:
448 | ```bash
449 | # Clear Docker cache and rebuild
450 | docker system prune -f
451 | ./scripts/docker-build.sh --no-cache
452 |
453 | # For local development
454 | rm -rf node_modules package-lock.json
455 | npm install
456 | ```
457 |
458 | ### **Debug Mode**
459 | ```bash
460 | # Enable verbose logging
461 | export LOG_LEVEL=debug
462 |
463 | # Run with debug output
464 | DEBUG=codecompass:* ./scripts/docker-run.sh --env-file .env
465 |
466 | # Check logs location
467 | ./scripts/docker-logs.sh -f --timestamps
468 | ```
469 |
470 | ### **Health Check Commands**
471 | ```bash
472 | # Quick health check
473 | curl -X POST http://localhost:3000/health
474 |
475 | # Detailed health check with metrics
476 | echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "health_check", "arguments": {"options": {"include_metrics": true}}}}' | docker exec -i codecompass-mcp node build/index.js
477 | ```
478 |
479 | ### **Getting Help**
480 | If you're still having issues:
481 |
482 | 1. **Check GitHub Issues**: [github.com/TheAlchemist6/codecompass-mcp/issues](https://github.com/TheAlchemist6/codecompass-mcp/issues)
483 | 2. **Include in your issue**:
484 | - Operating system and version
485 | - Docker version (`docker --version`)
486 | - Node.js version (`node --version`)
487 | - Complete error message
488 | - Steps to reproduce
489 |
490 | 3. **Useful diagnostic commands**:
491 | ```bash
492 | # System info
493 | docker --version
494 | node --version
495 | npm --version
496 |
497 | # Container logs
498 | docker logs codecompass-mcp --tail 50
499 |
500 | # Environment check
501 | env | grep -E "(GITHUB|OPENROUTER|MCP)"
502 | ```
503 |
504 | ### **Debug Mode**
505 | ```bash
506 | # Enable debug logging
507 | export LOG_LEVEL=debug
508 |
509 | # Run with debug output
510 | DEBUG=codecompass:* npm run dev
511 |
512 | # Docker debug mode
513 | ./scripts/docker-run.sh -e LOG_LEVEL=debug
514 | ```
515 |
516 | ### **Performance Optimization**
517 | ```bash
518 | # Optimize for large repositories
519 | export CHUNK_MODE=true
520 | export CHUNK_SIZE=large
521 | export MAX_CONCURRENT_REQUESTS=20
522 |
523 | # Reduce response sizes
524 | export MAX_RESPONSE_TOKENS=15000
525 | export MAX_FILE_CONTENT_LENGTH=2000
526 | ```
527 |
528 | ## 🔄 **Updates and Maintenance**
529 |
530 | ### **Updating**
531 | ```bash
532 | # Pull latest changes
533 | git pull origin main
534 |
535 | # Rebuild and restart
536 | ./scripts/docker-build.sh
537 | ./scripts/docker-run.sh --remove-existing
538 | ```
539 |
540 | ### **Backup Configuration**
541 | ```bash
542 | # Backup environment configuration
543 | cp .env .env.backup
544 |
545 | # Export Docker configuration
546 | docker inspect codecompass-mcp > container-config.json
547 | ```
548 |
549 | ### **Monitoring Health**
550 | ```bash
551 | # Set up automated health checks
552 | echo "*/5 * * * * curl -f http://localhost:3000/health || echo 'Health check failed'" | crontab -
553 |
554 | # Monitor logs for errors
555 | tail -f $(docker inspect codecompass-mcp | jq -r '.[0].LogPath') | grep ERROR
556 | ```
557 |
558 | ## 🎯 **Next Steps**
559 |
560 | 1. **Test the setup** with a small repository
561 | 2. **Configure monitoring** for your environment
562 | 3. **Integrate with your MCP client** (Claude Desktop, VS Code, etc.)
563 | 4. **Customize configuration** for your specific needs
564 | 5. **Set up automated backups** for important data
565 |
566 | ## 📚 **Additional Resources**
567 |
568 | - [API Documentation](API.md) - Complete tool reference
569 | - [Docker Guide](DOCKER.md) - Advanced Docker configuration
570 | - [Monitoring Guide](MONITORING.md) - Observability setup
571 | - [Contributing Guide](../CONTRIBUTING.md) - Development guidelines
572 | - [Examples](../examples/) - Usage examples and templates
573 |
574 | For support, please check the [Issues](https://github.com/your-org/codecompass-mcp/issues) page or create a new issue with detailed information about your setup and the problem you're experiencing.
```
--------------------------------------------------------------------------------
/examples/basic-usage.md:
--------------------------------------------------------------------------------
```markdown
1 | # CodeCompass MCP Usage Examples
2 |
3 | This document provides practical examples of how to use the CodeCompass MCP server's 18 tools.
4 |
5 | ## 🔍 Core Data Tools
6 |
7 | ### 1. Repository Analysis
8 |
9 | ```javascript
10 | // Basic repository analysis
11 | {
12 | "tool": "fetch_repository_data",
13 | "arguments": {
14 | "url": "https://github.com/facebook/react",
15 | "options": {
16 | "include_structure": true,
17 | "include_dependencies": true,
18 | "include_key_files": true,
19 | "max_files": 50
20 | }
21 | }
22 | }
23 |
24 | // For large repositories, control response size
25 | {
26 | "tool": "fetch_repository_data",
27 | "arguments": {
28 | "url": "https://github.com/facebook/react",
29 | "options": {
30 | "include_structure": true,
31 | "include_dependencies": true,
32 | "include_key_files": true,
33 | "max_files": 30,
34 | "max_response_tokens": 15000,
35 | "max_file_content_length": 500
36 | }
37 | }
38 | }
39 |
40 | // For large repositories, use chunking (recommended)
41 | {
42 | "tool": "fetch_repository_data",
43 | "arguments": {
44 | "url": "https://github.com/facebook/react",
45 | "options": {
46 | "chunk_mode": true,
47 | "chunk_index": 0,
48 | "chunk_size": "medium"
49 | }
50 | }
51 | }
52 |
53 | // Get next chunk
54 | {
55 | "tool": "fetch_repository_data",
56 | "arguments": {
57 | "url": "https://github.com/facebook/react",
58 | "options": {
59 | "chunk_mode": true,
60 | "chunk_index": 1,
61 | "chunk_size": "medium"
62 | }
63 | }
64 | }
65 |
66 | // Expected response (regular mode):
67 | {
68 | "success": true,
69 | "data": {
70 | "info": {
71 | "name": "react",
72 | "description": "The library for web and native user interfaces",
73 | "language": "JavaScript",
74 | "stars": 220000,
75 | "forks": 45000
76 | },
77 | "structure": {
78 | "fileCount": 850,
79 | "lineCount": 125000,
80 | "keyFiles": {
81 | "package.json": "...",
82 | "README.md": "..."
83 | }
84 | },
85 | "dependencies": {
86 | "production": 5,
87 | "development": 45
88 | }
89 | }
90 | }
91 |
92 | // Expected response (chunked mode):
93 | {
94 | "success": true,
95 | "data": {
96 | "info": { /* repository info */ },
97 | "structure": {
98 | "fileCount": 850,
99 | "lineCount": 125000,
100 | "keyFiles": {
101 | "package.json": "...",
102 | "README.md": "...",
103 | "src/index.js": "..."
104 | // Only 20 files in this chunk
105 | }
106 | },
107 | "dependencies": { /* dependency analysis */ },
108 | "architecture": { /* architecture analysis */ },
109 | "chunkInfo": {
110 | "chunkIndex": 0,
111 | "chunkSize": "medium",
112 | "totalFiles": 85,
113 | "totalChunks": 5,
114 | "filesInChunk": 20,
115 | "hasMore": true,
116 | "nextChunkIndex": 1
117 | }
118 | }
119 | }
120 | ```
121 |
122 | ### 2. Advanced Repository Search
123 |
124 | ```javascript
125 | // Search for React hooks
126 | {
127 | "tool": "search_repository",
128 | "arguments": {
129 | "url": "https://github.com/facebook/react",
130 | "query": "useState",
131 | "search_type": "function",
132 | "options": {
133 | "file_extensions": [".js", ".jsx", ".ts", ".tsx"],
134 | "include_context": true,
135 | "max_results": 20
136 | }
137 | }
138 | }
139 |
140 | // Search with regex pattern
141 | {
142 | "tool": "search_repository",
143 | "arguments": {
144 | "url": "https://github.com/facebook/react",
145 | "query": "use[A-Z]\\w+",
146 | "search_type": "regex",
147 | "options": {
148 | "case_sensitive": false,
149 | "include_context": true
150 | }
151 | }
152 | }
153 | ```
154 |
155 | ### 3. File Content Retrieval
156 |
157 | ```javascript
158 | // Basic file retrieval
159 | {
160 | "tool": "get_file_content",
161 | "arguments": {
162 | "url": "https://github.com/facebook/react",
163 | "file_paths": [
164 | "package.json",
165 | "README.md",
166 | "packages/react/src/React.js"
167 | ],
168 | "options": {
169 | "max_size": 50000,
170 | "include_metadata": true
171 | }
172 | }
173 | }
174 |
175 | // Advanced batch processing with filtering
176 | {
177 | "tool": "get_file_content",
178 | "arguments": {
179 | "url": "https://github.com/facebook/react",
180 | "file_paths": [
181 | "src/index.js",
182 | "src/components/Button.js",
183 | "src/utils/helpers.js",
184 | "test/Button.test.js"
185 | ],
186 | "options": {
187 | "max_concurrent": 10,
188 | "continue_on_error": true,
189 | "include_metadata": true,
190 | "file_extensions": [".js", ".jsx", ".ts", ".tsx"],
191 | "exclude_patterns": ["node_modules", "\\.test\\.", "\\.spec\\."]
192 | }
193 | }
194 | }
195 |
196 | // Expected response with rich metadata:
197 | {
198 | "success": true,
199 | "data": {
200 | "files": {
201 | "src/index.js": {
202 | "content": "import React from 'react'...",
203 | "metadata": {
204 | "name": "index.js",
205 | "extension": ".js",
206 | "size": 1024,
207 | "type": "text",
208 | "mimeType": "text/javascript",
209 | "language": "javascript",
210 | "lineCount": 42,
211 | "encoding": "utf8"
212 | },
213 | "size": 1024,
214 | "truncated": false
215 | }
216 | },
217 | "summary": {
218 | "total": 4,
219 | "successful": 3,
220 | "failed": 1,
221 | "fetchErrors": 0,
222 | "statistics": {
223 | "totalSize": 3072,
224 | "totalLines": 126,
225 | "languageDistribution": {
226 | "javascript": 3
227 | },
228 | "typeDistribution": {
229 | "text": 3
230 | }
231 | }
232 | }
233 | }
234 | }
235 | ```
236 |
237 | ### 4. Code Structure Analysis
238 |
239 | ```javascript
240 | // Analyze code structure
241 | {
242 | "tool": "analyze_code_structure",
243 | "arguments": {
244 | "url": "https://github.com/facebook/react",
245 | "options": {
246 | "include_functions": true,
247 | "include_classes": true,
248 | "include_complexity": true,
249 | "languages": ["javascript", "typescript"]
250 | }
251 | }
252 | }
253 | ```
254 |
255 | ## 🤖 AI-Enhanced Tools
256 |
257 | ### 1. AI Code Review
258 |
259 | ```javascript
260 | // Comprehensive AI code review
261 | {
262 | "tool": "ai_code_review",
263 | "arguments": {
264 | "url": "https://github.com/your-username/your-repo",
265 | "review_focus": ["security", "performance", "maintainability"],
266 | "options": {
267 | "ai_model": "auto",
268 | "severity_threshold": "medium",
269 | "include_examples": true
270 | }
271 | }
272 | }
273 |
274 | // Review specific files
275 | {
276 | "tool": "ai_code_review",
277 | "arguments": {
278 | "url": "https://github.com/your-username/your-repo",
279 | "file_paths": ["src/auth.js", "src/api.js"],
280 | "review_focus": ["security"],
281 | "options": {
282 | "ai_model": "anthropic/claude-3.5-sonnet",
283 | "severity_threshold": "high"
284 | }
285 | }
286 | }
287 | ```
288 |
289 | ### 2. AI Code Explanation
290 |
291 | ```javascript
292 | // Get overview explanation
293 | {
294 | "tool": "ai_explain_code",
295 | "arguments": {
296 | "url": "https://github.com/sindresorhus/is-online",
297 | "explanation_type": "overview",
298 | "options": {
299 | "ai_model": "auto",
300 | "target_audience": "intermediate",
301 | "include_examples": true,
302 | "include_diagrams": true
303 | }
304 | }
305 | }
306 |
307 | // Detailed architectural explanation
308 | {
309 | "tool": "ai_explain_code",
310 | "arguments": {
311 | "url": "https://github.com/facebook/react",
312 | "explanation_type": "architecture",
313 | "options": {
314 | "ai_model": "anthropic/claude-3-opus",
315 | "target_audience": "advanced",
316 | "focus_on_patterns": true
317 | }
318 | }
319 | }
320 |
321 | // Tutorial-style explanation
322 | {
323 | "tool": "ai_explain_code",
324 | "arguments": {
325 | "url": "https://github.com/simple-example/todo-app",
326 | "explanation_type": "tutorial",
327 | "options": {
328 | "target_audience": "beginner",
329 | "include_examples": true
330 | }
331 | }
332 | }
333 | ```
334 |
335 | ### 3. AI Refactoring Suggestions
336 |
337 | ```javascript
338 | // Modernization suggestions
339 | {
340 | "tool": "ai_refactor_suggestions",
341 | "arguments": {
342 | "url": "https://github.com/legacy-app/old-codebase",
343 | "refactoring_goals": ["modernize", "performance", "maintainability"],
344 | "options": {
345 | "ai_model": "auto",
346 | "include_code_examples": true,
347 | "estimate_effort": true
348 | }
349 | }
350 | }
351 |
352 | // Framework migration suggestions
353 | {
354 | "tool": "ai_refactor_suggestions",
355 | "arguments": {
356 | "url": "https://github.com/jquery-app/legacy-frontend",
357 | "refactoring_goals": ["modernize"],
358 | "target_framework": "react",
359 | "options": {
360 | "ai_model": "anthropic/claude-3.5-sonnet",
361 | "priority_level": "high"
362 | }
363 | }
364 | }
365 | ```
366 |
367 | ## 🔧 Code Transformation Tools
368 |
369 | ### 1. Code Transformation
370 |
371 | ```javascript
372 | // Modernize JavaScript code
373 | {
374 | "tool": "transform_code",
375 | "arguments": {
376 | "code": "var userName = 'John'; function greet() { return 'Hello ' + userName; }",
377 | "transformations": [
378 | {
379 | "type": "modernize",
380 | "options": {
381 | "target_es_version": "ES2020"
382 | }
383 | }
384 | ],
385 | "language": "javascript",
386 | "options": {
387 | "preserve_comments": true,
388 | "validate_syntax": true
389 | }
390 | }
391 | }
392 |
393 | // Convert to TypeScript
394 | {
395 | "tool": "transform_code",
396 | "arguments": {
397 | "code": "function calculateTotal(items) { return items.reduce((sum, item) => sum + item.price, 0); }",
398 | "transformations": [
399 | {
400 | "type": "modernize"
401 | }
402 | ],
403 | "language": "javascript",
404 | "target_language": "typescript"
405 | }
406 | }
407 | ```
408 |
409 | ### 2. Component Extraction
410 |
411 | ```javascript
412 | // Extract React components
413 | {
414 | "tool": "extract_components",
415 | "arguments": {
416 | "url": "https://github.com/react-app/components",
417 | "extraction_types": ["components", "hooks", "utilities"],
418 | "options": {
419 | "min_reusability_score": 75,
420 | "framework": "react",
421 | "include_examples": true
422 | }
423 | }
424 | }
425 |
426 | // Extract utility functions
427 | {
428 | "tool": "extract_components",
429 | "arguments": {
430 | "url": "https://github.com/utils-library/helpers",
431 | "extraction_types": ["functions", "utilities"],
432 | "options": {
433 | "min_reusability_score": 60,
434 | "include_dependencies": true
435 | }
436 | }
437 | }
438 | ```
439 |
440 | ### 3. Code Structure Adaptation
441 |
442 | ```javascript
443 | // Migrate to React from Vue
444 | {
445 | "tool": "adapt_code_structure",
446 | "arguments": {
447 | "url": "https://github.com/vue-app/frontend",
448 | "target_structure": {
449 | "framework": "react",
450 | "pattern": "mvc"
451 | },
452 | "options": {
453 | "preserve_logic": true,
454 | "update_imports": true,
455 | "generate_config": true
456 | }
457 | }
458 | }
459 |
460 | // Restructure for microservices
461 | {
462 | "tool": "adapt_code_structure",
463 | "arguments": {
464 | "url": "https://github.com/monolith-app/backend",
465 | "target_structure": {
466 | "pattern": "microservices",
467 | "folder_structure": {
468 | "services/": "Individual service modules",
469 | "shared/": "Shared utilities and types",
470 | "gateway/": "API gateway configuration"
471 | }
472 | }
473 | }
474 | }
475 | ```
476 |
477 | ## 📊 Analysis Tools
478 |
479 | ### 1. Architecture Analysis
480 |
481 | ```javascript
482 | // Analyze architectural patterns
483 | {
484 | "tool": "analyze_architecture",
485 | "arguments": {
486 | "url": "https://github.com/enterprise-app/backend",
487 | "options": {
488 | "pattern_types": ["mvc", "clean", "hexagonal"],
489 | "include_frameworks": true,
490 | "confidence_threshold": 0.8
491 | }
492 | }
493 | }
494 | ```
495 |
496 | ### 2. Implementation Comparison
497 |
498 | ```javascript
499 | // Compare different implementations
500 | {
501 | "tool": "compare_implementations",
502 | "arguments": {
503 | "implementations": [
504 | {
505 | "name": "React Implementation",
506 | "url": "https://github.com/team-a/react-solution",
507 | "focus_areas": ["performance", "maintainability"]
508 | },
509 | {
510 | "name": "Vue Implementation",
511 | "url": "https://github.com/team-b/vue-solution",
512 | "focus_areas": ["performance", "maintainability"]
513 | }
514 | ],
515 | "comparison_criteria": ["performance", "maintainability", "security", "complexity"],
516 | "options": {
517 | "include_metrics": true,
518 | "include_recommendations": true
519 | }
520 | }
521 | }
522 | ```
523 |
524 | ### 3. Code Quality Validation
525 |
526 | ```javascript
527 | // Comprehensive quality check
528 | {
529 | "tool": "validate_code_quality",
530 | "arguments": {
531 | "url": "https://github.com/production-app/codebase",
532 | "validation_types": ["security", "performance", "best-practices"],
533 | "options": {
534 | "severity_level": "medium",
535 | "include_fixes": true,
536 | "framework_specific": true
537 | }
538 | }
539 | }
540 | ```
541 |
542 | ## 🔄 Utility Tools
543 |
544 | ### 1. Batch Processing
545 |
546 | ```javascript
547 | // Process multiple operations
548 | {
549 | "tool": "batch_process",
550 | "arguments": {
551 | "operations": [
552 | {
553 | "id": "health",
554 | "tool": "health_check",
555 | "params": {
556 | "checks": ["api-limits", "system-health"]
557 | },
558 | "priority": 1
559 | },
560 | {
561 | "id": "analyze",
562 | "tool": "analyze_code_structure",
563 | "params": {
564 | "url": "https://github.com/example/repo"
565 | },
566 | "priority": 2
567 | },
568 | {
569 | "id": "metrics",
570 | "tool": "calculate_metrics",
571 | "params": {
572 | "url": "https://github.com/example/repo"
573 | },
574 | "priority": 3
575 | }
576 | ],
577 | "options": {
578 | "max_concurrent": 2,
579 | "fail_fast": false
580 | }
581 | }
582 | }
583 | ```
584 |
585 | ### 2. Health Check
586 |
587 | ```javascript
588 | // Basic health check
589 | {
590 | "tool": "health_check",
591 | "arguments": {
592 | "checks": ["api-limits", "system-health"],
593 | "options": {
594 | "include_metrics": true
595 | }
596 | }
597 | }
598 |
599 | // Comprehensive health check
600 | {
601 | "tool": "health_check",
602 | "arguments": {
603 | "checks": ["api-limits", "cache-status", "system-health", "dependencies"],
604 | "options": {
605 | "include_metrics": true,
606 | "include_diagnostics": true
607 | }
608 | }
609 | }
610 | ```
611 |
612 | ## 📈 Quality Metrics
613 |
614 | ### 1. Calculate Metrics
615 |
616 | ```javascript
617 | // Basic quality metrics
618 | {
619 | "tool": "calculate_metrics",
620 | "arguments": {
621 | "url": "https://github.com/example/repo",
622 | "options": {
623 | "metrics": ["complexity", "maintainability", "security"],
624 | "include_file_level": true
625 | }
626 | }
627 | }
628 |
629 | // Comprehensive metrics with trends
630 | {
631 | "tool": "calculate_metrics",
632 | "arguments": {
633 | "url": "https://github.com/example/repo",
634 | "options": {
635 | "metrics": ["complexity", "maintainability", "duplication", "security"],
636 | "include_file_level": true,
637 | "include_trend_analysis": true
638 | }
639 | }
640 | }
641 | ```
642 |
643 | ### 2. Dependency Analysis
644 |
645 | ```javascript
646 | // Security-focused dependency analysis
647 | {
648 | "tool": "analyze_dependencies",
649 | "arguments": {
650 | "url": "https://github.com/production-app/backend",
651 | "options": {
652 | "include_security_scan": true,
653 | "include_version_analysis": true,
654 | "check_outdated": true
655 | }
656 | }
657 | }
658 | ```
659 |
660 | ## 🔄 Workflow Examples
661 |
662 | ### 1. Complete Repository Analysis
663 |
664 | ```javascript
665 | // Step 1: Get repository overview
666 | {
667 | "tool": "fetch_repository_data",
668 | "arguments": {
669 | "url": "https://github.com/new-project/analysis-target"
670 | }
671 | }
672 |
673 | // Step 2: Analyze code structure
674 | {
675 | "tool": "analyze_code_structure",
676 | "arguments": {
677 | "url": "https://github.com/new-project/analysis-target",
678 | "options": {
679 | "include_complexity": true
680 | }
681 | }
682 | }
683 |
684 | // Step 3: Check dependencies
685 | {
686 | "tool": "analyze_dependencies",
687 | "arguments": {
688 | "url": "https://github.com/new-project/analysis-target",
689 | "options": {
690 | "include_security_scan": true
691 | }
692 | }
693 | }
694 |
695 | // Step 4: Get AI insights
696 | {
697 | "tool": "ai_explain_code",
698 | "arguments": {
699 | "url": "https://github.com/new-project/analysis-target",
700 | "explanation_type": "overview",
701 | "options": {
702 | "ai_model": "auto"
703 | }
704 | }
705 | }
706 | ```
707 |
708 | ### 2. Code Review Workflow
709 |
710 | ```javascript
711 | // Step 1: Quality validation
712 | {
713 | "tool": "validate_code_quality",
714 | "arguments": {
715 | "url": "https://github.com/team/pull-request-branch",
716 | "validation_types": ["security", "performance", "best-practices"]
717 | }
718 | }
719 |
720 | // Step 2: AI code review
721 | {
722 | "tool": "ai_code_review",
723 | "arguments": {
724 | "url": "https://github.com/team/pull-request-branch",
725 | "review_focus": ["security", "performance", "maintainability"],
726 | "options": {
727 | "ai_model": "anthropic/claude-3.5-sonnet",
728 | "severity_threshold": "medium"
729 | }
730 | }
731 | }
732 |
733 | // Step 3: Calculate metrics
734 | {
735 | "tool": "calculate_metrics",
736 | "arguments": {
737 | "url": "https://github.com/team/pull-request-branch",
738 | "options": {
739 | "metrics": ["complexity", "maintainability"]
740 | }
741 | }
742 | }
743 | ```
744 |
745 | ### 3. Refactoring Planning
746 |
747 | ```javascript
748 | // Step 1: Analyze current architecture
749 | {
750 | "tool": "analyze_architecture",
751 | "arguments": {
752 | "url": "https://github.com/legacy-app/monolith"
753 | }
754 | }
755 |
756 | // Step 2: Get AI refactoring suggestions
757 | {
758 | "tool": "ai_refactor_suggestions",
759 | "arguments": {
760 | "url": "https://github.com/legacy-app/monolith",
761 | "refactoring_goals": ["modernize", "performance", "maintainability"],
762 | "options": {
763 | "ai_model": "auto",
764 | "estimate_effort": true
765 | }
766 | }
767 | }
768 |
769 | // Step 3: Plan structure adaptation
770 | {
771 | "tool": "adapt_code_structure",
772 | "arguments": {
773 | "url": "https://github.com/legacy-app/monolith",
774 | "target_structure": {
775 | "framework": "react",
776 | "pattern": "clean"
777 | }
778 | }
779 | }
780 | ```
781 |
782 | ## 🎯 Best Practices
783 |
784 | ### 1. Model Selection
785 |
786 | ```javascript
787 | // Use auto for most cases
788 | {
789 | "options": {
790 | "ai_model": "auto"
791 | }
792 | }
793 |
794 | // Use specific models for specialized tasks
795 | {
796 | "options": {
797 | "ai_model": "anthropic/claude-3-opus" // For complex analysis
798 | }
799 | }
800 |
801 | // Cost-effective for batch operations
802 | {
803 | "options": {
804 | "ai_model": "openai/gpt-4o-mini" // For large-scale processing
805 | }
806 | }
807 | ```
808 |
809 | ### 2. Rate Limit Management
810 |
811 | ```javascript
812 | // Check limits before large operations
813 | {
814 | "tool": "health_check",
815 | "arguments": {
816 | "checks": ["api-limits"]
817 | }
818 | }
819 |
820 | // Use batch processing for multiple operations
821 | {
822 | "tool": "batch_process",
823 | "arguments": {
824 | "operations": [/* multiple operations */],
825 | "options": {
826 | "max_concurrent": 2 // Respect rate limits
827 | }
828 | }
829 | }
830 | ```
831 |
832 | ### 3. Chunking Best Practices
833 |
834 | ```javascript
835 | // Function to process all chunks
836 | async function processRepositoryInChunks(url, chunkSize = 'medium') {
837 | let chunkIndex = 0;
838 | let hasMore = true;
839 | const allFiles = {};
840 |
841 | while (hasMore) {
842 | const response = await callTool('fetch_repository_data', {
843 | url,
844 | options: {
845 | chunk_mode: true,
846 | chunk_index: chunkIndex,
847 | chunk_size: chunkSize
848 | }
849 | });
850 |
851 | if (response.success) {
852 | // Merge files from this chunk
853 | Object.assign(allFiles, response.data.structure.keyFiles);
854 |
855 | // Check if there are more chunks
856 | hasMore = response.data.chunkInfo.hasMore;
857 | chunkIndex = response.data.chunkInfo.nextChunkIndex;
858 | } else {
859 | break;
860 | }
861 | }
862 |
863 | return allFiles;
864 | }
865 |
866 | // Usage
867 | const allFiles = await processRepositoryInChunks('https://github.com/facebook/react');
868 | ```
869 |
870 | ### 4. Error Handling
871 |
872 | ```javascript
873 | // Always check response success
874 | const response = await callTool('fetch_repository_data', params);
875 | if (!response.success) {
876 | console.error('Tool failed:', response.error);
877 | // Handle error appropriately
878 | }
879 | ```
880 |
881 | This comprehensive guide covers the main usage patterns for all 18 tools in the CodeCompass MCP server. Each example includes realistic parameters and expected response formats to help you get started quickly.
```
--------------------------------------------------------------------------------
/src/services/openai.ts:
--------------------------------------------------------------------------------
```typescript
1 | import OpenAI from 'openai';
2 | import { GitHubRepoInfo, ChatContext, RefactoringPlan, ArchitectureExplanation } from '../types/index.js';
3 |
4 | export class OpenAIService {
5 | private openai: OpenAI;
6 | private currentConfig: {
7 | apiKey: string;
8 | model: string;
9 | systemPrompt: string;
10 | };
11 |
12 | // Model characteristics for intelligent selection and warnings
13 | private modelCharacteristics = {
14 | 'anthropic/claude-3.5-sonnet': { speed: 'fast', cost: 'medium', quality: 'high', recommended: true },
15 | 'anthropic/claude-3-opus': { speed: 'slow', cost: 'high', quality: 'highest', recommended: false },
16 | 'anthropic/claude-3-haiku': { speed: 'fastest', cost: 'low', quality: 'good', recommended: true },
17 | 'openai/gpt-4o': { speed: 'fast', cost: 'medium', quality: 'high', recommended: true },
18 | 'openai/gpt-4o-mini': { speed: 'fastest', cost: 'low', quality: 'good', recommended: true },
19 | 'openai/gpt-4-turbo': { speed: 'medium', cost: 'medium', quality: 'high', recommended: true },
20 | 'openai/o1-mini': { speed: 'slow', cost: 'high', quality: 'highest', recommended: false },
21 | 'openai/o1-preview': { speed: 'slowest', cost: 'highest', quality: 'highest', recommended: false },
22 | 'meta-llama/llama-3.1-405b-instruct': { speed: 'medium', cost: 'high', quality: 'high', recommended: false },
23 | };
24 |
25 | constructor() {
26 | this.currentConfig = {
27 | apiKey: process.env.OPENROUTER_API_KEY || process.env.OPENAI_API_KEY || '',
28 | model: process.env.OPENAI_MODEL || 'anthropic/claude-3.5-sonnet',
29 | systemPrompt: `You are CodeCompass AI, an expert assistant specialized in code analysis, refactoring, and architectural guidance. Your primary role is to help developers understand, analyze, and refactor code from GitHub repositories for integration into their own projects.
30 |
31 | Core Capabilities:
32 | 1. Code Analysis: Understand code structure, patterns, and dependencies
33 | 2. Refactoring Guidance: Suggest improvements and modernization strategies
34 | 3. Architecture Explanation: Explain design patterns and architectural decisions
35 | 4. Integration Support: Help adapt code for different projects and frameworks
36 | 5. Best Practices: Recommend coding standards and optimization techniques
37 |
38 | Response Guidelines:
39 | - Provide clear, actionable advice with code examples
40 | - Focus on practical solutions that can be implemented
41 | - Consider maintainability, performance, and scalability
42 | - Explain the reasoning behind recommendations
43 | - Suggest multiple approaches when appropriate
44 | - Include potential risks and trade-offs
45 |
46 | Communication Style:
47 | - Be concise but comprehensive
48 | - Use code examples to illustrate points
49 | - Structure responses with clear headings
50 | - Prioritize actionable insights over theoretical discussions`,
51 | };
52 |
53 | this.openai = new OpenAI({
54 | apiKey: this.currentConfig.apiKey,
55 | baseURL: 'https://openrouter.ai/api/v1',
56 | defaultHeaders: {
57 | 'HTTP-Referer': 'https://github.com/codecompass/codecompass-mcp',
58 | 'X-Title': 'CodeCompass MCP Server',
59 | },
60 | });
61 | }
62 |
63 | updateConfig(config: { apiKey?: string; model?: string; systemPrompt?: string }) {
64 | if (config.apiKey) {
65 | this.currentConfig.apiKey = config.apiKey;
66 | this.openai = new OpenAI({
67 | apiKey: config.apiKey,
68 | baseURL: 'https://openrouter.ai/api/v1',
69 | defaultHeaders: {
70 | 'HTTP-Referer': 'https://github.com/codecompass/codecompass-mcp',
71 | 'X-Title': 'CodeCompass MCP Server',
72 | },
73 | });
74 | }
75 | if (config.model) this.currentConfig.model = config.model;
76 | if (config.systemPrompt) this.currentConfig.systemPrompt = config.systemPrompt;
77 | }
78 |
79 | // Intelligent model selection based on task type
80 | private selectModel(requestedModel: string | undefined, taskType: 'review' | 'explain' | 'refactor', isBatchJob: boolean = false): string {
81 | if (requestedModel && requestedModel !== 'auto') {
82 | return requestedModel;
83 | }
84 |
85 | // Auto model selection logic
86 | if (isBatchJob) {
87 | // For batch jobs, prefer faster, cost-effective models
88 | return 'openai/gpt-4o-mini';
89 | }
90 |
91 | // Task-specific model selection
92 | switch (taskType) {
93 | case 'review':
94 | return 'anthropic/claude-3.5-sonnet'; // Good balance of quality and speed
95 | case 'explain':
96 | return 'openai/gpt-4o'; // Good for explanations
97 | case 'refactor':
98 | return 'anthropic/claude-3.5-sonnet'; // Good for strategic thinking
99 | default:
100 | return this.currentConfig.model;
101 | }
102 | }
103 |
104 | // Generate model warning message
105 | private generateModelWarning(model: string, isBatchJob: boolean = false): string | undefined {
106 | const characteristics = this.modelCharacteristics[model as keyof typeof this.modelCharacteristics];
107 | if (!characteristics) return undefined;
108 |
109 | const warnings = [];
110 |
111 | if (isBatchJob) {
112 | if (characteristics.speed === 'slow' || characteristics.speed === 'slowest') {
113 | warnings.push(`⚠️ Model ${model} is ${characteristics.speed} - batch job may take significant time`);
114 | }
115 | if (characteristics.cost === 'high' || characteristics.cost === 'highest') {
116 | warnings.push(`💰 Model ${model} has ${characteristics.cost} cost - batch job may be expensive`);
117 | }
118 | } else {
119 | if (characteristics.speed === 'slowest') {
120 | warnings.push(`⚠️ Model ${model} is very slow - expect longer response times`);
121 | }
122 | if (characteristics.cost === 'highest') {
123 | warnings.push(`💰 Model ${model} has highest cost - consider alternatives for frequent use`);
124 | }
125 | }
126 |
127 | return warnings.length > 0 ? warnings.join('\n') : undefined;
128 | }
129 |
130 | // Log model selection for audit/debug
131 | private logModelSelection(model: string, requestedModel: string | undefined, taskType: string, isBatchJob: boolean) {
132 | const timestamp = new Date().toISOString();
133 | const logEntry = {
134 | timestamp,
135 | taskType,
136 | requestedModel: requestedModel || 'auto',
137 | selectedModel: model,
138 | isBatchJob,
139 | characteristics: this.modelCharacteristics[model as keyof typeof this.modelCharacteristics]
140 | };
141 |
142 | console.log(`[MODEL_SELECTION] ${JSON.stringify(logEntry)}`);
143 | }
144 |
145 | async chatWithRepository(url: string, message: string, context?: ChatContext, model?: string, isBatchJob: boolean = false): Promise<{ content: string; modelUsed: string; warning?: string }> {
146 | if (!this.currentConfig.apiKey) {
147 | throw new Error('OpenRouter API key not configured. Please set OPENROUTER_API_KEY environment variable.');
148 | }
149 |
150 | // Select model intelligently
151 | const modelToUse = this.selectModel(model, 'explain', isBatchJob);
152 |
153 | // Log model selection
154 | this.logModelSelection(modelToUse, model, 'explain_code', isBatchJob);
155 |
156 | // Generate warning if needed
157 | const warning = this.generateModelWarning(modelToUse, isBatchJob);
158 |
159 | try {
160 | const messages: any[] = [
161 | { role: 'system', content: this.currentConfig.systemPrompt },
162 | ];
163 |
164 | // Add context if provided
165 | if (context) {
166 | const contextMessage = this.buildContextMessage(context);
167 | messages.push({ role: 'system', content: contextMessage });
168 |
169 | // Add conversation history
170 | if (context.conversationHistory) {
171 | messages.push(...context.conversationHistory.map(msg => ({
172 | role: msg.role,
173 | content: msg.content,
174 | })));
175 | }
176 | }
177 |
178 | messages.push({ role: 'user', content: message });
179 |
180 | const response = await this.openai.chat.completions.create({
181 | model: modelToUse,
182 | messages,
183 | max_tokens: 2000,
184 | temperature: 0.7,
185 | });
186 |
187 | const content = response.choices[0].message.content || 'I apologize, but I couldn\'t generate a response.';
188 |
189 | return {
190 | content,
191 | modelUsed: modelToUse,
192 | warning
193 | };
194 | } catch (error: any) {
195 | console.error('OpenAI API error:', error);
196 | throw new Error(`Failed to generate response: ${error.message}`);
197 | }
198 | }
199 |
200 | async suggestRefactoringPlan(url: string, targetProject: any, goals?: string[], model?: string, isBatchJob: boolean = false): Promise<{ content: string; modelUsed: string; warning?: string }> {
201 | if (!this.currentConfig.apiKey) {
202 | throw new Error('OpenRouter API key not configured. Please set OPENROUTER_API_KEY environment variable.');
203 | }
204 |
205 | // Select model intelligently
206 | const modelToUse = this.selectModel(model, 'refactor', isBatchJob);
207 |
208 | // Log model selection
209 | this.logModelSelection(modelToUse, model, 'refactor_suggestions', isBatchJob);
210 |
211 | // Generate warning if needed
212 | const warning = this.generateModelWarning(modelToUse, isBatchJob);
213 |
214 | const prompt = `
215 | Analyze the repository at ${url} and create a comprehensive refactoring plan.
216 |
217 | Target Project Context:
218 | - Framework: ${targetProject.framework}
219 | - Language: ${targetProject.language || 'Not specified'}
220 | - Constraints: ${targetProject.constraints?.join(', ') || 'None specified'}
221 | - Timeline: ${targetProject.timeline || 'Not specified'}
222 |
223 | Refactoring Goals:
224 | ${goals?.map(goal => `- ${goal}`).join('\n') || '- General modernization and improvement'}
225 |
226 | Please provide a detailed refactoring plan that includes:
227 | 1. Executive Summary
228 | 2. Phase-by-phase breakdown
229 | 3. Time estimates for each phase
230 | 4. Risk assessment
231 | 5. Success metrics
232 | 6. Recommended tools and resources
233 |
234 | Format the response as a structured plan with clear sections and actionable items.
235 | `;
236 |
237 | try {
238 | const response = await this.openai.chat.completions.create({
239 | model: modelToUse,
240 | messages: [
241 | { role: 'system', content: this.currentConfig.systemPrompt },
242 | { role: 'user', content: prompt },
243 | ],
244 | max_tokens: 1800,
245 | temperature: 0.7,
246 | });
247 |
248 | const content = response.choices[0].message.content || 'Failed to generate refactoring plan.';
249 |
250 | return {
251 | content,
252 | modelUsed: modelToUse,
253 | warning
254 | };
255 | } catch (error: any) {
256 | throw new Error(`Failed to generate refactoring plan: ${error.message}`);
257 | }
258 | }
259 |
260 | async explainArchitecture(url: string, repositoryInfo?: GitHubRepoInfo): Promise<string> {
261 | if (!this.currentConfig.apiKey) {
262 | throw new Error('OpenRouter API key not configured. Please set OPENROUTER_API_KEY environment variable.');
263 | }
264 |
265 | let contextInfo = '';
266 | if (repositoryInfo) {
267 | contextInfo = `
268 | Repository Information:
269 | - Name: ${repositoryInfo.name}
270 | - Description: ${repositoryInfo.description || 'No description'}
271 | - Primary Language: ${repositoryInfo.language || 'Unknown'}
272 | - File Count: ${repositoryInfo.fileCount}
273 | - Key Files: ${Object.keys(repositoryInfo.keyFiles).join(', ')}
274 |
275 | File Structure:
276 | ${JSON.stringify(repositoryInfo.fileTree, null, 2)}
277 |
278 | Key File Contents:
279 | ${Object.entries(repositoryInfo.keyFiles).map(([path, content]) =>
280 | `--- ${path} ---\n${content.substring(0, 1000)}${content.length > 1000 ? '...' : ''}`
281 | ).join('\n\n')}
282 | `;
283 | }
284 |
285 | const prompt = `
286 | Explain the architecture of the repository at ${url} in detail.
287 |
288 | ${contextInfo}
289 |
290 | Please provide a comprehensive architectural explanation that covers:
291 | 1. Overall Architecture Overview
292 | 2. Design Patterns Used
293 | 3. Project Structure and Organization
294 | 4. Data Flow and Components
295 | 5. Dependencies and External Integrations
296 | 6. Strengths and Potential Improvements
297 | 7. Scalability Considerations
298 | 8. Recommendations for Different Use Cases
299 |
300 | Make the explanation accessible to developers who want to understand and potentially adapt this architecture for their own projects.
301 | `;
302 |
303 | try {
304 | const response = await this.openai.chat.completions.create({
305 | model: this.currentConfig.model,
306 | messages: [
307 | { role: 'system', content: this.currentConfig.systemPrompt },
308 | { role: 'user', content: prompt },
309 | ],
310 | max_tokens: 1800,
311 | temperature: 0.7,
312 | });
313 |
314 | return response.choices[0].message.content || 'Failed to generate architecture explanation.';
315 | } catch (error: any) {
316 | throw new Error(`Failed to explain architecture: ${error.message}`);
317 | }
318 | }
319 |
320 | async generateCodeReview(code: string, language: string, focusAreas?: string[], model?: string, isBatchJob: boolean = false): Promise<{ content: string; modelUsed: string; warning?: string }> {
321 | if (!this.currentConfig.apiKey) {
322 | throw new Error('OpenRouter API key not configured. Please set OPENROUTER_API_KEY environment variable.');
323 | }
324 |
325 | // Select model intelligently
326 | const modelToUse = this.selectModel(model, 'review', isBatchJob);
327 |
328 | // Log model selection
329 | this.logModelSelection(modelToUse, model, 'code_review', isBatchJob);
330 |
331 | // Generate warning if needed
332 | const warning = this.generateModelWarning(modelToUse, isBatchJob);
333 |
334 | const prompt = `
335 | Please provide a comprehensive code review for the following ${language} code.
336 |
337 | ${focusAreas ? `Focus Areas: ${focusAreas.join(', ')}` : ''}
338 |
339 | Code to review:
340 | \`\`\`${language}
341 | ${code}
342 | \`\`\`
343 |
344 | Please provide feedback on:
345 | 1. Code Quality and Best Practices
346 | 2. Potential Bugs and Issues
347 | 3. Performance Optimizations
348 | 4. Security Considerations
349 | 5. Maintainability and Readability
350 | 6. Refactoring Suggestions
351 | 7. Testing Recommendations
352 |
353 | Format your response with clear sections and provide specific code examples for improvements.
354 | `;
355 |
356 | try {
357 | const response = await this.openai.chat.completions.create({
358 | model: modelToUse,
359 | messages: [
360 | { role: 'system', content: this.currentConfig.systemPrompt },
361 | { role: 'user', content: prompt },
362 | ],
363 | max_tokens: 1800,
364 | temperature: 0.7,
365 | });
366 |
367 | const content = response.choices[0].message.content || 'Failed to generate code review.';
368 |
369 | return {
370 | content,
371 | modelUsed: modelToUse,
372 | warning
373 | };
374 | } catch (error: any) {
375 | throw new Error(`Failed to generate code review: ${error.message}`);
376 | }
377 | }
378 |
379 | async generateRefactoringPlan(repositoryInfo: GitHubRepoInfo, goals: string[]): Promise<RefactoringPlan> {
380 | if (!this.currentConfig.apiKey) {
381 | throw new Error('OpenRouter API key not configured. Please set OPENROUTER_API_KEY environment variable.');
382 | }
383 |
384 | const prompt = `
385 | Create a detailed refactoring plan for the following repository:
386 |
387 | Repository: ${repositoryInfo.name}
388 | Description: ${repositoryInfo.description}
389 | Language: ${repositoryInfo.language}
390 | File Count: ${repositoryInfo.fileCount}
391 |
392 | Goals: ${goals.join(', ')}
393 |
394 | Please provide a JSON response with the following structure:
395 | {
396 | "overview": "Brief overview of the refactoring plan",
397 | "phases": [
398 | {
399 | "name": "Phase name",
400 | "description": "Phase description",
401 | "tasks": [
402 | {
403 | "name": "Task name",
404 | "description": "Task description",
405 | "type": "extract|transform|modernize|optimize|test",
406 | "files": ["file1.js", "file2.js"],
407 | "estimatedTimeHours": 4,
408 | "priority": "low|medium|high"
409 | }
410 | ],
411 | "estimatedTimeHours": 16,
412 | "dependencies": ["Phase 1", "Phase 2"]
413 | }
414 | ],
415 | "estimatedTimeHours": 40,
416 | "risks": ["Risk 1", "Risk 2"],
417 | "recommendations": ["Recommendation 1", "Recommendation 2"]
418 | }
419 | `;
420 |
421 | try {
422 | const response = await this.openai.chat.completions.create({
423 | model: this.currentConfig.model,
424 | messages: [
425 | { role: 'system', content: this.currentConfig.systemPrompt },
426 | { role: 'user', content: prompt },
427 | ],
428 | max_tokens: 2000,
429 | temperature: 0.7,
430 | });
431 |
432 | const content = response.choices[0].message.content;
433 | if (!content) {
434 | throw new Error('No response generated');
435 | }
436 |
437 | try {
438 | return JSON.parse(content);
439 | } catch (parseError) {
440 | // If JSON parsing fails, return a basic structure
441 | return {
442 | overview: content.substring(0, 200) + '...',
443 | phases: [],
444 | estimatedTimeHours: 0,
445 | risks: ['Failed to parse detailed plan'],
446 | recommendations: ['Review the generated plan manually'],
447 | };
448 | }
449 | } catch (error: any) {
450 | throw new Error(`Failed to generate refactoring plan: ${error.message}`);
451 | }
452 | }
453 |
454 | async generateWelcomeMessage(repositoryInfo: GitHubRepoInfo): Promise<string> {
455 | if (!this.currentConfig.apiKey) {
456 | // Return a default welcome message if no API key is configured
457 | return `Hello! I've analyzed the ${repositoryInfo.name} repository and I'm ready to help you understand and refactor the codebase.
458 |
459 | ⚠️ **Setup Required**: To enable AI-powered features, please configure your OpenAI API key.
460 |
461 | Repository Overview:
462 | - **${repositoryInfo.name}** by ${repositoryInfo.owner}
463 | - Language: ${repositoryInfo.language || 'Multiple'}
464 | - Files: ${repositoryInfo.fileCount}
465 | - Stars: ${repositoryInfo.stars}
466 |
467 | I can help you with:
468 | - Code analysis and understanding
469 | - Refactoring suggestions
470 | - Architecture explanations
471 | - Component extraction
472 | - Integration guidance
473 |
474 | What would you like to know about this repository?`;
475 | }
476 |
477 | const prompt = `
478 | Generate a welcoming introduction message for a repository analysis chat. The repository is:
479 | - Name: ${repositoryInfo.name}
480 | - Description: ${repositoryInfo.description || 'No description'}
481 | - Primary Language: ${repositoryInfo.language || 'Unknown'}
482 | - ${repositoryInfo.fileCount} files
483 | - ${repositoryInfo.stars} stars
484 |
485 | Create a friendly, professional greeting that:
486 | 1. Welcomes the user
487 | 2. Briefly summarizes what I found in the repository
488 | 3. Mentions what I can help with regarding refactoring and code analysis
489 | 4. Asks what they'd like to know
490 |
491 | Keep it concise (3-4 sentences) and engaging.
492 | `;
493 |
494 | try {
495 | const response = await this.openai.chat.completions.create({
496 | model: this.currentConfig.model,
497 | messages: [
498 | { role: 'system', content: this.currentConfig.systemPrompt },
499 | { role: 'user', content: prompt },
500 | ],
501 | max_tokens: 300,
502 | temperature: 0.8,
503 | });
504 |
505 | return response.choices[0].message.content ||
506 | `Hello! I've analyzed the ${repositoryInfo.name} repository. I can help you understand the codebase, suggest refactoring opportunities, and guide you through integrating components into your own projects. What would you like to explore?`;
507 | } catch (error) {
508 | return `Hello! I've analyzed the ${repositoryInfo.name} repository. I can help you understand the codebase, suggest refactoring opportunities, and guide you through integrating components into your own projects. What would you like to explore?`;
509 | }
510 | }
511 |
512 | private buildContextMessage(context: ChatContext): string {
513 | let contextMessage = `Repository Context: ${context.repositoryUrl}\n`;
514 |
515 | if (context.currentFile) {
516 | contextMessage += `Current File: ${context.currentFile}\n`;
517 | }
518 |
519 | if (context.selectedCode) {
520 | contextMessage += `Selected Code:\n\`\`\`\n${context.selectedCode}\n\`\`\`\n`;
521 | }
522 |
523 | if (context.refactoringGoals && context.refactoringGoals.length > 0) {
524 | contextMessage += `Refactoring Goals: ${context.refactoringGoals.join(', ')}\n`;
525 | }
526 |
527 | return contextMessage;
528 | }
529 | }
```