@aiready/ast-mcp-server 0.1.0 → 0.1.2

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,118 +1,64 @@
1
1
  # @aiready/ast-mcp-server
2
2
 
3
- AST-aware codebase exploration tools for Model Context Protocol. Explore and navigate multi-project TypeScript/JavaScript codebases with high precision using AST and type information.
3
+ **Best-in-Class TypeScript/JavaScript Codebase Exploration for AI Agents.**
4
4
 
5
- ## Installation & Distribution Channels
5
+ This Model Context Protocol (MCP) server provides high-precision, AST-aware tools for navigating complex codebases. Unlike standard search tools, it understands imports, exports, types, and references.
6
6
 
7
- You can install and use the AIReady AST MCP server through several supported channels.
7
+ ## 🚀 Key Features
8
8
 
9
- ### 1. Dedicated MCP Registries
9
+ - **Hybrid Indexing**: Combines `ripgrep` speed with `ts-morph` precision. Only loads relevant files into memory to handle massive monorepos.
10
+ - **O(1) Symbol Resolution**: Instantly find where any function, class, or type is defined using a pre-built disk cache.
11
+ - **Surgical Reference Finding**: Uses a two-stage lookup (Regex + AST) to find every usage of a symbol across the project without OOM crashes.
12
+ - **Monorepo Intelligent**: Automatically discovers `tsconfig.json` boundaries and respects TypeScript project references.
13
+ - **Zero Configuration**: Bundles the `ripgrep` binary and handles all TypeScript parsing out-of-the-box.
14
+ - **Path Security**: Hardened against path traversal and malicious agent inputs.
10
15
 
11
- - **[Smithery](https://smithery.ai/server/@aiready/ast-mcp-server)**: Discover and install our server directly via the Smithery CLI:
12
- ```bash
13
- npx @smithery/cli install @aiready/ast-mcp-server
14
- ```
15
- - **[Glama](https://glama.ai/mcp/@aiready/ast-mcp-server)**: View our listing and integration options on the Glama directory.
16
- - **[Pulsar](https://gotopulsar.com)**: Find us on the Pulsar registry for MCP servers.
16
+ ## 🛠 Tools Provided
17
17
 
18
- ### 2. Direct IDE / Assistant Integrations
18
+ | Tool | Purpose |
19
+ | ---------------------- | --------------------------------------------------------------- |
20
+ | `resolve_definition` | Find where a symbol is defined (file, line, signature, JSDoc). |
21
+ | `find_references` | Find all usages of a symbol across the project (paged). |
22
+ | `find_implementations` | Find concrete classes implementing an interface/abstract class. |
23
+ | `get_file_structure` | Return a structural tree of a file (classes, methods, enums). |
24
+ | `search_code` | Blazingly fast regex search via bundled ripgrep. |
25
+ | `get_symbol_docs` | Extract full JSDoc/TSDoc metadata for any symbol. |
26
+ | `build_symbol_index` | Warm the disk cache for a project (highly recommended). |
19
27
 
20
- #### Claude Desktop App
28
+ ## 📦 Installation
21
29
 
22
- To use the AIReady AST MCP server in the Claude Desktop app, add the following configuration to your `claude_desktop_config.json`:
23
-
24
- ```json
25
- "mcpServers": {
26
- "aiready-ast": {
27
- "command": "npx",
28
- "args": ["-y", "@aiready/ast-mcp-server"]
29
- }
30
- }
30
+ ```bash
31
+ npx -y @aiready/ast-mcp-server
31
32
  ```
32
33
 
33
- #### Cursor IDE
34
-
35
- 1. Open Cursor Settings.
36
- 2. Navigate to **Features** -> **MCP Servers**.
37
- 3. Add a new server.
38
- 4. Set the command to: `npx -y @aiready/ast-mcp-server`
39
-
40
- #### Windsurf IDE
41
-
42
- 1. Open Windsurf Settings or local environment configuration.
43
- 2. Add a new MCP Server integration.
44
- 3. Configure the execution command: `npx -y @aiready/ast-mcp-server`
45
-
46
- ## Features
47
-
48
- - **Resolve Definition**: Pinpoint exactly where symbols are defined using TypeScript's type system.
49
- - **Find References**: Locate every usage of a function, class, or variable.
50
- - **Find Implementations**: Discover concrete implementations of interfaces and abstract classes.
51
- - **File Structure overview**: Get a high-level summary of imports, exports, and declarations.
52
- - **Search Code**: Blazingly fast regex search powered by Ripgrep.
53
- - **Symbol Documentation**: Instantly fetch JSDoc/TSDoc for any symbol.
54
- - **Symbol Indexing**: Project-wide symbol indexing for rapid navigation.
55
-
56
- ## Tools
57
-
58
- ### 1. `resolve_definition`
59
-
60
- Find where a symbol is defined.
61
-
62
- - `symbol`: Name of the symbol.
63
- - `path`: Project root or target directory.
64
-
65
- ### 2. `find_references`
66
-
67
- Find all usages of a symbol.
68
-
69
- - `symbol`: Symbol name.
70
- - `path`: Project root.
71
-
72
- ### 3. `find_implementations`
34
+ ## ⚙️ Configuration
73
35
 
74
- Find implementations for interfaces/abstract classes.
36
+ ### Environment Variables
75
37
 
76
- - `symbol`: Symbol name.
77
- - `path`: Project root.
38
+ - `AST_WORKSPACE_ROOT`: Path to the root of the allowed workspace (default: `cwd`).
39
+ - `AST_MAX_HEAP_MB`: Max memory allowed for AST Projects (default: `1536`).
40
+ - `AST_WORKER_POOL_SIZE`: Number of worker threads for parsing (default: `2`).
78
41
 
79
- ### 4. `get_file_structure`
42
+ ### Agent Configuration (Cursor/Claude Desktop)
80
43
 
81
- Overview of a file's structure.
82
-
83
- - `file`: Path to the file.
84
-
85
- ### 5. `search_code`
86
-
87
- Fast regex search via bundled ripgrep.
88
-
89
- - `pattern`: Regex pattern.
90
- - `path`: Directory to search.
91
- - `filePattern`: Optional glob filter.
92
-
93
- ### 6. `get_symbol_docs`
94
-
95
- Retrieve only documentation for a symbol.
96
-
97
- - `symbol`: Symbol name.
98
- - `path`: Project root.
99
-
100
- ### 7. `build_symbol_index`
101
-
102
- Build/Warm project-wide index.
44
+ ```json
45
+ {
46
+ "mcpServers": {
47
+ "ast-explorer": {
48
+ "command": "npx",
49
+ "args": ["-y", "@aiready/ast-mcp-server"],
50
+ "env": {
51
+ "AST_WORKSPACE_ROOT": "/path/to/your/project"
52
+ }
53
+ }
54
+ }
55
+ }
56
+ ```
103
57
 
104
- - `path`: Project root to index.
58
+ ## 🛡 Security
105
59
 
106
- ## Quick Start
60
+ All tool arguments are strictly validated. Any attempt by an agent to access files outside the `AST_WORKSPACE_ROOT` will result in a hard rejection.
107
61
 
108
- ```bash
109
- npx @aiready/ast-mcp-server
110
- ```
111
-
112
- ## Development
62
+ ## 📄 License
113
63
 
114
- ```bash
115
- pnpm install
116
- npm run build
117
- npm run test
118
- ```
64
+ MIT © [AIReady](https://getaiready.dev)
@@ -0,0 +1,80 @@
1
+ // src/tools/search-code.ts
2
+ import { execFile } from "child_process";
3
+ import { promisify } from "util";
4
+ import { rgPath } from "@vscode/ripgrep";
5
+
6
+ // src/security.ts
7
+ import path from "path";
8
+ function resolveWorkspaceRoot() {
9
+ return process.env.AST_WORKSPACE_ROOT || process.cwd();
10
+ }
11
+ function validateWorkspacePath(inputPath) {
12
+ const root = resolveWorkspaceRoot();
13
+ const resolved = path.resolve(root, inputPath);
14
+ const normalized = path.normalize(resolved);
15
+ if (!normalized.startsWith(root)) {
16
+ throw new Error(
17
+ `Path traversal detected: ${inputPath} escapes workspace root`
18
+ );
19
+ }
20
+ if (normalized.includes("\0")) {
21
+ throw new Error("Path contains null bytes");
22
+ }
23
+ return normalized;
24
+ }
25
+
26
+ // src/tools/search-code.ts
27
+ var execFileAsync = promisify(execFile);
28
+ async function searchCode(pattern, searchPath, filePattern, limit = 50, regex = true) {
29
+ const safePath = validateWorkspacePath(searchPath);
30
+ const args = [
31
+ "--json",
32
+ "--max-count",
33
+ limit.toString(),
34
+ "--max-columns",
35
+ "500"
36
+ ];
37
+ if (!regex) {
38
+ args.push("--fixed-strings");
39
+ }
40
+ args.push(pattern, safePath);
41
+ if (filePattern) {
42
+ args.push("--glob", filePattern);
43
+ }
44
+ args.push("--glob", "!**/node_modules/**");
45
+ args.push("--glob", "!**/dist/**");
46
+ args.push("--glob", "!**/.git/**");
47
+ try {
48
+ const { stdout } = await execFileAsync(rgPath, args);
49
+ const lines = stdout.split("\n").filter(Boolean);
50
+ const results = [];
51
+ for (const line of lines) {
52
+ const data = JSON.parse(line);
53
+ if (data.type === "match") {
54
+ const file = data.data.path.text;
55
+ const lineNumber = data.data.line_number;
56
+ const submatches = data.data.submatches;
57
+ for (const submatch of submatches) {
58
+ results.push({
59
+ file,
60
+ line: lineNumber,
61
+ column: submatch.start,
62
+ text: data.data.lines.text.trim()
63
+ });
64
+ }
65
+ }
66
+ }
67
+ return results.slice(0, limit);
68
+ } catch (error) {
69
+ if (error.code === 1) {
70
+ return [];
71
+ }
72
+ throw error;
73
+ }
74
+ }
75
+
76
+ export {
77
+ validateWorkspacePath,
78
+ searchCode
79
+ };
80
+ //# sourceMappingURL=chunk-PRWMQQYW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tools/search-code.ts","../src/security.ts"],"sourcesContent":["import { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport { rgPath } from '@vscode/ripgrep';\nimport { validateWorkspacePath } from '../security.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport interface SearchResult {\n file: string;\n line: number;\n column: number;\n text: string;\n}\n\nexport async function searchCode(\n pattern: string,\n searchPath: string,\n filePattern?: string,\n limit: number = 50,\n regex: boolean = true\n): Promise<SearchResult[]> {\n const safePath = validateWorkspacePath(searchPath);\n\n const args = [\n '--json',\n '--max-count',\n limit.toString(),\n '--max-columns',\n '500',\n ];\n\n if (!regex) {\n args.push('--fixed-strings');\n }\n\n args.push(pattern, safePath);\n\n if (filePattern) {\n args.push('--glob', filePattern);\n }\n\n // Common exclusions\n args.push('--glob', '!**/node_modules/**');\n args.push('--glob', '!**/dist/**');\n args.push('--glob', '!**/.git/**');\n\n try {\n const { stdout } = await execFileAsync(rgPath, args);\n const lines = stdout.split('\\n').filter(Boolean);\n const results: SearchResult[] = [];\n\n for (const line of lines) {\n const data = JSON.parse(line);\n if (data.type === 'match') {\n const file = data.data.path.text;\n const lineNumber = data.data.line_number;\n const submatches = data.data.submatches;\n\n for (const submatch of submatches) {\n results.push({\n file,\n line: lineNumber,\n column: submatch.start,\n text: data.data.lines.text.trim(),\n });\n }\n }\n }\n\n return results.slice(0, limit);\n } catch (error: any) {\n if (error.code === 1) {\n // rg returns 1 if no matches found\n return [];\n }\n throw error;\n }\n}\n","import path from 'path';\nimport fs from 'fs';\n\nexport function resolveWorkspaceRoot(): string {\n return process.env.AST_WORKSPACE_ROOT || process.cwd();\n}\n\nexport function validateWorkspacePath(inputPath: string): string {\n const root = resolveWorkspaceRoot();\n const resolved = path.resolve(root, inputPath);\n const normalized = path.normalize(resolved);\n\n // Reject path traversal\n if (!normalized.startsWith(root)) {\n throw new Error(\n `Path traversal detected: ${inputPath} escapes workspace root`\n );\n }\n\n // Reject null bytes\n if (normalized.includes('\\0')) {\n throw new Error('Path contains null bytes');\n }\n\n return normalized;\n}\n\nexport function validateFileExists(filePath: string): string {\n const safe = validateWorkspacePath(filePath);\n if (!fs.existsSync(safe)) {\n throw new Error(`File not found: ${filePath}`);\n }\n if (!fs.statSync(safe).isFile()) {\n throw new Error(`Not a file: ${filePath}`);\n }\n return safe;\n}\n"],"mappings":";AAAA,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,cAAc;;;ACFvB,OAAO,UAAU;AAGV,SAAS,uBAA+B;AAC7C,SAAO,QAAQ,IAAI,sBAAsB,QAAQ,IAAI;AACvD;AAEO,SAAS,sBAAsB,WAA2B;AAC/D,QAAM,OAAO,qBAAqB;AAClC,QAAM,WAAW,KAAK,QAAQ,MAAM,SAAS;AAC7C,QAAM,aAAa,KAAK,UAAU,QAAQ;AAG1C,MAAI,CAAC,WAAW,WAAW,IAAI,GAAG;AAChC,UAAM,IAAI;AAAA,MACR,4BAA4B,SAAS;AAAA,IACvC;AAAA,EACF;AAGA,MAAI,WAAW,SAAS,IAAI,GAAG;AAC7B,UAAM,IAAI,MAAM,0BAA0B;AAAA,EAC5C;AAEA,SAAO;AACT;;;ADpBA,IAAM,gBAAgB,UAAU,QAAQ;AASxC,eAAsB,WACpB,SACA,YACA,aACA,QAAgB,IAChB,QAAiB,MACQ;AACzB,QAAM,WAAW,sBAAsB,UAAU;AAEjD,QAAM,OAAO;AAAA,IACX;AAAA,IACA;AAAA,IACA,MAAM,SAAS;AAAA,IACf;AAAA,IACA;AAAA,EACF;AAEA,MAAI,CAAC,OAAO;AACV,SAAK,KAAK,iBAAiB;AAAA,EAC7B;AAEA,OAAK,KAAK,SAAS,QAAQ;AAE3B,MAAI,aAAa;AACf,SAAK,KAAK,UAAU,WAAW;AAAA,EACjC;AAGA,OAAK,KAAK,UAAU,qBAAqB;AACzC,OAAK,KAAK,UAAU,aAAa;AACjC,OAAK,KAAK,UAAU,aAAa;AAEjC,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,cAAc,QAAQ,IAAI;AACnD,UAAM,QAAQ,OAAO,MAAM,IAAI,EAAE,OAAO,OAAO;AAC/C,UAAM,UAA0B,CAAC;AAEjC,eAAW,QAAQ,OAAO;AACxB,YAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,UAAI,KAAK,SAAS,SAAS;AACzB,cAAM,OAAO,KAAK,KAAK,KAAK;AAC5B,cAAM,aAAa,KAAK,KAAK;AAC7B,cAAM,aAAa,KAAK,KAAK;AAE7B,mBAAW,YAAY,YAAY;AACjC,kBAAQ,KAAK;AAAA,YACX;AAAA,YACA,MAAM;AAAA,YACN,QAAQ,SAAS;AAAA,YACjB,MAAM,KAAK,KAAK,MAAM,KAAK,KAAK;AAAA,UAClC,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,WAAO,QAAQ,MAAM,GAAG,KAAK;AAAA,EAC/B,SAAS,OAAY;AACnB,QAAI,MAAM,SAAS,GAAG;AAEpB,aAAO,CAAC;AAAA,IACV;AACA,UAAM;AAAA,EACR;AACF;","names":[]}