# Directory Structure
```
├── .editorconfig
├── .gitignore
├── .prettierrc
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│ └── index.ts
├── tsconfig.json
├── vitest.config.mts
├── worker-configuration.d.ts
└── wrangler.jsonc
```
# Files
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
```
1 | {
2 | "printWidth": 140,
3 | "singleQuote": true,
4 | "semi": true,
5 | "useTabs": true
6 | }
7 |
```
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
```
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = tab
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.yml]
12 | indent_style = space
13 |
```
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Logs
2 |
3 | logs
4 | _.log
5 | npm-debug.log_
6 | yarn-debug.log*
7 | yarn-error.log*
8 | lerna-debug.log*
9 | .pnpm-debug.log*
10 |
11 | # Diagnostic reports (https://nodejs.org/api/report.html)
12 |
13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
14 |
15 | # Runtime data
16 |
17 | pids
18 | _.pid
19 | _.seed
20 | \*.pid.lock
21 |
22 | # Directory for instrumented libs generated by jscoverage/JSCover
23 |
24 | lib-cov
25 |
26 | # Coverage directory used by tools like istanbul
27 |
28 | coverage
29 | \*.lcov
30 |
31 | # nyc test coverage
32 |
33 | .nyc_output
34 |
35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
36 |
37 | .grunt
38 |
39 | # Bower dependency directory (https://bower.io/)
40 |
41 | bower_components
42 |
43 | # node-waf configuration
44 |
45 | .lock-wscript
46 |
47 | # Compiled binary addons (https://nodejs.org/api/addons.html)
48 |
49 | build/Release
50 |
51 | # Dependency directories
52 |
53 | node_modules/
54 | jspm_packages/
55 |
56 | # Snowpack dependency directory (https://snowpack.dev/)
57 |
58 | web_modules/
59 |
60 | # TypeScript cache
61 |
62 | \*.tsbuildinfo
63 |
64 | # Optional npm cache directory
65 |
66 | .npm
67 |
68 | # Optional eslint cache
69 |
70 | .eslintcache
71 |
72 | # Optional stylelint cache
73 |
74 | .stylelintcache
75 |
76 | # Microbundle cache
77 |
78 | .rpt2_cache/
79 | .rts2_cache_cjs/
80 | .rts2_cache_es/
81 | .rts2_cache_umd/
82 |
83 | # Optional REPL history
84 |
85 | .node_repl_history
86 |
87 | # Output of 'npm pack'
88 |
89 | \*.tgz
90 |
91 | # Yarn Integrity file
92 |
93 | .yarn-integrity
94 |
95 | # dotenv environment variable files
96 |
97 | .env
98 | .env.development.local
99 | .env.test.local
100 | .env.production.local
101 | .env.local
102 |
103 | # parcel-bundler cache (https://parceljs.org/)
104 |
105 | .cache
106 | .parcel-cache
107 |
108 | # Next.js build output
109 |
110 | .next
111 | out
112 |
113 | # Nuxt.js build / generate output
114 |
115 | .nuxt
116 | dist
117 |
118 | # Gatsby files
119 |
120 | .cache/
121 |
122 | # Comment in the public line in if your project uses Gatsby and not Next.js
123 |
124 | # https://nextjs.org/blog/next-9-1#public-directory-support
125 |
126 | # public
127 |
128 | # vuepress build output
129 |
130 | .vuepress/dist
131 |
132 | # vuepress v2.x temp and cache directory
133 |
134 | .temp
135 | .cache
136 |
137 | # Docusaurus cache and generated files
138 |
139 | .docusaurus
140 |
141 | # Serverless directories
142 |
143 | .serverless/
144 |
145 | # FuseBox cache
146 |
147 | .fusebox/
148 |
149 | # DynamoDB Local files
150 |
151 | .dynamodb/
152 |
153 | # TernJS port file
154 |
155 | .tern-port
156 |
157 | # Stores VSCode versions used for testing VSCode extensions
158 |
159 | .vscode-test
160 |
161 | # yarn v2
162 |
163 | .yarn/cache
164 | .yarn/unplugged
165 | .yarn/build-state.yml
166 | .yarn/install-state.gz
167 | .pnp.\*
168 |
169 | # wrangler project
170 |
171 | .dev.vars
172 | .wrangler/
173 |
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Code Explainer MCP
2 |
3 | A Cloudflare Worker that serves as an MCP (Model Context Protocol) server for code explanation. It analyzes and explains code with a comprehensive breakdown of structure and functionality.
4 |
5 | 
6 |
7 | ## Features
8 |
9 | - **Architecture Diagram**: Generates an ASCII diagram showing the overall structure, relationships between components, and data flow.
10 | - **Core Functionality Analysis**: Identifies and explains the primary purpose of the code based on pattern recognition.
11 | - **Component Breakdown**: Lists all main classes and functions with brief descriptions of their roles.
12 | - **Multi-language Support**: Analyzes code in various programming languages including JavaScript, TypeScript, Python, Java, C#, and more.
13 | - **JSDoc/Docstring Recognition**: Extracts and utilizes existing documentation in the code.
14 | - **Secure API**: Bearer token authentication to secure your endpoints.
15 |
16 | ## How It Works
17 |
18 | The Code Explainer analyzes source code using a combination of techniques:
19 |
20 | 1. **Pattern Recognition**: Identifies code structures and common patterns
21 | 2. **Relationship Analysis**: Maps dependencies between components
22 | 3. **Documentation Extraction**: Prioritizes existing documentation comments
23 | 4. **Architecture Visualization**: Creates ASCII diagrams of the code structure
24 | 5. **Component Description**: Provides semantic descriptions of functions and classes
25 |
26 | All processing happens within the Cloudflare Worker with no external dependencies.
27 |
28 | ## Installation
29 |
30 | ### Prerequisites
31 |
32 | - [Node.js](https://nodejs.org/) (version 12 or higher)
33 | - [Wrangler](https://developers.cloudflare.com/workers/wrangler/get-started/) (Cloudflare Workers CLI)
34 | - A Cloudflare account
35 |
36 | ### Setup
37 |
38 | 1. Clone this repository:
39 | ```bash
40 | git clone https://github.com/BillDuke13/code-explainer-mcp.git
41 | cd code-explainer-mcp
42 | ```
43 |
44 | 2. Install dependencies:
45 | ```bash
46 | npm install
47 | ```
48 |
49 | 3. Configure your secret key:
50 | - Edit `wrangler.jsonc` and replace `YOUR_SECRET_KEY_HERE` with your chosen secret key, or
51 | - Use Cloudflare secrets (recommended for production):
52 | ```bash
53 | wrangler secret put SHARED_SECRET
54 | ```
55 |
56 | 4. Deploy to Cloudflare Workers:
57 | ```bash
58 | npm run deploy
59 | ```
60 |
61 | ## Usage
62 |
63 | ### API Endpoint
64 |
65 | Send a POST request to your worker URL with the following JSON body:
66 |
67 | ```json
68 | {
69 | "method": "explainCode",
70 | "params": ["your code here", "programming language"]
71 | }
72 | ```
73 |
74 | Include the Authorization header with your secret key:
75 | ```
76 | Authorization: Bearer YOUR_SECRET_KEY_HERE
77 | ```
78 |
79 | ### Response Format
80 |
81 | The response will be a JSON object with a `result` field containing the code analysis:
82 |
83 | ```json
84 | {
85 | "result": "# Code Analysis for JavaScript Code\n\n## Architecture Diagram\n...\n\n## Core Functionality\n..."
86 | }
87 | ```
88 |
89 | ### Example Usage
90 |
91 | #### JavaScript (Browser)
92 |
93 | ```javascript
94 | async function explainCode(code, language) {
95 | const response = await fetch('https://your-worker-url.workers.dev', {
96 | method: 'POST',
97 | headers: {
98 | 'Content-Type': 'application/json',
99 | 'Authorization': 'Bearer YOUR_SECRET_KEY_HERE',
100 | },
101 | body: JSON.stringify({
102 | method: "explainCode",
103 | params: [code, language]
104 | }),
105 | });
106 |
107 | if (!response.ok) {
108 | throw new Error(`HTTP error! status: ${response.status}`);
109 | }
110 |
111 | const data = await response.json();
112 | return data.result;
113 | }
114 |
115 | // Example usage
116 | const jsCode = `function add(a, b) { return a + b; }`;
117 | explainCode(jsCode, "javascript")
118 | .then(explanation => console.log(explanation))
119 | .catch(error => console.error('Error:', error));
120 | ```
121 |
122 | #### Python (Requests)
123 |
124 | ```python
125 | import requests
126 | import json
127 |
128 | def explain_code(code, language, api_url, secret_key):
129 | headers = {
130 | 'Content-Type': 'application/json',
131 | 'Authorization': f'Bearer {secret_key}'
132 | }
133 |
134 | payload = {
135 | 'method': 'explainCode',
136 | 'params': [code, language]
137 | }
138 |
139 | response = requests.post(api_url, headers=headers, json=payload)
140 | response.raise_for_status()
141 |
142 | return response.json()['result']
143 |
144 | # Example usage
145 | code = "def hello(): print('Hello, world!')"
146 | explanation = explain_code(code, "python", "https://your-worker-url.workers.dev", "YOUR_SECRET_KEY_HERE")
147 | print(explanation)
148 | ```
149 |
150 | #### Node.js (Axios)
151 |
152 | ```javascript
153 | const axios = require('axios');
154 |
155 | async function explainCode(code, language) {
156 | try {
157 | const response = await axios.post('https://your-worker-url.workers.dev', {
158 | method: 'explainCode',
159 | params: [code, language]
160 | }, {
161 | headers: {
162 | 'Content-Type': 'application/json',
163 | 'Authorization': 'Bearer YOUR_SECRET_KEY_HERE'
164 | }
165 | });
166 |
167 | return response.data.result;
168 | } catch (error) {
169 | console.error('Error:', error.response ? error.response.data : error.message);
170 | throw error;
171 | }
172 | }
173 |
174 | // Example usage
175 | const codeToAnalyze = `
176 | class Person {
177 | constructor(name) {
178 | this.name = name;
179 | }
180 |
181 | sayHello() {
182 | return \`Hello, my name is \${this.name}\`;
183 | }
184 | }
185 | `;
186 |
187 | explainCode(codeToAnalyze, 'javascript')
188 | .then(explanation => console.log(explanation))
189 | .catch(err => console.error('Failed to explain code:', err));
190 | ```
191 |
192 | ## Local Development
193 |
194 | 1. Clone the repository and install dependencies:
195 | ```bash
196 | git clone https://github.com/BillDuke13/code-explainer-mcp.git
197 | cd code-explainer-mcp
198 | npm install
199 | ```
200 |
201 | 2. Run the development server:
202 | ```bash
203 | wrangler dev
204 | ```
205 |
206 | 3. Test the endpoint locally:
207 | ```bash
208 | curl -X POST http://localhost:8787 \
209 | -H "Content-Type: application/json" \
210 | -H "Authorization: Bearer YOUR_SECRET_KEY_HERE" \
211 | -d '{"method":"explainCode","params":["function hello() { return \"Hello World\"; }","javascript"]}'
212 | ```
213 |
214 | ### Development Guidelines
215 |
216 | - Follow TypeScript best practices
217 | - Add comments for complex logic
218 | - Update documentation for public API changes
219 | - Add tests for new features
220 |
221 | ## Security
222 |
223 | - The API is secured with Bearer token authentication
224 | - Use environment secrets for storing the shared secret in production
225 | - Do not commit your actual secret key to version control
226 | - Rate limiting is recommended for production deployments
227 |
228 | ## License
229 |
230 | This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
231 |
```
--------------------------------------------------------------------------------
/worker-configuration.d.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Generated by Wrangler
2 | // After adding bindings to `wrangler.jsonc`, regenerate this interface via `npm run cf-typegen`
3 | interface Env {
4 | SHARED_SECRET: string;
5 | }
6 |
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "code-explainer-mcp",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "deploy": "workers-mcp docgen src/index.ts && wrangler deploy",
7 | "dev": "wrangler dev",
8 | "start": "wrangler dev",
9 | "test": "vitest",
10 | "cf-typegen": "wrangler types"
11 | },
12 | "devDependencies": {
13 | "@cloudflare/vitest-pool-workers": "^0.6.4",
14 | "@cloudflare/workers-types": "^4.20250224.0",
15 | "typescript": "^5.5.2",
16 | "vitest": "~2.1.9",
17 | "wrangler": "^3.111.0"
18 | },
19 | "dependencies": {
20 | "workers-mcp": "^0.0.13"
21 | }
22 | }
23 |
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
4 |
5 | /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
6 | "target": "es2021",
7 | /* Specify a set of bundled library declaration files that describe the target runtime environment. */
8 | "lib": ["es2021"],
9 | /* Specify what JSX code is generated. */
10 | "jsx": "react-jsx",
11 |
12 | /* Specify what module code is generated. */
13 | "module": "es2022",
14 | /* Specify how TypeScript looks up a file from a given module specifier. */
15 | "moduleResolution": "Bundler",
16 | /* Specify type package names to be included without being referenced in a source file. */
17 | "types": [
18 | "@cloudflare/workers-types/2023-07-01"
19 | ],
20 | /* Enable importing .json files */
21 | "resolveJsonModule": true,
22 |
23 | /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
24 | "allowJs": true,
25 | /* Enable error reporting in type-checked JavaScript files. */
26 | "checkJs": false,
27 |
28 | /* Disable emitting files from a compilation. */
29 | "noEmit": true,
30 |
31 | /* Ensure that each file can be safely transpiled without relying on other imports. */
32 | "isolatedModules": true,
33 | /* Allow 'import x from y' when a module doesn't have a default export. */
34 | "allowSyntheticDefaultImports": true,
35 | /* Ensure that casing is correct in imports. */
36 | "forceConsistentCasingInFileNames": true,
37 |
38 | /* Enable all strict type-checking options. */
39 | "strict": true,
40 |
41 | /* Skip type checking all .d.ts files. */
42 | "skipLibCheck": true
43 | },
44 | "exclude": ["test"],
45 | "include": ["worker-configuration.d.ts", "src/**/*.ts"]
46 | }
47 |
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Code Explainer MCP - A Cloudflare Worker that analyzes and explains code
3 | */
4 |
5 | // Define the environment interface
6 | interface Env {
7 | SHARED_SECRET: string;
8 | }
9 |
10 | /**
11 | * Analyzes and explains code with a comprehensive breakdown of structure and functionality.
12 | *
13 | * @param code {string} The source code to analyze
14 | * @param programming_language {string} The programming language of the code
15 | * @return {string} A detailed analysis of the code
16 | */
17 | async function explainCode(code: string, programming_language: string): Promise<string> {
18 | // Create a basic architecture diagram (ASCII)
19 | const architectureDiagram = generateArchitectureDiagram(code, programming_language);
20 |
21 | // Extract core functionality
22 | const coreFunctionality = extractCoreFunctionality(code, programming_language);
23 |
24 | // Extract main classes and functions
25 | const { mainClasses, mainFunctions } = extractComponents(code, programming_language);
26 |
27 | // Format the response according to the prompt template
28 | const response = `
29 | # Code Analysis for ${programming_language} Code
30 |
31 | ## Architecture Diagram
32 | \`\`\`
33 | ${architectureDiagram}
34 | \`\`\`
35 |
36 | ## Core Functionality
37 | ${coreFunctionality}
38 |
39 | ## Main Classes:
40 | ${mainClasses.map(c => `- ${c.name}: ${c.description}`).join('\n')}
41 |
42 | ## Main Functions:
43 | ${mainFunctions.map(f => `- ${f.name}: ${f.description}`).join('\n')}
44 |
45 | Would you like me to explain all the functions and classes in detail? Or are you more interested in a specific part?
46 | `;
47 |
48 | return response;
49 | }
50 |
51 | /**
52 | * Generates a comprehensive ASCII architecture diagram based on code analysis.
53 | *
54 | * @param code {string} The source code to analyze
55 | * @param programming_language {string} The programming language of the code
56 | * @return {string} An ASCII diagram representing the code architecture
57 | */
58 | function generateArchitectureDiagram(code: string, programming_language: string): string {
59 | // Detect language-specific patterns
60 | let classRegex, functionRegex, methodRegex, importRegex;
61 |
62 | switch(programming_language.toLowerCase()) {
63 | case 'javascript':
64 | case 'typescript':
65 | case 'js':
66 | case 'ts':
67 | classRegex = /class\s+(\w+)(?:\s+extends\s+(\w+))?/g;
68 | functionRegex = /function\s+(\w+)/g;
69 | methodRegex = /(\w+)\s*\([^)]*\)\s*{/g;
70 | importRegex = /import\s+(?:{[^}]*}|[^{;]*)(?:\s+from)?\s+['"]([^'"]+)['"]/g;
71 | break;
72 | case 'python':
73 | classRegex = /class\s+(\w+)(?:\(([^)]+)\))?:/g;
74 | functionRegex = /def\s+(\w+)/g;
75 | methodRegex = /def\s+(\w+)\s*\(self,?[^)]*\):/g;
76 | importRegex = /(?:from\s+(\w+(?:\.\w+)*)\s+import|import\s+(\w+(?:\.\w+)*))/g;
77 | break;
78 | case 'java':
79 | case 'c#':
80 | case 'csharp':
81 | classRegex = /class\s+(\w+)(?:\s+extends\s+(\w+))?(?:\s+implements\s+([^{]+))?/g;
82 | functionRegex = /(?:public|private|protected|static)?\s+\w+\s+(\w+)\s*\([^)]*\)\s*{/g;
83 | methodRegex = /(?:public|private|protected|static)?\s+\w+\s+(\w+)\s*\([^)]*\)\s*{/g;
84 | importRegex = /import\s+([^;]+);/g;
85 | break;
86 | default:
87 | // Generic patterns for other languages
88 | classRegex = /class\s+(\w+)/g;
89 | functionRegex = /function\s+(\w+)|def\s+(\w+)|(\w+)\s*\([^)]*\)\s*{/g;
90 | methodRegex = /(\w+)\s*\([^)]*\)\s*{|def\s+(\w+)/g;
91 | importRegex = /import|require|include|using/g;
92 | }
93 |
94 | // Extract components
95 | let match;
96 |
97 | // Reset regex lastIndex
98 | classRegex.lastIndex = 0;
99 | functionRegex.lastIndex = 0;
100 | importRegex.lastIndex = 0;
101 |
102 | // Extract classes with inheritance
103 | interface ClassInfo {
104 | name: string;
105 | parent: string | null;
106 | index: number;
107 | }
108 |
109 | const classes: ClassInfo[] = [];
110 | while ((match = classRegex.exec(code)) !== null) {
111 | classes.push({
112 | name: match[1],
113 | parent: match[2] || null,
114 | index: match.index
115 | });
116 | }
117 |
118 | // Extract functions
119 | interface FunctionInfo {
120 | name: string;
121 | index: number;
122 | }
123 |
124 | const functions: FunctionInfo[] = [];
125 | while ((match = functionRegex.exec(code)) !== null) {
126 | // Skip if inside a class
127 | let isInsideClass = false;
128 | for (const cls of classes) {
129 | const classCode = extractBlock(code, cls.index);
130 | if (match.index > cls.index && match.index < cls.index + classCode.length) {
131 | isInsideClass = true;
132 | break;
133 | }
134 | }
135 |
136 | if (!isInsideClass) {
137 | functions.push({
138 | name: match[1] || match[2] || match[3],
139 | index: match.index
140 | });
141 | }
142 | }
143 |
144 | // Extract imports/dependencies
145 | const imports: string[] = [];
146 | while ((match = importRegex.exec(code)) !== null) {
147 | imports.push(match[1] || match[2] || "external dependency");
148 | }
149 |
150 | // Analyze relationships between components
151 | interface Relationship {
152 | from: string;
153 | to: string;
154 | type: string;
155 | }
156 |
157 | const relationships: Relationship[] = [];
158 |
159 | // Check for class inheritance
160 | for (const cls of classes) {
161 | if (cls.parent) {
162 | relationships.push({
163 | from: cls.name,
164 | to: cls.parent,
165 | type: 'inherits'
166 | });
167 | }
168 | }
169 |
170 | // Check for function calls
171 | for (const func of functions) {
172 | const functionCode = extractBlock(code, func.index);
173 |
174 | // Check if this function calls other functions
175 | for (const otherFunc of functions) {
176 | if (func.name !== otherFunc.name &&
177 | functionCode.includes(`${otherFunc.name}(`)) {
178 | relationships.push({
179 | from: func.name,
180 | to: otherFunc.name,
181 | type: 'calls'
182 | });
183 | }
184 | }
185 | }
186 |
187 | // Generate the diagram
188 | let diagram = '';
189 |
190 | // Add header
191 | diagram += '+----------------------+\n';
192 | diagram += '| Code Structure |\n';
193 | diagram += '+----------------------+\n';
194 |
195 | // Add imports/dependencies if found
196 | if (imports.length > 0) {
197 | diagram += '\n Dependencies:\n';
198 | for (let i = 0; i < Math.min(imports.length, 3); i++) {
199 | diagram += ` [${imports[i]}]\n`;
200 | }
201 | if (imports.length > 3) {
202 | diagram += ' [...more dependencies...]\n';
203 | }
204 | diagram += '\n |\n v\n';
205 | }
206 |
207 | // Add classes with inheritance
208 | if (classes.length > 0) {
209 | diagram += '\n Classes:\n';
210 |
211 | // First add parent classes
212 | const parentClasses = classes.filter(c => !c.parent);
213 | for (const cls of parentClasses) {
214 | diagram += ` +------------------+\n`;
215 | diagram += ` | ${cls.name.padEnd(14)} |\n`;
216 | diagram += ` +------------------+\n`;
217 |
218 | // Add child classes
219 | const childClasses = classes.filter(c => c.parent === cls.name);
220 | if (childClasses.length > 0) {
221 | for (let i = 0; i < childClasses.length; i++) {
222 | const isLast = i === childClasses.length - 1;
223 | diagram += ` ${isLast ? '└' : '├'}──extends──┐\n`;
224 | diagram += ` +------------------+\n`;
225 | diagram += ` | ${childClasses[i].name.padEnd(14)} |\n`;
226 | diagram += ` +------------------+\n`;
227 | }
228 | }
229 |
230 | diagram += '\n';
231 | }
232 |
233 | // Add standalone classes (no inheritance)
234 | const standaloneClasses = classes.filter(c => !c.parent && !classes.some(other => other.parent === c.name));
235 | if (standaloneClasses.length > 0) {
236 | for (const cls of standaloneClasses) {
237 | diagram += ` +------------------+\n`;
238 | diagram += ` | ${cls.name.padEnd(14)} |\n`;
239 | diagram += ` +------------------+\n`;
240 | }
241 | }
242 | }
243 |
244 | // Add functions and their relationships
245 | if (functions.length > 0) {
246 | diagram += '\n Functions:\n';
247 |
248 | // Group functions by relationships
249 | const processedFunctions = new Set();
250 |
251 | // First process functions that call other functions
252 | for (const rel of relationships.filter(r => r.type === 'calls')) {
253 | if (!processedFunctions.has(rel.from)) {
254 | diagram += ` [${rel.from}] ──calls──> [${rel.to}]\n`;
255 | processedFunctions.add(rel.from);
256 | processedFunctions.add(rel.to);
257 | }
258 | }
259 |
260 | // Then add remaining functions
261 | for (const func of functions) {
262 | if (!processedFunctions.has(func.name)) {
263 | diagram += ` [${func.name}]\n`;
264 | processedFunctions.add(func.name);
265 | }
266 | }
267 | }
268 |
269 | // If no classes or functions found, show generic structure
270 | if (classes.length === 0 && functions.length === 0) {
271 | diagram += '\n +------------------+\n';
272 | diagram += ' | Implementation |\n';
273 | diagram += ' +------------------+\n';
274 | }
275 |
276 | return diagram;
277 | }
278 |
279 | /**
280 | * Extracts and summarizes the core functionality of the code.
281 | *
282 | * @param code {string} The source code to analyze
283 | * @param programming_language {string} The programming language of the code
284 | * @return {string} A description of the core functionality
285 | */
286 | function extractCoreFunctionality(code: string, programming_language: string): string {
287 | // Define language-specific patterns
288 | const patterns: Record<string, RegExp[]> = {
289 | network: [
290 | /fetch\s*\(/i,
291 | /http/i,
292 | /request/i,
293 | /api/i,
294 | /url/i,
295 | /endpoint/i
296 | ],
297 | ui: [
298 | /render/i,
299 | /component/i,
300 | /view/i,
301 | /display/i,
302 | /ui/i,
303 | /interface/i,
304 | /dom/i
305 | ],
306 | dataProcessing: [
307 | /map\s*\(/i,
308 | /filter\s*\(/i,
309 | /reduce\s*\(/i,
310 | /transform/i,
311 | /convert/i,
312 | /parse/i
313 | ],
314 | authentication: [
315 | /auth/i,
316 | /login/i,
317 | /password/i,
318 | /credential/i,
319 | /token/i,
320 | /permission/i
321 | ],
322 | database: [
323 | /database/i,
324 | /db\./i,
325 | /query/i,
326 | /sql/i,
327 | /mongo/i,
328 | /store/i,
329 | /save/i,
330 | /repository/i
331 | ],
332 | testing: [
333 | /test/i,
334 | /assert/i,
335 | /expect/i,
336 | /mock/i,
337 | /spec/i
338 | ],
339 | algorithm: [
340 | /algorithm/i,
341 | /sort/i,
342 | /search/i,
343 | /calculate/i,
344 | /compute/i
345 | ],
346 | fileSystem: [
347 | /file/i,
348 | /read/i,
349 | /write/i,
350 | /path/i,
351 | /directory/i,
352 | /folder/i
353 | ]
354 | };
355 |
356 | // Add language-specific patterns
357 | switch(programming_language.toLowerCase()) {
358 | case 'javascript':
359 | case 'typescript':
360 | case 'js':
361 | case 'ts':
362 | patterns.ui.push(/react/i, /angular/i, /vue/i, /component/i, /jsx/i, /tsx/i);
363 | patterns.network.push(/axios/i, /fetch/i, /xhr/i);
364 | patterns.database.push(/mongoose/i, /sequelize/i, /typeorm/i);
365 | break;
366 |
367 | case 'python':
368 | patterns.ui.push(/flask/i, /django/i, /template/i);
369 | patterns.network.push(/requests/i, /urllib/i);
370 | patterns.database.push(/sqlalchemy/i, /django\.db/i, /cursor/i);
371 | break;
372 |
373 | case 'java':
374 | patterns.ui.push(/swing/i, /javafx/i, /awt/i);
375 | patterns.network.push(/httpclient/i, /urlconnection/i);
376 | patterns.database.push(/jdbc/i, /repository/i, /entity/i);
377 | break;
378 |
379 | case 'c#':
380 | case 'csharp':
381 | patterns.ui.push(/wpf/i, /xaml/i, /winforms/i);
382 | patterns.network.push(/httpclient/i, /webclient/i);
383 | patterns.database.push(/entity\s*framework/i, /dbcontext/i);
384 | break;
385 | }
386 |
387 | // Check for patterns in the code
388 | const matches: Record<string, boolean> = {
389 | network: false,
390 | ui: false,
391 | dataProcessing: false,
392 | authentication: false,
393 | database: false,
394 | testing: false,
395 | algorithm: false,
396 | fileSystem: false
397 | };
398 |
399 | // Count matches for each category
400 | const matchCounts: Record<string, number> = {};
401 |
402 | for (const category in patterns) {
403 | matchCounts[category] = 0;
404 | for (const pattern of patterns[category]) {
405 | if (pattern.test(code)) {
406 | matchCounts[category]++;
407 | matches[category] = true;
408 | }
409 | }
410 | }
411 |
412 | // Determine primary and secondary purposes based on match counts
413 | const sortedCategories = Object.entries(matchCounts)
414 | .sort((a, b) => b[1] - a[1])
415 | .filter(entry => entry[1] > 0)
416 | .map(entry => entry[0]);
417 |
418 | const primaryPurpose = sortedCategories[0];
419 | const secondaryPurpose = sortedCategories[1];
420 |
421 | // Generate description
422 | let functionality = 'This code appears to ';
423 |
424 | if (!primaryPurpose) {
425 | functionality += 'provide general utility functions or core logic for the application.';
426 | return functionality;
427 | }
428 |
429 | // Primary purpose description
430 | switch(primaryPurpose) {
431 | case 'ui':
432 | functionality += 'implement a user interface ';
433 | if (matches.network) {
434 | functionality += 'that communicates with external services. ';
435 | } else if (matches.database) {
436 | functionality += 'that interacts with a database. ';
437 | } else {
438 | functionality += 'for displaying and interacting with data. ';
439 | }
440 | break;
441 |
442 | case 'network':
443 | functionality += 'handle network communication, ';
444 | if (matches.api) {
445 | functionality += 'likely serving as an API or service layer. ';
446 | } else {
447 | functionality += 'facilitating data exchange with external systems. ';
448 | }
449 | break;
450 |
451 | case 'dataProcessing':
452 | functionality += 'process and transform data, ';
453 | if (matches.algorithm) {
454 | functionality += 'implementing specific algorithms for data manipulation. ';
455 | } else {
456 | functionality += 'implementing business logic or data transformation. ';
457 | }
458 | break;
459 |
460 | case 'database':
461 | functionality += 'interact with a database, ';
462 | if (matches.dataProcessing) {
463 | functionality += 'performing data operations and transformations. ';
464 | } else {
465 | functionality += 'managing data persistence and retrieval. ';
466 | }
467 | break;
468 |
469 | case 'authentication':
470 | functionality += 'handle authentication and authorization, ';
471 | if (matches.ui) {
472 | functionality += 'providing secure user access to the interface. ';
473 | } else if (matches.network) {
474 | functionality += 'securing API endpoints or network resources. ';
475 | } else {
476 | functionality += 'managing user credentials and permissions. ';
477 | }
478 | break;
479 |
480 | case 'testing':
481 | functionality += 'implement tests for ';
482 | if (matches.ui) {
483 | functionality += 'user interface components. ';
484 | } else if (matches.network) {
485 | functionality += 'network communication. ';
486 | } else if (matches.database) {
487 | functionality += 'database operations. ';
488 | } else {
489 | functionality += 'application functionality. ';
490 | }
491 | break;
492 |
493 | case 'algorithm':
494 | functionality += 'implement specific algorithms ';
495 | if (matches.dataProcessing) {
496 | functionality += 'for data processing and transformation. ';
497 | } else {
498 | functionality += 'to solve computational problems. ';
499 | }
500 | break;
501 |
502 | case 'fileSystem':
503 | functionality += 'handle file system operations, ';
504 | if (matches.dataProcessing) {
505 | functionality += 'processing file data. ';
506 | } else {
507 | functionality += 'managing file reading, writing, or organization. ';
508 | }
509 | break;
510 |
511 | default:
512 | functionality += 'provide utility functions or core logic for the application. ';
513 | }
514 |
515 | // Add secondary purpose if available
516 | if (secondaryPurpose && matchCounts[secondaryPurpose] > 1) {
517 | functionality += 'It also includes functionality for ';
518 |
519 | switch(secondaryPurpose) {
520 | case 'ui':
521 | functionality += 'user interface presentation';
522 | break;
523 | case 'network':
524 | functionality += 'network communication';
525 | break;
526 | case 'dataProcessing':
527 | functionality += 'data processing and transformation';
528 | break;
529 | case 'database':
530 | functionality += 'database interaction';
531 | break;
532 | case 'authentication':
533 | functionality += 'authentication and security';
534 | break;
535 | case 'testing':
536 | functionality += 'testing and validation';
537 | break;
538 | case 'algorithm':
539 | functionality += 'algorithmic computation';
540 | break;
541 | case 'fileSystem':
542 | functionality += 'file system operations';
543 | break;
544 | }
545 |
546 | functionality += '.';
547 | }
548 |
549 | return functionality;
550 | }
551 |
552 | /**
553 | * Extracts main classes and functions from the code.
554 | *
555 | * @param code {string} The source code to analyze
556 | * @param programming_language {string} The programming language of the code
557 | * @return {Object} Object containing arrays of main classes and functions with descriptions
558 | */
559 | function extractComponents(code: string, programming_language: string): {
560 | mainClasses: Array<{name: string, description: string}>,
561 | mainFunctions: Array<{name: string, description: string}>
562 | } {
563 | const mainClasses: Array<{name: string, description: string}> = [];
564 | const mainFunctions: Array<{name: string, description: string}> = [];
565 |
566 | // Simple regex to find class names
567 | const classRegex = /class\s+(\w+)/g;
568 | let classMatch;
569 | while ((classMatch = classRegex.exec(code)) !== null) {
570 | const className = classMatch[1];
571 | const classCode = extractBlock(code, classMatch.index);
572 | const description = generateComponentDescription(classCode, 'class');
573 | mainClasses.push({ name: className, description });
574 | }
575 |
576 | // Simple regex to find function names
577 | const functionRegex = /function\s+(\w+)/g;
578 | let functionMatch;
579 | while ((functionMatch = functionRegex.exec(code)) !== null) {
580 | const functionName = functionMatch[1];
581 | const functionCode = extractBlock(code, functionMatch.index);
582 | const description = generateComponentDescription(functionCode, 'function');
583 | mainFunctions.push({ name: functionName, description });
584 | }
585 |
586 | // For languages like JavaScript, also look for arrow functions assigned to variables
587 | const arrowFunctionRegex = /const\s+(\w+)\s*=\s*(\([^)]*\)|[^=]*)\s*=>/g;
588 | let arrowMatch;
589 | while ((arrowMatch = arrowFunctionRegex.exec(code)) !== null) {
590 | const functionName = arrowMatch[1];
591 | const functionCode = extractBlock(code, arrowMatch.index);
592 | const description = generateComponentDescription(functionCode, 'function');
593 | mainFunctions.push({ name: functionName, description });
594 | }
595 |
596 | return { mainClasses, mainFunctions };
597 | }
598 |
599 | /**
600 | * Extracts a code block starting from a given position.
601 | *
602 | * @param code {string} The full source code
603 | * @param startIndex {number} The starting index of the block
604 | * @return {string} The extracted code block
605 | */
606 | function extractBlock(code: string, startIndex: number): string {
607 | // Find the opening brace after the start index
608 | const openingBraceIndex = code.indexOf('{', startIndex);
609 | if (openingBraceIndex === -1) return '';
610 |
611 | // Count braces to find the matching closing brace
612 | let braceCount = 1;
613 | let currentIndex = openingBraceIndex + 1;
614 |
615 | while (braceCount > 0 && currentIndex < code.length) {
616 | if (code[currentIndex] === '{') {
617 | braceCount++;
618 | } else if (code[currentIndex] === '}') {
619 | braceCount--;
620 | }
621 | currentIndex++;
622 | }
623 |
624 | return code.substring(startIndex, currentIndex);
625 | }
626 |
627 | /**
628 | * Generates a detailed description for a code component based on its content.
629 | *
630 | * @param componentCode {string} The code of the component
631 | * @param type {string} The type of component ('class' or 'function')
632 | * @return {string} A description of the component's purpose
633 | */
634 | function generateComponentDescription(componentCode: string, type: string): string {
635 | // First, look for JSDoc or other documentation comments
636 | let commentRegex: RegExp | null = null;
637 |
638 | // Different comment styles for different languages
639 | if (componentCode.includes('"""') || componentCode.includes("'''")) {
640 | // Python docstring
641 | commentRegex = /(?:'''|""")([^]*?)(?:'''|""")/;
642 | } else if (componentCode.includes('/**')) {
643 | // JSDoc style comment
644 | commentRegex = /\/\*\*([^]*?)\*\//;
645 | } else if (componentCode.includes('//')) {
646 | // Single line comments (try to find consecutive ones)
647 | const lines = componentCode.split('\n');
648 | const commentLines: string[] = [];
649 | let inCommentBlock = false;
650 |
651 | for (const line of lines) {
652 | const trimmedLine = line.trim();
653 | if (trimmedLine.startsWith('//')) {
654 | commentLines.push(trimmedLine.substring(2).trim());
655 | inCommentBlock = true;
656 | } else if (inCommentBlock && trimmedLine.length === 0) {
657 | // Allow empty lines in comment blocks
658 | continue;
659 | } else {
660 | inCommentBlock = false;
661 | if (commentLines.length > 0) {
662 | break;
663 | }
664 | }
665 | }
666 |
667 | if (commentLines.length > 0) {
668 | return commentLines.join(' ');
669 | }
670 | }
671 |
672 | // Try to extract comment if regex was set
673 | if (commentRegex) {
674 | const commentMatch = componentCode.match(commentRegex);
675 | if (commentMatch) {
676 | // Clean up the comment
677 | const comment = commentMatch[1]
678 | .replace(/\s*\*\s*/g, ' ') // Remove asterisks from JSDoc
679 | .replace(/\s+/g, ' ') // Normalize whitespace
680 | .replace(/@\w+\s+[^\n]+/g, '') // Remove JSDoc tags
681 | .trim();
682 |
683 | if (comment.length > 0) {
684 | return comment;
685 | }
686 | }
687 | }
688 |
689 | // If no comment is found, analyze the code to generate a description
690 |
691 | // Extract the component name
692 | let componentName = '';
693 | if (type === 'class') {
694 | const classNameMatch = componentCode.match(/class\s+(\w+)/);
695 | if (classNameMatch) {
696 | componentName = classNameMatch[1];
697 | }
698 | } else {
699 | const functionNameMatch = componentCode.match(/function\s+(\w+)|def\s+(\w+)/);
700 | if (functionNameMatch) {
701 | componentName = functionNameMatch[1] || functionNameMatch[2];
702 | }
703 | }
704 |
705 | // Convert camelCase or PascalCase to words
706 | if (componentName) {
707 | componentName = componentName
708 | .replace(/([A-Z])/g, ' $1') // Add space before capital letters
709 | .replace(/^./, str => str.toUpperCase()) // Capitalize first letter
710 | .trim();
711 | }
712 |
713 | // Analyze code patterns
714 | const patterns: Record<string, RegExp[]> = {
715 | ui: [/render/i, /component/i, /view/i, /display/i, /dom/i, /element/i, /ui/i],
716 | data: [/data/i, /state/i, /store/i, /model/i, /entity/i, /repository/i],
717 | network: [/fetch/i, /http/i, /request/i, /api/i, /url/i, /endpoint/i],
718 | utility: [/util/i, /helper/i, /format/i, /convert/i, /transform/i],
719 | event: [/event/i, /listener/i, /handler/i, /callback/i, /click/i, /change/i],
720 | auth: [/auth/i, /login/i, /permission/i, /role/i, /access/i, /token/i],
721 | file: [/file/i, /read/i, /write/i, /save/i, /load/i, /path/i],
722 | math: [/calc/i, /compute/i, /sum/i, /average/i, /math/i, /formula/i]
723 | };
724 |
725 | // Count matches for each category
726 | const matchCounts: Record<string, number> = {};
727 | for (const category in patterns) {
728 | matchCounts[category] = 0;
729 | for (const pattern of patterns[category]) {
730 | if (pattern.test(componentCode)) {
731 | matchCounts[category]++;
732 | }
733 | }
734 | }
735 |
736 | // Find the primary purpose based on the most matches
737 | const primaryPurpose = Object.entries(matchCounts)
738 | .sort((a, b) => b[1] - a[1])
739 | .filter(entry => entry[1] > 0)[0]?.[0];
740 |
741 | // Generate description based on component type and primary purpose
742 | if (type === 'class') {
743 | switch (primaryPurpose) {
744 | case 'ui':
745 | return `${componentName ? componentName + ' - ' : ''}A UI component that handles rendering and user interaction`;
746 | case 'data':
747 | return `${componentName ? componentName + ' - ' : ''}Manages data and state for the application`;
748 | case 'network':
749 | return `${componentName ? componentName + ' - ' : ''}Provides services or API interactions`;
750 | case 'auth':
751 | return `${componentName ? componentName + ' - ' : ''}Handles authentication and authorization`;
752 | case 'file':
753 | return `${componentName ? componentName + ' - ' : ''}Manages file operations and storage`;
754 | default:
755 | // Check if it's a base/parent class
756 | if (componentCode.includes('extends') || componentCode.includes('implements')) {
757 | return `${componentName ? componentName + ' - ' : ''}A base class that defines core functionality`;
758 | }
759 | return `${componentName ? componentName + ' - ' : ''}Encapsulates related functionality and data`;
760 | }
761 | } else { // function
762 | // Check for parameters and return values
763 | const hasParameters = /\([^)]+\)/.test(componentCode);
764 | const hasReturn = /return/.test(componentCode);
765 |
766 | switch (primaryPurpose) {
767 | case 'ui':
768 | return `${componentName ? componentName + ' - ' : ''}${hasReturn ? 'Generates' : 'Renders'} UI elements${hasParameters ? ' based on input parameters' : ''}`;
769 | case 'data':
770 | return `${componentName ? componentName + ' - ' : ''}${hasReturn ? 'Processes and transforms' : 'Manages'} data${hasParameters ? ' from input parameters' : ''}`;
771 | case 'network':
772 | return `${componentName ? componentName + ' - ' : ''}Handles network communication${hasParameters ? ' with specified endpoints' : ''}`;
773 | case 'utility':
774 | return `${componentName ? componentName + ' - ' : ''}Utility function that ${hasReturn ? 'processes input and returns a result' : 'performs operations'}`;
775 | case 'event':
776 | return `${componentName ? componentName + ' - ' : ''}Event handler that responds to user interactions`;
777 | case 'auth':
778 | return `${componentName ? componentName + ' - ' : ''}Manages authentication or authorization processes`;
779 | case 'file':
780 | return `${componentName ? componentName + ' - ' : ''}Handles file system operations`;
781 | case 'math':
782 | return `${componentName ? componentName + ' - ' : ''}Performs mathematical calculations${hasParameters ? ' on input values' : ''}`;
783 | default:
784 | if (hasReturn && hasParameters) {
785 | return `${componentName ? componentName + ' - ' : ''}Processes input parameters and returns a result`;
786 | } else if (hasReturn) {
787 | return `${componentName ? componentName + ' - ' : ''}Computes and returns a value`;
788 | } else if (hasParameters) {
789 | return `${componentName ? componentName + ' - ' : ''}Performs operations based on input parameters`;
790 | } else {
791 | return `${componentName ? componentName + ' - ' : ''}Performs a specific operation or task`;
792 | }
793 | }
794 | }
795 | }
796 |
797 | // Main worker handler
798 | export default {
799 | async fetch(request: Request, env: Env): Promise<Response> {
800 | // Check if this is a POST request
801 | if (request.method === 'POST') {
802 | // Check for authentication
803 | const authHeader = request.headers.get('Authorization');
804 | const expectedAuth = `Bearer ${env.SHARED_SECRET}`;
805 |
806 | if (!authHeader || authHeader !== expectedAuth) {
807 | return new Response('Unauthorized', { status: 401 });
808 | }
809 |
810 | // Parse the request body
811 | try {
812 | const body = await request.json() as {
813 | method: string;
814 | params: string[];
815 | };
816 |
817 | // Check if this is an MCP request
818 | if (body.method === 'explainCode' && Array.isArray(body.params) && body.params.length >= 2) {
819 | const code = body.params[0];
820 | const language = body.params[1];
821 |
822 | // Call the explainCode function
823 | const result = await explainCode(code, language);
824 |
825 | // Return the result
826 | return new Response(JSON.stringify({ result }), {
827 | headers: { 'Content-Type': 'application/json' }
828 | });
829 | } else {
830 | return new Response('Invalid method or parameters', { status: 400 });
831 | }
832 | } catch (error: any) {
833 | const errorMessage = error?.message || 'Unknown error';
834 | return new Response(`Error processing request: ${errorMessage}`, { status: 500 });
835 | }
836 | }
837 |
838 | // Return a simple HTML page for GET requests
839 | return new Response(`
840 | <!DOCTYPE html>
841 | <html>
842 | <head>
843 | <title>Code Explainer MCP</title>
844 | <style>
845 | body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
846 | h1 { color: #333; }
847 | pre { background: #f5f5f5; padding: 10px; border-radius: 5px; }
848 | </style>
849 | </head>
850 | <body>
851 | <h1>Code Explainer MCP</h1>
852 | <p>This is a Cloudflare Worker that analyzes and explains code.</p>
853 | <p>To use it, send a POST request with the following JSON body:</p>
854 | <pre>
855 | {
856 | "method": "explainCode",
857 | "params": ["your code here", "programming language"]
858 | }
859 | </pre>
860 | <p>Make sure to include the Authorization header with your shared secret.</p>
861 | </body>
862 | </html>
863 | `, {
864 | headers: { 'Content-Type': 'text/html' }
865 | });
866 | }
867 | };
868 |
```