한줄 요약
zvec-grep(zg)은 사람과 코딩 에이전트가 같은 workspace를 탐색하도록 ripgrep, BM25, vector search를 하나의 local-first interface로 묶은 TypeScript 도구다.[1][2]
핵심 가치는 단순 키워드 검색이 아니라 의미·관계·cross-file 질문을 hybrid retrieval로 좁힌 뒤, 짧은 source-linked evidence를 반환하는 데 있다.[2][6]
이 저장소는 moc-mcp의 로컬 MCP 서버 계층, 2026-04-22-claude-context의 semantic code-search 계열, moc-dev-tools의 코드 분석 도구 지형과 직접 연결된다.[2]
소스 범위와 provenance
- 원본: zvec-ai/zvec-grep.[1]
- 조사 기준:
mainpinned commit52653951b24617762f4ab0c71c34d594e5001617.[2] - 패키지:
@zvec/zvec-grep0.2.1, Node.js>=22, Apache-2.0.[3] - GitHub API 관찰값(2026-09-04T22:56:18Z): TypeScript, 2,623 stars, 128 forks, open issues 31, archived=false.[1]
- 같은 시점 bounded query에서 최신 published release는
v0.2.0, 최신 tag는v0.2.1이었다. 따라서 main pinned commit·npm package version·published release를 서로 같은 것으로 취급하지 않았다.[1][3] - raw capture는 selected file
35개와 전체 manifest를 보존한 provenance record이며, complete source mirror가 아니다.[1] - upstream checkout/raw URL은
35/35개 HTTP 200, byte count·SHA-256 일치로 검증했다. persistent raw body는 credential-safe sanitized derivative이고, body는487,491bytes, SHA-256은0eb140d42def833cf62f113b2b4190755cd8c7fb53157cca9b51f1f21bc39f16이다.[1] - credential-like 값은 capture 과정에서
13건[REDACTED]처리했고, 최종 persistent capture의 unredacted matcher는0이었다. 원래 값은 raw, evidence, metadata, summary에 보존하지 않았다.
제품 표면
CLI와 agent integration
zg install은 로컬 MCP server를 통해 Codex, Claude Code, Qwen Code, Qoder, OpenCode, Cursor 같은 agent integration을 설정하는 installer다.[4] package manifest는 zg binary를 dist/cli/index.js에 연결하고, build·lint·format·typecheck·test 명령과 package lifecycle을 명시한다.[3]
Direct 실행과 shared daemon/server 실행을 모두 제공하며, server guide는 refresh 정책·lifecycle·인증·로그의 경계를 별도로 설명한다.[7] README의 기본 방향은 파일·index·local model은 로컬에 두고, remote embedding은 사용자 허가가 있을 때만 보내는 local-first 모델이다.[2]
MCP toolset
기본 agent toolset은 의도적으로 indexed search만 노출한다.[5] 정확한 단어·파일명·regex 탐색은 native grep/rg로 보내고, wording·의미·관계·cross-file synthesis가 필요한 workspace 질문은 zvec_grep_search를 사용한다.[5]
full toolset에는 zvec_grep_index, zvec_grep_index_drop, zvec_grep_rg, zvec_grep_index_status, zvec_grep_server_status가 추가된다.[9] 특히 persistent index의 생성·재빌드·삭제는 명시적 요청이 있을 때만 호출하도록 MCP guidance와 tool description이 함께 제한한다.[9]
MCP 검색 응답은 agent context를 과도하게 부풀리지 않도록 bounded snippet 중심의 compact text를 기본으로 하며, 내부 CLI transport에서는 별도 structured content를 선택할 수 있다.[9] Streamable HTTP transport는 modern/legacy session 경계와 idle session expiry/capacity 처리를 갖고, 만료되거나 모르는 session에는 Unknown or expired MCP session. 오류를 반환한다.[10]
구현 구조
색인 pipeline
색인은 scanner가 include/exclude, gitignore, hidden/generated directory, symlink, file-size/type 규칙을 적용한 뒤 변경 파일만 diff한다. 그 결과를 extractor → embedding model → zvec storage 순서로 처리하고, 실패 파일·실패 이유·progress·truncation 진단을 상태로 남긴다.[6][13]
indexing implementation에는 transient embedding failure에 대한 bounded retry 상수와 permanent remote model/dimension failure의 fail-fast 분류가 있다.[13] 이번 테스트에서는 실패 파일 재시도·stale record 삭제·rebuild·request-specific embedding failure context가 실제로 동작하는 것을 확인했다.[13]
추출과 저장
입력은 code, Markdown, text, image route로 나뉜다. 코드 extractor는 Tree-sitter grammar와 language adapter로 symbol/scope/source range를 수집하고, grammar 또는 adapter가 없으면 plain-text fallback으로 내려간다.[14]
영속 index는 @zvec/zvec native binding을 통해 fragment/entity metadata, text field, vector field, file metadata를 보관하는 구조다. 저장 implementation은 batch upsert와 파일별 replace/delete/failed 상태 기록을 제공한다.[18]
검색은 lexical FTS route와 vector route에서 후보를 회수하고, 필요하면 adaptive recall과 symbol preference를 적용한 뒤 candidate를 deduplicate/fuse/rerank하고 bounded evidence로 materialize한다.[6][12]
query, queries, fts, vector, fuse, path glob, file type, symbol, modification time, freshness 같은 입력이 이 계획으로 정규화된다.[5][12]
Embedding model
catalog에는 local llama-cpp 모델(EmbeddingGemma·Qwen3), transformers-js, model2vec, remote Qwen text/multimodal embedding이 함께 등록되어 있다.[15] local model은 pinned Hugging Face recipe/revision과 dimension·metric·token limit을 catalog에 두고, Qwen 모델은 endpoint·batch/input/image limit을 명시한다.[15]
Remote embedding은 Qwen API key/endpoint를 설정할 수 있지만, credential은 provider 접근 설정일 뿐 data transfer authorization 자체가 아니다. CLI의 --allow-remote는 현재 command 범위이고, MCP/Workspace grant는 별도 승인 경로다.[8]
보안·권한 경계
daemon HTTP server는 loopback host만 허용한다. 코드에는 1 MiB request body limit, host/origin 검사, bearer token 검사가 있고, shutdown control endpoint도 별도 token 검사를 거친다.[7][11]
remote embedding Workspace grant는 workspace roots, provider, model, endpoint, target fingerprint를 묶어 서명한다. signing key의 기본 위치는 workspace 밖의 ~/.zvec-grep/authorization-signing.key이고, grant revoke/revoke-all과 signature verification 경로가 구현되어 있다.[8][16]
MCP request state는 tool arguments·authorization target·disclosure에 binding되고 replay guard를 둔다. 이 경계는 remote provider로 data가 나가는 순간을 tool approval과 별도의 사용자 authorization으로 취급하기 위한 것이다.[8][9]
이번 정적 scan에서 child process 호출은 managed ripgrep와 server/CLI lifecycle 구현에 한정되어 보였고, dynamic eval/new Function 패턴은 0건이었다.[2] 다만 이는 source-level inspection이지 독립적인 production security audit이나 악성 workspace에 대한 sandbox 검증은 아니다.
이번 checkout에서 실제 실행한 검증
| 구분 | 실제 결과 |
|---|---|
| 의존성 | npm ci --ignore-scripts 성공 |
| build | npm run build 성공 |
| 품질 | npm run lint, npm run format:check, npm run typecheck 모두 성공 |
| focused tests | MCP/HTTP·authorization·search·extraction·model/storage 중심 71 pass / 0 fail |
test:all root | 231 pass / 0 fail |
test:all unit | 113 pass / 1 skip / 0 fail — Windows path separator 테스트 skip |
test:all integration | 26 pass / 0 fail |
test:all e2e | 9 pass / 0 fail |
| aggregate | 379 pass / 1 skip / 0 fail |
| package consumer | 1 pass / 0 fail |
| package dry-run | zvec-zvec-grep-0.2.1.tgz, 403 files, 441,045 bytes |
| CLI smoke | local build의 version/help와 managed-rg fixture 검색 성공 |
| CI 정의 | quality·coverage·package candidate·multi-OS unit/integration-e2e job 확인; test:coverage 자체는 이번 로컬 run에서 실행하지 않음.[3][17] |
한계와 미실행 범위
- 실제 사용자의 대규모 repository를 대상으로 한 index 품질·검색 relevance benchmark는 실행하지 않았다.
- Qwen API에 실제 credential을 사용한 외부 embedding request, remote data transfer, provider quota/rate-limit 운영은 실행하지 않았다.[8]
- local model artifact를 새로 다운로드하는 장시간 cold-start, GPU/WebGPU/Metal 성능 비교, macOS·Windows native runtime은 이 Linux checkout에서 독립 재현하지 않았다.[15][17]
- 실제 Codex/Claude/Qwen/Qoder IDE agent session과 MCP client 연결, prompt injection·malicious workspace·production deployment 보안 audit은 수행하지 않았다.
- benchmark corpus에 기록된 agent/검색 품질 수치는 source-reported 또는 repository fixture 범위로만 취급하며, 이번 조사에서 새 benchmark 성공으로 승격하지 않았다.
판단
zvec-grep은 “에이전트가 알아서 index를 만들고 모든 파일을 외부 모델로 보내는 RAG 서버”라기보다, exact grep과 semantic/hybrid retrieval을 분리하고 persistent index mutation·remote embedding·MCP tool surface에 각각 동의 경계를 둔 local workspace search substrate에 가깝다.[2][5]
강점은 AST-aware evidence, incremental lifecycle, bounded agent output, local model catalog, explicit remote authorization의 결합이다.[6][8][14]
반대로 검색 품질·cold-start 비용·native zvec/model backend의 운영 신뢰성은 실제 프로젝트와 target platform에서 별도로 측정해야 한다.[7][15]
Sources
[1] https://api.github.com/repos/zvec-ai/zvec-grep — GitHub REST repository metadata
> ""description”: “Local-first search across your workspace, built for humans and AI agents.”,”
[2] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/README.md — zvec-grep pinned README
> “The local-first search layer for humans and agents.”
[3] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/package.json — zvec-grep pinned package manifest
> ""description”: “Agent-friendly hybrid workspace search across code and non-code content.”,”
[4] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/docs/01-agents.md — zvec-grep agent guidance
> “zg install connects zvec-grep to supported agents through the local MCP server.”
[5] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/docs/03-mcp.md — zvec-grep MCP documentation
> “The default agent toolset intentionally exposes only search:”
[6] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/docs/04-pipeline.md — zvec-grep indexing and search pipeline
> “The shortest query uses hybrid ranked retrieval:”
[7] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/docs/06-server.md — zvec-grep server documentation
> “The Server only accepts loopback listen addresses.”
[8] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/docs/07-embedding.md — zvec-grep embedding documentation
> “Credentials configure access to a provider; they do not authorize data transfer.”
[9] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/src/mcp/tools.ts — zvec-grep MCP tool implementation
> “Call zvec_grep_index only when persistent indexing or index deletion is explicitly requested.”
[10] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/src/mcp/http-transport.ts — zvec-grep MCP HTTP transport
> “Unknown or expired MCP session.”
[11] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/src/daemon/http-server.ts — zvec-grep daemon HTTP server
> “Daemon HTTP server requires a loopback host.”
[12] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/src/engine/pipeline/search/index.ts — zvec-grep hybrid search implementation
> “const fused = timings.timeSync(“fusion”, () => fuseCandidates(candidates));”
[13] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/src/engine/pipeline/indexing/index.ts — zvec-grep indexing implementation
> “const EMBEDDING_TRANSIENT_MAX_RETRIES = 3;”
[14] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/src/engine/extraction/code/extractor.ts — zvec-grep code extractor
> “if (!hasGrammar(source.file.format) || !adapter) {”
[15] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/src/engine/models/catalog.ts — zvec-grep embedding model catalog
> ""local/embeddinggemma-300m”: { backend: “llama-cpp”, reference: “local/embeddinggemma-300m”,”
[16] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/src/authorization/store.ts — zvec-grep remote embedding authorization store
> “signature: sign(unsigned, key),”
[17] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/.github/workflows/ci.yml — zvec-grep CI workflow
> ”- run: npm run test:coverage”
[18] https://raw.githubusercontent.com/zvec-ai/zvec-grep/52653951b24617762f4ab0c71c34d594e5001617/src/engine/storage/zvec.ts — zvec-grep zvec storage implementation
> “const ZVEC_UPSERT_BATCH_SIZE = 1024;”