# How to Perform Structural Search in Code-Graph-RAG: AST Pattern Matching Guide

> Learn structural search in Code-Graph-RAG with AST pattern matching. Use ast-grep via Python Tool, MCP command, or API to find code patterns beyond text.

- Repository: [Vitali Avagyan/code-graph-rag](https://github.com/vitali87/code-graph-rag)
- Tags: how-to-guide
- Published: 2026-09-04

---

**Code-Graph-RAG enables structural search through AST pattern matching using the ast-grep library, exposing the functionality via an agentic Python Tool, an MCP command, or a direct service API to find code patterns beyond simple text matching.**

Structural search in Code-Graph-RAG allows you to query codebases using Abstract Syntax Tree (AST) patterns rather than plain-text regular expressions. This capability, implemented in the `vitali87/code-graph-rag` repository, leverages the **ast-grep** engine to match syntactic structures across supported languages. Whether you need to find all function definitions with specific signatures or locate print statements with particular arguments, the structural search tool provides precise, context-aware code discovery.

## Understanding AST Pattern Matching

Unlike traditional regex searches that match raw text, **structural search** operates on the parsed syntax tree of your code. This approach understands language constructs like function definitions, class hierarchies, and control flow statements. The implementation relies on the **ast-grep** library, which parses source files into ASTs and matches patterns using metavariables.

The core implementation resides in [`codebase_rag/tools/structural_search.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/tools/structural_search.py), with the underlying service logic in [`codebase_rag/tools/ast_grep_service.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/tools/ast_grep_service.py). All three access methods share this common foundation, ensuring consistent behavior regardless of how you invoke the search.

## Three Methods to Execute Structural Search

### 1. Agentic Tool (Async Python API)

The primary interface for RAG agents is the `create_structural_search_tool` function in [`codebase_rag/tools/structural_search.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/tools/structural_search.py). This creates an async-compatible Tool that can be invoked from any asynchronous code or agent framework. The async wrapper implementation spans lines 33-55, utilizing `asyncio.to_thread` to run the I/O-heavy directory traversal and AST parsing in a background thread.

```python
import asyncio
from codebase_rag.tools.structural_search import create_structural_search_tool
from codebase_rag.tools.ast_grep_service import AstGrepService

async def demo():
    # Point the service at the root of the repository you want to search

    service = AstGrepService(root_path="/path/to/your/repo")
    
    # Build the structural_search tool

    tool = create_structural_search_tool(service)
    
    # Perform a search – the function stored in `tool.function` is async

    pattern = "print($A)"           # ast-grep pattern

    language = "python"            # optional language filter

    result = await tool.function(pattern, language=language)
    
    print("Matches returned by structural_search:")
    print(result)

# Run the demo

asyncio.run(demo())

```

### 2. MCP Command (CLI and Remote Clients)

For command-line usage or remote clients, Code-Graph-RAG exposes structural search through the **Memgraph Control Protocol (MCP)**. The command registration appears in [`codebase_rag/tools/tool_descriptions.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/tools/tool_descriptions.py) at line 20 (`MCP_STRUCTURAL_SEARCH`), defining the JSON RPC interface for the tool.

```python
from codebase_rag.mcp.client import MCPClient

# Connect to the running MCP server (adjust host/port as needed)

client = MCPClient(host="localhost", port=7687)

# Perform a structural search

pattern = "def $F($$$ARGS): $$$BODY"
response = client.structural_search(
    pattern=pattern,
    language="python",          # optional

    project="my_project"       # optional – limits search to a project

)

print("MCP structural_search result:")
print(response)

```

### 3. Direct Service API (Low-Level Synchronous)

If you need raw match data without the tool wrapper overhead, instantiate `AstGrepService` directly from [`codebase_rag/tools/ast_grep_service.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/tools/ast_grep_service.py). The `search()` method performs synchronous pattern matching and returns structured dictionaries conforming to the `StructuralSearchMatch` interface.

```python
from codebase_rag.tools.ast_grep_service import AstGrepService

service = AstGrepService(root_path="/path/to/repo")
matches = service.search("print($A)", language="python")

for m in matches:
    print(f"{m['file']}:{m['line']}:{m['column']}  {m['text']}")

```

## AST Pattern Syntax and Metavariables

Code-Graph-RAG uses **ast-grep syntax** for pattern definitions, supporting two types of metavariables:

- **`$NAME`** – Matches a single AST node (e.g., a variable name, expression, or statement)
- **`$$$NAME`** – Matches a repeating subtree (e.g., multiple arguments or a function body)

Common pattern examples include:

- `print($A)` – Matches any `print` call and captures its argument as `$A`
- `def $F($$$ARGS): $$$BODY` – Matches function definitions, capturing the name (`$F`), arguments (`$$$ARGS`), and body (`$$$BODY`)

The optional `language` parameter filters files by programming language (e.g., `"python"`, `"typescript"`, `"csharp"`), ensuring patterns match only against files the parser can handle.

## Result Format and System Requirements

Search results follow a standardized string format where each line represents one match:

```

file_path:line:column  matched_code

```

If the result set exceeds the internal `AST_GREP_MAX_RESULTS` limit, the output includes an extra line indicating truncation has occurred.

Before executing, the system verifies the **ast-grep** binary is available via `has_ast_grep()`. If the binary is missing, the tool returns `AST_GREP_NOT_AVAILABLE` rather than failing silently. All search operations run in background threads using `asyncio.to_thread` to prevent blocking the event loop during filesystem traversal and AST parsing.

## Summary

- **Three access methods**: Agentic Tool (async), MCP command (remote/CLI), and direct Service API (synchronous) all leverage the same core implementation in [`codebase_rag/tools/structural_search.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/tools/structural_search.py)
- **Pattern syntax**: Use `$` for single nodes and `$$$` for repeating subtrees in ast-grep patterns
- **Source locations**: Tool wrapper at lines 33-55 of [`structural_search.py`](https://github.com/vitali87/code-graph-rag/blob/main/structural_search.py), MCP registration at line 20 of [`tool_descriptions.py`](https://github.com/vitali87/code-graph-rag/blob/main/tool_descriptions.py), service logic in [`ast_grep_service.py`](https://github.com/vitali87/code-graph-rag/blob/main/ast_grep_service.py)
- **Requirements**: Requires the ast-grep binary installed on the system; results capped at `AST_GREP_MAX_RESULTS` with truncation indicators

## Frequently Asked Questions

### What is the difference between structural search and regular text search in Code-Graph-RAG?

Structural search matches the Abstract Syntax Tree representation of code using patterns like `def $F($$$ARGS)`, while text search matches raw character sequences. AST matching understands language syntax, preventing false positives when variable names happen to contain keywords, and can capture entire code blocks as single matches using `$$$` metavariables.

### How do I limit structural search to a specific programming language?

Pass the `language` parameter when calling any of the three interfaces. Valid values include `"python"`, `"typescript"`, and `"csharp"`. When specified, the search restricts file traversal to extensions associated with that language and ensures the ast-grep parser uses the correct grammar for pattern matching.

### What happens if the ast-grep binary is not installed on the system?

The system calls `has_ast_grep()` to check for binary availability before execution. If ast-grep is missing, all structural search interfaces return the constant `AST_GREP_NOT_AVAILABLE` immediately, providing a clear error message rather than attempting execution and failing with a system error.

### Can I use structural search synchronously or only asynchronously?

Both options exist. The `AstGrepService.search()` method provides synchronous execution suitable for scripts and blocking contexts. For agent workflows and web servers, the `create_structural_search_tool` function returns an async wrapper (lines 33-55 of [`structural_search.py`](https://github.com/vitali87/code-graph-rag/blob/main/structural_search.py)) that offloads work to a background thread while maintaining an async-compatible interface.