@sunub/obsidian-mcp-server 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 Sunub
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -1,257 +1,320 @@
1
1
  # Obsidian MCP Server
2
2
 
3
- `obsidian-mcp-server`는 [Model Context Protocol(MCP)](https://modelcontextprotocol.io/docs/getting-started/intro)을 구현한 서버로, 로컬 Obsidian vault의 문서들을 AI 에이전트나 외부 애플리케이션에서 쉽게 탐색하고 관리할 수 있도록 강력한 도구 API를 제공합니다.
3
+ [![npm version](https://img.shields.io/npm/v/@sunub/obsidian-mcp-server.svg)](https://www.npmjs.com/package/@sunub/obsidian-mcp-server)
4
4
 
5
- Obsidian Vault 이용해 AI 활용 가능한 지식 베이스(Knowledge Base)로 확장하여 사용할 있게끔 하고 문서 검색, 요약, 정리와 같은 부가적인 작업을 자동화하여 사용자가 핵심적인 "글쓰기 활동"에만 집중할 수 있는 환경을 구축하고자 제작했습니다.
5
+ `obsidian-mcp-server`는 Obsidian Vault Markdown 문서를 AI 에이전트가 조회하고, 검색하고, 요약할있게 해주는 MCP 서버입니다.
6
6
 
7
- ## 핵심 아키텍처
7
+ MCP 클라이언트에 연결해 에이전트에게 **토큰 사용량을 제어하면서** Vault 내용을 조회할 수 있게 합니다.
8
8
 
9
- 본 서버는 `VaultManager`와 `Indexer`를 중심으로 구축되어 대규모 Vault에서도 높은 성능과 메모리 효율성을 보장합니다.
10
9
 
11
- - **`Indexer` 기반 검색**: 서버 시작 시 가벼운 역 인덱스(Inverted Index)를 생성하여 키워드 검색 시 거의 즉각적인 결과를 반환합니다(O(1)). 전체 파일 내용을 메모리에 상주시키지 않아 메모리 사용량을 최소화합니다.
12
- - **`VaultManager`**: Vault 내의 모든 문서를 효율적으로 관리하며, 파일 시스템과 상호작용하여 문서의 생성, 수정, 삭제를 처리합니다.
10
+ ## 무엇을 있나
13
11
 
14
- ## 주요 기능
12
+ - 키워드 기반 노트 검색 (`vault`, `action="search"`)
13
+ - 특정 노트 열람 (`vault`, `action="read"`)
14
+ - Vault 전체 문서 목록 조회 (`vault`, `action="list_all"`)
15
+ - Vault 상태 조회 (`vault`, `action="stats"`)
16
+ - 장기 컨텍스트용 메모리 패킷 생성 (`vault`, `action="collect_context"`)
17
+ - 저장된 메모리 노트 조회 (`vault`, `action="load_memory"`)
18
+ - frontmatter 자동 생성 제안 (`generate_property`)
19
+ - frontmatter 실제 반영 (`write_property`)
20
+ - 문서 기반 프롬프트 워크플로우 (`create_document_with_properties`)
21
+ - 이미지 첨부파일 정리 (`organize_attachments`)
15
22
 
16
- - **고급 문서 탐색**: `vault` 도구를 통해 키워드 검색, 전체 목록 조회, 특정 문서 읽기, 통계 분석 등 다양한 탐색 기능을 제공합니다.
17
- - **컨텍스트 수집/기억 패킷 생성**: `vault collect_context`로 문서 배치 수집, 압축, continuation 토큰 발급, 메모리 패킷(JSON canonical)을 생성합니다.
18
- - **저장된 메모리 재호출**: `vault load_memory`로 `memory/resume_context.v1.md`를 빠르게 로드해 다음 턴 컨텍스트로 재사용할 수 있습니다.
19
- - **AI 기반 속성 생성**: `generate_property` 도구는 문서 본문을 분석하여 `title`, `tags`, `summary` 등 적절한 frontmatter 속성을 자동으로 생성합니다.
20
- - **안전한 속성 업데이트**: `write_property` 도구를 사용하여 생성된 속성을 기존 frontmatter와 병합하여 파일에 안전하게 기록합니다.
21
- - **첨부 파일 자동 정리**: `organize_attachments` 도구는 문서와 연결된 첨부 파일(예: 이미지)을 자동으로 감지하여 문서 제목에 맞는 폴더로 이동시키고 링크를 업데이트합니다.
22
- - **통합 워크플로우**: `create_document_with_properties`와 같은 도구를 통해 문서 분석부터 속성 생성, 파일 업데이트까지의 전체 과정을 단일 명령으로 실행합니다.
23
- - **신뢰성 및 테스트**: `vitest`를 사용한 End-to-End 테스트와 GitHub Actions 기반의 CI/CD 파이프라인을 통해 서버의 안정성과 각 도구 API의 응답 스키마를 검증합니다.
23
+ ## 사전 주의사항
24
24
 
25
- ## 도구 API
25
+ 서버는 Vault 내용을 클라이언트에 노출합니다. 운영 환경에서 신중히 사용하세요.
26
26
 
27
- `obsidian-mcp-server`는 MCP 클라이언트를 통해 호출할 있는 다음과 같은 도구들을 제공합니다.
27
+ - 신뢰할없는 AI 에이전트와 연결하지 마세요.
28
+ - Vault 경로(`VAULT_DIR_PATH`)는 최소 권한으로 제한하세요.
29
+ - 조회량이 큰 Vault는 `maxOutputChars`, `limit`를 조절해 토큰 비용을 통제하세요.
30
+ - `vault` 액션 기본 압축 모드는 `balanced`입니다.
28
31
 
29
- ### `vault`
30
-
31
- Vault 내 문서를 탐색하고 분석하는 핵심 도구입니다. `action` 파라미터를 통해 다양한 기능을 수행할 수 있습니다.
32
-
33
- - **`list_all`**: Vault 내 모든 문서의 목록과 메타데이터를 반환합니다.
34
- - **`search`**: 키워드를 기반으로 문서 제목, 내용, 태그를 검색합니다.
35
- - **`read`**: 특정 파일의 내용을 읽고 frontmatter와 본문을 반환합니다.
36
- - **`stats`**: Vault 내 모든 문서의 통계(단어, 글자 수 등)를 제공합니다.
37
- - **`collect_context`**: 문서를 배치 처리하여 메모리 패킷을 생성하고, 필요 시 `memory/resume_context.v1.md`에 저장합니다.
38
- - **`load_memory`**: 저장된 메모리 노트의 canonical JSON 블록을 파싱하여 빠른 재주입용 payload를 반환합니다.
39
-
40
- ### `generate_property`
32
+ ### 설정 확인사항
41
33
 
42
- 문서 경로(`filePath`)를 입력받아 해당 문서의 내용을 분석하고, AI가 추천하는 frontmatter 속성을 생성하여 반환합니다.
34
+ 1. Vault 경로: VAULT_DIR_PATH에는 반드시 절대 경로를 입력해야 합니다.
43
35
 
44
- ### `write_property`
36
+ ```plaintext
37
+ // ✅ 올바른 예시
38
+ "VAULT_DIR_PATH": "/Users/username/Documents/MyVault"
39
+ "VAULT_DIR_PATH": "C:\\Users\\username\\Documents\\MyVault" // Windows
40
+ "VAULT_DIR_PATH": "/mnt/c/Users/username/Documents/MyVault" // WSL
45
41
 
46
- 파일 경로(`filePath`)와 JSON 형식의 속성(`properties`)을 입력받아, 해당 파일의 frontmatter를 업데이트합니다.
42
+ // 잘못된 예시
43
+ "VAULT_DIR_PATH": "~/Documents/MyVault" // 상대 경로 사용 불가
44
+ "VAULT_DIR_PATH": "./vault" // 상대 경로 사용 불가
45
+ ```
47
46
 
48
- ### `create_document_with_properties`
47
+ 2. Node.js 요구사항: Node.js 22 이상이 설치되어 있어야 합니다.
49
48
 
50
- 문서 분석, 속성 생성, 파일 업데이트의 전 과정을 한 번에 처리하는 통합 도구입니다.
49
+ ```bash
50
+ node --version # v22.0.0 이상 확인
51
+ ```
51
52
 
52
- ### `organize_attachments`
53
+ ## 시작하기 (빠른 설정)
53
54
 
54
- 키워드로 문서를 찾아 해당 문서에 연결된 모든 첨부 파일을 `images/{문서 제목}` 폴더로 이동시키고, 문서 내의 링크를 자동으로 업데이트합니다.
55
+ ### 1) 공통 요구사항
55
56
 
56
- ## 메모리 운영 원칙
57
+ - 최소 설정은 `VAULT_DIR_PATH` (Vault 절대 경로)입니다.
58
+ - MCP 실행 자체는 배포 패키지 기준으로 맞춰 설명합니다.
59
+ - 배포 패키지(`npx`) 사용 (권장)
60
+ - 로컬 `build/index.js`는 개발/디버깅 목적
61
+ - 로컬 실행은 마지막 섹션(5)에서 분리 안내합니다.
62
+ - Vault 경로가 없으면 시작이 실패합니다.
63
+ - 아래 예시는 복붙으로 바로 사용할 수 있도록 정리했습니다.
57
64
 
58
- ### 서버와 에이전트 책임 분리
65
+ ### 2) 배포 패키지 (`npx`) 설정
59
66
 
60
- - **MCP 서버(Data Plane)**: 검색, 읽기, 압축, continuation, memory packet 생성/저장까지 담당합니다.
61
- - **에이전트 런타임(Memory Plane)**: 사용자 의도 감지, `load_memory` 자동 호출, 다음 턴 프롬프트 선주입을 담당합니다.
67
+ ```json
68
+ {
69
+ "mcpServers": {
70
+ "obsidian": {
71
+ "command": "npx",
72
+ "args": ["-y", "@sunub/obsidian-mcp-server@latest"],
73
+ "env": {
74
+ "VAULT_DIR_PATH": "/abs/path/to/your/vault",
75
+ "LOGGING_LEVEL": "info"
76
+ }
77
+ }
78
+ }
79
+ }
80
+ ```
62
81
 
63
- 중요: 서버만으로는 "다음 턴 자동 기억 반영"을 보장할 수 없습니다. 이 동작은 반드시 클라이언트/에이전트 런타임에서 구현해야 합니다.
82
+ CLI 직접 실행:
64
83
 
65
- ### 메모리 산출물 포맷
84
+ ```bash
85
+ npx -y @sunub/obsidian-mcp-server@latest --vault-path /abs/path/to/your/vault --logging-level info
86
+ ```
66
87
 
67
- - 기본 저장 경로: `memory/resume_context.v1.md`
68
- - 구성: 사람이 읽는 Markdown 요약 + AI 파싱용 canonical JSON code block
69
- - 스키마 키: `schema_version`, `generated_at`, `source_hash`, `documents[].doc_hash`, `memory_packet`
88
+ ### 3) MCP Client configuration
70
89
 
71
- ## collect_context 추천 프리셋
90
+ 클라이언트별 UI가 다르더라도 `command`/`args`/`env`의 기본 형태는 동일합니다.
72
91
 
73
- | 목적 | 주요 파라미터 | 권장 값 |
74
- | --- | --- | --- |
75
- | 빠른 토픽 스캔 | `scope`, `maxDocs`, `maxCharsPerDoc`, `compressionMode` | `topic`, `8`, `700`, `aggressive` |
76
- | 이력서 컨텍스트 구축 | `scope`, `maxDocs`, `maxCharsPerDoc`, `memoryMode`, `compressionMode` | `all`, `20`, `1200`, `both`, `balanced` |
77
- | 장문 Vault 단계 처리 | `maxDocs`, `maxCharsPerDoc`, `maxOutputChars` | `10`, `900`, `2800` |
92
+ > 배포 패키지 기준 핵심: `command="npx"`, `args=["-y","@sunub/obsidian-mcp-server@latest"]`, `env.VAULT_DIR_PATH`
78
93
 
79
- 가드레일은 출력 상한 초과 시 다음 순서로 축소됩니다: `backlinks -> per-doc chars -> doc count -> continuation`.
94
+ <details>
95
+ <summary>Codex</summary>
96
+ `.codex/config.toml`에 아래처럼 등록합니다.
80
97
 
81
- ## 예제 MCP 요청 (3개)
98
+ ```toml
99
+ [mcp_servers.obsidian]
100
+ command = "npx"
101
+ args = ["-y", "@sunub/obsidian-mcp-server@latest"]
102
+ env = { VAULT_DIR_PATH = "/abs/path/to/your/vault" }
103
+ ```
104
+ </details>
82
105
 
83
- 아래는 MCP 클라이언트의 `callTool`에 전달하는 `arguments` 예시입니다.
106
+ <details>
107
+ <summary>Copilot CLI</summary>
84
108
 
85
- ### 1) 전체 Vault에서 메모리 구축 시작
109
+ 1) `copilot` 실행
110
+ 2) `/mcp add`
111
+ 3) 아래 값 입력
86
112
 
87
- ```json
88
- {
89
- "action": "collect_context",
90
- "scope": "all",
91
- "maxDocs": 20,
92
- "maxCharsPerDoc": 1200,
93
- "memoryMode": "both",
94
- "compressionMode": "balanced"
95
- }
96
- ```
113
+ - **Server name:** `obsidian`
114
+ - **Server Type:** `[1] Local`
115
+ - **Command:** `npx -y @sunub/obsidian-mcp-server@latest`
116
+ - **Environment:** `{ "VAULT_DIR_PATH": "/abs/path/to/your/vault" }`
117
+ </details>
97
118
 
98
- ### 2) continuationToken으로 다음 배치 이어서 수집
119
+ <details>
120
+ <summary>Copilot / VS Code</summary>
99
121
 
100
- ```json
101
- {
102
- "action": "collect_context",
103
- "continuationToken": "<previous_response.batch.continuation_token>",
104
- "compressionMode": "balanced"
105
- }
106
- ```
122
+ 버전별 JSON 키 이름이 다를 수 있으므로(예: `servers`/`mcpServers`), 프로젝트 문서에 맞춰 적용하세요.
107
123
 
108
- ### 3) 저장된 메모리 빠른 로드(quiet)
124
+ ```json
125
+ {
126
+ "mcpServers": {
127
+ "obsidian": {
128
+ "command": "npx",
129
+ "args": ["-y", "@sunub/obsidian-mcp-server@latest"],
130
+ "env": { "VAULT_DIR_PATH": "/abs/path/to/your/vault" }
131
+ }
132
+ }
133
+ }
134
+ ```
135
+ </details>
109
136
 
110
- ```json
111
- {
112
- "action": "load_memory",
113
- "memoryPath": "memory/resume_context.v1.md",
114
- "quiet": true
115
- }
116
- ```
137
+ <details>
138
+ <summary>Cursor</summary>
117
139
 
118
- 클라이언트 자동 주입 규칙은 `docs/CLIENT_INJECTION_GUIDE.md`를 참고하세요.
140
+ `Cursor Settings` `MCP` `New MCP Server`에서 등록합니다.
119
141
 
120
- ## 설치 및 사용
142
+ ```json
143
+ {
144
+ "obsidian": {
145
+ "command": "npx",
146
+ "args": ["-y", "@sunub/obsidian-mcp-server@latest"],
147
+ "env": { "VAULT_DIR_PATH": "/abs/path/to/your/vault" }
148
+ }
149
+ }
150
+ ```
151
+
152
+ ※ 일부 버전은 서버 식별 키명이 다를 수 있으니 설정 화면 안내에 맞춰 붙여넣으세요.
153
+ </details>
121
154
 
122
- ### MCP 클라이언트 설정
155
+ <details>
156
+ <summary>Gemini CLI</summary>
123
157
 
124
- MCP를 지원하는 AI 도구(Claude Desktop, Gemini 등)의 설정 파일에 다음 구성을 추가하세요.
158
+ 패키지 설치형 예시:
125
159
 
126
- #### Claude Desktop
160
+ ```bash
161
+ gemini mcp add obsidian npx -y @sunub/obsidian-mcp-server@latest --vault-path /abs/path/to/your/vault
162
+ ```
163
+
164
+ ※ 일부 Gemini 버전은 `--vault-path` 지원이 다를 수 있으므로, `gemini mcp add`의 최신 문서를 확인하세요.
165
+ </details>
127
166
 
128
- `claude_desktop_config.json` 파일에 추가해야할 내용:
167
+ ### 4) 완성 예시 설정
129
168
 
130
169
  ```json
131
170
  {
132
171
  "mcpServers": {
133
- "obsidian-mcp-server": {
172
+ "obsidian": {
134
173
  "command": "npx",
135
- "args": ["-y", "@sunub/obsidian-mcp-server"],
174
+ "args": [
175
+ "-y",
176
+ "@sunub/obsidian-mcp-server@latest"
177
+ ],
136
178
  "env": {
137
- "VAULT_DIR_PATH": "/absolute/path/to/your/obsidian/vault"
179
+ "VAULT_DIR_PATH": "/path/to/obsidian-vault",
180
+ "VAULT_METRICS_LOG_PATH": "/path/to/vault-metrics.ndjson",
181
+ "LOGGING_LEVEL": "info"
138
182
  }
139
183
  }
140
184
  }
141
185
  }
142
186
  ```
143
187
 
144
- #### Gemini
188
+ ## 환경 변수 설정
189
+
190
+ - `VAULT_DIR_PATH` (필수): Obsidian Vault 절대 경로
191
+ - `VAULT_METRICS_LOG_PATH` (선택): 액션 응답 압축/토큰 메트릭을 JSONL로 기록
192
+ - `LOGGING_LEVEL` (선택): `debug` | `info` | `warn` | `error`
193
+
194
+ ## 시작 후 빠른 검증
145
195
 
146
- `gemini_config.json` 파일에 추가해야할 내용:
196
+ 연결이 끝나면 아래 3가지만 먼저 확인하세요.
197
+ 문서가 안 열리더라도 어디서 실패했는지 바로 좁힐 수 있습니다.
198
+
199
+ 1) Vault 상태 확인
200
+
201
+ ```text
202
+ "Vault 상태를 요약해줘."
203
+ ```
204
+
205
+ 예상 내부 동작:
147
206
 
148
207
  ```json
149
208
  {
150
- "mcpServers": {
151
- "obsidian-mcp-server": {
152
- "command": "npx",
153
- "args": ["-y", "@sunub/obsidian-mcp-server"],
154
- "env": {
155
- "VAULT_DIR_PATH": "/absolute/path/to/your/obsidian/vault"
156
- }
157
- }
209
+ "method": "tools/call",
210
+ "params": {
211
+ "name": "vault",
212
+ "arguments": { "action": "stats" }
158
213
  }
159
214
  }
160
215
  ```
161
216
 
162
- ### 설정 확인사항
163
-
164
- 1. **Vault 경로**: `VAULT_DIR_PATH`에는 반드시 **절대 경로**를 입력해야 합니다.
217
+ 정상 동작 시 응답에 `totalFiles`, `isInitialized`, `vaultPath`가 들어갑니다.
165
218
 
166
- ```json
167
- // ✅ 올바른 예시
168
- "VAULT_DIR_PATH": "/Users/username/Documents/MyVault"
169
- "VAULT_DIR_PATH": "C:\\Users\\username\\Documents\\MyVault" // Windows
170
- "VAULT_DIR_PATH": "/mnt/c/Users/username/Documents/MyVault" // WSL
171
-
172
- // ❌ 잘못된 예시
173
- "VAULT_DIR_PATH": "~/Documents/MyVault" // 상대 경로 사용 불가
174
- "VAULT_DIR_PATH": "./vault" // 상대 경로 사용 불가
175
- ```
219
+ 2) 검색 인덱스 확인
176
220
 
177
- 2. **Node.js 요구사항**: Node.js 22 이상이 설치되어 있어야 합니다.
178
-
179
- ```bash
180
- node --version # v22.0.0 이상 확인
181
- ```
221
+ ```text
222
+ "노트 제목에 'MCP'가 들어간 문서만 5개 찾아줘."
223
+ ```
182
224
 
183
- 3. **설정 적용**: 설정 파일 저장 후 AI 도구를 재시작하면 MCP 서버가 자동으로 연결됩니다.
225
+ 예상 내부 동작:
184
226
 
185
- ### 수동 실행 (테스트용)
227
+ ```json
228
+ {
229
+ "method": "tools/call",
230
+ "params": {
231
+ "name": "vault",
232
+ "arguments": {
233
+ "action": "search",
234
+ "keyword": "MCP",
235
+ "limit": 5
236
+ }
237
+ }
238
+ }
239
+ ```
186
240
 
187
- 터미널에서 직접 서버를 실행하여 테스트할 수도 있습니다:
241
+ `search`에서 결과가 비어 있으면 인덱싱/경로 또는 키워드 범위를 의심합니다.
188
242
 
189
- ```bash
190
- # 환경 변수 설정 후 실행
191
- VAULT_DIR_PATH=/path/to/vault npx -y @sunub/obsidian-mcp-server
243
+ 3) 문서 읽기 확인
192
244
 
193
- # 또는 명령줄 인자로 경로 지정
194
- npx -y @sunub/obsidian-mcp-server --vault-path /path/to/vault
245
+ ```text
246
+ "특정 노트 하나를 읽어줘."
195
247
  ```
196
248
 
197
- ### 테스트
249
+ 예상 내부 동작:
198
250
 
199
- `vitest`를 사용한 End-to-End 테스트:
200
-
201
- ```bash
202
- # 테스트 실행
203
- npm test
204
-
205
- # Watch 모드
206
- npm run test:watch
251
+ ```json
252
+ {
253
+ "method": "tools/call",
254
+ "params": {
255
+ "name": "vault",
256
+ "arguments": {
257
+ "action": "read",
258
+ "filename": "예: 어떤 문서 이름"
259
+ }
260
+ }
261
+ }
207
262
  ```
208
263
 
209
- ### 비용 계측(B1)
264
+ `filename`이 틀리면 `{ "error": "Document not found: ..." }`가 오고, `read` 대신 `list_all`으로 후보를 먼저 확인하면 해결이 빠릅니다.
210
265
 
211
- `VAULT_METRICS_LOG_PATH`를 지정하면 `vault` 도구 응답마다 아래 메트릭이 JSONL로 기록됩니다.
266
+ `search`, `list_all`, `load_memory`는 `quiet` 기본값이 `true`라서 기본 응답이 간략해질 수 있습니다.
267
+ 필요하면 `quiet: false`, `includeContent: true`, `excerptLength`(또는 `maxOutputChars`)를 함께 써서 상세를 확인하세요.
212
268
 
213
- - `estimated_tokens`
214
- - `mode`
215
- - `truncated`
216
- - `doc_count`
269
+ ## 사용 예시
217
270
 
218
- 예시:
271
+ MCP를 어떻게 호출하는지보다 “무슨 동작을 하려고 하는지”가 더 중요합니다.
219
272
 
220
- ```bash
221
- # 1) 메트릭 로그 경로 지정
222
- export VAULT_METRICS_LOG_PATH=.tmp/vault-metrics.jsonl
273
+ 직접 실행용 질문 예시:
223
274
 
224
- # 2) 평소처럼 MCP 시나리오 실행 (search/read/collect_context/load_memory)
225
- npm run inspector
275
+ - `README.md`에서 `시작하기 (빠른 설정)`의 `MCP Client configuration` 부분만 찾아줘.
276
+ - `docs/tools-usage-guide.md`의 `vault` 설정 예시만 읽고 정리해줘.
277
+ - `docs/tool-reference.md`에서 `vault`의 `collect_context` 파라미터만 찾아줘.
278
+ - `docs/tools-usage-guide.md`에서 `MCP 서버` 설정 블록만 정리해줘.
226
279
 
227
- # 3) 시나리오 종료 리포트 생성
228
- npm run metrics:report -- .tmp/vault-metrics.jsonl
229
- ```
280
+ 자연어 예시는 아래처럼 구체적으로 쓰면 도구가 더 정확하게 동작합니다.
230
281
 
231
- 리포트는 액션별 `count`, `total_tokens`, `avg/p95_tokens`, `avg_doc_count`, `truncated_rate(%)`를 출력합니다.
282
+ - `"README.md의 시작하기 부분에서 실행 명령 예시만 찾아줘"`
283
+ - `"docs/tools-usage-guide.md에서 vault 관련 사용 예시만 찾아서 비교해줘"`
284
+ - `"docs/tool-reference.md의 vault.read 파라미터 설명만 읽어줘"`
232
285
 
233
- ### 코드 품질
286
+ `vault`는 사용자 질문을 토대로 내부적으로 매핑되어 호출되며, 실제 흐름은 아래처럼 동작합니다.
234
287
 
235
- ```bash
236
- # 포맷팅
237
- npm run format
288
+ - `README.md의 시작하기 (빠른 설정)에서 npx 예시만 보여줘`
289
+ 핵심 키워드만 추출해 `vault.search`가 먼저 호출됩니다.
290
+ - `docs/tool-reference.md의 collect_context 파라미터만 읽어줘`
291
+ → 먼저 `vault.read`로 문서의 해당 부분을 읽고, 필요 시 `vault.collect_context`로 정리합니다.
292
+ - `docs/tools-usage-guide.md에서 frontmatter 처리 과정을 읽어줘`
293
+ → 문서 위치를 `vault.read`로 찾은 뒤 `generate_property`/`write_property`/`create_document_with_properties`를 순차 호출할 수 있습니다.
238
294
 
239
- # 린팅
240
- npm run lint
295
+ 동작하는 도구의 호출 흐름은 [사용 예시(도구 호출 흐름)](docs/tool-call-flows.md)에서 구체 JSON 예시와 함께 확인하세요.
241
296
 
242
- # 전체 체크 (포맷팅 + 린팅)
243
- npm run check
244
- ```
297
+ ## 등록된 도구
245
298
 
246
- ### CI/CD
299
+ <!-- BEGIN AUTO GENERATED TOOLS -->
247
300
 
248
- 프로젝트는 GitHub Actions를 사용하여 CI/CD 파이프라인을 구축했습니다:
301
+ - **Obsidian Tools (6 actions)**
302
+ - [`vault`](docs/tool-reference.md#vault-action)
303
+ - `search`
304
+ - `read`
305
+ - `list_all`
306
+ - `stats`
307
+ - `collect_context`
308
+ - `load_memory`
309
+ - [`generate_property`](docs/tool-reference.md#generate_property)
310
+ - [`write_property`](docs/tool-reference.md#write_property)
311
+ - [`create_document_with_properties`](docs/tool-reference.md#create_document_with_properties)
312
+ - [`organize_attachments`](docs/tool-reference.md#organize_attachments)
249
313
 
250
- - **빌드**: TypeScript 컴파일 빌드 검증
251
- - **린트**: Biome를 사용한 코드 품질 검사
252
- - **테스트**: Vitest를 통한 E2E 테스트
253
- - **배포**: 태그 푸시 시 자동으로 npm에 배포
314
+ <!-- END AUTO GENERATED TOOLS -->
254
315
 
255
- ## 라이선스
316
+ ## 자세한 사용 규약
256
317
 
257
- ISC License
318
+ - 도구 상세 동작, 파라미터 기본값, 실제 응답 형식은 [Tool Reference](docs/tool-reference.md)에서 확인하세요.
319
+ - `vault`는 하나의 MCP tool이고 `action` 값으로 실제 동작이 분기됩니다. 오타가 가장 흔한 실패 원인입니다.
320
+ - 대규모 Vault에서는 `collect_context`를 `scope="all"`, `maxDocs`를 작게 시작해 단계적으로 확장하세요.
package/build/index.js CHANGED
@@ -2,10 +2,6 @@
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
3
  import { getOptions } from "./config.js";
4
4
  import createMcpServer from "./server.js";
5
- export default function smitheryEntryPoint() {
6
- const server = createMcpServer();
7
- return server.server;
8
- }
9
5
  async function main() {
10
6
  const options = getOptions();
11
7
  if (!options) {
@@ -83,7 +83,7 @@ export const obsidianContentContinuationToken = z
83
83
  .describe("Continuation token to resume a previous collect_context batch operation");
84
84
  export const obsidianContentMemoryPath = z
85
85
  .string()
86
- .describe("Path to a stored memory note for load_memory (default: memory/resume_context.v1.md)");
86
+ .describe("Path to a stored memory note for load_memory (default: memory/context_memory_snapshot.v1.md)");
87
87
  // input schema
88
88
  export const obsidianContentQueryParamsZod = z.object({
89
89
  action: obsidianContentActions,
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { createToolError } from "../../../../utils/createToolError.js";
3
3
  import { collectContextMemoryPacketSchema, collectContextPayloadSchema, collectContextResponseDataSchema, collectContextTokenV1Schema, } from "../../types/collect_context.js";
4
- import { RESUME_CONTEXT_MEMORY_NOTE_PATH, RESUME_CONTEXT_SCHEMA_VERSION, } from "../constants.js";
4
+ import { CONTEXT_MEMORY_SNAPSHOT_NOTE_PATH, CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION, } from "../constants.js";
5
5
  import { ACTION_DEFAULT_MAX_OUTPUT_CHARS, finalizePayloadWithCompression, jsonCharLength, normalizeWhitespace, resolveCompressionMode, stripFrontmatterBlock, trimWithEllipsis, } from "../shared.js";
6
6
  const COLLECT_CONTEXT_CACHE_MAX_ENTRIES = 200;
7
7
  const COLLECT_CONTEXT_MIN_EXCERPT_CHARS = 220;
@@ -230,7 +230,7 @@ function buildCollectContextCacheKey(params) {
230
230
  scope: params.scope,
231
231
  topic: params.topic,
232
232
  doc_hash: params.docHash,
233
- schema_version: RESUME_CONTEXT_SCHEMA_VERSION,
233
+ schema_version: CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION,
234
234
  mode: params.mode,
235
235
  start_cursor: params.startCursor,
236
236
  max_docs: params.maxDocs,
@@ -282,10 +282,10 @@ function buildCollectContextSourceHash(payload) {
282
282
  .update(JSON.stringify(normalized), "utf8")
283
283
  .digest("hex");
284
284
  }
285
- function buildResumeContextMarkdown(params) {
285
+ function buildContextMemorySnapshotMarkdown(params) {
286
286
  const { payload, generatedAt, sourceHash, notePath } = params;
287
287
  const canonical = {
288
- schema_version: RESUME_CONTEXT_SCHEMA_VERSION,
288
+ schema_version: CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION,
289
289
  generated_at: generatedAt,
290
290
  source_hash: sourceHash,
291
291
  note_path: notePath,
@@ -324,11 +324,11 @@ function buildResumeContextMarkdown(params) {
324
324
  ? payload.memory_packet.openQuestions.map((q) => `- ${q}`).join("\n")
325
325
  : "- None";
326
326
  return [
327
- "# Resume Context v1",
327
+ "# Context Memory Snapshot v1",
328
328
  "",
329
329
  `- generated_at: ${generatedAt}`,
330
330
  `- source_hash: ${sourceHash}`,
331
- `- schema_version: ${RESUME_CONTEXT_SCHEMA_VERSION}`,
331
+ `- schema_version: ${CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION}`,
332
332
  `- topic: ${payload.topic ?? "null"}`,
333
333
  `- scope: ${payload.scope}`,
334
334
  `- matched_total: ${payload.matched_total}`,
@@ -359,21 +359,21 @@ function buildResumeContextMarkdown(params) {
359
359
  "",
360
360
  ].join("\n");
361
361
  }
362
- async function writeResumeContextMemoryNote(vaultManager, payload) {
362
+ async function writeContextMemorySnapshotNote(vaultManager, payload) {
363
363
  const generatedAt = new Date().toISOString();
364
364
  const sourceHash = buildCollectContextSourceHash(payload);
365
- const markdown = buildResumeContextMarkdown({
365
+ const markdown = buildContextMemorySnapshotMarkdown({
366
366
  payload,
367
367
  generatedAt,
368
368
  sourceHash,
369
- notePath: RESUME_CONTEXT_MEMORY_NOTE_PATH,
369
+ notePath: CONTEXT_MEMORY_SNAPSHOT_NOTE_PATH,
370
370
  });
371
371
  try {
372
- await vaultManager.writeRawDocument(RESUME_CONTEXT_MEMORY_NOTE_PATH, markdown);
372
+ await vaultManager.writeRawDocument(CONTEXT_MEMORY_SNAPSHOT_NOTE_PATH, markdown);
373
373
  return {
374
374
  requested: true,
375
375
  status: "written",
376
- note_path: RESUME_CONTEXT_MEMORY_NOTE_PATH,
376
+ note_path: CONTEXT_MEMORY_SNAPSHOT_NOTE_PATH,
377
377
  generated_at: generatedAt,
378
378
  source_hash: sourceHash,
379
379
  };
@@ -382,7 +382,7 @@ async function writeResumeContextMemoryNote(vaultManager, payload) {
382
382
  return {
383
383
  requested: true,
384
384
  status: "failed",
385
- note_path: RESUME_CONTEXT_MEMORY_NOTE_PATH,
385
+ note_path: CONTEXT_MEMORY_SNAPSHOT_NOTE_PATH,
386
386
  generated_at: generatedAt,
387
387
  source_hash: sourceHash,
388
388
  reason: error instanceof Error ? error.message : String(error),
@@ -526,7 +526,7 @@ export async function collectContext(vaultManager, params) {
526
526
  cache: {
527
527
  key: emptyCacheKey,
528
528
  hit: false,
529
- schema_version: RESUME_CONTEXT_SCHEMA_VERSION,
529
+ schema_version: CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION,
530
530
  topic,
531
531
  doc_hash: emptyDocHash,
532
532
  mode: memoryMode,
@@ -549,7 +549,7 @@ export async function collectContext(vaultManager, params) {
549
549
  });
550
550
  }
551
551
  if (memoryMode !== "response_only") {
552
- const memoryWrite = await writeResumeContextMemoryNote(vaultManager, emptyPayload);
552
+ const memoryWrite = await writeContextMemorySnapshotNote(vaultManager, emptyPayload);
553
553
  emptyPayload = collectContextPayloadSchema.parse({
554
554
  ...emptyPayload,
555
555
  memory_mode: memoryMode,
@@ -680,7 +680,7 @@ export async function collectContext(vaultManager, params) {
680
680
  cache: {
681
681
  key: cacheKey,
682
682
  hit: false,
683
- schema_version: RESUME_CONTEXT_SCHEMA_VERSION,
683
+ schema_version: CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION,
684
684
  topic,
685
685
  doc_hash: docHash,
686
686
  mode: memoryMode,
@@ -704,7 +704,7 @@ export async function collectContext(vaultManager, params) {
704
704
  : {
705
705
  key: cacheKey,
706
706
  hit: true,
707
- schema_version: RESUME_CONTEXT_SCHEMA_VERSION,
707
+ schema_version: CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION,
708
708
  topic,
709
709
  doc_hash: docHash,
710
710
  mode: memoryMode,
@@ -712,7 +712,7 @@ export async function collectContext(vaultManager, params) {
712
712
  })
713
713
  : basePayload;
714
714
  if (memoryMode !== "response_only") {
715
- const memoryWrite = await writeResumeContextMemoryNote(vaultManager, basePayload);
715
+ const memoryWrite = await writeContextMemorySnapshotNote(vaultManager, basePayload);
716
716
  payloadForCompression = collectContextPayloadSchema.parse({
717
717
  ...basePayload,
718
718
  memory_mode: memoryMode,
@@ -1,6 +1,6 @@
1
1
  import { createToolError } from "../../../../utils/createToolError.js";
2
2
  import { collectContextMemoryPacketSchema, collectContextScopeSchema, } from "../../types/collect_context.js";
3
- import { RESUME_CONTEXT_MEMORY_NOTE_PATH, RESUME_CONTEXT_SCHEMA_VERSION, } from "../constants.js";
3
+ import { CONTEXT_MEMORY_SNAPSHOT_NOTE_PATH, CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION, } from "../constants.js";
4
4
  import { ACTION_DEFAULT_MAX_OUTPUT_CHARS, finalizePayloadWithCompression, jsonCharLength, normalizeWhitespace, resolveCompressionMode, stripFrontmatterBlock, trimWithEllipsis, } from "../shared.js";
5
5
  function stripCanonicalJsonBlock(content) {
6
6
  const match = content.match(/```json\s*[\s\S]*?```/m);
@@ -109,7 +109,7 @@ function clampLoadMemoryPayloadByOutputChars(payload, maxOutputChars) {
109
109
  export async function loadMemory(vaultManager, params) {
110
110
  await vaultManager.initialize();
111
111
  const mode = resolveCompressionMode(params);
112
- const memoryPath = params.memoryPath?.trim() || RESUME_CONTEXT_MEMORY_NOTE_PATH;
112
+ const memoryPath = params.memoryPath?.trim() || CONTEXT_MEMORY_SNAPSHOT_NOTE_PATH;
113
113
  const memoryNote = await vaultManager.getDocumentInfo(memoryPath, {
114
114
  includeStats: true,
115
115
  });
@@ -140,7 +140,8 @@ export async function loadMemory(vaultManager, params) {
140
140
  has_canonical_json: quietPayload.has_canonical_json,
141
141
  topic: quietPayload.topic,
142
142
  scope: quietPayload.scope,
143
- schema_version: quietPayload.schema_version ?? RESUME_CONTEXT_SCHEMA_VERSION,
143
+ schema_version: quietPayload.schema_version ??
144
+ CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION,
144
145
  }),
145
146
  },
146
147
  ],
@@ -1,2 +1,2 @@
1
- export const RESUME_CONTEXT_MEMORY_NOTE_PATH = "memory/resume_context.v1.md";
2
- export const RESUME_CONTEXT_SCHEMA_VERSION = "resume_context.v1";
1
+ export const CONTEXT_MEMORY_SNAPSHOT_NOTE_PATH = "memory/context_memory_snapshot.v1.md";
2
+ export const CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION = "context_memory_snapshot.v1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sunub/obsidian-mcp-server",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "A server for the Obsidian Model Context Protocol.",
5
5
  "keywords": [
6
6
  "obsidian",
@@ -22,10 +22,7 @@
22
22
  "exports": {
23
23
  ".": "./build/index.js"
24
24
  },
25
- "module": "build/index.ts",
26
25
  "scripts": {
27
- "smithery:dev": "npx @smithery/cli dev",
28
- "smithery:build": "npx @smithery/cli build",
29
26
  "test": "vitest",
30
27
  "test:watch": "vitest --watch",
31
28
  "metrics:report": "node scripts/vault-metrics-report.mjs",
@@ -47,7 +44,6 @@
47
44
  },
48
45
  "devDependencies": {
49
46
  "@biomejs/biome": "2.3.8",
50
- "@smithery/cli": "^1.2.31",
51
47
  "@types/node": "^24.3.1",
52
48
  "jsdom": "^27.0.0",
53
49
  "shx": "^0.4.0",