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 +4 -4
- data/lib/mcp/mcp.rb +3 -0
- data/lib/mcp/server/http.rb +37 -0
- data/lib/mcp/server/request_handler.rb +36 -0
- data/lib/mcp/server/resource_template.rb +40 -0
- data/lib/mcp/server/stdio/reader.rb +36 -0
- data/lib/mcp/server/stdio/writer.rb +40 -0
- data/lib/mcp/server/tool.rb +62 -0
- data/lib/mcp/server.rb +55 -0
- data/lib/mcp-sdk.rb +8 -0
- metadata +11 -3
- data/lib/mcp_sdk.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5bfccbc3dbbb652bb24f57517c8b28aa4d517d3d7635df0db7611fdad12c3e63
|
4
|
+
data.tar.gz: 747f6bad3ba4342cfc531b07df8e705accebc62a8cdded803086840aecd88b1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d8c69bdd6f0ef59abd6d6db7e5d53fd5d6348c9ce82aaef590cc1438671c0f8bfd7fea129b116bbb6decde0591904cdbce211e7170b2274095f20fb34601002
|
7
|
+
data.tar.gz: 24547b56808d1cc94652d00c66d7d2671a0cf54b31ace47ed1edd346118113974e32bbddd04289c6a3a4f220f1daa0ff37e14e748e81ad037a45531f5c7530db
|
data/lib/mcp/mcp.rb
ADDED
@@ -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
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.
|
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-
|
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/
|
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
|