bioruby-mcp-server 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9af8854132608c26884e31bb79bfa446eec6847f198757afddca808daf879aa8
4
+ data.tar.gz: 0a7dc98c744e4007cb6b9e785fa592932005480beee802d03fd9cbc0f01d4432
5
+ SHA512:
6
+ metadata.gz: 81e2e6489387e4cbb564b7881cbc7fddad3742831acc1ba609b54d41205c2d4978062aa02c8eafad90e2ffcc6b8d67eb289ae2ef0979ed3c56d3e70ab6781380
7
+ data.tar.gz: 6ed869a261b186ec7743d2d1edbda88daf452ccd2a8061af66c040680ae7e9261549811389875bbee05aac771f5e2ddd0ee6a41fbd1ece00d4a1b3c3cd52e200
data/CHANGELOG.md ADDED
@@ -0,0 +1,31 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2025-09-19
9
+
10
+ ### Added
11
+ - Initial release of bioruby-mcp-server
12
+ - Model Context Protocol (MCP) server implementation using official MCP Ruby SDK
13
+ - Integration with BioRuby KEGG module
14
+ - KEGG pathway information retrieval (`kegg_pathway_info`)
15
+ - KEGG compound information retrieval (`kegg_compound_info`)
16
+ - KEGG enzyme information retrieval (`kegg_enzyme_info`)
17
+ - KEGG compound search functionality (`kegg_search_compounds`)
18
+ - Pathway discovery by compound (`kegg_find_pathways_by_compound`)
19
+ - KEGG organism listing (`kegg_list_organisms`)
20
+ - Comprehensive test suite
21
+ - Command-line executable (`bioruby-mcp-server`)
22
+ - Full documentation and examples
23
+
24
+ ### Technical Details
25
+ - Built on Ruby 3.0+ with BioRuby and official MCP Ruby SDK
26
+ - Uses KEGG REST API for data access
27
+ - Follows MCP protocol specification via official SDK
28
+ - Each KEGG operation implemented as MCP::Tool subclass
29
+ - Includes proper error handling and logging
30
+ - Supports filtering and search capabilities
31
+ - Full MCP protocol compliance through SDK
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2025, Kozo Nishida
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,293 @@
1
+ # bioruby-mcp-server
2
+
3
+ A Model Context Protocol (MCP) server that provides access to BioRuby KEGG functionality. This server allows AI assistants to query KEGG databases for biological pathways, compounds, enzymes, and other molecular information through the standardized MCP protocol.
4
+
5
+ Built using the official [MCP Ruby SDK](https://github.com/modelcontextprotocol/ruby-sdk).
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'bioruby-mcp-server'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ ```bash
18
+ $ bundle install
19
+ ```
20
+
21
+ Or install it yourself as:
22
+
23
+ ```bash
24
+ $ gem install bioruby-mcp-server
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### Starting the Server
30
+
31
+ To start the MCP server:
32
+
33
+ ```bash
34
+ $ bioruby-mcp-server
35
+ ```
36
+
37
+ The server will start and listen for MCP protocol messages on stdin/stdout.
38
+
39
+ ### Available Tools
40
+
41
+ The server provides the following tools for interacting with KEGG databases:
42
+
43
+ #### 1. `kegg_pathway_info`
44
+ Get detailed information about a KEGG pathway.
45
+
46
+ **Parameters:**
47
+ - `pathway_id` (string): KEGG pathway ID (e.g., 'map00010', 'hsa00010')
48
+
49
+ **Example:**
50
+ ```json
51
+ {
52
+ "name": "kegg_pathway_info",
53
+ "arguments": {
54
+ "pathway_id": "map00010"
55
+ }
56
+ }
57
+ ```
58
+
59
+ #### 2. `kegg_compound_info`
60
+ Get detailed information about a KEGG compound.
61
+
62
+ **Parameters:**
63
+ - `compound_id` (string): KEGG compound ID (e.g., 'C00002', 'cpd:C00002')
64
+
65
+ **Example:**
66
+ ```json
67
+ {
68
+ "name": "kegg_compound_info",
69
+ "arguments": {
70
+ "compound_id": "C00002"
71
+ }
72
+ }
73
+ ```
74
+
75
+ #### 3. `kegg_enzyme_info`
76
+ Get detailed information about a KEGG enzyme.
77
+
78
+ **Parameters:**
79
+ - `enzyme_id` (string): KEGG enzyme ID (e.g., 'ec:1.1.1.1')
80
+
81
+ **Example:**
82
+ ```json
83
+ {
84
+ "name": "kegg_enzyme_info",
85
+ "arguments": {
86
+ "enzyme_id": "ec:1.1.1.1"
87
+ }
88
+ }
89
+ ```
90
+
91
+ #### 4. `kegg_search_compounds`
92
+ Search for KEGG compounds by name or formula.
93
+
94
+ **Parameters:**
95
+ - `query` (string): Search query (compound name, formula, etc.)
96
+ - `database` (string, optional): Database to search (default: 'compound')
97
+
98
+ **Example:**
99
+ ```json
100
+ {
101
+ "name": "kegg_search_compounds",
102
+ "arguments": {
103
+ "query": "glucose",
104
+ "database": "compound"
105
+ }
106
+ }
107
+ ```
108
+
109
+ #### 5. `kegg_find_pathways_by_compound`
110
+ Find pathways containing a specific compound.
111
+
112
+ **Parameters:**
113
+ - `compound_id` (string): KEGG compound ID (e.g., 'C00002')
114
+
115
+ **Example:**
116
+ ```json
117
+ {
118
+ "name": "kegg_find_pathways_by_compound",
119
+ "arguments": {
120
+ "compound_id": "C00002"
121
+ }
122
+ }
123
+ ```
124
+
125
+ #### 6. `kegg_list_organisms`
126
+ List available organisms in KEGG.
127
+
128
+ **Parameters:**
129
+ - `filter` (string, optional): Optional filter for organism names
130
+
131
+ **Example:**
132
+ ```json
133
+ {
134
+ "name": "kegg_list_organisms",
135
+ "arguments": {
136
+ "filter": "human"
137
+ }
138
+ }
139
+ ```
140
+
141
+ ### Integration with AI Assistants
142
+
143
+ This MCP server can be used with any AI assistant that supports the Model Context Protocol. Configure your AI assistant to connect to this server to enable KEGG database queries.
144
+
145
+ #### Claude Desktop Integration
146
+
147
+ To connect this server to Claude Desktop, you'll need to configure it in Claude's MCP settings:
148
+
149
+ ##### 1. Install the Server
150
+
151
+ First, install the bioruby-mcp-server gem:
152
+
153
+ ```bash
154
+ gem install bioruby-mcp-server
155
+ ```
156
+
157
+ ##### 2. Configure Claude Desktop
158
+
159
+ Add the server configuration to Claude Desktop's configuration file:
160
+
161
+ **On macOS:**
162
+ Edit or create `~/Library/Application Support/Claude/claude_desktop_config.json`
163
+
164
+ **On Windows:**
165
+ Edit or create `%APPDATA%\Claude\claude_desktop_config.json`
166
+
167
+ **On Linux:**
168
+ Edit or create `~/.config/Claude/claude_desktop_config.json`
169
+
170
+ Add the following configuration:
171
+
172
+ ```json
173
+ {
174
+ "mcpServers": {
175
+ "bioruby-kegg": {
176
+ "command": "bioruby-mcp-server",
177
+ "args": []
178
+ }
179
+ }
180
+ }
181
+ ```
182
+
183
+ If you are Windows WSL user:
184
+
185
+ ```json
186
+ {
187
+ "mcpServers": {
188
+ "bioruby-kegg": {
189
+ "command": "wsl.exe",
190
+ "args": [
191
+ "bioruby-mcp-server"
192
+ ]
193
+ }
194
+ }
195
+ }
196
+ ```
197
+
198
+ ##### 3. Restart Claude Desktop
199
+
200
+ After saving the configuration file, restart Claude Desktop to load the new MCP server.
201
+
202
+ ##### 4. Verify Connection
203
+
204
+ Once connected, you can test the integration by asking Claude to query KEGG databases. For example:
205
+
206
+ - "Get information about KEGG pathway map00010"
207
+ - "Search for glucose compounds in KEGG"
208
+ - "Find pathways containing compound C00002"
209
+
210
+ ##### Troubleshooting
211
+
212
+ If the connection fails:
213
+
214
+ 1. **Check gem installation**: Ensure `bioruby-mcp-server` is installed and accessible in your PATH
215
+ ```bash
216
+ which bioruby-mcp-server
217
+ gem list bioruby-mcp-server
218
+ ```
219
+
220
+ 2. **Verify configuration**: Check that the JSON configuration file is valid and in the correct location
221
+
222
+ 3. **Check permissions**: Ensure Claude Desktop has permission to execute the bioruby-mcp-server command
223
+
224
+ 4. **Review logs**: Check Claude Desktop's logs for any error messages related to MCP server connections
225
+
226
+ #### General MCP Configuration
227
+
228
+ For other AI assistants that support MCP, use this general configuration format:
229
+
230
+ ```json
231
+ {
232
+ "mcpServers": {
233
+ "bioruby-kegg": {
234
+ "command": "bioruby-mcp-server",
235
+ "args": []
236
+ }
237
+ }
238
+ }
239
+ ```
240
+
241
+ ## Development
242
+
243
+ After checking out the repo, run `bundle install` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
244
+
245
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
246
+
247
+ ## Testing
248
+
249
+ Run the test suite:
250
+
251
+ ```bash
252
+ $ bundle exec rake test
253
+ ```
254
+
255
+ Run RuboCop for code style checking:
256
+
257
+ ```bash
258
+ $ bundle exec rubocop
259
+ ```
260
+
261
+ ## Requirements
262
+
263
+ - Ruby 3.0 or higher
264
+ - Internet connectivity for accessing KEGG REST API
265
+
266
+ ## Dependencies
267
+
268
+ - [bio](https://github.com/bioruby/bioruby) - BioRuby library for biological data processing
269
+ - [mcp](https://github.com/modelcontextprotocol/ruby-sdk) - Official MCP Ruby SDK
270
+
271
+ ## Technical Implementation
272
+
273
+ This server is built using the official [MCP Ruby SDK](https://github.com/modelcontextprotocol/ruby-sdk), ensuring full compliance with the Model Context Protocol specification. Each KEGG tool is implemented as an `MCP::Tool` subclass, providing proper input schema validation and structured responses.
274
+
275
+ ### Architecture
276
+
277
+ - **KEGG REST API Integration**: Direct integration with KEGG's REST API for real-time data access
278
+ - **BioRuby Parser Integration**: Uses BioRuby's KEGG parsers for proper data structure handling
279
+ - **MCP Protocol Compliance**: Built on the official MCP Ruby SDK for standardized protocol handling
280
+ - **Tool-based Architecture**: Each KEGG operation is implemented as a separate MCP tool
281
+ - **Robust Error Handling**: Comprehensive error handling for network issues, invalid IDs, and API failures
282
+
283
+ ## Contributing
284
+
285
+ Bug reports and pull requests are welcome on GitHub at https://github.com/kozo2/bioruby-mcp-server. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/kozo2/bioruby-mcp-server/blob/main/CODE_OF_CONDUCT.md).
286
+
287
+ ## License
288
+
289
+ The gem is available as open source under the terms of the [BSD 2-Clause License](https://opensource.org/licenses/BSD-2-Clause).
290
+
291
+ ## Code of Conduct
292
+
293
+ Everyone interacting in the bioruby-mcp-server project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/kozo2/bioruby-mcp-server/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'minitest/test_task'
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[test rubocop]
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/bioruby/mcp/server/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'bioruby-mcp-server'
7
+ spec.version = BioRuby::MCP::Server::VERSION
8
+ spec.authors = ['Kozo Nishida']
9
+ spec.email = ['kozo2@gmail.com']
10
+
11
+ spec.summary = 'Model Context Protocol server for BioRuby KEGG module'
12
+ spec.description = 'An MCP server that provides access to BioRuby KEGG functionality, ' \
13
+ 'allowing AI assistants to query KEGG databases for biological pathways, ' \
14
+ 'compounds, and other molecular information.'
15
+ spec.homepage = 'https://github.com/kozo2/bioruby-mcp-server'
16
+ spec.license = 'BSD-2-Clause'
17
+ spec.required_ruby_version = '>= 3.0.0'
18
+
19
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
20
+ spec.metadata['homepage_uri'] = spec.homepage
21
+ spec.metadata['source_code_uri'] = 'https://github.com/kozo2/bioruby-mcp-server'
22
+ spec.metadata['changelog_uri'] = 'https://github.com/kozo2/bioruby-mcp-server/blob/main/CHANGELOG.md'
23
+
24
+ # Specify which files should be added to the gem when it is released.
25
+ spec.files = Dir.chdir(__dir__) do
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ (File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git appveyor Gemfile])
28
+ end
29
+ end
30
+ spec.bindir = 'exe'
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ['lib']
33
+
34
+ # Dependencies
35
+ spec.add_dependency 'bio', '~> 2.0'
36
+ spec.add_dependency 'mcp', '~> 0.1'
37
+
38
+ # Development dependencies
39
+ spec.add_development_dependency 'minitest', '~> 5.0'
40
+ spec.add_development_dependency 'rake', '~> 13.0'
41
+ spec.add_development_dependency 'rubocop', '~> 1.21'
42
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/bioruby'
5
+
6
+ # Start the BioRuby MCP Server using the SDK
7
+ BioRuby::MCP::Server::Core.start_stdio_server
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mcp'
4
+ require 'mcp/server/transports/stdio_transport'
5
+ require_relative 'kegg_tools'
6
+
7
+ module BioRuby
8
+ module MCP
9
+ module Server
10
+ # BioRuby MCP Server using the official MCP Ruby SDK
11
+ class Core
12
+ def self.create_server
13
+ # Create MCP server with all KEGG tools
14
+ ::MCP::Server.new(
15
+ name: 'bioruby-mcp-server',
16
+ version: VERSION,
17
+ tools: [
18
+ KEGGPathwayTool,
19
+ KEGGCompoundTool,
20
+ KEGGEnzymeTool,
21
+ KEGGSearchTool,
22
+ KEGGPathwayFinderTool,
23
+ KEGGOrganismTool
24
+ ]
25
+ )
26
+ end
27
+
28
+ def self.start_stdio_server
29
+ server = create_server
30
+ transport = ::MCP::Server::Transports::StdioTransport.new(server)
31
+ transport.open
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,417 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mcp'
4
+ require 'bio'
5
+ require 'open-uri'
6
+ require 'uri'
7
+
8
+ module BioRuby
9
+ module MCP
10
+ module Server
11
+ # KEGG pathway information tool
12
+ class KEGGPathwayTool < ::MCP::Tool
13
+ tool_name 'kegg_pathway_info'
14
+ description 'Get information about a KEGG pathway'
15
+ input_schema(
16
+ properties: {
17
+ pathway_id: {
18
+ type: 'string',
19
+ description: 'KEGG pathway ID (e.g., "map00010", "hsa00010")'
20
+ }
21
+ },
22
+ required: ['pathway_id']
23
+ )
24
+
25
+ KEGG_REST_BASE = 'http://rest.kegg.jp'
26
+
27
+ class << self
28
+ def call(pathway_id:, server_context: nil)
29
+ # Clean up pathway ID if needed
30
+ pathway_id = pathway_id.gsub(/^(pathway:|path:|map:)/, '')
31
+
32
+ # Get pathway entry from KEGG REST API
33
+ pathway_data = kegg_get_entry(pathway_id)
34
+
35
+ if pathway_data.nil? || pathway_data.empty?
36
+ return ::MCP::Tool::Response.new([{
37
+ type: 'text',
38
+ text: "Pathway not found: #{pathway_id}"
39
+ }])
40
+ end
41
+
42
+ # Parse the pathway data using BioRuby
43
+ pathway = Bio::KEGG::PATHWAY.new(pathway_data)
44
+
45
+ result = []
46
+ result << {
47
+ type: 'text',
48
+ text: "KEGG Pathway: #{pathway_id}\n" \
49
+ "Name: #{pathway.name}\n" \
50
+ "Description: #{pathway.description}\n" \
51
+ "Class: #{pathway.pathway_class}\n" \
52
+ "Genes: #{pathway.genes&.length || 0} genes\n" \
53
+ "Compounds: #{pathway.compounds&.length || 0} compounds"
54
+ }
55
+
56
+ if pathway.genes && !pathway.genes.empty?
57
+ gene_list = pathway.genes.first(10).map { |gene_id, name| "#{gene_id}: #{name}" }
58
+ result << {
59
+ type: 'text',
60
+ text: "Sample genes (first 10):\n#{gene_list.join("\n")}"
61
+ }
62
+ end
63
+
64
+ ::MCP::Tool::Response.new(result)
65
+ rescue => e
66
+ ::MCP::Tool::Response.new([{
67
+ type: 'text',
68
+ text: "Error retrieving pathway info: #{e.message}"
69
+ }])
70
+ end
71
+
72
+ private
73
+
74
+ def kegg_get_entry(entry_id)
75
+ uri = URI("#{KEGG_REST_BASE}/get/#{entry_id}")
76
+ begin
77
+ uri.open.read
78
+ rescue OpenURI::HTTPError
79
+ nil
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ # KEGG compound information tool
86
+ class KEGGCompoundTool < ::MCP::Tool
87
+ tool_name 'kegg_compound_info'
88
+ description 'Get information about a KEGG compound'
89
+ input_schema(
90
+ properties: {
91
+ compound_id: {
92
+ type: 'string',
93
+ description: 'KEGG compound ID (e.g., "C00002", "cpd:C00002")'
94
+ }
95
+ },
96
+ required: ['compound_id']
97
+ )
98
+
99
+ KEGG_REST_BASE = 'http://rest.kegg.jp'
100
+
101
+ class << self
102
+ def call(compound_id:, server_context: nil)
103
+ # Clean up compound ID if needed
104
+ compound_id = compound_id.gsub(/^(cpd:|compound:)/, '')
105
+
106
+ # Get compound entry from KEGG REST API
107
+ compound_data = kegg_get_entry(compound_id)
108
+
109
+ if compound_data.nil? || compound_data.empty?
110
+ return ::MCP::Tool::Response.new([{
111
+ type: 'text',
112
+ text: "Compound not found: #{compound_id}"
113
+ }])
114
+ end
115
+
116
+ # Parse the compound data using BioRuby
117
+ compound = Bio::KEGG::COMPOUND.new(compound_data)
118
+
119
+ ::MCP::Tool::Response.new([{
120
+ type: 'text',
121
+ text: "KEGG Compound: #{compound_id}\n" \
122
+ "Name: #{compound.name}\n" \
123
+ "Formula: #{compound.formula}\n" \
124
+ "Mass: #{compound.mass}\n" \
125
+ "Comment: #{compound.comment}\n" \
126
+ "Pathways: #{compound.pathways&.length || 0} pathways\n" \
127
+ "Enzymes: #{compound.enzymes&.length || 0} enzymes"
128
+ }])
129
+ rescue => e
130
+ ::MCP::Tool::Response.new([{
131
+ type: 'text',
132
+ text: "Error retrieving compound info: #{e.message}"
133
+ }])
134
+ end
135
+
136
+ private
137
+
138
+ def kegg_get_entry(entry_id)
139
+ uri = URI("#{KEGG_REST_BASE}/get/#{entry_id}")
140
+ begin
141
+ uri.open.read
142
+ rescue OpenURI::HTTPError
143
+ nil
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ # KEGG enzyme information tool
150
+ class KEGGEnzymeTool < ::MCP::Tool
151
+ tool_name 'kegg_enzyme_info'
152
+ description 'Get information about a KEGG enzyme'
153
+ input_schema(
154
+ properties: {
155
+ enzyme_id: {
156
+ type: 'string',
157
+ description: 'KEGG enzyme ID (e.g., "ec:1.1.1.1")'
158
+ }
159
+ },
160
+ required: ['enzyme_id']
161
+ )
162
+
163
+ KEGG_REST_BASE = 'http://rest.kegg.jp'
164
+
165
+ class << self
166
+ def call(enzyme_id:, server_context: nil)
167
+ # Clean up enzyme ID if needed
168
+ enzyme_id = enzyme_id.gsub(/^(ec:|enzyme:)/, '')
169
+
170
+ # Get enzyme entry from KEGG REST API
171
+ enzyme_data = kegg_get_entry(enzyme_id)
172
+
173
+ if enzyme_data.nil? || enzyme_data.empty?
174
+ return ::MCP::Tool::Response.new([{
175
+ type: 'text',
176
+ text: "Enzyme not found: #{enzyme_id}"
177
+ }])
178
+ end
179
+
180
+ # Parse the enzyme data using BioRuby
181
+ enzyme = Bio::KEGG::ENZYME.new(enzyme_data)
182
+
183
+ ::MCP::Tool::Response.new([{
184
+ type: 'text',
185
+ text: "KEGG Enzyme: #{enzyme_id}\n" \
186
+ "Name: #{enzyme.name}\n" \
187
+ "Class: #{enzyme.enzyme_class}\n" \
188
+ "Reaction: #{enzyme.reaction}\n" \
189
+ "Substrate: #{enzyme.substrate}\n" \
190
+ "Product: #{enzyme.product}\n" \
191
+ "Comment: #{enzyme.comment}"
192
+ }])
193
+ rescue => e
194
+ ::MCP::Tool::Response.new([{
195
+ type: 'text',
196
+ text: "Error retrieving enzyme info: #{e.message}"
197
+ }])
198
+ end
199
+
200
+ private
201
+
202
+ def kegg_get_entry(entry_id)
203
+ uri = URI("#{KEGG_REST_BASE}/get/#{entry_id}")
204
+ begin
205
+ uri.open.read
206
+ rescue OpenURI::HTTPError
207
+ nil
208
+ end
209
+ end
210
+ end
211
+ end
212
+
213
+ # KEGG compound search tool
214
+ class KEGGSearchTool < ::MCP::Tool
215
+ tool_name 'kegg_search_compounds'
216
+ description 'Search for KEGG compounds by name or formula'
217
+ input_schema(
218
+ properties: {
219
+ query: {
220
+ type: 'string',
221
+ description: 'Search query (compound name, formula, etc.)'
222
+ },
223
+ database: {
224
+ type: 'string',
225
+ description: 'Database to search (default: "compound")',
226
+ default: 'compound'
227
+ }
228
+ },
229
+ required: ['query']
230
+ )
231
+
232
+ KEGG_REST_BASE = 'http://rest.kegg.jp'
233
+
234
+ class << self
235
+ def call(query:, database: 'compound', server_context: nil)
236
+ # Use KEGG REST API to search for compounds
237
+ results = kegg_find_entries(database, query)
238
+
239
+ if results.nil? || results.empty?
240
+ return ::MCP::Tool::Response.new([{
241
+ type: 'text',
242
+ text: "No compounds found for query: #{query}"
243
+ }])
244
+ end
245
+
246
+ # Parse and format results
247
+ formatted_results = results.split("\n").first(20).map do |line|
248
+ parts = line.split("\t")
249
+ "#{parts[0]}: #{parts[1]}" if parts.length >= 2
250
+ end.compact
251
+
252
+ ::MCP::Tool::Response.new([{
253
+ type: 'text',
254
+ text: "Search results for '#{query}' (first 20):\n#{formatted_results.join("\n")}"
255
+ }])
256
+ rescue => e
257
+ ::MCP::Tool::Response.new([{
258
+ type: 'text',
259
+ text: "Error searching compounds: #{e.message}"
260
+ }])
261
+ end
262
+
263
+ private
264
+
265
+ def kegg_find_entries(database, query)
266
+ uri = URI("#{KEGG_REST_BASE}/find/#{database}/#{query}")
267
+ begin
268
+ uri.open.read
269
+ rescue OpenURI::HTTPError
270
+ nil
271
+ end
272
+ end
273
+ end
274
+ end
275
+
276
+ # KEGG pathway finder tool
277
+ class KEGGPathwayFinderTool < ::MCP::Tool
278
+ tool_name 'kegg_find_pathways_by_compound'
279
+ description 'Find pathways containing a specific compound'
280
+ input_schema(
281
+ properties: {
282
+ compound_id: {
283
+ type: 'string',
284
+ description: 'KEGG compound ID (e.g., "C00002")'
285
+ }
286
+ },
287
+ required: ['compound_id']
288
+ )
289
+
290
+ KEGG_REST_BASE = 'http://rest.kegg.jp'
291
+
292
+ class << self
293
+ def call(compound_id:, server_context: nil)
294
+ # Clean up compound ID
295
+ compound_id = compound_id.gsub(/^(cpd:|compound:)/, '')
296
+
297
+ # Get compound info first to find associated pathways
298
+ compound_data = kegg_get_entry(compound_id)
299
+
300
+ if compound_data.nil? || compound_data.empty?
301
+ return ::MCP::Tool::Response.new([{
302
+ type: 'text',
303
+ text: "Compound not found: #{compound_id}"
304
+ }])
305
+ end
306
+
307
+ compound = Bio::KEGG::COMPOUND.new(compound_data)
308
+
309
+ if compound.pathways.nil? || compound.pathways.empty?
310
+ return ::MCP::Tool::Response.new([{
311
+ type: 'text',
312
+ text: "No pathways found for compound: #{compound_id}"
313
+ }])
314
+ end
315
+
316
+ pathway_list = compound.pathways.map do |pathway_id, pathway_name|
317
+ "#{pathway_id}: #{pathway_name}"
318
+ end
319
+
320
+ ::MCP::Tool::Response.new([{
321
+ type: 'text',
322
+ text: "Pathways containing compound #{compound_id}:\n#{pathway_list.join("\n")}"
323
+ }])
324
+ rescue => e
325
+ ::MCP::Tool::Response.new([{
326
+ type: 'text',
327
+ text: "Error finding pathways: #{e.message}"
328
+ }])
329
+ end
330
+
331
+ private
332
+
333
+ def kegg_get_entry(entry_id)
334
+ uri = URI("#{KEGG_REST_BASE}/get/#{entry_id}")
335
+ begin
336
+ uri.open.read
337
+ rescue OpenURI::HTTPError
338
+ nil
339
+ end
340
+ end
341
+ end
342
+ end
343
+
344
+ # KEGG organism list tool
345
+ class KEGGOrganismTool < ::MCP::Tool
346
+ tool_name 'kegg_list_organisms'
347
+ description 'List available organisms in KEGG'
348
+ input_schema(
349
+ properties: {
350
+ filter: {
351
+ type: 'string',
352
+ description: 'Optional filter for organism names'
353
+ }
354
+ }
355
+ )
356
+
357
+ KEGG_REST_BASE = 'http://rest.kegg.jp'
358
+
359
+ class << self
360
+ def call(filter: nil, server_context: nil)
361
+ # Get list of organisms from KEGG REST API
362
+ organism_data = kegg_list_entries('organism')
363
+
364
+ if organism_data.nil? || organism_data.empty?
365
+ return ::MCP::Tool::Response.new([{
366
+ type: 'text',
367
+ text: 'No organisms found'
368
+ }])
369
+ end
370
+
371
+ organisms = organism_data.split("\n").map do |line|
372
+ parts = line.split("\t")
373
+ if parts.length >= 2
374
+ org_code = parts[0]
375
+ org_name = parts[1]
376
+ { code: org_code, name: org_name }
377
+ end
378
+ end.compact
379
+
380
+ # Apply filter if provided
381
+ if filter
382
+ organisms = organisms.select do |org|
383
+ org[:name].downcase.include?(filter.downcase) ||
384
+ org[:code].downcase.include?(filter.downcase)
385
+ end
386
+ end
387
+
388
+ # Limit results and format
389
+ organisms = organisms.first(50)
390
+ formatted_list = organisms.map { |org| "#{org[:code]}: #{org[:name]}" }
391
+
392
+ ::MCP::Tool::Response.new([{
393
+ type: 'text',
394
+ text: "KEGG Organisms#{filter ? " (filtered by '#{filter}')" : ''} (first 50):\n#{formatted_list.join("\n")}"
395
+ }])
396
+ rescue => e
397
+ ::MCP::Tool::Response.new([{
398
+ type: 'text',
399
+ text: "Error listing organisms: #{e.message}"
400
+ }])
401
+ end
402
+
403
+ private
404
+
405
+ def kegg_list_entries(database)
406
+ uri = URI("#{KEGG_REST_BASE}/list/#{database}")
407
+ begin
408
+ uri.open.read
409
+ rescue OpenURI::HTTPError
410
+ nil
411
+ end
412
+ end
413
+ end
414
+ end
415
+ end
416
+ end
417
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BioRuby
4
+ module MCP
5
+ module Server
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
data/lib/bioruby.rb ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'bioruby/mcp/server/version'
4
+ require_relative 'bioruby/mcp/server/kegg_tools'
5
+ require_relative 'bioruby/mcp/server/core'
6
+
7
+ module BioRuby
8
+ module MCP
9
+ module Server
10
+ class Error < StandardError; end
11
+ end
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bioruby-mcp-server
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kozo Nishida
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-09-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bio
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mcp
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '13.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '13.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.21'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.21'
83
+ description: An MCP server that provides access to BioRuby KEGG functionality, allowing
84
+ AI assistants to query KEGG databases for biological pathways, compounds, and other
85
+ molecular information.
86
+ email:
87
+ - kozo2@gmail.com
88
+ executables:
89
+ - bioruby-mcp-server
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - CHANGELOG.md
94
+ - LICENSE
95
+ - README.md
96
+ - Rakefile
97
+ - bioruby-mcp-server.gemspec
98
+ - exe/bioruby-mcp-server
99
+ - lib/bioruby.rb
100
+ - lib/bioruby/mcp/server/core.rb
101
+ - lib/bioruby/mcp/server/kegg_tools.rb
102
+ - lib/bioruby/mcp/server/version.rb
103
+ homepage: https://github.com/kozo2/bioruby-mcp-server
104
+ licenses:
105
+ - BSD-2-Clause
106
+ metadata:
107
+ allowed_push_host: https://rubygems.org
108
+ homepage_uri: https://github.com/kozo2/bioruby-mcp-server
109
+ source_code_uri: https://github.com/kozo2/bioruby-mcp-server
110
+ changelog_uri: https://github.com/kozo2/bioruby-mcp-server/blob/main/CHANGELOG.md
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: 3.0.0
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubygems_version: 3.4.20
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Model Context Protocol server for BioRuby KEGG module
130
+ test_files: []