tree-ast-grep-mcp
Version:
Simple, direct ast-grep wrapper for AI coding agents. Zero abstractions, maximum performance.
219 lines (212 loc) • 8.21 kB
JavaScript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { AstGrepBinaryManager } from './core/binary-manager.js';
import { WorkspaceManager } from './core/workspace-manager.js';
import { SearchTool } from './tools/search.js';
import { ReplaceTool } from './tools/replace.js';
import { ScanTool } from './tools/scan.js';
import { BinaryError, ValidationError, ExecutionError } from './types/errors.js';
// Removed complex schema imports - using simple any types now
/**
* Parse CLI arguments and environment variables into installation options.
*/
function parseArgs() {
const args = process.argv.slice(2);
const options = {};
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === '--use-system') {
options.useSystem = true;
}
else if (arg === '--auto-install') {
options.autoInstall = true;
}
else if (arg.startsWith('--platform=')) {
options.platform = arg.split('=')[1];
}
else if (arg.startsWith('--cache-dir=')) {
options.cacheDir = arg.split('=')[1];
}
else if (arg === '--help' || arg === '-h') {
printHelp();
process.exit(0);
}
}
// Environment variable overrides
options.customBinaryPath = process.env.AST_GREP_BINARY_PATH;
options.cacheDir = options.cacheDir || process.env.AST_GREP_CACHE_DIR;
return options;
}
/**
* Print usage information for installing and configuring the MCP server.
*/
function printHelp() {
console.log(`
tree-ast-grep MCP Server - Installation Options:
LIGHTWEIGHT OPTIONS:
--use-system Use ast-grep from system PATH (200KB package)
--platform=<os> Install platform-specific binary (7MB)
Options: win32, darwin, linux
--auto-install Auto-detect and install for current platform (7MB)
CONFIGURATION:
--cache-dir=<path> Custom cache directory for binaries
ENVIRONMENT VARIABLES:
AST_GREP_BINARY_PATH Path to custom ast-grep binary
AST_GREP_CACHE_DIR Cache directory for downloaded binaries
WORKSPACE_ROOT Explicit workspace root directory
EXAMPLES:
# Lightweight (requires system ast-grep)
npx -y tree-ast-grep-mcp --use-system
# Platform-specific
npx -y tree-ast-grep-mcp --platform=win32
# Auto-install (recommended)
npx -y tree-ast-grep-mcp --auto-install
MCP CONFIGURATION:
Add to your MCP settings:
{
"mcpServers": {
"tree-ast-grep": {
"command": "npx",
"args": ["-y", "@cabbages/tree-ast-grep-mcp", "--auto-install"]
}
}
}
`);
}
/**
* Entry point for launching the MCP server and registering available tools.
*/
async function main() {
try {
// Parse installation options
const installOptions = parseArgs();
// Initialize workspace manager
const workspaceRoot = process.env.WORKSPACE_ROOT;
const workspaceManager = new WorkspaceManager(workspaceRoot);
console.error(`tree-ast-grep MCP server starting...`);
console.error(`Workspace root: ${workspaceManager.getWorkspaceRoot()}`);
// Initialize binary manager
const binaryManager = new AstGrepBinaryManager(installOptions);
await binaryManager.initialize();
console.error(`Binary initialized: ${binaryManager.getBinaryPath()}`);
// Initialize tools
const searchTool = new SearchTool(binaryManager, workspaceManager);
const replaceTool = new ReplaceTool(binaryManager, workspaceManager);
const scanTool = new ScanTool(workspaceManager, binaryManager);
// Create MCP server
const server = new Server({
name: 'tree-ast-grep',
version: '1.0.0',
}, {
capabilities: {
tools: {},
},
});
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
SearchTool.getSchema(),
ReplaceTool.getSchema(),
ScanTool.getSchema(),
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'ast_search':
const searchResult = await searchTool.execute(args);
return {
content: [
{
type: 'text',
text: JSON.stringify(searchResult, null, 2),
},
],
};
case 'ast_replace':
const replaceResult = await replaceTool.execute(args);
return {
content: [
{
type: 'text',
text: JSON.stringify(replaceResult, null, 2),
},
],
};
case 'ast_run_rule':
const scanResult = await scanTool.execute(args);
return {
content: [
{ type: 'text', text: scanResult.yaml },
{ type: 'text', text: `\n---\n${JSON.stringify(scanResult.scan, null, 2)}` },
],
};
default:
throw new Error(`Unknown tool: ${name}`);
}
}
catch (error) {
// Handle different error types
let errorMessage = 'An unknown error occurred';
let isRecoverable = false;
if (error instanceof ValidationError) {
errorMessage = `Validation Error: ${error.message}`;
if (error.context?.errors) {
errorMessage += `\nDetails: ${error.context.errors.join(', ')}`;
}
isRecoverable = true;
}
else if (error instanceof BinaryError) {
errorMessage = `Binary Error: ${error.message}`;
isRecoverable = false;
}
else if (error instanceof ExecutionError) {
errorMessage = `Execution Error: ${error.message}`;
isRecoverable = true;
}
else if (error instanceof Error) {
errorMessage = `Error: ${error.message}`;
isRecoverable = true;
}
return {
content: [
{
type: 'text',
text: errorMessage,
},
],
isError: !isRecoverable,
};
}
});
// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('tree-ast-grep MCP server running on stdio');
}
catch (error) {
console.error('Failed to start MCP server:', error);
process.exit(1);
}
}
// Handle graceful shutdown
process.on('SIGINT', () => {
console.error('Shutting down tree-ast-grep MCP server...');
process.exit(0);
});
process.on('SIGTERM', () => {
console.error('Shutting down tree-ast-grep MCP server...');
process.exit(0);
});
// Start the server
main().catch((error) => {
console.error('Unhandled error:', error);
process.exit(1);
});
//# sourceMappingURL=index.js.map