@reverse-craft/ai-tools 1.0.0 → 1.0.1

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 CHANGED
@@ -1,12 +1,13 @@
1
1
  # @reverse-craft/ai-tools
2
2
 
3
- AI-powered code analysis tools for smart-fs. Provides LLM-driven functionality including JSVMP (JavaScript Virtual Machine Protection) dispatcher detection.
3
+ MCP server for AI-powered JSVMP detection. Provides LLM-driven code analysis tools for identifying JavaScript Virtual Machine Protection patterns.
4
4
 
5
5
  ## Features
6
6
 
7
+ - **MCP Server** - Model Context Protocol server for AI assistant integration
7
8
  - **JSVMP Detection** - Detect VM protection patterns using LLM analysis
8
- - **LLM Configuration** - Flexible OpenAI-compatible API configuration
9
- - **Code Formatting** - Format code for LLM analysis with source map coordinates
9
+ - **Multiple Pattern Types** - Identifies dispatchers, instruction arrays, stack operations
10
+ - **Confidence Levels** - Results include ultra_high, high, medium, low confidence ratings
10
11
 
11
12
  ## Installation
12
13
 
@@ -24,106 +25,115 @@ export OPENAI_API_KEY=your-api-key
24
25
 
25
26
  # Optional (defaults shown)
26
27
  export OPENAI_BASE_URL=https://api.openai.com/v1
27
- export OPENAI_MODEL=gpt-4
28
+ export OPENAI_MODEL=gpt-4o-mini
28
29
  ```
29
30
 
30
- ## Usage
31
+ ## MCP Server Usage
31
32
 
32
- ### JSVMP Dispatcher Detection
33
+ ### Running the Server
33
34
 
34
- ```typescript
35
- import { findJsvmpDispatcher } from '@reverse-craft/ai-tools';
35
+ ```bash
36
+ # Via npx
37
+ npx @reverse-craft/ai-tools
38
+
39
+ # Or if installed globally
40
+ ai-tools-mcp
41
+ ```
36
42
 
37
- const result = await findJsvmpDispatcher(
38
- './obfuscated.js',
39
- 1, // startLine
40
- 500, // endLine
41
- { charLimit: 300 }
42
- );
43
+ ### MCP Configuration
43
44
 
44
- if (result.success) {
45
- console.log(result.formattedOutput);
46
- // Detected regions with confidence levels
47
- for (const region of result.result.regions) {
48
- console.log(`[${region.confidence}] Lines ${region.start}-${region.end}: ${region.type}`);
45
+ Add to your MCP client configuration (e.g., Claude Desktop, Kiro):
46
+
47
+ ```json
48
+ {
49
+ "mcpServers": {
50
+ "ai-tools": {
51
+ "command": "npx",
52
+ "args": ["@reverse-craft/ai-tools"],
53
+ "env": {
54
+ "OPENAI_API_KEY": "your-api-key"
55
+ }
56
+ }
49
57
  }
50
58
  }
51
59
  ```
52
60
 
53
- ### LLM Configuration
54
-
55
- ```typescript
56
- import { getLLMConfig, isLLMConfigured, createLLMClient } from '@reverse-craft/ai-tools';
57
-
58
- // Check if LLM is configured
59
- if (isLLMConfigured()) {
60
- const config = getLLMConfig();
61
- const client = createLLMClient(config);
61
+ Or with a local installation:
62
+
63
+ ```json
64
+ {
65
+ "mcpServers": {
66
+ "ai-tools": {
67
+ "command": "node",
68
+ "args": ["/path/to/ai-tools/dist/server.js"],
69
+ "env": {
70
+ "OPENAI_API_KEY": "your-api-key"
71
+ }
72
+ }
73
+ }
62
74
  }
63
75
  ```
64
76
 
65
- ### Code Formatting for Analysis
77
+ ## MCP Tools
66
78
 
67
- ```typescript
68
- import { formatCodeForAnalysis } from '@reverse-craft/ai-tools';
79
+ ### find_jsvmp_dispatcher
69
80
 
70
- const formatted = await formatCodeForAnalysis(
71
- './app.min.js',
72
- 1, // startLine
73
- 100, // endLine
74
- 300 // charLimit
75
- );
81
+ Detect JSVMP (JavaScript Virtual Machine Protection) patterns in code using LLM analysis.
76
82
 
77
- console.log(formatted.content);
78
- // Output: "LineNo SourceLoc Code" format
79
- ```
83
+ **Parameters:**
80
84
 
81
- ## API
85
+ | Name | Type | Required | Description |
86
+ |------|------|----------|-------------|
87
+ | filePath | string | Yes | Path to the JavaScript file to analyze |
88
+ | startLine | number | Yes | Start line number (1-based) |
89
+ | endLine | number | Yes | End line number (1-based) |
90
+ | charLimit | number | No | Character limit for string truncation (default: 300) |
82
91
 
83
- ### Types
92
+ **Detection Types:**
84
93
 
85
- ```typescript
86
- // Detection types
87
- type DetectionType =
88
- | "If-Else Dispatcher"
89
- | "Switch Dispatcher"
90
- | "Instruction Array"
91
- | "Stack Operation";
94
+ - **If-Else Dispatcher** - Nested if-else chains for instruction dispatch
95
+ - **Switch Dispatcher** - Large switch statements (>20 cases) for opcode handling
96
+ - **Instruction Array** - Arrays storing bytecode instructions
97
+ - **Stack Operation** - Virtual stack push/pop patterns
92
98
 
93
- type ConfidenceLevel = "ultra_high" | "high" | "medium" | "low";
99
+ **Confidence Levels:**
94
100
 
95
- interface DetectionRegion {
96
- start: number;
97
- end: number;
98
- type: DetectionType;
99
- confidence: ConfidenceLevel;
100
- description: string;
101
- }
101
+ - `ultra_high` - Multiple JSVMP features present (loop + dispatcher + stack)
102
+ - `high` - Clear dispatcher structure (>20 cases or >10 nesting levels)
103
+ - `medium` - Partial JSVMP features
104
+ - `low` - Possible but uncertain patterns
102
105
 
103
- interface JsvmpDetectionResult {
104
- success: boolean;
105
- filePath: string;
106
- startLine: number;
107
- endLine: number;
108
- result?: DetectionResult;
109
- formattedOutput?: string;
110
- error?: string;
111
- }
106
+ **Example Response:**
107
+
108
+ ```
109
+ === JSVMP Dispatcher Detection Result ===
110
+ File: ./obfuscated.js (1-500)
111
+
112
+ Summary: 检测到典型的 JSVMP 保护结构,包含主分发器和栈操作
113
+
114
+ Detected Regions:
115
+ [ultra_high] Lines 45-280: Switch Dispatcher
116
+ 大型 switch 语句包含 47 个 case,典型的 JSVMP 指令分发器
117
+
118
+ [high] Lines 12-44: Stack Operation
119
+ 虚拟栈初始化和操作,使用数组索引进行 push/pop
112
120
  ```
113
121
 
114
- ### Functions
122
+ ## What is JSVMP?
123
+
124
+ JSVMP (JavaScript Virtual Machine Protection) is a code protection technique that:
125
+
126
+ 1. Converts JavaScript source code to custom bytecode
127
+ 2. Implements a virtual machine to execute the bytecode
128
+ 3. Uses dispatchers (switch/if-else) to handle different opcodes
129
+ 4. Maintains a virtual stack for operand storage
115
130
 
116
- - `findJsvmpDispatcher(filePath, startLine, endLine, options?)` - Detect JSVMP patterns
117
- - `formatCodeForAnalysis(filePath, startLine, endLine, charLimit?)` - Format code for LLM
118
- - `parseDetectionResult(jsonString)` - Parse LLM response
119
- - `getLLMConfig()` - Get LLM configuration from environment
120
- - `isLLMConfigured()` - Check if LLM is configured
121
- - `createLLMClient(config)` - Create LLM client instance
131
+ This makes reverse engineering significantly harder as the original logic is hidden behind VM interpretation.
122
132
 
123
133
  ## Related Packages
124
134
 
125
- - **[@reverse-craft/smart-fs](https://github.com/reverse-craft/smart-fs)** - Core library
126
- - **[@reverse-craft/smart-fs-mcp](https://github.com/reverse-craft/smart-fs-mcp)** - MCP server
135
+ - **[@reverse-craft/smart-fs](https://github.com/reverse-craft/smart-fs)** - Core library for code processing
136
+ - **[@reverse-craft/smart-fs-mcp](https://github.com/reverse-craft/smart-fs-mcp)** - MCP server for smart-fs
127
137
 
128
138
  ## License
129
139
 
package/dist/index.d.ts CHANGED
@@ -1,11 +1,16 @@
1
1
  /**
2
- * AI Tools - AI-powered code analysis tools
2
+ * AI Tools - MCP server for AI-powered JSVMP detection
3
3
  *
4
- * This package provides LLM-driven functionality for code analysis,
4
+ * This package provides an MCP server with LLM-driven functionality for code analysis,
5
5
  * including JSVMP (JavaScript Virtual Machine Protection) detection.
6
6
  *
7
+ * Run as MCP server: npx @reverse-craft/ai-tools
8
+ *
7
9
  * @packageDocumentation
8
10
  */
9
11
  export { type LLMConfig, type LLMClient, getLLMConfig, isLLMConfigured, createLLMClient, } from './llmConfig.js';
10
12
  export { type FormattedCode, type DetectionType, type ConfidenceLevel, type DetectionRegion, type DetectionResult, type JsvmpDetectionOptions, type JsvmpDetectionResult, formatCodeForAnalysis, parseDetectionResult, findJsvmpDispatcher, } from './jsvmpDetector.js';
13
+ export { tools } from './tools/index.js';
14
+ export { findJsvmpDispatcherTool, FindJsvmpDispatcherInputSchema } from './tools/findJsvmpDispatcherTool.js';
15
+ export { ToolDefinition, defineTool } from './tools/ToolDefinition.js';
11
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EACL,KAAK,SAAS,EACd,KAAK,SAAS,EACd,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EACL,KAAK,SAAS,EACd,KAAK,SAAS,EACd,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,oCAAoC,CAAC;AAC7G,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":""}
package/dist/server.js ADDED
@@ -0,0 +1,418 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/server.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z as z2 } from "zod";
7
+
8
+ // src/tools/findJsvmpDispatcherTool.ts
9
+ import { z } from "zod";
10
+
11
+ // src/tools/ToolDefinition.ts
12
+ function defineTool(definition) {
13
+ return definition;
14
+ }
15
+
16
+ // src/jsvmpDetector.ts
17
+ import { SourceMapConsumer } from "source-map-js";
18
+ import { ensureBeautified, truncateCodeHighPerf } from "@reverse-craft/smart-fs";
19
+ import { existsSync } from "fs";
20
+
21
+ // src/llmConfig.ts
22
+ function getLLMConfig() {
23
+ const apiKey = process.env.OPENAI_API_KEY;
24
+ if (!apiKey) {
25
+ return null;
26
+ }
27
+ const baseUrl = process.env.OPENAI_BASE_URL || "https://api.openai.com/v1";
28
+ const model = process.env.OPENAI_MODEL || "gpt-4o-mini";
29
+ return {
30
+ apiKey,
31
+ baseUrl,
32
+ model
33
+ };
34
+ }
35
+ function buildJSVMPSystemPrompt() {
36
+ return `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684 JavaScript \u9006\u5411\u5DE5\u7A0B\u4E13\u5BB6\uFF0C\u4E13\u95E8\u8BC6\u522B JSVMP\uFF08JavaScript Virtual Machine Protection\uFF09\u4FDD\u62A4\u4EE3\u7801\u3002
37
+
38
+ JSVMP \u662F\u4E00\u79CD\u4EE3\u7801\u4FDD\u62A4\u6280\u672F\uFF0C\u5C06 JavaScript \u4EE3\u7801\u8F6C\u6362\u4E3A\u5B57\u8282\u7801\uFF0C\u5E76\u901A\u8FC7\u865A\u62DF\u673A\u6267\u884C\u3002\u5178\u578B\u7279\u5F81\u5305\u62EC\uFF1A
39
+
40
+ 1. **\u865A\u62DF\u6808\uFF08Virtual Stack\uFF09**\uFF1A\u4E2D\u592E\u6570\u7EC4\u7528\u4E8E\u5B58\u50A8\u64CD\u4F5C\u6570\u548C\u7ED3\u679C
41
+ 2. **\u5206\u53D1\u5668\uFF08Dispatcher\uFF09**\uFF1A\u5927\u578B switch \u8BED\u53E5\u6216\u5D4C\u5957 if-else \u94FE\uFF0C\u6839\u636E\u6307\u4EE4\u7801\u6267\u884C\u4E0D\u540C\u64CD\u4F5C
42
+ 3. **\u6307\u4EE4\u6570\u7EC4\uFF08Instruction Array\uFF09**\uFF1A\u5B58\u50A8\u5B57\u8282\u7801\u6307\u4EE4\u7684\u6570\u7EC4
43
+ 4. **\u4E3B\u5FAA\u73AF\uFF08Main Loop\uFF09**\uFF1Awhile \u5FAA\u73AF\u6301\u7EED\u6267\u884C\u6307\u4EE4
44
+
45
+ \u68C0\u6D4B\u89C4\u5219\uFF1A
46
+
47
+ **Ultra High \u7F6E\u4FE1\u5EA6**\uFF1A
48
+ - \u540C\u65F6\u51FA\u73B0\uFF1A\u4E3B\u5FAA\u73AF + \u5206\u53D1\u5668 + \u6808\u64CD\u4F5C
49
+ - \u5206\u53D1\u5668\u6709 >20 \u4E2A case \u6216 >10 \u5C42\u5D4C\u5957
50
+ - \u660E\u786E\u7684\u6808\u64CD\u4F5C\u6A21\u5F0F\uFF08push/pop/\u6570\u7EC4\u7D22\u5F15\uFF09
51
+
52
+ **High \u7F6E\u4FE1\u5EA6**\uFF1A
53
+ - \u72EC\u7ACB\u7684\u5927\u578B\u5206\u53D1\u5668\u7ED3\u6784\uFF08>20 case \u7684 switch \u6216 >10 \u5C42\u5D4C\u5957\u7684 if-else\uFF09
54
+ - \u660E\u786E\u7684\u6307\u4EE4\u6570\u7EC4\u548C\u7A0B\u5E8F\u8BA1\u6570\u5668\u6A21\u5F0F
55
+
56
+ **Medium \u7F6E\u4FE1\u5EA6**\uFF1A
57
+ - \u5B64\u7ACB\u7684\u6808\u64CD\u4F5C\u6216\u53EF\u7591\u7684 while \u5FAA\u73AF
58
+ - \u90E8\u5206 JSVMP \u7279\u5F81\u4F46\u4E0D\u5B8C\u6574
59
+
60
+ **Low \u7F6E\u4FE1\u5EA6**\uFF1A
61
+ - \u901A\u7528\u6DF7\u6DC6\u6A21\u5F0F
62
+ - \u53EF\u80FD\u76F8\u5173\u4F46\u4E0D\u786E\u5B9A\u7684\u7ED3\u6784
63
+
64
+ \u8BF7\u5206\u6790\u63D0\u4F9B\u7684\u4EE3\u7801\uFF0C\u8BC6\u522B JSVMP \u76F8\u5173\u533A\u57DF\u3002\u8FD4\u56DE JSON \u683C\u5F0F\uFF1A
65
+
66
+ {
67
+ "summary": "\u5206\u6790\u6458\u8981\uFF08\u4E2D\u6587\uFF09",
68
+ "regions": [
69
+ {
70
+ "start": \u8D77\u59CB\u884C\u53F7,
71
+ "end": \u7ED3\u675F\u884C\u53F7,
72
+ "type": "If-Else Dispatcher" | "Switch Dispatcher" | "Instruction Array" | "Stack Operation",
73
+ "confidence": "ultra_high" | "high" | "medium" | "low",
74
+ "description": "\u8BE6\u7EC6\u63CF\u8FF0\uFF08\u4E2D\u6587\uFF09"
75
+ }
76
+ ]
77
+ }
78
+
79
+ \u5982\u679C\u6CA1\u6709\u68C0\u6D4B\u5230 JSVMP \u7279\u5F81\uFF0C\u8FD4\u56DE\u7A7A\u7684 regions \u6570\u7EC4\u3002`;
80
+ }
81
+ function createLLMClient(config) {
82
+ return {
83
+ async analyzeJSVMP(formattedCode) {
84
+ const systemPrompt = buildJSVMPSystemPrompt();
85
+ const requestBody = {
86
+ model: config.model,
87
+ messages: [
88
+ {
89
+ role: "system",
90
+ content: systemPrompt
91
+ },
92
+ {
93
+ role: "user",
94
+ content: `\u8BF7\u5206\u6790\u4EE5\u4E0B\u4EE3\u7801\uFF0C\u8BC6\u522B JSVMP \u4FDD\u62A4\u7ED3\u6784\uFF1A
95
+
96
+ ${formattedCode}`
97
+ }
98
+ ],
99
+ temperature: 0.1,
100
+ response_format: { type: "json_object" }
101
+ };
102
+ try {
103
+ const response = await fetch(`${config.baseUrl}/chat/completions`, {
104
+ method: "POST",
105
+ headers: {
106
+ "Content-Type": "application/json",
107
+ "Authorization": `Bearer ${config.apiKey}`
108
+ },
109
+ body: JSON.stringify(requestBody)
110
+ });
111
+ if (!response.ok) {
112
+ const errorText = await response.text();
113
+ throw new Error(`API \u8BF7\u6C42\u5931\u8D25 (${response.status}): ${errorText}`);
114
+ }
115
+ const data = await response.json();
116
+ if (!data.choices || !data.choices[0] || !data.choices[0].message) {
117
+ throw new Error("API \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF1A\u7F3A\u5C11 choices \u6216 message \u5B57\u6BB5");
118
+ }
119
+ const content = data.choices[0].message.content;
120
+ if (typeof content !== "string") {
121
+ throw new Error("API \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF1Amessage.content \u4E0D\u662F\u5B57\u7B26\u4E32");
122
+ }
123
+ return content;
124
+ } catch (error) {
125
+ if (error instanceof Error) {
126
+ throw new Error(`LLM \u8BF7\u6C42\u5931\u8D25: ${error.message}`);
127
+ }
128
+ throw new Error(`LLM \u8BF7\u6C42\u5931\u8D25: ${String(error)}`);
129
+ }
130
+ }
131
+ };
132
+ }
133
+
134
+ // src/jsvmpDetector.ts
135
+ function formatSourcePosition(line, column) {
136
+ if (line !== null && column !== null) {
137
+ return `L${line}:${column}`;
138
+ }
139
+ return "";
140
+ }
141
+ function formatCodeLine(lineNumber, sourcePos, code) {
142
+ const lineNumStr = String(lineNumber).padStart(5, " ");
143
+ const srcPosPadded = sourcePos ? sourcePos.padEnd(10, " ") : " ";
144
+ return `${lineNumStr} ${srcPosPadded} ${code}`;
145
+ }
146
+ async function formatCodeForAnalysis(filePath, startLine, endLine, charLimit = 300) {
147
+ const beautifyResult = await ensureBeautified(filePath);
148
+ const { code, rawMap } = beautifyResult;
149
+ const truncatedCode = truncateCodeHighPerf(code, charLimit);
150
+ const lines = truncatedCode.split("\n");
151
+ const totalLines = lines.length;
152
+ const effectiveStartLine = Math.max(1, Math.min(totalLines, startLine));
153
+ const effectiveEndLine = Math.max(effectiveStartLine, Math.min(totalLines, endLine));
154
+ const formattedLines = [];
155
+ let consumer = null;
156
+ if (rawMap && rawMap.sources && rawMap.names && rawMap.mappings) {
157
+ consumer = new SourceMapConsumer({
158
+ version: String(rawMap.version),
159
+ sources: rawMap.sources,
160
+ names: rawMap.names,
161
+ mappings: rawMap.mappings,
162
+ file: rawMap.file,
163
+ sourceRoot: rawMap.sourceRoot
164
+ });
165
+ }
166
+ for (let lineNum = effectiveStartLine; lineNum <= effectiveEndLine; lineNum++) {
167
+ const lineIndex = lineNum - 1;
168
+ const lineContent = lines[lineIndex] ?? "";
169
+ let sourcePos = "";
170
+ if (consumer) {
171
+ const originalPos = consumer.originalPositionFor({
172
+ line: lineNum,
173
+ column: 0
174
+ });
175
+ sourcePos = formatSourcePosition(originalPos.line, originalPos.column);
176
+ }
177
+ formattedLines.push(formatCodeLine(lineNum, sourcePos, lineContent));
178
+ }
179
+ return {
180
+ content: formattedLines.join("\n"),
181
+ totalLines,
182
+ startLine: effectiveStartLine,
183
+ endLine: effectiveEndLine
184
+ };
185
+ }
186
+ var VALID_DETECTION_TYPES = [
187
+ "If-Else Dispatcher",
188
+ "Switch Dispatcher",
189
+ "Instruction Array",
190
+ "Stack Operation"
191
+ ];
192
+ var VALID_CONFIDENCE_LEVELS = [
193
+ "ultra_high",
194
+ "high",
195
+ "medium",
196
+ "low"
197
+ ];
198
+ function isValidDetectionType(value) {
199
+ return VALID_DETECTION_TYPES.includes(value);
200
+ }
201
+ function isValidConfidenceLevel(value) {
202
+ return VALID_CONFIDENCE_LEVELS.includes(value);
203
+ }
204
+ function parseDetectionResult(jsonString) {
205
+ let parsed;
206
+ try {
207
+ parsed = JSON.parse(jsonString);
208
+ } catch (error) {
209
+ throw new Error(`\u65E0\u6CD5\u89E3\u6790 LLM \u54CD\u5E94: ${error instanceof Error ? error.message : String(error)}`);
210
+ }
211
+ if (typeof parsed !== "object" || parsed === null) {
212
+ throw new Error("LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0C\u671F\u671B\u5BF9\u8C61\u7C7B\u578B");
213
+ }
214
+ const obj = parsed;
215
+ if (typeof obj.summary !== "string") {
216
+ throw new Error("LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0C\u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: summary");
217
+ }
218
+ if (!Array.isArray(obj.regions)) {
219
+ throw new Error("LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0C\u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: regions");
220
+ }
221
+ const validatedRegions = [];
222
+ for (let i = 0; i < obj.regions.length; i++) {
223
+ const region = obj.regions[i];
224
+ if (typeof region !== "object" || region === null) {
225
+ throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u4E0D\u662F\u5BF9\u8C61`);
226
+ }
227
+ if (typeof region.start !== "number") {
228
+ throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: start`);
229
+ }
230
+ if (typeof region.end !== "number") {
231
+ throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: end`);
232
+ }
233
+ if (typeof region.type !== "string") {
234
+ throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: type`);
235
+ }
236
+ if (typeof region.confidence !== "string") {
237
+ throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: confidence`);
238
+ }
239
+ if (typeof region.description !== "string") {
240
+ throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: description`);
241
+ }
242
+ if (!isValidDetectionType(region.type)) {
243
+ throw new Error(
244
+ `LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}].type \u503C\u65E0\u6548: "${region.type}". \u6709\u6548\u503C: ${VALID_DETECTION_TYPES.join(", ")}`
245
+ );
246
+ }
247
+ if (!isValidConfidenceLevel(region.confidence)) {
248
+ throw new Error(
249
+ `LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}].confidence \u503C\u65E0\u6548: "${region.confidence}". \u6709\u6548\u503C: ${VALID_CONFIDENCE_LEVELS.join(", ")}`
250
+ );
251
+ }
252
+ validatedRegions.push({
253
+ start: region.start,
254
+ end: region.end,
255
+ type: region.type,
256
+ confidence: region.confidence,
257
+ description: region.description
258
+ });
259
+ }
260
+ return {
261
+ summary: obj.summary,
262
+ regions: validatedRegions
263
+ };
264
+ }
265
+ function formatDetectionResultOutput(result, filePath, startLine, endLine) {
266
+ const lines = [];
267
+ lines.push("=== JSVMP Dispatcher Detection Result ===");
268
+ lines.push(`File: ${filePath} (${startLine}-${endLine})`);
269
+ lines.push("");
270
+ lines.push(`Summary: ${result.summary}`);
271
+ lines.push("");
272
+ if (result.regions.length > 0) {
273
+ lines.push("Detected Regions:");
274
+ for (const region of result.regions) {
275
+ lines.push(`[${region.confidence}] Lines ${region.start}-${region.end}: ${region.type}`);
276
+ lines.push(` ${region.description}`);
277
+ lines.push("");
278
+ }
279
+ } else {
280
+ lines.push("No JSVMP dispatcher patterns detected.");
281
+ }
282
+ return lines.join("\n");
283
+ }
284
+ async function findJsvmpDispatcher(filePath, startLine, endLine, options) {
285
+ const charLimit = options?.charLimit ?? 300;
286
+ const config = getLLMConfig();
287
+ if (!config) {
288
+ return {
289
+ success: false,
290
+ filePath,
291
+ startLine,
292
+ endLine,
293
+ error: "\u672A\u914D\u7F6E LLM\u3002\u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF OPENAI_API_KEY \u4EE5\u542F\u7528 JSVMP dispatcher \u68C0\u6D4B\u529F\u80FD\u3002"
294
+ };
295
+ }
296
+ if (!existsSync(filePath)) {
297
+ return {
298
+ success: false,
299
+ filePath,
300
+ startLine,
301
+ endLine,
302
+ error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`
303
+ };
304
+ }
305
+ try {
306
+ const formattedCode = await formatCodeForAnalysis(
307
+ filePath,
308
+ startLine,
309
+ endLine,
310
+ charLimit
311
+ );
312
+ const client = createLLMClient(config);
313
+ const llmResponse = await client.analyzeJSVMP(formattedCode.content);
314
+ const result = parseDetectionResult(llmResponse);
315
+ const formattedOutput = formatDetectionResultOutput(result, filePath, startLine, endLine);
316
+ return {
317
+ success: true,
318
+ filePath,
319
+ startLine: formattedCode.startLine,
320
+ endLine: formattedCode.endLine,
321
+ result,
322
+ formattedOutput
323
+ };
324
+ } catch (error) {
325
+ return {
326
+ success: false,
327
+ filePath,
328
+ startLine,
329
+ endLine,
330
+ error: error instanceof Error ? error.message : String(error)
331
+ };
332
+ }
333
+ }
334
+
335
+ // src/tools/findJsvmpDispatcherTool.ts
336
+ var FindJsvmpDispatcherInputSchema = {
337
+ filePath: z.string().describe("Path to the JavaScript file to analyze"),
338
+ startLine: z.number().int().positive().describe("Start line number (1-based)"),
339
+ endLine: z.number().int().positive().describe("End line number (1-based)"),
340
+ charLimit: z.number().int().positive().optional().describe("Character limit for string truncation (default: 300)")
341
+ };
342
+ var findJsvmpDispatcherTool = defineTool({
343
+ name: "find_jsvmp_dispatcher",
344
+ description: `Detect JSVMP (JavaScript Virtual Machine Protection) patterns in code using LLM analysis.
345
+
346
+ JSVMP is a code protection technique that converts JavaScript to bytecode executed by a virtual machine. This tool identifies:
347
+ - If-Else Dispatchers: Nested if-else chains for instruction dispatch
348
+ - Switch Dispatchers: Large switch statements (>20 cases) for opcode handling
349
+ - Instruction Arrays: Arrays storing bytecode instructions
350
+ - Stack Operations: Virtual stack push/pop patterns
351
+
352
+ Returns detection results with confidence levels (ultra_high, high, medium, low) and detailed descriptions.
353
+
354
+ Requires OPENAI_API_KEY environment variable. Optional: OPENAI_BASE_URL, OPENAI_MODEL.`,
355
+ schema: FindJsvmpDispatcherInputSchema,
356
+ handler: async (params) => {
357
+ const { filePath, startLine, endLine, charLimit } = params;
358
+ if (endLine < startLine) {
359
+ throw new Error("endLine must be >= startLine");
360
+ }
361
+ const result = await findJsvmpDispatcher(filePath, startLine, endLine, {
362
+ charLimit: charLimit ?? 300
363
+ });
364
+ if (!result.success) {
365
+ throw new Error(result.error ?? "Detection failed");
366
+ }
367
+ return result.formattedOutput ?? "No output generated";
368
+ }
369
+ });
370
+
371
+ // src/tools/index.ts
372
+ var tools = [
373
+ findJsvmpDispatcherTool
374
+ ];
375
+
376
+ // src/server.ts
377
+ var server = new McpServer({
378
+ name: "ai-tools-mcp",
379
+ version: "1.0.0"
380
+ });
381
+ function registerTool(tool) {
382
+ const zodSchema = z2.object(tool.schema);
383
+ server.registerTool(
384
+ tool.name,
385
+ {
386
+ description: tool.description,
387
+ inputSchema: tool.schema
388
+ },
389
+ async (params, _extra) => {
390
+ try {
391
+ const validatedParams = zodSchema.parse(params);
392
+ const result = await tool.handler(validatedParams);
393
+ return {
394
+ content: [{ type: "text", text: result }]
395
+ };
396
+ } catch (error) {
397
+ const message = error instanceof Error ? error.message : String(error);
398
+ return {
399
+ content: [{ type: "text", text: `Error: ${message}` }],
400
+ isError: true
401
+ };
402
+ }
403
+ }
404
+ );
405
+ }
406
+ for (const tool of tools) {
407
+ registerTool(tool);
408
+ }
409
+ async function main() {
410
+ const transport = new StdioServerTransport();
411
+ await server.connect(transport);
412
+ console.error("AI Tools MCP Server running on stdio");
413
+ }
414
+ main().catch((error) => {
415
+ console.error("Fatal error:", error);
416
+ process.exit(1);
417
+ });
418
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/server.ts", "../src/tools/findJsvmpDispatcherTool.ts", "../src/tools/ToolDefinition.ts", "../src/jsvmpDetector.ts", "../src/llmConfig.ts", "../src/tools/index.ts"],
4
+ "sourcesContent": ["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport { tools } from './tools/index.js';\n\n// Create MCP Server instance\nconst server = new McpServer({\n name: 'ai-tools-mcp',\n version: '1.0.0',\n});\n\n/**\n * Register a tool with the MCP server.\n */\nfunction registerTool(tool: {\n name: string;\n description: string;\n schema: z.ZodRawShape;\n handler: (params: Record<string, unknown>) => Promise<string>;\n}): void {\n const zodSchema = z.object(tool.schema);\n\n server.registerTool(\n tool.name,\n {\n description: tool.description,\n inputSchema: tool.schema,\n },\n async (params, _extra) => {\n try {\n const validatedParams = zodSchema.parse(params);\n const result = await tool.handler(validatedParams as Record<string, unknown>);\n\n return {\n content: [{ type: 'text' as const, text: result }],\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n }\n }\n );\n}\n\n// Register all tools\nfor (const tool of tools) {\n registerTool(tool as unknown as {\n name: string;\n description: string;\n schema: z.ZodRawShape;\n handler: (params: Record<string, unknown>) => Promise<string>;\n });\n}\n\n// Main entry point\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error('AI Tools MCP Server running on stdio');\n}\n\nmain().catch((error) => {\n console.error('Fatal error:', error);\n process.exit(1);\n});\n", "import { z } from 'zod';\nimport { defineTool } from './ToolDefinition.js';\nimport { findJsvmpDispatcher } from '../jsvmpDetector.js';\n\n/**\n * Input schema for find_jsvmp_dispatcher tool\n */\nexport const FindJsvmpDispatcherInputSchema = {\n filePath: z.string().describe('Path to the JavaScript file to analyze'),\n startLine: z.number().int().positive().describe('Start line number (1-based)'),\n endLine: z.number().int().positive().describe('End line number (1-based)'),\n charLimit: z.number().int().positive().optional().describe('Character limit for string truncation (default: 300)'),\n};\n\n/**\n * MCP Tool: find_jsvmp_dispatcher\n * \n * Uses LLM to detect JSVMP (JavaScript Virtual Machine Protection) patterns in code.\n * \n * JSVMP is a code protection technique that converts JavaScript to bytecode\n * executed by a virtual machine. This tool identifies:\n * - If-Else Dispatchers\n * - Switch Dispatchers \n * - Instruction Arrays\n * - Stack Operations\n * \n * Requires OPENAI_API_KEY environment variable to be set.\n */\nexport const findJsvmpDispatcherTool = defineTool({\n name: 'find_jsvmp_dispatcher',\n description: `Detect JSVMP (JavaScript Virtual Machine Protection) patterns in code using LLM analysis.\n\nJSVMP is a code protection technique that converts JavaScript to bytecode executed by a virtual machine. This tool identifies:\n- If-Else Dispatchers: Nested if-else chains for instruction dispatch\n- Switch Dispatchers: Large switch statements (>20 cases) for opcode handling\n- Instruction Arrays: Arrays storing bytecode instructions\n- Stack Operations: Virtual stack push/pop patterns\n\nReturns detection results with confidence levels (ultra_high, high, medium, low) and detailed descriptions.\n\nRequires OPENAI_API_KEY environment variable. Optional: OPENAI_BASE_URL, OPENAI_MODEL.`,\n schema: FindJsvmpDispatcherInputSchema,\n handler: async (params): Promise<string> => {\n const { filePath, startLine, endLine, charLimit } = params;\n\n // Validate endLine >= startLine\n if (endLine < startLine) {\n throw new Error('endLine must be >= startLine');\n }\n\n const result = await findJsvmpDispatcher(filePath, startLine, endLine, {\n charLimit: charLimit ?? 300,\n });\n\n if (!result.success) {\n throw new Error(result.error ?? 'Detection failed');\n }\n\n return result.formattedOutput ?? 'No output generated';\n },\n});\n", "import { z } from 'zod';\n\n/**\n * Tool definition interface for MCP tools.\n * Each tool has a name, description, schema (Zod raw shape), and async handler.\n */\nexport interface ToolDefinition<TSchema extends z.ZodRawShape = z.ZodRawShape> {\n /** Unique tool name (e.g., 'find_jsvmp_dispatcher') */\n name: string;\n /** Human-readable description of what the tool does */\n description: string;\n /** Zod schema object defining input parameters */\n schema: TSchema;\n /** Async handler function that processes the tool request */\n handler: (params: z.infer<z.ZodObject<TSchema>>) => Promise<string>;\n}\n\n/**\n * Helper function to create a type-safe tool definition.\n * Validates the tool definition structure at compile time.\n * \n * @param definition - The tool definition object\n * @returns The same definition with proper typing\n */\nexport function defineTool<TSchema extends z.ZodRawShape>(\n definition: ToolDefinition<TSchema>\n): ToolDefinition<TSchema> {\n return definition;\n}\n", "/**\n * JSVMP Detector Module\n * AI-powered detection of JSVMP (JavaScript Virtual Machine Protection) patterns\n */\n\nimport { SourceMapConsumer } from 'source-map-js';\nimport { ensureBeautified, truncateCodeHighPerf } from '@reverse-craft/smart-fs';\nimport { existsSync } from 'fs';\nimport { getLLMConfig, createLLMClient } from './llmConfig.js';\n\n/**\n * Formatted code result interface\n */\nexport interface FormattedCode {\n content: string; // \u683C\u5F0F\u5316\u540E\u7684\u4EE3\u7801\u5B57\u7B26\u4E32\n totalLines: number; // \u603B\u884C\u6570\n startLine: number; // \u5B9E\u9645\u8D77\u59CB\u884C\n endLine: number; // \u5B9E\u9645\u7ED3\u675F\u884C\n}\n\n/**\n * Detection type for JSVMP patterns\n */\nexport type DetectionType = \n | \"If-Else Dispatcher\" \n | \"Switch Dispatcher\" \n | \"Instruction Array\" \n | \"Stack Operation\";\n\n/**\n * Confidence level for detection results\n */\nexport type ConfidenceLevel = \"ultra_high\" | \"high\" | \"medium\" | \"low\";\n\n/**\n * A detected region in the code\n */\nexport interface DetectionRegion {\n start: number; // \u8D77\u59CB\u884C\u53F7\n end: number; // \u7ED3\u675F\u884C\u53F7\n type: DetectionType; // \u68C0\u6D4B\u7C7B\u578B\n confidence: ConfidenceLevel; // \u7F6E\u4FE1\u5EA6\n description: string; // \u63CF\u8FF0\uFF08\u4E2D\u6587\uFF09\n}\n\n/**\n * Complete detection result from LLM analysis\n */\nexport interface DetectionResult {\n summary: string; // \u5206\u6790\u6458\u8981\uFF08\u4E2D\u6587\uFF09\n regions: DetectionRegion[];\n}\n\n/**\n * Options for JSVMP detection\n */\nexport interface JsvmpDetectionOptions {\n charLimit?: number;\n}\n\n/**\n * Result from findJsvmpDispatcher function\n */\nexport interface JsvmpDetectionResult {\n success: boolean;\n filePath: string;\n startLine: number;\n endLine: number;\n result?: DetectionResult;\n formattedOutput?: string;\n error?: string;\n}\n\n/**\n * Format source position as \"L{line}:{column}\" or empty placeholder\n */\nfunction formatSourcePosition(line: number | null, column: number | null): string {\n if (line !== null && column !== null) {\n return `L${line}:${column}`;\n }\n return '';\n}\n\n/**\n * Format a single code line with line number, source coordinates, and content\n * Format: \"LineNo SourceLoc Code\"\n */\nfunction formatCodeLine(lineNumber: number, sourcePos: string, code: string): string {\n const lineNumStr = String(lineNumber).padStart(5, ' ');\n const srcPosPadded = sourcePos ? sourcePos.padEnd(10, ' ') : ' ';\n return `${lineNumStr} ${srcPosPadded} ${code}`;\n}\n\n/**\n * \u683C\u5F0F\u5316\u4EE3\u7801\u4E3A LLM \u5206\u6790\u683C\u5F0F\n * \u683C\u5F0F: \"LineNo SourceLoc Code\"\n * \n * \u5904\u7406\u6D41\u7A0B\uFF1A\n * 1. \u8C03\u7528 ensureBeautified \u7F8E\u5316\u4EE3\u7801\n * 2. \u8C03\u7528 truncateCodeHighPerf \u622A\u65AD\u957F\u5B57\u7B26\u4E32\n * 3. \u4F7F\u7528 SourceMapConsumer \u83B7\u53D6\u539F\u59CB\u5750\u6807\n * 4. \u683C\u5F0F\u5316\u4E3A \"LineNo SourceLoc Code\" \u683C\u5F0F\n * \n * @param filePath - Path to the JavaScript file\n * @param startLine - Start line number (1-based)\n * @param endLine - End line number (1-based)\n * @param charLimit - Character limit for string truncation (default 300)\n * @returns FormattedCode object with formatted content and metadata\n */\nexport async function formatCodeForAnalysis(\n filePath: string,\n startLine: number,\n endLine: number,\n charLimit: number = 300\n): Promise<FormattedCode> {\n // Step 1: Beautify the file and get source map\n const beautifyResult = await ensureBeautified(filePath);\n const { code, rawMap } = beautifyResult;\n\n // Step 2: Truncate long strings\n const truncatedCode = truncateCodeHighPerf(code, charLimit);\n\n // Split into lines\n const lines = truncatedCode.split('\\n');\n const totalLines = lines.length;\n\n // Step 3: Adjust line range boundaries\n const effectiveStartLine = Math.max(1, Math.min(totalLines, startLine));\n const effectiveEndLine = Math.max(effectiveStartLine, Math.min(totalLines, endLine));\n\n // Step 4: Format each line with \"LineNo SourceLoc Code\" format\n const formattedLines: string[] = [];\n\n // Create source map consumer if available\n let consumer: SourceMapConsumer | null = null;\n if (rawMap && rawMap.sources && rawMap.names && rawMap.mappings) {\n consumer = new SourceMapConsumer({\n version: String(rawMap.version),\n sources: rawMap.sources,\n names: rawMap.names,\n mappings: rawMap.mappings,\n file: rawMap.file,\n sourceRoot: rawMap.sourceRoot,\n });\n }\n\n for (let lineNum = effectiveStartLine; lineNum <= effectiveEndLine; lineNum++) {\n const lineIndex = lineNum - 1;\n const lineContent = lines[lineIndex] ?? '';\n\n // Get original position from source map if available\n let sourcePos = '';\n if (consumer) {\n const originalPos = consumer.originalPositionFor({\n line: lineNum,\n column: 0,\n });\n sourcePos = formatSourcePosition(originalPos.line, originalPos.column);\n }\n \n formattedLines.push(formatCodeLine(lineNum, sourcePos, lineContent));\n }\n\n return {\n content: formattedLines.join('\\n'),\n totalLines,\n startLine: effectiveStartLine,\n endLine: effectiveEndLine,\n };\n}\n\n/**\n * Valid detection types for validation\n */\nconst VALID_DETECTION_TYPES: DetectionType[] = [\n \"If-Else Dispatcher\",\n \"Switch Dispatcher\",\n \"Instruction Array\",\n \"Stack Operation\"\n];\n\n/**\n * Valid confidence levels for validation\n */\nconst VALID_CONFIDENCE_LEVELS: ConfidenceLevel[] = [\n \"ultra_high\",\n \"high\",\n \"medium\",\n \"low\"\n];\n\n/**\n * Check if a value is a valid DetectionType\n */\nfunction isValidDetectionType(value: unknown): value is DetectionType {\n return VALID_DETECTION_TYPES.includes(value as DetectionType);\n}\n\n/**\n * Check if a value is a valid ConfidenceLevel\n */\nfunction isValidConfidenceLevel(value: unknown): value is ConfidenceLevel {\n return VALID_CONFIDENCE_LEVELS.includes(value as ConfidenceLevel);\n}\n\n/**\n * Parse and validate LLM detection result from JSON string\n * \n * Validates:\n * - JSON is parseable\n * - Required fields exist: summary, regions\n * - Each region has required fields: start, end, type, confidence, description\n * - Enum values are valid\n * \n * @param jsonString - JSON string from LLM response\n * @returns Parsed and validated DetectionResult\n * @throws Error if JSON is invalid or structure doesn't match expected format\n */\nexport function parseDetectionResult(jsonString: string): DetectionResult {\n // Parse JSON\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonString);\n } catch (error) {\n throw new Error(`\u65E0\u6CD5\u89E3\u6790 LLM \u54CD\u5E94: ${error instanceof Error ? error.message : String(error)}`);\n }\n\n // Validate required top-level fields\n if (typeof parsed !== 'object' || parsed === null) {\n throw new Error('LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0C\u671F\u671B\u5BF9\u8C61\u7C7B\u578B');\n }\n\n const obj = parsed as Record<string, unknown>;\n\n if (typeof obj.summary !== 'string') {\n throw new Error('LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0C\u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: summary');\n }\n\n if (!Array.isArray(obj.regions)) {\n throw new Error('LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0C\u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: regions');\n }\n\n // Validate each region\n const validatedRegions: DetectionRegion[] = [];\n\n for (let i = 0; i < obj.regions.length; i++) {\n const region = obj.regions[i] as Record<string, unknown>;\n\n // Check region is an object\n if (typeof region !== 'object' || region === null) {\n throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u4E0D\u662F\u5BF9\u8C61`);\n }\n\n // Validate required fields exist and have correct types\n if (typeof region.start !== 'number') {\n throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: start`);\n }\n\n if (typeof region.end !== 'number') {\n throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: end`);\n }\n\n if (typeof region.type !== 'string') {\n throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: type`);\n }\n\n if (typeof region.confidence !== 'string') {\n throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: confidence`);\n }\n\n if (typeof region.description !== 'string') {\n throw new Error(`LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}] \u7F3A\u5C11\u5FC5\u9700\u5B57\u6BB5: description`);\n }\n\n // Validate enum values\n if (!isValidDetectionType(region.type)) {\n throw new Error(\n `LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}].type \u503C\u65E0\u6548: \"${region.type}\". ` +\n `\u6709\u6548\u503C: ${VALID_DETECTION_TYPES.join(', ')}`\n );\n }\n\n if (!isValidConfidenceLevel(region.confidence)) {\n throw new Error(\n `LLM \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF0Cregions[${i}].confidence \u503C\u65E0\u6548: \"${region.confidence}\". ` +\n `\u6709\u6548\u503C: ${VALID_CONFIDENCE_LEVELS.join(', ')}`\n );\n }\n\n validatedRegions.push({\n start: region.start,\n end: region.end,\n type: region.type,\n confidence: region.confidence,\n description: region.description,\n });\n }\n\n return {\n summary: obj.summary,\n regions: validatedRegions,\n };\n}\n\n/**\n * Format detection result for display\n */\nfunction formatDetectionResultOutput(\n result: DetectionResult,\n filePath: string,\n startLine: number,\n endLine: number\n): string {\n const lines: string[] = [];\n \n lines.push('=== JSVMP Dispatcher Detection Result ===');\n lines.push(`File: ${filePath} (${startLine}-${endLine})`);\n lines.push('');\n lines.push(`Summary: ${result.summary}`);\n lines.push('');\n \n if (result.regions.length > 0) {\n lines.push('Detected Regions:');\n for (const region of result.regions) {\n lines.push(`[${region.confidence}] Lines ${region.start}-${region.end}: ${region.type}`);\n lines.push(` ${region.description}`);\n lines.push('');\n }\n } else {\n lines.push('No JSVMP dispatcher patterns detected.');\n }\n \n return lines.join('\\n');\n}\n\n/**\n * Find JSVMP dispatcher patterns in JavaScript code using LLM analysis\n * \n * @param filePath - Path to the JavaScript file to analyze\n * @param startLine - Start line number (1-based)\n * @param endLine - End line number (1-based)\n * @param options - Optional configuration\n * @returns JsvmpDetectionResult with detection results or error\n */\nexport async function findJsvmpDispatcher(\n filePath: string,\n startLine: number,\n endLine: number,\n options?: JsvmpDetectionOptions\n): Promise<JsvmpDetectionResult> {\n const charLimit = options?.charLimit ?? 300;\n \n // Check LLM configuration\n const config = getLLMConfig();\n if (!config) {\n return {\n success: false,\n filePath,\n startLine,\n endLine,\n error: '\u672A\u914D\u7F6E LLM\u3002\u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF OPENAI_API_KEY \u4EE5\u542F\u7528 JSVMP dispatcher \u68C0\u6D4B\u529F\u80FD\u3002'\n };\n }\n \n // Check file exists\n if (!existsSync(filePath)) {\n return {\n success: false,\n filePath,\n startLine,\n endLine,\n error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`\n };\n }\n \n try {\n // Format code for analysis\n const formattedCode = await formatCodeForAnalysis(\n filePath,\n startLine,\n endLine,\n charLimit\n );\n \n // Create LLM client and analyze\n const client = createLLMClient(config);\n const llmResponse = await client.analyzeJSVMP(formattedCode.content);\n \n // Parse detection result\n const result = parseDetectionResult(llmResponse);\n \n // Format output\n const formattedOutput = formatDetectionResultOutput(result, filePath, startLine, endLine);\n \n return {\n success: true,\n filePath,\n startLine: formattedCode.startLine,\n endLine: formattedCode.endLine,\n result,\n formattedOutput\n };\n \n } catch (error) {\n return {\n success: false,\n filePath,\n startLine,\n endLine,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n}\n", "/**\n * LLM Configuration Module\n * Handles reading and validating LLM configuration from environment variables\n */\n\nexport interface LLMConfig {\n apiKey: string;\n baseUrl: string;\n model: string;\n}\n\n/**\n * \u4ECE\u73AF\u5883\u53D8\u91CF\u8BFB\u53D6 LLM \u914D\u7F6E\n * @returns LLMConfig | null (null \u8868\u793A\u672A\u914D\u7F6E)\n */\nexport function getLLMConfig(): LLMConfig | null {\n const apiKey = process.env.OPENAI_API_KEY;\n \n // API Key is required\n if (!apiKey) {\n return null;\n }\n \n // Use defaults for optional configuration\n const baseUrl = process.env.OPENAI_BASE_URL || \"https://api.openai.com/v1\";\n const model = process.env.OPENAI_MODEL || \"gpt-4o-mini\";\n \n return {\n apiKey,\n baseUrl,\n model\n };\n}\n\n/**\n * \u68C0\u67E5 LLM \u662F\u5426\u5DF2\u914D\u7F6E\n */\nexport function isLLMConfigured(): boolean {\n return getLLMConfig() !== null;\n}\n\n/**\n * LLM Client Interface\n */\nexport interface LLMClient {\n /**\n * \u53D1\u9001 JSVMP \u68C0\u6D4B\u8BF7\u6C42\u5230 LLM\n * @param formattedCode \u683C\u5F0F\u5316\u540E\u7684\u4EE3\u7801\n * @returns LLM \u8FD4\u56DE\u7684\u539F\u59CB JSON \u5B57\u7B26\u4E32\n */\n analyzeJSVMP(formattedCode: string): Promise<string>;\n}\n\n/**\n * \u6784\u5EFA JSVMP \u68C0\u6D4B\u7CFB\u7EDF\u63D0\u793A\u8BCD\n */\nfunction buildJSVMPSystemPrompt(): string {\n return `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684 JavaScript \u9006\u5411\u5DE5\u7A0B\u4E13\u5BB6\uFF0C\u4E13\u95E8\u8BC6\u522B JSVMP\uFF08JavaScript Virtual Machine Protection\uFF09\u4FDD\u62A4\u4EE3\u7801\u3002\n\nJSVMP \u662F\u4E00\u79CD\u4EE3\u7801\u4FDD\u62A4\u6280\u672F\uFF0C\u5C06 JavaScript \u4EE3\u7801\u8F6C\u6362\u4E3A\u5B57\u8282\u7801\uFF0C\u5E76\u901A\u8FC7\u865A\u62DF\u673A\u6267\u884C\u3002\u5178\u578B\u7279\u5F81\u5305\u62EC\uFF1A\n\n1. **\u865A\u62DF\u6808\uFF08Virtual Stack\uFF09**\uFF1A\u4E2D\u592E\u6570\u7EC4\u7528\u4E8E\u5B58\u50A8\u64CD\u4F5C\u6570\u548C\u7ED3\u679C\n2. **\u5206\u53D1\u5668\uFF08Dispatcher\uFF09**\uFF1A\u5927\u578B switch \u8BED\u53E5\u6216\u5D4C\u5957 if-else \u94FE\uFF0C\u6839\u636E\u6307\u4EE4\u7801\u6267\u884C\u4E0D\u540C\u64CD\u4F5C\n3. **\u6307\u4EE4\u6570\u7EC4\uFF08Instruction Array\uFF09**\uFF1A\u5B58\u50A8\u5B57\u8282\u7801\u6307\u4EE4\u7684\u6570\u7EC4\n4. **\u4E3B\u5FAA\u73AF\uFF08Main Loop\uFF09**\uFF1Awhile \u5FAA\u73AF\u6301\u7EED\u6267\u884C\u6307\u4EE4\n\n\u68C0\u6D4B\u89C4\u5219\uFF1A\n\n**Ultra High \u7F6E\u4FE1\u5EA6**\uFF1A\n- \u540C\u65F6\u51FA\u73B0\uFF1A\u4E3B\u5FAA\u73AF + \u5206\u53D1\u5668 + \u6808\u64CD\u4F5C\n- \u5206\u53D1\u5668\u6709 >20 \u4E2A case \u6216 >10 \u5C42\u5D4C\u5957\n- \u660E\u786E\u7684\u6808\u64CD\u4F5C\u6A21\u5F0F\uFF08push/pop/\u6570\u7EC4\u7D22\u5F15\uFF09\n\n**High \u7F6E\u4FE1\u5EA6**\uFF1A\n- \u72EC\u7ACB\u7684\u5927\u578B\u5206\u53D1\u5668\u7ED3\u6784\uFF08>20 case \u7684 switch \u6216 >10 \u5C42\u5D4C\u5957\u7684 if-else\uFF09\n- \u660E\u786E\u7684\u6307\u4EE4\u6570\u7EC4\u548C\u7A0B\u5E8F\u8BA1\u6570\u5668\u6A21\u5F0F\n\n**Medium \u7F6E\u4FE1\u5EA6**\uFF1A\n- \u5B64\u7ACB\u7684\u6808\u64CD\u4F5C\u6216\u53EF\u7591\u7684 while \u5FAA\u73AF\n- \u90E8\u5206 JSVMP \u7279\u5F81\u4F46\u4E0D\u5B8C\u6574\n\n**Low \u7F6E\u4FE1\u5EA6**\uFF1A\n- \u901A\u7528\u6DF7\u6DC6\u6A21\u5F0F\n- \u53EF\u80FD\u76F8\u5173\u4F46\u4E0D\u786E\u5B9A\u7684\u7ED3\u6784\n\n\u8BF7\u5206\u6790\u63D0\u4F9B\u7684\u4EE3\u7801\uFF0C\u8BC6\u522B JSVMP \u76F8\u5173\u533A\u57DF\u3002\u8FD4\u56DE JSON \u683C\u5F0F\uFF1A\n\n{\n \"summary\": \"\u5206\u6790\u6458\u8981\uFF08\u4E2D\u6587\uFF09\",\n \"regions\": [\n {\n \"start\": \u8D77\u59CB\u884C\u53F7,\n \"end\": \u7ED3\u675F\u884C\u53F7,\n \"type\": \"If-Else Dispatcher\" | \"Switch Dispatcher\" | \"Instruction Array\" | \"Stack Operation\",\n \"confidence\": \"ultra_high\" | \"high\" | \"medium\" | \"low\",\n \"description\": \"\u8BE6\u7EC6\u63CF\u8FF0\uFF08\u4E2D\u6587\uFF09\"\n }\n ]\n}\n\n\u5982\u679C\u6CA1\u6709\u68C0\u6D4B\u5230 JSVMP \u7279\u5F81\uFF0C\u8FD4\u56DE\u7A7A\u7684 regions \u6570\u7EC4\u3002`;\n}\n\n/**\n * \u521B\u5EFA LLM \u5BA2\u6237\u7AEF\u5B9E\u4F8B\n */\nexport function createLLMClient(config: LLMConfig): LLMClient {\n return {\n async analyzeJSVMP(formattedCode: string): Promise<string> {\n const systemPrompt = buildJSVMPSystemPrompt();\n \n const requestBody = {\n model: config.model,\n messages: [\n {\n role: \"system\",\n content: systemPrompt\n },\n {\n role: \"user\",\n content: `\u8BF7\u5206\u6790\u4EE5\u4E0B\u4EE3\u7801\uFF0C\u8BC6\u522B JSVMP \u4FDD\u62A4\u7ED3\u6784\uFF1A\\n\\n${formattedCode}`\n }\n ],\n temperature: 0.1,\n response_format: { type: \"json_object\" }\n };\n \n try {\n const response = await fetch(`${config.baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${config.apiKey}`\n },\n body: JSON.stringify(requestBody)\n });\n \n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`API \u8BF7\u6C42\u5931\u8D25 (${response.status}): ${errorText}`);\n }\n \n const data = await response.json();\n \n if (!data.choices || !data.choices[0] || !data.choices[0].message) {\n throw new Error(\"API \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF1A\u7F3A\u5C11 choices \u6216 message \u5B57\u6BB5\");\n }\n \n const content = data.choices[0].message.content;\n \n if (typeof content !== \"string\") {\n throw new Error(\"API \u54CD\u5E94\u683C\u5F0F\u65E0\u6548\uFF1Amessage.content \u4E0D\u662F\u5B57\u7B26\u4E32\");\n }\n \n return content;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`LLM \u8BF7\u6C42\u5931\u8D25: ${error.message}`);\n }\n throw new Error(`LLM \u8BF7\u6C42\u5931\u8D25: ${String(error)}`);\n }\n }\n };\n}\n", "/**\n * Tool aggregation module\n * Collects all tool definitions and exports them as a unified array.\n */\n\nimport { findJsvmpDispatcherTool, FindJsvmpDispatcherInputSchema } from './findJsvmpDispatcherTool.js';\n\n/**\n * Array of all available MCP tool definitions.\n * To add a new tool:\n * 1. Create a new tool module in src/tools/\n * 2. Import it here\n * 3. Add it to this array\n */\nexport const tools = [\n findJsvmpDispatcherTool,\n] as const;\n\n// Re-export ToolDefinition interface and defineTool helper\nexport { ToolDefinition, defineTool } from './ToolDefinition.js';\n\n// Re-export tool and input schema\nexport { findJsvmpDispatcherTool, FindJsvmpDispatcherInputSchema };\n"],
5
+ "mappings": "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",
6
+ "names": ["z", "z"]
7
+ }
@@ -0,0 +1,24 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Tool definition interface for MCP tools.
4
+ * Each tool has a name, description, schema (Zod raw shape), and async handler.
5
+ */
6
+ export interface ToolDefinition<TSchema extends z.ZodRawShape = z.ZodRawShape> {
7
+ /** Unique tool name (e.g., 'find_jsvmp_dispatcher') */
8
+ name: string;
9
+ /** Human-readable description of what the tool does */
10
+ description: string;
11
+ /** Zod schema object defining input parameters */
12
+ schema: TSchema;
13
+ /** Async handler function that processes the tool request */
14
+ handler: (params: z.infer<z.ZodObject<TSchema>>) => Promise<string>;
15
+ }
16
+ /**
17
+ * Helper function to create a type-safe tool definition.
18
+ * Validates the tool definition structure at compile time.
19
+ *
20
+ * @param definition - The tool definition object
21
+ * @returns The same definition with proper typing
22
+ */
23
+ export declare function defineTool<TSchema extends z.ZodRawShape>(definition: ToolDefinition<TSchema>): ToolDefinition<TSchema>;
24
+ //# sourceMappingURL=ToolDefinition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolDefinition.d.ts","sourceRoot":"","sources":["../../src/tools/ToolDefinition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,OAAO,SAAS,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW;IAC3E,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,MAAM,EAAE,OAAO,CAAC;IAChB,6DAA6D;IAC7D,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACrE;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,WAAW,EACtD,UAAU,EAAE,cAAc,CAAC,OAAO,CAAC,GAClC,cAAc,CAAC,OAAO,CAAC,CAEzB"}
@@ -0,0 +1,41 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Input schema for find_jsvmp_dispatcher tool
4
+ */
5
+ export declare const FindJsvmpDispatcherInputSchema: {
6
+ filePath: z.ZodString;
7
+ startLine: z.ZodNumber;
8
+ endLine: z.ZodNumber;
9
+ charLimit: z.ZodOptional<z.ZodNumber>;
10
+ };
11
+ /**
12
+ * MCP Tool: find_jsvmp_dispatcher
13
+ *
14
+ * Uses LLM to detect JSVMP (JavaScript Virtual Machine Protection) patterns in code.
15
+ *
16
+ * JSVMP is a code protection technique that converts JavaScript to bytecode
17
+ * executed by a virtual machine. This tool identifies:
18
+ * - If-Else Dispatchers
19
+ * - Switch Dispatchers
20
+ * - Instruction Arrays
21
+ * - Stack Operations
22
+ *
23
+ * Requires OPENAI_API_KEY environment variable to be set.
24
+ */
25
+ export declare const findJsvmpDispatcherTool: {
26
+ name: string;
27
+ description: string;
28
+ schema: {
29
+ filePath: z.ZodString;
30
+ startLine: z.ZodNumber;
31
+ endLine: z.ZodNumber;
32
+ charLimit: z.ZodOptional<z.ZodNumber>;
33
+ };
34
+ handler: (params: {
35
+ filePath: string;
36
+ startLine: number;
37
+ endLine: number;
38
+ charLimit?: number;
39
+ }) => Promise<string>;
40
+ };
41
+ //# sourceMappingURL=findJsvmpDispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findJsvmpDispatcher.d.ts","sourceRoot":"","sources":["../../src/tools/findJsvmpDispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;GAEG;AACH,eAAO,MAAM,8BAA8B;;;;;CAK1C,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;sBAcV;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,KAAG,OAAO,CAAC,MAAM,CAAC;CAapB,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Input schema for find_jsvmp_dispatcher tool
4
+ */
5
+ export declare const FindJsvmpDispatcherInputSchema: {
6
+ filePath: z.ZodString;
7
+ startLine: z.ZodNumber;
8
+ endLine: z.ZodNumber;
9
+ charLimit: z.ZodOptional<z.ZodNumber>;
10
+ };
11
+ /**
12
+ * MCP Tool: find_jsvmp_dispatcher
13
+ *
14
+ * Uses LLM to detect JSVMP (JavaScript Virtual Machine Protection) patterns in code.
15
+ *
16
+ * JSVMP is a code protection technique that converts JavaScript to bytecode
17
+ * executed by a virtual machine. This tool identifies:
18
+ * - If-Else Dispatchers
19
+ * - Switch Dispatchers
20
+ * - Instruction Arrays
21
+ * - Stack Operations
22
+ *
23
+ * Requires OPENAI_API_KEY environment variable to be set.
24
+ */
25
+ export declare const findJsvmpDispatcherTool: import("./ToolDefinition.js").ToolDefinition<{
26
+ filePath: z.ZodString;
27
+ startLine: z.ZodNumber;
28
+ endLine: z.ZodNumber;
29
+ charLimit: z.ZodOptional<z.ZodNumber>;
30
+ }>;
31
+ //# sourceMappingURL=findJsvmpDispatcherTool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findJsvmpDispatcherTool.d.ts","sourceRoot":"","sources":["../../src/tools/findJsvmpDispatcherTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;GAEG;AACH,eAAO,MAAM,8BAA8B;;;;;CAK1C,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB;;;;;EAgClC,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Property-based tests for findJsvmpDispatcherTool
3
+ *
4
+ * Tests the MCP tool's input validation and response format properties.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=findJsvmpDispatcherTool.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findJsvmpDispatcherTool.test.d.ts","sourceRoot":"","sources":["../../src/tools/findJsvmpDispatcherTool.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Tool aggregation module
3
+ * Collects all tool definitions and exports them as a unified array.
4
+ */
5
+ import { findJsvmpDispatcherTool, FindJsvmpDispatcherInputSchema } from './findJsvmpDispatcherTool.js';
6
+ /**
7
+ * Array of all available MCP tool definitions.
8
+ * To add a new tool:
9
+ * 1. Create a new tool module in src/tools/
10
+ * 2. Import it here
11
+ * 3. Add it to this array
12
+ */
13
+ export declare const tools: readonly [import("./ToolDefinition.js").ToolDefinition<{
14
+ filePath: import("zod").ZodString;
15
+ startLine: import("zod").ZodNumber;
16
+ endLine: import("zod").ZodNumber;
17
+ charLimit: import("zod").ZodOptional<import("zod").ZodNumber>;
18
+ }>];
19
+ export { ToolDefinition, defineTool } from './ToolDefinition.js';
20
+ export { findJsvmpDispatcherTool, FindJsvmpDispatcherInputSchema };
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,8BAA8B,CAAC;AAEvG;;;;;;GAMG;AACH,eAAO,MAAM,KAAK;;;;;GAER,CAAC;AAGX,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjE,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,23 +1,22 @@
1
1
  {
2
2
  "name": "@reverse-craft/ai-tools",
3
- "version": "1.0.0",
4
- "description": "AI-powered code analysis tools - LLM-driven functionality for smart-fs",
3
+ "version": "1.0.1",
4
+ "description": "MCP server for AI-powered JSVMP detection - provides LLM-driven code analysis tools",
5
5
  "type": "module",
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "import": "./dist/index.js",
11
- "types": "./dist/index.d.ts"
12
- }
6
+ "main": "dist/server.js",
7
+ "bin": {
8
+ "ai-tools-mcp": "./dist/server.js"
13
9
  },
14
10
  "scripts": {
15
- "build": "tsc",
11
+ "build": "node build.js",
16
12
  "build:types": "tsc --emitDeclarationOnly",
13
+ "start": "node dist/server.js",
17
14
  "test": "vitest --run",
18
15
  "prepublishOnly": "npm run build"
19
16
  },
20
17
  "keywords": [
18
+ "mcp",
19
+ "model-context-protocol",
21
20
  "ai",
22
21
  "llm",
23
22
  "openai",
@@ -44,7 +43,9 @@
44
43
  "node": ">=18"
45
44
  },
46
45
  "dependencies": {
47
- "source-map-js": "^1.2.1"
46
+ "@modelcontextprotocol/sdk": "^1.25.1",
47
+ "source-map-js": "^1.2.1",
48
+ "zod": "^4.2.1"
48
49
  },
49
50
  "peerDependencies": {
50
51
  "@reverse-craft/smart-fs": "^2.0.0"
@@ -52,6 +53,8 @@
52
53
  "devDependencies": {
53
54
  "@reverse-craft/smart-fs": "^2.0.0",
54
55
  "@types/node": "^22.15.29",
56
+ "esbuild": "^0.27.2",
57
+ "fast-check": "^4.5.2",
55
58
  "typescript": "^5.8.3",
56
59
  "vitest": "^4.0.16"
57
60
  }