obsidian_fetch 0.1.8 → 0.1.11
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.
- checksums.yaml +4 -4
- data/README.md +9 -9
- data/exe/obsidian_fetch +4 -4
- data/lib/obsidian_fetch/version.rb +1 -1
- data/lib/obsidian_fetch.rb +108 -57
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 996b482b6dcf13de18f54917084aa60f9470d6903596d449fb5a0a5c7c2c51cd
|
4
|
+
data.tar.gz: 5d76e2811f3f5e18de3a217fe570d168156e531ec20b86fbecbb74722ec82b58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 187c1f785491440b2f221ce29fd253cc57b421d31c458016c5577ba906f256fbbf510c9ae5de101084db49bf0d754b84466834ff7b500b4c96e72dc95383964b
|
7
|
+
data.tar.gz: 2eabd6144aa231db4900e563e315b7be74cc3cc7460f52a8f604c00d8108fe91fec9eaf334fb64e53fc109c19eca3d1ab34316883dc1718a38eb35903dcf9db2
|
data/README.md
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
# ObsidianFetch
|
2
2
|
|
3
|
-
MCP servers
|
3
|
+
MCP servers focused on fetching and presenting information from Obsidian vaults.
|
4
4
|
|
5
5
|
The existing MCP server has the following drawbacks:
|
6
|
-
-
|
7
|
-
- When reading a note labeled "
|
8
|
-
- Some tools
|
6
|
+
- It supports many commands, which can cause slow prompt loading when computational resources are limited.
|
7
|
+
- When reading a note labeled "Sample Note", it is necessary to search for its path first before loading it, but the LLM may not always follow this procedure.
|
8
|
+
- Some tools include unnecessary options, leading the LLM to sometimes fail to invoke them correctly.
|
9
9
|
|
10
10
|
These issues become particularly noticeable when running an LLM on a local GPU.
|
11
|
-
To address this, we developed a new MCP server that simply retrieves and loads
|
11
|
+
To address this, we developed a new MCP server that simply retrieves and loads lists of notes.
|
12
12
|
|
13
|
-
|
14
|
-
- When the LLM
|
15
|
-
-
|
16
|
-
-
|
13
|
+
The new server also provides the following additional features:
|
14
|
+
- When the LLM attempts to retrieve link information by searching with brackets like `[[link name]]`, the server automatically removes any characters that cannot be used in links.
|
15
|
+
- In addition to loading the note contents, it also displays backlinks—notes that link to the currently opened note.
|
16
|
+
- This allows the LLM to load and understand the connections between related notes via backlinks.
|
17
17
|
|
18
18
|
## Installation
|
19
19
|
|
data/exe/obsidian_fetch
CHANGED
@@ -24,7 +24,7 @@ tool 'read' do
|
|
24
24
|
name = args[:name]
|
25
25
|
# 名前が文字列でない場合
|
26
26
|
next 'Name must be a string' unless name.is_a?(String)
|
27
|
-
|
27
|
+
vault.tool_read(name)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -39,7 +39,7 @@ tool 'read_multiple' do
|
|
39
39
|
# 名前が文字列の配列でない場合
|
40
40
|
next 'Name must be an array of strings' unless names.is_a?(Array) && names.all? { |name| name.is_a?(String) }
|
41
41
|
names.map do |name|
|
42
|
-
|
42
|
+
vault.tool_read(name)
|
43
43
|
end.join("\n\n---\n\n")
|
44
44
|
end
|
45
45
|
end
|
@@ -53,7 +53,7 @@ tool 'list' do
|
|
53
53
|
name = args[:name]
|
54
54
|
# 名前が文字列でない場合
|
55
55
|
next 'Name must be a string' unless name.is_a?(String)
|
56
|
-
|
56
|
+
vault.tool_list(name)
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
@@ -67,7 +67,7 @@ tool 'list_multiple' do
|
|
67
67
|
# 名前が文字列の配列でない場合
|
68
68
|
next 'Name must be an array of strings' unless names.is_a?(Array) && names.all? { |name| name.is_a?(String) }
|
69
69
|
names.map do |name|
|
70
|
-
|
70
|
+
vault.tool_list(name)
|
71
71
|
end.join("\n\n---\n\n")
|
72
72
|
end
|
73
73
|
end
|
data/lib/obsidian_fetch.rb
CHANGED
@@ -127,73 +127,124 @@ module ObsidianFetch
|
|
127
127
|
file_pathes.uniq!
|
128
128
|
end
|
129
129
|
end
|
130
|
-
end
|
131
130
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
131
|
+
public
|
132
|
+
|
133
|
+
def tool_read name
|
134
|
+
name = Vault.normalize_note_name(name)
|
135
|
+
file_pathes = @notes[name]
|
136
|
+
|
137
|
+
# 名前のノートが見つからないが、nameがパスっぽい場合は、パスを修正したうえでもう一度試す
|
138
|
+
preface = ""
|
139
|
+
if file_pathes.nil? && name.include?('/')
|
140
|
+
fixed_name = File.basename(name, '.md')
|
141
|
+
file_pathes = @notes[fixed_name]
|
142
|
+
if file_pathes.nil?
|
143
|
+
# もしも名前で見つからなければ、リンクにも存在しないか確認する
|
144
|
+
link_pathes = @links_by_file_name[fixed_name]
|
145
|
+
if link_pathes.nil?
|
146
|
+
return note_not_found(name)
|
147
|
+
else
|
148
|
+
# リンク先のノートが見つかった場合は、prefaceを追加する
|
149
|
+
preface = <<~EOS
|
150
|
+
Presumably a path was specified. The process was automatically renamed and processed.
|
151
|
+
EOS
|
152
|
+
return list_links(fixed_name, preface)
|
153
|
+
end
|
154
|
+
else
|
155
|
+
# ノート名が見つかった場合は、prefaceを追加する
|
156
|
+
preface = <<~EOS
|
157
|
+
Presumably a path was specified. The process was automatically renamed and processed.
|
158
|
+
EOS
|
159
|
+
return open_file(fixed_name, file_pathes, preface)
|
160
|
+
end
|
156
161
|
end
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
end
|
166
|
-
|
167
|
-
MAX_LIST_SIZE = 20
|
168
|
-
def self.tool_list vault, name
|
169
|
-
name = Vault.normalize_note_name(name)
|
170
|
-
split_name = name.split(/[\s ]+/)
|
171
|
-
matched_notes = vault.notes.select do |note_name, _file_pathes|
|
172
|
-
split_name.map {|name_part| note_name.include?(name_part) }.all?
|
162
|
+
|
163
|
+
# 名前のノートが存在しない場合
|
164
|
+
if file_pathes.nil?
|
165
|
+
return note_not_found(name) if @links_by_file_name[name].nil?
|
166
|
+
return list_links(name, preface)
|
167
|
+
end
|
168
|
+
|
169
|
+
open_file(name, file_pathes, preface)
|
173
170
|
end
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
return <<~EOS unless has_found_link
|
171
|
+
|
172
|
+
private def note_not_found name
|
173
|
+
return <<~EOS
|
178
174
|
Note not found: #{name}
|
179
|
-
Search again with a substring or a string with a different notation.
|
180
175
|
EOS
|
176
|
+
end
|
177
|
+
|
178
|
+
private def list_links name, preface
|
181
179
|
return <<~EOS
|
180
|
+
#{preface}
|
182
181
|
Note not found: #{name}
|
183
182
|
However, I found other notes linked to this note.
|
184
|
-
#{
|
183
|
+
#{@links_by_file_name[name].shuffle.map { |file_path| "- #{File.basename(file_path, '.md')}" }.join("\n")}
|
185
184
|
EOS
|
186
185
|
end
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
186
|
+
|
187
|
+
private def open_file name, file_pathes, preface
|
188
|
+
# 複数のファイルがある場合は、---とファイル名で区切って返す
|
189
|
+
result = file_pathes.map do |file_path|
|
190
|
+
content = open(file_path) { |f| f.read.force_encoding('UTF-8') }
|
191
|
+
link_notes = if @links_by_file_path[file_path].nil?
|
192
|
+
""
|
193
|
+
else
|
194
|
+
<<~EOS
|
195
|
+
This note is linked by the following notes:
|
196
|
+
#{(@links_by_file_path[file_path] || []).shuffle.map { |file_path| "- #{File.basename(file_path, '.md')}" }.join("\n")}
|
197
|
+
EOS
|
198
|
+
end
|
199
|
+
metadata = <<~EOS
|
200
|
+
The contents of the note '#{name}' is as follows.
|
201
|
+
#{link_notes}
|
202
|
+
---
|
203
|
+
EOS
|
204
|
+
metadata + content
|
205
|
+
end.join("\n\n---\n\n")
|
206
|
+
preface + result
|
207
|
+
end
|
208
|
+
|
209
|
+
MAX_LIST_SIZE = 20
|
210
|
+
def tool_list name
|
211
|
+
name = Vault.normalize_note_name(name)
|
212
|
+
split_name = name.split(/[\s ]+/)
|
213
|
+
# 空白文字で検索された場合は失敗した旨を返す
|
214
|
+
# 仮に全ノートからランダムなMAX_LIST_SIZEを返してしまうと、LLMが誤って呼び出した場合に偽の関連性を持ってしまうため
|
215
|
+
if split_name.empty?
|
216
|
+
return <<~EOS
|
217
|
+
It cannot be listed in a blank string.
|
218
|
+
EOS
|
219
|
+
end
|
220
|
+
matched_notes = @notes.select do |note_name, _file_pathes|
|
221
|
+
split_name.map {|name_part| note_name.include?(name_part) }.all?
|
222
|
+
end
|
223
|
+
# 名前で検索したが見つからない場合
|
224
|
+
if matched_notes.empty?
|
225
|
+
has_found_link = @links_by_file_name[name] && !@links_by_file_name[name].empty?
|
226
|
+
return <<~EOS unless has_found_link
|
227
|
+
Note not found: #{name}
|
228
|
+
Search again with a substring or a string with a different notation.
|
229
|
+
EOS
|
230
|
+
return <<~EOS
|
231
|
+
Note not found: #{name}
|
232
|
+
However, I found other notes linked to this note.
|
233
|
+
#{@links_by_file_name[name].shuffle.map { |file_path| "- #{File.basename(file_path, '.md')}" }.join("\n")}
|
234
|
+
EOS
|
235
|
+
end
|
236
|
+
# マッチした名前の数が多すぎる場合は、ランダムにMAX_LIST_SIZE個選ぶ
|
237
|
+
preface = "Notes matching '#{name}' are as follows.\n"
|
238
|
+
if matched_notes.size > MAX_LIST_SIZE
|
239
|
+
matched_notes = matched_notes.to_a.sample(MAX_LIST_SIZE).to_h
|
240
|
+
preface = "Too many notes matched. I will show you only #{MAX_LIST_SIZE} of them.\n" + preface
|
241
|
+
end
|
242
|
+
# マッチした名前のリストで返す
|
243
|
+
list = matched_notes.keys.shuffle.map do |note_name|
|
244
|
+
"- #{note_name}"
|
245
|
+
end.join("\n")
|
246
|
+
preface + list
|
192
247
|
end
|
193
|
-
# マッチした名前のリストで返す
|
194
|
-
list = matched_notes.keys.shuffle.map do |note_name|
|
195
|
-
"- #{note_name}"
|
196
|
-
end.join("\n")
|
197
|
-
preface + list
|
198
248
|
end
|
249
|
+
|
199
250
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: obsidian_fetch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sou7
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mcp-rb
|
@@ -65,5 +65,6 @@ requirements: []
|
|
65
65
|
rubygems_version: 3.5.22
|
66
66
|
signing_key:
|
67
67
|
specification_version: 4
|
68
|
-
summary: MCP servers
|
68
|
+
summary: MCP servers focused on fetching and presenting information from Obsidian
|
69
|
+
vaults
|
69
70
|
test_files: []
|