Showing preview only (746K chars total). Download the full file or copy to clipboard to get everything.
Repository: wrale/mcp-server-tree-sitter
Branch: main
Commit: 6c8992eadbda
Files: 126
Total size: 706.1 KB
Directory structure:
gitextract_2grt6c54/
├── .codestateignore
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── release.yml
├── .gitignore
├── .python-version
├── AGENTS.md
├── CONTRIBUTING.md
├── FEATURES.md
├── LICENSE
├── Makefile
├── NOTICE
├── README.md
├── ROADMAP.md
├── TODO.md
├── docs/
│ ├── architecture.md
│ ├── cli.md
│ ├── config.md
│ ├── diagnostics.md
│ ├── logging.md
│ ├── requirements/
│ │ └── logging.md
│ └── tree-sitter-type-safety.md
├── pyproject.toml
├── scripts/
│ └── implementation-search.sh
├── src/
│ └── mcp_server_tree_sitter/
│ ├── __init__.py
│ ├── __main__.py
│ ├── api.py
│ ├── bootstrap/
│ │ ├── __init__.py
│ │ └── logging_bootstrap.py
│ ├── cache/
│ │ ├── __init__.py
│ │ └── parser_cache.py
│ ├── capabilities/
│ │ ├── __init__.py
│ │ └── server_capabilities.py
│ ├── config.py
│ ├── context.py
│ ├── di.py
│ ├── exceptions.py
│ ├── language/
│ │ ├── __init__.py
│ │ ├── query_templates.py
│ │ ├── registry.py
│ │ └── templates/
│ │ ├── __init__.py
│ │ ├── apl.py
│ │ ├── c.py
│ │ ├── cpp.py
│ │ ├── dart.py
│ │ ├── go.py
│ │ ├── java.py
│ │ ├── javascript.py
│ │ ├── julia.py
│ │ ├── kotlin.py
│ │ ├── python.py
│ │ ├── rust.py
│ │ ├── swift.py
│ │ └── typescript.py
│ ├── logging_config.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── ast.py
│ │ ├── ast_cursor.py
│ │ └── project.py
│ ├── prompts/
│ │ ├── __init__.py
│ │ └── code_patterns.py
│ ├── server.py
│ ├── testing/
│ │ ├── __init__.py
│ │ └── pytest_diagnostic.py
│ ├── tools/
│ │ ├── __init__.py
│ │ ├── analysis.py
│ │ ├── ast_operations.py
│ │ ├── debug.py
│ │ ├── file_operations.py
│ │ ├── project.py
│ │ ├── query_builder.py
│ │ ├── registration.py
│ │ └── search.py
│ └── utils/
│ ├── __init__.py
│ ├── context/
│ │ ├── __init__.py
│ │ └── mcp_context.py
│ ├── file_io.py
│ ├── path.py
│ ├── security.py
│ ├── tree_sitter_helpers.py
│ └── tree_sitter_types.py
└── tests/
├── .gitignore
├── __init__.py
├── conftest.py
├── test_ast_cursor.py
├── test_basic.py
├── test_cache_config.py
├── test_cli_arguments.py
├── test_config_behavior.py
├── test_config_manager.py
├── test_context.py
├── test_debug_flag.py
├── test_di.py
├── test_diagnostics/
│ ├── __init__.py
│ ├── test_ast.py
│ ├── test_ast_parsing.py
│ ├── test_cursor_ast.py
│ ├── test_language_pack.py
│ ├── test_language_registry.py
│ └── test_unpacking_errors.py
├── test_env_config.py
├── test_failure_modes.py
├── test_file_operations.py
├── test_find_similar_code.py
├── test_helpers.py
├── test_language_listing.py
├── test_logging_bootstrap.py
├── test_logging_config.py
├── test_logging_config_di.py
├── test_logging_early_init.py
├── test_logging_env_vars.py
├── test_logging_handlers.py
├── test_makefile_targets.py
├── test_mcp_context.py
├── test_models_ast.py
├── test_persistent_server.py
├── test_project_persistence.py
├── test_query_result_handling.py
├── test_registration.py
├── test_rust_compatibility.py
├── test_server.py
├── test_server_capabilities.py
├── test_smoke.py
├── test_symbol_extraction.py
├── test_tree_sitter_helpers.py
├── test_yaml_config.py
└── test_yaml_config_di.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .codestateignore
================================================
uv.lock
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.12"]
install-method: ["uv", "uvx"]
steps:
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install dependencies with uv
if: matrix.install-method == 'uv'
run: |
uv venv
source .venv/bin/activate
uv pip install -e ".[dev]"
which ruff
which python
- name: Install globally with uvx (system-wide)
if: matrix.install-method == 'uvx'
run: |
python -m pip install -e ".[dev]"
which ruff
which python
- name: Run checks and tests (uv)
if: matrix.install-method == 'uv'
run: |
source .venv/bin/activate
# Linting and formatting
ruff check .
ruff format . --check
mypy src/mcp_server_tree_sitter
# Run all tests including diagnostics
pytest tests
pytest tests/test_diagnostics/ -v
env:
PYTHONPATH: ${{ github.workspace }}/src
- name: Run checks and tests (system)
if: matrix.install-method == 'uvx'
run: |
# Linting and formatting
ruff check .
ruff format . --check
mypy src/mcp_server_tree_sitter
# Run all tests including diagnostics
pytest tests
pytest tests/test_diagnostics/ -v
env:
PYTHONPATH: ${{ github.workspace }}/src
- name: Ensure diagnostic results directory exists
if: always()
run: mkdir -p diagnostic_results
- name: Create placeholder if needed
if: always()
run: |
if [ -z "$(ls -A diagnostic_results 2>/dev/null)" ]; then
echo '{"info": "No diagnostic results generated"}' > diagnostic_results/placeholder.json
fi
- name: Archive diagnostic results
if: always()
uses: actions/upload-artifact@v6
with:
name: diagnostic-results-${{ matrix.install-method }}
path: diagnostic_results/
retention-days: 7
if-no-files-found: warn
verify-uvx:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v6
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install build dependencies
run: |
python -m pip install build
python -m pip install uv
- name: Build package
run: python -m build
- name: Install and verify
run: |
python -m pip install dist/*.whl
mcp-server-tree-sitter --help
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
release:
types: [published]
permissions:
contents: read
id-token: write
jobs:
release:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install development dependencies
run: |
uv venv
source .venv/bin/activate
uv pip install -e ".[dev]"
- name: Run comprehensive tests
run: |
source .venv/bin/activate
# Run linting and formatting
ruff check .
ruff format . --check
mypy src/mcp_server_tree_sitter
# Run all tests (regular + diagnostics)
pytest tests
pytest tests/test_diagnostics/ -v
env:
PYTHONPATH: ${{ github.workspace }}/src
- name: Ensure diagnostic results directory exists
if: always()
run: mkdir -p diagnostic_results
- name: Create placeholder if needed
if: always()
run: |
if [ -z "$(ls -A diagnostic_results 2>/dev/null)" ]; then
echo '{"info": "No diagnostic results generated"}' > diagnostic_results/placeholder.json
fi
- name: Archive diagnostic results
if: always()
uses: actions/upload-artifact@v4
with:
name: diagnostic-results-release
path: diagnostic_results/
retention-days: 7
if-no-files-found: warn
- name: Install build dependencies
run: |
source .venv/bin/activate
uv pip install build twine
- name: Build package
run: |
source .venv/bin/activate
python -m build
- name: Test wheel
run: |
python -m pip install dist/*.whl
mcp-server-tree-sitter --help
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
# etc.
results/
diagnostic_results/
*.json
================================================
FILE: .python-version
================================================
3.12
================================================
FILE: AGENTS.md
================================================
# AGENTS.md
Instructions for AI coding agents working in this repository.
## Project Overview
MCP Tree-sitter Server — a Model Context Protocol server providing code analysis via tree-sitter. Python 3.10+, packaged with hatchling, managed with uv.
## Setup
```bash
uv venv --python 3.12
uv pip install -e ".[dev]"
```
## Before Committing
All of these must pass — CI enforces them:
```bash
ruff check src/ # Lint (E, F, I, W, B rules)
ruff format --check src/ # Format check
mypy src/mcp_server_tree_sitter # Type check
pytest tests/ # 217+ tests, must all pass
```
Or use the Makefile: `make prepare`
## Key Architecture
- **Source:** `src/mcp_server_tree_sitter/`
- **DI container:** `di.py` — constructs all dependencies; avoid circular imports with it
- **Config:** `config.py` — `ConfigurationManager` auto-loads YAML from `MCP_TS_CONFIG_PATH` or `~/.config/tree-sitter/config.yaml`. Precedence: env vars > YAML > defaults
- **Language registry:** `language/registry.py` — maps file extensions to tree-sitter-language-pack identifiers
- **Templates:** `language/templates/` — per-language query templates (one file per language)
- **Tools:** `tools/` — MCP tool implementations (analysis, search, ast_operations, file_operations)
- **Helpers:** `utils/tree_sitter_helpers.py` — includes `query_captures()` compat wrapper for tree-sitter >= 0.24
## tree-sitter API Compatibility
tree-sitter >= 0.24 removed `Query.captures()`. Always use the `query_captures(query, node)` wrapper from `utils/tree_sitter_helpers.py` instead of calling `query.captures()` directly. This applies to both source and test code.
## Adding a New Language
1. Create `language/templates/<lang>.py` with a `TEMPLATES` dict (follow existing patterns like `go.py`)
2. Register the file extension in `language/registry.py` `_language_map`
3. Import and register in `language/templates/__init__.py`
4. Add default symbol types in `tools/analysis.py` `extract_symbols()`
5. Verify the language identifier works: `from tree_sitter_language_pack import get_language; get_language("<id>")`
## Common Pitfalls
- **Circular imports with `di.py`:** The DI container constructs registries. Don't import `get_container` from `__init__` methods of objects the container creates. Use method injection instead.
- **Root logger:** Do NOT call `configure_root_logger()` at module import time. Libraries must not reconfigure the root logger.
- **`common_languages` list:** Uses tree-sitter-language-pack identifiers (e.g., `csharp` not `c_sharp`). Verify with `get_language()` before adding.
- **TypeScript grammar:** Import statements require the `import_clause` node between `import_statement` and `named_imports`/`namespace_import`.
- **Test isolation:** Some tests are order-dependent due to shared singleton state. If a test passes alone but fails in suite, check for state leakage.
## PR Process
- All PRs must pass CI (ruff check, ruff format, mypy, pytest)
- Squash merge to main
- Credit community contributors in commit messages and PR descriptions
- For dependency bumps, pin transitive deps with security floors in `pyproject.toml`
## Release Process
1. Bump version in `pyproject.toml`
2. Update README if features/languages changed
3. Merge to main, confirm CI green
4. Create GitHub release with tag `vX.Y.Z` — this triggers the release workflow which publishes to PyPI
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to MCP Tree-sitter Server
Thank you for your interest in contributing to MCP Tree-sitter Server! This guide will help you understand our development process and coding standards.
## Development Setup
1. Clone the repository:
```bash
git clone https://github.com/organization/mcp-server-tree-sitter.git
cd mcp-server-tree-sitter
```
2. Install with development dependencies:
```bash
make install-dev
```
3. Install language parsers (optional):
```bash
make install-languages
```
## Code Style and Standards
We follow a strict set of coding standards to maintain consistency throughout the codebase:
### Python Style
- We use [Black](https://black.readthedocs.io/) for code formatting with a line length of 88 characters
- We use [Ruff](https://github.com/charliermarsh/ruff) for linting
- We use [MyPy](https://mypy.readthedocs.io/) for static type checking
### Exception Handling
- Use specific exception types rather than catching generic exceptions when possible
- When re-raising exceptions, use the `from` clause to preserve the stack trace:
```python
try:
# Some code
except SomeError as e:
raise CustomError("Meaningful message") from e
```
### Testing
- Write tests for all new functionality
- Run tests before submitting:
```bash
make test
```
### Documentation
- Document all functions, classes, and modules using docstrings
- Follow the Google Python Style Guide for docstrings
- Include type hints for all function parameters and return values
## Development Workflow
1. Create a branch for your feature or bugfix:
```bash
git checkout -b feature/your-feature-name
```
2. Make your changes and ensure they pass linting and tests:
```bash
make format
make lint
make test
```
3. Commit your changes with a clear message describing the change
4. Submit a pull request to the main repository
## Running the Server
You can run the server in different modes:
- For development and testing:
```bash
make mcp-dev
```
- For direct execution:
```bash
make mcp-run
```
- To install in Claude Desktop:
```bash
make mcp-install
```
## Project Architecture
The project follows a modular architecture:
- `config.py` - Configuration management
- `language/` - Tree-sitter language handling
- `models/` - Data models for AST and projects
- `cache/` - Caching mechanisms
- `resources/` - MCP resources (files, AST)
- `tools/` - MCP tools (search, analysis, etc.)
- `utils/` - Utility functions
- `prompts/` - MCP prompts
- `server.py` - FastMCP server implementation
## Seeking Help
If you have questions or need help, please open an issue or contact the maintainers.
================================================
FILE: FEATURES.md
================================================
# MCP Tree-sitter Server: Feature Matrix
This document provides a comprehensive overview of all MCP Tree-sitter server commands, their status, dependencies, and common usage patterns. It serves as both a reference guide and a test matrix for ongoing development.
## Table of Contents
- [Supported Languages](#supported-languages)
- [Command Status Legend](#command-status-legend)
- [Command Reference](#command-reference)
- [Project Management Commands](#project-management-commands)
- [Language Tools Commands](#language-tools-commands)
- [File Operations Commands](#file-operations-commands)
- [AST Analysis Commands](#ast-analysis-commands)
- [Search and Query Commands](#search-and-query-commands)
- [Code Analysis Commands](#code-analysis-commands)
- [Cache Management Commands](#cache-management-commands)
- [Implementation Status](#implementation-status)
- [Language Pack Integration](#language-pack-integration)
- [Implementation Gaps](#implementation-gaps)
- [MCP SDK Implementation](#mcp-sdk-implementation)
- [Implementation Notes](#implementation-notes)
- [Testing Guidelines](#testing-guidelines)
- [Implementation Progress](#implementation-progress)
---
## Supported Languages
The following programming languages are fully supported with symbol extraction, AST analysis, and query capabilities:
| Language | Symbol Extraction | AST Analysis | Query Support |
|----------|-------------------|--------------|--------------|
| Python | ✅ | ✅ | ✅ |
| JavaScript | ✅ | ✅ | ✅ |
| TypeScript | ✅ | ✅ | ✅ |
| Go | ✅ | ✅ | ✅ |
| Rust | ✅ | ✅ | ✅ |
| C | ✅ | ✅ | ✅ |
| C++ | ✅ | ✅ | ✅ |
| Swift | ✅ | ✅ | ✅ |
| Java | ✅ | ✅ | ✅ |
| Kotlin | ✅ | ✅ | ✅ |
| Julia | ✅ | ✅ | ✅ |
| APL | ✅ | ✅ | ✅ |
Additional languages are available via tree-sitter-language-pack, including Bash, C#, Clojure, Elixir, Elm, Haskell, Lua, Objective-C, OCaml, PHP, Protobuf, Ruby, Scala, SCSS, SQL, and XML.
---
## Command Status Legend
| Status | Meaning |
|--------|---------|
| ✅ | Working - Feature is fully operational |
| ⚠️ | Partially Working - Feature works with limitations or in specific conditions |
| ❌ | Not Working - Feature fails or is unavailable |
| 🔄 | Requires Dependency - Needs external components (e.g., language parsers) |
---
## Command Reference
### Project Management Commands
These commands handle project registration and management.
| Command | Status | Dependencies | Notes |
|---------|--------|--------------|-------|
| `register_project_tool` | ✅ | None | Successfully registers projects with path, name, and description |
| `list_projects_tool` | ✅ | None | Successfully lists all registered projects |
| `remove_project_tool` | ✅ | None | Successfully removes registered projects |
**Example Usage:**
```python
# Register a project
register_project_tool(path="/path/to/project", name="my-project", description="My awesome project")
# List all projects
list_projects_tool()
# Remove a project
remove_project_tool(name="my-project")
```
### Language Tools Commands
These commands manage tree-sitter language parsers.
| Command | Status | Dependencies | Notes |
|---------|--------|--------------|-------|
| `list_languages` | ✅ | None | Lists all available languages from tree-sitter-language-pack |
| `check_language_available` | ✅ | None | Checks if a specific language is available via tree-sitter-language-pack |
**Example Usage:**
```python
# List all available languages
list_languages()
# Check if a specific language is available
check_language_available(language="python")
```
### File Operations Commands
These commands access and manipulate project files.
| Command | Status | Dependencies | Notes |
|---------|--------|--------------|-------|
| `list_files` | ✅ | Project registration | Successfully lists files with optional filtering |
| `get_file` | ✅ | Project registration | Successfully retrieves file content |
| `get_file_metadata` | ✅ | Project registration | Returns file information including size, modification time, etc. |
**Example Usage:**
```python
# List Python files
list_files(project="my-project", pattern="**/*.py")
# Get file content
get_file(project="my-project", path="src/main.py")
# Get file metadata
get_file_metadata(project="my-project", path="src/main.py")
```
### AST Analysis Commands
These commands perform abstract syntax tree (AST) operations.
| Command | Status | Dependencies | Notes |
|---------|--------|--------------|-------|
| `get_ast` | ✅ | Project registration | Returns AST using efficient cursor-based traversal with proper node IDs |
| `get_node_at_position` | ✅ | Project registration | Successfully retrieves nodes at a specific position in a file |
**Example Usage:**
```python
# Get AST for a file
get_ast(project="my-project", path="src/main.py", max_depth=5, include_text=True)
# Find node at position
get_node_at_position(project="my-project", path="src/main.py", row=10, column=5)
```
### Search and Query Commands
These commands search code and execute tree-sitter queries.
| Command | Status | Dependencies | Notes |
|---------|--------|--------------|-------|
| `find_text` | ✅ | Project registration | Text search works correctly with pattern matching |
| `run_query` | ✅ | Project registration, Language | Successfully executes tree-sitter queries and returns results |
| `get_query_template_tool` | ✅ | None | Successfully returns templates when available |
| `list_query_templates_tool` | ✅ | None | Successfully lists available templates |
| `build_query` | ✅ | None | Successfully builds and combines query templates |
| `adapt_query` | ✅ | None | Successfully adapts queries between different languages |
| `get_node_types` | ✅ | None | Successfully returns descriptions of node types for a language |
**Example Usage:**
```python
# Find text in project files
find_text(project="my-project", pattern="TODO", file_pattern="**/*.py")
# Run a tree-sitter query
run_query(
project="my-project",
query="(function_definition name: (identifier) @function.name) @function.def",
file_path="src/main.py",
language="python"
)
# List query templates for a language
list_query_templates_tool(language="python")
# Get descriptions of node types
get_node_types(language="python")
```
### Code Analysis Commands
These commands analyze code structure and complexity.
| Command | Status | Dependencies | Notes |
|---------|--------|--------------|-------|
| `get_symbols` | ✅ | Project registration | Successfully extracts symbols (functions, classes, imports) from files |
| `analyze_project` | ✅ | Project registration | Project structure analysis works with support for detailed code analysis |
| `get_dependencies` | ✅ | Project registration | Successfully identifies dependencies from import statements |
| `analyze_complexity` | ✅ | Project registration | Provides accurate code complexity metrics |
| `find_similar_code` | ⚠️ | Project registration | Execution successful but no results returned in testing |
| `find_usage` | ✅ | Project registration | Successfully finds usage of symbols across project files |
**Example Usage:**
```python
# Extract symbols from a file
get_symbols(project="my-project", file_path="src/main.py")
# Analyze project structure
analyze_project(project="my-project", scan_depth=3)
# Get dependencies for a file
get_dependencies(project="my-project", file_path="src/main.py")
# Analyze code complexity
analyze_complexity(project="my-project", file_path="src/main.py")
# Find similar code
find_similar_code(
project="my-project",
snippet="print('Hello, world!')",
language="python"
)
# Find symbol usage
find_usage(project="my-project", symbol="main", language="python")
```
### Configuration Management Commands
These commands manage the service and its parse tree cache.
| Command | Status | Dependencies | Notes |
|---------|--------|--------------|-------|
| `clear_cache` | ✅ | None | Successfully clears caches at all levels (global, project, or file) |
| `configure` | ✅ | None | Successfully configures cache, log level, and other settings |
| `diagnose_config` | ✅ | None | Diagnoses issues with YAML configuration loading |
**Example Usage:**
```python
# Clear all caches
clear_cache()
# Clear cache for a specific project
clear_cache(project="my-project")
# Configure cache settings
configure(cache_enabled=True, max_file_size_mb=10, log_level="DEBUG")
# Diagnose configuration issues
diagnose_config(config_path="/path/to/config.yaml")
```
---
## Implementation Status
### Language Pack Integration
The integration of tree-sitter-language-pack is complete with comprehensive language support. All 31 languages are available and functional.
| Feature Area | Status | Test Results |
|--------------|--------|--------------|
| Language Tools | ✅ Working | All tests pass. Language tools properly report and list available languages |
| AST Analysis | ✅ Working | All tests pass. `get_ast` and `get_node_at_position` work correctly with proper node IDs and AST traversal operations |
| Search Queries | ✅ Working | All tests pass. Text search works, query building works, and tree-sitter query execution returns expected results |
| Code Analysis | ✅ Working | All tests pass. Structure and complexity analysis works, symbol extraction and dependency analysis provide useful results |
**Current Integration Capabilities:**
- AST functionality works well for retrieving and traversing trees and nodes
- Query execution and result handling work correctly
- Symbol extraction and dependency analysis provide useful results
- Project management, file operations, and search features work correctly
### Implementation Gaps
Based on the latest tests as of March 18, 2025, these are the current implementation gaps:
#### Tree Editing and Incremental Parsing
- **Status:** ⚠️ Partially Working
- Core AST functionality works
- Tree manipulation functionality requires additional implementation
#### Tree Cursor API
- **Status:** ✅ Fully Working
- AST node traversal works correctly
- Cursor-based tree walking is efficient and reliable
- Can be extended for more advanced semantic analysis
#### Similar Code Detection
- **Status:** ⚠️ Partially Working
- Command executes successfully but testing did not yield results
- May require more specific snippets or fine-tuning of similarity thresholds
#### UTF-16 Support
- **Status:** ❌ Not Implemented
- Encoding detection and support is not yet available
- Will require parser improvements after core AST functionality is fixed
#### Read Callable Support
- **Status:** ❌ Not Implemented
- Custom read strategies are not yet available
- Streaming parsing for large files remains unavailable
### MCP SDK Implementation
| Feature | Status | Notes |
|---------|--------|-------|
| Application Lifecycle Management | ✅ Working | Basic lifespan support is functioning correctly |
| Image Handling | ❌ Not Implemented | No support for returning images from tools |
| MCP Context Handling | ⚠️ Partial | Basic context access works, but progress reporting not fully implemented |
| Claude Desktop Integration | ✅ Working | MCP server can be installed in Claude Desktop |
| Server Capabilities Declaration | ✅ Working | Capabilities are properly declared |
---
## Implementation Notes
This project uses a structured dependency injection (DI) pattern, but still has global singletons at its core:
1. A central `DependencyContainer` singleton that holds all shared services
2. A `global_context` object that provides a convenient interface to the container
3. API functions that access the container internally
This architecture provides three main ways to access functionality:
```python
# Option 1: API Functions (preferred for most use cases)
from mcp_server_tree_sitter.api import get_config, get_language_registry
config = get_config()
languages = get_language_registry().list_available_languages()
# Option 2: Direct Container Access
from mcp_server_tree_sitter.di import get_container
container = get_container()
project_registry = container.project_registry
tree_cache = container.tree_cache
# Option 3: Global Context
from mcp_server_tree_sitter.context import global_context
config = global_context.get_config()
result = global_context.register_project("/path/to/project")
```
The dependency injection approach helps make the code more testable and maintainable, even though it still uses singletons internally.
---
## Testing Guidelines
When testing the MCP Tree-sitter server, use this structured approach:
1. **Project Setup**
- Register a project with `register_project_tool`
- Verify registration with `list_projects_tool`
2. **Basic File Operations**
- Test `list_files` to ensure project access
- Test `get_file` to verify content retrieval
- Test `get_file_metadata` to check file information
3. **Language Parser Verification**
- Test `check_language_available` to verify specific language support
- Use `list_languages` to see all available languages
4. **Feature Testing**
- Test AST operations with `get_ast` to ensure proper node IDs and structure
- Test query execution with `run_query` to verify proper result capture
- Test symbol extraction with `get_symbols` to verify proper function, class, and import detection
- Test dependency analysis with `get_dependencies` to verify proper import detection
- Test complexity analysis with `analyze_complexity` to verify metrics are being calculated correctly
- Test usage finding with `find_usage` to verify proper symbol reference detection
5. **Test Outcomes**
- All 185 tests now pass successfully
- No diagnostic errors reported
- Core functionality works reliably across all test cases
---
## Implementation Progress
Based on the test results as of March 18, 2025, all critical functionality is now working:
1. **✅ Tree-Sitter Query Result Handling**
- Query result handling works correctly
- Queries execute and return proper results with correct capture processing
2. **✅ Tree Cursor Functionality**
- Tree cursor-based traversal is working correctly
- Efficient navigation and analysis of ASTs is now possible
3. **✅ AST Node ID Generation**
- AST nodes are correctly assigned unique IDs
- Node traversal and reference works reliably
4. **✅ Symbol Extraction**
- Symbol extraction correctly identifies functions, classes, and imports
- Location information is accurate
5. **✅ Dependency Analysis**
- Dependency analysis correctly identifies imports and references
- Properly handles different import styles
6. **✅ Code Complexity Analysis**
- Complexity metrics are calculated correctly
- Line counts, cyclomatic complexity, and other metrics are accurate
7. **⚠️ Similar Code Detection**
- Command completes execution but testing did not yield results
- May need further investigation with more appropriate test cases
8. **Future Work: Complete MCP Context Progress Reporting**
- Add progress reporting for long-running operations to improve user experience
---
This feature matrix reflects test results as of March 18, 2025. All core functionality is now working correctly, with only minor issues in similar code detection. The project is fully operational with all 185 tests passing successfully.
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2025 Wrale
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
# Makefile for mcp-server-tree-sitter
# Uses uv as the package manager
# Package information
PACKAGE := mcp_server_tree_sitter
PACKAGE_PATH := src/$(PACKAGE)
# Environment variables
PYTHONPATH ?= $(shell pwd)/src
export PYTHONPATH
# Installation method (uv or uvx)
INSTALL_METHOD ?= uv
# uv commands
UV := uv
# Default target
.PHONY: all help
help: show-help
all: install
# Installation targets
.PHONY: install
install:
$(UV) pip install -e .
.PHONY: install-dev
install-dev:
$(UV) pip install -e ".[dev]"
.PHONY: install-all
install-all:
$(UV) pip install -e ".[dev]"
.PHONY: install-global
install-global:
python -m pip install -e ".[dev]"
# Pre-commit preparation
.PHONY: prepare
prepare: clean format lint test-ci ensure-diagnostic-dir verify
# CI-like test target that better simulates CI environment
.PHONY: test-ci
test-ci:
# Use CI=true to help tests detect when they're in a CI-like environment
CI=true $(MAKE) test-with-args
CI=true $(UV) run pytest tests/test_diagnostics/ -v
# Testing targets
.PHONY: test
test:
# Regular test target
$(UV) run pytest
# Run tests with explicit cli args to catch arg parsing conflicts
.PHONY: test-with-args
test-with-args:
$(UV) run pytest tests -- tests
.PHONY: test-diagnostics
test-diagnostics: ensure-diagnostic-dir
$(UV) run pytest tests/test_diagnostics/ -v
.PHONY: test-diagnostics-ci
test-diagnostics-ci: ensure-diagnostic-dir
$(UV) run pytest tests/test_diagnostics/ -v || echo "Diagnostic tests completed with issues - see diagnostic_results directory"
.PHONY: test-coverage
test-coverage:
$(UV) run pytest --cov=$(PACKAGE) --cov-report=term --cov-report=html
# Matrix testing support
.PHONY: test-matrix
test-matrix:
@echo "Running tests with $(INSTALL_METHOD) installation method"
ifeq ($(INSTALL_METHOD),uv)
$(MAKE) install-dev
$(MAKE) test-all
else ifeq ($(INSTALL_METHOD),uvx)
$(MAKE) install-global
$(MAKE) test-all
else
@echo "Unknown installation method: $(INSTALL_METHOD)"
@echo "Supported methods: uv, uvx"
@exit 1
endif
# Unified test target
.PHONY: test-all
test-all: test test-diagnostics
# Verification targets
.PHONY: verify
verify: build verify-wheel
.PHONY: verify-wheel
verify-wheel:
@echo "Verifying the built wheel..."
@echo "Creating temporary virtual environment for verification..."
rm -rf .verify_venv 2>/dev/null || true
$(shell command -v python3 || command -v python) -m venv .verify_venv
.verify_venv/bin/pip install dist/*.whl
.verify_venv/bin/mcp-server-tree-sitter --help || true
rm -rf .verify_venv
.PHONY: verify-global
verify-global: build
@echo "Verifying global installation..."
@echo "Creating temporary virtual environment for verification..."
rm -rf .verify_venv 2>/dev/null || true
$(shell command -v python3 || command -v python) -m venv .verify_venv
.verify_venv/bin/pip install dist/*.whl
.verify_venv/bin/mcp-server-tree-sitter --help || true
rm -rf .verify_venv
# Linting and formatting targets
.PHONY: lint
lint:
$(UV) run mypy .
$(UV) run ruff check .
.PHONY: mypy
mypy:
$(UV) run mypy .
.PHONY: format
format:
$(UV) run ruff format .
$(UV) run ruff check --fix .
# Cleaning targets
.PHONY: clean
clean:
rm -rf build/ dist/ *.egg-info/ .pytest_cache/ htmlcov/ .coverage .ruff_cache diagnostic_results .verify_venv
# Use rmdir with -p to handle non-empty directories more gracefully
find .mypy_cache -type d -exec rmdir -p {} \; 2>/dev/null || true
rm -rf .mypy_cache 2>/dev/null || true
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
rm -f tests/issue_tests/*.json 2>/dev/null || true
rm -f tests/issue_tests/results/*.json 2>/dev/null || true
# Diagnostic directory handling
.PHONY: ensure-diagnostic-dir
ensure-diagnostic-dir:
@mkdir -p diagnostic_results
@if [ -z "$$(ls -A diagnostic_results 2>/dev/null)" ]; then \
echo '{"info": "No diagnostic results generated"}' > diagnostic_results/placeholder.json; \
fi
# Building and packaging
.PHONY: build
build:
$(UV) run python -m build
# Release targets
.PHONY: pre-release
pre-release: clean lint test-all build verify
.PHONY: release-local
release-local: pre-release
@echo "Local release process completed. Run 'make publish' to publish to PyPI."
@echo "NOTE: Publishing to PyPI requires proper credentials and should be done via CI."
.PHONY: publish
publish:
@echo "This target would publish to PyPI, but is intended to be run via CI."
@echo "For manual publishing, use: python -m twine upload dist/*"
# CI integration
.PHONY: ci
ci: clean install-dev lint test-all build verify
# Run the server
# ARGS can be passed like: make run ARGS="--help"
.PHONY: run
run:
$(UV) run python -m $(PACKAGE) $(ARGS)
# MCP specific targets
# ARGS can be passed like: make mcp-dev ARGS="--help"
.PHONY: mcp-dev
mcp-dev:
$(UV) run mcp dev $(PACKAGE).server $(ARGS)
.PHONY: mcp-run
mcp-run:
$(UV) run mcp run $(PACKAGE).server $(ARGS)
.PHONY: mcp-install
mcp-install:
$(UV) run mcp install $(PACKAGE).server:mcp --name "tree_sitter" $(ARGS)
# Help target
.PHONY: show-help
show-help:
@echo "Available targets:"
@echo " help : Show this help message (default target)"
@echo " all : Install the package"
@echo " install : Install the package"
@echo " install-dev : Install the package with development dependencies"
@echo " install-all : Install with all dependencies"
@echo " install-global : Install the package globally (system-wide)"
@echo " prepare : Run pre-commit checks (format, lint, test, verify)"
@echo " test : Run normal tests"
@echo " test-with-args : Run tests with extra arguments to catch CLI parsing issues"
@echo " test-ci : Run tests in a CI-like environment (catches more issues)"
@echo " test-diagnostics : Run pytest-based diagnostic tests"
@echo " test-diagnostics-ci : Run diagnostic tests in CI mode (won't fail the build)"
@echo " test-coverage : Run tests with coverage report"
@echo " test-matrix : Run tests with different installation methods (set INSTALL_METHOD=uv|uvx)"
@echo " test-all : Run both normal tests and diagnostic tests"
@echo " verify : Verify the built package works correctly"
@echo " verify-wheel : Verify the built wheel by installing and running a basic check"
@echo " verify-global : Verify global installation (similar to CI verify-uvx job)"
@echo " clean : Clean build artifacts and test results"
@echo " ensure-diagnostic-dir : Create diagnostic results directory if it doesn't exist"
@echo " lint : Run linting checks"
@echo " format : Format code using ruff"
@echo " build : Build distribution packages"
@echo " pre-release : Run all pre-release checks (clean, lint, test, build, verify)"
@echo " release-local : Perform a complete local release process"
@echo " publish : Placeholder for publishing to PyPI (intended for CI use)"
@echo " ci : Run the CI workflow steps locally"
@echo " run : Run the server directly (use ARGS=\"--help\" to pass arguments)"
@echo " mcp-dev : Run the server with MCP Inspector (use ARGS=\"--help\" to pass arguments)"
@echo " mcp-run : Run the server with MCP (use ARGS=\"--help\" to pass arguments)"
@echo " mcp-install : Install the server in Claude Desktop"
================================================
FILE: NOTICE
================================================
MCP Tree-sitter Server
Copyright (c) 2025 Wrale
Licensed under the MIT License (see LICENSE file)
This software includes or depends upon the following third-party components:
--------------------------------------------------
tree-sitter
--------------------------------------------------
https://github.com/tree-sitter/tree-sitter
Copyright (c) 2018-2024 Max Brunsfeld
Licensed under the MIT License
--------------------------------------------------
tree-sitter-language-pack
--------------------------------------------------
https://github.com/Goldziher/tree-sitter-language-pack
Dual licensed:
1. MIT License
Copyright (c) 2024-2025 Na'aman Hirschfeld
2. Apache License 2.0
Copyright (c) 2022 Grant Jenks
As a fork of tree-sitter-languages
tree-sitter-language-pack bundles numerous tree-sitter language parsers,
each with their own licenses (all permissive: MIT, Apache 2.0, etc.).
See the tree-sitter-language-pack repository for details on individual language parsers.
--------------------------------------------------
Python Dependencies
--------------------------------------------------
- mcp: Model Context Protocol implementation
- pydantic: Data validation library
- pyyaml: YAML parsing library
All Python dependencies are used in accordance with their respective licenses.
--------------------------------------------------
Note on Language Grammars
--------------------------------------------------
When using tree-sitter-language-pack, this project indirectly incorporates
numerous tree-sitter language grammars. As noted in tree-sitter-language-pack's
documentation, all bundled grammars are under permissive open-source licenses
(MIT, Apache 2.0, etc.) and no GPL-licensed grammars are included.
For a complete list of included grammars and their specific licenses, please refer to:
https://github.com/Goldziher/tree-sitter-language-pack#available-languages
================================================
FILE: README.md
================================================
# MCP Tree-sitter Server
A Model Context Protocol (MCP) server that provides code analysis capabilities using tree-sitter, designed to give AI assistants intelligent access to codebases with appropriate context management. Claude Desktop is the reference implementation target.
## Features
- 🔍 **Flexible Exploration**: Examine code at multiple levels of granularity
- 🧠 **Context Management**: Provides just enough information without overwhelming the context window
- 🌐 **Language Agnostic**: Supports many programming languages including Python, JavaScript, TypeScript, Go, Rust, C, C++, C#, Swift, Java, Kotlin, Dart, Julia, and APL via tree-sitter-language-pack
- 🌳 **Structure-Aware**: Uses AST-based understanding with efficient cursor-based traversal
- 🔎 **Searchable**: Find specific patterns using text search and tree-sitter queries
- 🔄 **Caching**: Optimized performance through parse tree caching
- 🔑 **Symbol Extraction**: Extract and analyze functions, classes, and other code symbols
- 📊 **Dependency Analysis**: Identify and analyze code dependencies and relationships
- 🧩 **State Persistence**: Maintains project registrations and cached data between invocations
- 🔒 **Secure**: Built-in security boundaries and input validation
For a comprehensive list of all available commands, their current implementation status, and detailed feature matrix, please refer to the [FEATURES.md](FEATURES.md) document.
## Installation
### Prerequisites
- Python 3.10+
- Tree-sitter language parsers for your preferred languages
### Basic Installation
```bash
pip install mcp-server-tree-sitter
```
### Development Installation
```bash
git clone https://github.com/wrale/mcp-server-tree-sitter.git
cd mcp-server-tree-sitter
pip install -e ".[dev]"
```
## Quick Start
### Running with Claude Desktop
You can make the server available in Claude Desktop either through the MCP CLI or by manually configuring Claude Desktop.
#### Using MCP CLI
Register the server with Claude Desktop:
```bash
mcp install mcp_server_tree_sitter.server:mcp --name "tree_sitter"
```
#### Manual Configuration
Alternatively, you can manually configure Claude Desktop:
1. Open your Claude Desktop configuration file:
- macOS/Linux: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
Create the file if it doesn't exist.
2. Add the server to the `mcpServers` section:
```json
{
"mcpServers": {
"tree_sitter": {
"command": "python",
"args": [
"-m",
"mcp_server_tree_sitter.server"
]
}
}
}
```
Alternatively, if using uv or another package manager:
```json
{
"mcpServers": {
"tree_sitter": {
"command": "uv",
"args": [
"--directory",
"/ABSOLUTE/PATH/TO/YOUR/PROJECT",
"run",
"-m",
"mcp_server_tree_sitter.server"
]
}
}
}
```
Note: Make sure to replace `/ABSOLUTE/PATH/TO/YOUR/PROJECT` with the actual absolute path to your project directory.
3. Save the file and restart Claude Desktop.
The MCP tools icon (hammer) will appear in Claude Desktop's interface once you have properly configured at least one MCP server. You can then access the `tree_sitter` server's functionality by clicking on this icon.
### Configuring with Released Version
If you prefer not to manually install the package from PyPI (released version) or clone the repository, simply use the following configuration for Claude Desktop:
1. Open your Claude Desktop configuration file (same location as above).
2. Add the tree-sitter server to the `mcpServers` section:
```json
{
"mcpServers": {
"tree_sitter": {
"command": "uvx",
"args": [
"--directory", "/ABSOLUTE/PATH/TO/YOUR/PROJECT",
"mcp-server-tree-sitter"
]
}
}
}
```
3. Save the file and restart Claude Desktop.
This method uses `uvx` to run the installed PyPI package directly, which is the recommended approach for the released version. The server doesn't require any additional parameters to run in its basic configuration.
## State Persistence
The MCP Tree-sitter Server maintains state between invocations. This means:
- Projects stay registered until explicitly removed or the server is restarted
- Parse trees are cached according to configuration settings
- Language information is retained throughout the server's lifetime
This persistence is maintained in-memory during the server's lifetime using singleton patterns for key components.
### Running as a standalone server
There are several ways to run the server:
#### Using the MCP CLI directly:
```bash
python -m mcp run mcp_server_tree_sitter.server
```
#### Using Makefile targets:
```bash
# Show available targets
make
# Run the server with default settings
make mcp-run
# Show help information
make mcp-run ARGS="--help"
# Show version information
make mcp-run ARGS="--version"
# Run with custom configuration file
make mcp-run ARGS="--config /path/to/config.yaml"
# Enable debug logging
make mcp-run ARGS="--debug"
# Disable parse tree caching
make mcp-run ARGS="--disable-cache"
```
#### Using the installed script:
```bash
# Run the server with default settings
mcp-server-tree-sitter
# Show help information
mcp-server-tree-sitter --help
# Show version information
mcp-server-tree-sitter --version
# Run with custom configuration file
mcp-server-tree-sitter --config /path/to/config.yaml
# Enable debug logging
mcp-server-tree-sitter --debug
# Disable parse tree caching
mcp-server-tree-sitter --disable-cache
```
### Using with the MCP Inspector
Using the MCP CLI directly:
```bash
python -m mcp dev mcp_server_tree_sitter.server
```
Or using the Makefile target:
```bash
make mcp-dev
```
You can also pass arguments:
```bash
make mcp-dev ARGS="--debug"
```
## Usage
### Register a Project
First, register a project to analyze:
```
register_project_tool(path="/path/to/your/project", name="my-project")
```
### Explore Files
List files in the project:
```
list_files(project="my-project", pattern="**/*.py")
```
View file content:
```
get_file(project="my-project", path="src/main.py")
```
### Analyze Code Structure
Get the syntax tree:
```
get_ast(project="my-project", path="src/main.py", max_depth=3)
```
Extract symbols:
```
get_symbols(project="my-project", path="src/main.py")
```
### Search Code
Search for text:
```
find_text(project="my-project", pattern="function", file_pattern="**/*.py")
```
Run tree-sitter queries:
```
run_query(
project="my-project",
query='(function_definition name: (identifier) @function.name)',
language="python"
)
```
### Analyze Complexity
```
analyze_complexity(project="my-project", path="src/main.py")
```
## Direct Python Usage
While the primary intended use is through the MCP server, you can also use the library directly in Python code:
```python
# Import from the API module
from mcp_server_tree_sitter.api import (
register_project, list_projects, get_config, get_language_registry
)
# Register a project
project_info = register_project(
path="/path/to/project",
name="my-project",
description="Description"
)
# List projects
projects = list_projects()
# Get configuration
config = get_config()
# Access components through dependency injection
from mcp_server_tree_sitter.di import get_container
container = get_container()
project_registry = container.project_registry
language_registry = container.language_registry
```
## Configuration
Create a YAML configuration file:
```yaml
cache:
enabled: true # Enable/disable caching (default: true)
max_size_mb: 100 # Maximum cache size in MB (default: 100)
ttl_seconds: 300 # Cache entry time-to-live in seconds (default: 300)
security:
max_file_size_mb: 5 # Maximum file size to process in MB (default: 5)
excluded_dirs: # Directories to exclude from processing
- .git
- node_modules
- __pycache__
allowed_extensions: # Optional list of allowed file extensions
# - py
# - js
# Leave empty or omit for all extensions
language:
default_max_depth: 5 # Default max depth for AST traversal (default: 5)
preferred_languages: # List of languages to pre-load at startup for faster performance
- python # Pre-loading reduces latency for first operations
- javascript
log_level: INFO # Logging level (DEBUG, INFO, WARNING, ERROR)
max_results_default: 100 # Default maximum results for search operations
```
Load it with:
```
configure(config_path="/path/to/config.yaml")
```
### Logging Configuration
The server's logging verbosity can be controlled using environment variables:
```bash
# Enable detailed debug logging
export MCP_TS_LOG_LEVEL=DEBUG
# Use normal informational logging (default)
export MCP_TS_LOG_LEVEL=INFO
# Only show warning and error messages
export MCP_TS_LOG_LEVEL=WARNING
```
For comprehensive information about logging configuration, please refer to the [logging documentation](docs/logging.md). For details on the command-line interface, see the [CLI documentation](docs/cli.md).
### About preferred_languages
The `preferred_languages` setting controls which language parsers are pre-loaded at server startup rather than on-demand. This provides several benefits:
- **Faster initial analysis**: No delay when first analyzing a file of a pre-loaded language
- **Early error detection**: Issues with parsers are discovered at startup, not during use
- **Predictable memory allocation**: Memory for frequently used parsers is allocated upfront
By default, all parsers are loaded on-demand when first needed. For optimal performance, specify the languages you use most frequently in your projects.
You can also configure specific settings:
```
configure(cache_enabled=True, max_file_size_mb=10, log_level="DEBUG")
```
Or use environment variables:
```bash
export MCP_TS_CACHE_MAX_SIZE_MB=256
export MCP_TS_LOG_LEVEL=DEBUG
export MCP_TS_CONFIG_PATH=/path/to/config.yaml
```
Environment variables use the format `MCP_TS_SECTION_SETTING` (e.g., `MCP_TS_CACHE_MAX_SIZE_MB`) for section settings, or `MCP_TS_SETTING` (e.g., `MCP_TS_LOG_LEVEL`) for top-level settings.
Configuration values are applied in this order of precedence:
1. Environment variables (highest)
2. Values set via `configure()` calls
3. YAML configuration file
4. Default values (lowest)
The server will look for configuration in:
1. Path specified in `configure()` call
2. Path specified by `MCP_TS_CONFIG_PATH` environment variable
3. Default location: `~/.config/tree-sitter/config.yaml`
## For Developers
### Diagnostic Capabilities
The MCP Tree-sitter Server includes a diagnostic framework to help identify and fix issues:
```bash
# Run diagnostic tests
make test-diagnostics
# CI-friendly version (won't fail the build on diagnostic issues)
make test-diagnostics-ci
```
Diagnostic tests provide detailed information about the server's behavior and can help isolate specific issues. For more information about the diagnostic framework, please see the [diagnostics documentation](docs/diagnostics.md).
### Type Safety Considerations
The MCP Tree-sitter Server maintains type safety when interfacing with tree-sitter libraries through careful design patterns and protocols. If you're extending the codebase, please review the [type safety guide](docs/tree-sitter-type-safety.md) for important information about handling tree-sitter API variations.
## Available Resources
The server provides the following MCP resources:
- `project://{project}/files` - List all files in a project
- `project://{project}/files/{pattern}` - List files matching a pattern
- `project://{project}/file/{path}` - Get file content
- `project://{project}/file/{path}/lines/{start}-{end}` - Get specific lines from a file
- `project://{project}/ast/{path}` - Get the AST for a file
- `project://{project}/ast/{path}/depth/{depth}` - Get the AST with custom depth
## Available Tools
The server provides tools for:
- Project management: `register_project_tool`, `list_projects_tool`, `remove_project_tool`
- Language management: `list_languages`, `check_language_available`
- File operations: `list_files`, `get_file`, `get_file_metadata`
- AST analysis: `get_ast`, `get_node_at_position`
- Code search: `find_text`, `run_query`
- Symbol extraction: `get_symbols`, `find_usage`
- Project analysis: `analyze_project`, `get_dependencies`, `analyze_complexity`
- Query building: `get_query_template_tool`, `list_query_templates_tool`, `build_query`, `adapt_query`, `get_node_types`
- Similar code detection: `find_similar_code`
- Cache management: `clear_cache`
- Configuration diagnostics: `diagnose_config`
See [FEATURES.md](FEATURES.md) for detailed information about each tool's implementation status, dependencies, and usage examples.
## Available Prompts
The server provides the following MCP prompts:
- `code_review` - Create a prompt for reviewing code
- `explain_code` - Create a prompt for explaining code
- `explain_tree_sitter_query` - Explain tree-sitter query syntax
- `suggest_improvements` - Create a prompt for suggesting code improvements
- `project_overview` - Create a prompt for a project overview analysis
## Feedback & Community
We'd love to hear how you're using mcp-server-tree-sitter and what would make it more useful for your workflow.
- **Questions & Feature Requests**: [GitHub Discussions](https://github.com/wrale/mcp-server-tree-sitter/discussions)
- **Bug Reports**: [GitHub Issues](https://github.com/wrale/mcp-server-tree-sitter/issues)
## License
MIT
================================================
FILE: ROADMAP.md
================================================
# MCP Tree-sitter Server Roadmap
This document outlines the planned improvements and future features for the MCP Tree-sitter Server project.
CRITICAL: When a task is done, update this document to mark it done. However, you must ensure it is done for all files/subjects present in the repo. DO NOT mark a task done simply because a subset of the targeted files/subjects have been handled. Mark it [WIP] in that case.
## Short-term Goals
### Code Quality
- ✅ Fix linting issues identified by ruff
- ✅ Improve exception handling using proper `from` clause
- ✅ Remove unused variables and improve code organization
- ✅ Implement TreeCursor API support with proper type handling
- ✅ Add incremental parsing support
- ✅ Add MCP Progress Reporting
- ✅ Add Server Capabilities Declaration
- [ ] Add mcp server start flag(s) for enabling (allow list approach) and disabling (block list approach) a list of features. Only one approach may be applied at a time. The default should be minimal allowed, for now. Add meta features such as stable, wip, advanced, basic
- ✅ Add mcp server start flag(s) for ensuring language packs are installed - Resolved by tree-sitter-language-pack integration
- [ ] Add mcp server start flag(s) for ensuring project is configured beforehand.
- [ ] Achieve 100% type hinting coverage (and ensure this is enforced by our linting)
- [ ] Improve docstring coverage and quality (Don't thrash on updating docs that are already good) (HOLD pending other work)
- [ ] Split files until the longest .py file is less than 500 lines (unless that breaks functionality, in which case do not)
### Testing
- ✅ Create and maintain tests for AST functionality, query execution, and symbol extraction
- 🔄 [WIP] Create additional tests for context utilities, incremental parsing, and cursor traversal
- [ ] Increase unit test coverage to 100% and begin enforcing that in pre-commit and CI
- [ ] Add integration tests for MCP server functionality (HOLD pending other work)
- [ ] Create automated testing workflow with GitHub Actions (unit, integration, static, etc) (HOLD pending other work)
### Documentation (HOLD)
- ✅ Create CONTRIBUTING.md with developer guidelines
- 🔄 [WIP] Create a docs/user-guide.md with more examples and clearer installation instructions. Link to it from README.md
- [ ] Add detailed API documentation in docs/api-guide.md
- 🔄 [WIP] Create usage tutorials and examples -- focus only on Claude Desktop for now.
## Medium-term Goals (HOLD)
### Feature Improvements
- ✅ Add support for more tree-sitter languages by implementing https://github.com/Goldziher/tree-sitter-language-pack/
- ✅ Add support for query execution with proper result handling
- [ ] Improve query building tools with more sophisticated matching options (HOLD because we could cripple the codebase with complexity)
- [ ] Implement more advanced code analysis metrics (HOLD because we could cripple the codebase with complexity)
- [ ] Enhance caching system with better invalidation strategy (HOLD because we could cripple the codebase with complexity)
### User Experience
- [ ] Create a web-based UI for visualizing ASTs and running queries (HOLD because Claude's experience is more important)
- [ ] Add CLI commands for common operations (HOLD because Claude runs commands by a different channel)
- [✅] Implement progress reporting for long-running operations
- [ ] Add configuration presets for different use cases (HOLD because we could cripple the codebase with complexity)
### Security
- [ ] Add comprehensive input validation (HOLD because we could cripple the codebase with complexity)
- [ ] Implement access control for multi-user environments (HOLD because we could cripple the codebase with complexity)
- [ ] Add sandbox mode for running untrusted queries (HOLD because we could cripple the codebase with complexity)
## Long-term Goals (HOLD)
### Advanced Features
- [ ] Implement semantic analysis capabilities (HOLD because we need stability first)
- [ ] Add code transformation tools (HOLD because we need stability first)
- [ ] Support cross-language analysis (HOLD because we need stability first)
### Integration
- [ ] Create plugins for popular IDEs (VS Code, IntelliJ) (HOLD because we need stability first)
- [ ] Implement integration with CI/CD pipelines (HOLD because we need stability first)
- [ ] Add support for other LLM frameworks beyond MCP (HOLD because we need stability first)
### Performance
- [ ] Optimize for large codebases (> 1M LOC) (HOLD because we need stability first)
- [ ] Implement distributed analysis for very large projects (HOLD because we need stability first)
- [ ] Add streaming responses for large result sets (HOLD because we need stability first)
## Completed Implementations
### MCP Context Handling
- Added `utils/context/mcp_context.py` with progress tracking capabilities
- Implemented `MCPContext` class with progress reporting
- Created `ProgressScope` for structured operation tracking
- Added context information passing to analysis tools
### TreeCursor API Support
- Enhanced `utils/tree_sitter_types.py` with TreeCursor protocol
- Added efficient cursor-based tree traversal in `utils/tree_sitter_helpers.py`
- Implemented collector pattern using cursors to efficiently find nodes
### Incremental Parsing
- Added support for tree editing in `utils/tree_sitter_helpers.py`
- Enhanced cache to track tree modifications in `cache/parser_cache.py`
- Implemented changed_ranges detection for optimization
### Server Capabilities Declaration
- Created `capabilities/server_capabilities.py` for capability declaration
- Implemented required MCP server capabilities
- Added support for completion suggestions
- Added structured logging integration
## Features and Ideas
Below are some ideas and feature requests being considered:
1. **Semantic Diff**: Show semantic differences between code versions rather than just text diffs (HOLD because we need stability first)
2. **Code Quality Metrics**: Integrate with code quality metrics and linters (HOLD because we need stability first)
3. **Interactive Query Builder**: Visual tool to build and test tree-sitter queries (HOLD because we need stability first)
4. **Code Completion**: Use tree-sitter for more intelligent code completion suggestions (HOLD because we need stability first)
5. **Visualization Export**: Export AST visualizations to various formats (SVG, PNG, etc.) (HOLD because we need stability first)
================================================
FILE: TODO.md
================================================
# MCP Tree-sitter Server: TODO Board
This Kanban board tracks tasks specifically focused on improving partially working commands and implementing missing features.
## In Progress
### High Priority
---
#### Fix Similar Code Detection
- **Description**: Improve the `find_similar_code` command to reliably return results
- **Tasks**:
- [ ] Debug why command completes but doesn't return results
- [ ] Optimize similarity threshold and matching algorithm
- [ ] Add more detailed logging for troubleshooting
- [ ] Create comprehensive test cases with expected results
- **Acceptance Criteria**:
- Command reliably returns similar code snippets when they exist
- Appropriate feedback when no similar code is found
- Documentation updated with examples and recommended thresholds
- **Complexity**: Medium
- **Dependencies**: None
#### Complete Tree Editing and Incremental Parsing
- **Description**: Extend AST functionality to support tree manipulation
- **Tasks**:
- [ ] Implement tree editing operations (insert, delete, replace nodes)
- [ ] Add incremental parsing to efficiently update trees after edits
- [ ] Ensure node IDs remain consistent during tree manipulations
- **Acceptance Criteria**:
- Trees can be modified through API calls
- Incremental parsing reduces parse time for small changes
- Proper error handling for invalid modifications
- **Complexity**: High
- **Dependencies**: None
### Medium Priority
---
#### Implement UTF-16 Support
- **Description**: Add encoding detection and support for UTF-16
- **Tasks**:
- [ ] Implement encoding detection for input files
- [ ] Add UTF-16 to UTF-8 conversion for parser compatibility
- [ ] Handle position mapping between different encodings
- **Acceptance Criteria**:
- Correctly parse and handle UTF-16 encoded files
- Maintain accurate position information in different encodings
- Test suite includes UTF-16 encoded files
- **Complexity**: Medium
- **Dependencies**: None
#### Add Read Callable Support
- **Description**: Implement custom read strategies for efficient large file handling
- **Tasks**:
- [ ] Create streaming parser interface for large files
- [ ] Implement memory-efficient parsing strategy
- [ ] Add support for custom read handlers
- **Acceptance Criteria**:
- Successfully parse files larger than memory constraints
- Performance tests show acceptable parsing speed
- Documentation on how to use custom read strategies
- **Complexity**: High
- **Dependencies**: None
## Ready for Review
### High Priority
---
#### Complete MCP Context Progress Reporting
- **Description**: Implement progress reporting for long-running operations
- **Tasks**:
- [ ] Add progress tracking to all long-running operations
- [ ] Implement progress callbacks in the MCP context
- [ ] Update API to report progress percentage
- **Acceptance Criteria**:
- Long-running operations report progress
- Progress is visible to the user
- Cancellation is possible for operations in progress
- **Complexity**: Low
- **Dependencies**: None
## Done
*No tasks completed yet*
## Backlog
### Low Priority
---
#### Add Image Handling Support
- **Description**: Implement support for returning images/visualizations from tools
- **Tasks**:
- [ ] Create image generation utilities for AST visualization
- [ ] Add support for returning images in MCP responses
- [ ] Implement SVG or PNG export of tree structures
- **Acceptance Criteria**:
- Tools can return visual representations of code structures
- AST visualizations can be generated and returned
- **Complexity**: Medium
- **Dependencies**: None
---
## Task Metadata
### Priority Levels
- **High**: Critical for core functionality, should be addressed immediately
- **Medium**: Important for comprehensive feature set, address after high priority items
- **Low**: Nice to have, address when resources permit
### Complexity Levels
- **Low**: Estimated 1-2 days of work
- **Medium**: Estimated 3-5 days of work
- **High**: Estimated 1-2 weeks of work
================================================
FILE: docs/architecture.md
================================================
# Architecture Overview
This document provides an overview of the MCP Tree-sitter Server's architecture, focusing on key components and design patterns.
## Core Architecture
The MCP Tree-sitter Server follows a structured architecture with the following components:
1. **Bootstrap Layer**: Core initialization systems that must be available to all modules with minimal dependencies
2. **Configuration Layer**: Configuration management with environment variable support
3. **Dependency Injection Container**: Central container for managing and accessing services
4. **Tree-sitter Integration**: Interfaces with the tree-sitter library for parsing and analysis
5. **MCP Protocol Layer**: Handles interactions with the Model Context Protocol
## Bootstrap Layer
The bootstrap layer handles critical initialization tasks that must happen before anything else:
```
src/mcp_server_tree_sitter/bootstrap/
├── __init__.py # Exports key bootstrap functions
└── logging_bootstrap.py # Canonical logging configuration
```
This layer is imported first in the package's `__init__.py` and has minimal dependencies. The bootstrap module ensures that core services like logging are properly initialized and globally available to all modules.
**Key Design Principle**: Each component in the bootstrap layer must have minimal dependencies to avoid import cycles and ensure reliable initialization.
## Dependency Injection Pattern
Instead of using global variables (which was the approach in earlier versions), the application now uses a structured dependency injection pattern:
1. **DependencyContainer**: The `DependencyContainer` class holds all application components and services
2. **ServerContext**: A context class provides a clean interface for interacting with dependencies
3. **Access Functions**: API functions like `get_logger()` and `update_log_levels()` provide easy access to functionality
This approach has several benefits:
- Cleaner testing with the ability to mock dependencies
- Better encapsulation of implementation details
- Reduced global state and improved thread safety
- Clearer dependency relationships between components
## Logging Design
Logging follows a hierarchical model using Python's standard `logging` module:
1. **Root Package Logger**: Only the root package logger (`mcp_server_tree_sitter`) has its level explicitly set
2. **Child Loggers**: Child loggers inherit their level from the root package logger
3. **Handler Synchronization**: Handler levels are synchronized with their logger's effective level
**Canonical Implementation**: The logging system is defined in a single location - `bootstrap/logging_bootstrap.py`. Other modules import from this module to ensure consistent behavior.
### Logging Functions
The bootstrap module provides these key logging functions:
```python
# Get log level from environment variable
get_log_level_from_env()
# Configure the root logger
configure_root_logger()
# Get a properly configured logger
get_logger(name)
# Update log levels
update_log_levels(level_name)
```
## Configuration System
The configuration system uses a layered approach:
1. **Environment Variables**: Highest precedence (e.g., `MCP_TS_LOG_LEVEL=DEBUG`)
2. **Explicit Updates**: Updates made via `update_value()` calls
3. **YAML Configuration**: Settings from YAML configuration files
4. **Default Values**: Fallback defaults defined in model classes
The `ConfigurationManager` is responsible for loading, managing, and applying configuration, while a `ServerConfig` model encapsulates the actual configuration settings.
## Project and Language Management
Projects and languages are managed by registry classes:
1. **ProjectRegistry**: Maintains active project registrations
2. **LanguageRegistry**: Manages tree-sitter language parsers
These registries are accessed through the dependency container or context, providing a clean interface for operations.
## Use of Builder and Factory Patterns
The server uses several design patterns for cleaner code:
1. **Builder Pattern**: Used for constructing complex objects like `Project` instances
2. **Factory Methods**: Used to create tree-sitter parsers and queries
3. **Singleton Pattern**: Used for the dependency container to ensure consistent state
## Lifecycle Management
The server's lifecycle is managed in a structured way:
1. **Bootstrap Phase**: Initializes logging and critical systems (from `__init__.py`)
2. **Configuration Phase**: Loads configuration from files and environment
3. **Dependency Initialization**: Sets up all dependencies in the container
4. **Server Setup**: Configures MCP tools and capabilities
5. **Running Phase**: Processes requests from the MCP client
6. **Shutdown**: Gracefully handles shutdown and cleanup
## Error Handling Strategy
The server implements a layered error handling approach:
1. **Custom Exceptions**: Defined in `exceptions.py` for specific error cases
2. **Function-Level Handlers**: Most low-level functions do error handling
3. **Tool-Level Handlers**: MCP tools handle errors and return structured responses
4. **Global Exception Handling**: FastMCP provides top-level error handling
## Future Architecture Improvements
Planned architectural improvements include:
1. **Complete Decoupling**: Further reduce dependencies between components
2. **Module Structure Refinement**: Better organize modules by responsibility
3. **Configuration Caching**: Optimize configuration access patterns
4. **Async Support**: Add support for asynchronous operations
5. **Plugin Architecture**: Support for extensibility through plugins
================================================
FILE: docs/cli.md
================================================
# MCP Tree-sitter Server CLI Guide
This document explains the command-line interface (CLI) for the MCP Tree-sitter Server, including available options and usage patterns.
## Command-Line Arguments
The MCP Tree-sitter Server provides a command-line interface with several options:
```bash
mcp-server-tree-sitter [options]
```
### Available Options
| Option | Description |
|--------|-------------|
| `--help` | Show help message and exit |
| `--version` | Show version information and exit |
| `--config CONFIG` | Path to configuration file |
| `--debug` | Enable debug logging |
| `--disable-cache` | Disable parse tree caching |
### Examples
Display help information:
```bash
mcp-server-tree-sitter --help
```
Show version information:
```bash
mcp-server-tree-sitter --version
```
Run with a custom configuration file:
```bash
mcp-server-tree-sitter --config /path/to/config.yaml
```
Enable debug logging:
```bash
mcp-server-tree-sitter --debug
```
Disable parse tree caching:
```bash
mcp-server-tree-sitter --disable-cache
```
## Running with MCP
The server can also be run using the MCP command-line interface:
```bash
# Run the server
mcp run mcp_server_tree_sitter.server
# Run with the MCP Inspector
mcp dev mcp_server_tree_sitter.server
```
You can pass the same arguments to these commands:
```bash
# Enable debug logging
mcp run mcp_server_tree_sitter.server --debug
# Use a custom configuration file with the inspector
mcp dev mcp_server_tree_sitter.server --config /path/to/config.yaml
```
## Using Makefile Targets
For convenience, the project provides Makefile targets for common operations:
```bash
# Show available targets
make
# Run the server with default settings
make mcp-run
# Run with specific arguments
make mcp-run ARGS="--debug --config /path/to/config.yaml"
# Run with the inspector
make mcp-dev ARGS="--debug"
```
## Environment Variables
The server also supports configuration through environment variables:
```bash
# Set log level
export MCP_TS_LOG_LEVEL=DEBUG
# Set configuration file path
export MCP_TS_CONFIG_PATH=/path/to/config.yaml
# Run the server
mcp-server-tree-sitter
```
See the [Configuration Guide](./config.md) for more details on environment variables and configuration options.
================================================
FILE: docs/config.md
================================================
# MCP Tree-sitter Server Configuration Guide
This document explains the configuration system for the MCP Tree-sitter Server, including both the YAML configuration format and the internal architecture changes for configuration management.
## YAML Configuration Format
The MCP Tree-sitter Server can be configured using a YAML file with the following sections:
### Cache Settings
Controls the parser tree cache behavior:
```yaml
cache:
enabled: true # Enable/disable caching (default: true)
max_size_mb: 100 # Maximum cache size in MB (default: 100)
ttl_seconds: 300 # Cache entry time-to-live in seconds (default: 300)
```
### Security Settings
Controls security boundaries:
```yaml
security:
max_file_size_mb: 5 # Maximum file size to process in MB (default: 5)
excluded_dirs: # Directories to exclude from processing
- .git
- node_modules
- __pycache__
allowed_extensions: # Optional list of allowed file extensions
# - py
# - js
# - ts
# Leave empty or omit for all extensions
```
### Language Settings
Controls language behavior:
```yaml
language:
auto_install: false # DEPRECATED: No longer used with tree-sitter-language-pack
default_max_depth: 5 # Default max depth for AST traversal (default: 5)
preferred_languages: # List of languages to pre-load at server startup for improved performance
- python # Pre-loading reduces latency for first operations
- javascript
- typescript
```
### General Settings
Controls general server behavior:
```yaml
log_level: INFO # General logging level (DEBUG, INFO, WARNING, ERROR)
max_results_default: 100 # Default maximum results for search operations
```
### Complete Example
Here's a complete example configuration file:
```yaml
cache:
enabled: true
max_size_mb: 256
ttl_seconds: 3600
security:
max_file_size_mb: 10
excluded_dirs:
- .git
- node_modules
- __pycache__
- .cache
- .venv
- vendor
allowed_extensions:
- py
- js
- ts
- rs
- go
language:
default_max_depth: 7
preferred_languages:
- python # Pre-load these language parsers at startup
- javascript # for faster initial performance
- typescript
log_level: INFO
max_results_default: 100
```
## Deprecated Settings
The following settings are deprecated and should not be used in new configurations:
```yaml
language:
auto_install: true # DEPRECATED: No longer used with tree-sitter-language-pack
```
This setting was used to control automatic installation of language parsers, but it's no longer relevant since the server now uses tree-sitter-language-pack which includes all supported languages.
## Language Settings: preferred_languages
The `preferred_languages` setting allows you to specify which language parsers should be pre-loaded at server startup:
```yaml
language:
preferred_languages:
- python
- javascript
- typescript
```
**Purpose and benefits:**
- **Performance improvement**: Pre-loading parsers avoids the latency of loading them on first use
- **Early error detection**: Any issues with parsers are detected at startup, not during operation
- **Predictable memory usage**: Memory for parsers is allocated upfront
By default, this list is empty and parsers are loaded on-demand when first needed. For best performance, specify the languages you plan to use most frequently in your projects.
## Configuration Architecture
### Dependency Injection Approach
The MCP Tree-sitter Server uses a dependency injection (DI) pattern for configuration management. This is implemented with a central container and a global context that serve as structured access points. This approach improves:
- **Testability**: Components can be tested with mock configurations
- **Thread safety**: Configuration access is centralized with proper locking
- **Modularity**: Components are decoupled from direct global variable access
While the system does use singleton objects internally, they are accessed through proper dependency injection patterns rather than direct global variable usage.
### Key Components
#### Dependency Container
The central component is the `DependencyContainer` which holds all shared services:
```python
from mcp_server_tree_sitter.di import get_container
# Get the global container instance
container = get_container()
# Access services
config_manager = container.config_manager
project_registry = container.project_registry
language_registry = container.language_registry
tree_cache = container.tree_cache
```
#### ServerContext
The `ServerContext` provides a convenient high-level interface to the container:
```python
from mcp_server_tree_sitter.context import ServerContext, global_context
# Use the global context instance
config = global_context.get_config()
# Or create a custom context for testing
test_context = ServerContext()
test_config = test_context.get_config()
```
#### API Functions
The most convenient way to access functionality is through API functions:
```python
from mcp_server_tree_sitter.api import get_config, get_language_registry, register_project
# Access services through API functions
config = get_config()
language_registry = get_language_registry()
project = register_project("/path/to/project")
```
### Global Context vs. Pure Dependency Injection
The server provides multiple approaches to accessing services:
1. **API Functions**: For simplicity and convenience, most code should use these functions
2. **Dependency Container**: For more control, access the container directly
3. **Global Context**: A higher-level interface to the container
4. **Pure DI**: For testing, components can accept explicit dependencies as parameters
Example of pure DI:
```python
def configure_with_context(context, config_path=None, cache_enabled=None, ...):
# Use the provided context rather than global state
result, config = context.config_manager.load_from_file(config_path)
return result, config
```
## Configuring the Server
### Using the MCP Tool
Use the `configure` MCP tool to apply configuration:
```python
# Load from YAML file
configure(config_path="/path/to/config.yaml")
# Set specific values
configure(cache_enabled=True, max_file_size_mb=10, log_level="DEBUG")
```
### Using Environment Variables
Set environment variables to configure the server:
```sh
# Set cache size
export MCP_TS_CACHE_MAX_SIZE_MB=256
# Set log level
export MCP_TS_LOG_LEVEL=DEBUG
# Set config file path
export MCP_TS_CONFIG_PATH=/path/to/config.yaml
# Run the server
mcp run mcp_server_tree_sitter.server
```
Environment variables use the format `MCP_TS_SECTION_SETTING` where:
- `MCP_TS_` is the required prefix for all environment variables
- `SECTION` corresponds to a configuration section (e.g., `CACHE`, `SECURITY`, `LANGUAGE`)
- `SETTING` corresponds to a specific setting within that section (e.g., `MAX_SIZE_MB`, `MAX_FILE_SIZE_MB`)
For top-level settings like `log_level`, the format is simply `MCP_TS_SETTING` (e.g., `MCP_TS_LOG_LEVEL`).
#### Configuration Precedence
The server follows this precedence order when determining configuration values:
1. **Environment Variables** (highest precedence)
2. **Explicit Updates** via `update_value()`
3. **YAML Configuration** from file
4. **Default Values** (lowest precedence)
This means environment variables will always override values from other sources.
##### Reasoning for this Precedence Order
This precedence model was chosen for several important reasons:
1. **Containerization compatibility**: Environment variables are the standard way to configure applications in containerized environments like Docker and Kubernetes. Having them at the highest precedence ensures compatibility with modern deployment practices.
2. **Operational control**: System administrators and DevOps teams can set environment variables to enforce certain behaviors without worrying about code accidentally or intentionally overriding those settings.
3. **Security boundaries**: Critical security settings like `max_file_size_mb` are better protected when environment variables take precedence, creating a hard boundary that code cannot override.
4. **Debugging convenience**: Setting `MCP_TS_LOG_LEVEL=DEBUG` should reliably increase logging verbosity regardless of other configuration sources, making troubleshooting easier.
5. **Runtime adjustability**: Having explicit updates second in precedence allows for runtime configuration changes that don't persist beyond the current session, unlike environment variables which might be set system-wide.
6. **Fallback clarity**: With this model, it's clear that YAML provides the persistent configuration and defaults serve as the ultimate fallback, leading to predictable behavior.
## Default Configuration Locations
The server will look for configuration files in the following locations:
1. Path specified by `MCP_TS_CONFIG_PATH` environment variable
2. Default location: `~/.config/tree-sitter/config.yaml`
## Best Practices
### For Server Users
1. Create a `.treesitter.yaml` file in your project root with your preferred settings
2. Use the `configure` MCP tool with the path to your YAML file
3. Adjust cache size based on your project size and available memory
### For Server Developers
1. Use API functions for most operations
2. Use dependency injection with explicit parameters for new code
3. Access the dependency container directly only when necessary
4. Write tests with isolated contexts rather than relying on global state
## Migration from Global CONFIG
If you have code that previously used the global `CONFIG` variable directly, update it as follows:
**Old code:**
```python
from mcp_server_tree_sitter.config import CONFIG
max_depth = CONFIG.language.default_max_depth
```
**New code:**
```python
from mcp_server_tree_sitter.api import get_config
config = get_config()
max_depth = config.language.default_max_depth
```
### Importing Exceptions
With the dependency injection approach, exceptions must be imported explicitly. For example, if using `SecurityError` or `FileAccessError`:
```python
from mcp_server_tree_sitter.exceptions import SecurityError, FileAccessError
# Now you can use these exceptions in your code
```
For tests, create isolated contexts:
```python
from mcp_server_tree_sitter.context import ServerContext
from mcp_server_tree_sitter.config import ConfigurationManager
# Create test context
config_manager = ConfigurationManager()
config_manager.update_value("cache.enabled", False)
test_context = ServerContext(config_manager=config_manager)
# Use test context in your function
result = my_function(context=test_context)
```
================================================
FILE: docs/diagnostics.md
================================================
# MCP Tree-sitter Server Diagnostics
This document describes the diagnostic testing approach for the MCP Tree-sitter Server project.
## Overview
The diagnostics suite consists of targeted pytest tests that isolate and document specific issues in the codebase. These tests are designed to:
1. Document current behavior with proper pass/fail results
2. Isolate failure points to specific functions or modules
3. Provide detailed error information and stack traces
4. Create a foundation for developing targeted fixes
The diagnostic framework combines standard pytest behavior with enhanced diagnostic capabilities:
- Tests properly pass or fail based on assertions
- Comprehensive diagnostic data is captured for debugging
- Diagnostic information is saved to JSON for further analysis
## Running Diagnostics
The Makefile includes several targets for running diagnostics:
```bash
# Run all diagnostic tests
make test-diagnostics
# CI-friendly version (won't fail the build on diagnostic issues)
make test-diagnostics-ci
```
For running diagnostics alongside regular tests:
```bash
# Run both regular tests and diagnostics
make test-all
```
## Using the Diagnostic Framework
### Basic Test Structure
```python
import pytest
from mcp_server_tree_sitter.testing import diagnostic
@pytest.mark.diagnostic # Mark the test as producing diagnostic data
def test_some_feature(diagnostic): # Use the diagnostic fixture
# Add details to diagnostic data
diagnostic.add_detail("key", "value")
try:
# Test your functionality
result = some_functionality()
# Use standard assertions - the test will fail if they don't pass
assert result is not None, "Result should not be None"
except Exception as e:
# Record the error in diagnostic data
diagnostic.add_error("ErrorType", str(e))
# Add any artifacts you want to save
diagnostic.add_artifact("error_artifact", {"error": str(e)})
# Re-raise to fail the test
raise
```
### Diagnostic Operations
The `diagnostic` fixture provides several methods:
- `add_detail(key, value)`: Add a key-value pair to diagnostic details
- `add_error(error_type, message, traceback=None)`: Add an error
- `add_artifact(name, content)`: Add an artifact (e.g., JSON data)
- `finalize(status="completed")`: Mark the diagnostic as complete
## Key Issues Identified and Fixed
The following issues were identified during the diagnostic process and have since been fixed in the current implementation:
### 1. Language Registry Issues (FIXED)
- `list_languages()` previously returned empty lists despite languages being available
- Language detection through `install_language()` worked, but languages didn't appear in available lists
### 2. AST Parsing Failures (FIXED)
- `get_ast()` previously failed with errors when attempting to build the tree
- Core AST parsing functionality is now operational with efficient cursor-based traversal
### 3. "Too Many Values to Unpack" Errors (FIXED)
- Several analysis functions failed with "too many values to unpack (expected 2)"
- Affected `get_symbols()`, `get_dependencies()`, and `analyze_complexity()`
- These issues were resolved by fixing query captures handling
### 4. Tree-sitter Language Pack Integration (FIXED)
- Integration with tree-sitter-language-pack is now complete and stable
- All supported languages are correctly recognized and available for analysis
## Diagnostic Results
The diagnostic tests generate detailed JSON result files in the `diagnostic_results` directory with timestamps. These files contain valuable information for debugging:
- Error messages and stack traces
- Current behavior documentation
- Environment and configuration details
- Detailed information about tree-sitter integration
In addition, the test output includes a diagnostic summary:
```
============================== Diagnostic Summary ==============================
Collected 4 diagnostics, 2 with errors
-------------------------------- Error Details ---------------------------------
- /path/to/test.py::test_function
Error 1: ErrorType: Error message
```
## Recommended Debugging Approach
1. Run the diagnostic tests to verify current issues
```
make test-diagnostics
```
2. Examine the diagnostic results in the terminal output and the `diagnostic_results` directory
3. Review specific error patterns to identify the root cause:
- For unpacking errors, check the query capture processing code
- For AST parsing, examine the tree-sitter integration layer
- For language registry issues, check the initialization sequence
4. Make targeted fixes to address specific issues, using the diagnostic tests to verify repairs
5. After fixes, run both diagnostics and regular tests to ensure no regressions
```
make test-all
```
## Previous Issue Priority (Now Resolved)
The following priority was used to address the previously identified issues, which have all been resolved:
1. ✅ **Language Registry Issues** - Fixed language listing to enable proper language detection
2. ✅ **AST Parsing** - Fixed core parsing functionality with efficient cursor-based traversal
3. ✅ **Query Handling** - Resolved unpacking errors in query captures to enable analysis tools
4. ✅ **Incremental Improvements** - Core functionality is working correctly and ready for further refinement
All 90 tests are now passing, including the diagnostic tests.
## Integrating with Development Workflow
Diagnostics should be run:
- After any significant changes to core tree-sitter integration code
- Before submitting pull requests that touch language or AST handling
- When investigating specific failures in higher-level functionality
- As part of debugging for issues reported by users
## Continuous Integration
For CI environments, the diagnostic tests have special considerations:
### CI-Friendly Targets
The Makefile includes CI-friendly targets that won't fail the build due to known issues:
- `make test-diagnostics-ci`: Runs diagnostics but always returns success
### CI Setup Recommendations
1. **Primary CI Pipeline**: Use `make test` for regression testing of working functionality
```yaml
test:
script:
- make test
```
2. **Diagnostic Job**: Add a separate, optional job for diagnostics
```yaml
diagnostics:
script:
- make test-diagnostics-ci
artifacts:
paths:
- diagnostic_results/
allow_failure: true
```
## Benefits of the Pytest-based Approach
The pytest-based diagnostic framework offers significant advantages:
1. **Unified framework**: All tests use pytest with consistent behavior
2. **Clear pass/fail**: Tests fail when they should, making issues obvious
3. **Rich diagnostics**: Detailed diagnostic information is still collected
4. **Standard integration**: Works with pytest's fixtures, plugins, and reporting
## Future Improvements
In the future, we plan to:
1. Enhance the diagnostic plugin with more features
2. Integrate with CI/CD pipelines for better reporting
3. Add automatic visualization of diagnostic data
4. Improve the organization of diagnostic tests
================================================
FILE: docs/logging.md
================================================
# Logging Configuration Guide
This document explains how logging is configured in the MCP Tree-sitter Server and how to control log verbosity using environment variables.
## Environment Variable Configuration
The simplest way to control logging verbosity is by setting the `MCP_TS_LOG_LEVEL` environment variable:
```bash
# Enable detailed debug logging
export MCP_TS_LOG_LEVEL=DEBUG
# Use normal informational logging
export MCP_TS_LOG_LEVEL=INFO
# Only show warning and error messages
export MCP_TS_LOG_LEVEL=WARNING
```
## Log Level Values
The following log level values are supported:
| Level | Description |
|-------|-------------|
| DEBUG | Most verbose, includes detailed diagnostic information |
| INFO | Standard informational messages |
| WARNING | Only warning and error messages |
| ERROR | Only error messages |
| CRITICAL | Only critical failures |
## How Logging Is Configured
The logging system follows these principles:
1. **Early Environment Variable Processing**: Environment variables are processed at the earliest point in the application lifecycle
2. **Root Logger Configuration**: The package root logger (`mcp_server_tree_sitter`) is configured based on the environment variable value
3. **Logger Hierarchy**: Levels are set _only_ on the root package logger, allowing child loggers to inherit properly
4. **Handler Synchronization**: Handler levels are synchronized to match their logger's effective level
5. **Consistent Propagation**: Log record propagation is preserved throughout the hierarchy
## Using Loggers in Code
When adding logging to code, use the centralized utility function:
```python
from mcp_server_tree_sitter.bootstrap import get_logger
# Create a properly configured logger
logger = get_logger(__name__)
# Use standard logging methods
logger.debug("Detailed diagnostic information")
logger.info("Standard information")
logger.warning("Warning message")
logger.error("Error message")
```
> **Note**: For backwards compatibility, you can also import from `mcp_server_tree_sitter.logging_config`, but new code should use the bootstrap module directly.
The `get_logger()` function respects the logger hierarchy and only sets explicit levels on the root package logger, allowing proper level inheritance for all child loggers.
## Dynamically Changing Log Levels
Log levels can be updated at runtime using:
```python
from mcp_server_tree_sitter.bootstrap import update_log_levels
# Set to debug level
update_log_levels("DEBUG")
# Or use numeric values
import logging
update_log_levels(logging.INFO)
```
This will update _only_ the root package logger and its handlers while maintaining the proper logger hierarchy. Child loggers will automatically inherit the new level.
> **Note**: You can also import these functions from `mcp_server_tree_sitter.logging_config`, which forwards to the bootstrap module for backwards compatibility.
## Command-line Configuration
When running the server directly, you can use the `--debug` flag:
```bash
python -m mcp_server_tree_sitter --debug
```
This flag sets the log level to DEBUG both via environment variable and direct configuration, ensuring consistent behavior.
## Persistence of Log Levels
Log level changes persist through the current server session, but environment variables must be set before the server starts to ensure they are applied from the earliest initialization point. Environment variables always take highest precedence in the configuration hierarchy.
## How Logger Hierarchy Works
The package uses a proper hierarchical logger structure following Python's best practices:
- `mcp_server_tree_sitter` (root package logger) - **only logger with explicitly set level**
- `mcp_server_tree_sitter.config` (module logger) - **inherits level from parent**
- `mcp_server_tree_sitter.server` (module logger) - **inherits level from parent**
- etc.
### Level Inheritance
In Python's logging system:
- Each logger maintains its own level setting
- Child loggers inherit levels from parent loggers **unless** explicitly set
- Log **records** (not levels) propagate up the hierarchy if `propagate=True`
- The effective level of a logger is determined by its explicit level, or if not set, its nearest ancestor with an explicit level
Setting `MCP_TS_LOG_LEVEL=DEBUG` sets the root package logger's level to DEBUG, which affects all child loggers that don't have explicit levels. Our implementation strictly adheres to this principle and avoids setting individual logger levels unnecessarily.
### Handler vs. Logger Levels
There are two separate level checks in the logging system:
1. **Logger Level**: Determines if a message is processed by the logger
2. **Handler Level**: Determines if a processed message is output by a specific handler
Our system synchronizes handler levels with their corresponding logger's effective level (which may be inherited). This ensures that messages that pass the logger level check also pass the handler level check.
## Troubleshooting
If logs are not appearing at the expected level:
1. Ensure the environment variable is set before starting the server
2. Verify the log level was applied to the root package logger (`mcp_server_tree_sitter`)
3. Check that handler levels match their logger's effective level
4. Verify that log record propagation is enabled (`propagate=True`)
5. Use `logger.getEffectiveLevel()` to check the actual level being used by any logger
6. Remember that environment variables have the highest precedence in the configuration hierarchy
## Implementation Details
The logging system follows strict design requirements:
1. **Environment Variable Processing**: Environment variables are processed at the earliest point in the application lifecycle, before any module imports
2. **Root Logger Configuration**: Only the package root logger has its level explicitly set
3. **Handler Synchronization**: Handler levels are synchronized with their logger's effective level
4. **Propagation Preservation**: Log record propagation is enabled for consistent behavior
5. **Centralized Configuration**: All logging is configured through the `logging_config.py` module
6. **Configuration Precedence**: Environment variables > Explicit updates > YAML config > Defaults
For the complete implementation details, see the `bootstrap/logging_bootstrap.py` module source code.
## Bootstrap Architecture
The logging system is now implemented using a bootstrap architecture for improved dependency management:
1. The canonical implementation of all logging functionality is in `bootstrap/logging_bootstrap.py`
2. This module is imported first in the package's `__init__.py` before any other modules
3. The module has minimal dependencies to avoid import cycles
4. All other modules import logging utilities from the bootstrap module
### Why Bootstrap?
The bootstrap approach solves several problems:
1. **Import Order**: Ensures logging is configured before any other modules are imported
2. **Avoiding Redundancy**: Provides a single canonical implementation of logging functionality
3. **Dependency Management**: Prevents circular imports and configuration issues
4. **Consistency**: Ensures all modules use the same logging setup
### Migration from logging_config.py
For backwards compatibility, `logging_config.py` still exists but now forwards all imports to the bootstrap module. Existing code that imports from `logging_config.py` will continue to work, but new code should import directly from the bootstrap module.
```python
# Preferred for new code
from mcp_server_tree_sitter.bootstrap import get_logger, update_log_levels
# Still supported for backwards compatibility
from mcp_server_tree_sitter.logging_config import get_logger, update_log_levels
```
================================================
FILE: docs/requirements/logging.md
================================================
# Requirements for Correct Logging Behavior in MCP Tree-sitter Server
This document specifies the requirements for implementing correct logging behavior in the MCP Tree-sitter Server, with particular focus on ensuring that environment variables like `MCP_TS_LOG_LEVEL=DEBUG` work as expected.
## Core Requirements
### 1. Environment Variable Processing
- Environment variables MUST be processed before any logging configuration is applied
- The system MUST correctly parse `MCP_TS_LOG_LEVEL` and convert it to the appropriate numeric logging level
- Environment variable values MUST take precedence over hardcoded defaults and other configuration sources
```python
# Example of correct implementation
def get_log_level_from_env() -> int:
env_level = os.environ.get("MCP_TS_LOG_LEVEL", "INFO").upper()
return LOG_LEVEL_MAP.get(env_level, logging.INFO)
```
### 2. Root Logger Configuration
- `logging.basicConfig()` MUST use the level derived from environment variables
- Root logger configuration MUST happen early in the application lifecycle, before other modules are imported
- Root logger handlers MUST be configured with the same level as the logger itself
```python
# Example of correct implementation
def configure_root_logger() -> None:
log_level = get_log_level_from_env()
# Configure the root logger with proper format and level
logging.basicConfig(
level=log_level,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
# Ensure the root logger for our package is also set correctly
pkg_logger = logging.getLogger("mcp_server_tree_sitter")
pkg_logger.setLevel(log_level)
# Ensure all handlers have the correct level
for handler in logging.root.handlers:
handler.setLevel(log_level)
# Ensure propagation is preserved
pkg_logger.propagate = True
```
### 3. Package Logger Hierarchy
- The main package logger (`mcp_server_tree_sitter`) MUST be explicitly set to the level from environment variables
- **DO NOT** explicitly set levels for all individual loggers in the hierarchy unless specifically needed
- Log record propagation MUST be preserved (default `propagate=True`) to ensure messages flow up the hierarchy
- Child loggers SHOULD inherit the effective level from their parents by default
```python
# INCORRECT approach - setting levels for all loggers
def get_logger(name: str) -> logging.Logger:
logger = logging.getLogger(name)
# Setting levels for all package loggers disrupts hierarchy
if name.startswith("mcp_server_tree_sitter"):
logger.setLevel(get_log_level_from_env())
return logger
# CORRECT approach - respecting logger hierarchy
def get_logger(name: str) -> logging.Logger:
logger = logging.getLogger(name)
# Only set the level explicitly for the root package logger
if name == "mcp_server_tree_sitter":
logger.setLevel(get_log_level_from_env())
return logger
```
### 4. Handler Configuration
- Every logger with handlers MUST have those handlers' levels explicitly set to match the logger level
- New handlers created during runtime MUST inherit the appropriate level setting
- Handler formatter configuration MUST be consistent to ensure uniform log output
```python
# Example of correct handler synchronization
def update_handler_levels(logger: logging.Logger, level: int) -> None:
for handler in logger.handlers:
handler.setLevel(level)
```
### 5. Configuration Timing
- Logging configuration MUST occur before any module imports that might create loggers
- Environment variable processing MUST happen at the earliest possible point in the application lifecycle
- Any dynamic reconfiguration MUST update both logger and handler levels simultaneously
### 6. Level Update Mechanism
- When updating log levels, the system MUST update the root package logger level
- The system MUST update handler levels to match their logger levels
- The system SHOULD preserve the propagation setting when updating loggers
```python
# Example of correct level updating
def update_log_levels(level_name: str) -> None:
level_value = LOG_LEVEL_MAP.get(level_name.upper(), logging.INFO)
# Update root package logger
pkg_logger = logging.getLogger("mcp_server_tree_sitter")
pkg_logger.setLevel(level_value)
# Update all handlers on the package logger
for handler in pkg_logger.handlers:
handler.setLevel(level_value)
# Update existing loggers in our package
for name in logging.root.manager.loggerDict:
if name == "mcp_server_tree_sitter" or name.startswith("mcp_server_tree_sitter."):
logger = logging.getLogger(name)
logger.setLevel(level_value)
# Update all handlers for this logger
for handler in logger.handlers:
handler.setLevel(level_value)
# Preserve propagation
logger.propagate = True
```
## Implementation Requirements
### 7. Logging Utility Functions
- Helper functions MUST be provided for creating correctly configured loggers
- Utility functions MUST ensure consistent behavior across different modules
- These utilities MUST respect Python's logging hierarchy where each logger maintains its own level
### 8. Error Handling
- The system MUST handle invalid log level strings in environment variables gracefully
- Default fallback values MUST be used when environment variables are not set
- When importing logging utilities fails, modules SHOULD fall back to standard logging
```python
# Example of robust logger acquisition with fallback
try:
from ..logging_config import get_logger
logger = get_logger(__name__)
except (ImportError, AttributeError):
# Fallback to standard logging
import logging
logger = logging.getLogger(__name__)
```
### 9. Module Structure
- The `logging_config.py` module MUST be designed to be imported before other modules
- The module MUST automatically configure the root logger when imported
- The module MUST provide utility functions for getting loggers and updating levels
## Documentation Requirements
### 10. Documentation
- Documentation MUST explain how to use environment variables to control logging
- Documentation MUST provide examples for common logging configuration scenarios
- Documentation MUST explain the logger hierarchy and level inheritance
- Documentation MUST clarify that log records (not levels) propagate up the hierarchy
## Testing Requirements
### 11. Testing
- Tests MUST verify that environment variables are correctly processed
- Tests MUST verify that logger levels are correctly inherited in the hierarchy
- Tests MUST verify that handler levels are synchronized with logger levels
- Tests MUST verify that log messages flow up the hierarchy as expected
## Expected Behavior
When all these requirements are satisfied, setting `MCP_TS_LOG_LEVEL=DEBUG` will properly increase log verbosity throughout the application, allowing users to see detailed debug information for troubleshooting.
================================================
FILE: docs/tree-sitter-type-safety.md
================================================
# Tree-sitter Type Safety Guide
This document explains our approach to type safety when interfacing with the tree-sitter library and why certain type-checking suppressions are necessary.
## Background
The MCP Tree-sitter Server maintains type safety through Python's type hints and mypy verification. However, when interfacing with external libraries like tree-sitter, we encounter challenges:
1. Tree-sitter's Python bindings have inconsistent API signatures across versions
2. Tree-sitter objects don't always match our protocol definitions
3. The library may work at runtime but fail static type checking
## Type Suppression Strategy
We use targeted `# type: ignore` comments to handle specific scenarios where mypy can't verify correctness, but our runtime code handles the variations properly.
### Examples of Necessary Type Suppressions
#### Parser Interface Variations
Some versions of tree-sitter use `set_language()` while others use `language` as the attribute/method:
```python
try:
parser.set_language(safe_language) # type: ignore
except AttributeError:
if hasattr(parser, 'language'):
# Use the language method if available
parser.language = safe_language # type: ignore
else:
# Fallback to setting the attribute directly
parser.language = safe_language # type: ignore
```
#### Node Handling Safety
For cursor navigation and tree traversal, we need to handle potential `None` values:
```python
def visit(node: Optional[Node], field_name: Optional[str], depth: int) -> bool:
if node is None:
return False
# Continue with node operations...
```
## Guidelines for Using Type Suppressions
1. **Be specific**: Always use `# type: ignore` on the exact line with the issue, not for entire blocks or files
2. **Add comments**: Explain why the suppression is necessary
3. **Try alternatives first**: Only use suppressions after trying to fix the actual type issue
4. **Include runtime checks**: Always pair suppressions with runtime checks (try/except, if hasattr, etc.)
## Our Pattern for Library Compatibility
We follow a consistent pattern for tree-sitter API compatibility:
1. **Define Protocols**: Use Protocol classes to define expected interfaces
2. **Safe Type Casting**: Use wrapper functions like `ensure_node()` to safely cast objects
3. **Feature Detection**: Use `hasattr()` checks before accessing attributes
4. **Fallback Mechanisms**: Provide multiple ways to accomplish the same task
5. **Graceful Degradation**: Handle missing features by providing simplified alternatives
## Testing Approach
Even with type suppressions, we ensure correctness through:
1. Comprehensive test coverage for different tree-sitter operations
2. Tests with and without tree-sitter installed to verify fallback mechanisms
3. Runtime verification of object capabilities before operations
## When to Update Type Suppressions
Review and potentially remove type suppressions when:
1. Upgrading minimum supported tree-sitter version
2. Refactoring the interface to the tree-sitter library
3. Adding new wrapper functions that can handle type variations
4. Improving Protocol definitions to better match runtime behavior
By following these guidelines, we maintain a balance between static type safety and runtime flexibility when working with the tree-sitter library.
================================================
FILE: pyproject.toml
================================================
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "mcp-server-tree-sitter"
version = "0.7.0"
description = "MCP Server for Tree-sitter code analysis"
readme = "README.md"
requires-python = ">=3.10"
license = {text = "MIT"}
authors = [
{name = "Wrale LTD", email = "contact@wrale.com"}
]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dependencies = [
"mcp[cli]>=1.23.0",
"tree-sitter>=0.24.0",
"tree-sitter-language-pack>=0.6.1",
"pyyaml>=6.0",
"pydantic>=2.0.0",
"types-pyyaml>=6.0.12.20241230",
# Transitive dep floors for security (see dependabot alerts)
"h11>=0.16.0",
"starlette>=0.49.1",
"pygments>=2.20.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"pytest-asyncio>=0.23.0",
"pytest-cov>=4.0.0",
"ruff>=0.0.262",
"mypy>=1.2.0",
]
# Language support (now included via tree-sitter-language-pack)
languages = [
# No individual languages needed as tree-sitter-language-pack provides all
]
[project.urls]
"Homepage" = "https://github.com/wrale/mcp-server-tree-sitter"
"Bug Tracker" = "https://github.com/wrale/mcp-server-tree-sitter/issues"
[project.scripts]
mcp-server-tree-sitter = "mcp_server_tree_sitter.server:main"
[tool.hatch.build.targets.wheel]
packages = ["src/mcp_server_tree_sitter"]
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = "test_*.py"
python_classes = "Test*"
python_functions = "test_*"
markers = [
"diagnostic: mark test as producing diagnostic information",
]
[tool.mypy]
python_version = "3.10"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
[[tool.mypy.overrides]]
module = "tree_sitter.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
disallow_incomplete_defs = false
check_untyped_defs = false
warn_return_any = false
warn_no_return = false
[tool.ruff]
line-length = 120
target-version = "py310"
[tool.ruff.lint]
select = ["E", "F", "I", "W", "B"]
================================================
FILE: scripts/implementation-search.sh
================================================
#!/bin/bash
# implementation-search.sh - Script to spot check implementation patterns
# Enable strict mode
set -euo pipefail
# Check if search term is provided
if [ $# -eq 0 ]; then
echo "Usage: $0 <search_term>"
exit 1
fi
# Directories to exclude
EXCLUDE_DIRS=(
".venv"
".git"
"./diagnostic_results"
"./.pytest_cache"
"./.ruff_cache"
"./.mypy_cache"
"./tests/__pycache__"
"./__pycache__"
"./src/mcp_server_tree_sitter/__pycache__"
"./src/*/bootstrap/__pycache__"
"./src/*/__pycache__"
)
# Files to exclude
EXCLUDE_FILES=(
"./.gitignore"
"./TODO.md"
"./FEATURES.md"
)
# Build exclude arguments for grep
EXCLUDE_ARGS=""
for dir in "${EXCLUDE_DIRS[@]}"; do
EXCLUDE_ARGS+="--exclude-dir=${dir} "
done
for file in "${EXCLUDE_FILES[@]}"; do
EXCLUDE_ARGS+="--exclude=${file} "
done
# Run grep with all exclusions
grep -r "${1}" . ${EXCLUDE_ARGS} --binary-files=without-match
================================================
FILE: src/mcp_server_tree_sitter/__init__.py
================================================
"""MCP Server for Tree-sitter - Code analysis capabilities using tree-sitter.
This module provides a Model Context Protocol server that gives LLMs like Claude
intelligent access to codebases with appropriate context management.
"""
# Import bootstrap package first to ensure core services are set up
# before any other modules are imported
from . import bootstrap as bootstrap # noqa: F401 - Import needed for initialization
# Logging is now configured via the bootstrap.logging_bootstrap module
# The bootstrap module automatically calls configure_root_logger() when imported
__version__ = "0.1.0"
================================================
FILE: src/mcp_server_tree_sitter/__main__.py
================================================
"""Main entry point for mcp-server-tree-sitter."""
import argparse
import os
import sys
from .bootstrap import get_logger, update_log_levels
from .config import load_config
from .context import global_context
from .server import mcp
# Get a properly configured logger
logger = get_logger(__name__)
def main() -> int:
"""Run the server with optional arguments."""
# Parse command line arguments
parser = argparse.ArgumentParser(description="MCP Tree-sitter Server - Code analysis with tree-sitter")
parser.add_argument("--config", help="Path to configuration file")
parser.add_argument("--debug", action="store_true", help="Enable debug logging")
parser.add_argument("--disable-cache", action="store_true", help="Disable parse tree caching")
parser.add_argument("--version", action="store_true", help="Show version and exit")
args = parser.parse_args()
# Handle version display
if args.version:
import importlib.metadata
try:
version = importlib.metadata.version("mcp-server-tree-sitter")
print(f"mcp-server-tree-sitter version {version}")
except importlib.metadata.PackageNotFoundError:
print("mcp-server-tree-sitter (version unknown - package not installed)")
return 0
# Set up logging level
if args.debug:
# Set environment variable first for consistency
os.environ["MCP_TS_LOG_LEVEL"] = "DEBUG"
# Then update log levels
update_log_levels("DEBUG")
logger.debug("Debug logging enabled")
# Load configuration
try:
config = load_config(args.config)
# Update global context with config
if args.config:
global_context.config_manager.load_from_file(args.config)
else:
# Update individual settings from config
global_context.config_manager.update_value("cache.enabled", config.cache.enabled)
global_context.config_manager.update_value("cache.max_size_mb", config.cache.max_size_mb)
global_context.config_manager.update_value("security.max_file_size_mb", config.security.max_file_size_mb)
global_context.config_manager.update_value("language.default_max_depth", config.language.default_max_depth)
logger.debug("Configuration loaded successfully")
except Exception as e:
logger.error(f"Error loading configuration: {e}")
return 1
# Run the server
try:
logger.info("Starting MCP Tree-sitter Server (with state persistence)")
mcp.run()
except KeyboardInterrupt:
logger.info("Server stopped by user")
except Exception as e:
logger.error(f"Error running server: {e}")
return 1
return 0
if __name__ == "__main__":
sys.exit(main())
================================================
FILE: src/mcp_server_tree_sitter/api.py
================================================
"""API functions for accessing container dependencies.
This module provides function-based access to dependencies managed by the
container, helping to break circular import chains and simplify access.
"""
import logging
from typing import Any, Dict, List, Optional
from .di import get_container
from .exceptions import ProjectError
logger = logging.getLogger(__name__)
def get_project_registry() -> Any:
"""Get the project registry."""
return get_container().project_registry
def get_language_registry() -> Any:
"""Get the language registry."""
return get_container().language_registry
def get_tree_cache() -> Any:
"""Get the tree cache."""
return get_container().tree_cache
def get_config() -> Any:
"""Get the current configuration."""
return get_container().get_config()
def get_config_manager() -> Any:
"""Get the configuration manager."""
return get_container().config_manager
def register_project(path: str, name: Optional[str] = None, description: Optional[str] = None) -> Dict[str, Any]:
"""Register a project."""
project_registry = get_project_registry()
language_registry = get_language_registry()
try:
# Register project
project = project_registry.register_project(name or path, path, description)
# Scan for languages
project.scan_files(language_registry)
project_dict = project.to_dict()
# Add type annotations
result: Dict[str, Any] = {
"name": project_dict["name"],
"root_path": project_dict["root_path"],
"description": project_dict["description"],
"languages": project_dict["languages"],
"last_scan_time": project_dict["last_scan_time"],
}
return result
except Exception as e:
raise ProjectError(f"Failed to register project: {e}") from e
def list_projects() -> List[Dict[str, Any]]:
"""List all registered projects."""
projects_list = get_project_registry().list_projects()
# Convert to explicitly typed list
result: List[Dict[str, Any]] = []
for project in projects_list:
result.append(
{
"name": project["name"],
"root_path": project["root_path"],
"description": project["description"],
"languages": project["languages"],
"last_scan_time": project["last_scan_time"],
}
)
return result
def remove_project(name: str) -> Dict[str, str]:
"""Remove a registered project."""
get_project_registry().remove_project(name)
return {"status": "success", "message": f"Project '{name}' removed"}
def clear_cache(project: Optional[str] = None, file_path: Optional[str] = None) -> Dict[str, str]:
"""Clear the parse tree cache."""
tree_cache = get_tree_cache()
if project and file_path:
# Get file path
project_registry = get_project_registry()
project_obj = project_registry.get_project(project)
abs_path = project_obj.get_file_path(file_path)
# Clear cache
tree_cache.invalidate(abs_path)
return {"status": "success", "message": f"Cache cleared for {file_path} in {project}"}
else:
# Clear all
tree_cache.invalidate()
return {"status": "success", "message": "Cache cleared"}
================================================
FILE: src/mcp_server_tree_sitter/bootstrap/__init__.py
================================================
"""Bootstrap package for early initialization dependencies.
This package contains modules that should be imported and initialized before
any other modules in the project to ensure proper setup of core services.
"""
# Import logging bootstrap module to ensure it's available
from . import logging_bootstrap
# Export key functions for convenience
from .logging_bootstrap import get_log_level_from_env, get_logger, update_log_levels
__all__ = ["get_logger", "update_log_levels", "get_log_level_from_env", "logging_bootstrap"]
================================================
FILE: src/mcp_server_tree_sitter/bootstrap/logging_bootstrap.py
================================================
"""Bootstrap module for logging configuration with minimal dependencies.
This module is imported first in the initialization sequence to ensure logging
is configured before any other modules are imported. It has no dependencies
on other modules in the project to avoid import cycles.
This is the CANONICAL implementation of logging configuration. If you need to
modify how logging is configured, make changes here and nowhere else.
"""
import logging
import os
from typing import Dict, Union
# Numeric values corresponding to log level names
LOG_LEVEL_MAP: Dict[str, int] = {
"DEBUG": logging.DEBUG,
"INFO": logging.INFO,
"WARNING": logging.WARNING,
"ERROR": logging.ERROR,
"CRITICAL": logging.CRITICAL,
}
def get_log_level_from_env() -> int:
"""
Get log level from environment variable MCP_TS_LOG_LEVEL.
Returns:
int: Logging level value (e.g., logging.DEBUG, logging.INFO)
"""
env_level = os.environ.get("MCP_TS_LOG_LEVEL", "INFO").upper()
return LOG_LEVEL_MAP.get(env_level, logging.INFO)
def configure_root_logger() -> None:
"""
Configure the root logger based on environment variables.
This should be called at the earliest possible point in the application.
"""
log_level = get_log_level_from_env()
# Configure the root logger with proper format and level
logging.basicConfig(level=log_level, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
# Ensure the root logger for our package is also set correctly
pkg_logger = logging.getLogger("mcp_server_tree_sitter")
pkg_logger.setLevel(log_level)
# Ensure all handlers have the correct level
for handler in logging.root.handlers:
handler.setLevel(log_level)
# Ensure propagation is preserved
pkg_logger.propagate = True
# Ensure all existing loggers' handlers are synchronized
for name in logging.root.manager.loggerDict:
if name.startswith("mcp_server_tree_sitter"):
logger = logging.getLogger(name)
# Only synchronize handler levels, don't set logger level
for handler in logger.handlers:
handler.setLevel(logger.getEffectiveLevel())
def update_log_levels(level_name: Union[str, int]) -> None:
"""
Update the root package logger level and synchronize handler levels.
This function sets the level of the root package logger only. Child loggers
will inherit this level unless they have their own explicit level settings.
Handler levels are updated to match their logger's effective level.
Args:
level_name: Log level name (DEBUG, INFO, etc.) or numeric value
"""
# Convert string level name to numeric value if needed
if isinstance(level_name, str):
level_value = LOG_LEVEL_MAP.get(level_name.upper(), logging.INFO)
else:
level_value = level_name
# Update ONLY the root package logger level
pkg_logger = logging.getLogger("mcp_server_tree_sitter")
pkg_logger.setLevel(level_value)
# Update all handlers on the root package logger
for handler in pkg_logger.handlers:
handler.setLevel(level_value)
# Also update the root logger for consistency - this helps with debug flag handling
# when the module is already imported
root_logger = logging.getLogger()
root_logger.setLevel(level_value)
for handler in root_logger.handlers:
handler.setLevel(level_value)
# Synchronize handler levels with their logger's effective level
# for all existing loggers in our package hierarchy
for name in logging.root.manager.loggerDict:
if name == "mcp_server_tree_sitter" or name.startswith("mcp_server_tree_sitter."):
logger = logging.getLogger(name)
# DO NOT set the logger's level explicitly to maintain hierarchy
# Only synchronize handler levels with the logger's effective level
for handler in logger.handlers:
handler.setLevel(logger.getEffectiveLevel())
# Ensure propagation is preserved
logger.propagate = True
def get_logger(name: str) -> logging.Logger:
"""
Get a properly configured logger with appropriate level.
Args:
name: Logger name, typically __name__
Returns:
logging.Logger: Configured logger
"""
logger = logging.getLogger(name)
# Only set level explicitly for the root package logger
# Child loggers will inherit levels as needed
if name == "mcp_server_tree_sitter":
log_level = get_log_level_from_env()
logger.setLevel(log_level)
# Ensure all handlers have the correct level
for handler in logger.handlers:
handler.setLevel(log_level)
else:
# For child loggers, ensure handlers match their effective level
# without setting the logger level explicitly
effective_level = logger.getEffectiveLevel()
for handler in logger.handlers:
handler.setLevel(effective_level)
# Ensure propagation is enabled
logger.propagate = True
return logger
================================================
FILE: src/mcp_server_tree_sitter/cache/__init__.py
================================================
"""Cache components for MCP server."""
================================================
FILE: src/mcp_server_tree_sitter/cache/parser_cache.py
================================================
"""Caching system for tree-sitter parse trees."""
import logging
import threading
import time
from functools import lru_cache
from pathlib import Path
from typing import Any, Dict, Optional, Tuple
# Import global_context at runtime to avoid circular imports
from ..utils.tree_sitter_types import (
Parser,
Tree,
ensure_language,
ensure_parser,
ensure_tree,
)
logger = logging.getLogger(__name__)
class TreeCache:
"""Cache for parsed syntax trees."""
def __init__(self, max_size_mb: Optional[int] = None, ttl_seconds: Optional[int] = None):
"""Initialize the tree cache with explicit size and TTL settings."""
self.cache: Dict[str, Tuple[Any, bytes, float]] = {} # (tree, source, timestamp)
self.lock = threading.RLock()
self.current_size_bytes = 0
self.modified_trees: Dict[str, bool] = {}
self.max_size_mb = max_size_mb or 100
self.ttl_seconds = ttl_seconds or 300
self.enabled = True
def _get_cache_key(self, file_path: Path, language: str) -> str:
"""Generate cache key from file path and language."""
return f"{language}:{str(file_path)}:{file_path.stat().st_mtime}"
def set_enabled(self, enabled: bool) -> None:
"""Set whether caching is enabled."""
self.enabled = enabled
def set_max_size_mb(self, max_size_mb: int) -> None:
"""Set maximum cache size in MB."""
self.max_size_mb = max_size_mb
def set_ttl_seconds(self, ttl_seconds: int) -> None:
"""Set TTL for cache entries in seconds."""
self.ttl_seconds = ttl_seconds
def _get_max_size_mb(self) -> float:
"""Get current max size setting."""
# Always get the latest from container config
try:
from ..di import get_container
config = get_container().get_config()
return config.cache.max_size_mb if self.enabled else 0 # Return 0 if disabled
except (ImportError, AttributeError):
# Fallback to instance value if container unavailable
return self.max_size_mb
def _get_ttl_seconds(self) -> int:
"""Get current TTL setting."""
# Always get the latest from container config
try:
from ..di import get_container
config = get_container().get_config()
return config.cache.ttl_seconds
except (ImportError, AttributeError):
# Fallback to instance value if container unavailable
return self.ttl_seconds
def _is_cache_enabled(self) -> bool:
"""Check if caching is enabled."""
# Honor both local setting and container config
try:
from ..di import get_container
config = get_container().get_config()
is_enabled = self.enabled and config.cache.enabled
# For very small caches, log the state
if not is_enabled:
logger.debug(
f"Cache disabled: self.enabled={self.enabled}, config.cache.enabled={config.cache.enabled}"
)
return is_enabled
except (ImportError, AttributeError):
# Fallback to instance value if container unavailable
return self.enabled
def get(self, file_path: Path, language: str) -> Optional[Tuple[Tree, bytes]]:
"""
Get cached tree if available and not expired.
Args:
file_path: Path to the source file
language: Language identifier
Returns:
Tuple of (tree, source_bytes) if cached, None otherwise
"""
# Check if caching is enabled
if not self._is_cache_enabled():
return None
try:
cache_key = self._get_cache_key(file_path, language)
except (FileNotFoundError, OSError):
return None
with self.lock:
if cache_key in self.cache:
tree, source, timestamp = self.cache[cache_key]
# Check if cache entry has expired (using current config TTL)
ttl_seconds = self._get_ttl_seconds()
current_time = time.time()
entry_age = current_time - timestamp
if entry_age > ttl_seconds:
logger.debug(f"Cache entry expired: age={entry_age:.2f}s, ttl={ttl_seconds}s")
del self.cache[cache_key]
# Approximate size reduction
self.current_size_bytes -= len(source)
if cache_key in self.modified_trees:
del self.modified_trees[cache_key]
return None
# Cast to the correct type for type checking
safe_tree = ensure_tree(tree)
return safe_tree, source
return None
def put(self, file_path: Path, language: str, tree: Tree, source: bytes) -> None:
"""
Cache a parsed tree.
Args:
file_path: Path to the source file
language: Language identifier
tree: Parsed tree
source: Source bytes
"""
# Check if caching is enabled
is_enabled = self._is_cache_enabled()
if not is_enabled:
logger.debug(f"Skipping cache for {file_path}: caching is disabled")
return
try:
cache_key = self._get_cache_key(file_path, language)
except (FileNotFoundError, OSError):
return
source_size = len(source)
# Check if adding this entry would exceed cache size limit (using current max size)
max_size_mb = self._get_max_size_mb()
max_size_bytes = max_size_mb * 1024 * 1024
# If max_size is 0 or very small, disable caching
if max_size_bytes <= 1024: # If less than 1KB, don't cache
logger.debug(f"Cache size too small: {max_size_mb}MB, skipping cache")
return
if source_size > max_size_bytes:
logger.warning(f"File too large to cache: {file_path} ({source_size / (1024 * 1024):.2f}MB)")
return
with self.lock:
# If entry already exists, subtract its size
if cache_key in self.cache:
_, old_source, _ = self.cache[cache_key]
self.current_size_bytes -= len(old_source)
else:
# If we need to make room for a new entry, remove oldest entries
if self.current_size_bytes + source_size > max_size_bytes:
self._evict_entries(source_size)
# Store the new entry
self.cache[cache_key] = (tree, source, time.time())
self.current_size_bytes += source_size
logger.debug(
f"Added entry to cache: {file_path}, size: {source_size / 1024:.1f}KB, "
f"total cache: {self.current_size_bytes / (1024 * 1024):.2f}MB"
)
# Mark as not modified (fresh parse)
self.modified_trees[cache_key] = False
def mark_modified(self, file_path: Path, language: str) -> None:
"""
Mark a tree as modified for tracking changes.
Args:
file_path: Path to the source file
language: Language identifier
"""
try:
cache_key = self._get_cache_key(file_path, language)
with self.lock:
if cache_key in self.cache:
self.modified_trees[cache_key] = True
except (FileNotFoundError, OSError):
pass
def is_modified(self, file_path: Path, language: str) -> bool:
"""
Check if a tree has been modified since last parse.
Args:
file_path: Path to the source file
language: Language identifier
Returns:
True if the tree has been modified, False otherwise
"""
try:
cache_key = self._get_cache_key(file_path, language)
with self.lock:
return self.modified_trees.get(cache_key, False)
except (FileNotFoundError, OSError):
return False
def update_tree(self, file_path: Path, language: str, tree: Tree, source: bytes) -> None:
"""
Update a cached tree after modification.
Args:
file_path: Path to the source file
language: Language identifier
tree: Updated parsed tree
source: Updated source bytes
"""
try:
cache_key = self._get_cache_key(file_path, language)
except (FileNotFoundError, OSError):
return
with self.lock:
if cache_key in self.cache:
_, old_source, _ = self.cache[cache_key]
# Update size tracking
self.current_size_bytes -= len(old_source)
self.current_size_bytes += len(source)
# Update cache entry
self.cache[cache_key] = (tree, source, time.time())
# Reset modified flag
self.modified_trees[cache_key] = False
else:
# If not already in cache, just add it
self.put(file_path, language, tree, source)
def _evict_entries(self, required_bytes: int) -> None:
"""
Evict entries to make room for new data.
Args:
required_bytes: Number of bytes to make room for
"""
# Get current max size from config
max_size_mb = self._get_max_size_mb()
max_size_bytes = max_size_mb * 1024 * 1024
# Check if we actually need to evict anything
if self.current_size_bytes + required_bytes <= max_size_bytes:
return
# If cache is empty (happens in tests sometimes), nothing to evict
if not self.cache:
return
# Sort by timestamp (oldest first)
sorted_entries = sorted(self.cache.items(), key=lambda item: item[1][2])
bytes_freed = 0
entries_removed = 0
# Force removal of at least one entry in tests with very small caches (< 0.1MB)
force_removal = max_size_mb < 0.1
target_to_free = required_bytes
# If cache is small, make sure we remove at least one item
min_entries_to_remove = 1
# If cache is very small, removing any entry should be enough
if force_removal or max_size_bytes < 10 * 1024: # Less than 10KB
# For tests with very small caches, we need to be more aggressive
target_to_free = self.current_size_bytes // 2 # Remove half the cache
min_entries_to_remove = max(1, len(self.cache) // 2)
logger.debug(f"Small cache detected ({max_size_mb}MB), removing {min_entries_to_remove} entries")
# If cache is already too full, free more space to prevent continuous evictions
elif self.current_size_bytes > max_size_bytes * 0.9:
target_to_free += int(max_size_bytes * 0.2) # Free extra 20%
min_entries_to_remove = max(1, len(self.cache) // 4)
for key, (_, source, _) in sorted_entries:
# Remove entry
del self.cache[key]
if key in self.modified_trees:
del self.modified_trees[key]
entry_size = len(source)
bytes_freed += entry_size
self.current_size_bytes -= entry_size
entries_removed += 1
# Stop once we've freed enough space AND removed minimum entries
if bytes_freed >= target_to_free and entries_removed >= min_entries_to_remove:
break
# Log the eviction with appropriate level
log_msg = (
f"Evicted {entries_removed} cache entries, freed {bytes_freed / 1024:.1f}KB, "
f"current size: {self.current_size_bytes / (1024 * 1024):.2f}MB"
)
if force_removal:
logger.debug(log_msg)
else:
logger.info(log_msg)
def invalidate(self, file_path: Optional[Path] = None) -> None:
"""
Invalidate cache entries.
Args:
file_path: If provided, invalidate only entries for this file.
If None, invalidate the entire cache.
"""
with self.lock:
if file_path is None:
# Clear entire cache
self.cache.clear()
self.modified_trees.clear()
self.current_size_bytes = 0
else:
# Clear only entries for this file
keys_to_remove = [key for key in self.cache if str(file_path) in key]
for key in keys_to_remove:
_, source, _ = self.cache[key]
self.current_size_bytes -= len(source)
del self.cache[key]
if key in self.modified_trees:
del self.modified_trees[key]
# The TreeCache is now initialized and managed by the DependencyContainer in di.py
# No global instance is needed here anymore.
# The following function is maintained for backward compatibility
def get_tree_cache() -> TreeCache:
"""Get the tree cache from the dependency container."""
from ..di import get_container
tree_cache = get_container().tree_cache
return tree_cache
@lru_cache(maxsize=32)
def get_cached_parser(language: Any) -> Parser:
"""Get a cached parser for a language."""
parser = Parser()
safe_language = ensure_language(language)
# Try both set_language and language methods
try:
parser.set_language(safe_language) # type: ignore
except AttributeError:
if hasattr(parser, "language"):
# Use the language method if available
parser.language = safe_language # type: ignore
else:
# Fallback to setting the attribute directly
parser.language = safe_language # type: ignore
return ensure_parser(parser)
================================================
FILE: src/mcp_server_tree_sitter/capabilities/__init__.py
================================================
"""MCP capability declarations."""
from .server_capabilities import register_capabilities
__all__ = ["register_capabilities"]
================================================
FILE: src/mcp_server_tree_sitter/capabilities/server_capabilities.py
================================================
"""Server capability declarations for MCP integration."""
import logging
from typing import Any, Dict, List
logger = logging.getLogger(__name__)
def register_capabilities(mcp_server: Any) -> None:
"""
Register MCP server capabilities.
Args:
mcp_server: MCP server instance
"""
# Use dependency injection instead of global context
from ..di import get_container
# Get container and dependencies
container = get_container()
config_manager = container.config_manager
config = config_manager.get_config()
# FastMCP may not have capability method, so we'll skip this for now
# @mcp_server.capability("prompts.listChanged")
def handle_prompts_list_changed() -> Dict[str, Any]:
"""Handle prompt template management events."""
logger.debug("Received prompts.listChanged event")
return {"status": "success"}
# @mcp_server.capability("resources.subscribe")
def handle_resources_subscribe(resource_uri: str) -> Dict[str, Any]:
"""
Handle resource subscription requests.
Args:
resource_uri: Resource URI to subscribe to
Returns:
Subscription response
"""
logger.debug(f"Received subscription request for {resource_uri}")
return {"status": "success", "resource": resource_uri}
# @mcp_server.capability("resources.listChanged")
def handle_resources_list_changed() -> Dict[str, Any]:
"""Handle resource discovery events."""
logger.debug("Received resources.listChanged event")
return {"status": "success"}
# @mcp_server.capability("tools.listChanged")
def handle_tools_list_changed() -> Dict[str, Any]:
"""Handle tool discovery events."""
logger.debug("Received tools.listChanged event")
return {"status": "success"}
# @mcp_server.capability("logging")
def handle_logging(level: str, message: str) -> Dict[str, Any]:
"""
Handle logging configuration.
Args:
level: Log level
message: Log message
Returns:
Logging response
"""
log_levels = {
"debug": logging.DEBUG,
"info": logging.INFO,
"warning": logging.WARNING,
"error": logging.ERROR,
}
log_level = log_levels.get(level.lower(), logging.INFO)
logger.log(log_level, f"MCP: {message}")
return {"status": "success"}
# @mcp_server.capability("completion")
def handle_completion(text: str, position: int) -> Dict[str, Any]:
"""
Handle argument completion suggestions.
Args:
text: Current input text
position: Cursor position in text
Returns:
Completion suggestions
"""
# Simple completion for commonly used arguments
suggestions: List[Dict[str, str]] = []
# Extract the current word being typed
current_word = ""
i = position - 1
while i >= 0 and text[i].isalnum() or text[i] == "_":
current_word = text[i] + current_word
i -= 1
# Project name suggestions
if current_word and "project" in text[:position].lower():
# Use container's project registry
project_registry = container.project_registry
for project_dict in project_registry.list_projects():
project_name = project_dict["name"]
if project_name.startswith(current_word):
suggestions.append(
{
"text": project_name,
"description": f"Project: {project_name}",
}
)
# Language suggestions
if current_word and "language" in text[:position].lower():
# Use container's language registry
language_registry = container.language_registry
for language in language_registry.list_available_languages():
if language.startswith(current_word):
suggestions.append({"text": language, "description": f"Language: {language}"})
# Config suggestions
if current_word and "config" in text[:position].lower():
if "cache_enabled".startswith(current_word):
suggestions.append(
{
"text": "cache_enabled",
"description": f"Cache enabled: {config.cache.enabled}",
}
)
if "max_file_size_mb".startswith(current_word):
# Store in variable to avoid line length error
size_mb = config.security.max_file_size_mb
suggestions.append(
{
"text": "max_file_size_mb",
"description": f"Max file size: {size_mb} MB",
}
)
if "log_level".startswith(current_word):
suggestions.append(
{
"text": "log_level",
"description": f"Log level: {config.log_level}",
}
)
return {"suggestions": suggestions}
# Ensure capabilities are accessible to tests
if hasattr(mcp_server, "capabilities"):
mcp_server.capabilities["logging"] = handle_logging
mcp_server.capabilities["completion"] = handle_completion
================================================
FILE: src/mcp_server_tree_sitter/config.py
================================================
"""Configuration management with explicit manager class.
Environment variables can be used to override configuration settings with the following format:
- MCP_TS_SECTION_SETTING - For section settings (e.g., MCP_TS_CACHE_MAX_SIZE_MB)
- MCP_TS_SETTING - For top-level settings (e.g., MCP_TS_LOG_LEVEL)
The precedence order for configuration is:
1. Environment variables (highest)
2. Explicit updates via update_value()
3. YAML configuration from file
4. Default values (lowest)
"""
import logging
import os
from pathlib import Path
from typing import Any, Dict, List, Optional, Union
import yaml
from pydantic import BaseModel, Field
# Import logging from bootstrap package
from .bootstrap import get_logger, update_log_levels
logger = get_logger(__name__)
class CacheConfig(BaseModel):
"""Configuration for caching behavior."""
enabled: bool = True
max_size_mb: int = 100
ttl_seconds: int = 300 # Time-to-live for cached items
class SecurityConfig(BaseModel):
"""Security settings."""
max_file_size_mb: int = 5
excluded_dirs: List[str] = Field(
default_factory=lambda: [".git", "node_modules", "__pycache__", ".venv", "venv", ".tox"]
)
allowed_extensions: Optional[List[str]] = None # None means all extensions allowed
class LanguageConfig(BaseModel):
"""Language-specific configuration."""
auto_install: bool = False # DEPRECATED: No longer used with tree-sitter-language-pack
default_max_depth: int = 5 # Default depth for AST traversal
preferred_languages: List[str] = Field(default_factory=list)
class ServerConfig(BaseModel):
"""Main server configuration."""
cache: CacheConfig = Field(default_factory=CacheConfig)
security: SecurityConfig = Field(default_factory=SecurityConfig)
language: LanguageConfig = Field(default_factory=LanguageConfig)
log_level: str = "INFO"
max_results_default: int = 100
@classmethod
def from_file(cls, path: str) -> "ServerConfig":
"""Load configuration from YAML file."""
logger = logging.getLogger(__name__)
config_path = Path(path)
if not config_path.exists():
logger.warning(f"Config file does not exist: {path}")
return cls()
try:
with open(config_path, "r") as f:
file_content = f.read()
logger.debug(f"YAML File content:\n{file_content}")
config_data = yaml.safe_load(file_content)
logger.debug(f"Loaded config data: {config_data}")
if config_data is None:
logger.warning(f"Config file is empty or contains only comments: {path}")
return cls()
# Create config from file
config = cls(**config_data)
# Apply environment variables on top of file config
update_config_from_env(config)
return config
except Exception as e:
logger.error(f"Error loading configuration from {path}: {e}")
import traceback
logger.debug(traceback.format_exc())
return cls()
@classmethod
def from_env(cls) -> "ServerConfig":
"""Load configuration from environment variables."""
config = cls()
update_config_from_env(config)
return config
def update_config_from_env(config: ServerConfig) -> None:
"""Update configuration from environment variables.
Supports two formats:
MCP_TS_CACHE__MAX_SIZE_MB (double underscore = explicit section separator)
MCP_TS_CACHE_MAX_SIZE_MB (single underscore = greedy first-part match)
Args:
config: The ServerConfig object to update with environment variables
"""
logger = logging.getLogger(__name__)
env_prefix = "MCP_TS_"
# Get all environment variables with our prefix
env_vars = {k: v for k, v in os.environ.items() if k.startswith(env_prefix)}
# Process the environment variables
for env_name, env_value in env_vars.items():
# Remove the prefix
key = env_name[len(env_prefix) :]
logger.debug(f"Processing environment variable: {env_name}, key after prefix removal: {key}")
# Double underscore format (MCP_TS_CACHE__MAX_SIZE_MB) — unambiguous
if "__" in key:
dparts = key.lower().split("__", 1)
section = dparts[0]
setting = dparts[1]
logger.debug(f"Double underscore format: section={section}, setting={setting}")
else:
# Single underscore format (MCP_TS_CACHE_MAX_SIZE_MB) — greedy first-part match
parts = key.lower().split("_")
if len(parts) > 1 and hasattr(config, parts[0]):
section = parts[0]
setting = "_".join(parts[1:])
logger.debug(f"Single underscore format: section={section}, setting={setting}")
else:
section = None
setting = key.lower()
logger.debug(f"Top-level setting: {setting}")
# Apply the setting to the configuration
if section is None:
# Top-level setting
if hasattr(config, setting):
orig_value = getattr(config, setting)
new_value = _convert_value(env_value, orig_value)
setattr(config, setting, new_value)
logger.debug(f"Applied environment variable {env_name} to {setting}: {orig_value} -> {new_value}")
else:
logger.warning(f"Unknown top-level setting in environment variable {env_name}: {setting}")
elif hasattr(config, section):
# Section setting
section_obj = getattr(config, section)
if hasattr(section_obj, setting):
# Convert the value to the appropriate type
orig_value = getattr(section_obj, setting)
new_value = _convert_value(env_value, orig_value)
setattr(section_obj, setting, new_value)
logger.debug(
f"Applied environment variable {env_name} to {section}.{setting}: {orig_value} -> {new_value}"
)
else:
logger.warning(f"Unknown setting {setting} in section {section} from environment variable {env_name}")
def _convert_value(value_str: str, current_value: Any) -> Any:
"""Convert string value from environment variable to the appropriate type.
Args:
value_str: The string value from the environment variable
current_value: The current value to determine the type
Returns:
The converted value with the appropriate type, or the original value if conversion fails
"""
logger = logging.getLogger(__name__)
# Handle different types
try:
if isinstance(current_value, bool):
return value_str.lower() in ("true", "yes", "1", "y", "t", "on")
elif isinstance(current_value, int):
return int(value_str)
elif isinstance(current_value, float):
return float(value_str)
elif isinstance(current_value, list):
# Convert comma-separated string to list
return [item.strip() for item in value_str.split(",")]
else:
# Default to string
return value_str
except (ValueError, TypeError) as e:
# If conversion fails, log a warning and return the original value
logger.warning(f"Failed to convert value '{value_str}' to type {type(current_value).__name__}: {e}")
return current_value
class ConfigurationManager:
"""Manages server configuration without relying on global variables."""
def __init__(self, initial_config: Optional[ServerConfig] = None):
"""Initialize with optional initial configuration.
Auto-discovers and loads YAML config from MCP_TS_CONFIG_PATH env var
or the default platform path (~/.config/tree-sitter/config.yaml).
Environment variables are applied last to ensure highest precedence.
"""
self._config = initial_config or ServerConfig()
self._logger = logging.getLogger(__name__)
# Auto-discover and load YAML config from env var or default path
config_path = os.environ.get("MCP_TS_CONFIG_PATH")
if config_path:
path_to_load: Optional[Path] = Path(config_path)
else:
path_to_load = get_default_config_path()
if path_to_load and path_to_load.exists():
self._logger.info(f"Auto-loading configuration from {path_to_load}")
try:
new_config = ServerConfig.from_file(str(path_to_load))
update_config_from_new(self._config, new_config)
except Exception as e:
self._logger.error(f"Error auto-loading configuration from {path_to_load}: {e}")
# Apply environment variables (highest precedence)
update_config_from_env(self._config)
def get_config(self) -> ServerConfig:
"""Get the current configuration."""
return self._config
def load_from_file(self, path: Union[str, Path]) -> ServerConfig:
"""Load configuration from a YAML file."""
self._logger.info(f"Loading configuration from file: {path}")
config_path = Path(path)
# Log more information for debugging
self._logger.info(f"Absolute path: {config_path.absolute()}")
self._logger.info(f"Path exists: {config_path.exists()}")
if not config_path.exists():
self._logger.error(f"Config file does not exist: {path}")
return self._config
try:
with open(config_path, "r") as f:
file_content = f.read()
self._logger.info(f"YAML File content:\n{file_content}")
# Check if file content is empty
if not file_content.strip():
self._logger.error(f"Config file is empty: {path}")
return self._config
# Try to parse YAML
config_data = yaml.safe_load(file_content)
self._logger.info(f"YAML parsing successful? {config_data is not None}")
self._logger.info(f"Loaded config data: {config_data}")
if config_data is None:
self._logger.error(f"Config file is empty or contains only comments: {path}")
return self._config
# Debug output before update
self._logger.info(
f"Before update: cache.max_size_mb = {self._config.cache.max_size_mb}, "
f"security.max_file_size_mb = {self._config.security.max_file_size_mb}"
)
# Better error handling for invalid YAML data
if not isinstance(config_data, dict):
self._logger.error(f"YAML data is not a dictionary: {type(config_data)}")
return self._config
# Log the YAML structure
self._logger.info(f"YAML structure: {list(config_data.keys()) if config_data else 'None'}")
# Create new config from file data
try:
new_config = ServerConfig(**config_data)
# Debug output for new config
self._logger.info(
f"New config: cache.max_size_mb = {new_config.cache.max_size_mb}, "
f"security.max_file_size_mb = {new_config.security.max_file_size_mb}"
)
except Exception as e:
self._logger.error(f"Error creating ServerConfig from YAML data: {e}")
return self._config
# Instead of simply replacing config object, use update_config_from_new to ensure
# all attributes are copied correctly (similar to how load_config function works)
update_config_from_new(self._config, new_config)
# Debug output after update
self._logger.info(
f"After update: cache.max_size_mb = {self._config.cache.max_size_mb}, "
f"security.max_file_size_mb = {self._config.security.max_file_size_mb}"
)
# Apply environment variables AFTER loading YAML
# This ensures environment variables have highest precedence
self._logger.info("Applying environment variables to override YAML settings")
update_config_from_env(self._config)
# Log after applying environment variables to show final state
self._logger.info(
f"After applying env vars: cache.max_size_mb = {self._config.cache.max_size_mb}, "
f"security.max_file_size_mb = {self._config.security.max_file_size_mb}"
)
# Apply configuration to dependencies
try:
from .di import get_container
container = get_container()
# Update tree cache settings
self._logger.info(
f"Setting tree cache: enabled={self._config.cache.enabled}, "
f"size={self._config.cache.max_size_mb}MB, ttl={self._config.cache.ttl_seconds}s"
)
container.tree_cache.set_enabled(self._config.cache.enabled)
container.tree_cache.set_max_size_mb(self._config.cache.max_size_mb)
container.tree_cache.set_ttl_seconds(self._config.cache.ttl_seconds)
# Update logging configuration using centralized bootstrap module
update_log_levels(self._config.log_level)
self._logger.debug(f"Applied log level {self._config.log_level} to mcp_server_tree_sitter loggers")
self._logger.info("Applied configuration to dependencies")
except (ImportError, AttributeError) as e:
self._logger.warning(f"Could not apply config to dependencies: {e}")
self._logger.info(f"Successfully loaded configuration from {path}")
return self._config
except Exception as e:
self._logger.error(f"Error loading configuration from {path}: {e}")
import traceback
self._logger.error(traceback.format_exc())
return self._config
def update_value(self, path: str, value: Any) -> None:
"""Update a specific configuration value by dot-notation path."""
parts = path.split(".")
# Store original value for logging
old_value = None
# Handle two levels deep for now (e.g., "cache.max_size_mb")
if len(parts) == 2:
section, key = parts
if hasattr(self._config, section):
section_obj = getattr(self._config, section)
if hasattr(section_obj, key):
old_value = getattr(section_obj, key)
setattr(section_obj, key, value)
self._logger.debug(f"Updated config value {path} from {old_value} to {value}")
else:
self._logger.warning(f"Unknown config key: {key} in section {section}")
else:
self._logger.warning(f"Unknown config section: {section}")
else:
# Handle top-level attributes
if hasattr(self._config, path):
old_value = getattr(self._config, path)
setattr(self._config, path, value)
self._logger.debug(f"Updated config value {path} from {old_value} to {value}")
# If updating log_level, apply it using centralized bootstrap function
if path == "log_level":
# Use centralized bootstrap module
update_log_levels(value)
self._logger.debug(f"Applied log level {value} to mcp_server_tree_sitter loggers")
else:
self._logger.warning(f"Unknown config path: {path}")
# After direct updates, ensure environment variables still have precedence
# by reapplying them - this ensures consistency in the precedence model
# Environment variables > Explicit updates > YAML > Defaults
update_config_from_env(self._config)
def to_dict(self) -> Dict[str, Any]:
"""Convert configuration to a dictionary."""
return {
"cache": {
"enabled": self._config.cache.enabled,
"max_size_mb": self._config.cache.max_size_mb,
"ttl_seconds": self._config.cache.ttl_seconds,
},
"security": {
"max_file_size_mb": self._config.security.max_file_size_mb,
"excluded_dirs": self._config.security.excluded_dirs,
},
"language": {
"auto_install": self._config.language.auto_install,
"default_max_depth": self._config.language.default_max_depth,
},
"log_level": self._config.log_level,
}
# We've removed the global CONFIG instance to eliminate global state and
# potential concurrency issues. All code should now use either:
# 1. The context's config_manager.get_config() method
# 2. A locally instantiated ServerConfig object
# 3. Configuration passed as function parameters
def get_default_config_path() -> Optional[Path]:
"""Get the default configuration file path based on the platform."""
import platform
if platform.system() == "Windows":
config_dir = Path(os.environ.get("USERPROFILE", "")) / ".config" / "tree-sitter"
else:
config_dir = Path(os.environ.get("HOME", "")) / ".config" / "tree-sitter"
config_path = config_dir / "config.yaml"
if config_path.exists():
return config_path
return None
def update_config_from_new(original: ServerConfig, new: ServerConfig) -> None:
"""Update the original config with values from the new config."""
logger = logging.getLogger(__name__)
# Log before values
logger.info(
f"[update_config_from_new] Before: cache.max_size_mb={original.cache.max_size_mb}, "
f"security.max_file_size_mb={original.security.max_file_size_mb}"
)
logger.info(
f"[update_config_from_new] New values: cache.max_size_mb={new.cache.max_size_mb}, "
f"security.max_file_size_mb={new.security.max_file_size_mb}"
)
# Update all attributes, copying collections to avoid reference issues
try:
# Cache settings
original.cache.enabled = new.cache.enabled
original.cache.max_size_mb = new.cache.max_size_mb
original.cache.ttl_seconds = new.cache.ttl_seconds
# Security settings
original.security.max_file_size_mb = new.security.max_file_size_mb
original.security.excluded_dirs = new.security.excluded_dirs.copy()
if new.security.allowed_extensions:
original.security.allowed_extensions = new.security.allowed_extensions.copy()
else:
original.security.allowed_extensions = None
# Language settings
original.language.auto_install = new.language.auto_install
original.language.default_max_depth = new.language.default_max_depth
original.language.preferred_languages = new.language.preferred_languages.copy()
# Other settings
original.log_level = new.log_level
original.max_results_default = new.max_results_default
# Log after values to confirm update succeeded
logger.info(
f"[update_config_from_new] After: cache.max_size_mb={original.cache.max_size_mb}, "
f"security.max_file_size_mb={original.security.max_file_size_mb}"
)
except Exception as e:
logger.error(f"Error updating config: {e}")
# Ensure at least some values get updated
try:
original.cache.max_size_mb = new.cache.max_size_mb
original.security.max_file_size_mb = new.security.max_file_size_mb
original.language.default_max_depth = new.language.default_max_depth
logger.info("Fallback update succeeded with basic values")
except Exception as e2:
logger.error(f"Fallback update also failed: {e2}")
def load_config(config_path: Optional[str] = None) -> ServerConfig:
"""Load and initialize configuration.
Args:
config_path: Path to YAML config file
Returns:
ServerConfig: The loaded configuration
"""
logger = logging.getLogger(__name__)
logger.info(f"load_config called with config_path={config_path}")
# Create a new config instance
config = ServerConfig()
# Determine which config path to use
path_to_load = None
if config_path:
# Use explicitly provided path
path_to_load = Path(config_path)
elif os.environ.get("MCP_TS_CONFIG_PATH"):
# Use path from environment variable
config_path_env = os.environ.get("MCP_TS_CONFIG_PATH")
if config_path_env is not None:
path_to_load = Path(config_path_env)
else:
# Try to use default config path
default_path = get_default_config_path()
if default_path:
path_to_load = default_path
logger.info(f"Using default configuration from {path_to_load}")
# Load configuration from the determined path
if path_to_load and path_to_load.exists():
try:
logger.info(f"Loading configuration from file: {path_to_load}")
with open(path_to_load, "r") as f:
content = f.read()
logger.debug(f"File content:\n{content}")
if not content.strip():
logger.warning("Config file is empty")
# Continue to apply environment variables below
else:
# Load new configuration
logger.info(f"Loading configuration from {str(path_to_load)}")
new_config = ServerConfig.from_file(str(path_to_load))
# Debug output before update
logger.info(
f"New configuration loaded: cache.max_size_mb = {new_config.cache.max_size_mb}, "
f"security.max_file_size_mb = {new_config.security.max_file_size_mb}"
)
# Update the config by copying all attributes
update_config_from_new(config, new_config)
# Debug output after update
logger.info(f"Successfully loaded configuration from {path_to_load}")
logger.debug(
f"Updated config: cache.max_size_mb = {config.cache.max_size_mb}, "
f"security.max_file_size_mb = {config.security.max_file_size_mb}"
)
except Exception as e:
logger.error(f"Error loading configuration from {path_to_load}: {e}")
import traceback
logger.debug(traceback.format_exc())
# Apply environment variables to configuration
# This ensures that environment variables have the highest precedence
# regardless of whether a config file was found
update_config_from_env(config)
logger.info(
f"Final configuration: cache.max_size_mb = {config.cache.max_size_mb}, "
f"security.max_file_size_mb = {config.security.max_file_size_mb}"
)
return config
================================================
FILE: src/mcp_server_tree_sitter/context.py
================================================
"""Context class for managing dependency injection.
This module provides a ServerContext class to manage dependencies
and provide a cleaner interface for interacting with the application's
components while supporting dependency injection.
"""
from typing import Any, Dict, List, Optional
# Import logging from bootstrap package
from .bootstrap import get_logger, update_log_levels
from .cache.parser_cache import TreeCache
from .config import ConfigurationManager, ServerConfig
from .di import get_container
from .exceptions import ProjectError
from .language.registry import LanguageRegistry
from .models.project import ProjectRegistry
logger = get_logger(__name__)
class ServerContext:
"""Context for managing application state with dependency injection."""
def __init__(
self,
config_manager: Optional[ConfigurationManager] = None,
project_registry: Optional[ProjectRegistry] = None,
language_registry: Optional[LanguageRegistry] = None,
tree_cache: Optional[TreeCache] = None,
):
"""
Initialize with optional components.
If components are not provided, they will be fetched from the global container.
"""
container = get_container()
self.config_manager = config_manager or container.config_manager
self.project_registry = project_registry or container.project_registry
self.language_registry = language_registry or container.language_registry
self.tree_cache = tree_cache or container.tree_cache
def get_config(self) -> ServerConfig:
"""Get the current configuration."""
return self.config_manager.get_config()
# Project management methods
def register_project(
self, path: str, name: Optional[str] = None, description: Optional[str] = None
) -> Dict[str, Any]:
"""Register a project for code analysis."""
try:
# Register project
project = self.project_registry.register_project(name or path, path, description)
# Scan for languages
project.scan_files(self.language_registry)
return project.to_dict()
except Exception as e:
raise ProjectError(f"Failed to register project: {e}") from e
def list_projects(self) -> List[Dict[str, Any]]:
"""List all registered projects."""
return self.project_registry.list_projects()
def remove_project(self, name: str) -> Dict[str, str]:
"""Remove a registered project."""
self.project_registry.remove_project(name)
return {"status": "success", "message": f"Project '{name}' removed"}
# Cache management methods
def clear_cache(self, project: Optional[str] = None, file_path: Optional[str] = None) -> Dict[str, str]:
"""Clear the parse tree cache."""
if project and file_path:
# Get file path
project_obj = self.project_registry.get_project(project)
abs_path = project_obj.get_file_path(file_path)
# Clear cache
self.tree_cache.invalidate(abs_path)
return {"status": "success", "message": f"Cache cleared for {file_path} in {project}"}
else:
# Clear all
self.tree_cache.invalidate()
return {"status": "success", "message": "Cache cleared"}
# Configuration management methods
def configure(
self,
config_path: Optional[str] = None,
cache_enabled: Optional[bool] = None,
max_file_size_mb: Optional[int] = None,
log_level: Optional[str] = None,
) -> Dict[str, Any]:
"""Configure the server."""
# Load config if path provided
if config_path:
logger.info(f"Configuring server with YAML config from: {config_path}")
self.config_manager.load_from_file(config_path)
# Update specific settings
if cache_enabled is not None:
logger.info(f"Setting cache.enabled to {cache_enabled}")
self.config_manager.update_value("cache.enabled", cache_enabled)
self.tree_cache.set_enabled(cache_enabled)
if max_file_size_mb is not None:
logger.info(f"Setting security.max_file_size_mb to {max_file_size_mb}")
self.config_manager.update_value("security.max_file_size_mb", max_file_size_mb)
if log_level is not None:
logger.info(f"Setting log_level to {log_level}")
self.config_manager.update_value("log_level", log_level)
# Apply log level using centralized bootstrap function
update_log_levels(log_level)
logger.debug(f"Applied log level {log_level} to mcp_server_tree_sitter loggers")
# Return current config as dict
return self.config_manager.to_dict()
# Create a global context instance for convenience
global_context = ServerContext()
def get_global_context() -> ServerContext:
"""Get the global server context."""
return global_context
================================================
FILE: src/mcp_server_tree_sitter/di.py
================================================
"""Dependency injection container for MCP Tree-sitter Server.
This module provides a central container for managing all application dependencies,
replacing the global variables and singletons previously used throughout the codebase.
"""
from typing import Any, Dict
# Import logging from bootstrap package
from .bootstrap import get_logger
from .cache.parser_cache import TreeCache
from .config import ConfigurationManager, ServerConfig
from .language.registry import LanguageRegistry
from .models.project import ProjectRegistry
logger = get_logger(__name__)
class DependencyContainer:
"""Container for all application dependencies."""
def __init__(self) -> None:
"""Initialize container with all core dependencies."""
logger.debug("Initializing dependency container")
# Create core dependencies
self.config_manager = ConfigurationManager()
self._config = self.config_manager.get_config()
self.project_registry = ProjectRegistry()
self.language_registry = LanguageRegistry()
self.tree_cache = TreeCache(
max_size_mb=self._config.cache.max_size_mb, ttl_seconds=self._config.cache.ttl_seconds
)
# Pre-load preferred languages after all dependencies are created
# This avoids circular import issues during LanguageRegistry initialization
self.language_registry.preload_languages(self._config)
# Storage for any additional dependencies
self._additional: Dict[str, Any] = {}
def get_config(self) -> ServerConfig:
"""Get the current configuration."""
# Always get the latest from the config manager
config = self.config_manager.get_config()
return config
def register_dependency(self, name: str, instance: Any) -> None:
"""Register an additional dependency."""
self._additional[name] = instance
def get_dependency(self, name: str) -> Any:
"""Get a registered dependency."""
return self._additional.get(name)
# Create the single container instance - this will be the ONLY global
container = DependencyContainer()
def get_container() -> DependencyContainer:
"""Get the dependency container."""
return container
================================================
FILE: src/mcp_server_tree_sitter/exceptions.py
================================================
"""Exception classes for mcp-server-tree-sitter."""
class MCPTreeSitterError(Exception):
"""Base exception for mcp-server-tree-sitter."""
pass
class LanguageError(MCPTreeSitterError):
"""Errors related to tree-sitter languages."""
pass
class LanguageNotFoundError(LanguageError):
"""Raised when a language parser is not available."""
pass
class LanguageInstallError(LanguageError):
"""Raised when language installation fails."""
pass
class ParsingError(MCPTreeSitterError):
"""Errors during parsing."""
pass
class ProjectError(MCPTreeSitterError):
"""Errors related to project management."""
pass
class FileAccessError(MCPTreeSitterError):
"""Errors accessing project files."""
pass
class QueryError(MCPTreeSitterError):
"""Errors related to tree-sitter queries."""
pass
class SecurityError(MCPTreeSitterError):
"""Security-related errors."""
pass
class CacheError(MCPTreeSitterError):
"""Errors related to caching."""
pass
================================================
FILE: src/mcp_server_tree_sitter/language/__init__.py
================================================
"""Language handling components for MCP server."""
================================================
FILE: src/mcp_server_tree_sitter/language/query_templates.py
================================================
"""Query templates for common code patterns by language."""
from typing import Any, Dict, List, Optional, Union
from .templates import QUERY_TEMPLATES
def get_query_template(language: str, template_name: str) -> Optional[str]:
"""
Get a query template for a language.
Args:
language: Language identifier
template_name: Template name
Returns:
Query string or None if not found
"""
language_templates = QUERY_TEMPLATES.get(language)
if language_templates:
return language_templates.get(template_name)
return None
def list_query_templates(language: Optional[Union[str, List[str]]] = None) -> Dict[str, Any]:
"""
List available query templates.
Args:
language: Optional language or list of languages to filter by
Returns:
Dictionary of templates by language
"""
if language:
if isinstance(language, str):
languages = [lang.strip() for lang in language.split(",")]
else:
languages = language
return {lang: QUERY_TEMPLATES.get(lang, {}) for lang in languages}
return QUERY_TEMPLATES
================================================
FILE: src/mcp_server_tree_sitter/language/registry.py
================================================
"""Language registry for tree-sitter languages."""
import logging
import threading
from typing import Any, Dict, List, Optional, Tuple
from tree_sitter_language_pack import get_language, get_parser
from ..config import ServerConfig
# Import parser_cache functions inside methods to avoid circular imports
# Import global_context inside methods to avoid circular imports
from ..exceptions import LanguageNotFoundError
from ..utils.tree_sitter_types import (
Language,
Parser,
ensure_language,
)
logger = logging.getLogger(__name__)
class LanguageRegistry:
"""Manages tree-sitter language parsers."""
def __init__(self) -> None:
"""Initialize the registry."""
self._lock = threading.RLock()
self.languages: Dict[str, Language] = {}
self._language_map = {
"py": "python",
"js": "javascript",
"ts": "typescript",
"jsx": "javascript",
"tsx": "typescript",
"rb": "ruby",
"rs": "rust",
"go": "go",
"java": "java",
"c": "c",
"cpp": "cpp",
"cc": "cpp",
"h": "c",
"hpp": "cpp",
"cs": "csharp",
"php": "php",
"scala": "scala",
"swift": "swift",
"dart": "dart",
"kt": "kotlin",
"lua": "lua",
"hs": "haskell",
"ml": "ocaml",
"sh": "bash",
"yaml": "yaml",
"yml": "yaml",
"json": "json",
"md": "markdown",
"html": "html",
"css": "css",
"scss": "scss",
"sass": "scss",
"sql": "sql",
"proto": "proto",
"elm": "elm",
"clj": "clojure",
"ex": "elixir",
"exs": "elixir",
}
def preload_languages(self, config: ServerConfig) -> None:
"""
Pre-load preferred languages from configuration.
This method should be called after the dependency container is fully
initialized to avoid circular import issues.
Args:
config: Server configuration containing language preferences
"""
for lang in config.language.preferred_languages:
try:
self.get_language(lang)
except Exception as e:
logger.warning(f"Failed to pre-load language {lang}: {e}")
def language_for_file(self, file_path: str) -> Optional[str]:
"""
Detect language from file extension.
Args:
file_path: Path to the file
Returns:
Language identifier or None if unknown
"""
ext = file_path.split(".")[-1].lower() if "." in file_path else ""
return self._language_map.get(ext)
def list_available_languages(self) -> List[str]:
"""
List languages that are available via tree-sitter-language-pack.
Returns:
List of available language identifiers
"""
# Start with loaded languages
available = set(self.languages.keys())
# Add all mappable languages from our extension map
# These correspond to the languages available in tree-sitter-language-pack
available.update(set(self._language_map.values()))
# Add frequently used languages that might not be in the map
common_languages = [
"python",
"javascript",
"typescript",
"java",
"c",
"cpp",
"go",
"rust",
"ruby",
"php",
"swift",
"kotlin",
"scala",
"bash",
"html",
"css",
"json",
"yaml",
"markdown",
"csharp",
"objective_c",
"xml",
]
available.update(common_languages)
# Return as a sorted list
return sorted(available)
def list_installable_languages(self) -> List[Tuple[str, str]]:
"""
List languages that can be installed.
With tree-sitter-language-pack, no additional installation is needed.
Returns:
Empty list (all languages are available via language-pack)
"""
return []
def is_language_available(self, language_name: str) -> bool:
"""
Check if a language is available in tree-sitter-language-pack.
Args:
language_name: Language identifier
Returns:
True if language is available
"""
try:
self.get_language(language_name)
return True
except Exception:
return False
def get_language(self, language_name: str) -> Any:
"""
Get or load a language by name from tree-sitter-language-pack.
Args:
language_name: Language identifier
Returns:
Tree-sitter Language object
Raises:
LanguageNotFoundError: If language cannot be loaded
"""
with self._lock:
if language_name in self.languages:
return self.languages[language_name]
try:
# Get language from language pack
# Type ignore: language_name is dynamic but tree-sitter-language-pack
# types expect a Literal with specific language names
language_obj = get_language(language_name) # type: ignore
# Cast to our Language type for type safety
language = ensure_language(language_obj)
self.languages[language_name] = language
return language
except Exception as e:
raise LanguageNotFoundError(
f"Language {language_name} not available via tree-sitter-language-pack: {e}"
) from e
def get_parser(self, language_name: str) -> Parser:
"""
Get a parser for the specified language.
Args:
language_name: Language identifier
Returns:
Tree-sitter Parser configured for the language
"""
try:
# Try to get a parser directly from the language pack
# Type ignore: language_name is dynamic but tree-sitter-language-pack
# types expect a Literal with specific language names
parser = get_parser(language_name) # type: ignore
return parser
except Exception:
# Fall back to older method, importing at runtime to avoid circular imports
from ..cache.parser_cache import get_cached_parser
language = self.get_language(language_name)
return get_cached_parser(language)
================================================
FILE: src/mcp_server_tree_sitter/language/templates/__init__.py
================================================
"""Language-specific query templates collection."""
from typing import Dict
from . import (
apl,
c,
cpp,
dart,
go,
java,
javascript,
julia,
kotlin,
python,
rust,
swift,
typescript,
)
# Combine all language templates
QUERY_TEMPLATES: Dict[str, Dict[str, str]] = {
"python": python.TEMPLATES,
"javascript": javascript.TEMPLATES,
"typescript": typescript.TEMPLATES,
"go": go.TEMPLATES,
"rust": rust.TEMPLATES,
"c": c.TEMPLATES,
"cpp": cpp.TEMPLATES,
"dart": dart.TEMPLATES,
"swift": swift.TEMPLATES,
"java": java.TEMPLATES,
"kotlin": kotlin.TEMPLATES,
"julia": julia.TEMPLATES,
"apl": apl.TEMPLATES,
}
================================================
FILE: src/mcp_server_tree_sitter/language/templates/apl.py
================================================
"""Query templates for APL language."""
TEMPLATES = {
"functions": """
(function_definition
name: (identifier) @function.name
body: (block) @function.body) @function.def
""",
"namespaces": """
(namespace_declaration
name: (identifier) @namespace.name) @namespace.def
""",
"variables": """
(assignment
left: (identifier) @variable.name) @variable.def
""",
"imports": """
(import_statement
module: (identifier) @import.module) @import
""",
"operators": """
(operator_definition
operator: (_) @operator.sym
body: (block) @operator.body) @operator.def
""",
"classes": """
(class_definition
name: (identifier) @class.name
body: (block) @class.body) @class.def
""",
}
================================================
FILE: src/mcp_server_tree_sitter/language/templates/c.py
================================================
"""Query templates for C language."""
TEMPLATES = {
"functions": """
(function_definition
declarator: (function_declarator
declarator: (identifier) @function.name)) @function.def
(declaration
declarator: (function_declarator
declarator: (identifier) @function.name)) @function.decl
""",
"structs": """
(struct_specifier
name: (type_identifier) @struct.name) @struct.def
(union_specifier
name: (type_identifier) @union.name) @union.def
(enum_specifier
name: (type_identifier) @enum.name) @enum.def
""",
"imports": """
(preproc_include) @import
(preproc_include
path: (string_literal) @import.system) @import.system
(preproc_include
path: (system_lib_string) @import.system) @import.system
""",
"macros": """
(preproc_function_def
name: (identifier) @macro.name) @macro.def
""",
}
================================================
FILE: src/mcp_server_tree_sitter/language/templates/cpp.py
================================================
"""Query templates for C++ language."""
TEMPLATES = {
"functions": """
(function_definition
declarator: (function_declarator
declarator: (identifier) @function.name)) @function.def
(declaration
declarator: (function_declarator
declarator: (identifier) @function.name)) @function.decl
(method_definition
declarator: (function_declarator
declarator: (field_identifier) @method.name)) @method.def
""",
"classes": """
(class_specifier
name: (type_identifier) @class.name) @class.def
""",
"structs": """
(struct_specifier
name: (type_identifier) @struct.name) @struct.def
(union_specifier
name: (type_identifier) @union.name) @union.def
(enum_specifier
name: (type_identifier) @enum.name) @enum.def
""",
"imports": """
(preproc_include) @import
(preproc_include
path: (string_literal) @import.path) @import.user
(preproc_include
path: (system_lib_string) @import.path) @import.system
(namespace_definition
name: (namespace_identifier) @import.namespace) @import.namespace_def
""",
"templates": """
(template_declaration) @template.def
(template_declaration
declaration: (class_specifier
name: (type_identifier) @template.class)) @template.class_def
""",
}
================================================
FILE: src/mcp_server_tree_sitter/language/templates/dart.py
================================================
"""Query templates for Dart language."""
TEMPLATES = {
"functions": """
(program
(function_signature
name: (identifier) @function.name) @function.def)
""",
"classes": """
(class_definition
name: (identifier) @class.name) @class.def
(class_definition
name: (identifier) @class.name
body: (class_body) @class.body) @class.def
""",
"imports": """
(import_or_export
(library_import
(import_specification) @import.spec)) @import
(import_or_export
(library_export) @export) @export.stmt
(part_directive) @part
(part_of_directive) @part_of
""",
"enums": """
(enum_declaration
name: (identifier) @enum.name) @enum.def
(enum_declaration
name: (identifier) @enum.name
body: (enum_body) @enum.body) @enum.def
""",
"mixins": """
(mixin_declaration
(identifier) @mixin.name) @mixin.def
(mixin_declaration
(identifier) @mixin.name
(class_body) @mixin.body) @mixin.def
""",
"extensions": """
(extension_declaration
(identifier) @extension.name) @extension.def
(extension_declaration
(identifier) @extension.name
body: (extension_body) @extension.body) @extension.def
""",
"typedefs": """
(type_alias
. (type_identifier) @typedef.name) @typedef.def
""",
}
================================================
FILE: src/mcp_server_tree_sitter/language/templates/go.py
================================================
"""Query templates for Go."""
TEMPLATES = {
"functions": """
(function_declaration
name: (identifier) @function.name
parameters: (parameter_list) @function.params
body: (block) @function.body) @function.def
(method_declaration
name: (field_identifier) @method.name
parameters: (parameter_list) @method.params
body: (block) @method.body) @method.def
""",
"structs": """
(type_declaration
(type_spec
name: (type_identifier) @struct.name
type: (struct_type) @struct.body)) @struct.def
(type_declaration
(type_spec
name: (type_identifier) @type.name
type: (_) @type.body)) @type.def
""",
"imports": """
(import_declaration) @import
(import_declaration
(import_spec_list
(import_spec) @import.spec)) @import.list
(import_declaration
(import_spec_list
(import_spec
path: (_) @import.path))) @import.path_list
(import_declaration
(import_spec
path: (_) @import.path)) @import.single
""",
"interfaces": """
(type_declaration
(type_spec
name: (type_identifier) @interface.name
type: (interface_type) @interface.body)) @interface.def
""",
}
================================================
FILE: src/mcp_server_tree_sitter/language/templates/java.py
================================================
"""Query templates for Java language."""
TEMPLATES = {
"functions": """
(method_declaration
name: (identifier) @function.name
parameters: (formal_parameters) @function.params
body: (block) @function.body) @function.def
(constructor_declaration
name: (identifier) @constructor.name
parameters: (formal_parameters) @constructor.params
body: (block) @constructor.body) @constructor.def
""",
"classes": """
(class_declaration
name: (identifier) @class.name
body: (class_body) @class.body) @class.def
""",
"interfaces": """
(interface_declaration
name: (identifier) @interface.name
body: (class_body) @interface.body) @interface.def
""",
"imports": """
(import_declaration) @import
(import_declaration
name: (qualified_name) @import.name) @import.qualified
(import_declaration
name: (qualified_name
name: (identifier) @import.class)) @import.class
(import_declaration
asterisk: "*") @import.wildcard
""",
"annotations": """
(annotation
name: (identifier) @annotation.name) @annotation
(annotation_type_declaration
name: (identifier) @annotation.type_name) @annotation.type
""",
"enums": """
(enum_declaration
name: (identifier) @enum.name
body: (enum_body) @enum.body) @enum.def
""",
}
================================================
FILE: src/mcp_server_tree_sitter/language/templates/javascript.py
================================================
"""Query templates for JavaScript."""
TEMPLATES = {
"functions": """
(function_declaration
name: (identifier) @function.name
parameters: (formal_parameters) @function.params
body: (statement_block) @function.body) @function.def
(arrow_function
parameters: (formal_parameters) @function.params
body: (_) @function.body) @function.def
""",
"classes": """
(class_declaration
name: (identifier) @class.name
body: (class_body) @class.body) @class.def
""",
"imports": """
(import_statement) @import
(import_statement
source: (string) @import.source
specifier: (_) @import.specifier) @import.full
""",
"function_calls": """
(call_expression
function: (identifier) @call.function
arguments: (arguments) @call.args) @call
""",
"assignments": """
(variable_declarator
name: (_) @assign.target
value: (_) @assign.val
gitextract_2grt6c54/
├── .codestateignore
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── release.yml
├── .gitignore
├── .python-version
├── AGENTS.md
├── CONTRIBUTING.md
├── FEATURES.md
├── LICENSE
├── Makefile
├── NOTICE
├── README.md
├── ROADMAP.md
├── TODO.md
├── docs/
│ ├── architecture.md
│ ├── cli.md
│ ├── config.md
│ ├── diagnostics.md
│ ├── logging.md
│ ├── requirements/
│ │ └── logging.md
│ └── tree-sitter-type-safety.md
├── pyproject.toml
├── scripts/
│ └── implementation-search.sh
├── src/
│ └── mcp_server_tree_sitter/
│ ├── __init__.py
│ ├── __main__.py
│ ├── api.py
│ ├── bootstrap/
│ │ ├── __init__.py
│ │ └── logging_bootstrap.py
│ ├── cache/
│ │ ├── __init__.py
│ │ └── parser_cache.py
│ ├── capabilities/
│ │ ├── __init__.py
│ │ └── server_capabilities.py
│ ├── config.py
│ ├── context.py
│ ├── di.py
│ ├── exceptions.py
│ ├── language/
│ │ ├── __init__.py
│ │ ├── query_templates.py
│ │ ├── registry.py
│ │ └── templates/
│ │ ├── __init__.py
│ │ ├── apl.py
│ │ ├── c.py
│ │ ├── cpp.py
│ │ ├── dart.py
│ │ ├── go.py
│ │ ├── java.py
│ │ ├── javascript.py
│ │ ├── julia.py
│ │ ├── kotlin.py
│ │ ├── python.py
│ │ ├── rust.py
│ │ ├── swift.py
│ │ └── typescript.py
│ ├── logging_config.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── ast.py
│ │ ├── ast_cursor.py
│ │ └── project.py
│ ├── prompts/
│ │ ├── __init__.py
│ │ └── code_patterns.py
│ ├── server.py
│ ├── testing/
│ │ ├── __init__.py
│ │ └── pytest_diagnostic.py
│ ├── tools/
│ │ ├── __init__.py
│ │ ├── analysis.py
│ │ ├── ast_operations.py
│ │ ├── debug.py
│ │ ├── file_operations.py
│ │ ├── project.py
│ │ ├── query_builder.py
│ │ ├── registration.py
│ │ └── search.py
│ └── utils/
│ ├── __init__.py
│ ├── context/
│ │ ├── __init__.py
│ │ └── mcp_context.py
│ ├── file_io.py
│ ├── path.py
│ ├── security.py
│ ├── tree_sitter_helpers.py
│ └── tree_sitter_types.py
└── tests/
├── .gitignore
├── __init__.py
├── conftest.py
├── test_ast_cursor.py
├── test_basic.py
├── test_cache_config.py
├── test_cli_arguments.py
├── test_config_behavior.py
├── test_config_manager.py
├── test_context.py
├── test_debug_flag.py
├── test_di.py
├── test_diagnostics/
│ ├── __init__.py
│ ├── test_ast.py
│ ├── test_ast_parsing.py
│ ├── test_cursor_ast.py
│ ├── test_language_pack.py
│ ├── test_language_registry.py
│ └── test_unpacking_errors.py
├── test_env_config.py
├── test_failure_modes.py
├── test_file_operations.py
├── test_find_similar_code.py
├── test_helpers.py
├── test_language_listing.py
├── test_logging_bootstrap.py
├── test_logging_config.py
├── test_logging_config_di.py
├── test_logging_early_init.py
├── test_logging_env_vars.py
├── test_logging_handlers.py
├── test_makefile_targets.py
├── test_mcp_context.py
├── test_models_ast.py
├── test_persistent_server.py
├── test_project_persistence.py
├── test_query_result_handling.py
├── test_registration.py
├── test_rust_compatibility.py
├── test_server.py
├── test_server_capabilities.py
├── test_smoke.py
├── test_symbol_extraction.py
├── test_tree_sitter_helpers.py
├── test_yaml_config.py
└── test_yaml_config_di.py
SYMBOL INDEX (559 symbols across 74 files)
FILE: src/mcp_server_tree_sitter/__main__.py
function main (line 16) | def main() -> int:
FILE: src/mcp_server_tree_sitter/api.py
function get_project_registry (line 16) | def get_project_registry() -> Any:
function get_language_registry (line 21) | def get_language_registry() -> Any:
function get_tree_cache (line 26) | def get_tree_cache() -> Any:
function get_config (line 31) | def get_config() -> Any:
function get_config_manager (line 36) | def get_config_manager() -> Any:
function register_project (line 41) | def register_project(path: str, name: Optional[str] = None, description:...
function list_projects (line 67) | def list_projects() -> List[Dict[str, Any]]:
function remove_project (line 85) | def remove_project(name: str) -> Dict[str, str]:
function clear_cache (line 91) | def clear_cache(project: Optional[str] = None, file_path: Optional[str] ...
FILE: src/mcp_server_tree_sitter/bootstrap/logging_bootstrap.py
function get_log_level_from_env (line 25) | def get_log_level_from_env() -> int:
function configure_root_logger (line 36) | def configure_root_logger() -> None:
function update_log_levels (line 66) | def update_log_levels(level_name: Union[str, int]) -> None:
function get_logger (line 113) | def get_logger(name: str) -> logging.Logger:
FILE: src/mcp_server_tree_sitter/cache/parser_cache.py
class TreeCache (line 22) | class TreeCache:
method __init__ (line 25) | def __init__(self, max_size_mb: Optional[int] = None, ttl_seconds: Opt...
method _get_cache_key (line 35) | def _get_cache_key(self, file_path: Path, language: str) -> str:
method set_enabled (line 39) | def set_enabled(self, enabled: bool) -> None:
method set_max_size_mb (line 43) | def set_max_size_mb(self, max_size_mb: int) -> None:
method set_ttl_seconds (line 47) | def set_ttl_seconds(self, ttl_seconds: int) -> None:
method _get_max_size_mb (line 51) | def _get_max_size_mb(self) -> float:
method _get_ttl_seconds (line 63) | def _get_ttl_seconds(self) -> int:
method _is_cache_enabled (line 75) | def _is_cache_enabled(self) -> bool:
method get (line 93) | def get(self, file_path: Path, language: str) -> Optional[Tuple[Tree, ...
method put (line 136) | def put(self, file_path: Path, language: str, tree: Tree, source: byte...
method mark_modified (line 193) | def mark_modified(self, file_path: Path, language: str) -> None:
method is_modified (line 209) | def is_modified(self, file_path: Path, language: str) -> bool:
method update_tree (line 227) | def update_tree(self, file_path: Path, language: str, tree: Tree, sour...
method _evict_entries (line 256) | def _evict_entries(self, required_bytes: int) -> None:
method invalidate (line 325) | def invalidate(self, file_path: Optional[Path] = None) -> None:
function get_tree_cache (line 355) | def get_tree_cache() -> TreeCache:
function get_cached_parser (line 364) | def get_cached_parser(language: Any) -> Parser:
FILE: src/mcp_server_tree_sitter/capabilities/server_capabilities.py
function register_capabilities (line 9) | def register_capabilities(mcp_server: Any) -> None:
FILE: src/mcp_server_tree_sitter/config.py
class CacheConfig (line 28) | class CacheConfig(BaseModel):
class SecurityConfig (line 36) | class SecurityConfig(BaseModel):
class LanguageConfig (line 46) | class LanguageConfig(BaseModel):
class ServerConfig (line 54) | class ServerConfig(BaseModel):
method from_file (line 64) | def from_file(cls, path: str) -> "ServerConfig":
method from_env (line 99) | def from_env(cls) -> "ServerConfig":
function update_config_from_env (line 106) | def update_config_from_env(config: ServerConfig) -> None:
function _convert_value (line 171) | def _convert_value(value_str: str, current_value: Any) -> Any:
class ConfigurationManager (line 203) | class ConfigurationManager:
method __init__ (line 206) | def __init__(self, initial_config: Optional[ServerConfig] = None):
method get_config (line 234) | def get_config(self) -> ServerConfig:
method load_from_file (line 238) | def load_from_file(self, path: Union[str, Path]) -> ServerConfig:
method update_value (line 352) | def update_value(self, path: str, value: Any) -> None:
method to_dict (line 393) | def to_dict(self) -> Dict[str, Any]:
function get_default_config_path (line 420) | def get_default_config_path() -> Optional[Path]:
function update_config_from_new (line 437) | def update_config_from_new(original: ServerConfig, new: ServerConfig) ->...
function load_config (line 492) | def load_config(config_path: Optional[str] = None) -> ServerConfig:
FILE: src/mcp_server_tree_sitter/context.py
class ServerContext (line 22) | class ServerContext:
method __init__ (line 25) | def __init__(
method get_config (line 43) | def get_config(self) -> ServerConfig:
method register_project (line 48) | def register_project(
method list_projects (line 63) | def list_projects(self) -> List[Dict[str, Any]]:
method remove_project (line 67) | def remove_project(self, name: str) -> Dict[str, str]:
method clear_cache (line 73) | def clear_cache(self, project: Optional[str] = None, file_path: Option...
method configure (line 89) | def configure(
function get_global_context (line 128) | def get_global_context() -> ServerContext:
FILE: src/mcp_server_tree_sitter/di.py
class DependencyContainer (line 19) | class DependencyContainer:
method __init__ (line 22) | def __init__(self) -> None:
method get_config (line 42) | def get_config(self) -> ServerConfig:
method register_dependency (line 48) | def register_dependency(self, name: str, instance: Any) -> None:
method get_dependency (line 52) | def get_dependency(self, name: str) -> Any:
function get_container (line 61) | def get_container() -> DependencyContainer:
FILE: src/mcp_server_tree_sitter/exceptions.py
class MCPTreeSitterError (line 4) | class MCPTreeSitterError(Exception):
class LanguageError (line 10) | class LanguageError(MCPTreeSitterError):
class LanguageNotFoundError (line 16) | class LanguageNotFoundError(LanguageError):
class LanguageInstallError (line 22) | class LanguageInstallError(LanguageError):
class ParsingError (line 28) | class ParsingError(MCPTreeSitterError):
class ProjectError (line 34) | class ProjectError(MCPTreeSitterError):
class FileAccessError (line 40) | class FileAccessError(MCPTreeSitterError):
class QueryError (line 46) | class QueryError(MCPTreeSitterError):
class SecurityError (line 52) | class SecurityError(MCPTreeSitterError):
class CacheError (line 58) | class CacheError(MCPTreeSitterError):
FILE: src/mcp_server_tree_sitter/language/query_templates.py
function get_query_template (line 8) | def get_query_template(language: str, template_name: str) -> Optional[str]:
function list_query_templates (line 25) | def list_query_templates(language: Optional[Union[str, List[str]]] = Non...
FILE: src/mcp_server_tree_sitter/language/registry.py
class LanguageRegistry (line 23) | class LanguageRegistry:
method __init__ (line 26) | def __init__(self) -> None:
method preload_languages (line 71) | def preload_languages(self, config: ServerConfig) -> None:
method language_for_file (line 87) | def language_for_file(self, file_path: str) -> Optional[str]:
method list_available_languages (line 100) | def list_available_languages(self) -> List[str]:
method list_installable_languages (line 144) | def list_installable_languages(self) -> List[Tuple[str, str]]:
method is_language_available (line 154) | def is_language_available(self, language_name: str) -> bool:
method get_language (line 170) | def get_language(self, language_name: str) -> Any:
method get_parser (line 202) | def get_parser(self, language_name: str) -> Parser:
FILE: src/mcp_server_tree_sitter/models/ast.py
function node_to_dict (line 18) | def node_to_dict(
function summarize_node (line 46) | def summarize_node(node: Any, source_bytes: Optional[bytes] = None) -> D...
function find_node_at_position (line 87) | def find_node_at_position(root_node: Any, row: int, column: int) -> Opti...
function extract_node_path (line 112) | def extract_node_path(
FILE: src/mcp_server_tree_sitter/models/ast_cursor.py
function node_to_dict_cursor (line 12) | def node_to_dict_cursor(
FILE: src/mcp_server_tree_sitter/models/project.py
class Project (line 13) | class Project:
method __init__ (line 16) | def __init__(self, name: str, path: Path, description: Optional[str] =...
method to_dict (line 24) | def to_dict(self) -> Dict[str, Any]:
method scan_files (line 34) | def scan_files(self, language_registry: Any, force: bool = False) -> D...
method get_file_path (line 92) | def get_file_path(self, relative_path: str) -> Path:
class ProjectRegistry (line 115) | class ProjectRegistry:
method __new__ (line 122) | def __new__(cls) -> "ProjectRegistry":
method __init__ (line 132) | def __init__(self) -> None:
method register_project (line 138) | def register_project(self, name: str, path: str, description: Optional...
method get_project (line 172) | def get_project(self, name: str) -> Project:
method list_projects (line 191) | def list_projects(self) -> List[Dict[str, Any]]:
method remove_project (line 201) | def remove_project(self, name: str) -> None:
FILE: src/mcp_server_tree_sitter/prompts/code_patterns.py
function get_language_pattern (line 257) | def get_language_pattern(language: str, pattern_name: str) -> str:
function get_review_pattern (line 263) | def get_review_pattern(pattern_name: str) -> str:
function get_available_patterns (line 268) | def get_available_patterns(language: Optional[str] = None) -> Dict[str, ...
FILE: src/mcp_server_tree_sitter/server.py
function configure_with_context (line 19) | def configure_with_context(
function main (line 103) | def main() -> None:
FILE: src/mcp_server_tree_sitter/testing/pytest_diagnostic.py
class DiagnosticJSONEncoder (line 18) | class DiagnosticJSONEncoder(JSONEncoder):
method default (line 21) | def default(self, obj: Any) -> Any:
class DiagnosticData (line 55) | class DiagnosticData:
method __init__ (line 58) | def __init__(self, test_id: str):
method add_error (line 68) | def add_error(self, error_type: str, message: str, tb: Optional[str] =...
method add_detail (line 79) | def add_detail(self, key: str, value: Any) -> None:
method add_artifact (line 83) | def add_artifact(self, name: str, content: Any) -> None:
method finalize (line 87) | def finalize(self, status: str = "completed") -> None:
method to_dict (line 93) | def to_dict(self) -> Dict[str, Any]:
function diagnostic (line 108) | def diagnostic(request: Any) -> Generator[DiagnosticData, None, None]:
function pytest_configure (line 123) | def pytest_configure(config: Any) -> None:
function pytest_runtest_protocol (line 129) | def pytest_runtest_protocol(item: Any, nextitem: Any) -> Optional[bool]:
function pytest_runtest_setup (line 135) | def pytest_runtest_setup(item: Any) -> None:
function pytest_runtest_teardown (line 141) | def pytest_runtest_teardown(item: Any) -> None:
function pytest_terminal_summary (line 147) | def pytest_terminal_summary(terminalreporter: Any, exitstatus: Any, conf...
function pytest_sessionfinish (line 164) | def pytest_sessionfinish(session: Any, exitstatus: Any) -> None:
function pytest_exception_interact (line 196) | def pytest_exception_interact(node: Any, call: Any, report: Any) -> None:
FILE: src/mcp_server_tree_sitter/tools/analysis.py
function extract_symbols (line 22) | def extract_symbols(
function process_symbol_matches (line 267) | def process_symbol_matches(
function analyze_project_structure (line 419) | def analyze_project_structure(
function find_dependencies (line 598) | def find_dependencies(
function analyze_code_complexity (line 803) | def analyze_code_complexity(
FILE: src/mcp_server_tree_sitter/tools/ast_operations.py
function get_file_ast (line 17) | def get_file_ast(
function parse_file (line 69) | def parse_file(file_path: Any, language: str, language_registry: Any, tr...
function find_node_at_position (line 118) | def find_node_at_position(root_node: Any, row: int, column: int) -> Opti...
FILE: src/mcp_server_tree_sitter/tools/debug.py
function diagnose_yaml_config (line 12) | def diagnose_yaml_config(config_path: str) -> Dict[str, Any]:
FILE: src/mcp_server_tree_sitter/tools/file_operations.py
function list_project_files (line 13) | def list_project_files(
function get_file_content (line 77) | def get_file_content(
function get_file_info (line 176) | def get_file_info(project: Any, path: str) -> Dict[str, Any]:
function count_lines (line 220) | def count_lines(file_path: Path) -> int:
FILE: src/mcp_server_tree_sitter/tools/project.py
function register_project (line 9) | def register_project(path: str, name: Optional[str] = None, description:...
function get_project (line 46) | def get_project(name: str) -> Dict[str, Any]:
function list_projects (line 75) | def list_projects() -> List[Dict[str, Any]]:
function remove_project (line 101) | def remove_project(name: str) -> Dict[str, str]:
FILE: src/mcp_server_tree_sitter/tools/query_builder.py
function get_template (line 8) | def get_template(language: str, pattern: str) -> str:
function build_compound_query (line 28) | def build_compound_query(language: str, patterns: List[str], combine: st...
function adapt_query (line 59) | def adapt_query(query: str, from_language: str, to_language: str) -> Dic...
function adapt_query_for_language (line 80) | def adapt_query_for_language(query: str, from_language: str, to_language...
function describe_node_types (line 129) | def describe_node_types(language: str) -> Dict[str, str]:
FILE: src/mcp_server_tree_sitter/tools/registration.py
function register_tools (line 17) | def register_tools(mcp_server: Any, container: DependencyContainer) -> N...
function _register_prompts (line 662) | def _register_prompts(mcp_server: Any, container: DependencyContainer) -...
FILE: src/mcp_server_tree_sitter/tools/search.py
function search_text (line 12) | def search_text(
function query_code (line 139) | def query_code(
function _extract_ast_fingerprint (line 352) | def _extract_ast_fingerprint(node: Any, source_bytes: bytes) -> set:
function _iter_top_level_blocks (line 377) | def _iter_top_level_blocks(tree: Any) -> list:
function find_similar_code (line 401) | def find_similar_code(
FILE: src/mcp_server_tree_sitter/utils/context/mcp_context.py
class ProgressScope (line 12) | class ProgressScope:
method __init__ (line 15) | def __init__(self, context: "MCPContext", total: int, description: str):
method update (line 29) | def update(self, step: int = 1) -> None:
method set_progress (line 41) | def set_progress(self, current: int) -> None:
class MCPContext (line 52) | class MCPContext:
method __init__ (line 55) | def __init__(self, ctx: Optional[Any] = None):
method report_progress (line 66) | def report_progress(self, current: int, total: int) -> None:
method info (line 89) | def info(self, message: str) -> None:
method warning (line 103) | def warning(self, message: str) -> None:
method error (line 117) | def error(self, message: str) -> None:
method progress_scope (line 132) | def progress_scope(self, total: int, description: str) -> Generator[Pr...
method with_mcp_context (line 153) | def with_mcp_context(self, ctx: Any) -> "MCPContext":
method from_mcp_context (line 166) | def from_mcp_context(ctx: Optional[Any]) -> "MCPContext":
method try_get_mcp_context (line 178) | def try_get_mcp_context(self) -> Optional[Any]:
FILE: src/mcp_server_tree_sitter/utils/file_io.py
function read_text_file (line 11) | def read_text_file(path: Union[str, Path]) -> List[str]:
function read_binary_file (line 25) | def read_binary_file(path: Union[str, Path]) -> bytes:
function get_file_content_and_lines (line 39) | def get_file_content_and_lines(path: Union[str, Path]) -> Tuple[bytes, L...
function is_line_comment (line 54) | def is_line_comment(line: str, comment_prefix: str) -> bool:
function count_comment_lines (line 68) | def count_comment_lines(lines: List[str], comment_prefix: str) -> int:
function get_comment_prefix (line 82) | def get_comment_prefix(language: str) -> Optional[str]:
function parse_file_with_encoding (line 120) | def parse_file_with_encoding(path: Union[str, Path], encoding: str = "ut...
function read_file_lines (line 140) | def read_file_lines(path: Union[str, Path], start_line: int = 0, max_lin...
FILE: src/mcp_server_tree_sitter/utils/path.py
function normalize_path (line 8) | def normalize_path(path: Union[str, Path], ensure_absolute: bool = False...
function safe_relative_path (line 27) | def safe_relative_path(path: Union[str, Path], base: Union[str, Path]) -...
function get_project_root (line 55) | def get_project_root(path: Union[str, Path]) -> Path:
FILE: src/mcp_server_tree_sitter/utils/security.py
function validate_file_access (line 11) | def validate_file_access(file_path: Union[str, Path], project_root: Unio...
FILE: src/mcp_server_tree_sitter/utils/tree_sitter_helpers.py
function create_query (line 28) | def create_query(language: Any, query_string: str) -> Any:
function query_captures (line 42) | def query_captures(query: Any, node: Any) -> Any:
function create_parser (line 57) | def create_parser(language_obj: Any) -> Parser:
function parse_source (line 84) | def parse_source(source: bytes, parser: Union[Parser, Any]) -> Tree:
function parse_source_incremental (line 100) | def parse_source_incremental(source: bytes, old_tree: Optional[Tree], pa...
function edit_tree (line 117) | def edit_tree(
function get_changed_ranges (line 174) | def get_changed_ranges(old_tree: Tree, new_tree: Tree) -> List[Tuple[int...
function parse_file (line 204) | def parse_file(
function get_node_text (line 239) | def get_node_text(node: Node, source_bytes: bytes, decode: bool = True) ...
function walk_tree (line 264) | def walk_tree(node: Node) -> TreeCursor:
function cursor_walk_tree (line 279) | def cursor_walk_tree(node: Node, visit_fn: Callable[[Optional[Node], Opt...
function collect_with_cursor (line 332) | def collect_with_cursor(
function find_nodes_by_type (line 361) | def find_nodes_by_type(root_node: Node, node_type: str) -> List[Node]:
function get_node_descendants (line 383) | def get_node_descendants(node: Optional[Node], max_depth: Optional[int] ...
function parse_with_cached_tree (line 414) | def parse_with_cached_tree(
function update_cached_tree (line 453) | def update_cached_tree(
function create_edit (line 524) | def create_edit(
function parse_file_with_detection (line 556) | def parse_file_with_detection(file_path: Path, language: Optional[str], ...
function parse_file_incremental (line 613) | def parse_file_incremental(file_path: Path, old_tree: Tree, language: st...
function get_node_with_text (line 639) | def get_node_with_text(node: Node, source_bytes: bytes, text: bytes) -> ...
function is_node_inside (line 663) | def is_node_inside(pos_or_node: Union[Node, Tuple[int, int]], container_...
function find_all_descendants (line 698) | def find_all_descendants(node: Node, max_depth: Optional[int] = None) ->...
FILE: src/mcp_server_tree_sitter/utils/tree_sitter_types.py
class LanguageProtocol (line 12) | class LanguageProtocol(Protocol):
method query (line 15) | def query(self, query_string: str) -> Any: ...
class ParserProtocol (line 18) | class ParserProtocol(Protocol):
method set_language (line 21) | def set_language(self, language: Any) -> None: ...
method language (line 22) | def language(self, language: Any) -> None: ... # Alternative name for...
method parse (line 23) | def parse(self, bytes_input: bytes) -> Any: ...
class TreeProtocol (line 26) | class TreeProtocol(Protocol):
method root_node (line 30) | def root_node(self) -> Any: ...
class NodeProtocol (line 33) | class NodeProtocol(Protocol):
method children (line 37) | def children(self) -> list[Any]: ...
method named_children (line 39) | def named_children(self) -> list[Any]: ...
method child_count (line 41) | def child_count(self) -> int: ...
method named_child_count (line 43) | def named_child_count(self) -> int: ...
method start_point (line 45) | def start_point(self) -> tuple[int, int]: ...
method end_point (line 47) | def end_point(self) -> tuple[int, int]: ...
method start_byte (line 49) | def start_byte(self) -> int: ...
method end_byte (line 51) | def end_byte(self) -> int: ...
method type (line 53) | def type(self) -> str: ...
method is_named (line 55) | def is_named(self) -> bool: ...
method parent (line 57) | def parent(self) -> Any: ...
method children_by_field_name (line 59) | def children_by_field_name(self) -> dict[str, list[Any]]: ...
method walk (line 61) | def walk(self) -> Any: ...
class CursorProtocol (line 64) | class CursorProtocol(Protocol):
method node (line 68) | def node(self) -> Any: ...
method goto_first_child (line 70) | def goto_first_child(self) -> bool: ...
method goto_next_sibling (line 71) | def goto_next_sibling(self) -> bool: ...
method goto_parent (line 72) | def goto_parent(self) -> bool: ...
class DummyLanguage (line 97) | class DummyLanguage:
method __init__ (line 100) | def __init__(self, *args: Any, **kwargs: Any) -> None:
method query (line 103) | def query(self, query_string: str) -> Any:
class DummyParser (line 107) | class DummyParser:
method set_language (line 110) | def set_language(self, language: Any) -> None:
method language (line 114) | def language(self, language: Any) -> None:
method parse (line 118) | def parse(self, bytes_input: bytes) -> Any:
class DummyNode (line 122) | class DummyNode:
method children (line 126) | def children(self) -> list[Any]:
method named_children (line 130) | def named_children(self) -> list[Any]:
method child_count (line 134) | def child_count(self) -> int:
method named_child_count (line 138) | def named_child_count(self) -> int:
method start_point (line 142) | def start_point(self) -> tuple[int, int]:
method end_point (line 146) | def end_point(self) -> tuple[int, int]:
method start_byte (line 150) | def start_byte(self) -> int:
method end_byte (line 154) | def end_byte(self) -> int:
method type (line 158) | def type(self) -> str:
method is_named (line 162) | def is_named(self) -> bool:
method parent (line 166) | def parent(self) -> Any:
method children_by_field_name (line 170) | def children_by_field_name(self) -> dict[str, list[Any]]:
method walk (line 173) | def walk(self) -> Any:
class DummyTreeCursor (line 176) | class DummyTreeCursor:
method node (line 180) | def node(self) -> Any:
method goto_first_child (line 183) | def goto_first_child(self) -> bool:
method goto_next_sibling (line 186) | def goto_next_sibling(self) -> bool:
method goto_parent (line 189) | def goto_parent(self) -> bool:
class DummyTree (line 192) | class DummyTree:
method root_node (line 196) | def root_node(self) -> Any:
function ensure_language (line 209) | def ensure_language(obj: Any) -> "Language":
function ensure_parser (line 214) | def ensure_parser(obj: Any) -> "Parser":
function ensure_tree (line 219) | def ensure_tree(obj: Any) -> "Tree":
function ensure_node (line 224) | def ensure_node(obj: Any) -> "Node":
function ensure_cursor (line 229) | def ensure_cursor(obj: Any) -> "TreeCursor":
FILE: tests/conftest.py
function reset_project_registry (line 10) | def reset_project_registry():
FILE: tests/test_ast_cursor.py
function test_cursor_based_ast (line 12) | def test_cursor_based_ast() -> None:
FILE: tests/test_basic.py
function test_config_default (line 10) | def test_config_default() -> None:
function test_project_registry (line 22) | def test_project_registry() -> None:
function test_language_registry (line 55) | def test_language_registry() -> None:
FILE: tests/test_cache_config.py
function test_project (line 14) | def test_project():
function test_cache_max_size_setting (line 45) | def test_cache_max_size_setting(test_project):
function test_cache_ttl_setting (line 85) | def test_cache_ttl_setting(test_project):
function test_cache_eviction_policy (line 118) | def test_cache_eviction_policy(test_project):
FILE: tests/test_cli_arguments.py
function test_help_flag_does_not_start_server (line 12) | def test_help_flag_does_not_start_server():
function test_version_flag_exits_without_starting_server (line 34) | def test_version_flag_exits_without_starting_server():
function test_direct_script_help_flag (line 53) | def test_direct_script_help_flag():
function test_entry_point_implementation (line 75) | def test_entry_point_implementation():
FILE: tests/test_config_behavior.py
function test_project (line 14) | def test_project():
function test_cache_enabled_setting (line 38) | def test_cache_enabled_setting(test_project):
function test_security_file_size_limit (line 133) | def test_security_file_size_limit(test_project):
function test_excluded_dirs_setting (line 163) | def test_excluded_dirs_setting(test_project):
function test_default_max_depth_setting (line 191) | def test_default_max_depth_setting(test_project):
FILE: tests/test_config_manager.py
function temp_yaml_file (line 13) | def temp_yaml_file():
function test_config_manager_initialization (line 31) | def test_config_manager_initialization():
function test_config_manager_load_from_file (line 45) | def test_config_manager_load_from_file(temp_yaml_file):
function test_config_manager_update_values (line 60) | def test_config_manager_update_values():
function test_config_manager_to_dict (line 77) | def test_config_manager_to_dict():
function test_env_overrides_defaults (line 92) | def test_env_overrides_defaults(monkeypatch):
function test_env_overrides_yaml (line 107) | def test_env_overrides_yaml(temp_yaml_file, monkeypatch):
FILE: tests/test_context.py
function mock_dependencies (line 17) | def mock_dependencies():
function server_context (line 46) | def server_context(mock_dependencies):
function test_server_context_initialization (line 56) | def test_server_context_initialization(mock_dependencies):
function test_server_context_initialization_with_container (line 72) | def test_server_context_initialization_with_container(mock_get_container...
function test_get_config (line 99) | def test_get_config(server_context, mock_dependencies):
function test_register_project (line 107) | def test_register_project(server_context, mock_dependencies):
function test_register_project_with_error (line 127) | def test_register_project_with_error(server_context, mock_dependencies):
function test_list_projects (line 140) | def test_list_projects(server_context, mock_dependencies):
function test_remove_project (line 154) | def test_remove_project(server_context, mock_dependencies):
function test_clear_cache_all (line 167) | def test_clear_cache_all(server_context, mock_dependencies):
function test_clear_cache_for_file (line 180) | def test_clear_cache_for_file(server_context, mock_dependencies):
function test_configure_with_yaml (line 200) | def test_configure_with_yaml(mock_get_logger, server_context, mock_depen...
function test_configure_cache_enabled (line 215) | def test_configure_cache_enabled(server_context, mock_dependencies):
function test_configure_max_file_size (line 230) | def test_configure_max_file_size(server_context, mock_dependencies):
function test_configure_log_level (line 244) | def test_configure_log_level(mock_get_logger, server_context, mock_depen...
function test_global_context_is_instance (line 264) | def test_global_context_is_instance():
FILE: tests/test_debug_flag.py
function test_debug_flag_with_preexisting_env (line 13) | def test_debug_flag_with_preexisting_env():
function test_update_log_levels_reconfigures_root_logger (line 72) | def test_update_log_levels_reconfigures_root_logger():
function test_environment_variable_updates_log_level (line 128) | def test_environment_variable_updates_log_level():
function test_configure_root_logger_syncs_handlers (line 180) | def test_configure_root_logger_syncs_handlers():
function test_log_message_levels (line 249) | def test_log_message_levels():
FILE: tests/test_di.py
function test_container_singleton (line 6) | def test_container_singleton():
function test_register_custom_dependency (line 13) | def test_register_custom_dependency():
function test_core_dependencies_initialized (line 26) | def test_core_dependencies_initialized():
FILE: tests/test_diagnostics/test_ast.py
function test_project (line 17) | def test_project():
function test_ast_failure (line 44) | def test_ast_failure(test_project, diagnostic) -> None:
function test_language_detection (line 87) | def test_language_detection(diagnostic) -> None:
FILE: tests/test_diagnostics/test_ast_parsing.py
function test_project (line 15) | def test_project() -> Generator[Dict[str, Any], None, None]:
function parse_file (line 48) | def parse_file(file_path: Path, language: str) -> Tuple[Any, bytes]:
function test_get_ast_functionality (line 64) | def test_get_ast_functionality(test_project, diagnostic) -> None:
function test_direct_parsing (line 110) | def test_direct_parsing(test_project, diagnostic) -> None:
FILE: tests/test_diagnostics/test_cursor_ast.py
function parse_file (line 15) | def parse_file(file_path: Path, language: str) -> Tuple[Any, bytes]:
function test_project (line 31) | def test_project() -> Generator[Dict[str, Any], None, None]:
function test_cursor_ast_implementation (line 58) | def test_cursor_ast_implementation(test_project, diagnostic) -> None:
function test_large_ast_handling (line 131) | def test_large_ast_handling(test_project, diagnostic) -> None:
FILE: tests/test_diagnostics/test_language_pack.py
function test_tree_sitter_import (line 9) | def test_tree_sitter_import(diagnostic) -> None:
function test_language_pack_import (line 49) | def test_language_pack_import(diagnostic) -> None:
function test_language_binding_available (line 83) | def test_language_binding_available(diagnostic) -> None:
function _get_language_binding (line 135) | def _get_language_binding(language_name) -> dict:
function test_python_environment (line 162) | def test_python_environment(diagnostic) -> None:
FILE: tests/test_diagnostics/test_language_registry.py
function test_language_detection (line 9) | def test_language_detection(diagnostic) -> None:
function test_language_list_empty (line 48) | def test_language_list_empty(diagnostic) -> None:
function test_language_detection_vs_listing (line 86) | def test_language_detection_vs_listing(diagnostic) -> None:
FILE: tests/test_diagnostics/test_unpacking_errors.py
function test_project (line 14) | def test_project() -> Generator[Dict[str, Any], None, None]:
function test_get_symbols_error (line 62) | def test_get_symbols_error(test_project, diagnostic) -> None:
function test_get_dependencies_error (line 97) | def test_get_dependencies_error(test_project, diagnostic) -> None:
function test_analyze_complexity_error (line 130) | def test_analyze_complexity_error(test_project, diagnostic) -> None:
function test_run_query_error (line 164) | def test_run_query_error(test_project, diagnostic) -> None:
FILE: tests/test_env_config.py
function temp_yaml_file (line 13) | def temp_yaml_file():
function test_env_overrides_defaults (line 31) | def test_env_overrides_defaults(monkeypatch):
function test_env_overrides_yaml (line 45) | def test_env_overrides_yaml(temp_yaml_file, monkeypatch):
function test_log_level_env_var (line 71) | def test_log_level_env_var(monkeypatch):
function test_invalid_env_var_handling (line 81) | def test_invalid_env_var_handling(monkeypatch):
FILE: tests/test_failure_modes.py
function mock_project (line 31) | def mock_project(request) -> Generator[Dict[str, Any], None, None]:
class TestQueryExecution (line 59) | class TestQueryExecution:
method test_run_query_with_valid_query (line 62) | def test_run_query_with_valid_query(self, mock_project) -> None:
method test_adapt_query_language_specific_syntax (line 88) | def test_adapt_query_language_specific_syntax(self, mock_project) -> N...
class TestSymbolExtraction (line 110) | class TestSymbolExtraction:
method test_get_symbols_function_detection (line 113) | def test_get_symbols_function_detection(self, mock_project) -> None:
class TestDependencyAnalysis (line 146) | class TestDependencyAnalysis:
method test_get_dependencies_import_detection (line 149) | def test_get_dependencies_import_detection(self, mock_project) -> None:
class TestCodeSearch (line 167) | class TestCodeSearch:
method test_find_similar_code_with_exact_match (line 170) | def test_find_similar_code_with_exact_match(self, mock_project) -> None:
method test_find_usage_for_function (line 184) | def test_find_usage_for_function(self, mock_project) -> None:
function test_error_handling_with_invalid_project (line 229) | def test_error_handling_with_invalid_project(command_name, function, arg...
class TestASTHandling (line 242) | class TestASTHandling:
method test_ast_node_traversal (line 245) | def test_ast_node_traversal(self, mock_project) -> None:
FILE: tests/test_file_operations.py
function test_project (line 20) | def test_project() -> Generator[Dict[str, Any], None, None]:
function test_list_project_files_basic (line 86) | def test_list_project_files_basic(test_project):
function test_list_project_files_with_pattern (line 104) | def test_list_project_files_with_pattern(test_project):
function test_list_project_files_with_max_depth (line 122) | def test_list_project_files_with_max_depth(test_project):
function test_list_project_files_with_extensions (line 140) | def test_list_project_files_with_extensions(test_project):
function test_get_file_content_basic (line 165) | def test_get_file_content_basic(test_project):
function test_get_file_content_empty (line 181) | def test_get_file_content_empty(test_project):
function test_get_file_content_with_line_limits (line 196) | def test_get_file_content_with_line_limits(test_project):
function test_get_file_content_nonexistent_file (line 220) | def test_get_file_content_nonexistent_file(test_project):
function test_get_file_content_outside_project (line 233) | def test_get_file_content_outside_project(test_project):
function test_get_file_content_as_bytes (line 246) | def test_get_file_content_as_bytes(test_project):
function test_get_file_info_basic (line 263) | def test_get_file_info_basic(test_project):
function test_get_file_info_directory (line 282) | def test_get_file_info_directory(test_project):
function test_get_file_info_nonexistent_file (line 299) | def test_get_file_info_nonexistent_file(test_project):
function test_get_file_info_outside_project (line 312) | def test_get_file_info_outside_project(test_project):
function test_count_lines (line 326) | def test_count_lines(test_project):
function test_count_lines_empty_file (line 338) | def test_count_lines_empty_file(test_project):
function test_count_lines_large_file (line 350) | def test_count_lines_large_file(test_project):
FILE: tests/test_find_similar_code.py
function project (line 18) | def project():
function test_extract_ast_fingerprint (line 67) | def test_extract_ast_fingerprint():
function test_extract_ast_fingerprint_empty (line 86) | def test_extract_ast_fingerprint_empty():
function test_iter_top_level_blocks (line 96) | def test_iter_top_level_blocks():
function test_find_similar_function (line 119) | def test_find_similar_function(project):
function test_find_similar_class (line 143) | def test_find_similar_class(project):
function test_find_similar_no_match (line 165) | def test_find_similar_no_match(project):
function test_find_similar_respects_max_results (line 187) | def test_find_similar_respects_max_results(project):
function test_find_similar_requires_language (line 204) | def test_find_similar_requires_language(project):
function test_find_similar_result_structure (line 212) | def test_find_similar_result_structure(project):
FILE: tests/test_helpers.py
function temp_config (line 52) | def temp_config(**kwargs):
function register_project_tool (line 129) | def register_project_tool(path: str, name: Optional[str] = None, descrip...
function list_projects_tool (line 134) | def list_projects_tool() -> List[Dict[str, Any]]:
function remove_project_tool (line 139) | def remove_project_tool(name: str) -> Dict[str, str]:
function list_languages (line 145) | def list_languages() -> Dict[str, Any]:
function check_language_available (line 155) | def check_language_available(language: str) -> Dict[str, str]:
function list_files (line 171) | def list_files(
function get_file (line 182) | def get_file(project: str, path: str, max_lines: Optional[int] = None, s...
function get_file_metadata (line 188) | def get_file_metadata(project: str, path: str) -> Dict[str, Any]:
function get_ast (line 195) | def get_ast(project: str, path: str, max_depth: Optional[int] = None, in...
function get_node_at_position (line 214) | def get_node_at_position(project: str, path: str, row: int, column: int)...
function find_text (line 239) | def find_text(
function run_query (line 263) | def run_query(
function get_query_template_tool (line 286) | def get_query_template_tool(language: str, template_name: str) -> Dict[s...
function list_query_templates_tool (line 299) | def list_query_templates_tool(language: Optional[str] = None) -> Dict[st...
function build_query (line 304) | def build_query(language: str, patterns: List[str], combine: str = "or")...
function adapt_query (line 313) | def adapt_query(query: str, from_language: str, to_language: str) -> Dic...
function get_node_types (line 324) | def get_node_types(language: str) -> Dict[str, str]:
function get_symbols (line 330) | def get_symbols(
function analyze_project (line 340) | def analyze_project(project: str, scan_depth: int = 3, ctx: Optional[Any...
function get_dependencies (line 348) | def get_dependencies(project: str, file_path: str) -> Dict[str, List[str]]:
function analyze_complexity (line 360) | def analyze_complexity(project: str, file_path: str) -> Dict[str, Any]:
function find_similar_code (line 372) | def find_similar_code(
function find_usage (line 410) | def find_usage(
function clear_cache (line 440) | def clear_cache(project: Optional[str] = None, file_path: Optional[str] ...
function configure (line 446) | def configure(
function configure_with_context (line 487) | def configure_with_context(
FILE: tests/test_language_listing.py
function test_list_available_languages (line 7) | def test_list_available_languages() -> None:
function test_language_api_consistency (line 33) | def test_language_api_consistency() -> None:
function test_server_language_tools (line 59) | def test_server_language_tools() -> None:
FILE: tests/test_logging_bootstrap.py
function test_bootstrap_imported_first (line 9) | def test_bootstrap_imported_first():
function test_logging_config_forwards_to_bootstrap (line 35) | def test_logging_config_forwards_to_bootstrap():
function test_key_modules_use_bootstrap (line 49) | def test_key_modules_use_bootstrap():
function test_log_level_update_consistency (line 86) | def test_log_level_update_consistency():
function test_no_duplicate_log_level_implementations (line 127) | def test_no_duplicate_log_level_implementations():
FILE: tests/test_logging_config.py
function capture_logs (line 19) | def capture_logs(logger_name="mcp_server_tree_sitter"):
function test_project (line 60) | def test_project():
function test_log_level_setting (line 84) | def test_log_level_setting(test_project):
function test_log_level_in_yaml_config (line 143) | def test_log_level_in_yaml_config():
FILE: tests/test_logging_config_di.py
function capture_logs (line 16) | def capture_logs(logger_name="mcp_server_tree_sitter"):
function test_project (line 51) | def test_project():
function test_log_level_setting_di (line 75) | def test_log_level_setting_di(test_project):
function test_log_level_in_yaml_config_di (line 144) | def test_log_level_in_yaml_config_di():
FILE: tests/test_logging_early_init.py
function test_early_init_in_package (line 9) | def test_early_init_in_package():
function test_configure_is_not_called_at_import (line 37) | def test_configure_is_not_called_at_import():
function test_environment_vars_processed_early (line 52) | def test_environment_vars_processed_early():
function test_handlers_not_synchronized_at_init (line 87) | def test_handlers_not_synchronized_at_init():
FILE: tests/test_logging_env_vars.py
function capture_logs (line 14) | def capture_logs(logger_name="mcp_server_tree_sitter"):
function test_get_log_level_from_env (line 53) | def test_get_log_level_from_env():
function test_update_log_levels (line 81) | def test_update_log_levels():
function test_env_var_affects_logging (line 144) | def test_env_var_affects_logging(monkeypatch):
FILE: tests/test_logging_handlers.py
function temp_logger (line 12) | def temp_logger(name="mcp_server_tree_sitter.test_handlers"):
function test_handler_level_synchronization (line 44) | def test_handler_level_synchronization():
function test_get_logger_handler_sync (line 119) | def test_get_logger_handler_sync():
function test_multiple_handlers_with_log_streams (line 200) | def test_multiple_handlers_with_log_streams():
FILE: tests/test_makefile_targets.py
function test_makefile_target_syntax (line 9) | def test_makefile_target_syntax():
function test_makefile_target_execution (line 35) | def test_makefile_target_execution():
FILE: tests/test_mcp_context.py
function mock_mcp_context (line 11) | def mock_mcp_context():
function test_progress_scope_init (line 21) | def test_progress_scope_init():
function test_progress_scope_update (line 32) | def test_progress_scope_update():
function test_progress_scope_set_progress (line 56) | def test_progress_scope_set_progress():
function test_mcp_context_init (line 80) | def test_mcp_context_init():
function test_mcp_context_report_progress_with_ctx (line 94) | def test_mcp_context_report_progress_with_ctx(mock_mcp_context):
function test_mcp_context_report_progress_without_ctx (line 110) | def test_mcp_context_report_progress_without_ctx(mock_logger):
function test_mcp_context_report_progress_with_exception (line 126) | def test_mcp_context_report_progress_with_exception(mock_logger, mock_mc...
function test_mcp_context_info (line 148) | def test_mcp_context_info(mock_logger, mock_mcp_context):
function test_mcp_context_warning (line 163) | def test_mcp_context_warning(mock_logger, mock_mcp_context):
function test_mcp_context_error (line 178) | def test_mcp_context_error(mock_logger, mock_mcp_context):
function test_mcp_context_info_without_ctx (line 193) | def test_mcp_context_info_without_ctx(mock_logger):
function test_mcp_context_progress_scope (line 204) | def test_mcp_context_progress_scope():
function test_mcp_context_progress_scope_with_exception (line 231) | def test_mcp_context_progress_scope_with_exception():
function test_mcp_context_with_mcp_context (line 260) | def test_mcp_context_with_mcp_context():
function test_mcp_context_from_mcp_context (line 278) | def test_mcp_context_from_mcp_context():
function test_mcp_context_try_get_mcp_context (line 294) | def test_mcp_context_try_get_mcp_context():
FILE: tests/test_models_ast.py
function test_files (line 19) | def test_files() -> Generator[Dict[str, Path], None, None]:
function parsed_trees (line 75) | def parsed_trees(test_files) -> Dict[str, Any]:
function test_node_to_dict_basic (line 108) | def test_node_to_dict_basic(parsed_trees):
function test_node_to_dict_with_text (line 136) | def test_node_to_dict_with_text(parsed_trees):
function test_node_to_dict_without_text (line 155) | def test_node_to_dict_without_text(parsed_trees):
function test_node_to_dict_without_children (line 172) | def test_node_to_dict_without_children(parsed_trees):
function test_node_to_dict_different_languages (line 185) | def test_node_to_dict_different_languages(parsed_trees):
function test_node_to_dict_with_large_depth (line 200) | def test_node_to_dict_with_large_depth(parsed_trees):
function test_summarize_node (line 227) | def test_summarize_node(parsed_trees):
function test_summarize_node_without_source (line 247) | def test_summarize_node_without_source(parsed_trees):
function test_find_node_at_position (line 263) | def test_find_node_at_position(parsed_trees):
function test_find_node_at_position_out_of_bounds (line 283) | def test_find_node_at_position_out_of_bounds(parsed_trees):
function test_extract_node_path (line 299) | def test_extract_node_path(parsed_trees):
function test_extract_node_path_same_node (line 317) | def test_extract_node_path_same_node(parsed_trees):
function test_extract_node_path_intermediate_node (line 327) | def test_extract_node_path_intermediate_node(parsed_trees):
FILE: tests/test_persistent_server.py
function test_persistent_mcp_instance (line 14) | def test_persistent_mcp_instance() -> None:
function test_persistent_project_registration (line 21) | def test_persistent_project_registration() -> None:
function test_project_registry_singleton (line 48) | def test_project_registry_singleton() -> None:
FILE: tests/test_project_persistence.py
function test_project_registry_singleton (line 11) | def test_project_registry_singleton() -> None:
function test_mcp_tool_persistence (line 42) | def test_mcp_tool_persistence() -> None:
function test_project_registry_threads (line 65) | def test_project_registry_threads() -> None:
function test_server_lifecycle (line 97) | def test_server_lifecycle() -> None:
function test_project_persistence_in_mcp_server (line 136) | def test_project_persistence_in_mcp_server() -> None:
FILE: tests/test_query_result_handling.py
function test_project (line 17) | def test_project(request) -> Generator[Dict[str, Any], None, None]:
function test_query_capture_processing (line 72) | def test_query_capture_processing(test_project) -> None:
function test_query_result_capture_types (line 121) | def test_query_result_capture_types(test_project, query_string, expected...
function test_direct_query_with_language_pack (line 153) | def test_direct_query_with_language_pack() -> None:
function test_query_result_structure_transformation (line 239) | def test_query_result_structure_transformation() -> None:
FILE: tests/test_registration.py
class MockMCPServer (line 15) | class MockMCPServer:
method __init__ (line 18) | def __init__(self):
method tool (line 22) | def tool(self):
method prompt (line 31) | def prompt(self):
function mock_mcp_server (line 42) | def mock_mcp_server():
function mock_container (line 48) | def mock_container():
function test_register_tools_registers_all_tools (line 70) | def test_register_tools_registers_all_tools(mock_mcp_server, mock_contai...
function test_register_prompts_registers_all_prompts (line 108) | def test_register_prompts_registers_all_prompts(mock_mcp_server, mock_co...
function test_get_symbols_tool_calls_extract_symbols (line 127) | def test_get_symbols_tool_calls_extract_symbols(mock_extract_symbols, mo...
function test_run_query_tool_calls_query_code (line 145) | def test_run_query_tool_calls_query_code(mock_query_code, mock_mcp_serve...
function test_configure_tool_updates_config (line 167) | def test_configure_tool_updates_config(mock_mcp_server, mock_container):
function test_list_files_tool_calls_list_project_files (line 183) | def test_list_files_tool_calls_list_project_files(mock_list_files, mock_...
function test_get_ast_tool_calls_get_file_ast (line 200) | def test_get_ast_tool_calls_get_file_ast(mock_get_ast, mock_mcp_server, ...
FILE: tests/test_rust_compatibility.py
function rust_project (line 20) | def rust_project(request) -> Generator[Dict[str, Any], None, None]:
function test_rust_ast_parsing (line 148) | def test_rust_ast_parsing(rust_project) -> None:
function test_rust_symbol_extraction (line 217) | def test_rust_symbol_extraction(rust_project) -> None:
function test_rust_dependency_analysis (line 253) | def test_rust_dependency_analysis(rust_project) -> None:
function test_rust_specific_queries (line 267) | def test_rust_specific_queries(rust_project) -> None:
function test_rust_trait_and_macro_handling (line 329) | def test_rust_trait_and_macro_handling(rust_project) -> None:
FILE: tests/test_server.py
function mock_container (line 16) | def mock_container():
function test_mcp_server_initialized (line 72) | def test_mcp_server_initialized():
function test_configure_with_context_basic (line 78) | def test_configure_with_context_basic(mock_container):
function test_configure_with_context_cache_enabled (line 96) | def test_configure_with_context_cache_enabled(mock_container):
function test_configure_with_context_max_file_size (line 108) | def test_configure_with_context_max_file_size(mock_container):
function test_configure_with_context_log_level (line 117) | def test_configure_with_context_log_level(mock_container):
function test_configure_with_context_config_path (line 150) | def test_configure_with_context_config_path(mock_container):
function test_configure_with_context_nonexistent_config_path (line 177) | def test_configure_with_context_nonexistent_config_path(mock_container):
function test_main (line 189) | def test_main():
FILE: tests/test_server_capabilities.py
class MockMCPServer (line 11) | class MockMCPServer:
method __init__ (line 14) | def __init__(self):
method capability (line 18) | def capability(self, name):
function mock_server (line 29) | def mock_server():
function mock_config (line 35) | def mock_config():
function test_register_capabilities (line 45) | def test_register_capabilities(mock_get_container, mock_server, mock_con...
function test_handle_logging (line 62) | def test_handle_logging(mock_get_container, mock_logger, mock_server, mo...
function test_handle_completion_project_suggestions (line 98) | def test_handle_completion_project_suggestions(mock_get_container, mock_...
function test_handle_completion_language_suggestions (line 137) | def test_handle_completion_language_suggestions(mock_get_container, mock...
function test_handle_completion_config_suggestions (line 172) | def test_handle_completion_config_suggestions(mock_get_container, mock_s...
FILE: tests/test_smoke.py
function test_server_help (line 26) | def test_server_help():
function test_server_version (line 39) | def test_server_version():
function test_all_tools_registered (line 52) | def test_all_tools_registered():
function test_mcp_protocol_smoke (line 107) | async def test_mcp_protocol_smoke():
FILE: tests/test_symbol_extraction.py
function test_project (line 25) | def test_project(request) -> Generator[Dict[str, Any], None, None]:
function test_symbol_extraction_diagnostics (line 154) | def test_symbol_extraction_diagnostics(test_project) -> None:
function test_dependency_analysis_diagnostics (line 238) | def test_dependency_analysis_diagnostics(test_project) -> None:
function test_symbol_extraction_with_ast_access (line 274) | def test_symbol_extraction_with_ast_access(test_project) -> None:
function test_query_based_symbol_extraction (line 381) | def test_query_based_symbol_extraction(test_project) -> None:
function test_debug_file_saving (line 582) | def test_debug_file_saving(test_project) -> None:
FILE: tests/test_tree_sitter_helpers.py
function test_files (line 27) | def test_files() -> Dict[str, Path]:
function parsed_files (line 80) | def parsed_files(test_files) -> Dict[str, Dict[str, Any]]:
function test_parse_file_with_detection (line 115) | def test_parse_file_with_detection(test_files, tmp_path):
function test_parse_file_with_unknown_language (line 138) | def test_parse_file_with_unknown_language(tmp_path):
function test_parse_source (line 158) | def test_parse_source(parsed_files):
function test_parse_source_incremental (line 181) | def test_parse_source_incremental(parsed_files):
function test_edit_tree (line 202) | def test_edit_tree(parsed_files):
function test_get_changed_ranges (line 236) | def test_get_changed_ranges(parsed_files):
function test_get_node_text (line 258) | def test_get_node_text(parsed_files):
function test_get_node_with_text (line 283) | def test_get_node_with_text(parsed_files):
function test_walk_tree (line 296) | def test_walk_tree(parsed_files):
function test_is_node_inside (line 324) | def test_is_node_inside(parsed_files):
function test_find_all_descendants (line 348) | def test_find_all_descendants(parsed_files):
function test_get_node_text_with_invalid_byte_range (line 365) | def test_get_node_text_with_invalid_byte_range(parsed_files):
function test_parse_file_incremental (line 389) | def test_parse_file_incremental(test_files, tmp_path):
function test_parse_file_nonexistent (line 416) | def test_parse_file_nonexistent():
function test_parse_file_without_language (line 427) | def test_parse_file_without_language(test_files):
FILE: tests/test_yaml_config.py
function temp_yaml_file (line 18) | def temp_yaml_file():
function test_server_config_from_file (line 36) | def test_server_config_from_file(temp_yaml_file):
function test_load_config_function_di (line 59) | def test_load_config_function_di(temp_yaml_file):
function test_configure_helper (line 90) | def test_configure_helper(temp_yaml_file):
function test_real_yaml_example (line 139) | def test_real_yaml_example():
FILE: tests/test_yaml_config_di.py
function temp_yaml_file (line 15) | def temp_yaml_file():
function test_server_config_from_file (line 33) | def test_server_config_from_file(temp_yaml_file):
function test_load_config_function_di (line 56) | def test_load_config_function_di(temp_yaml_file):
function test_configure_helper (line 87) | def test_configure_helper(temp_yaml_file):
function test_real_yaml_example_di (line 136) | def test_real_yaml_example_di():
Condensed preview — 126 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (766K chars).
[
{
"path": ".codestateignore",
"chars": 8,
"preview": "uv.lock\n"
},
{
"path": ".github/workflows/ci.yml",
"chars": 2950,
"preview": "name: CI\n\non:\n push:\n branches: [ main ]\n pull_request:\n branches: [ main ]\n\njobs:\n test:\n runs-on: ubuntu-l"
},
{
"path": ".github/workflows/release.yml",
"chars": 2038,
"preview": "name: Release\n\non:\n release:\n types: [published]\n\npermissions:\n contents: read\n id-token: write\n\njobs:\n release:\n"
},
{
"path": ".gitignore",
"chars": 3487,
"preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
},
{
"path": ".python-version",
"chars": 5,
"preview": "3.12\n"
},
{
"path": "AGENTS.md",
"chars": 3402,
"preview": "# AGENTS.md\n\nInstructions for AI coding agents working in this repository.\n\n## Project Overview\n\nMCP Tree-sitter Server "
},
{
"path": "CONTRIBUTING.md",
"chars": 2704,
"preview": "# Contributing to MCP Tree-sitter Server\n\nThank you for your interest in contributing to MCP Tree-sitter Server! This gu"
},
{
"path": "FEATURES.md",
"chars": 15270,
"preview": "# MCP Tree-sitter Server: Feature Matrix\n\nThis document provides a comprehensive overview of all MCP Tree-sitter server "
},
{
"path": "LICENSE",
"chars": 1062,
"preview": "MIT License\n\nCopyright (c) 2025 Wrale\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof t"
},
{
"path": "Makefile",
"chars": 7543,
"preview": "# Makefile for mcp-server-tree-sitter\n# Uses uv as the package manager\n\n# Package information\nPACKAGE := mcp_server_tree"
},
{
"path": "NOTICE",
"chars": 1902,
"preview": "MCP Tree-sitter Server\nCopyright (c) 2025 Wrale\nLicensed under the MIT License (see LICENSE file)\n\nThis software include"
},
{
"path": "README.md",
"chars": 13983,
"preview": "# MCP Tree-sitter Server\n\nA Model Context Protocol (MCP) server that provides code analysis capabilities using tree-sitt"
},
{
"path": "ROADMAP.md",
"chars": 6435,
"preview": "# MCP Tree-sitter Server Roadmap\n\nThis document outlines the planned improvements and future features for the MCP Tree-s"
},
{
"path": "TODO.md",
"chars": 4036,
"preview": "# MCP Tree-sitter Server: TODO Board\n\nThis Kanban board tracks tasks specifically focused on improving partially working"
},
{
"path": "docs/architecture.md",
"chars": 5606,
"preview": "# Architecture Overview\n\nThis document provides an overview of the MCP Tree-sitter Server's architecture, focusing on ke"
},
{
"path": "docs/cli.md",
"chars": 2253,
"preview": "# MCP Tree-sitter Server CLI Guide\n\nThis document explains the command-line interface (CLI) for the MCP Tree-sitter Serv"
},
{
"path": "docs/config.md",
"chars": 10790,
"preview": "# MCP Tree-sitter Server Configuration Guide\n\nThis document explains the configuration system for the MCP Tree-sitter Se"
},
{
"path": "docs/diagnostics.md",
"chars": 7190,
"preview": "# MCP Tree-sitter Server Diagnostics\n\nThis document describes the diagnostic testing approach for the MCP Tree-sitter Se"
},
{
"path": "docs/logging.md",
"chars": 7750,
"preview": "# Logging Configuration Guide\n\nThis document explains how logging is configured in the MCP Tree-sitter Server and how to"
},
{
"path": "docs/requirements/logging.md",
"chars": 7092,
"preview": "# Requirements for Correct Logging Behavior in MCP Tree-sitter Server\n\nThis document specifies the requirements for impl"
},
{
"path": "docs/tree-sitter-type-safety.md",
"chars": 3350,
"preview": "# Tree-sitter Type Safety Guide\n\nThis document explains our approach to type safety when interfacing with the tree-sitte"
},
{
"path": "pyproject.toml",
"chars": 2331,
"preview": "[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \"mcp-server-tree-sitter\"\nver"
},
{
"path": "scripts/implementation-search.sh",
"chars": 948,
"preview": "#!/bin/bash\n# implementation-search.sh - Script to spot check implementation patterns\n\n# Enable strict mode\nset -euo pip"
},
{
"path": "src/mcp_server_tree_sitter/__init__.py",
"chars": 604,
"preview": "\"\"\"MCP Server for Tree-sitter - Code analysis capabilities using tree-sitter.\n\nThis module provides a Model Context Prot"
},
{
"path": "src/mcp_server_tree_sitter/__main__.py",
"chars": 2795,
"preview": "\"\"\"Main entry point for mcp-server-tree-sitter.\"\"\"\n\nimport argparse\nimport os\nimport sys\n\nfrom .bootstrap import get_log"
},
{
"path": "src/mcp_server_tree_sitter/api.py",
"chars": 3359,
"preview": "\"\"\"API functions for accessing container dependencies.\n\nThis module provides function-based access to dependencies manag"
},
{
"path": "src/mcp_server_tree_sitter/bootstrap/__init__.py",
"chars": 527,
"preview": "\"\"\"Bootstrap package for early initialization dependencies.\n\nThis package contains modules that should be imported and i"
},
{
"path": "src/mcp_server_tree_sitter/bootstrap/logging_bootstrap.py",
"chars": 5109,
"preview": "\"\"\"Bootstrap module for logging configuration with minimal dependencies.\n\nThis module is imported first in the initializ"
},
{
"path": "src/mcp_server_tree_sitter/cache/__init__.py",
"chars": 39,
"preview": "\"\"\"Cache components for MCP server.\"\"\"\n"
},
{
"path": "src/mcp_server_tree_sitter/cache/parser_cache.py",
"chars": 13969,
"preview": "\"\"\"Caching system for tree-sitter parse trees.\"\"\"\n\nimport logging\nimport threading\nimport time\nfrom functools import lru"
},
{
"path": "src/mcp_server_tree_sitter/capabilities/__init__.py",
"chars": 128,
"preview": "\"\"\"MCP capability declarations.\"\"\"\n\nfrom .server_capabilities import register_capabilities\n\n__all__ = [\"register_capabil"
},
{
"path": "src/mcp_server_tree_sitter/capabilities/server_capabilities.py",
"chars": 5517,
"preview": "\"\"\"Server capability declarations for MCP integration.\"\"\"\n\nimport logging\nfrom typing import Any, Dict, List\n\nlogger = l"
},
{
"path": "src/mcp_server_tree_sitter/config.py",
"chars": 23323,
"preview": "\"\"\"Configuration management with explicit manager class.\n\nEnvironment variables can be used to override configuration se"
},
{
"path": "src/mcp_server_tree_sitter/context.py",
"chars": 5004,
"preview": "\"\"\"Context class for managing dependency injection.\n\nThis module provides a ServerContext class to manage dependencies\na"
},
{
"path": "src/mcp_server_tree_sitter/di.py",
"chars": 2236,
"preview": "\"\"\"Dependency injection container for MCP Tree-sitter Server.\n\nThis module provides a central container for managing all"
},
{
"path": "src/mcp_server_tree_sitter/exceptions.py",
"chars": 1034,
"preview": "\"\"\"Exception classes for mcp-server-tree-sitter.\"\"\"\n\n\nclass MCPTreeSitterError(Exception):\n \"\"\"Base exception for mcp"
},
{
"path": "src/mcp_server_tree_sitter/language/__init__.py",
"chars": 51,
"preview": "\"\"\"Language handling components for MCP server.\"\"\"\n"
},
{
"path": "src/mcp_server_tree_sitter/language/query_templates.py",
"chars": 1143,
"preview": "\"\"\"Query templates for common code patterns by language.\"\"\"\n\nfrom typing import Any, Dict, List, Optional, Union\n\nfrom ."
},
{
"path": "src/mcp_server_tree_sitter/language/registry.py",
"chars": 6823,
"preview": "\"\"\"Language registry for tree-sitter languages.\"\"\"\n\nimport logging\nimport threading\nfrom typing import Any, Dict, List, "
},
{
"path": "src/mcp_server_tree_sitter/language/templates/__init__.py",
"chars": 706,
"preview": "\"\"\"Language-specific query templates collection.\"\"\"\n\nfrom typing import Dict\n\nfrom . import (\n apl,\n c,\n cpp,\n "
},
{
"path": "src/mcp_server_tree_sitter/language/templates/apl.py",
"chars": 867,
"preview": "\"\"\"Query templates for APL language.\"\"\"\n\nTEMPLATES = {\n \"functions\": \"\"\"\n (function_definition\n nam"
},
{
"path": "src/mcp_server_tree_sitter/language/templates/c.py",
"chars": 1017,
"preview": "\"\"\"Query templates for C language.\"\"\"\n\nTEMPLATES = {\n \"functions\": \"\"\"\n (function_definition\n decla"
},
{
"path": "src/mcp_server_tree_sitter/language/templates/cpp.py",
"chars": 1500,
"preview": "\"\"\"Query templates for C++ language.\"\"\"\n\nTEMPLATES = {\n \"functions\": \"\"\"\n (function_definition\n dec"
},
{
"path": "src/mcp_server_tree_sitter/language/templates/dart.py",
"chars": 1540,
"preview": "\"\"\"Query templates for Dart language.\"\"\"\n\nTEMPLATES = {\n \"functions\": \"\"\"\n (program\n (function_sign"
},
{
"path": "src/mcp_server_tree_sitter/language/templates/go.py",
"chars": 1449,
"preview": "\"\"\"Query templates for Go.\"\"\"\n\nTEMPLATES = {\n \"functions\": \"\"\"\n (function_declaration\n name: (ident"
},
{
"path": "src/mcp_server_tree_sitter/language/templates/java.py",
"chars": 1543,
"preview": "\"\"\"Query templates for Java language.\"\"\"\n\nTEMPLATES = {\n \"functions\": \"\"\"\n (method_declaration\n nam"
},
{
"path": "src/mcp_server_tree_sitter/language/templates/javascript.py",
"chars": 1079,
"preview": "\"\"\"Query templates for JavaScript.\"\"\"\n\nTEMPLATES = {\n \"functions\": \"\"\"\n (function_declaration\n name"
},
{
"path": "src/mcp_server_tree_sitter/language/templates/julia.py",
"chars": 1551,
"preview": "\"\"\"Query templates for Julia language.\"\"\"\n\nTEMPLATES = {\n \"functions\": \"\"\"\n (function_definition\n n"
},
{
"path": "src/mcp_server_tree_sitter/language/templates/kotlin.py",
"chars": 1735,
"preview": "\"\"\"Query templates for Kotlin language.\"\"\"\n\nTEMPLATES = {\n \"functions\": \"\"\"\n (function_declaration\n "
},
{
"path": "src/mcp_server_tree_sitter/language/templates/python.py",
"chars": 1231,
"preview": "\"\"\"Query templates for Python.\"\"\"\n\nTEMPLATES = {\n \"functions\": \"\"\"\n (function_definition\n name: (id"
},
{
"path": "src/mcp_server_tree_sitter/language/templates/rust.py",
"chars": 1221,
"preview": "\"\"\"Query templates for Rust.\"\"\"\n\nTEMPLATES = {\n \"functions\": \"\"\"\n (function_item\n name: (identifier"
},
{
"path": "src/mcp_server_tree_sitter/language/templates/swift.py",
"chars": 1606,
"preview": "\"\"\"Query templates for Swift language.\"\"\"\n\nTEMPLATES = {\n \"functions\": \"\"\"\n (function_declaration\n "
},
{
"path": "src/mcp_server_tree_sitter/language/templates/typescript.py",
"chars": 1553,
"preview": "\"\"\"Query templates for TypeScript.\"\"\"\n\nTEMPLATES = {\n \"functions\": \"\"\"\n (function_declaration\n name"
},
{
"path": "src/mcp_server_tree_sitter/logging_config.py",
"chars": 933,
"preview": "\"\"\"Logging configuration for MCP Tree-sitter Server.\n\nThis module is maintained for backwards compatibility.\nAll functio"
},
{
"path": "src/mcp_server_tree_sitter/models/__init__.py",
"chars": 34,
"preview": "\"\"\"Data models for MCP server.\"\"\"\n"
},
{
"path": "src/mcp_server_tree_sitter/models/ast.py",
"chars": 4715,
"preview": "\"\"\"AST representation models for MCP server.\n\nThis module provides functions for converting tree-sitter AST nodes to dic"
},
{
"path": "src/mcp_server_tree_sitter/models/ast_cursor.py",
"chars": 5888,
"preview": "\"\"\"AST representation models using cursor-based traversal.\"\"\"\n\nfrom typing import Any, Dict, Optional\n\nfrom ..utils.tree"
},
{
"path": "src/mcp_server_tree_sitter/models/project.py",
"chars": 7088,
"preview": "\"\"\"Project model for MCP server.\"\"\"\n\nimport os\nimport threading\nimport time\nfrom pathlib import Path\nfrom typing import "
},
{
"path": "src/mcp_server_tree_sitter/prompts/__init__.py",
"chars": 29,
"preview": "\"\"\"MCP prompt components.\"\"\"\n"
},
{
"path": "src/mcp_server_tree_sitter/prompts/code_patterns.py",
"chars": 9210,
"preview": "\"\"\"Common prompt templates for code analysis.\"\"\"\n\nfrom typing import Dict, List, Optional\n\n# Language-specific common pa"
},
{
"path": "src/mcp_server_tree_sitter/server.py",
"chars": 6224,
"preview": "\"\"\"MCP server implementation for Tree-sitter with dependency injection.\"\"\"\n\nimport os\nfrom typing import Any, Dict, Opti"
},
{
"path": "src/mcp_server_tree_sitter/testing/__init__.py",
"chars": 155,
"preview": "\"\"\"Testing utilities for mcp-server-tree-sitter.\"\"\"\n\nfrom .pytest_diagnostic import DiagnosticData, diagnostic\n\n__all__ "
},
{
"path": "src/mcp_server_tree_sitter/testing/pytest_diagnostic.py",
"chars": 7383,
"preview": "\"\"\"Pytest plugin for enhanced diagnostic testing.\n\nThis plugin extends pytest with capabilities for detailed diagnostic "
},
{
"path": "src/mcp_server_tree_sitter/tools/__init__.py",
"chars": 27,
"preview": "\"\"\"MCP tool components.\"\"\"\n"
},
{
"path": "src/mcp_server_tree_sitter/tools/analysis.py",
"chars": 38096,
"preview": "\"\"\"Code analysis tools using tree-sitter.\"\"\"\n\nimport os\nfrom collections import Counter, defaultdict\nfrom typing import "
},
{
"path": "src/mcp_server_tree_sitter/tools/ast_operations.py",
"chars": 3905,
"preview": "\"\"\"AST operation tools for MCP server.\"\"\"\n\nimport logging\nfrom typing import Any, Dict, Optional\n\nfrom ..exceptions impo"
},
{
"path": "src/mcp_server_tree_sitter/tools/debug.py",
"chars": 3143,
"preview": "\"\"\"Debug tools for diagnosing configuration issues.\"\"\"\n\nfrom pathlib import Path\nfrom typing import Any, Dict\n\nimport ya"
},
{
"path": "src/mcp_server_tree_sitter/tools/file_operations.py",
"chars": 7750,
"preview": "\"\"\"File operation tools for MCP server.\"\"\"\n\nimport logging\nfrom pathlib import Path\nfrom typing import Any, Dict, List, "
},
{
"path": "src/mcp_server_tree_sitter/tools/project.py",
"chars": 3429,
"preview": "\"\"\"Project management tools for MCP server.\"\"\"\n\nfrom typing import Any, Dict, List, Optional\n\nfrom ..api import get_lang"
},
{
"path": "src/mcp_server_tree_sitter/tools/query_builder.py",
"chars": 6000,
"preview": "\"\"\"Tools for building and manipulating tree-sitter queries.\"\"\"\n\nfrom typing import Dict, List\n\nfrom ..language.query_tem"
},
{
"path": "src/mcp_server_tree_sitter/tools/registration.py",
"chars": 28202,
"preview": "\"\"\"Tool registration with dependency injection for MCP server.\n\nThis module centralizes all tool registrations with prop"
},
{
"path": "src/mcp_server_tree_sitter/tools/search.py",
"chars": 19152,
"preview": "\"\"\"Search tools for tree-sitter code analysis.\"\"\"\n\nimport concurrent.futures\nimport re\nfrom pathlib import Path\nfrom typ"
},
{
"path": "src/mcp_server_tree_sitter/utils/__init__.py",
"chars": 40,
"preview": "\"\"\"Utility functions for MCP server.\"\"\"\n"
},
{
"path": "src/mcp_server_tree_sitter/utils/context/__init__.py",
"chars": 148,
"preview": "\"\"\"Context handling utilities for MCP operations.\"\"\"\n\nfrom .mcp_context import MCPContext, ProgressScope\n\n__all__ = [\"MC"
},
{
"path": "src/mcp_server_tree_sitter/utils/context/mcp_context.py",
"chars": 5272,
"preview": "\"\"\"Context handling for MCP operations with progress reporting.\"\"\"\n\nimport logging\nfrom contextlib import contextmanager"
},
{
"path": "src/mcp_server_tree_sitter/utils/file_io.py",
"chars": 3996,
"preview": "\"\"\"Utilities for safe file operations.\n\nThis module provides safe file I/O operations with proper encoding handling\nand "
},
{
"path": "src/mcp_server_tree_sitter/utils/path.py",
"chars": 2506,
"preview": "\"\"\"Path utilities for mcp-server-tree-sitter.\"\"\"\n\nimport os\nfrom pathlib import Path\nfrom typing import Union\n\n\ndef norm"
},
{
"path": "src/mcp_server_tree_sitter/utils/security.py",
"chars": 2060,
"preview": "\"\"\"Security utilities for mcp-server-tree-sitter.\"\"\"\n\nimport logging\nfrom pathlib import Path\nfrom typing import Union\n\n"
},
{
"path": "src/mcp_server_tree_sitter/utils/tree_sitter_helpers.py",
"chars": 22010,
"preview": "\"\"\"Helper functions for tree-sitter operations.\n\nThis module provides wrappers and utility functions for common tree-sit"
},
{
"path": "src/mcp_server_tree_sitter/utils/tree_sitter_types.py",
"chars": 5907,
"preview": "\"\"\"Type handling utilities for tree-sitter.\n\nThis module provides type definitions and safety wrappers for\nthe tree-sitt"
},
{
"path": "tests/.gitignore",
"chars": 17,
"preview": "# Reports\n*.json\n"
},
{
"path": "tests/__init__.py",
"chars": 47,
"preview": "\"\"\"Test package for mcp-server-tree-sitter.\"\"\"\n"
},
{
"path": "tests/conftest.py",
"chars": 971,
"preview": "\"\"\"Pytest configuration for mcp-server-tree-sitter tests.\"\"\"\n\nimport pytest\n\n# Import and register the diagnostic plugin"
},
{
"path": "tests/test_ast_cursor.py",
"chars": 2861,
"preview": "\"\"\"Test the cursor-based AST implementation.\"\"\"\n\nimport tempfile\nfrom pathlib import Path\n\nfrom mcp_server_tree_sitter.l"
},
{
"path": "tests/test_basic.py",
"chars": 2532,
"preview": "\"\"\"Basic tests for mcp-server-tree-sitter.\"\"\"\n\nimport tempfile\n\nfrom mcp_server_tree_sitter.config import ServerConfig\nf"
},
{
"path": "tests/test_cache_config.py",
"chars": 7822,
"preview": "\"\"\"Tests for cache-specific configuration settings.\"\"\"\n\nimport tempfile\nimport time\nfrom pathlib import Path\n\nimport pyt"
},
{
"path": "tests/test_cli_arguments.py",
"chars": 3001,
"preview": "\"\"\"Tests for command-line argument handling.\"\"\"\n\nimport subprocess\nimport sys\nfrom unittest.mock import patch\n\nimport py"
},
{
"path": "tests/test_config_behavior.py",
"chars": 8738,
"preview": "\"\"\"Tests for how configuration settings affect actual system behavior.\"\"\"\n\nimport tempfile\nfrom pathlib import Path\n\nimp"
},
{
"path": "tests/test_config_manager.py",
"chars": 4112,
"preview": "\"\"\"Tests for the new ConfigurationManager class.\"\"\"\n\nimport os\nimport tempfile\n\nimport pytest\nimport yaml\n\n# Import will"
},
{
"path": "tests/test_context.py",
"chars": 10342,
"preview": "\"\"\"Tests for context.py module.\"\"\"\n\nimport logging\nfrom unittest.mock import MagicMock, patch\n\nimport pytest\n\nfrom mcp_s"
},
{
"path": "tests/test_debug_flag.py",
"chars": 12579,
"preview": "\"\"\"Tests for debug flag behavior and environment variable processing.\"\"\"\n\nimport io\nimport logging\nimport os\n\nimport pyt"
},
{
"path": "tests/test_di.py",
"chars": 1037,
"preview": "\"\"\"Tests for the dependency injection container.\"\"\"\n\nfrom mcp_server_tree_sitter.di import get_container\n\n\ndef test_cont"
},
{
"path": "tests/test_diagnostics/__init__.py",
"chars": 64,
"preview": "\"\"\"Pytest-based diagnostic tests for mcp-server-tree-sitter.\"\"\"\n"
},
{
"path": "tests/test_diagnostics/test_ast.py",
"chars": 3761,
"preview": "\"\"\"Example of using pytest with diagnostic plugin for testing.\"\"\"\n\nimport tempfile\nfrom pathlib import Path\n\nimport pyte"
},
{
"path": "tests/test_diagnostics/test_ast_parsing.py",
"chars": 7508,
"preview": "\"\"\"Pytest-based diagnostic tests for AST parsing functionality.\"\"\"\n\nimport tempfile\nfrom pathlib import Path\nfrom typing"
},
{
"path": "tests/test_diagnostics/test_cursor_ast.py",
"chars": 8932,
"preview": "\"\"\"Pytest-based diagnostic tests for cursor-based AST functionality.\"\"\"\n\nimport tempfile\nfrom pathlib import Path\nfrom t"
},
{
"path": "tests/test_diagnostics/test_language_pack.py",
"chars": 6134,
"preview": "\"\"\"Pytest-based diagnostic tests for tree-sitter language pack integration.\"\"\"\n\nimport sys\n\nimport pytest\n\n\n@pytest.mark"
},
{
"path": "tests/test_diagnostics/test_language_registry.py",
"chars": 4730,
"preview": "\"\"\"Pytest-based diagnostic tests for language registry functionality.\"\"\"\n\nimport pytest\n\nfrom mcp_server_tree_sitter.lan"
},
{
"path": "tests/test_diagnostics/test_unpacking_errors.py",
"chars": 6781,
"preview": "\"\"\"Pytest-based diagnostic tests for the unpacking errors in analysis functions.\"\"\"\n\nimport tempfile\nfrom pathlib import"
},
{
"path": "tests/test_env_config.py",
"chars": 3264,
"preview": "\"\"\"Tests for environment variable configuration overrides.\"\"\"\n\nimport os\nimport tempfile\n\nimport pytest\nimport yaml\n\nfro"
},
{
"path": "tests/test_failure_modes.py",
"chars": 10796,
"preview": "\"\"\"Test cases for tree-sitter API robustness.\n\nThis module contains tests that verify proper error handling and robustne"
},
{
"path": "tests/test_file_operations.py",
"chars": 12394,
"preview": "\"\"\"Tests for file_operations.py module.\"\"\"\n\nimport tempfile\nfrom pathlib import Path\nfrom typing import Any, Dict, Gener"
},
{
"path": "tests/test_find_similar_code.py",
"chars": 5632,
"preview": "\"\"\"Tests for AST-based find_similar_code.\"\"\"\n\nimport tempfile\nfrom pathlib import Path\n\nimport pytest\n\nfrom mcp_server_t"
},
{
"path": "tests/test_helpers.py",
"chars": 17051,
"preview": "\"\"\"Helper functions for tests using the new dependency injection pattern.\"\"\"\n\nimport logging\nfrom contextlib import cont"
},
{
"path": "tests/test_language_listing.py",
"chars": 2695,
"preview": "\"\"\"Test for language listing functionality.\"\"\"\n\nfrom mcp_server_tree_sitter.language.registry import LanguageRegistry\nfr"
},
{
"path": "tests/test_logging_bootstrap.py",
"chars": 5801,
"preview": "\"\"\"Tests for the logging bootstrap module.\"\"\"\n\nimport importlib\nimport logging\n\nimport pytest\n\n\ndef test_bootstrap_impor"
},
{
"path": "tests/test_logging_config.py",
"chars": 6419,
"preview": "\"\"\"Tests for log level configuration settings.\n\nThis file is being kept as an integration test but has been updated to f"
},
{
"path": "tests/test_logging_config_di.py",
"chars": 6479,
"preview": "\"\"\"Tests for log level configuration settings with dependency injection.\"\"\"\n\nimport io\nimport logging\nimport tempfile\nfr"
},
{
"path": "tests/test_logging_early_init.py",
"chars": 4148,
"preview": "\"\"\"Test that logging configuration is applied early in application lifecycle.\"\"\"\n\nimport importlib\nimport logging\nimport"
},
{
"path": "tests/test_logging_env_vars.py",
"chars": 9658,
"preview": "\"\"\"Tests for environment variable-based logging configuration.\"\"\"\n\nimport io\nimport logging\nimport os\nfrom contextlib im"
},
{
"path": "tests/test_logging_handlers.py",
"chars": 11541,
"preview": "\"\"\"Tests for handler level synchronization in logging configuration.\"\"\"\n\nimport io\nimport logging\nfrom contextlib import"
},
{
"path": "tests/test_makefile_targets.py",
"chars": 3262,
"preview": "\"\"\"Tests for Makefile targets to ensure they execute correctly.\"\"\"\n\nimport os\nimport re\nimport subprocess\nfrom pathlib i"
},
{
"path": "tests/test_mcp_context.py",
"chars": 9258,
"preview": "\"\"\"Tests for mcp_context.py module.\"\"\"\n\nfrom unittest.mock import MagicMock, patch\n\nimport pytest\n\nfrom mcp_server_tree_"
},
{
"path": "tests/test_models_ast.py",
"chars": 11389,
"preview": "\"\"\"Tests for ast.py module.\"\"\"\n\nimport tempfile\nfrom pathlib import Path\nfrom typing import Any, Dict, Generator, List\n\n"
},
{
"path": "tests/test_persistent_server.py",
"chars": 2128,
"preview": "\"\"\"Tests for the persistent MCP server implementation.\"\"\"\n\nimport tempfile\n\nfrom mcp_server_tree_sitter.models.project i"
},
{
"path": "tests/test_project_persistence.py",
"chars": 6412,
"preview": "\"\"\"Tests for project registry persistence between MCP tool calls.\"\"\"\n\nimport tempfile\nimport threading\n\nfrom mcp_server_"
},
{
"path": "tests/test_query_result_handling.py",
"chars": 10747,
"preview": "\"\"\"\nTests for tree-sitter query result handling.\n\nThis module contains tests focused on ensuring query result handling i"
},
{
"path": "tests/test_registration.py",
"chars": 7845,
"preview": "\"\"\"Tests for the tools.registration module.\"\"\"\n\nfrom unittest.mock import MagicMock, patch\n\nimport pytest\n\nfrom mcp_serv"
},
{
"path": "tests/test_rust_compatibility.py",
"chars": 13455,
"preview": "\"\"\"Tests for Rust compatibility in the Tree-sitter server.\"\"\"\n\nimport tempfile\nimport time\nfrom pathlib import Path\nfrom"
},
{
"path": "tests/test_server.py",
"chars": 7137,
"preview": "\"\"\"Tests for the server module.\"\"\"\n\nimport logging\nimport os\nimport tempfile\nfrom unittest.mock import MagicMock, patch\n"
},
{
"path": "tests/test_server_capabilities.py",
"chars": 7105,
"preview": "\"\"\"Tests for server capabilities module.\"\"\"\n\nimport logging\nfrom unittest.mock import MagicMock, patch\n\nimport pytest\n\nf"
},
{
"path": "tests/test_smoke.py",
"chars": 6867,
"preview": "\"\"\"Smoke tests for the MCP server.\n\nTests at two levels:\n1. Startup tests: verify the server module imports, --help/--ve"
},
{
"path": "tests/test_symbol_extraction.py",
"chars": 24305,
"preview": "\"\"\"\nTests for symbol extraction and dependency analysis issues.\n\nThis module contains tests specifically focused on the "
},
{
"path": "tests/test_tree_sitter_helpers.py",
"chars": 13742,
"preview": "\"\"\"Tests for tree_sitter_helpers.py module.\"\"\"\n\nimport tempfile\nfrom pathlib import Path\nfrom typing import Any, Dict\n\ni"
},
{
"path": "tests/test_yaml_config.py",
"chars": 7966,
"preview": "\"\"\"Tests for configuration loading from YAML files.\n\nThis file is being kept as an integration test but has been updated"
},
{
"path": "tests/test_yaml_config_di.py",
"chars": 7891,
"preview": "\"\"\"Tests for configuration loading from YAML files using DI.\"\"\"\n\nimport os\nimport tempfile\n\nimport pytest\nimport yaml\n\nf"
}
]
About this extraction
This page contains the full source code of the wrale/mcp-server-tree-sitter GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 126 files (706.1 KB), approximately 155.1k tokens, and a symbol index with 559 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.