Skip to main content
Glama
sangwookp9591

obsidian-local-mcp-server

obsidian-local-mcp-server

Obsidian vault를 MCP 서버로 노출해 Claude Code와 Codex에서 언제든 검색·조회·편집할 수 있게 한다. 서버는 Docker 컨테이너로 돌고, vault는 호스트 폴더를 볼륨으로 마운트한다.

MCP 2026-07-28 명세(Stateless HTTP)를 구현했다. 세션도 initialize 핸드셰이크도 없이 요청 하나하나가 독립적으로 완결되므로, 컨테이너 한 대에 여러 클라이언트가 자유롭게 붙는다.

호스트                                      Docker
┌──────────────┐
│ Claude Code  │──┐
├──────────────┤  │  127.0.0.1:8787   ┌─────────────────────────┐
│ Codex        │──┼──────────────────▶│ obsidian-mcp (non-root) │
├──────────────┤  │   Stateless HTTP  │ Stateless HTTP :8787    │
│ 그 외 MCP     │──┘                   └───────────┬─────────────┘
│ 클라이언트     │                                  │ volume
└──────────────┘                                  ▼
                                    ┌───────────────────────────┐
                                    │ 호스트의 Obsidian vault    │
                                    │ (VAULT_PATH → /vault)     │
                                    └───────────────────────────┘

1. 빠른 시작

git clone <이 저장소>
cd obsidian-local-mcp-server

cp .env.example .env      # vault 경로를 여기서 지정한다 (2절 참고)
npm run up                # 이미지 빌드 + 컨테이너 기동
npm run health            # {"ok":true,"notes":652,"vault":"/vault"}

그다음 클라이언트를 붙인다.

# Claude Code (모든 프로젝트에서 사용)
claude mcp add --transport http --scope user obsidian-vault http://127.0.0.1:8787/mcp

# Codex
codex mcp add obsidian-vault --url http://127.0.0.1:8787/mcp

두 클라이언트 모두 새 세션부터 도구가 보인다. MCP 서버 목록은 세션 시작 시 로드된다.


Related MCP server: Recalla

2. 최초 vault 폴더 등록

vault 경로는 .envVAULT_PATH 하나로 정해진다. 이미지에 노트를 굽지 않고 호스트 폴더를 그대로 마운트하므로, Obsidian에서 고친 내용이 즉시 서버에 보이고 MCP 쓰기 도구의 결과도 호스트 파일에 그대로 남는다.

cp .env.example .env

.env를 열어 세 값을 채운다.

# 연결할 Obsidian vault의 절대경로 (필수)
VAULT_PATH=/Users/iron/Project/ai-docs/vault

# 호스트에 노출할 포트 (기본 8787)
MCP_PORT=8787

# 마운트된 vault를 읽고 쓰려면 호스트 사용자와 UID를 맞춰야 한다
UID=501
GID=20

UID/GID는 아래로 확인한다. 이 값이 틀리면 읽기는 되는데 쓰기 도구만 권한 오류가 난다.

echo "UID=$(id -u)"
echo "GID=$(id -g)"

경로를 바꿨으면 컨테이너를 다시 만든다. restart가 아니라 up이어야 마운트가 새로 잡힌다.

npm run down && npm run up
npm run health     # notes 개수로 새 vault가 잡혔는지 확인

vault를 여러 개 쓰려면 .envMCP_PORT를 다르게 준 복사본으로 컨테이너를 하나 더 띄우고, 클라이언트에 다른 이름으로 등록하면 된다.


3. 서버 실행 방법

평소 운영 (Docker Compose)

npm run up        # 빌드 + 기동 (docker compose up -d --build)
npm run down      # 중지 및 컨테이너 제거
npm run restart   # 재시작 (코드 변경은 반영되지 않는다 — up을 써야 한다)
npm run logs      # 로그 추적
npm run health    # 상태 확인
docker compose ps # 컨테이너 상태 (healthy 여부)

restart: unless-stopped 정책이라 Docker Desktop이 켜지면 컨테이너도 함께 올라온다. 직접 npm run down으로 내리기 전까지는 계속 살아 있다.

코드를 고쳤다면 npm run up으로 다시 빌드해야 반영된다. restart는 이미지가 아니라 컨테이너만 다시 띄우므로 옛 코드가 그대로 돈다.

stdio로 붙이고 싶다면

HTTP 대신 클라이언트가 컨테이너를 직접 띄우는 방식도 지원한다. 상시 데몬이 필요 없는 대신 호출 때마다 컨테이너가 새로 뜬다.

claude mcp add --scope user obsidian-vault -- \
  docker run -i --rm -v /Users/iron/Project/ai-docs/vault:/vault \
  -e OBSIDIAN_VAULT=/vault obsidian-vault-mcp:latest node dist/stdio.js

Docker 없이 (개발용)

npm ci && npm run build
node dist/http.js     # http://127.0.0.1:8787/mcp
node dist/stdio.js    # stdio

4. 아키텍처와 프로토콜

프로토콜: MCP 2026-07-28 Stateless

@modelcontextprotocol/server v2로 구현했다. 이 개정판의 핵심은 세션 제거다.

항목

2025 이전

2026-07-28

연결 개시

initialize / notifications/initialized

없음 — 요청이 스스로 완결

세션

Mcp-Session-Id 헤더

제거

능력 확인

핸드셰이크에서 1회

server/discover (선택) + 요청별 _meta

서버 상태

세션에 암묵적으로 보관

명시적 핸들을 인자로 전달

요청은 이런 모양이 된다.

POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: vault_search

Mcp-MethodMcp-Name이 헤더로 올라와, 게이트웨이가 본문을 파싱하지 않고도 라우팅·인가·레이트리밋을 걸 수 있다.

이 설계가 컨테이너와 잘 맞는 이유는 서버가 요청 사이에 아무것도 기억하지 않기 때문이다. Claude Code와 Codex가 같은 컨테이너에 동시에 붙어도 서로 간섭하지 않고, 컨테이너를 재시작해도 클라이언트가 세션을 잃지 않는다.

한 factory가 두 전송을 덮는다

                    buildServer(index)          ← 도구 정의는 여기 한 곳뿐
                     ╱              ╲
        serveStdio(factory)    createMcpHandler(factory)
              │                        │
           stdio                Stateless HTTP
     (docker run -i)          (docker compose, :8787)

src/server.ts의 factory 하나를 두 진입점이 공유하므로 전송별로 도구가 갈라질 수 없다. 2025 era 클라이언트가 붙으면 SDK가 stateless fallback으로 알아서 받아준다.

레이어

파일

역할

src/vault.ts

vault 인덱스 · 파싱 · 검색 · 링크 그래프 · 경로 보안

src/server.ts

MCP 서버 factory (도구 8개). 인덱스를 주입받는다

src/stdio.ts

stdio 진입점

src/http.ts

Stateless HTTP 진입점 + Host/Origin 검증 + /health

buildServer(index)가 인덱스를 주입받는 구조라서 테스트가 임시 vault를 꽂아 넣을 수 있다. 모듈 전역 인덱스였다면 도구 테스트가 실제 vault를 건드려야 했을 것이다.

인덱싱

652파일 0.8MB 전체 스캔이 ~150ms라 역색인을 유지할 이유가 없다. 통째로 메모리에 올리고, TTL 5초로 외부 변경(Obsidian에서 직접 편집한 것)을 따라잡는다. 쓰기 도구는 즉시 invalidate()하므로 방금 쓴 노트가 다음 검색에 바로 잡힌다.

보안

명세는 두 가지를 요구한다: Origin 헤더 검증 후 403, 그리고 로컬 배포는 localhost에만 바인드.

  • createMcpHandler는 의도적으로 검증을 하지 않으므로 src/http.ts가 앞단에서 Host/Origin을 확인하고 403을 낸다. 이게 없으면 사용자가 열어둔 아무 웹페이지나 localhost:8787로 요청을 보내 vault를 읽고 쓸 수 있다(DNS rebinding).

  • 컨테이너 내부는 0.0.0.0에 바인드할 수밖에 없다(그래야 포트 매핑이 닿는다). 루프백 제한은 호스트 매핑 127.0.0.1:8787:8787이 담당한다. compose에서 앞의 127.0.0.1을 빼면 같은 네트워크의 누구나 vault를 편집할 수 있게 되므로 빼지 말 것.

  • 컨테이너는 non-root로 돈다. 쓰기 경로는 전부 safePath()를 통과해 vault 밖(../, 절대경로)과 숨김 경로를 막는다.


5. MCP 주요 기능 — 도구 8개

읽기

도구

하는 일

주요 인자

vault_search

제목·별칭·태그·본문을 가중치로 채점해 검색

query, type, tag, folder, limit, full

vault_read

노트 전문 읽기 + 연결 관계 동봉

ref, with_links

vault_links

백링크(참조하는 노트) / 아웃링크(참조되는 노트)

ref, direction

vault_browse

전체 개요, 태그별·타입별 목록, 주제 맵(MOC) 열기

tag, moc, type, limit

vault_sources

개념 → 강의 유튜브 타임스탬프 링크

ref

ref제목·별칭·경로 아무거나 받는다. "환경 변수", "env", ".env", "10-Concepts/환경 변수.md"가 모두 같은 노트로 해석된다.

검색 점수는 제목 정확일치(100) > 별칭 정확일치(80) > 제목 부분일치(50) > 별칭 부분일치(30) > 태그(25) > 본문 빈도(최대 20) 순으로 쌓인다.

vault_sources는 개념 노트의 sources frontmatter를 읽어 강의 영상의 해당 지점으로 바로 가는 링크를 만든다.

**그래프 엔지니어링** 강의 출처 3건

- [13:39] **Loop와 Graph는 무엇이 다른가?**
  https://youtu.be/I_c8R_PckJ8?t=819
  영상 노트: `20-Videos/2026-08-31 암묵지 자산화를 위한 딥트윈 에이전트의 전체 원리와 설계 프로세스.md`

쓰기

도구

하는 일

안전장치

vault_write

노트 생성/덮어쓰기

기존 노트는 overwrite=true 없이 못 덮는다

vault_append

기존 노트에 섹션 추가

기존 내용을 지우지 않는다

vault_link

노트 간 위키링크 [[대상]] 연결

이미 있으면 아무것도 바꾸지 않는다

도구마다 MCP 어노테이션을 붙여 클라이언트가 위험도를 구분할 수 있게 했다 — 읽기 5개는 readOnlyHint, vault_writedestructiveHint, vault_linkidempotentHint.


6. 개발

npm test          # 전체 100개 (Docker 통합 포함, 이미지 빌드 수행)
npm run test:unit # Docker 제외 — 빠른 반복용
npm run typecheck
npm run build

TDD로 만들었다. 테스트는 네 층이다.

파일

검증 대상

test/vault.test.ts

인덱스 단위 + 실제 vault 통합(위키링크 100% 해석)

test/tools.test.ts

InMemoryTransport로 MCP 왕복 — 도구 8개 입출력

test/transports.test.ts

실제 포트 · 실제 프로세스 spawn · Host/Origin 403

test/docker.test.ts

이미지 빌드 · 볼륨 양방향 · non-root · 루프백 · compose 설정

Docker 테스트는 데몬이 없으면 자동으로 skip된다. 쓰기 테스트는 전부 임시 vault에서만 돌므로 실제 vault는 건드리지 않는다.

설계 메모

  • NFC 정규화가 핵심이다. macOS는 한글 파일명을 NFD(자모 분리)로 저장하지만 본문의 위키링크는 NFC다. 정규화 없이는 [[환경 변수]]가 파일에 매칭되지 않는다. 실제 vault의 위키링크 3,990개가 100% 해석되는지를 테스트로 못박아 두었다.

  • safePath()는 선행 슬래시를 벗기지 않는다. 벗기면 /etc/hosts<vault>/etc/hosts.md로 둔갑해 의도와 다른 곳에 쓰인다. 원문 그대로 resolve()해서 절대경로가 절대경로로 판정되게 둔다. (TDD로 잡은 버그다.)

  • fetch()로는 Host 헤더를 위조할 수 없다. Node가 금지 헤더로 보고 무시하므로, DNS rebinding 방어를 시험하려면 node:http raw 요청이어야 한다. 이걸 모르고 쓴 첫 테스트는 406을 403으로 착각해 통과하고 있었다.


환경변수

변수

기본값

설명

VAULT_PATH

(compose) 호스트 vault 절대경로. 필수

MCP_PORT

8787

(compose) 호스트에 노출할 포트

UID / GID

501 / 20

(compose) 컨테이너 실행 사용자

OBSIDIAN_VAULT

/vault

서버가 읽을 vault 경로

OBSIDIAN_MCP_PORT

8787

서버 리슨 포트

OBSIDIAN_MCP_HOST

127.0.0.1

바인드 주소. 컨테이너에서는 0.0.0.0

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides an MCP server that allows AI assistants to interact with Obsidian vaults, enabling reading/writing notes, managing metadata, searching content, and working with daily notes.
    37
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to read and search your Obsidian vault through a local MCP server, keeping everything private and local.
    Apache 2.0
  • A
    license
    A
    quality
    C
    maintenance
    Enables MCP-compatible AI hosts to read, search, link, and write notes in a local Obsidian vault with sandboxed file access.
    1
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Exposes a personal markdown-based second brain (Obsidian-style) as an MCP server, enabling agents to search, read, and write notes with privacy controls.
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sangwookp9591/obsidian-local-mcp-server-'

If you have feedback or need assistance with the MCP directory API, please join our Discord server