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
19 changes: 19 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.git
.github
.hypothesis
.mypy_cache
.pytest_cache
.ruff_cache
.venv
.aci
.compare
__pycache__
dist
build
tests
doc
*.pyc
*.pyo
*.pyd
*.log
uv.lock
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1

WORKDIR /build

COPY pyproject.toml README.md ./
COPY src ./src

RUN pip install --no-cache-dir uv \
&& uv pip install --system .

WORKDIR /data

ENTRYPOINT ["aci-mcp"]
55 changes: 52 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ aci shell
```

This launches an interactive REPL (Read-Eval-Print Loop) with:

- Command history (up/down arrows to navigate)
- Tab completion for commands
- Persistent history across sessions
Expand All @@ -102,7 +103,7 @@ This launches an interactive REPL (Read-Eval-Print Loop) with:

### Example Session

```
```text
$ aci shell

_ ____ ___ ____ _ _ _
Expand Down Expand Up @@ -142,6 +143,7 @@ Search queries support inline modifiers to filter results:
| `exclude:<pattern>` | Alias for `-path:` | `exclude:fixtures` |

Multiple exclusions can be combined:

```bash
aci search "database query -path:tests -path:fixtures"
```
Expand Down Expand Up @@ -190,13 +192,53 @@ ACI supports the Model Context Protocol (MCP), allowing LLMs to directly interac
}
```

2. Ensure `.env` exists in the working directory with required settings (see `.env.example`)
1. Ensure `.env` exists in the working directory with required settings (see `.env.example`)

3. Use natural language to interact with your codebase:
2. Use natural language to interact with your codebase:
- "Index the current directory"
- "Search for authentication functions"
- "Show me the index status"

### Docker Sidecar Delivery

For agentic coding tools, the recommended deployment model is a local Docker sidecar:

- The code repository stays on the user's machine
- The MCP server runs in a local container
- Qdrant runs either as another local container or as a cloud endpoint
- The embedding API uses the user's own API key

Build the image:

```bash
docker build -t aci-mcp:latest .
```

If you want a local Qdrant container, start it separately:

```bash
docker run -d --name aci-qdrant -p 6333:6333 qdrant/qdrant:latest
```

Then configure your MCP client to launch ACI through Docker. A complete template is available in `mcp-config.docker.example.json`.

Important runtime rules:

- Mount the host source tree read-only into the container, for example `/workspace`
- Persist `/data` as a Docker volume so `.aci/index.db` survives container restarts
- Set `ACI_MCP_WORKSPACE_ROOT` for relative paths
- Set `ACI_MCP_PATH_MAPPINGS` when the MCP client sends host-native absolute paths such as `D:\repo` or `/Users/alice/repo`

Example mapping values:

```text
ACI_MCP_WORKSPACE_ROOT=/workspace
ACI_MCP_PATH_MAPPINGS=D:\repo=/workspace
ACI_MCP_PATH_MAPPINGS=/Users/alice/repo=/workspace
```

When path mappings are configured, MCP tools can accept the host path provided by the client and resolve it to the mounted container path automatically.

### Available MCP Tools

| Tool | Description |
Expand Down Expand Up @@ -235,6 +277,7 @@ REINDEX=1 uv run python scripts/measure_mcp_search.py
### Debug Mode

Set `ACI_ENV=development` in `.env` to enable debug logging:

```
ACI_ENV=development
```
Expand Down Expand Up @@ -266,6 +309,7 @@ cp .env.example .env
```

Key settings:

| Variable | Description | Required |
|----------|-------------|----------|
| `ACI_EMBEDDING_API_KEY` | API key for embedding service | Yes |
Expand All @@ -275,6 +319,8 @@ Key settings:
| `ACI_VECTOR_STORE_API_KEY` | Qdrant API key (for Qdrant Cloud) | No |
| `ACI_VECTOR_STORE_HOST` | Qdrant host | No (defaults to localhost) |
| `ACI_VECTOR_STORE_PORT` | Qdrant port | No (defaults to 6333) |
| `ACI_MCP_WORKSPACE_ROOT` | Base directory for relative MCP paths inside the container/runtime | No |
| `ACI_MCP_PATH_MAPPINGS` | Host-to-container path prefix mappings for MCP, separated by `;` | No |
| `ACI_SERVER_HOST` | HTTP server host | No (defaults to 0.0.0.0) |
| `ACI_SERVER_PORT` | HTTP server port | No (defaults to 8000) |
| `ACI_ENV` | Environment (development/production) | No |
Expand All @@ -284,3 +330,6 @@ See `.env.example` for the full list of options.
The CLI and HTTP server will attempt to auto-start a local Qdrant Docker container only when
targeting a local endpoint (`localhost` / `127.0.0.1`). For cloud Qdrant (`ACI_VECTOR_STORE_URL`),
it will not run Docker.

When ACI itself is running inside a container, it will not attempt to launch nested Docker for Qdrant.
In that setup, run Qdrant as a separate local container or point `ACI_VECTOR_STORE_URL` to Qdrant Cloud.
49 changes: 47 additions & 2 deletions doc/README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ aci shell
```

启动后会进入 REPL(Read-Eval-Print Loop),包含:

- 命令历史(方向键上下浏览)
- 命令自动补全(Tab)
- 跨会话持久化历史
Expand Down Expand Up @@ -190,13 +191,53 @@ ACI 支持 Model Context Protocol(MCP),使 LLM 可直接调用你的代码
}
```

2. 确保工作目录存在 `.env` 且配置完整(参考 `../.env.example`)
1. 确保工作目录存在 `.env` 且配置完整(参考 `../.env.example`)

3. 可用自然语言与代码库交互,例如:
2. 可用自然语言与代码库交互,例如:
- “索引当前目录”
- “搜索认证相关函数”
- “查看当前索引状态”

### 以 Docker Sidecar 交付 MCP

对于 Agentic coding tools,推荐的交付模型是本地 Docker sidecar:

- 代码仓库保留在用户机器上
- MCP server 运行在本地容器里
- Qdrant 可以是另一个本地容器,也可以是云端地址
- Embedding API 使用用户自己的 key

构建镜像:

```bash
docker build -t aci-mcp:latest .
```

如果使用本地 Qdrant 容器,单独启动它:

```bash
docker run -d --name aci-qdrant -p 6333:6333 qdrant/qdrant:latest
```

然后让 MCP 客户端通过 Docker 拉起 ACI。完整模板见 `mcp-config.docker.example.json`。

运行时约定:

- 把宿主机源码目录以只读方式挂载进容器,例如 `/workspace`
- 把 `/data` 挂成 Docker volume,这样 `.aci/index.db` 不会随容器重建丢失
- 相对路径场景设置 `ACI_MCP_WORKSPACE_ROOT`
- 如果 MCP 客户端传的是宿主机绝对路径,例如 `D:\repo` 或 `/Users/alice/repo`,设置 `ACI_MCP_PATH_MAPPINGS`

示例:

```text
ACI_MCP_WORKSPACE_ROOT=/workspace
ACI_MCP_PATH_MAPPINGS=D:\repo=/workspace
ACI_MCP_PATH_MAPPINGS=/Users/alice/repo=/workspace
```

配置后,MCP 工具可以接受客户端传来的宿主机路径,并自动解析到容器内的挂载路径。

### MCP 可用工具

| 工具 | 说明 |
Expand Down Expand Up @@ -277,10 +318,14 @@ cp .env.example .env
| `ACI_VECTOR_STORE_API_KEY` | Qdrant API Key(Qdrant Cloud) | 否 |
| `ACI_VECTOR_STORE_HOST` | Qdrant 主机地址 | 否(默认 localhost) |
| `ACI_VECTOR_STORE_PORT` | Qdrant 端口 | 否(默认 6333) |
| `ACI_MCP_WORKSPACE_ROOT` | MCP 在容器/运行时中解析相对路径时使用的基础目录 | 否 |
| `ACI_MCP_PATH_MAPPINGS` | MCP 使用的宿主路径前缀到容器路径前缀映射,使用 `;` 分隔 | 否 |
| `ACI_SERVER_HOST` | HTTP 服务主机地址 | 否(默认 0.0.0.0) |
| `ACI_SERVER_PORT` | HTTP 服务端口 | 否(默认 8000) |
| `ACI_ENV` | 运行环境(development/production) | 否 |

完整配置请查看 `../.env.example`。

CLI 和 HTTP 服务仅在目标为本地端点(`localhost` / `127.0.0.1`)时尝试自动启动本地 Qdrant Docker 容器。若使用云端 Qdrant(`ACI_VECTOR_STORE_URL`),不会启动 Docker。

当 ACI 自身运行在容器内时,不会再尝试嵌套启动 Docker 来拉起 Qdrant。此时请把 Qdrant 作为独立本地容器运行,或直接配置 `ACI_VECTOR_STORE_URL` 指向 Qdrant Cloud。
39 changes: 39 additions & 0 deletions mcp-config.docker.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"mcpServers": {
"aci": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-v",
"HOST_SOURCE_PATH:/workspace:ro",
"-v",
"aci-mcp-data:/data",
"-e",
"ACI_MCP_WORKSPACE_ROOT=/workspace",
"-e",
"ACI_MCP_PATH_MAPPINGS=HOST_SOURCE_PATH=/workspace",
"-e",
"ACI_EMBEDDING_API_URL",
"-e",
"ACI_EMBEDDING_API_KEY",
"-e",
"ACI_EMBEDDING_MODEL",
"-e",
"ACI_EMBEDDING_DIMENSION",
"-e",
"ACI_VECTOR_STORE_URL=http://host.docker.internal:6333",
"-e",
"ACI_VECTOR_STORE_VECTOR_SIZE=1024",
"aci-mcp:latest"
],
"env": {
"ACI_EMBEDDING_API_URL": "https://api.openai.com/v1/embeddings",
"ACI_EMBEDDING_API_KEY": "your_api_key_here",
"ACI_EMBEDDING_MODEL": "text-embedding-3-small",
"ACI_EMBEDDING_DIMENSION": "1024"
}
}
}
}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ classifiers = [
"Topic :: Utilities",
]

# 运行时核心依赖(字母顺序)
# runtime dependencies
dependencies = [
"fastapi>=0.111.0",
"httpx>=0.25.0",
Expand Down
22 changes: 13 additions & 9 deletions src/aci/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,13 @@
ScannedFile,
get_default_registry,
)
from aci.core.tokenizer import (
TiktokenTokenizer,
TokenizerInterface,
get_default_tokenizer,
)
from aci.core.tokenizer import (
CharacterTokenizer,
SimpleTokenizer,
TiktokenTokenizer,
TokenizerInterface,
get_default_tokenizer,
)
from aci.core.watch_config import WatchConfig

__all__ = [
Expand All @@ -70,10 +72,12 @@
"TreeSitterParser",
"SUPPORTED_LANGUAGES",
"check_tree_sitter_setup",
# Tokenizer
"TokenizerInterface",
"TiktokenTokenizer",
"get_default_tokenizer",
# Tokenizer
"TokenizerInterface",
"TiktokenTokenizer",
"CharacterTokenizer",
"SimpleTokenizer",
"get_default_tokenizer",
# Chunker
"CodeChunk",
"ChunkerConfig",
Expand Down
2 changes: 2 additions & 0 deletions src/aci/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ class IndexingConfig:
default_factory=lambda: _get_default("indexing", "chunk_overlap_lines", 2)
)
max_workers: int = field(default_factory=lambda: _get_default("indexing", "max_workers", 4))
tokenizer: str = field(default_factory=lambda: _get_default("indexing", "tokenizer", "tiktoken"))


@dataclass
Expand Down Expand Up @@ -226,6 +227,7 @@ def apply_env_overrides(self) -> "ACIConfig":
"ACI_INDEXING_MAX_CHUNK_TOKENS": ("indexing", "max_chunk_tokens", int),
"ACI_INDEXING_CHUNK_OVERLAP_LINES": ("indexing", "chunk_overlap_lines", int),
"ACI_INDEXING_MAX_WORKERS": ("indexing", "max_workers", int),
"ACI_TOKENIZER": ("indexing", "tokenizer", str),
"ACI_INDEXING_FILE_EXTENSIONS": (
"indexing",
"file_extensions",
Expand Down
Loading
Loading