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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be242fb83e36e23491358f1109b2cf71b0b849d685aadc5752483733d8d92b3b
4
- data.tar.gz: f960f0a39c9a008e35a27f4d420440583232907c8f2114be8df84de12f66a67d
3
+ metadata.gz: 996b482b6dcf13de18f54917084aa60f9470d6903596d449fb5a0a5c7c2c51cd
4
+ data.tar.gz: 5d76e2811f3f5e18de3a217fe570d168156e531ec20b86fbecbb74722ec82b58
5
5
  SHA512:
6
- metadata.gz: '00738a009acd96f5545035d30a5c4fc96987c90d014abd62c2b159c09a4c3e2ee64bbc85ea454242f1c86b0124deafa06f2d1fba010fb0445a1e5a55434357f5'
7
- data.tar.gz: bde26b406b1cad4f1554b17970ed8d79ab816cbd2735ff8542e82ebc53d3934b95d9f4ad5a03216141af0a0b125ed82d106a22008eb2ea7d6816b7acae1954eb
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 specialising in retrieving information from Obsidian vaults.
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
- - There are many commands, and when computational resources are limited, it can take a long time to load the prompt.
7
- - When reading a note labeled "LLM," it is necessary to search for the path first before loading it, but the LLM may not always follow this procedure.
8
- - Some tools have unnecessary options, causing the LLM to sometimes fail to invoke the tool correctly.
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 a list of notes.
11
+ To address this, we developed a new MCP server that simply retrieves and loads lists of notes.
12
12
 
13
- Additionally, the new server has the following features:
14
- - When the LLM tries to retrieve link information and searches with brackets like `[[link name]]`, it automatically removes characters that cannot be used in links.
15
- - When reading a file, if there are links pointing to the opened file, it displays them.
16
- - Especially in network-style note tools like Obsidian, following such links to load related notes can be very powerful.
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
- ObsidianFetch::tool_read(vault, name)
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
- ObsidianFetch::tool_read(vault, name)
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
- ObsidianFetch::tool_list(vault, name)
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
- ObsidianFetch::tool_list(vault, name)
70
+ vault.tool_list(name)
71
71
  end.join("\n\n---\n\n")
72
72
  end
73
73
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ObsidianFetch
4
- VERSION = "0.1.8"
4
+ VERSION = "0.1.11"
5
5
  end
@@ -127,73 +127,124 @@ module ObsidianFetch
127
127
  file_pathes.uniq!
128
128
  end
129
129
  end
130
- end
131
130
 
132
- def self.tool_read vault, name
133
- name = Vault.normalize_note_name(name)
134
- file_pathes = vault.notes[name]
135
- # 名前のノートが存在しない場合
136
- if file_pathes.nil?
137
- return "Note not found: #{name}" if vault.links_by_file_name[name].nil?
138
- file_pathes = vault.links_by_file_name[name]
139
- return <<~EOS
140
- Note not found: #{name}
141
- However, I found other notes linked to this note.
142
- #{file_pathes.map { |file_path| "- #{file_path}" }.join("\n")}
143
- EOS
144
- end
145
-
146
- # 複数のファイルがある場合は、---とファイル名で区切って返す
147
- file_pathes.map do |file_path|
148
- content = open(file_path) { |f| f.read.force_encoding('UTF-8') }
149
- link_notes = if vault.links_by_file_path[file_path].nil?
150
- ""
151
- else
152
- <<~EOS
153
- This note is linked by the following notes:
154
- #{(vault.links_by_file_path[file_path] || []).shuffle.map { |file_path| "- #{File.basename(file_path, '.md')}" }.join("\n")}
155
- EOS
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
- preface = <<~EOS
158
- The contents of the note '#{name}' is as follows.
159
- #{link_notes}
160
- ---
161
-
162
- EOS
163
- preface + content
164
- end.join("\n\n---\n\n")
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
- if matched_notes.empty?
176
- has_found_link = vault.links_by_file_name[name] && !vault.links_by_file_name[name].empty?
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
- #{vault.links_by_file_name[name].shuffle.map { |file_path| "- #{File.basename(file_path, '.md')}" }.join("\n")}
183
+ #{@links_by_file_name[name].shuffle.map { |file_path| "- #{File.basename(file_path, '.md')}" }.join("\n")}
185
184
  EOS
186
185
  end
187
- # マッチした名前の数が多すぎる場合は、ランダムにMAX_LIST_SIZE個選ぶ
188
- preface = "Notes matching '#{name}' are as follows.\n"
189
- if matched_notes.size > MAX_LIST_SIZE
190
- matched_notes = matched_notes.to_a.sample(MAX_LIST_SIZE).to_h
191
- preface = "Too many notes matched. I will show you only #{MAX_LIST_SIZE} of them.\n" + preface
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.8
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-26 00:00:00.000000000 Z
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 specialising in retrieving information from Obsidian vaults.
68
+ summary: MCP servers focused on fetching and presenting information from Obsidian
69
+ vaults
69
70
  test_files: []