@opencodereview/mcp-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +94 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/explain.d.ts +29 -0
- package/dist/tools/explain.js +40 -0
- package/dist/tools/explain.js.map +1 -0
- package/dist/tools/heal.d.ts +27 -0
- package/dist/tools/heal.js +44 -0
- package/dist/tools/heal.js.map +1 -0
- package/dist/tools/scan.d.ts +42 -0
- package/dist/tools/scan.js +80 -0
- package/dist/tools/scan.js.map +1 -0
- package/dist/utils/diff-runner.d.ts +6 -0
- package/dist/utils/diff-runner.js +35 -0
- package/dist/utils/diff-runner.js.map +1 -0
- package/dist/utils/runner.d.ts +2 -0
- package/dist/utils/runner.js +22 -0
- package/dist/utils/runner.js.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# @opencodereview/mcp-server
|
|
2
|
+
|
|
3
|
+
**MCP Server for Open Code Review** — AI code quality gate for Claude Desktop, Cursor, Windsurf, VS Code Copilot, and any MCP-compatible AI agent.
|
|
4
|
+
|
|
5
|
+
Connect your AI coding assistant to [Open Code Review](https://github.com/raye-deng/open-code-review) to detect hallucinated imports, phantom packages, stale APIs, security anti-patterns, and more — directly from your editor.
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
| Tool | Description |
|
|
10
|
+
|------|-------------|
|
|
11
|
+
| `scan_directory` | Scan a directory for AI-generated code quality issues |
|
|
12
|
+
| `scan_diff` | Scan git diff between branches (PR review) |
|
|
13
|
+
| `explain_issue` | Explain a detected issue with fix guidance |
|
|
14
|
+
| `heal_code` | Load file source + issue context for AI-assisted repair |
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
### Claude Desktop
|
|
19
|
+
|
|
20
|
+
Add to your Claude Desktop config file:
|
|
21
|
+
|
|
22
|
+
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
23
|
+
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"mcpServers": {
|
|
28
|
+
"open-code-review": {
|
|
29
|
+
"command": "npx",
|
|
30
|
+
"args": ["-y", "@opencodereview/mcp-server"],
|
|
31
|
+
"env": {
|
|
32
|
+
"OCR_LICENSE_KEY": "ocr-xxxx-xxxx-xxxx"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Cursor
|
|
40
|
+
|
|
41
|
+
Create or edit `.cursor/mcp.json` in your project root:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"mcpServers": {
|
|
46
|
+
"open-code-review": {
|
|
47
|
+
"command": "npx",
|
|
48
|
+
"args": ["-y", "@opencodereview/mcp-server"],
|
|
49
|
+
"env": {
|
|
50
|
+
"OCR_LICENSE_KEY": "ocr-xxxx-xxxx-xxxx"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Windsurf
|
|
58
|
+
|
|
59
|
+
Create or edit `.windsurf/mcp.json` in your project root:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"mcpServers": {
|
|
64
|
+
"open-code-review": {
|
|
65
|
+
"command": "npx",
|
|
66
|
+
"args": ["-y", "@opencodereview/mcp-server"],
|
|
67
|
+
"env": {
|
|
68
|
+
"OCR_LICENSE_KEY": "ocr-xxxx-xxxx-xxxx"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### VS Code Copilot
|
|
76
|
+
|
|
77
|
+
Add to your VS Code `settings.json`:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"mcp": {
|
|
82
|
+
"servers": {
|
|
83
|
+
"open-code-review": {
|
|
84
|
+
"command": "npx",
|
|
85
|
+
"args": ["-y", "@opencodereview/mcp-server"],
|
|
86
|
+
"env": {
|
|
87
|
+
"OCR_LICENSE_KEY": "ocr-xxxx-xxxx-xxxx"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
> **Note:** Requires VS Code Copilot with MCP support (Insiders or latest stable).
|
|
96
|
+
|
|
97
|
+
## Usage
|
|
98
|
+
|
|
99
|
+
Once configured, ask your AI assistant to scan your code:
|
|
100
|
+
|
|
101
|
+
- "Scan my `./src` directory for code quality issues"
|
|
102
|
+
- "Review the diff between `main` and my current branch"
|
|
103
|
+
- "Explain this hallucinated-import issue in `utils.ts`"
|
|
104
|
+
- "Fix the stale API issue in `api/client.ts`"
|
|
105
|
+
|
|
106
|
+
### Example: Scan Directory
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
You: Scan ./src for AI code quality issues at L2 level
|
|
110
|
+
|
|
111
|
+
→ Claude/Copilot calls scan_directory({ path: "./src", level: "L2" })
|
|
112
|
+
→ Returns JSON with all detected issues
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Example: PR Review
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
You: Review the diff between origin/main and HEAD
|
|
119
|
+
|
|
120
|
+
→ Claude/Copilot calls scan_diff({ base: "origin/main", head: "HEAD", path: "." })
|
|
121
|
+
→ Returns issues found only on changed lines
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Example: Explain & Fix
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
You: Explain and fix the hallucinated import issue in utils.ts
|
|
128
|
+
|
|
129
|
+
→ Claude/Copilot calls explain_issue(...) then heal_code(...)
|
|
130
|
+
→ Provides explanation and applies the fix
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## SLA Levels
|
|
134
|
+
|
|
135
|
+
| Level | Speed | Detection |
|
|
136
|
+
|-------|-------|-----------|
|
|
137
|
+
| **L1** | Fast (~1s) | Structural analysis (AST, imports, registry checks) |
|
|
138
|
+
| **L2** | Standard (~5s) | L1 + embedding similarity + local AI |
|
|
139
|
+
| **L3** | Deep (~30s) | L1 + L2 + remote LLM analysis |
|
|
140
|
+
|
|
141
|
+
## Supported Languages
|
|
142
|
+
|
|
143
|
+
TypeScript, JavaScript, Python, Java, Go, Kotlin
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
[BSL 1.1](https://spdx.org/licenses/BSL-1.1.html)
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { scanDirectorySchema, scanDiffSchema, handleScanDirectory, handleScanDiff, } from "./tools/scan.js";
|
|
6
|
+
import { explainIssueSchema, handleExplainIssue, } from "./tools/explain.js";
|
|
7
|
+
import { healCodeSchema, handleHealCode, } from "./tools/heal.js";
|
|
8
|
+
const server = new Server({ name: "open-code-review", version: "1.0.0" }, { capabilities: { tools: {} } });
|
|
9
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
10
|
+
tools: [
|
|
11
|
+
{
|
|
12
|
+
name: "scan_directory",
|
|
13
|
+
description: "Scan a directory for AI-generated code quality issues. Detects hallucinated imports, phantom packages, stale APIs, security anti-patterns, and more. Supports TypeScript, JavaScript, Python, Java, Go, and Kotlin.",
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: "object",
|
|
16
|
+
properties: {
|
|
17
|
+
path: { type: "string", description: "Directory path to scan" },
|
|
18
|
+
level: { type: "string", enum: ["L1", "L2", "L3"], default: "L1", description: "SLA level: L1 (fast structural), L2 (standard + local AI), L3 (deep + remote AI)" },
|
|
19
|
+
languages: { type: "string", description: "Comma-separated languages (e.g. 'typescript,python'). Auto-detect if omitted." },
|
|
20
|
+
},
|
|
21
|
+
required: ["path"],
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "scan_diff",
|
|
26
|
+
description: "Scan git diff between two branches for code quality issues. Ideal for PR/MR review — only analyzes changed files and lines.",
|
|
27
|
+
inputSchema: {
|
|
28
|
+
type: "object",
|
|
29
|
+
properties: {
|
|
30
|
+
base: { type: "string", description: "Base branch (e.g. 'origin/main')" },
|
|
31
|
+
head: { type: "string", description: "Head branch (e.g. 'HEAD')" },
|
|
32
|
+
path: { type: "string", description: "Repository path" },
|
|
33
|
+
level: { type: "string", enum: ["L1", "L2", "L3"], default: "L1", description: "SLA level" },
|
|
34
|
+
},
|
|
35
|
+
required: ["base", "head", "path"],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "explain_issue",
|
|
40
|
+
description: "Explain a code quality issue detected by OCR. Returns detailed explanation, category context, and fix guidance for the AI agent to act on.",
|
|
41
|
+
inputSchema: {
|
|
42
|
+
type: "object",
|
|
43
|
+
properties: {
|
|
44
|
+
issue: { type: "string", description: "The issue description to explain" },
|
|
45
|
+
file: { type: "string", description: "File path where the issue was found" },
|
|
46
|
+
line: { type: "number", description: "Line number" },
|
|
47
|
+
severity: { type: "string", description: "Severity: critical/high/medium/low/info" },
|
|
48
|
+
category: { type: "string", description: "Issue category" },
|
|
49
|
+
suggestion: { type: "string", description: "Auto-generated fix suggestion" },
|
|
50
|
+
},
|
|
51
|
+
required: ["issue"],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "heal_code",
|
|
56
|
+
description: "Load a file's source code and prepare a repair prompt for the AI agent. The agent (you) should then apply the fix based on the issue description and suggestion. Returns the file content along with the repair context.",
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: "object",
|
|
59
|
+
properties: {
|
|
60
|
+
path: { type: "string", description: "File path to heal" },
|
|
61
|
+
issue: { type: "string", description: "Issue description to fix" },
|
|
62
|
+
suggestion: { type: "string", description: "Suggested fix from OCR scan" },
|
|
63
|
+
},
|
|
64
|
+
required: ["path", "issue"],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
}));
|
|
69
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
70
|
+
const { name, arguments: args } = request.params;
|
|
71
|
+
switch (name) {
|
|
72
|
+
case "scan_directory": {
|
|
73
|
+
const parsed = scanDirectorySchema.parse(args);
|
|
74
|
+
return handleScanDirectory(parsed);
|
|
75
|
+
}
|
|
76
|
+
case "scan_diff": {
|
|
77
|
+
const parsed = scanDiffSchema.parse(args);
|
|
78
|
+
return handleScanDiff(parsed);
|
|
79
|
+
}
|
|
80
|
+
case "explain_issue": {
|
|
81
|
+
const parsed = explainIssueSchema.parse(args);
|
|
82
|
+
return handleExplainIssue(parsed);
|
|
83
|
+
}
|
|
84
|
+
case "heal_code": {
|
|
85
|
+
const parsed = healCodeSchema.parse(args);
|
|
86
|
+
return handleHealCode(parsed);
|
|
87
|
+
}
|
|
88
|
+
default:
|
|
89
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const transport = new StdioServerTransport();
|
|
93
|
+
await server.connect(transport);
|
|
94
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const explainIssueSchema: z.ZodObject<{
|
|
3
|
+
issue: z.ZodString;
|
|
4
|
+
file: z.ZodOptional<z.ZodString>;
|
|
5
|
+
line: z.ZodOptional<z.ZodNumber>;
|
|
6
|
+
severity: z.ZodOptional<z.ZodString>;
|
|
7
|
+
category: z.ZodOptional<z.ZodString>;
|
|
8
|
+
suggestion: z.ZodOptional<z.ZodString>;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
issue: string;
|
|
11
|
+
file?: string | undefined;
|
|
12
|
+
line?: number | undefined;
|
|
13
|
+
severity?: string | undefined;
|
|
14
|
+
category?: string | undefined;
|
|
15
|
+
suggestion?: string | undefined;
|
|
16
|
+
}, {
|
|
17
|
+
issue: string;
|
|
18
|
+
file?: string | undefined;
|
|
19
|
+
line?: number | undefined;
|
|
20
|
+
severity?: string | undefined;
|
|
21
|
+
category?: string | undefined;
|
|
22
|
+
suggestion?: string | undefined;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function handleExplainIssue(args: z.infer<typeof explainIssueSchema>): Promise<{
|
|
25
|
+
content: {
|
|
26
|
+
type: "text";
|
|
27
|
+
text: string;
|
|
28
|
+
}[];
|
|
29
|
+
}>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const explainIssueSchema = z.object({
|
|
3
|
+
issue: z.string().describe("The issue description to explain"),
|
|
4
|
+
file: z.string().optional().describe("File path where the issue was found"),
|
|
5
|
+
line: z.number().optional().describe("Line number where the issue was found"),
|
|
6
|
+
severity: z.string().optional().describe("Issue severity (critical/high/medium/low/info)"),
|
|
7
|
+
category: z.string().optional().describe("Issue category"),
|
|
8
|
+
suggestion: z.string().optional().describe("Auto-generated fix suggestion"),
|
|
9
|
+
});
|
|
10
|
+
const CATEGORY_EXPLANATIONS = {
|
|
11
|
+
"hallucinated-import": "The code imports a package that does not exist in the npm registry. This is a common AI hallucination — the model invented a plausible-sounding package name that has never been published. Remove the import and replace with a real package, or implement the functionality inline.",
|
|
12
|
+
"phantom-package": "A dependency is declared in package.json but never actually imported or used anywhere in the source code. This bloats the install size and may indicate an AI added an unnecessary dependency.",
|
|
13
|
+
"stale-api": "The code calls an API or uses a function signature that has been deprecated or removed in the current version of the library. Check the library's changelog and migration guide for the replacement.",
|
|
14
|
+
"context-break": "The code contains logic that appears inconsistent with the surrounding context — possibly caused by an AI losing track of what it was building mid-generation. Review the surrounding code for coherence.",
|
|
15
|
+
"duplication": "Significant code duplication detected. This may indicate the AI copy-pasted code blocks without abstracting shared logic. Consider extracting a shared function or utility.",
|
|
16
|
+
"security-pattern": "A potential security anti-pattern was detected (e.g., hardcoded secrets, eval usage, SQL injection risk, path traversal). This requires immediate review.",
|
|
17
|
+
"over-engineering": "The code is unnecessarily complex for what it does. AI models tend to add extra abstraction layers, generic types, or design patterns that aren't needed. Simplify the implementation.",
|
|
18
|
+
};
|
|
19
|
+
export async function handleExplainIssue(args) {
|
|
20
|
+
const categoryExplanation = args.category ? CATEGORY_EXPLANATIONS[args.category] : null;
|
|
21
|
+
const explanation = {
|
|
22
|
+
issue: args.issue,
|
|
23
|
+
file: args.file ?? null,
|
|
24
|
+
line: args.line ?? null,
|
|
25
|
+
severity: args.severity ?? null,
|
|
26
|
+
category: args.category ?? null,
|
|
27
|
+
suggestion: args.suggestion ?? null,
|
|
28
|
+
categoryExplanation,
|
|
29
|
+
analysis: `This is a ${args.severity ?? "unknown"} severity issue in the "${args.category ?? "uncategorized"}" category. ${categoryExplanation ?? "Review the flagged code and consider the suggestion for resolution."}`,
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
content: [
|
|
33
|
+
{
|
|
34
|
+
type: "text",
|
|
35
|
+
text: JSON.stringify(explanation, null, 2),
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=explain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.js","sourceRoot":"","sources":["../../src/tools/explain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;IAC9D,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;IAC3E,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;IAC7E,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gDAAgD,CAAC;IAC1F,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAC1D,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+BAA+B,CAAC;CAC5E,CAAC,CAAC;AAEH,MAAM,qBAAqB,GAA2B;IACpD,qBAAqB,EAAE,uRAAuR;IAC9S,iBAAiB,EAAE,gMAAgM;IACnN,WAAW,EAAE,sMAAsM;IACnN,eAAe,EAAE,2MAA2M;IAC5N,aAAa,EAAE,6KAA6K;IAC5L,kBAAkB,EAAE,2JAA2J;IAC/K,kBAAkB,EAAE,wLAAwL;CAC7M,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAwC;IAC/E,MAAM,mBAAmB,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAExF,MAAM,WAAW,GAAG;QAClB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI;QACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI;QAC/B,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI;QAC/B,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,IAAI;QACnC,mBAAmB;QACnB,QAAQ,EAAE,aAAa,IAAI,CAAC,QAAQ,IAAI,SAAS,2BAA2B,IAAI,CAAC,QAAQ,IAAI,eAAe,eAAe,mBAAmB,IAAI,qEAAqE,EAAE;KAC1N,CAAC;IAEF,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;aAC3C;SACF;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const healCodeSchema: z.ZodObject<{
|
|
3
|
+
path: z.ZodString;
|
|
4
|
+
issue: z.ZodString;
|
|
5
|
+
suggestion: z.ZodOptional<z.ZodString>;
|
|
6
|
+
}, "strip", z.ZodTypeAny, {
|
|
7
|
+
path: string;
|
|
8
|
+
issue: string;
|
|
9
|
+
suggestion?: string | undefined;
|
|
10
|
+
}, {
|
|
11
|
+
path: string;
|
|
12
|
+
issue: string;
|
|
13
|
+
suggestion?: string | undefined;
|
|
14
|
+
}>;
|
|
15
|
+
export declare function handleHealCode(args: z.infer<typeof healCodeSchema>): Promise<{
|
|
16
|
+
content: {
|
|
17
|
+
type: "text";
|
|
18
|
+
text: string;
|
|
19
|
+
}[];
|
|
20
|
+
isError: boolean;
|
|
21
|
+
} | {
|
|
22
|
+
content: {
|
|
23
|
+
type: "text";
|
|
24
|
+
text: string;
|
|
25
|
+
}[];
|
|
26
|
+
isError?: undefined;
|
|
27
|
+
}>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
export const healCodeSchema = z.object({
|
|
5
|
+
path: z.string().describe("File path to heal"),
|
|
6
|
+
issue: z.string().describe("Issue description to fix"),
|
|
7
|
+
suggestion: z.string().optional().describe("Suggested fix from OCR scan"),
|
|
8
|
+
});
|
|
9
|
+
export async function handleHealCode(args) {
|
|
10
|
+
const filePath = resolve(args.path);
|
|
11
|
+
let content;
|
|
12
|
+
try {
|
|
13
|
+
content = await readFile(filePath, "utf-8");
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return {
|
|
17
|
+
content: [
|
|
18
|
+
{
|
|
19
|
+
type: "text",
|
|
20
|
+
text: JSON.stringify({
|
|
21
|
+
error: `Cannot read file: ${filePath}`,
|
|
22
|
+
suggestion: "Check the file path and ensure it exists.",
|
|
23
|
+
}, null, 2),
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
isError: true,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
content: [
|
|
31
|
+
{
|
|
32
|
+
type: "text",
|
|
33
|
+
text: JSON.stringify({
|
|
34
|
+
file: filePath,
|
|
35
|
+
issue: args.issue,
|
|
36
|
+
suggestion: args.suggestion ?? null,
|
|
37
|
+
code: content,
|
|
38
|
+
instructions: `You are an expert code repair assistant. The file "${args.path}" contains the following issue detected by Open Code Review:\n\n**Issue:** ${args.issue}\n${args.suggestion ? `**Suggestion:** ${args.suggestion}\n` : ""}\n\nPlease analyze the code below and provide the fixed version. Only output the corrected code, preserving the original structure and style.`,
|
|
39
|
+
}, null, 2),
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=heal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heal.js","sourceRoot":"","sources":["../../src/tools/heal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;IAC9C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;IACtD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;CAC1E,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAoC;IACvE,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEpC,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;wBACnB,KAAK,EAAE,qBAAqB,QAAQ,EAAE;wBACtC,UAAU,EAAE,2CAA2C;qBACxD,EAAE,IAAI,EAAE,CAAC,CAAC;iBACZ;aACF;YACD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,IAAI,EAAE,QAAQ;oBACd,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,IAAI;oBACnC,IAAI,EAAE,OAAO;oBACb,YAAY,EAAE,sDAAsD,IAAI,CAAC,IAAI,8EAA8E,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,mBAAmB,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,EAAE,+IAA+I;iBACvX,EAAE,IAAI,EAAE,CAAC,CAAC;aACZ;SACF;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const scanDirectorySchema: z.ZodObject<{
|
|
3
|
+
path: z.ZodString;
|
|
4
|
+
level: z.ZodDefault<z.ZodOptional<z.ZodEnum<["L1", "L2", "L3"]>>>;
|
|
5
|
+
languages: z.ZodOptional<z.ZodString>;
|
|
6
|
+
}, "strip", z.ZodTypeAny, {
|
|
7
|
+
path: string;
|
|
8
|
+
level: "L1" | "L2" | "L3";
|
|
9
|
+
languages?: string | undefined;
|
|
10
|
+
}, {
|
|
11
|
+
path: string;
|
|
12
|
+
level?: "L1" | "L2" | "L3" | undefined;
|
|
13
|
+
languages?: string | undefined;
|
|
14
|
+
}>;
|
|
15
|
+
export declare const scanDiffSchema: z.ZodObject<{
|
|
16
|
+
base: z.ZodString;
|
|
17
|
+
head: z.ZodString;
|
|
18
|
+
path: z.ZodString;
|
|
19
|
+
level: z.ZodDefault<z.ZodOptional<z.ZodEnum<["L1", "L2", "L3"]>>>;
|
|
20
|
+
}, "strip", z.ZodTypeAny, {
|
|
21
|
+
path: string;
|
|
22
|
+
level: "L1" | "L2" | "L3";
|
|
23
|
+
base: string;
|
|
24
|
+
head: string;
|
|
25
|
+
}, {
|
|
26
|
+
path: string;
|
|
27
|
+
base: string;
|
|
28
|
+
head: string;
|
|
29
|
+
level?: "L1" | "L2" | "L3" | undefined;
|
|
30
|
+
}>;
|
|
31
|
+
export declare function handleScanDirectory(args: z.infer<typeof scanDirectorySchema>): Promise<{
|
|
32
|
+
content: {
|
|
33
|
+
type: "text";
|
|
34
|
+
text: string;
|
|
35
|
+
}[];
|
|
36
|
+
}>;
|
|
37
|
+
export declare function handleScanDiff(args: z.infer<typeof scanDiffSchema>): Promise<{
|
|
38
|
+
content: {
|
|
39
|
+
type: "text";
|
|
40
|
+
text: string;
|
|
41
|
+
}[];
|
|
42
|
+
}>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runScan } from "../utils/runner.js";
|
|
3
|
+
import { runDiffScan } from "../utils/diff-runner.js";
|
|
4
|
+
export const scanDirectorySchema = z.object({
|
|
5
|
+
path: z.string().describe("Directory path to scan"),
|
|
6
|
+
level: z.enum(["L1", "L2", "L3"]).optional().default("L1").describe("SLA level: L1 (fast), L2 (standard), L3 (deep)"),
|
|
7
|
+
languages: z.string().optional().describe("Comma-separated languages to scan (e.g. 'typescript,python')"),
|
|
8
|
+
});
|
|
9
|
+
export const scanDiffSchema = z.object({
|
|
10
|
+
base: z.string().describe("Base branch (e.g. 'origin/main')"),
|
|
11
|
+
head: z.string().describe("Head branch (e.g. 'HEAD')"),
|
|
12
|
+
path: z.string().describe("Repository path"),
|
|
13
|
+
level: z.enum(["L1", "L2", "L3"]).optional().default("L1").describe("SLA level"),
|
|
14
|
+
});
|
|
15
|
+
export async function handleScanDirectory(args) {
|
|
16
|
+
const languages = args.languages ? args.languages.split(",").map((s) => s.trim()) : undefined;
|
|
17
|
+
const result = await runScan(args.path, args.level, languages);
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: "text",
|
|
22
|
+
text: JSON.stringify({
|
|
23
|
+
version: "4.0",
|
|
24
|
+
sla: result.sla,
|
|
25
|
+
files: result.files,
|
|
26
|
+
languages: result.languages,
|
|
27
|
+
durationMs: result.durationMs,
|
|
28
|
+
issuesCount: result.issues.length,
|
|
29
|
+
issues: result.issues.map((issue) => ({
|
|
30
|
+
detectorId: issue.detectorId,
|
|
31
|
+
file: issue.file,
|
|
32
|
+
line: issue.line,
|
|
33
|
+
endLine: issue.endLine ?? null,
|
|
34
|
+
severity: issue.severity,
|
|
35
|
+
category: issue.category,
|
|
36
|
+
message: issue.message,
|
|
37
|
+
confidence: issue.confidence,
|
|
38
|
+
})),
|
|
39
|
+
}, null, 2),
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export async function handleScanDiff(args) {
|
|
45
|
+
const { result, diffResult } = await runDiffScan(args.path, args.base, args.head, args.level);
|
|
46
|
+
return {
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: "text",
|
|
50
|
+
text: JSON.stringify({
|
|
51
|
+
version: "4.0",
|
|
52
|
+
mode: "diff",
|
|
53
|
+
base: args.base,
|
|
54
|
+
head: args.head,
|
|
55
|
+
changedFiles: diffResult.files.map((f) => ({
|
|
56
|
+
path: f.path,
|
|
57
|
+
status: f.status,
|
|
58
|
+
additions: f.additions,
|
|
59
|
+
deletions: f.deletions,
|
|
60
|
+
})),
|
|
61
|
+
sla: result.sla,
|
|
62
|
+
files: result.files,
|
|
63
|
+
durationMs: result.durationMs,
|
|
64
|
+
issuesCount: result.issues.length,
|
|
65
|
+
issues: result.issues.map((issue) => ({
|
|
66
|
+
detectorId: issue.detectorId,
|
|
67
|
+
file: issue.file,
|
|
68
|
+
line: issue.line,
|
|
69
|
+
endLine: issue.endLine ?? null,
|
|
70
|
+
severity: issue.severity,
|
|
71
|
+
category: issue.category,
|
|
72
|
+
message: issue.message,
|
|
73
|
+
confidence: issue.confidence,
|
|
74
|
+
})),
|
|
75
|
+
}, null, 2),
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/tools/scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type V4ScanResult, type DiffResult } from "@opencodereview/core";
|
|
2
|
+
export interface DiffScanResult {
|
|
3
|
+
result: V4ScanResult;
|
|
4
|
+
diffResult: DiffResult;
|
|
5
|
+
}
|
|
6
|
+
export declare function runDiffScan(repoPath: string, base: string, head: string, level?: string): Promise<DiffScanResult>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import { V4Scanner, parseDiff, getScannableFiles, filterByDiff } from "@opencodereview/core";
|
|
4
|
+
export async function runDiffScan(repoPath, base, head, level = "L1") {
|
|
5
|
+
const projectRoot = resolve(repoPath);
|
|
6
|
+
const diffText = execSync(`git diff ${base}...${head}`, {
|
|
7
|
+
cwd: projectRoot,
|
|
8
|
+
encoding: "utf-8",
|
|
9
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
10
|
+
});
|
|
11
|
+
const diffResult = parseDiff(diffText);
|
|
12
|
+
const scannableFiles = getScannableFiles(diffResult);
|
|
13
|
+
if (scannableFiles.length === 0) {
|
|
14
|
+
const empty = {
|
|
15
|
+
issues: [],
|
|
16
|
+
codeUnits: [],
|
|
17
|
+
files: [],
|
|
18
|
+
languages: [],
|
|
19
|
+
durationMs: 0,
|
|
20
|
+
stages: { discovery: 0, parsing: 0, detection: 0 },
|
|
21
|
+
projectRoot,
|
|
22
|
+
sla: level,
|
|
23
|
+
};
|
|
24
|
+
return { result: empty, diffResult };
|
|
25
|
+
}
|
|
26
|
+
const scanner = new V4Scanner({
|
|
27
|
+
projectRoot,
|
|
28
|
+
sla: level,
|
|
29
|
+
include: scannableFiles.map((f) => `**/${f.split("/").pop()}`),
|
|
30
|
+
});
|
|
31
|
+
const result = await scanner.scan();
|
|
32
|
+
result.issues = filterByDiff(result.issues, diffResult);
|
|
33
|
+
return { result, diffResult };
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=diff-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-runner.js","sourceRoot":"","sources":["../../src/utils/diff-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE,YAAY,EAAsC,MAAM,sBAAsB,CAAC;AAQjI,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAgB,EAChB,IAAY,EACZ,IAAY,EACZ,QAAgB,IAAI;IAEpB,MAAM,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEtC,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,IAAI,MAAM,IAAI,EAAE,EAAE;QACtD,GAAG,EAAE,WAAW;QAChB,QAAQ,EAAE,OAAO;QACjB,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;KAC5B,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,cAAc,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAErD,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,KAAK,GAAiB;YAC1B,MAAM,EAAE,EAAE;YACV,SAAS,EAAE,EAAE;YACb,KAAK,EAAE,EAAE;YACT,SAAS,EAAE,EAAE;YACb,UAAU,EAAE,CAAC;YACb,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE;YAClD,WAAW;YACX,GAAG,EAAE,KAAiB;SACvB,CAAC;QACF,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IACvC,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,SAAS,CAAC;QAC5B,WAAW;QACX,GAAG,EAAE,KAAiB;QACtB,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC;KAC/D,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;IACpC,MAAM,CAAC,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAExD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { V4Scanner } from "@opencodereview/core";
|
|
3
|
+
const VALID_LANGUAGES = new Set([
|
|
4
|
+
"typescript", "javascript", "python", "java", "go", "kotlin",
|
|
5
|
+
]);
|
|
6
|
+
export async function runScan(dir, level = "L1", languages) {
|
|
7
|
+
const projectRoot = resolve(dir);
|
|
8
|
+
let langList;
|
|
9
|
+
if (languages && languages.length > 0) {
|
|
10
|
+
const valid = languages.filter((l) => VALID_LANGUAGES.has(l.toLowerCase()));
|
|
11
|
+
if (valid.length > 0) {
|
|
12
|
+
langList = valid;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const scanner = new V4Scanner({
|
|
16
|
+
projectRoot,
|
|
17
|
+
sla: level,
|
|
18
|
+
languages: langList,
|
|
19
|
+
});
|
|
20
|
+
return scanner.scan();
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/utils/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAqB,MAAM,sBAAsB,CAAC;AAGpE,MAAM,eAAe,GAAG,IAAI,GAAG,CAAS;IACtC,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ;CAC7D,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,GAAW,EACX,QAAgB,IAAI,EACpB,SAAoB;IAEpB,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAEjC,IAAI,QAAyC,CAAC;IAC9C,IAAI,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAC5E,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,QAAQ,GAAG,KAA4B,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,SAAS,CAAC;QAC5B,WAAW;QACX,GAAG,EAAE,KAAiB;QACtB,SAAS,EAAE,QAAQ;KACpB,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;AACxB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opencodereview/mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP Server for Open Code Review - AI code quality gate for Claude Desktop, Cursor, Windsurf, VS Code Copilot",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ocr-mcp": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"start": "node dist/index.js",
|
|
20
|
+
"dev": "tsx src/index.ts"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp",
|
|
24
|
+
"mcp-server",
|
|
25
|
+
"model-context-protocol",
|
|
26
|
+
"ai-code-review",
|
|
27
|
+
"claude",
|
|
28
|
+
"cursor",
|
|
29
|
+
"windsurf",
|
|
30
|
+
"copilot",
|
|
31
|
+
"code-quality",
|
|
32
|
+
"code-scanner",
|
|
33
|
+
"quality-gate"
|
|
34
|
+
],
|
|
35
|
+
"author": "Raye Deng <fengsen.deng@gmail.com>",
|
|
36
|
+
"license": "BUSL-1.1",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/raye-deng/open-code-review"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://codes.evallab.ai",
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
44
|
+
"@opencodereview/core": "^2.1.1",
|
|
45
|
+
"zod": "^3.24.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^20.11.0",
|
|
49
|
+
"tsx": "^4.7.0",
|
|
50
|
+
"typescript": "^5.4.0"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=20.0.0"
|
|
54
|
+
}
|
|
55
|
+
}
|