Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: CI

on:
push:
branches: [main, "feat/**", "fix/**"]
pull_request:
branches: [main]

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
cache-dependency-glob: pyproject.toml

- name: Build wheel (verify it builds)
run: uv build --wheel

- name: Verify wheel contents
run: |
uv run python -c "
import zipfile, sys
from pathlib import Path
wheels = list(Path('dist').glob('*.whl'))
if not wheels:
print('ERROR: no wheel found', file=sys.stderr)
sys.exit(1)
whl = wheels[0]
with zipfile.ZipFile(whl) as z:
names = z.namelist()
for req in ['fastcontext_mcp/__init__.py', 'fastcontext_mcp/server.py', 'fastcontext_mcp/_shared.py', 'fastcontext_mcp/configwriter.py', 'fastcontext_mcp/release.py']:
assert req in names, f'Missing in wheel: {req}'
print(f'Wheel OK: {whl.name}')
"

- name: Install test dependencies
run: |
# Install only the light deps needed by configwriter/release modules
# (tomli_w) plus pytest. The heavy mlx-lm / fastcontext git deps
# require Apple Silicon and are not needed for the test suite.
uv pip install --system pytest tomli-w

- name: Run tests
env:
PYTHONPATH: ${{ github.workspace }}/src
run: pytest tests/ -v

shellcheck:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Run shellcheck
uses: ludeeus/action-shellcheck@master
with:
severity: warning
ignore_paths: benchmark
83 changes: 83 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Release

on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
version:
description: "Version tag (e.g. v0.1.0) — leave empty to use pyproject.toml"
required: false

permissions:
contents: write

jobs:
build-and-release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
cache-dependency-glob: pyproject.toml

- name: Build wheel
run: |
uv build --wheel
echo "Built wheel:"
ls -la dist/*.whl

- name: Verify wheel metadata
run: |
uv run python -c "
import zipfile, sys
from pathlib import Path
wheels = list(Path('dist').glob('*.whl'))
if not wheels:
print('ERROR: no wheel found', file=sys.stderr)
sys.exit(1)
whl = wheels[0]
print(f'Wheel: {whl.name} ({whl.stat().st_size} bytes)')
with zipfile.ZipFile(whl) as z:
names = z.namelist()
required = [
'fastcontext_mcp/__init__.py',
'fastcontext_mcp/server.py',
'fastcontext_mcp/_shared.py',
'fastcontext_mcp/configwriter.py',
'fastcontext_mcp/release.py',
]
for req in required:
assert req in names, f'Missing in wheel: {req}'
print('All required modules present in wheel')
"

- name: Determine version
id: version
run: |
if [ -n "${{ inputs.version }}" ]; then
TAG="${{ inputs.version }}"
else
TAG="${GITHUB_REF#refs/tags/}"
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "Version: $TAG"

- name: Create or update release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.tag }}
name: ${{ steps.version.outputs.tag }}
generate_release_notes: true
files: dist/*.whl
fail_on_unmatched_files: true
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
__pycache__/
*.pyc
*.pyo
*.pyd
.DS_Store
.fastcontext/
dist/
build/
*.egg-info/
.pytest_cache/
.ruff_cache/

# venvs
.venv/
venv/
101 changes: 99 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,117 @@

MCP server wrapping [Microsoft FastContext](https://github.com/microsoft/fastcontext) with in-process MLX inference on Apple Silicon.

Loads the fine-tuned `mattrobenolt/FastContext-1.0-4B-SFT-mlx-bf16` model directly via `mlx_lm` — no external server, no env vars, no subprocess management.
Loads the fine-tuned `rubybear/FastContext-1.0-4B-SFT-mlx-4bit-g32` model directly via `mlx_lm` — no external server, no env vars, no subprocess management.

## Architecture

FastContext is a two-part system:

```
┌─────────────────────────────────────────────────────────────┐
│ AI agent (Claude Code / OpenCode / Codex CLI) │
│ │ │
│ ├── reads SKILL.md ──▶ knows when/how to call the tool │
│ │ │
│ └── calls fastcontext_explore ──┐ │
│ │ │
└────────────────────────────────────│────────────────────────┘
│ stdio (MCP protocol)
▼
┌─────────────────────────────────────────────────────────────┐
│ fastcontext-mcp (long-running Python process) │
│ │ │
│ ├── loads FastContext-1.0-4B-mlx-4bit model into MLX │
│ │ │
│ └── agent loop: Read / Glob / Grep in parallel │
│ returns file:line citations │
└─────────────────────────────────────────────────────────────┘
```

| Part | What it is | What it does |
|------|-----------|--------------|
| **MCP server** (`fastcontext-mcp`) | Python program running the 4B model via MLX | Provides the `fastcontext_explore` tool — the actual engine |
| **Skill** (`SKILL.md`) | Markdown instructions the agent loads | Tells the agent *when* and *how* to use `fastcontext_explore` |

Both are needed: the MCP server provides the tool, the skill tells the agent to use it.

## Install

### One-command install (recommended)

Install the setup skill, then ask your agent to install the MCP server:

```bash
uv pip install "fastcontext-mcp @ git+https://github.com/rubybear-lgtm/fastcontext.git"
npx add skill fastcontext-setup
```

Then in your AI coding tool, say:

> Install FastContext

The agent will follow the skill instructions, run the install script, register the MCP server with your detected tool(s), and tell you to restart.

### Direct install

If you prefer to run the install script yourself:

```bash
curl -fsSL https://raw.githubusercontent.com/rubybear-lgtm/fastcontext/main/scripts/install.sh | bash
```

Target a specific tool with `--target claude|opencode|codex|both` (default: auto-detect).

### What the install script does

1. Ensures `uv` is available (installs if missing)
2. Creates a venv at `~/.cache/fastcontext/venv` and installs `fastcontext-mcp`
- **Default**: downloads a prebuilt wheel from the latest GitHub release (fast — no git clone)
- **Fallback**: if no release exists, builds from the git repo
- **`--from-source`**: skip the prebuilt check and always build from git
3. Auto-detects installed AI coding tools (or uses explicit `--target`)
4. Registers the MCP server in the appropriate config file(s):
- Claude Code → `~/.mcp.json`
- OpenCode → `~/.config/opencode/opencode.json`
- Codex CLI → `~/.codex/config.toml`
5. Verifies the model loads correctly

With `--install-skills`, also downloads and installs the FastContext skill files (`fastcontext`, `fastcontext-setup`) to `~/.agents/skills/` and symlinks them into detected tool skill directories.

After installation, **restart your AI coding tool**. The `fastcontext_explore` MCP tool becomes available automatically.

### Prebuilt wheels

Releases include a prebuilt `fastcontext_mcp-<version>-py3-none-any.whl` wheel. The install script downloads it automatically by default. To install it manually:

```bash
# Download from the latest release (replace <version> accordingly)
uv pip install --python ~/.cache/fastcontext/venv/bin/python \
https://github.com/rubybear-lgtm/fastcontext/releases/latest/download/fastcontext_mcp-0.1.0-py3-none-any.whl
```

The wheel is platform-independent (pure Python) — only the `mlx-lm` and `fastcontext` dependencies require Apple Silicon at runtime.

### Manual installation

```bash
uv venv ~/.cache/fastcontext/venv --python ">=3.12"
uv pip install --python ~/.cache/fastcontext/venv/bin/python "fastcontext-mcp @ git+https://github.com/rubybear-lgtm/fastcontext.git"
```

Then register the MCP server manually — see [`skills/fastcontext-setup/SKILL.md`](skills/fastcontext-setup/SKILL.md) for the per-tool config snippets.

## Run

```bash
fastcontext-mcp
```

## Requirements

- macOS with Apple Silicon (M1/M2/M3/M4) — required for MLX
- ~5 GB free RAM for the 4B model
- Python ≥ 3.12

## License

MIT
15 changes: 15 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ dependencies = [
"tomli-w>=1.2.0",
]

[project.optional-dependencies]
test = ["pytest>=8.0.0"]

[project.scripts]
fastcontext-mcp = "fastcontext_mcp.server:main"

Expand All @@ -23,3 +26,15 @@ allow-direct-references = true

[tool.hatch.build.targets.wheel]
packages = ["src/fastcontext_mcp"]

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra"

[tool.ruff]
line-length = 100
target-version = "py312"

[tool.ruff.lint]
select = ["E", "F", "W", "I"]
ignore = ["E501"]
Loading
Loading