AI 어시스턴트에게 파일 시스템 기능을 부여하세요
요약
Octofs는 Model Context Protocol(MCP)을 기반으로 개발된 독립형 Rust 바이너리로, AI 코딩 어시스턴트에게 파일 시스템 접근 기능을 제공합니다. 이 도구는 파일 읽기, 원자적 편집, 디렉토리 탐색 등 일반적인 스크립팅 방식의 한계를 뛰어넘어 높은 안전성과 효율성을 자랑합니다.
핵심 포인트
- AI 어시스턴트에 파일 시스템 기능 부여 (눈/손)
- Rust 바이너리 기반으로 안정성 및 성능 우수
- 원자적 다중 편집, SSH/SFTP 등 고급 기능 지원
- MCP 레지스트리를 통해 다양한 AI 도구와 연동
AI 어시스턴트에게 파일 시스템 슈퍼파워를 부여하세요
rmcp 3.x, tokio, axum을 기반으로 모델 컨텍스트 프로토콜(Model Context Protocol)을 통해 파일 시스템 도구를 노출하는 독립형 Rust 바이너리입니다.
설치 · 빠른 시작 · 기능 · 도구 참조 · 문서 · 아키텍처 · 변경 로그
MCP 레지스트리: io.github.Muvon/octofs
사용자님의 AI 코딩 어시스턴트(Cursor, Claude, Windsurf 등)는 똑똑하지만, 사용자님의 파일 시스템에는 눈이 없습니다. Octofs가 그 간극을 메워주어 AI에게 다음 기능을 제공합니다:
눈 — 파일 읽기, 내용 검색, 디렉토리 탐색
손 — 파일을 원자적으로 생성, 편집, 일괄 수정
컨텍스트 — 명령어 실행, 작업 디렉토리 관리
도달 범위 — 투명한 SSH/SFTP: 모든 파일 도구가 ssh://를 허용합니다.
URL
┌─────────────────────────────────────────────────────────────┐
│ 사용자: "모든 에러 핸들링을 anyhow::Context를 사용하도록 리팩토링하세요" │
├─────────────────────────────────────────────────────────────┤
...
| 기능 | Octofs | 일반적인 대안 |
|---|---|---|
| 구현 방식 | 컴파일된 Rust 바이너리, 런타임 없음 | Python/Node 스크립트 |
| 내용 검색 | 컨텍스트 라인을 포함한 내장 검색 | 문자열 매칭만 |
| 일괄 작업 | 단일 파일에 대한 원자적 다중 편집 | 한 번에 하나씩 |
| 라인 주소 지정 | 복합 N:hh ID — 적용 시 해시 검증; 오래된 수정은 재배치 힌트와 함께 실패 | 숫자만 |
| 전송 방식 | STDIO + 스트림 가능한 HTTP | STDIO만 |
| 셸 통합 | 자동 포그라운드-백그라운드 핸드오프 | 제한적이거나 없음 |
| 원격 파일 | 모든 파일 도구에서 투명한 SSH/SFTP 지원 | 없음 |
| 재읽기 | 델타 뷰 — 파일을 다시 볼 때 변경된 부분만 반환 | 매번 전체 파일 |
| 안전성 | Gitignore 인식, 오래된 쓰기 감지, 경로 유효성 검사 | 전체 파일 시스템 접근 |
cargo install octofs
brew install muvon/tap/octofs
npx -y @muvon/octofs --version
래퍼(wrapper)가 첫 실행 시점에 맞는 사전 빌드 바이너리를 다운로드합니다. MCP 설정:
{
"mcpServers": {
"octofs": {
...
플랫폼별 GitHub 릴리스에서 다운로드하세요:
| Platform | Target |
|---|---|
| Linux (x86_64) | x86_64-unknown-linux-musl |
| ... |
각 릴리스는 또한 MCP 번들을 지원하는 클라이언트(예: Claude Desktop)에서 원클릭 설치를 위한 .mcpb 번들을 함께 제공합니다. MCP 레지스트리에는 io.github.Muvon/octofs로 게시됩니다.
Rust 1.95 이상이 필요합니다.
git clone https://github.com/muvon/octofs
cd octofs
cargo build --release
...
CLI는 mcp 하위 명령어를 사용하므로, 구성(config)은 인수로 ["mcp"]를 전달해야 합니다.
Cursor (~/.cursor/mcp.json):
{
"mcpServers": {
"octofs": {
...
Claude Desktop (macOS의 경우 ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"octofs": {
...
Windsurf (~/.windsurf/mcp.json):
{
"mcpServers": {
"octofs": {
...
만약 Octofs가 PATH에 없다면, 바이너리의 전체 경로를 사용하십시오(예: /usr/local/bin/octofs 또는 ./target/release/octofs).
MCP 서버는 AI 어시스턴트가 연결될 때 자동으로 시작됩니다.
AI 어시스턴트에게 다음을 요청하십시오:
- "프로젝트 구조를 보여줘"
- "main.rs 파일을 읽어줘"
- "코드베이스에서
unwrap()사용처를 모두 검색해 줘" - "test.rs라는 새 파일을 만들어 줘"
파일 및 디렉토리 보기(View Files & Directories) — 단일 파일 읽기 (여러 개에 대해 병렬로 view 호출), 글롭 패턴으로 디렉토리 목록화, 내용 검색
스마트 자르기(Smart Truncation) — 대용량 파일은 컨텍스트 창을 압도하는 것을 방지하기 위해 지능적으로 잘립니다.
Gitignore 인식(Gitignore-Aware) — 디렉토리 순회 중 .gitignore 패턴을 준수합니다.
라인 범위(Line Ranges) — 음수 인덱싱(-1 = 마지막 줄)을 사용하여 특정 라인 범위를 읽습니다.
원격 파일 (SSH/SFTP) — 모든 파일 도구는 ssh://user@host:port/path URL을 허용합니다 (원격 파일 시스템 참조).
파일 생성 (Create Files) — 자동 부모 디렉터리 생성을 통해 새 파일 생성문자열 대체 (String Replace) — 공백에 대한 퍼지 폴백(fuzzy fallback)을 사용하여 정확한 문자열 일치 항목을 대체삭제 (Delete) — 파일 제거 (실행 취소로 복구 가능)실행 취소 (Undo) — 마지막 편집 상태 되돌리기 (파일당 최대 10단계, 메모리 내)일괄 편집 (Batch Edit) — 단일 파일에 대해 여러 삽입/대체 작업을 원자적으로 수행오래된 쓰기 보호 (Stale-Write Protection) — 모든 편집 대상의 N:hh id는 적용 시점의 파일과 검증됩니다. 오래된 id가 실패할 경우, 잘못된 줄을 편집하는 대신 현재 내용과 이동한 위치를 통해 알려줍니다.콘텐츠 검색 (Content Search) — 컨텍스트 라인과 함께 파일 내 문자열 검색라인 추출 (Line Extraction) — 한 파일에서 다른 파일로 특정 라인 범위 복사명령 실행 (Command Execution) — 출력 캡처와 함께 셸 명령어 실행백그라운드 프로세스 (Background Processes) — 긴 명령은 완료 알림과 함께 백그라운드에서 자동으로 계속 진행됨작업 디렉터리 (Working Directory) — 작업에 대한 작업 디렉터리 컨텍스트 설정/가져오기/초기화
모든 라인은 복합 id N:hh로 주소 지정됩니다.
— 이는 1부터 시작하는 위치와 내용의 2자리 헥스 해시(FNV-1a)를 더한 것입니다. view는 라인을 N:hh|content 형식으로 렌더링합니다:
1:a3|fn main() {
2:f1| println!(
범위(ranges, 끝에서부터의 음수 카운트)와 삽입 앵커 `0`
(파일 시작) / `-1`
(추가).
Octofs는 셸 오용을 감지합니다. 예를 들어 `cat`, `grep`, `find`, 또는 `sed` 같은 명령어들은 전용 MCP 도구를 사용해야 하는데, Octofs는 이를 감지하고 어떤 도구를 사용할지 설명하는 오류를 반환하며 거부합니다. 호출은 실패하며 아무것도 실행되지 않습니다. 이는 의도적이며 설정할 수 없습니다. 전용 도구들은 원시 셸 출력으로는 얻을 수 없는 라인 ID, `.gitignore` 인식 기능, 그리고 원격 호스트 지원 기능을 제공하기 때문입니다. 파이프라인 (`cargo build 2>&1 | grep error`)은 계속 허용됩니다. 단독으로 해당 프로그램을 호출하는 것만 차단됩니다.
다른 안내 사항(범위 초과, fuzzy-match 알림)은 성공적인 응답에 추가됩니다.
**표준 입/출력 전송 (Standard input/output transport).** 모든 MCP 클라이언트와 작동합니다.
`octofs mcp`
**스트리밍 HTTP 전송 (Streamable HTTP transport)** 원격 액세스 또는 다중 클라이언트 시나리오용입니다.
`octofs mcp --bind 0.0.0.0:12345`
클라이언트를 `http://localhost:12345/mcp`에 연결합니다.
기본적으로 Octofs는 현재 디렉토리에서 작동합니다. 다른 루트를 지정하려면:
{
"mcpServers": {
"octofs": {
...
모든 경로 매개변수 — 그리고 `--path` 자체 — 는 `ssh://` 또는 `sftp://` URL을 허용합니다:
원격 세션 루트: 상대 경로는 원격 호스트에서 해결됩니다.
octofs mcp --path ssh://[email protected]/var/www/app --ssh-key ~/.ssh/id_ed25519
view path="ssh://[email protected]/etc/nginx/nginx.conf"
**로그인 홈(Login home)** — `ssh://host` (경로 없음) 및 `ssh://host/~/dir`는 `ssh host` / `scp host:dir`와 같이 로그인 사용자 홈을 기준으로 해결됩니다. `.`ssh://host/dir`는 절대 경로를 유지합니다.**OpenSSH 설정** — 호스트 별칭은 로컬 OpenSSH 설정(Include 및 Match 포함)을 통해 해결되며, `HostName`, `User`, `Port`, 그리고 단일한 `ProxyJump`를 준수합니다. 명시적인 URL 값이 우선하므로, `ssh://dev/path`와 `ssh://[email protected]:2222/path` 모두 예상대로 작동합니다. 다중 홉(Multi-hop) `ProxyJump` 및 `ProxyCommand` 라우팅은 명확한 오류와 함께 거부됩니다.**인증 (Authentication)** — 호스트의 `IdentityAgent`를 사용합니다.
(e.g. 1Password) 또는 `$SSH_AUTH_SOCK`, 그런 다음 키 파일 —`--ssh-key`가 제공된 경우 호스트의 `IdentityFile` 항목들, 그리고 `~/.ssh`에 있는 기본값들(`id_ed25519`, `id_ecdsa`)을 통과합니다. 암호 구문으로 보호되는 키 파일은 직접 지원되지 않으므로 대신 에이전트를 사용하십시오.**RSA 키는 지원되지 않습니다** — Rust의 `rsa` 크레이트는 수정되지 않은 타이밍 측면 채널(Marvin attack, RUSTSEC-2023-0071)을 가지고 있으므로 octofs는 RSA를 완전히 제외하고 빌드되었습니다. 대신 ed25519 키를 사용하십시오 (`ssh-keygen -t ed25519`); ecdsa도 작동합니다. RSA 전용 설정은 키 이름을 명시하는 명확한 오류로 실패합니다.**호스트 키** — OpenSSH의 `accept-new` 정책에 따라 `~/.ssh/known_hosts`와 검증됩니다: 알 수 없는 호스트는 첫 사용 시 기록되며, 변경된 키는 닫힘(fail closed)으로 처리됩니다.— 연결 시간 초과 (기본값 30). 연결은 호스트별로 풀링되고, 전송 keepalive를 통해 활성 상태가 유지되며, 끊어지면 자동으로 재연결됩니다. `--ssh-timeout SECS` — 명령어는 항상 Octofs가 실행되는 머신에서 실행됩니다. 파일 도구(`shell`, `stays local`, `view`, `text_editor`, `batch_edit`, `extract_lines`, `workdir`)만 원격 호스트에 접근합니다.
**파일 읽기:** (`path`는 단일 경로입니다; `start`/`end`는 줄 번호 또는 줄 ID입니다)
{"path": "src/main.rs"} // 전체 파일
{"path": "src/main.rs", "start": 10, "end": 20} // 10–20번째 줄
{"path": "src/main.rs", "start": 42, "end": 42} // 단일 줄
...
출력은 모든 줄을 `N:hh|content`로 렌더링합니다 — `N:hh` 접두사는 `batch_edit`와 `extract_lines`가 대상으로 삼는 줄 ID입니다.
여러 파일을 읽으려면 여러 개의 `view` 호출을 수행하십시오 — 이들은 병렬로 실행됩니다.
이미 반환된 콘텐츠와 줄 ID를 재사용하십시오. 인접한 창들을 결합하여 관련 함수나 블록 전체를 한 번의 호출로 읽으십시오. 위치가 알려지지 않은 경우, 충분한 `context`와 함께 `content`를 사용하십시오.
일치하는 부분을 이해한 후, 누락된 주변 코드만 가져옵니다.
겹치는 범위를 다시 읽거나 편집 대상을 선택하기 위해 범위를 반복적으로 좁히지 마십시오.
파일이 변경되었을 수 있거나, 출력이 잘렸거나, 이전 컨텍스트를 사용할 수 없을 때 다시 읽습니다.
빈 파일은 `[empty file, 0 lines]`를 반환합니다.
초기 또는 강제 전체 읽기 시에만 해당됩니다.
**Delta views:** 세션이 이미 제공한 파일의 전체 보기(whole-file view)는 변경된 청크만 반환합니다. 즉, `...`와 동일한 스타일로,
삭제된 줄은 이전 ID 범위로 접혀서 표시되거나 — 또는
`[마지막으로 보거나 편집한 이후 변경되지 않은 부분: N줄. 전체 보기(Pass full): true를 전달하여 다시 읽을 수 있습니다.]`
Octofs 자체 편집 기록이 캐시를 최신 상태로 유지하므로, 사용자의 `batch_edit` 후 재보기는 한 줄의 비용만 발생합니다. 범위 보기와 콘텐츠 검색은 항상 요청된 내용을 정확하게 렌더링합니다.
`full`을 사용하면 범위나 검색에 영향을 미치지 않으므로, 완전한 파일 읽기를 강제하려면 `start`/`end` 및 콘텐츠 검색을 생략하십시오. 이는 출력 제한을 변경하지 않습니다.
`{"path": "src/main.rs", "full": true} // 전체 파일 강제 (예: 컨텍스트 압축 후)`
**디렉터리 목록:**
{"path": "src/"}
{"path": "src/", "pattern": "*.rs"}
{"path": "src/", "max_depth": 2, "include_hidden": true}
원격 디렉터리(`ssh://host/path`, `pattern` 또는 `content` 없음)의 단순 목록은 기본적으로 `max_depth: 1`—루트 항목만—을 사용합니다. 왜냐하면 모든 하위 디렉터리를 탐색하는 것이 SFTP 왕복(round trip) 비용이 들기 때문입니다. 더 깊게 탐색하려면 `max_depth`를 명시적으로 전달하십시오. `pattern` 또는 `content`로 검색할 때는 항상 전체 트리를 탐색하며, 이는 `rg`와 같습니다.
`pattern`은 ripgrep의 `-g/--glob`과 동일한 gitignore 스타일의 glob 문법을 사용합니다:
`/`가 없으면 어떤 깊이의 파일 이름에도 일치하며, `/`를 포함하는 패턴은 반환된 상대 경로에 일치합니다. 이 문법은 하나의 경로 구성 요소 내에서 `*`, 디렉터리 전반에 걸쳐 `**`, `?`, `[abc]`와 같은 문자 클래스, `*.{rs,toml}`과 같은 중괄호 대안(brace alternatives), 그리고 선행 `!` 제외를 지원합니다. 하나의 MCP 문자열에 순서가 지정된 glob을 전달하려면 `|`를 사용하십시오. 나중에 오는 glob이 우선권을 가집니다:
Octofs가 gitignore를 인식하는 방식으로 탐색하여 발견한 파일들을 필터링합니다. 숨겨진 경로를 포함하려면 `include_hidden: true`를 사용하세요. 패턴은 4096 UTF-8 바이트, 64개의 `|`로 구분된 규칙, 그리고 16개의 중첩된 중괄호 레벨로 제한됩니다. 이들은 단일 라인이어야 하며 탐색 전에 검증됩니다. 리터럴(literal)의 선행 `#` 또는 `!`를 사용하려면 각각 `\#` 또는 `\!`로 이스케이프해야 합니다. 그렇지 않으면 `#`는 gitignore 주석으로 조용히 작동하는 대신 거부됩니다. `{,rs}`와 같은 빈 중괄호 대안은 ripgrep이 지원하지 않기 때문에 거부됩니다.
**내용 검색 (Content search):** (기본적으로 리터럴이며, Rust 정규식의 경우 `regex: true`를 설정하세요. `(?i)` = 대소문자 구분 안 함)
{"path": "src", "content": "fn main"}
{"path": "src", "content": "unwrap()", "context": 3}
{"path": "src", "content": "(?i)error", "regex": true}
...
여러 루트에 걸친 rg 스타일 검색의 경우, `content`가 설정되면 `path`는 `|`로 구분된 리터럴 파일 및 디렉토리를 허용합니다. 이 루트들은 정규식이 아니므로, rg의 위치 기반 경로 인자처럼 각각 존재해야 합니다. `|`를 포함하는 실제 경로는
AI 자동 생성 콘텐츠
본 콘텐츠는 GitHub Claude Ecosystem의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기