foobara-mcp-connector 0.0.4 → 0.0.5

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: 9887a812a0026b235492eaf1c5c4af194bc11b784211baaeb3e742a5c4377aca
4
- data.tar.gz: 242f3614112c8a58613e7773421356a888a7214a2e235341efdfdc871425e23a
3
+ metadata.gz: 907ef0b728ab96d109ef5db1a5938a322537dc97b442d36386fcdbb8826fbcee
4
+ data.tar.gz: afef5e6b376f2f50cc630b535d0edc0f110d4421c5540ce33322b55e4ffc5c89
5
5
  SHA512:
6
- metadata.gz: 86a3e8605855df453482e0ac38b7bdf329b6afcaab574bea1061e8931cec9051cdcc032cf53a8c062d1422b83251ba5a32d1f71f0448bbf4df5f2cabf9f0685e
7
- data.tar.gz: df1d38a223b69e48f009881b3e4b4ecca9768c6b6a21030326e4d8310e4f23ec1c3c77e8725632bdbf4fb7dce65bde45731a656c294d1be22a3421d6132da5e8
6
+ metadata.gz: a43ce9d80d6f3540b4cb62e52c616758d1fb322d43d418405e90bc2226ac30406663659171c2f87e43483d4d20118569777cf3b0ff04cf3f5d29ba278917fc6a
7
+ data.tar.gz: c8a6234d60d32123cd97cc7fd6abfe8f98cf183b43e627b079094806f84ff4a159772da5002d0f895dd88b56e32d12b88103546e1cd3de165c8441b408ee05d0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## [0.0.5] - 2025-05-03
2
+
3
+ - Handle changes to CommandConnector interface
4
+
1
5
  ## [0.0.4] - 2025-04-14
2
6
 
3
7
  - Do not log requests/responses/errors by default
data/README.md CHANGED
@@ -6,6 +6,8 @@ Exposes Foobara commands according to the Model Context Protocol (MCP) specifica
6
6
  * [Foobara::McpConnector](#foobaramcpconnector)
7
7
  * [Installation](#installation)
8
8
  * [Usage](#usage)
9
+ * [Code demo video!](#code-demo-video)
10
+ * [Code examples](#code-examples)
9
11
  * [Super basic example](#super-basic-example)
10
12
  * [An example with entities](#an-example-with-entities)
11
13
  * [A destructive example](#a-destructive-example)
@@ -21,6 +23,12 @@ Typical stuff: add `gem "foobara-mcp-connector` to your Gemfile or .gemspec file
21
23
 
22
24
  ## Usage
23
25
 
26
+ ### Code demo video!
27
+
28
+ You can watch a code demo here: https://youtu.be/_w3ZHdiJEGU
29
+
30
+ ### Code examples
31
+
24
32
  You can find examples in `examples/`
25
33
 
26
34
  ### Super basic example
@@ -2,12 +2,29 @@ module Foobara
2
2
  class McpConnector < CommandConnector
3
3
  # Isolating jsonrpc specific logic to this abstract class
4
4
  class JsonrpcRequest < CommandConnector::Request
5
- class InvalidJsonrpcVersionError < StandardError; end
6
- class InvalidJsonrpcMethodError < StandardError; end
7
- class InvalidJsonrpcParamsError < StandardError; end
8
- class InvalidJsonrpcRequestStructureError < StandardError; end
9
- class EmptyBatchError < StandardError; end
10
- class InvalidJsonError < StandardError; end
5
+ class InvalidJsonrpcVersionError < Foobara::Error
6
+ context({})
7
+ end
8
+
9
+ class InvalidJsonrpcMethodError < Foobara::Error
10
+ context({})
11
+ end
12
+
13
+ class InvalidJsonrpcParamsError < Foobara::Error
14
+ context({})
15
+ end
16
+
17
+ class InvalidJsonrpcRequestStructureError < Foobara::Error
18
+ context({})
19
+ end
20
+
21
+ class EmptyBatchError < Foobara::Error
22
+ context({})
23
+ end
24
+
25
+ class InvalidJsonError < Foobara::Error
26
+ context({})
27
+ end
11
28
 
12
29
  # TODO: push response into base class?
13
30
  attr_accessor :raw_request_json, :parsed_request_body, :request_id, :batch, :is_batch_child
@@ -45,7 +62,9 @@ module Foobara
45
62
  begin
46
63
  JSON.parse(raw_request_json)
47
64
  rescue => e
48
- self.error = InvalidJsonError.new("Could not parse request: #{e.message}")
65
+ self.error = InvalidJsonError.new(
66
+ message: "Could not parse request: #{e.message}"
67
+ )
49
68
  error.set_backtrace(caller)
50
69
  nil
51
70
  end
@@ -62,7 +81,9 @@ module Foobara
62
81
 
63
82
  def verify_jsonrpc_version
64
83
  if parsed_request_body["jsonrpc"] != "2.0"
65
- self.error = InvalidJsonrpcVersionError.new("Unsupported jsonrpc version: #{parsed_request_body["jsonrpc"]}")
84
+ self.error = InvalidJsonrpcVersionError.new(
85
+ message: "Unsupported jsonrpc version: #{parsed_request_body["jsonrpc"]}"
86
+ )
66
87
  error.set_backtrace(caller)
67
88
  end
68
89
  end
@@ -93,7 +114,9 @@ module Foobara
93
114
 
94
115
  def validate_batch_not_empty
95
116
  if batch? && batch.empty?
96
- self.error = EmptyBatchError.new("An empty array/batch is not allowed")
117
+ self.error = EmptyBatchError.new(
118
+ message: "An empty array/batch is not allowed"
119
+ )
97
120
  error.set_backtrace(caller)
98
121
  end
99
122
  end
@@ -101,7 +124,7 @@ module Foobara
101
124
  def validate_request_structure
102
125
  unless parsed_request_body.is_a?(::Hash)
103
126
  self.error = InvalidJsonrpcRequestStructureError.new(
104
- "Invalid jsonrpc request structure. Expected a hash but got a #{parsed_request_body.class}"
127
+ message: "Invalid jsonrpc request structure. Expected a hash but got a #{parsed_request_body.class}"
105
128
  )
106
129
  error.set_backtrace(caller)
107
130
  end
data/src/mcp_connector.rb CHANGED
@@ -40,33 +40,41 @@ module Foobara
40
40
  StdioRunner.new(self).run(io_in: io_in, io_out: io_out, io_err: io_err)
41
41
  end
42
42
 
43
- def request_to_command(request)
44
- action = request.action
45
-
43
+ def request_to_command_class(request)
46
44
  return if request.error?
47
45
 
48
- case action
49
- when "initialize"
50
- command_class = find_builtin_command_class("Initialize")
51
- when "notifications/initialized", "notifications/cancelled", "notifications/progress",
52
- "notifications/roots/list_changed"
53
- command_class = find_builtin_command_class("Noop")
54
- else
55
- return super
56
- end
46
+ builtin_command_class_name = case request.action
47
+ when "initialize"
48
+ "Initialize"
49
+ when "notifications/initialized", "notifications/cancelled",
50
+ "notifications/progress", "notifications/roots/list_changed"
51
+ "Noop"
52
+ else
53
+ return super
54
+ end
57
55
 
58
- full_command_name = command_class.full_command_name
59
- inputs = (request.params || {}).merge(request:)
56
+ command_class = find_builtin_command_class(builtin_command_class_name)
60
57
 
61
- transformed_command_class = transformed_command_from_name(full_command_name) ||
62
- transform_command_class(command_class)
58
+ full_command_name = command_class.full_command_name
63
59
 
64
- transformed_command_class.new(inputs)
60
+ transformed_command_from_name(full_command_name) || transform_command_class(command_class)
65
61
  rescue CommandConnector::NoCommandFoundError => e
66
62
  request.error = e
67
63
  nil
68
64
  end
69
65
 
66
+ def request_to_command_inputs(request)
67
+ return if request.error?
68
+
69
+ case request.action
70
+ when "initialize", "notifications/initialized", "notifications/cancelled", "notifications/progress",
71
+ "notifications/roots/list_changed"
72
+ (request.params || {}).merge(request:)
73
+ else
74
+ super
75
+ end
76
+ end
77
+
70
78
  # TODO: figure out how to support multiple sessions
71
79
  def session_created(session)
72
80
  self.current_session = session
data/src/request.rb CHANGED
@@ -3,8 +3,15 @@ require_relative "jsonrpc_request"
3
3
  module Foobara
4
4
  class McpConnector < CommandConnector
5
5
  class Request < JsonrpcRequest
6
- class FoobaraCommandsDoNotAcceptArraysError < StandardError; end
7
- class MethodNotYetSupportedError < StandardError; end
6
+ class FoobaraCommandsDoNotAcceptArraysError < Foobara::Error
7
+ context({})
8
+ end
9
+
10
+ class MethodNotYetSupportedError < Foobara::Error
11
+ context({})
12
+ end
13
+
14
+ attr_accessor :action_loaded
8
15
 
9
16
  def full_command_name
10
17
  return if error || batch?
@@ -24,11 +31,11 @@ module Foobara
24
31
  unless inputs.is_a?(::Hash)
25
32
  self.error = if inputs.is_a?(::Array)
26
33
  FoobaraCommandsDoNotAcceptArraysError.new(
27
- "Foobara commands do not accept arrays as inputs"
34
+ message: "Foobara commands do not accept arrays as inputs"
28
35
  )
29
36
  else
30
37
  InvalidJsonrpcParamsError.new(
31
- "Invalid MCP arguments structure. Expected a hash got a #{inputs.class}"
38
+ message: "Invalid MCP arguments structure. Expected a hash got a #{inputs.class}"
32
39
  )
33
40
  end
34
41
 
@@ -46,7 +53,14 @@ module Foobara
46
53
  !error && super
47
54
  end
48
55
 
56
+ def error?
57
+ action unless action_loaded
58
+ super
59
+ end
60
+
49
61
  def action
62
+ self.action_loaded = true
63
+
50
64
  case method
51
65
  when "tools/call"
52
66
  "run"
@@ -58,9 +72,9 @@ module Foobara
58
72
  method
59
73
  when "completion/complete", "logging/setLevel", "prompts/get", "prompts/list",
60
74
  "resources/list", "resources/read", "resources/subscribe", "resources/unsubscribe"
61
- raise MethodNotYetSupportedError, "#{method} not yet supported!"
75
+ raise MethodNotYetSupportedError.new(message: "#{method} not yet supported!")
62
76
  else
63
- self.error = InvalidJsonrpcMethodError.new("Unknown method: #{method}")
77
+ self.error = InvalidJsonrpcMethodError.new(message: "Unknown method: #{method}")
64
78
  error.set_backtrace(caller)
65
79
  nil
66
80
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foobara-mcp-connector
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miles Georgi
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
10
+ date: 2025-05-03 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: foobara
@@ -78,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
78
  - !ruby/object:Gem::Version
79
79
  version: '0'
80
80
  requirements: []
81
- rubygems_version: 3.6.7
81
+ rubygems_version: 3.6.2
82
82
  specification_version: 4
83
83
  summary: Gives an easy way to expose your Foobara commands to tools like Claude Code
84
84
  via the Model Context Protocol (MCP)