ideavault-mcp

ideavault-mcp

A personal MCP server that manages an Obsidian vault of project notes, provides scoped external research tools, and offers code-graph analysis via a proxied tree-sitter engine.

Category
访问服务器

README

ideavault-mcp

A personal MCP server that turns this machine's project folders into an Obsidian vault of ideas, with a few scoped external-research tools and a structural code-graph alongside it. One Express endpoint, no upstream LLM calls from the server itself — Claude (via Claude Code, Claude.ai, or the Claude mobile app) reads and writes the vault and calls these external services directly through the tools.

Vault lives at ~/ideavault/Projects/*.md — open that folder directly in Obsidian.

Architecture note: the code-graph tools aren't implemented here — they're codebase-memory (a Python/SQLite/tree-sitter engine, its own repo history preserved via git subtree when it was folded in here) spawned as a persistent child process at startup and proxied in over MCP's stdio client transport (src/codebaseMemory.ts). One external MCP surface, one systemd service, one auth boundary — but two independent engines behind it, so neither codebase had to be rewritten in the other's language to merge them. If the child process connection drops, the whole server exits (process.exit(1)) rather than silently running vault-only, so systemd's Restart=on-failure brings both back up together instead of masking a real crash. Don't also run codebase-memory as its own separate stdio MCP server (e.g. via claude mcp add) alongside this — both would open the same SQLite DB file concurrently, which risks lock contention.

Tools

Vault (repos + notes)

Tool What it does
list_repos Scans REPOS_DIR (default: home dir), detects stack (node/python/rust/go/dotnet), flags which repos already have a note
list_repo_files Lists files/folders inside a repo so Claude can decide what to read before writing a note. Skips node_modules/.git/etc and secret-looking files
read_repo_file Reads a text file's contents (README, source, config) from inside a repo. Refuses binaries and anything matching a secret/key-file pattern. Capped at 200KB
write_repo_file Creates/overwrites a file inside a repo. If the repo has no .git yet, runs git init + a snapshot commit first. Doesn't commit its own change — review with git diff
edit_repo_file Precise search-replace on an existing file — old_string must match exactly once. Same guardrails and git safety net as write_repo_file
get_idea Reads one repo's note (frontmatter + body)
upsert_idea Creates/updates a note — status, tags, blockers, next_steps, body. Omitted fields keep their old value
append_log Appends a dated line to a note's ## Log section
list_ideas Lists all notes, filterable by status or tag
search_notes Full-text search across the vault

Note frontmatter: repo, status (idea/in-progress/blocked/done/abandoned), tags[], blockers[], next_steps[], created, updated.

External research (scoped, not a general web-fetch tool)

Tool What it does Needs
solana_get_mint_info On-chain mint info (supply, decimals, authorities) via public RPC nothing
solana_get_token_price Price/liquidity via Jupiter's price API (v3) nothing
package_registry_search crates.io fuzzy search, or PyPI exact-name lookup (no public PyPI search API exists) nothing
github_search_code Search public GitHub code for reference implementations GITHUB_TOKEN in .env
telegram_bot_info A bot's public info via Telegram's getMe — token never leaves the server TELEGRAM_BOT_TOKEN_<NAME> in .env per bot
telegram_send_message Send a plain-text message via a bot you control — e.g. notify yourself TELEGRAM_BOT_TOKEN_<NAME> in .env; chat_id param or TELEGRAM_CHAT_ID_<NAME> in .env
fetch_docs Fetches page text, restricted to an allowlist: docs.rs, pypi.org, solana.com, jup.ag, github.com, raw.githubusercontent.com, telegram.org nothing

Code graph (proxied from codebase-memory, read the architecture note above)

Tool What it does
index_repository Index (or re-index) a local repo into the graph via tree-sitter — call once per project before the others
list_projects List every indexed project with file/symbol counts
search_symbols Find functions/methods/classes by name substring
get_symbol_overview One call for "show me this function and what touches it" — source + callers + callees
get_code_snippet Just one function's source by qualified name, without reading the whole file
trace_calls Walk the call graph around a symbol (callers/callees/both, 1-10 hops)
get_architecture Language breakdown, symbol counts, top folders, hotspot files — orient in an unfamiliar repo
detect_changes Map uncommitted git changes to the symbols they touched, plus a rough blast-radius (caller count)

6 languages: C#, Python, JS, TS/TSX, Rust, Go. project is optional on every query tool except index_repository — omit it and the last project named anywhere on this server is reused. Note this "last project" state now lives in the one shared codebase-memory child process behind this HTTP server rather than a stdio process per Claude Code session (its original design) — fine for one person using one project at a time, but a second concurrent caller working on a different repo will get the first caller's active project if it omits project too. Pass project explicitly to avoid relying on this. See codebase-memory's own docstring (server.py) for the full scope/limitations (name-based call resolution, no type inference, no cross-repo graph).

Graph UI

A small visual dashboard for the code graph, served by this same process — open https://ideavault.app-me.online/ (or http://127.0.0.1:3007/ locally) in a browser instead of going through chat. Source in web/ (Vite + React + TypeScript + Tailwind + Cytoscape.js), built to static files and served via express.static.

  • Dashboard — get_architecture as language/symbol-kind charts, top folders, hotspot files.
  • Search — search_symbols, click a result to open it in Graph view.
  • Graph — trace_calls rendered as an actual node/edge graph (callers, callees, or both; depth 1-4), with the selected symbol's source (get_code_snippet) alongside it.
  • Changes — detect_changes: uncommitted edits mapped to the symbols they touched, with the existing caller-count risk heuristic.

It talks to a handful of new read-only JSON routes under /api/graph/* (src/graphApi.ts) that wrap the same proxied codebase-memory tool calls the MCP surface uses — no second engine, no direct SQLite access from the UI. Those routes sit behind the same rateLimit → validateOrigin → requireToken chain as /mcp. The static page itself is unauthenticated (it's just HTML/JS/CSS); on first load it prompts for AUTH_TOKEN and stores it in localStorage, sending it as Authorization: Bearer on every API call — same token as everywhere else, no second credential.

Local dev

npm install
cp .env.example .env   # edit AUTH_TOKEN at minimum
npm run build && npm start
# or: npm run dev   (tsx watch, no build step — but the UI needs its own build below)

npm run build builds both the server and the web UI (build:web runs npm --prefix web run build, needs cd web && npm install once first). For UI-only iteration with hot reload: cd web && npm install && npm run dev — its dev server proxies /api to 127.0.0.1:3007, so run the main server (npm run dev at the repo root) alongside it.

Smoke test:

curl http://127.0.0.1:3007/health

curl -X POST http://127.0.0.1:3007/mcp \
  -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer $AUTH_TOKEN" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Deploy (this machine, following the existing app-me.online pattern)

Port 3007 is used here since 3000-3003/3005/3006 are already taken by app1-4/sift/muse (see ../NGINX.md).

  1. npm run build
  2. Fill in .env (real AUTH_TOKEN — generate with openssl rand -hex 32)
  3. Install the systemd unit:
    sudo cp deploy/ideavault-mcp.service /etc/systemd/system/
    sudo systemctl daemon-reload
    sudo systemctl enable --now ideavault-mcp
    
  4. Add the nginx server block (deploy/nginx-ideavault.conf) next to your other app-me.online blocks, then certbot --nginx -d ideavault.app-me.online.

Status: already deployed. Live at https://ideavault.app-me.online, systemd service running, DNS auto-synced by the existing namecheap-ddns timer (it discovers hosts straight from nginx server_name directives — no config changes needed there), cert auto-renews via certbot's timer.

Connect Claude to it

Claude Code (this machine or any other):

claude mcp add --transport http ideavault https://ideavault.app-me.online/mcp \
  --header "Authorization: Bearer <your AUTH_TOKEN>"

Claude Desktop / Claude.ai / Claude mobile app: custom connector URL entry (Settings → Connectors → Add custom connector) only showed up on claude.ai in a browser, not in the native mobile app UI — add it there and it syncs to mobile since connectors are account-level. The connector UI has no field for a static bearer header, so the token goes in the URL instead:

https://ideavault.app-me.online/mcp?token=<your AUTH_TOKEN>

The server checks Authorization: Bearer, X-Vault-Token, and ?token= — whichever the client can send. Once added, it still needs to be toggled on per-conversation from the tools/connectors icon in the chat composer.

Security notes (personal-use tradeoffs, read before exposing to the internet)

  • Auth is a single static shared secret, not OAuth. Fine for a personal tool used by one person, but anyone who gets the token gets full read/write on your vault and every repo's code (write_repo_file/edit_repo_file are not read-only). Treat the token like a password — this is the highest-stakes tool on the connector.
  • write_repo_file/edit_repo_file never run git commit/git push for the actual change — only a one-time snapshot commit if a repo had no .git at all, to guarantee an undo path. Everything after that is a plain working-tree edit: review with git diff / git status and commit yourself. Repos that already had uncommitted changes before an edit get flagged (hadPriorChanges) rather than silently folded in.
  • POST /mcp is rate-limited to 60 req/min per client IP (src/rateLimit.ts, in-memory fixed window). Express is set to trust proxy: loopback so this keys on the real client IP from nginx's X-Forwarded-For, not nginx's own loopback address for every request.
  • The nginx site (deploy/nginx-ideavault.conf) has access_log off — the token travels as ?token=... for clients with no header field, and nginx's default log format would otherwise write that in plaintext to /var/log/nginx/access.log on every request.
  • The systemd unit (deploy/ideavault-mcp.service) drops all Linux capabilities and blocks kernel/namespace/cgroup access (NoNewPrivileges, ProtectKernelTunables, RestrictNamespaces, etc.) — standard hardening for a plain Node HTTP server, shrinks what a hypothetical RCE (e.g. a compromised npm dependency) could reach. Filesystem sandboxing (ProtectHome/ProtectSystem) is deliberately not used since the app's whole job is read/write across the home directory.
  • If you want more isolation than "public subdomain + secret token," put this behind Tailscale instead of the public nginx route and skip the token entirely — probably the better long-term answer for a homelab tool like this.
  • Origin header is checked against ALLOWED_ORIGINS (default https://claude.ai) only when the header is present — non-browser clients (curl, Claude Code) don't send one, so they're unaffected.
  • Rotate AUTH_TOKEN (openssl rand -hex 32, update .env, sudo systemctl restart ideavault-mcp) any time it's been displayed somewhere it shouldn't live long-term — e.g. pasted into a chat transcript.
  • read_repo_file/list_repo_files refuse dotfiles, .env*, *.pem/*.key, id_rsa/id_ed25519, and filenames that look like secrets/credentials/wallet keypairs (several of these repos hold Solana keypairs as plain JSON). It's a filename-pattern blocklist, not content scanning — good enough for a personal tool, not a substitute for actually keeping keys out of these directories.
  • The Graph UI stores AUTH_TOKEN in the browser's localStorage after you enter it once — same token as everywhere else, so anyone with it still has full read/write, not just graph-read access. /api/graph/* is read-only, but the token itself isn't scoped down for the browser. Fine on a device you trust; don't paste the token into that prompt on a shared machine.

Roadmap ideas (not built yet)

  • Bulk-seed notes for every repo in list_repos that has hasNote: false
  • A build-mcp-app widget for browsing/filtering ideas visually in chat
  • Swap the static token for OAuth (CIMD) if this ever needs to support more than one person

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选