44from pydantic import Field
55import json
66from enum import Enum
7+ import argparse
8+ import os
9+ import sys
10+
11+ # Determine how the script was invoked
12+ if sys .argv [0 ].endswith ('main.py' ):
13+ # Direct execution: python main.py
14+ prog = 'python main.py'
15+ else :
16+ # Installed script execution (via uvx, pip install, etc.)
17+ prog = None # Let argparse use the default
18+
19+ # Parse command-line arguments
20+ parser = argparse .ArgumentParser (
21+ prog = prog ,
22+ description = 'ast-grep MCP Server - Provides structural code search capabilities via Model Context Protocol' ,
23+ epilog = '''
24+ environment variables:
25+ AST_GREP_CONFIG Path to sgconfig.yaml file (overridden by --config flag)
26+
27+ For more information, see: https://github.com/ast-grep/ast-grep-mcp
28+ ''' ,
29+ formatter_class = argparse .RawDescriptionHelpFormatter
30+ )
31+ parser .add_argument (
32+ '--config' ,
33+ type = str ,
34+ metavar = 'PATH' ,
35+ help = 'Path to sgconfig.yaml file for customizing ast-grep behavior (language mappings, rule directories, etc.)'
36+ )
37+ args = parser .parse_args ()
38+
39+ # Determine config path with precedence: --config flag > AST_GREP_CONFIG env > None
40+ CONFIG_PATH = None
41+ if args .config :
42+ if not os .path .exists (args .config ):
43+ print (f"Error: Config file '{ args .config } ' does not exist" )
44+ sys .exit (1 )
45+ CONFIG_PATH = args .config
46+ elif os .environ .get ('AST_GREP_CONFIG' ):
47+ env_config = os .environ .get ('AST_GREP_CONFIG' )
48+ if not os .path .exists (env_config ):
49+ print (f"Error: Config file '{ env_config } ' specified in AST_GREP_CONFIG does not exist" )
50+ sys .exit (1 )
51+ CONFIG_PATH = env_config
752
853# Initialize FastMCP server
954mcp = FastMCP ("ast-grep" )
@@ -40,6 +85,8 @@ def test_match_code_rule(
4085 This is useful to test a rule before using it in a project.
4186 """
4287 args = ["ast-grep" , "scan" ,"--inline-rules" , yaml , "--json" , "--stdin" ]
88+ if CONFIG_PATH :
89+ args .extend (["--config" , CONFIG_PATH ])
4390 try :
4491 # Run command and capture output
4592 result = subprocess .run (
@@ -84,7 +131,9 @@ def find_code_by_rule(
84131 return run_ast_grep_yaml (yaml , project_folder )
85132
86133def run_ast_grep_dump (code : str , language : str , format : str ) -> str :
87- args = ["ast-grep" , "--pattern" , code , "--lang" , language , f"--debug-query={ format } " ]
134+ args = ["ast-grep" , "run" , "--pattern" , code , "--lang" , language , f"--debug-query={ format } " ]
135+ if CONFIG_PATH :
136+ args .extend (["--config" , CONFIG_PATH ])
88137 try :
89138 result = subprocess .run (
90139 args ,
@@ -100,9 +149,12 @@ def run_ast_grep_dump(code: str, language: str, format: str) -> str:
100149
101150def run_ast_grep_command (pattern : str , project_folder : str , language : Optional [str ]) -> List [dict [str , Any ]]:
102151 try :
103- args = ["ast-grep" , "--pattern" , pattern , "--json" , project_folder ]
152+ args = ["ast-grep" , "run" , "--pattern" , pattern , "--json" ]
153+ if CONFIG_PATH :
154+ args .extend (["--config" , CONFIG_PATH ])
104155 if language :
105156 args .extend (["--lang" , language ])
157+ args .append (project_folder )
106158 # Run command and capture output
107159 result = subprocess .run (
108160 args ,
@@ -121,7 +173,10 @@ def run_ast_grep_command(pattern: str, project_folder: str, language: Optional[s
121173
122174def run_ast_grep_yaml (yaml : str , project_folder : str ) -> List [dict [str , Any ]]:
123175 try :
124- args = ["ast-grep" , "scan" ,"--inline-rules" , yaml , "--json" , project_folder ]
176+ args = ["ast-grep" , "scan" , "--inline-rules" , yaml , "--json" ]
177+ if CONFIG_PATH :
178+ args .extend (["--config" , CONFIG_PATH ])
179+ args .append (project_folder )
125180 # Run command and capture output
126181 result = subprocess .run (
127182 args ,
0 commit comments