AgentStack
MCP verified Apache-2.0 Self-run

Doora

mcp-backpack-lab-doora · by backpack-lab

Doora parses source files into syntax trees and runs S-expression queries to find functions, types, and call sites that grep misses. Supports Rust, Python, JS, TS, Go, C, and C++. Exposes an MCP server so AI agents understand your codebase semantically, cutting hallucinations and wasted context tokens.

No reviews yet
0 installs
13 views
0.0% view→install

Install

$ agentstack add mcp-backpack-lab-doora

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README — it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-backpack-lab-doora)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming — see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps — measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Doora? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

doora

Search code the way compilers see it — not the way text editors do.

[](https://crates.io/crates/doora) [](./LICENSE) [](https://github.com/backpack-lab/doora/actions) [](#supported-languages) [](#installation)

doora is a high-performance structural code search engine built on Tree-sitter. It parses source files into Abstract Syntax Trees and executes pattern queries against them — finding functions, types, call sites, and structural relationships that text search tools are fundamentally incapable of locating. Unlike grep, which cannot tell the difference between a function named authenticate and a comment that mentions authenticate, doora understands your code's grammar.

Additionally, doora serves as a persistent "Codebase Memory" for AI coding agents. By exposing its structural index via the Model Context Protocol (MCP), LLMs can execute precise, graph-native queries directly against your codebase, retrieving exact function signatures and dependency relationships without overwhelming their context windows with raw source text.


Table of Contents

  • [Why Not grep?](#why-not-grep)
  • [Features](#features)
  • [Installation](#installation)
  • [Quick Start](#quick-start)
  • [CLI Reference](#cli-reference)
  • [search](#search-command-default)
  • [index](#index-command)
  • [lookup](#lookup-command)
  • [serve --mcp](#serve---mcp-command)
  • [Query Syntax Guide](#query-syntax-guide)
  • [Basics](#basics)
  • [Captures](#captures)
  • [Predicates](#predicates)
  • [Multiple queries](#multiple-queries)
  • [Usage Examples by Language](#usage-examples-by-language)
  • [Rust](#rust)
  • [Python](#python)
  • [JavaScript](#javascript)
  • [TypeScript](#typescript)
  • [Go](#go)
  • [C](#c)
  • [C++](#c-1)
  • [Auto-detection](#auto-detection)
  • [The Bloom Filter Index](#the-bloom-filter-index)
  • [The Persistent Structural Index](#the-persistent-structural-index)
  • [Semantic Rewriting](#semantic-rewriting)
  • [Interactive TUI](#interactive-tui)
  • [MCP Server for AI Agents](#mcp-server-for-ai-agents)
  • [Performance](#performance)
  • [Architecture](#architecture)
  • [Building from Source](#building-from-source)
  • [Contributing](#contributing)
  • [License](#license)

Why Not grep?

Every text-based search tool — grep, ripgrep, ack, ag — suffers from the same fundamental blindness: they treat source code as a string. They have no concept of grammar, scope, or structure.

When you run:

rg "authenticate"

You get every occurrence of those 12 characters — inside function names, variable names, string literals, comments, dead code, documentation, and test fixtures alike. You get everything, and you cannot filter it without writing increasingly fragile regular expressions.

doora answers questions that text search cannot:

| Question | grep / ripgrep | doora | |---|---|---| | Find function definitions named authenticate | Returns all occurrences everywhere | Returns only function_item definition nodes | | Find functions taking exactly 2 arguments | Cannot be expressed reliably | Trivial — query the parameter list child count | | Find all unwrap() calls outside test modules | Cannot express scope constraints | Single query with scope predicate | | Find structs that implement a specific trait | Multi-step, fragile, many false positives | One S-expression query | | Rename a function at every definition site | Risks corrupting string literals and comments | Semantic rewriting via AST — surgical precision | | Find all type aliases named Result | Returns Result everywhere | Returns only type_alias_declaration nodes |

The key insight: doora is to grep what a SQL database is to a flat text file. Both contain the same data; one understands its structure.


Features

  • Structural pattern matching via Tree-sitter S-expression queries
  • 7 languages: Rust, Python, JavaScript, TypeScript, Go, C, C++
  • Language auto-detection per file from extension — walk mixed-language repos in one command
  • Multiple queries in one pass — the AST is traversed exactly once regardless of how many -q flags you pass
  • Bloom filter pre-rejection index — skip files that mathematically cannot contain your search term before invoking the parser
  • Persistent SQLite structural index — extract and query all symbols (functions, structs, types, imports) across an entire codebase
  • Semantic rewriting — surgically replace structural patterns without corrupting surrounding syntax
  • Interactive TUI — split-pane AST visualizer with live streaming results
  • MCP server — expose your codebase's structural graph to LLM coding agents
  • Respects .gitignore — never parses node_modules, target/, or build artifacts
  • Parallel file processing via Rayon work-stealing thread pool
  • Flat RAM profile — memory usage is bounded by thread count, not repository size
  • Shell completions for bash, zsh, and fish

Installation

From crates.io

cargo install doora

Requires Rust 1.78 or later.

Pre-built binaries

Download from the Releases page:

| Platform | Architecture | Binary | |---|---|---| | Linux | x8664 | doora-x86_64-unknown-linux-gnu | | Linux | aarch64 | doora-aarch64-unknown-linux-gnu | | macOS | x8664 | doora-x86_64-apple-darwin | | macOS | Apple Silicon | doora-aarch64-apple-darwin | | Windows | x86_64 | doora-x86_64-pc-windows-msvc.exe |

From source

git clone https://github.com/backpack-lab/doora
cd doora
cargo build --release
# Binary at ./target/release/doora

Shell completions

# Bash
doora --generate-completions bash >> ~/.bashrc

# Zsh
doora --generate-completions zsh >> ~/.zshrc

# Fish
doora --generate-completions fish > ~/.config/fish/completions/doora.fish

Quick Start

# Find all Rust function definitions in ./src
doora -q '(function_item name: (identifier) @fn_name)' -p ./src

# Find a specific function by name
doora -q '(function_item name: (identifier) @fn (#eq? @fn "connect"))' -p ./src

# Search Python files
doora -q '(function_definition name: (identifier) @fn)' -p . --lang python

# Auto-detect language per file — search everything at once
doora -q '(function_declaration name: (identifier) @fn_name)' -p .

# Multiple queries in a single tree-traversal pass
doora \
  -q '(function_item name: (identifier) @fn_name)' \
  -q '(struct_item name: (type_identifier) @struct_name)' \
  -p ./src --no-color

# Build a Bloom filter index for faster searches
doora index ./src

# Build both Bloom filter and persistent SQLite symbol index
doora index ./src --persist

# Look up a symbol by name in the persistent index
doora lookup --symbol parse_file --path ./src

# Launch the interactive TUI
doora -q '(function_item name: (identifier) @fn)' -p . --tui

Example output:

src/auth/handler.rs:42:0  [@fn_name]  "parse_token"
src/auth/handler.rs:89:0  [@fn_name]  "validate_session"
src/db/pool.rs:14:0       [@fn_name]  "connect"

Found 47 matches across 23 files in 38ms

CLI Reference

search command (default)

When no subcommand is given, doora runs a structural search.

doora [search] [OPTIONS] --query 

| Flag | Type | Default | Description | |---|---|---|---| | -q, --query | String (repeatable) | required | S-expression query. Pass multiple -q flags for single-pass multi-query search. | | -p, --path | PathBuf | . | Root directory to search. Must exist and be a directory. | | -l, --lang | String | auto | Language: rust, python, js, ts, go, c, cpp, auto. | | --no-color | bool | false | Disable ANSI color. Also respected via NO_COLOR env var. | | -Q, --quiet | bool | false | Suppress per-match lines. Show only the summary. | | --stats | bool | false | Print detailed performance diagnostics to stderr. | | --tui | bool | false | Launch the interactive terminal UI. | | --rewrite | String | — | Rewrite matched captures using @capture_name substitution. Dry-run by default. | | --in-place | bool | false | Apply rewrites to files. Requires --rewrite. Shows diff and prompts for confirmation. | | --yes | bool | false | Skip confirmation prompt with --in-place. | | --no-update-index | bool | false | Disable automatic incremental index updates during search. |

Output format (stdout):

src/auth/handler.rs:42:0  [@fn_name]  "parse_token"
^─────────────────────^   ^─────────^ ^────────────^
      filepath:line:col   capture     matched text
  • Filepath: cyan
  • Capture name: yellow
  • Matched text: green, always in literal quotes
  • Line numbers: 1-indexed
  • Columns: 0-indexed byte offsets

Summary (stderr):

Found 47 matches across 23 files in 38ms

Stats output (with --stats):

--- search statistics ---
files walked:     47
files parsed:     46
files skipped:    1
matches found:    12
sieve rejected:   18
match rate:       26.09% (files with matches / files parsed)
wall time:        38ms
throughput:       1236.84 files/sec
index updated:    3 entries

index command

Builds or updates the Bloom filter index and optionally the persistent SQLite structural index.

doora index  [OPTIONS]

| Flag | Type | Default | Description | |---|---|---|---| | ` | PathBuf | required | Root directory to index. | | --lang | String | auto | Language filter for indexing. | | --persist | bool | false | Also extract symbols and insert into the SQLite structural index. | | --verbose | bool | false | Print one line per file: indexed:, fresh:, or removed:`. |

The Bloom filter index is stored at /.doora-index (bincode format). The SQLite structural index is stored at /.doora-memory.db.

Both indexes are updated incrementally — files whose mtime and size match the stored entry are skipped.

# Build Bloom filter index only
doora index ./src

# Build both indexes with verbose output
doora index ./src --persist --verbose
indexed: src/auth/handler.rs
indexed: src/db/pool.rs
fresh:   src/main.rs
removed: src/old/legacy.rs

indexed 44 files, skipped 2 fresh, removed 1 stale entries, extracted 312 symbols
index written to .doora-index

lookup command

Queries the persistent SQLite structural index for symbols by name, prefix, kind, or language.

doora lookup [OPTIONS]

| Flag | Type | Default | Description | |---|---|---|---| | --symbol | String | — | Exact symbol name. Mutually exclusive with --prefix. | | --prefix | String | — | Find all symbols whose name starts with PREFIX. | | --kind | String | — | Filter by kind: function, struct, class, enum, trait, interface, type_alias, constant, variable, module, import. | | --lang | String | — | Filter results to files of this language. | | -p, --path | PathBuf | . | Root directory where the index was built. | | --no-color | bool | false | Disable ANSI color output. |

At least one of --symbol or --prefix is required. Both cannot be used together.

Output format matches structural search output exactly:

src/parser.rs:45:0  [@function]  "parse_file"
  signature: pub fn parse_file(path: &Path, language: &tree_sitter::Language) -> Result

Found 1 symbol in 1 file in 2ms

Examples:

# Look up an exact function name
doora lookup --symbol authenticate --path ./src

# Find all symbols starting with "handle_"
doora lookup --prefix handle_ --path ./src

# Find all structs
doora lookup --prefix "" --kind struct --path ./src

# Find Rust functions only (in a mixed-language repo)
doora lookup --prefix connect --kind function --lang rust --path .

serve --mcp command

Starts an MCP (Model Context Protocol) server that exposes the structural index to LLM coding agents.

doora serve --mcp [OPTIONS]

| Flag | Type | Default | Description | |---|---|---|---| | --mcp | bool | required | Enable MCP server mode over JSON-RPC stdio transport. |

See [MCP Server for AI Agents](#mcp-server-for-ai-agents) for full setup instructions.


Query Syntax Guide

doora uses Tree-sitter's S-expression pattern syntax. An S-expression is a Lisp-like notation that mirrors the shape of the syntax tree.

Looking for how to write queries? Check out our detailed [Query Guide](QUERY_GUIDE.md).

Basics

Match any node of a given type:

(function_item)

Matches every function_item node anywhere in the tree.

Match a specific child:

(function_item name: (identifier))

Matches only function_item nodes that have a name field containing an identifier node.

Nested patterns:

(impl_item
  type: (type_identifier)
  body: (declaration_list
    (function_item name: (identifier))))

Patterns can nest to arbitrary depth, mirroring the tree structure.

Wildcard:

(function_item name: (_))

(_) matches any single node regardless of type.

Captures

A @capture_name tag extracts the matched node's text and includes it in the output.

(function_item name: (identifier) @fn_name)

Multiple captures per query are supported:

(function_item
  name: (identifier) @fn_name
  parameters: (parameters) @params)

Each capture produces a separate result line in the output.

Predicates

Predicates filter captures based on their text content. They appear inside the S-expression after the structural pattern.

#eq? — exact equality:

(function_item
  name: (identifier) @fn
  (#eq? @fn "connect"))

#match? — regular expression:

(function_item
  name: (identifier) @fn
  (#match? @fn "^(get|set|update)_"))

Matches function names starting with get_, set_, or update_. The regex is compiled once at query compile time and never recompiled per file.

#not-eq? — negative equality:

(function_item
  name: (identifier) @fn
  (#not-eq? @fn "main"))

#any-of? — match any value in a list:

(function_item
  name: (identifier) @fn
  (#any-of? @fn "get" "set" "delete"))

Multiple queries

Pass multiple -q flags to run several queries in a single tree traversal. The AST is walked exactly once per file regardless of query count:

doora \
  -q '(function_item name: (identifier) @fn_name)' \
  -q '(struct_item name: (type_identifier) @struct_name)' \
  -q '(enum_item name: (type_identifier) @enum_name)' \
  -p ./src

Results from all queries are merged, sorted by file and position, and deduplicated.


Usage Examples by Language

Rust

# All function definitions
doora -q '(function_item name: (identifier) @fn_name)' -p ./src

# A specific function
doora -q '(function_item name: (identifier) @fn (#eq? @fn "authenticate"))' -p .

# All functions matching a naming pattern
doora -q '(function_item name: (identifier) @fn (#match? @fn "^handle_"))' -p ./src

# All struct definitions
doora -q '(struct_item name: (type_identifier) @struct_name)' -p ./src

# All enum definitions
doora -q '(enum_item name: (type_identifier) @enum_name)' -p ./src

# All trait definitions
doora -q '(trait_item name: (type_identifier) @trait_name)' -p ./src

# All impl blocks for a specific type
doora \
  -q '(impl_item type: (type_identifier) @t (#eq? @t "Config"))' \
  -p ./src

# All trait implementations (impl Trait for Type)
doora \
  -q '(impl_item trait: (type_identifier) @trait type: (type_identifier) @type)' \
  -p ./src

# All .unwrap() call sites
doora \
  -q '(call_expression function: (field_expression field: (field_identifier) @m (#eq? @m "unwrap")))' \
  -p ./src

# All use declarations (imports)
doora -q '(use_declaration) @import' -p ./src

# Functions returning a specific type
doora \
  -q '(function_item return_type: (generic_type type: (type_identifier) @t (#eq? @t "Result")) @fn)' \
  -p ./src

# All type aliases
doora -q '(type_item name:

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [backpack-lab](https://github.com/backpack-lab)
- **Source:** [backpack-lab/doora](https://github.com/backpack-lab/doora)
- **License:** Apache-2.0
- **Homepage:** https://backpack-lab.github.io/maps/

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.