obsidian_fetch 0.1.11 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 996b482b6dcf13de18f54917084aa60f9470d6903596d449fb5a0a5c7c2c51cd
4
- data.tar.gz: 5d76e2811f3f5e18de3a217fe570d168156e531ec20b86fbecbb74722ec82b58
3
+ metadata.gz: 101c9a34b8abc3a5cb6ea08f4a8a2f969e91717ba47799e1b4a803dc9d3edc12
4
+ data.tar.gz: dc12e468bc254caab30bbb2982f5d981e24ef997bc39d6f880c441b6ec0583f1
5
5
  SHA512:
6
- metadata.gz: 187c1f785491440b2f221ce29fd253cc57b421d31c458016c5577ba906f256fbbf510c9ae5de101084db49bf0d754b84466834ff7b500b4c96e72dc95383964b
7
- data.tar.gz: 2eabd6144aa231db4900e563e315b7be74cc3cc7460f52a8f604c00d8108fe91fec9eaf334fb64e53fc109c19eca3d1ab34316883dc1718a38eb35903dcf9db2
6
+ metadata.gz: cc00c3e03ddd24d4990ee58a4bdfacfaca1a67f791a9ce7e011c114a751bd28b9bf8f9aadb16baa574a0497b4ffeaaed7978d2298cfcc08f85f8be5828881f9c
7
+ data.tar.gz: 06a0930f90be578c44d5437b9e8dd04a6b0ed7ae08f38ab8c8e4ddaa842bc4bf32cd8f34ea6c15c45073511caf625a465c2e613cde3d13242a8124f248c99914
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # ObsidianFetch
2
2
 
3
+ > Languages: [日本語](README_ja.md)
4
+
3
5
  MCP servers focused on fetching and presenting information from Obsidian vaults.
4
6
 
5
7
  The existing MCP server has the following drawbacks:
@@ -23,10 +25,31 @@ gem install obsidian_fetch
23
25
 
24
26
  ## Usage
25
27
 
28
+ ### Stdio Transport (default)
29
+
26
30
  ```bash
27
31
  obsidian_fetch /path/to/your/vault
28
32
  ```
29
33
 
34
+ ### Streamable HTTP Transport
35
+
36
+ To run the server as an HTTP server:
37
+
38
+ ```bash
39
+ obsidian_fetch /path/to/your/vault --transport streamable-http
40
+ ```
41
+
42
+ By default, the server listens on `http://localhost:9292`. You can customize the port:
43
+
44
+ ```bash
45
+ obsidian_fetch /path/to/your/vault --transport streamable-http --port 3000
46
+ ```
47
+
48
+ ## Tools
49
+
50
+ - **read**: Read a note from the Obsidian vault. If multiple notes with the same name are found, all will be shown.
51
+ - **list**: Search for files with matching names partially.
52
+
30
53
  ## Contributing
31
54
 
32
55
  Bug reports and pull requests are welcome on GitHub at https://github.com/soukouki/obsidian_fetch.
data/README_ja.md ADDED
@@ -0,0 +1,63 @@
1
+ # ObsidianFetch
2
+
3
+ > Languages: [English version](README.md)
4
+
5
+ ObsidianのVaultから必要な情報をサクッと取得するためのMCPサーバーです。
6
+
7
+ ## 概要
8
+
9
+ 既存のMCPサーバーを使っていると、こんな悩みはありませんでしたか?
10
+
11
+ - コマンドが多すぎて、ローカルLLMだと読み込みに時間がかかる
12
+ - 特定のノートを探してほしいのに、LLMがうまくパスを辿ってくれない
13
+ - ツール設定が複雑で、LLMが正しく呼び出せない
14
+
15
+ 特にローカルGPUでLLMを動かしている場合、これらの問題がボトルネックになります。
16
+
17
+ そこで、「ノートのリスト取得」と「中身の読み込み」というシンプルな機能に特化したサーバーを開発しました。
18
+
19
+ さらに、使い勝手を良くするために以下の機能を追加しています:
20
+
21
+ - **リンクの自動修正**: `[[リンク名]]` のように括弧付きで検索したとき、不要な文字を自動で消して正しくリンクを解決します。
22
+ - **バックリンク対応**: ノートの中身だけでなく、「どのノートから参照されているか」というバックリンクも一緒に取得します。これでLLMが知識のつながりをより正確に把握できます。
23
+
24
+ ## インストール
25
+
26
+ ```bash
27
+ gem install obsidian_fetch
28
+ ```
29
+
30
+ ## 使用方法
31
+
32
+ ### Stdio Transport(デフォルト)
33
+
34
+ ```bash
35
+ obsidian_fetch /path/to/your/vault
36
+ ```
37
+
38
+ ### Streamable HTTP Transport
39
+
40
+ HTTPサーバーとして実行する場合は、以下のコマンドを使用します。
41
+
42
+ ```bash
43
+ obsidian_fetch /path/to/your/vault --transport streamable-http
44
+ ```
45
+
46
+ デフォルトでは `http://localhost:9292` で接続できます。ポート番号は以下のようにカスタマイズ可能です。
47
+
48
+ ```bash
49
+ obsidian_fetch /path/to/your/vault --transport streamable-http --port 3000
50
+ ```
51
+
52
+ ## ツール
53
+
54
+ - **read**: Obsidian Vaultからノートを取得します。同名のノートが複数存在する場合、すべて取得します。
55
+ - **list**: ファイル名で検索します。部分一致での検索が可能です。
56
+
57
+ ## 貢献
58
+
59
+ バグ報告やプルリクエストは、GitHubリポジトリ(https://github.com/soukouki/obsidian_fetch)にてお待ちしております。
60
+
61
+ ## ライセンス
62
+
63
+ このジェムは [MITライセンス](https://opensource.org/licenses/MIT) の下で公開されています。
data/exe/obsidian_fetch CHANGED
@@ -1,73 +1,4 @@
1
1
  # !/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'obsidian_fetch'
5
-
6
-
7
- name 'obsidian-fetch'
8
-
9
- version '0.1.0'
10
-
11
- vault_pathes = ARGV
12
-
13
- vault = ObsidianFetch::Vault.new(vault_pathes)
14
- STDERR.puts "Found #{vault.notes.size} notes"
15
- STDERR.puts "Found #{vault.links_by_file_name.size} links and #{vault.links_by_file_path.size} files linked by notes"
16
-
17
- tool 'read' do
18
- description <<~EOS
19
- Read a note from Obsidian vault.
20
- If I find multiple notes with the same name, I will show you all of them.
21
- EOS
22
- argument :name, String, required: true, description: "Note name to read"
23
- call do |args|
24
- name = args[:name]
25
- # 名前が文字列でない場合
26
- next 'Name must be a string' unless name.is_a?(String)
27
- vault.tool_read(name)
28
- end
29
- end
30
-
31
- tool 'read_multiple' do
32
- description <<~EOS
33
- Read a notes from Obsidian vault.
34
- If I find multiple notes with the same name, I will show you all of them.
35
- EOS
36
- argument :names, Array, items: String, required: true, description: "Note names to read"
37
- call do |args|
38
- names = args[:names]
39
- # 名前が文字列の配列でない場合
40
- next 'Name must be an array of strings' unless names.is_a?(Array) && names.all? { |name| name.is_a?(String) }
41
- names.map do |name|
42
- vault.tool_read(name)
43
- end.join("\n\n---\n\n")
44
- end
45
- end
46
-
47
- tool 'list' do
48
- description <<~EOS
49
- Search for files with matching names partially.
50
- EOS
51
- argument :name, String, required: true, description: "Note name to search"
52
- call do |args|
53
- name = args[:name]
54
- # 名前が文字列でない場合
55
- next 'Name must be a string' unless name.is_a?(String)
56
- vault.tool_list(name)
57
- end
58
- end
59
-
60
- tool 'list_multiple' do
61
- description <<~EOS
62
- Search for files with matching names partially.
63
- EOS
64
- argument :names, Array, items: String, required: true, description: "Note names to search"
65
- call do |args|
66
- names = args[:names]
67
- # 名前が文字列の配列でない場合
68
- next 'Name must be an array of strings' unless names.is_a?(Array) && names.all? { |name| name.is_a?(String) }
69
- names.map do |name|
70
- vault.tool_list(name)
71
- end.join("\n\n---\n\n")
72
- end
73
- end
4
+ require 'exe'
data/lib/exe.rb ADDED
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mcp'
4
+ require 'obsidian_fetch'
5
+
6
+ # Parse arguments with validation
7
+ VALID_TRANSPORTS = %w[stdio streamable-http].freeze
8
+ DEFAULT_TRANSPORT = 'stdio'
9
+ DEFAULT_PORT = 9292
10
+
11
+ transport_type = DEFAULT_TRANSPORT
12
+ port = DEFAULT_PORT
13
+ vault_paths = []
14
+
15
+ i = 0
16
+ while i < ARGV.length
17
+ arg = ARGV[i]
18
+ case arg
19
+ when '--help', '-h'
20
+ puts "Usage: obsidian_fetch [vault_path] [--transport {stdio|streamable-http}] [--port PORT]"
21
+ puts ""
22
+ puts "Options:"
23
+ puts " --transport {stdio|streamable-http} Transport type (default: stdio)"
24
+ puts " stdio: Standard I/O transport for local MCP clients"
25
+ puts " streamable-http: Streamable HTTP transport (MCP spec latest)"
26
+ puts ""
27
+ puts " --port PORT HTTP port for streamable-http transport (default: 9292)"
28
+ puts ""
29
+ puts "Examples:"
30
+ puts " obsidian_fetch /path/to/vault"
31
+ puts " obsidian_fetch /path/to/vault --transport streamable-http"
32
+ puts " obsidian_fetch /path/to/vault --transport streamable-http --port 3000"
33
+ exit 0
34
+ when '--transport'
35
+ if i + 1 >= ARGV.length
36
+ STDERR.puts "Error: --transport requires an argument (stdio or sse)"
37
+ exit 1
38
+ end
39
+ transport_type = ARGV[i + 1]
40
+ unless VALID_TRANSPORTS.include?(transport_type)
41
+ STDERR.puts "Error: Invalid transport '#{transport_type}'. Must be one of: #{VALID_TRANSPORTS.join(', ')}"
42
+ exit 1
43
+ end
44
+ i += 2 # Skip the next argument (transport type)
45
+ when '--port'
46
+ if i + 1 >= ARGV.length
47
+ STDERR.puts "Error: --port requires a numeric argument"
48
+ exit 1
49
+ end
50
+ port_val = ARGV[i + 1]
51
+ unless port_val =~ /^\d+$/
52
+ STDERR.puts "Error: --port must be a positive integer"
53
+ exit 1
54
+ end
55
+ port = port_val.to_i
56
+ unless port > 0 && port <= 65535
57
+ STDERR.puts "Error: --port must be between 1 and 65535"
58
+ exit 1
59
+ end
60
+ i += 2 # Skip the next argument (port value)
61
+ else
62
+ vault_paths << arg
63
+ i += 1
64
+ end
65
+ end
66
+
67
+ # Validate vault paths
68
+ if vault_paths.empty?
69
+ STDERR.puts "Error: At least one vault path is required"
70
+ STDERR.puts "Run with --help for usage information"
71
+ exit 1
72
+ end
73
+
74
+ vault_paths.each do |path|
75
+ unless File.directory?(path)
76
+ STDERR.puts "Error: Vault path '#{path}' does not exist or is not a directory"
77
+ exit 1
78
+ end
79
+ end
80
+
81
+ # Obsidian Vaultの初期化
82
+ $vault = ObsidianFetch::Vault.new(vault_paths)
83
+ STDERR.puts "Found #{$vault.notes.size} notes"
84
+ STDERR.puts "Found #{$vault.links_by_file_name.size} links and #{$vault.links_by_file_path.size} files linked by notes"
85
+ STDERR.puts "Transport type: #{transport_type}"
86
+ STDERR.puts "Port: #{port}" if transport_type == 'streamable-http'
87
+
88
+ # readツールの定義
89
+ class ReadTool < MCP::Tool
90
+ description "Read a note from Obsidian vault. If multiple notes with the same name are found, all will be shown."
91
+ input_schema(
92
+ properties: {
93
+ name: { type: "string", description: "Note name to read" }
94
+ },
95
+ required: ["name"]
96
+ )
97
+
98
+ def self.call(name:)
99
+ # 名前が文字列でない場合
100
+ return MCP::Tool::Response.new([{ type: "text", text: "Name must be a string" }]) unless name.is_a?(String)
101
+
102
+ # Vaultからノートを読み取る
103
+ result = $vault.tool_read(name)
104
+ MCP::Tool::Response.new([{ type: "text", text: result }])
105
+ end
106
+ end
107
+
108
+ # listツールの定義
109
+ class ListTool < MCP::Tool
110
+ description "Search for files with matching names partially."
111
+ input_schema(
112
+ properties: {
113
+ name: { type: "string", description: "Note name to search" }
114
+ },
115
+ required: ["name"]
116
+ )
117
+
118
+ def self.call(name:)
119
+ # 名前が文字列でない場合
120
+ return MCP::Tool::Response.new([{ type: "text", text: "Name must be a string" }]) unless name.is_a?(String)
121
+
122
+ # Vaultからノートを検索
123
+ result = $vault.tool_list(name)
124
+ MCP::Tool::Response.new([{ type: "text", text: result }])
125
+ end
126
+ end
127
+
128
+ # MCPサーバーの初期化
129
+ server = MCP::Server.new(
130
+ name: "obsidian-fetch",
131
+ version: "0.1.0",
132
+ tools: [ReadTool, ListTool],
133
+ )
134
+
135
+ # トランスポートを選択してサーバーを起動
136
+ case transport_type
137
+ when 'streamable-http'
138
+ require 'puma'
139
+ require 'rack'
140
+
141
+ # Streamable HTTP トランスポートを使用してサーバーを起動
142
+ transport = MCP::Server::Transports::StreamableHTTPTransport.new(server, stateless: true)
143
+
144
+ # PumaでRack appとして起動
145
+ $stdout.sync = true
146
+ $stderr.sync = true
147
+
148
+ server_obj = Puma::Server.new(transport) do
149
+ max_threads 16
150
+ min_threads 1
151
+ end
152
+ server_obj.add_tcp_listener "127.0.0.1", port
153
+
154
+ STDERR.puts "Streamable HTTP transport started on http://localhost:#{port}"
155
+ STDERR.puts "Press Ctrl+C to stop"
156
+
157
+ # サーバーを起動してメインプロセスをブロックする
158
+ server_thread = server_obj.run
159
+ server_thread.join
160
+ when 'stdio'
161
+ # Stdioトランスポートを使用してサーバーを起動
162
+ transport = MCP::Server::Transports::StdioTransport.new(server)
163
+ transport.open
164
+ else
165
+ STDERR.puts "Unknown transport type: #{transport_type}. Supported: stdio, sse"
166
+ exit 1
167
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ObsidianFetch
4
- VERSION = "0.1.11"
4
+ VERSION = "1.1.0"
5
5
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'yaml'
4
4
  require 'date'
5
- require 'mcp'
6
5
 
7
6
  require_relative "obsidian_fetch/version"
8
7
 
@@ -62,7 +61,7 @@ module ObsidianFetch
62
61
  puts "YAML syntax error in #{file_path}: #{e.message}"
63
62
  next
64
63
  end
65
- (frontmatter&.[](:aliases) || []).each do |alias_name|
64
+ Array(frontmatter&.[](:aliases) || []).each do |alias_name|
66
65
  alias_name = Vault.normalize_note_name(alias_name)
67
66
  @notes[alias_name] ||= []
68
67
  @notes[alias_name] << file_path
@@ -82,6 +81,7 @@ module ObsidianFetch
82
81
  # もしリンクが設定されていれば、linksに追加する
83
82
  # [[link]]と[[link|displayname]]の場合
84
83
  # linkに.mdが付いている場合、付いていない場合両方を考慮する
84
+ # TODO : linkにディレクトリ名が着くことがあるので、それを除去する
85
85
  content.scan(/\[\[(.*?)(?:\|.*)?\]\]/) do |match|
86
86
  link_name = match[0]
87
87
  link_name = link_name.sub(/\.md$/, '') # .mdを削除
metadata CHANGED
@@ -1,30 +1,56 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: obsidian_fetch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - sou7
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-04-30 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: mcp-rb
13
+ name: mcp
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: 0.3.2
18
+ version: 0.14.0
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
- version: 0.3.2
27
- description:
25
+ version: 0.14.0
26
+ - !ruby/object:Gem::Dependency
27
+ name: rack
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: puma
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '5.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '5.0'
28
54
  email:
29
55
  - soukouki0@yahoo.co.jp
30
56
  executables:
@@ -35,8 +61,10 @@ files:
35
61
  - CHANGELOG.md
36
62
  - LICENSE.txt
37
63
  - README.md
64
+ - README_ja.md
38
65
  - Rakefile
39
66
  - exe/obsidian_fetch
67
+ - lib/exe.rb
40
68
  - lib/obsidian_fetch.rb
41
69
  - lib/obsidian_fetch/version.rb
42
70
  - sig/obsidian_fetch.rbs
@@ -47,7 +75,6 @@ metadata:
47
75
  homepage_uri: https://ob.sou7.io/2025-04/week17/obsidian_fetch
48
76
  source_code_uri: https://github.com/soukouki/obsidian_fetch
49
77
  changelog_uri: https://github.com/soukouki/obsidian_fetch/blob/main/CHANGELOG.md
50
- post_install_message:
51
78
  rdoc_options: []
52
79
  require_paths:
53
80
  - lib
@@ -62,8 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
62
89
  - !ruby/object:Gem::Version
63
90
  version: '0'
64
91
  requirements: []
65
- rubygems_version: 3.5.22
66
- signing_key:
92
+ rubygems_version: 3.6.9
67
93
  specification_version: 4
68
94
  summary: MCP servers focused on fetching and presenting information from Obsidian
69
95
  vaults