mcp-sdk 0.0.0 → 0.0.1

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: 6c9a0c7f3108df8bf65658de1f0882d8eea74acdd09ed41570ef300378c81d9f
4
- data.tar.gz: 6447dd877f10b0954faaa6b69bdd1eb4e50db0c7dfe2dea856eac23a9d820cac
3
+ metadata.gz: 5bfccbc3dbbb652bb24f57517c8b28aa4d517d3d7635df0db7611fdad12c3e63
4
+ data.tar.gz: 747f6bad3ba4342cfc531b07df8e705accebc62a8cdded803086840aecd88b1e
5
5
  SHA512:
6
- metadata.gz: 070a30c38b06d5c70e8502d64d483a6df0db426fb7defaa8897abe8f0d7bfc9a36c08f5121936c11a6dd7458e3038101ad9066c2f84262511db884d206d6e517
7
- data.tar.gz: 0c9f80d3614241faac6be2645f629cc5859b7d8920880968e608166dae2a2e2f8097bc0dc62b5d48c11eef47b49005da1fcc5d19097ba41412ed5d74abd17082
6
+ metadata.gz: 4d8c69bdd6f0ef59abd6d6db7e5d53fd5d6348c9ce82aaef590cc1438671c0f8bfd7fea129b116bbb6decde0591904cdbce211e7170b2274095f20fb34601002
7
+ data.tar.gz: 24547b56808d1cc94652d00c66d7d2671a0cf54b31ace47ed1edd346118113974e32bbddd04289c6a3a4f220f1daa0ff37e14e748e81ad037a45531f5c7530db
data/lib/mcp/mcp.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Mcp
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,37 @@
1
+ require 'json'
2
+
3
+ module Mcp
4
+ module Server
5
+ class Http
6
+ def initialize
7
+ @request_handler = RequestHandler.new
8
+ end
9
+
10
+ def call(env)
11
+ req = Rack::Request.new(env)
12
+ path = req.path_info
13
+
14
+ case path
15
+ when "/sse"
16
+ sse(env)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def sse(env)
23
+ body = env['rack.input'].read
24
+
25
+ begin
26
+ request = JSON.parse body
27
+ rescue JSON::ParserError
28
+ return [400, {"content-type" => "application/json"}, [{"message": "Bad Request"}.to_json]]
29
+ end
30
+
31
+ response = @request_handler.handle_request(request)
32
+
33
+ [200, { "content-type" => "application/json" }, [response.to_json]]
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,36 @@
1
+ module Mcp
2
+ module Server
3
+ class RequestHandler
4
+ def handle_request(request)
5
+ handler = Mcp::Server.request_handlers[request["method"]]
6
+ result = handler ? handler.call(request["params"]) : handle_default(request)
7
+
8
+ response = {"jsonrpc": "2.0", "result": result}
9
+ response["id"] = request["id"] if request["id"]
10
+
11
+ response
12
+ end
13
+
14
+ def handle_default(request)
15
+ case request["method"]
16
+ when "tools/list"
17
+ {
18
+ "tools": Mcp::Server.tools.map { |k, v| v.show },
19
+ "nextCursor": "next-page-cursor" # TODO: pagination
20
+ }
21
+ when "tools/call"
22
+ tool_name = request["params"]["name"]
23
+ tool = Mcp::Server.tools[tool_name.to_sym]
24
+ tool.call(request["params"]["arguments"])
25
+ when "resources/templates/list"
26
+ {
27
+ "resourceTemplates": Mcp::Server.resource_templates.map { |k, v| v.show }
28
+ }
29
+ else
30
+ # BUG
31
+ { "method": request["method"]}
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,40 @@
1
+ module Mcp
2
+ module Server
3
+ class ResourceTemplate
4
+ attr_reader :uri_template, :name, :description, :mime_type, :handler
5
+
6
+ def initialize(uri_template, name = nil, description = nil, mime_type = nil, &handler)
7
+ @uri_template = uri_template
8
+ @name = name
9
+ @description = description
10
+ @mime_type = mime_type
11
+ @handler = handler
12
+ end
13
+
14
+ def handler(&block)
15
+ @handler = block
16
+ end
17
+
18
+ def name(value)
19
+ @name = value
20
+ end
21
+
22
+ def description(value)
23
+ @description = value
24
+ end
25
+
26
+ def mime_type(value)
27
+ @mime_type = value
28
+ end
29
+
30
+ def show
31
+ {
32
+ "uriTemplate": uri_template,
33
+ "name": @name,
34
+ "description": @description,
35
+ "mimeType": @mime_type
36
+ }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Mcp
6
+ module Server
7
+ module Stdio
8
+ class Reader
9
+ def initialize(io = STDIN)
10
+ @io = io
11
+ io.binmode
12
+ end
13
+
14
+ def read(&block)
15
+ while (buffer = io.gets)
16
+ next unless (content_length_header = buffer.match(/Content-Length: (\d+)/i))
17
+
18
+ content_length = content_length_header[1].to_i
19
+ io.gets
20
+ request_bytes = io.read(content_length)
21
+ request = JSON.parse(request_bytes, symbolize_names: true)
22
+ return block.call(request)
23
+ end
24
+ end
25
+
26
+ def close
27
+ io.close
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :io
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Mcp
6
+ module Server
7
+ module Stdio
8
+ class Writer
9
+ attr_reader :io
10
+
11
+ def initialize(io = STDOUT)
12
+ @io = io
13
+ io.binmode
14
+ end
15
+
16
+ def write(response)
17
+ response_str = response.to_json
18
+
19
+ headers = {
20
+ "Content-Length" => response_str.bytesize
21
+ }
22
+
23
+ headers.each do |k, v|
24
+ io.print "#{k}: #{v}\r\n"
25
+ end
26
+
27
+ io.print "\r\n"
28
+
29
+ io.print response_str
30
+
31
+ io.flush
32
+ end
33
+
34
+ def close
35
+ io.close
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,62 @@
1
+ module Mcp
2
+ module Server
3
+ class Tool
4
+ attr_accessor :name, :description, :parameters
5
+
6
+ def initialize(name, description = nil, parameters = {}, &handler)
7
+ @name = name
8
+ @description = description
9
+ @parameters = parameters
10
+ @required_parameters = []
11
+ @handler = handler
12
+ end
13
+
14
+ def call(input)
15
+ # TODO: validate and handle errors
16
+ output = @handler.call(input)
17
+
18
+ {
19
+ "content": [
20
+ {
21
+ "type": "text",
22
+ "text": output
23
+ }
24
+ ]
25
+ }
26
+ end
27
+
28
+ def parameter(name, type:, description: nil, required: false)
29
+ @parameters[name.to_sym] = { "type": type }.tap do |param|
30
+ param["description"] = description if description
31
+ end
32
+ @required_parameters << name.to_sym if required
33
+ end
34
+
35
+ def description(value)
36
+ @description = value
37
+ end
38
+
39
+ def handler(&block)
40
+ @handler = block
41
+ end
42
+
43
+ def show
44
+ {
45
+ name: @name,
46
+ description: @description,
47
+ inputSchema: input_schema
48
+ }
49
+ end
50
+
51
+ private
52
+
53
+ def input_schema
54
+ {
55
+ "type": "object",
56
+ "properties": @parameters,
57
+ "required": @required_parameters
58
+ }
59
+ end
60
+ end
61
+ end
62
+ end
data/lib/mcp/server.rb ADDED
@@ -0,0 +1,55 @@
1
+ module Mcp
2
+ module Server
3
+ autoload :Configuration, "mcp/server/configuration"
4
+ autoload :Http, "mcp/server/http"
5
+
6
+ class << self
7
+ def configure(&block)
8
+ self.instance_eval(&block) if block_given?
9
+ end
10
+
11
+ def tools
12
+ @tools ||= {}
13
+ end
14
+
15
+ def resource_templates
16
+ @resource_templates ||= {}
17
+ end
18
+
19
+ def request_handlers
20
+ @request_handlers ||= {}
21
+ end
22
+
23
+ def tool(name, &block)
24
+ tool = Tool.new(name)
25
+ tool.instance_eval(&block)
26
+ tools[name] = tool
27
+ end
28
+
29
+ def resource(uri, &block)
30
+ resource_template = ResourceTemplate.new(uri)
31
+ resource_template.instance_eval(&block)
32
+ resource_templates[uri] = resource_template
33
+ end
34
+
35
+ def list_tools(&block)
36
+ request_handlers["tools/list"] = block
37
+ end
38
+
39
+ def http
40
+ Http.new
41
+ end
42
+
43
+ def stdio
44
+ reader = Stdio::Reader.new
45
+ writer = Stdio::Writer.new
46
+ request_handler = RequestHandler.new
47
+
48
+ reader.read do |request|
49
+ response = request_handler.handle_request(request)
50
+ writer.write(response)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
data/lib/mcp-sdk.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'mcp/mcp'
2
+ require 'mcp/server'
3
+ require 'mcp/server/http'
4
+ require 'mcp/server/request_handler'
5
+ require 'mcp/server/tool'
6
+ require 'mcp/server/resource_template'
7
+ require 'mcp/server/stdio/reader'
8
+ require 'mcp/server/stdio/writer'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mcp-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Donovan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-31 00:00:00.000000000 Z
11
+ date: 2025-04-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Implements the Model Context Protocol as a Ruby gem
14
14
  email: jsphdnvn@gmail.com
@@ -16,7 +16,15 @@ executables: []
16
16
  extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
- - lib/mcp_sdk.rb
19
+ - lib/mcp-sdk.rb
20
+ - lib/mcp/mcp.rb
21
+ - lib/mcp/server.rb
22
+ - lib/mcp/server/http.rb
23
+ - lib/mcp/server/request_handler.rb
24
+ - lib/mcp/server/resource_template.rb
25
+ - lib/mcp/server/stdio/reader.rb
26
+ - lib/mcp/server/stdio/writer.rb
27
+ - lib/mcp/server/tool.rb
20
28
  homepage: https://rubygems.org/gems/mcp-sdk
21
29
  licenses:
22
30
  - MIT
data/lib/mcp_sdk.rb DELETED
@@ -1,5 +0,0 @@
1
- class Mcp
2
- def self.hi
3
- puts "Hello world!"
4
- end
5
- end