Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Shire

                       .,:lccc:,.
                  .,codxkkOOOOkkxdoc,.
              .;ldkkOOOOOOOOOOOOOOOkkdl;.
           .:oxOOkxdollccccccccllodxkOOkxo:.
         ,lkOOxl;..                ..,lxOOkl,
       .ckOOd:.                        .:dOOkc.
      ;xOOo,          .,clllc,.          ,oOOx;
     lOOk;         .:dkOOOOOOkd:.         ;kOOl
    oOOx,        .ckOOOOOOOOOOOOkc.        ,xOOo
   lOOk,        ;xOOOkdl:;;:ldkOOOx;        ,kOOl
  ;OOO;        lOOOd;.        .;dOOOl        ;OOO;
  dOOd        :OOOl              lOOO:        dOOd
  kOOl        oOOx      .;;.     xOOo        lOOk
  kOOl        oOOx     .xOOx.    xOOo        lOOk
  dOOd        :OOOl    .oOOo.   lOOO:        dOOd
  ;OOO;        lOOOd;.  .,,. .;dOOOl        ;OOO;
   lOOk,        ;xOOOkdl:,:ldkOOOx;        ,kOOl
    oOOx,        .ckOOOOOOOOOOOOkc.        ,xOOo
     lOOk;         .:dkOOOOOOkd:.         ;kOOl
      ;xOOo,          .,clllc,.          ,oOOx;
       .ckOOd:.                        .:dOOkc.
         ,lkOOxl;..                ..,lxOOkl,
           .:oxOOkxdollccccccccllodxkOOkxo:.
              .;ldkkOOOOOOOOOOOOOOOkkdl;.
                  .,codxkkOOOOkkxdoc,.
                       .,:lccc:,.

One index to rule them all.

Search, Hierarchy, Index, Repo Explorer — a monorepo package indexer that builds a dependency graph in SQLite and serves it over Model Context Protocol.

Point it at a monorepo. It discovers every package, maps their dependency relationships, and gives your AI tools structured access to the result.

What it does

shire build walks a repository, parses manifest files, and stores packages + dependencies in a local SQLite database with full-text search. It also extracts public symbols (functions, classes, types, methods) from source files using tree-sitter, with full signatures, parameters, and return types. Every file in the repo is indexed with its path, extension, size, and owning package for instant file lookup. shire serve exposes that index as an MCP server over stdio.

Installation

From prebuilt binary

Download the latest release for macOS, Linux, or Windows from GitHub Releases and add the binary to your PATH.

From source

Requires Rust toolchain.

cargo install --path .

Quick Start

Index a monorepo

shire build --root /path/to/repo

Rebuild from scratch

Ignore cached hashes and re-parse everything:

shire build --root /path/to/repo --force

Write the index to a custom location

shire build --root /path/to/repo --db /tmp/my-index.db

Start the MCP server

shire serve

The index is written to .shire/index.db inside the repo root by default. You can override this with --db on the build command or db_path in shire.toml (see Configuration).

Incremental builds

Subsequent builds are incremental — only manifests whose content has changed (by SHA-256 hash) are re-parsed. Source files are also tracked: if source files change without a manifest change, symbols are re-extracted automatically. An mtime pre-check skips SHA-256 computation entirely for packages whose source files haven’t been touched since the last build.

File indexing is also incremental — a file-tree hash detects structural changes, skipping Phase 9 entirely when no files have been added, removed, or resized.

Performance

Symbol extraction and source hashing are parallelized across packages using rayon for multi-core throughput. All database writes use batched multi-row INSERTs within explicit transactions for maximum SQLite throughput. A per-phase timing breakdown is printed to stderr after each build. The server reads from the database in read-only mode.

Configuration

Drop a shire.toml in the repo root to customize behavior:

# Custom database location (default: .shire/index.db)
db_path = "/path/to/custom/index.db"

[discovery]
manifests = ["package.json", "go.mod", "go.work", "Cargo.toml", "pyproject.toml", "pom.xml", "build.gradle", "build.gradle.kts", "settings.gradle", "settings.gradle.kts", "cpanfile", "Gemfile"]
exclude = ["node_modules", "vendor", "dist", ".build", "target", "third_party", ".shire", ".gradle", "build"]

# Skip symbol extraction for specific file types
[symbols]
exclude_extensions = [".proto", ".pl"]

# Override package descriptions
[[packages]]
name = "legacy-auth"
description = "Deprecated auth service — do not add new dependencies"

All fields are optional. Defaults are shown above. The --db CLI flag takes precedence over db_path in config.

Custom package discovery

For codebases where packages aren’t defined by standard manifest files — Go single-module monorepos, repos that use ownership.yml + build files, or any non-standard convention — you can define custom discovery rules:

# Discover Go apps: directories containing both main.go and ownership.yml
[[discovery.custom]]
name = "go-apps"
kind = "go"
requires = ["main.go", "ownership.yml"]
paths = ["services/", "cmd/"]
exclude = ["testdata", "examples"]
max_depth = 3
name_prefix = "go:"

# Discover proto packages: directories containing *.proto and buf.yaml
[[discovery.custom]]
name = "proto-packages"
kind = "proto"
requires = ["*.proto", "buf.yaml"]
paths = ["proto/", "services/"]
max_depth = 4
FieldRequiredDescription
nameyesRule identifier
kindyesPackage kind for symbol extraction (go, proto, npm, etc.)
requiresyesFile patterns that must ALL exist in a directory (supports globs like *.proto)
pathsnoLimit search to specific subtrees (default: repo root)
excludenoRule-specific directory exclusions (on top of global excludes)
max_depthnoMaximum depth to search from each paths entry
name_prefixnoPrefix prepended to directory-derived package name (e.g., go:services/auth)
extensionsnoOverride which file extensions get symbol extraction

Custom discovery runs alongside manifest-based discovery. Directories already found by manifest parsers are skipped. Subdirectories of matched directories are also skipped to prevent nested matches.

MCP Tools

Shire exposes the following tools over the Model Context Protocol:

ToolDescription
search_packagesFull-text search across package names, descriptions, and paths
get_packageExact name lookup for a single package
list_packagesList all packages, optionally filtered by kind
package_dependenciesWhat a package depends on (optionally internal-only)
package_dependentsReverse lookup — what depends on this package
dependency_graphTransitive BFS traversal from a root package
search_symbolsFull-text search across symbol names and signatures
get_package_symbolsList all symbols in a package (functions, classes, types, methods)
get_symbolExact name lookup for a symbol across packages
get_file_symbolsList all symbols defined in a specific file
search_filesFull-text search across file paths, with optional package/extension filter
list_package_filesList all files belonging to a package, with optional extension filter
index_statusWhen the index was built, git commit, package/symbol/file counts, build duration

MCP Prompts

Prompts are pre-built templates for semantic codebase exploration. They compose multiple queries into structured context, giving your AI a map of where concepts live in the codebase.

PromptArgsDescription
explorequerySearch packages, symbols, and files for a concept — returns a structured context map organized by package
explore-packagenameDeep dive into a specific package — metadata, internal deps, dependents, public API surface, file tree
explore-areapathExplore a directory subtree — packages, files, and symbol summaries under a path prefix
onboardRepository overview for onboarding — tech stack, package counts by language, file distribution, index freshness
impact-analysisnameBlast radius analysis — direct dependents, transitive dependents, full dependency chain
understand-dependencyfrom, toTrace the dependency path between two packages

Watch Daemon

shire watch starts a background daemon that auto-rebuilds the index when files change. It uses Unix domain socket IPC with configurable debounce (default 2s).

Start the daemon

Idempotent — safe to call multiple times:

shire watch --root /path/to/repo

Signal a rebuild manually

shire rebuild --root /path/to/repo

Signal a rebuild from a Claude Code hook

Reads JSON from stdin, uses cwd as repo root:

shire rebuild --stdin

Stop the daemon

shire watch --root /path/to/repo --stop

Smart filtering

The watch daemon avoids unnecessary rebuilds:

  • Edit/Write tools — checks file extension relevance and repo boundary
  • Bash commands — filtered against a denylist of known read-only commands (ls, git status, cargo test, etc.) — unknown commands default to rebuild

Claude Code

Add Shire to your project’s .claude/settings.json:

{
  "mcpServers": {
    "shire": {
      "command": "shire",
      "args": ["serve", "--db", "/path/to/repo/.shire/index.db"]
    }
  }
}

Or add globally in ~/.claude/settings.json to use across all projects.

Auto-rebuild hook

To keep the index fresh during a session, add a PostToolUse hook that signals the watch daemon after file-modifying tools:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|NotebookEdit|Bash",
        "hooks": [{ "type": "command", "command": "shire rebuild --stdin" }]
      }
    ]
  }
}

Claude Desktop

Add Shire to your claude_desktop_config.json:

{
  "mcpServers": {
    "shire": {
      "command": "shire",
      "args": ["serve", "--db", "/path/to/repo/.shire/index.db"]
    }
  }
}

Architecture

src/
├── main.rs          # CLI (clap): build, serve, watch, rebuild subcommands
├── config.rs        # shire.toml parsing
├── db/
│   ├── mod.rs       # SQLite schema, open/create
│   └── queries.rs   # FTS search, dependency graph BFS, listing
├── index/
│   ├── mod.rs       # Walk + incremental index orchestrator
│   ├── custom_discovery.rs # Config-driven custom package discovery
│   ├── manifest.rs  # ManifestParser trait
│   ├── hash.rs      # SHA-256 content hashing for incremental builds
│   ├── npm.rs       # package.json parser (workspace: protocol)
│   ├── go.rs        # go.mod parser
│   ├── go_work.rs   # go.work parser (workspace use directives)
│   ├── cargo.rs     # Cargo.toml parser (workspace dep resolution)
│   ├── python.rs    # pyproject.toml parser
│   ├── maven.rs     # pom.xml parser (parent POM inheritance)
│   ├── gradle.rs    # build.gradle / build.gradle.kts parser
│   ├── gradle_settings.rs # settings.gradle parser (project inclusion)
│   ├── perl.rs      # cpanfile parser (requires, on 'test')
│   └── ruby.rs      # Gemfile parser (gem, group blocks)
├── symbols/
│   ├── mod.rs       # Symbol types, kind-agnostic extraction orchestrator
│   ├── walker.rs    # Source file discovery (extension filtering, excludes)
│   ├── typescript.rs # TS/JS extractor (tree-sitter)
│   ├── go.rs        # Go extractor (tree-sitter)
│   ├── rust_lang.rs # Rust extractor (tree-sitter)
│   ├── python.rs    # Python extractor (tree-sitter)
│   ├── proto.rs     # Protobuf extractor (tree-sitter)
│   ├── java.rs      # Java extractor (tree-sitter)
│   ├── kotlin.rs    # Kotlin extractor (tree-sitter)
│   ├── perl.rs      # Perl extractor (regex-based)
│   └── ruby.rs      # Ruby extractor (tree-sitter)
├── mcp/
│   ├── mod.rs       # MCP server setup (rmcp, stdio transport)
│   ├── tools.rs     # 13 tool handlers
│   └── prompts.rs   # 6 prompt templates for semantic codebase exploration
└── watch/
    ├── mod.rs       # Daemon event loop (UDS listener, debounce, rebuild)
    ├── daemon.rs    # Process management (start/stop/is_running via PID)
    └── protocol.rs  # Hook input parsing, Bash read-only denylist

Supported Ecosystems

ManifestKindWorkspace support
package.jsonnpmworkspace: protocol versions normalized
go.modgogo.work member metadata
go.workgouse directives parsed for workspace context
Cargo.tomlcargoworkspace = true deps resolved from root
pyproject.tomlpython
pom.xmlmavenParent POM inheritance (groupId, version)
build.gradle / build.gradle.ktsgradlesettings.gradle project inclusion
cpanfileperlrequires / on 'test' blocks
Gemfilerubygem / group :test blocks