{
  "markdown": "# SeekLink\n\n<!-- mcp-name: io.github.simonsysun/seeklink -->\n\n[English](README.md) · [中文](README.zh.md)\n\n[![PyPI](https://img.shields.io/pypi/v/seeklink)](https://pypi.org/project/seeklink/)\n[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://python.org)\n[![Tests](https://github.com/simonsysun/seeklink/actions/workflows/test.yml/badge.svg)](https://github.com/simonsysun/seeklink/actions/workflows/test.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n\n> **Status: paused.** SeekLink is no longer actively maintained. For an\n> actively-developed local Markdown search engine in the same space — local\n> hybrid keyword + vector search, line-anchored results, CLI + MCP, multilingual —\n> we recommend [qmd](https://github.com/tobi/qmd).\n\nSeekLink is a local semantic search CLI and optional read-only MCP stdio server\nfor Markdown vaults. It indexes a folder of `.md` files, searches with hybrid\nkeyword + vector retrieval, and returns line-anchored results that humans and\nagents can read with simple shell commands.\n\nIt is built for personal knowledge bases, Obsidian-compatible vaults, bilingual\nEnglish/Chinese notes, and local agent workflows. MCP clients such as Claude\nCode, Cursor, and VS Code can call the same read-only search/get/status/doctor\nsurface through `seeklink[mcp]`. It is also a useful search layer for Markdown\nwiki patterns such as Andrej Karpathy's\n[llm-wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f):\nan agent can search existing pages, read precise line windows, then update the\nwiki without sending the vault to a hosted service.\n\nEverything runs locally. No API key. No cloud search service. No Obsidian plugin\nrequired.\n\n## Install\n\n```bash\nuv tool install seeklink\n# or\npip install seeklink\n```\n\nFor Apple Silicon reranking support, install the optional MLX extra:\n\n```bash\nuv tool install \"seeklink[mlx]\"\n# or\npip install \"seeklink[mlx]\"\n```\n\nFor Model Context Protocol (MCP) clients such as Claude Code, Cursor, or VS\nCode, install the optional MCP extra:\n\n```bash\nuv tool install \"seeklink[mcp]\"\n# or\npip install \"seeklink[mcp]\"\n```\n\nSeekLink requires Python's `sqlite3` module to be linked against SQLite\n3.45 or newer with FTS5 enabled. `seeklink status --vault PATH` checks this and\nprints a clear error if the runtime SQLite is too old.\n\n## Quick Start\n\n```bash\n# 1. Build the index first.\nseeklink index --vault /path/to/vault\n\n# 2. Search it.\nseeklink search \"machine learning\" --vault /path/to/vault\n```\n\nDaily use is simpler if you set a default vault:\n\n```bash\nexport SEEKLINK_VAULT=/path/to/vault\nseeklink index\nseeklink search \"agent memory systems\"\nseeklink get notes/agent-memory-patterns.md:1 -C 20\n```\n\n`seeklink search` and single-file `seeklink index path/to/file.md` use a\nresident daemon when `--vault` is not passed. The daemon keeps the embedder and\noptional reranker warm in memory; on macOS this appears as a local `Python`\nprocess. It is local-only, uses a Unix socket, and does not open a network port\nor call a cloud service. By default it exits after 15 minutes of inactivity.\nFull-vault `seeklink index` runs in-process so progress stays on stderr and the\nfinal `Done:` summary stays on stdout. `seeklink status` and `seeklink get`\nalways stay cold-start: status only reads SQLite metadata, and get reads the\nfile directly from disk. Use `--no-daemon`, `SEEKLINK_NO_DAEMON=1`, or an\nexplicit `--vault PATH` when a script needs a one-shot cold-start path.\n\nMCP users follow the same first step: build the index with\n`seeklink index --vault PATH` before registering the MCP server.\n\n## Output\n\nText search output is stable:\n\n```text\n  SCORE  PATH[:LINE]  TITLE\n           <content preview, one line, up to 120 chars>\n```\n\n- `PATH` is relative to the vault root.\n- `LINE` is 1-indexed and points to the best matching chunk in the current file.\n- Exit code is `0` for success, including no results; `1` for runtime\n  vault/config/file errors detected by SeekLink; and `2` for command-line usage\n  errors from argument parsing.\n- Scores are useful for sorting within one query. Do not compare scores across\n  reranker-enabled and reranker-disabled runs.\n\nUse JSON when an agent needs structured output:\n\n```bash\nseeklink search \"agent memory systems\" --vault PATH --json\nseeklink status --vault PATH --json\nseeklink doctor --vault PATH --json\nseeklink daemon status --json\n```\n\n## Common Commands\n\n### Search\n\n```bash\nseeklink search \"query\" --vault PATH [options]\n```\n\nOptions:\n\n```text\n--top-k N          Number of results. Default: 10.\n--json             Emit one machine-readable JSON object.\n--tags TAG [TAG]   Filter by tags. AND semantics.\n--folder PREFIX    Filter by vault-relative folder prefix.\n--rerank-k N|auto  Rerank candidate budget. Default: auto.\n--no-rerank        Skip cross-encoder reranking for this query.\n--no-daemon        Force an in-process search instead of using the daemon.\n--title-weight F   Override title/alias/heading channel weight. Default: 1.5.\n```\n\n### Get\n\nRead a precise file window without using the database or daemon:\n\n```bash\nseeklink get notes/spaced-repetition.md\nseeklink get notes/spaced-repetition.md:12\nseeklink get notes/spaced-repetition.md:12 -l 40\nseeklink get notes/spaced-repetition.md:12 -C 20\n```\n\n`-l/--lines` prints lines starting at `LINE`. `-C/--context` prints lines before\nand after `LINE`, grep-style. Path escapes such as `../..` are rejected.\n\n### Status\n\n```bash\nseeklink status --vault PATH\nseeklink status --vault PATH --json\n```\n\nStatus reports index counts, model names, index-configuration compatibility,\nSQLite WAL status, and freshness warnings. It does not load the embedding or\nreranking models.\n\n### Doctor\n\n```bash\nseeklink doctor --vault PATH\nseeklink doctor --vault PATH --json\n```\n\nDoctor checks Python, SQLite, the local database, index compatibility, daemon\nstate, and optional MLX availability. It does not download or load models, but\nmay initialize the local SeekLink database/schema if missing.\n\n### MCP\n\nThe optional Model Context Protocol (MCP) adapter lets agent clients discover\nand call SeekLink's read-only tools directly. The CLI keeps working\nindependently; MCP is another surface for the same retrieval path, not a\nreplacement.\n\n```bash\nseeklink mcp --vault PATH\n```\n\nInstall it with `seeklink[mcp]`. Build the index with the CLI first:\n`seeklink index --vault PATH`. The MCP adapter is read-only and exposes four\ntools: `search`, `get`, `status`, and `doctor`. It does not expose `index`,\nwrite notes, use HTTP/OAuth, or route through the Unix-socket daemon. Run one\nMCP server per vault. `search` keeps its text summary compact with paths and\nline anchors; result previews stay in structured content for agents that need\nthem. `status` and `doctor` may initialize or migrate the local SeekLink schema\nwhen an existing `.seeklink/seeklink.db` needs it, but they do not index or\nmodify Markdown notes. If your MCP client does not inherit your shell `PATH`,\nuse the absolute path from `which seeklink` in the examples below.\n\nClaude Code:\n\n```bash\nclaude mcp add --transport stdio --scope project seeklink \\\n  -- seeklink mcp --vault /ABS/PATH/TO/VAULT\n```\n\nCursor `.cursor/mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"seeklink\": {\n      \"type\": \"stdio\",\n      \"command\": \"seeklink\",\n      \"args\": [\"mcp\", \"--vault\", \"/ABS/PATH/TO/VAULT\"]\n    }\n  }\n}\n```\n\nVS Code `.vscode/mcp.json`:\n\n```json\n{\n  \"servers\": {\n    \"seeklink\": {\n      \"type\": \"stdio\",\n      \"command\": \"seeklink\",\n      \"args\": [\"mcp\", \"--vault\", \"/ABS/PATH/TO/VAULT\"]\n    }\n  }\n}\n```\n\n### Index\n\n```bash\nseeklink index --vault PATH\nseeklink index path/to/file.md --vault PATH\n```\n\nFull-vault indexing skips unchanged files by content hash unless the stored\nindex was built with a different embedder, vector dimension, or chunker\nconfiguration, in which case SeekLink rebuilds the derived index contents.\nSingle-file indexing updates one Markdown file only when the existing index\nconfiguration is compatible.\n\n### Daemon\n\n```bash\nseeklink daemon status\nseeklink daemon stop\nseeklink daemon restart\nseeklink daemon pid\nseeklink daemon run --vault PATH\n```\n\nYou normally do not need to start the daemon manually. `search` and single-file\n`index` auto-spawn and auto-restart it when appropriate, then it exits after\n`SEEKLINK_DAEMON_IDLE_TIMEOUT` seconds of inactivity. The default is 900 seconds\n(15 minutes); set it to `0`, `off`, `false`, or `no` to keep the daemon warm\nuntil stopped.\n\nFull-vault `index` still runs in-process for progress output. Passing `--vault`\nto `search` or single-file `index` forces a one-shot cold-start path because the\ndaemon is bound to one vault at startup. `--no-daemon` and\n`SEEKLINK_NO_DAEMON=1` also force the same cold-start path. Use\n`seeklink daemon status` to inspect the warm process and `seeklink daemon stop`\nto release its memory immediately.\n\n## How Search Works\n\nSeekLink fuses four channels with Reciprocal Rank Fusion:\n\n| Channel | Purpose |\n|---|---|\n| BM25 / FTS5 | Exact words, code terms, acronyms, CJK lexical matches |\n| Vector search | Semantic matches across different wording |\n| Title / aliases / headings | Exact note and section lookup |\n| Wikilink indegree | Small graph-quality prior from existing `[[links]]` |\n\nThe default embedder is `jinaai/jina-embeddings-v2-base-zh` through\n`fastembed`. CJK full-text search uses a jieba FTS5 tokenizer when the local\nPython/SQLite build can safely register it; otherwise SeekLink falls back to\nSQLite's built-in trigram tokenizer instead of crashing.\n\nThe default vector dimension is 768. Advanced custom-embedder experiments can\nset `SEEKLINK_EMBEDDING_DIM`, but it must match the embedder output and requires\na full `seeklink index` rebuild.\n\nOn Apple Silicon, SeekLink can rerank candidates with\n`mlx-community/Qwen3-Reranker-0.6B-mxfp8` when installed with `seeklink[mlx]`.\nReranking is local and optional; if MLX is unavailable, SeekLink falls back to\nfirst-stage hybrid RRF ranking. Use `--no-rerank` for one query or set\n`SEEKLINK_RERANKER_MODEL=\"\"` to disable it globally.\n\n## Frontmatter\n\nMarkdown frontmatter is optional. When present, SeekLink uses it for tags and\naliases:\n\n```yaml\n---\ntags: [ai, memory]\naliases: [LLM memory, agent memory]\n---\n```\n\n- `tags` support filtered search: `seeklink search \"memory\" --tags ai`\n- `aliases` are indexed for search and used when resolving wikilinks\n\n## Storage\n\nSeekLink writes one SQLite database inside the vault:\n\n```text\n/path/to/vault/.seeklink/seeklink.db\n```\n\nThe database contains source metadata, chunks, FTS5 tables, sqlite-vec vectors,\nand a wikilink graph. Delete `.seeklink/` and run `seeklink index` to rebuild.\n\n## Supported\n\n| Area | Status |\n|---|---|\n| Python | 3.11, 3.12, 3.13, 3.14 |\n| SQLite | Python `sqlite3` linked against SQLite 3.45+ with FTS5 |\n| OS | macOS and Linux |\n| Windows | Not supported as a first-class path |\n| File format | Markdown `.md` |\n| Vault style | Plain folder or Obsidian-compatible vault |\n| CJK | Native path via jieba, with trigram fallback on static SQLite builds |\n| Reranker | Optional `seeklink[mlx]` extra on Apple Silicon; disabled elsewhere |\n| Daemon | Single vault per machine |\n| MCP | Optional `seeklink[mcp]` stdio adapter, one server per vault |\n\n## Not For\n\n- Hosted or synced multi-user search.\n- Non-Markdown sources without conversion.\n- A GUI or Obsidian plugin.\n- Sub-millisecond search over millions of notes.\n- Cloud embedding or reranking APIs.\n\n## Agent Notes\n\nAgents can use SeekLink through ordinary subprocess calls:\n\n```bash\nseeklink status --vault PATH\nseeklink index --vault PATH\nseeklink search \"query\" --vault PATH --json\nseeklink get PATH:LINE -C 20 --vault PATH\n```\n\nMCP clients can use the optional read-only adapter:\n\n```bash\nseeklink mcp --vault PATH\n```\n\nTo make an agent choose SeekLink for a Markdown vault, add this to the\nproject's `AGENTS.md`, `CLAUDE.md`, or editor rules:\n\n```text\nWhen you need to search or inspect this Markdown vault, use SeekLink for\nsemantic retrieval:\n\n1. Run `seeklink status --vault PATH --json`.\n2. If no index exists or files changed, run `seeklink index --vault PATH`.\n3. Run `seeklink search \"QUERY\" --vault PATH --json`.\n4. Read exact context with `seeklink get PATH:LINE -C 20 --vault PATH`.\n\nIf SeekLink is registered as an MCP server in this client, prefer the\n`search`, `get`, `status`, and `doctor` MCP tools over shelling out to the CLI.\n\nPrefer SeekLink for conceptual, cross-language, tag/folder-filtered, or\nObsidian-style note searches. Use rg for exact literal searches.\n```\n\nFor hot loops, the daemon exposes a length-prefixed JSON protocol over the Unix\nsocket at `~/.rhizome/seeklink.sock`. Most agents should prefer the CLI JSON\nsurface unless they specifically need socket-level latency.\n\nSee [llms.txt](llms.txt) for the compact agent contract.\n\n## Evaluation\n\nSearch-quality tests live in `tests/blind/`; the method is documented in\n[docs/blind-test.md](docs/blind-test.md). Release claims should be backed by\nthe bundled fixture queries or by clearly labeled private-vault measurements.\n\n## Contributing\n\n```bash\ngit clone https://github.com/simonsysun/seeklink\ncd seeklink\nuv sync --dev\nuv run python -m pytest tests/ -q\n```\n\nKeep runtime dependencies small, keep public docs user-facing, and add a\n`CHANGELOG.md` entry for user-visible changes.\n\n## License\n\nMIT\n",
  "bytes": 13371,
  "sha": "7e561a81a06da01feb07f1a71307425739035e0f66f790a7479aecf90474b99d",
  "repo_slug": "simonsysun/seeklink",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_simonsysun_seeklink_ad094a2b/readme"
}