mcp_ui_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 +7 -0
- data/README.md +182 -0
- data/lib/mcp_ui_server/version.rb +5 -0
- data/lib/mcp_ui_server.rb +131 -0
- metadata +131 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e86e8b7f99554463688dffab057d1a465bd988af0a4694d96d6e83ece785e8cd
|
4
|
+
data.tar.gz: d00cd82a7d4babd4fe3260d45cfd25778dd74a59ebe3474237b392463b9f4232
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7517c8f95a006b3447296b0944b72a0c7f04dfc2e62d53edc2dde6f21c6274488b00eec121878f9e51ef7b78d7d7ac1fee149c189e8fff9833ae085d93c68c61
|
7
|
+
data.tar.gz: '047493ed74e88cf1f0ce106e498400f60cda03843f0d21c9af84d381e61732660f67859725adce12bbf1f402ac343a5169a1c27ec469bf4057b498495a7d9d3e'
|
data/README.md
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
# MCP UI Server SDK for Ruby
|
2
|
+
|
3
|
+
This is the Ruby server-side SDK for MCP UI. It provides helper methods to create UI resources that can be sent to the mcp-ui client.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'mcp_ui_server', git: 'https://github.com/idosal/mcp-ui'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle install
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
The main method is `McpUiServer.create_ui_resource`. It helps you construct a valid UIResource hash.
|
20
|
+
|
21
|
+
### URI Requirements
|
22
|
+
|
23
|
+
All UI resources must use the `ui://` URI scheme. The SDK will validate it and raise a `McpUiServer::Error` if an invalid URI is provided.
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
# Valid URI
|
27
|
+
McpUiServer.create_ui_resource(uri: 'ui://my-app/greeting', ...)
|
28
|
+
|
29
|
+
# Invalid URI - will raise McpUiServer::Error
|
30
|
+
McpUiServer.create_ui_resource(uri: 'http://example.com', ...)
|
31
|
+
```
|
32
|
+
|
33
|
+
### Content Type Support
|
34
|
+
|
35
|
+
The Ruby SDK follows Ruby conventions by using snake_case symbols for content types:
|
36
|
+
|
37
|
+
- `:raw_html` - Raw HTML content
|
38
|
+
- `:external_url` - External URL content
|
39
|
+
- `:remote_dom` - Remote DOM content
|
40
|
+
|
41
|
+
The SDK maintains protocol consistency by internally mapping these to the camelCase format used in the MCP-UI specification.
|
42
|
+
|
43
|
+
### Creating a `raw_html` resource
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
require 'mcp_ui_server'
|
47
|
+
|
48
|
+
resource = McpUiServer.create_ui_resource(
|
49
|
+
uri: 'ui://my-app/greeting',
|
50
|
+
content: {
|
51
|
+
type: :raw_html,
|
52
|
+
htmlString: '<h1>Hello, World!</h1>'
|
53
|
+
}
|
54
|
+
)
|
55
|
+
|
56
|
+
# The resulting resource hash can be serialized to JSON and sent in your API response.
|
57
|
+
# {
|
58
|
+
# "type": "resource",
|
59
|
+
# "resource": {
|
60
|
+
# "uri": "ui://my-app/greeting",
|
61
|
+
# "mimeType": "text/html",
|
62
|
+
# "text": "<h1>Hello, World!</h1>"
|
63
|
+
# }
|
64
|
+
# }
|
65
|
+
```
|
66
|
+
|
67
|
+
### Creating an `external_url` resource
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
require 'mcp_ui_server'
|
71
|
+
|
72
|
+
resource = McpUiServer.create_ui_resource(
|
73
|
+
uri: 'ui://my-app/external-page',
|
74
|
+
content: {
|
75
|
+
type: :external_url,
|
76
|
+
iframeUrl: 'https://example.com'
|
77
|
+
}
|
78
|
+
)
|
79
|
+
|
80
|
+
# Results in:
|
81
|
+
# {
|
82
|
+
# "type": "resource",
|
83
|
+
# "resource": {
|
84
|
+
# "uri": "ui://my-app/external-page",
|
85
|
+
# "mimeType": "text/uri-list",
|
86
|
+
# "text": "https://example.com"
|
87
|
+
# }
|
88
|
+
# }
|
89
|
+
```
|
90
|
+
|
91
|
+
### Creating a `remote_dom` resource
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
require 'mcp_ui_server'
|
95
|
+
|
96
|
+
script = "..." # Your javascript bundle for the remote-dom
|
97
|
+
|
98
|
+
resource = McpUiServer.create_ui_resource(
|
99
|
+
uri: 'ui://my-app/complex-view',
|
100
|
+
content: {
|
101
|
+
type: :remote_dom,
|
102
|
+
script: script,
|
103
|
+
framework: :react # or :webcomponents
|
104
|
+
}
|
105
|
+
)
|
106
|
+
```
|
107
|
+
|
108
|
+
### Using `blob` encoding
|
109
|
+
|
110
|
+
For binary content or to avoid encoding issues, you can use `blob` encoding, which will Base64 encode the content.
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
resource = McpUiServer.create_ui_resource(
|
114
|
+
uri: 'ui://my-app/greeting',
|
115
|
+
content: {
|
116
|
+
type: :raw_html,
|
117
|
+
htmlString: '<h1>Hello, World!</h1>'
|
118
|
+
},
|
119
|
+
encoding: :blob
|
120
|
+
)
|
121
|
+
```
|
122
|
+
|
123
|
+
## Error Handling
|
124
|
+
|
125
|
+
The SDK uses a custom `McpUiServer::Error` class for all SDK-specific errors. This makes it easy to rescue and handle SDK errors separately from other application errors.
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
begin
|
129
|
+
resource = McpUiServer.create_ui_resource(
|
130
|
+
uri: 'http://invalid-scheme',
|
131
|
+
content: { type: :raw_html, htmlString: '<h1>Hello</h1>' }
|
132
|
+
)
|
133
|
+
rescue McpUiServer::Error => e
|
134
|
+
puts "MCP UI SDK Error: #{e.message}"
|
135
|
+
# Handle SDK-specific errors
|
136
|
+
rescue => e
|
137
|
+
puts "Other error: #{e.message}"
|
138
|
+
# Handle other errors
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
Common error scenarios:
|
143
|
+
- Invalid URI scheme (not starting with `ui://`)
|
144
|
+
- Unknown content type (only `:raw_html`, `:external_url`, `:remote_dom` are supported)
|
145
|
+
- Missing required content keys (e.g., `htmlString` for `raw_html`)
|
146
|
+
- Unknown encoding type
|
147
|
+
|
148
|
+
## Constants
|
149
|
+
|
150
|
+
The SDK provides constants for MIME types and content types:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
# MIME type constants
|
154
|
+
McpUiServer::MIME_TYPE_HTML # 'text/html'
|
155
|
+
McpUiServer::MIME_TYPE_URI_LIST # 'text/uri-list'
|
156
|
+
McpUiServer::MIME_TYPE_REMOTE_DOM # 'application/vnd.mcp-ui.remote-dom; framework=%s'
|
157
|
+
|
158
|
+
# Content type constants (Ruby snake_case)
|
159
|
+
McpUiServer::CONTENT_TYPE_RAW_HTML # :raw_html
|
160
|
+
McpUiServer::CONTENT_TYPE_EXTERNAL_URL # :external_url
|
161
|
+
McpUiServer::CONTENT_TYPE_REMOTE_DOM # :remote_dom
|
162
|
+
|
163
|
+
# Protocol mapping (for internal use)
|
164
|
+
McpUiServer::PROTOCOL_CONTENT_TYPES # { raw_html: 'rawHtml', ... }
|
165
|
+
|
166
|
+
# URI scheme constant
|
167
|
+
McpUiServer::UI_URI_SCHEME # 'ui://'
|
168
|
+
```
|
169
|
+
|
170
|
+
## Development
|
171
|
+
|
172
|
+
After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bundle exec rubocop` to check for code style.
|
173
|
+
|
174
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
175
|
+
|
176
|
+
## Contributing
|
177
|
+
|
178
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/idosal/mcp-ui.
|
179
|
+
|
180
|
+
## License
|
181
|
+
|
182
|
+
The gem is available as open source under the terms of the [Apache-2.0 License](https://www.apache.org/licenses/LICENSE-2.0).
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'mcp_ui_server/version'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
# The McpUiServer module provides helper methods for creating UI resources
|
7
|
+
# compatible with the Model Context Protocol UI (mcp-ui) client.
|
8
|
+
module McpUiServer
|
9
|
+
class Error < StandardError; end
|
10
|
+
|
11
|
+
# MIME type constants
|
12
|
+
MIME_TYPE_HTML = 'text/html'
|
13
|
+
MIME_TYPE_URI_LIST = 'text/uri-list'
|
14
|
+
MIME_TYPE_REMOTE_DOM = 'application/vnd.mcp-ui.remote-dom+javascript; framework=%s'
|
15
|
+
|
16
|
+
# Content type constants
|
17
|
+
CONTENT_TYPE_RAW_HTML = :raw_html
|
18
|
+
CONTENT_TYPE_EXTERNAL_URL = :external_url
|
19
|
+
CONTENT_TYPE_REMOTE_DOM = :remote_dom
|
20
|
+
|
21
|
+
# Protocol mapping (snake_case to camelCase for protocol consistency)
|
22
|
+
PROTOCOL_CONTENT_TYPES = {
|
23
|
+
raw_html: 'rawHtml',
|
24
|
+
external_url: 'externalUrl',
|
25
|
+
remote_dom: 'remoteDom'
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
# Required content keys for each content type
|
29
|
+
REQUIRED_CONTENT_KEYS = {
|
30
|
+
CONTENT_TYPE_RAW_HTML => :htmlString,
|
31
|
+
CONTENT_TYPE_EXTERNAL_URL => :iframeUrl,
|
32
|
+
CONTENT_TYPE_REMOTE_DOM => :script
|
33
|
+
}.freeze
|
34
|
+
|
35
|
+
# URI scheme constant
|
36
|
+
UI_URI_SCHEME = 'ui://'
|
37
|
+
|
38
|
+
# Creates a UIResource hash structure for an MCP response.
|
39
|
+
# This structure can then be serialized to JSON by your web framework.
|
40
|
+
#
|
41
|
+
# @param uri [String] The unique identifier for the resource (e.g., 'ui://greeting/1').
|
42
|
+
# @param content [Hash] A hash describing the UI content.
|
43
|
+
# - :type [Symbol] The type of content. One of :raw_html, :external_url, or :remote_dom.
|
44
|
+
# - :htmlString [String] The raw HTML content (required if type is :raw_html).
|
45
|
+
# - :iframeUrl [String] The URL for an external page (required if type is :external_url).
|
46
|
+
# - :script [String] The remote-dom script (required if type is :remote_dom).
|
47
|
+
# - :framework [Symbol] The remote-dom framework, e.g., :react or :webcomponents (required, for :remote_dom).
|
48
|
+
# @param encoding [Symbol] The encoding method. :text for plain string, :blob for base64 encoded.
|
49
|
+
#
|
50
|
+
# @return [Hash] A UIResource hash ready to be included in an MCP response.
|
51
|
+
#
|
52
|
+
# @raise [McpUiServer::Error] if URI scheme is invalid, content type is unknown,
|
53
|
+
# encoding type is unknown, or required content keys are missing.
|
54
|
+
def self.create_ui_resource(uri:, content:, encoding: :text)
|
55
|
+
validate_uri_scheme(uri)
|
56
|
+
|
57
|
+
resource = { uri: uri }
|
58
|
+
|
59
|
+
content_value = process_content(content, resource)
|
60
|
+
process_encoding(encoding, resource, content_value)
|
61
|
+
|
62
|
+
{
|
63
|
+
type: 'resource',
|
64
|
+
resource: resource
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
# private
|
69
|
+
|
70
|
+
def self.validate_uri_scheme(uri)
|
71
|
+
raise Error, "URI must start with '#{UI_URI_SCHEME}' but got: #{uri}" unless uri.start_with?(UI_URI_SCHEME)
|
72
|
+
end
|
73
|
+
private_class_method :validate_uri_scheme
|
74
|
+
|
75
|
+
def self.validate_content_type(content_type)
|
76
|
+
return if PROTOCOL_CONTENT_TYPES.key?(content_type)
|
77
|
+
|
78
|
+
supported_types = PROTOCOL_CONTENT_TYPES.keys.join(', ')
|
79
|
+
raise Error, "Unknown content type: #{content_type}. Supported types: #{supported_types}"
|
80
|
+
end
|
81
|
+
private_class_method :validate_content_type
|
82
|
+
|
83
|
+
def self.process_content(content, resource)
|
84
|
+
content_type = content.fetch(:type)
|
85
|
+
validate_content_type(content_type)
|
86
|
+
|
87
|
+
case content_type
|
88
|
+
when CONTENT_TYPE_RAW_HTML
|
89
|
+
process_raw_html_content(content, resource)
|
90
|
+
when CONTENT_TYPE_EXTERNAL_URL
|
91
|
+
process_external_url_content(content, resource)
|
92
|
+
when CONTENT_TYPE_REMOTE_DOM
|
93
|
+
process_remote_dom_content(content, resource)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
private_class_method :process_content
|
97
|
+
|
98
|
+
def self.process_raw_html_content(content, resource)
|
99
|
+
resource[:mimeType] = MIME_TYPE_HTML
|
100
|
+
required_key = REQUIRED_CONTENT_KEYS[CONTENT_TYPE_RAW_HTML]
|
101
|
+
content.fetch(required_key) { raise Error, "Missing required key :#{required_key} for raw_html content" }
|
102
|
+
end
|
103
|
+
private_class_method :process_raw_html_content
|
104
|
+
|
105
|
+
def self.process_external_url_content(content, resource)
|
106
|
+
resource[:mimeType] = MIME_TYPE_URI_LIST
|
107
|
+
required_key = REQUIRED_CONTENT_KEYS[CONTENT_TYPE_EXTERNAL_URL]
|
108
|
+
content.fetch(required_key) { raise Error, "Missing required key :#{required_key} for external_url content" }
|
109
|
+
end
|
110
|
+
private_class_method :process_external_url_content
|
111
|
+
|
112
|
+
def self.process_remote_dom_content(content, resource)
|
113
|
+
framework = content.fetch(:framework) { raise Error, 'Missing required key :framework for remote_dom content' }
|
114
|
+
resource[:mimeType] = MIME_TYPE_REMOTE_DOM % framework
|
115
|
+
required_key = REQUIRED_CONTENT_KEYS[CONTENT_TYPE_REMOTE_DOM]
|
116
|
+
content.fetch(required_key) { raise Error, "Missing required key :#{required_key} for remote_dom content" }
|
117
|
+
end
|
118
|
+
private_class_method :process_remote_dom_content
|
119
|
+
|
120
|
+
def self.process_encoding(encoding, resource, content_value)
|
121
|
+
case encoding
|
122
|
+
when :text
|
123
|
+
resource[:text] = content_value
|
124
|
+
when :blob
|
125
|
+
resource[:blob] = Base64.strict_encode64(content_value)
|
126
|
+
else
|
127
|
+
raise Error, "Unknown encoding type: #{encoding}. Supported types: :text, :blob"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
private_class_method :process_encoding
|
131
|
+
end
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mcp_ui_server
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ido Salomon
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-07-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop-rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop-rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: A Ruby server SDK for MCP UI.
|
98
|
+
email:
|
99
|
+
- idosalomon@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- README.md
|
105
|
+
- lib/mcp_ui_server.rb
|
106
|
+
- lib/mcp_ui_server/version.rb
|
107
|
+
homepage: https://github.com/idosal/mcp-ui
|
108
|
+
licenses:
|
109
|
+
- Apache-2.0
|
110
|
+
metadata:
|
111
|
+
rubygems_mfa_required: 'true'
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: 2.6.0
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubygems_version: 3.4.19
|
128
|
+
signing_key:
|
129
|
+
specification_version: 4
|
130
|
+
summary: Ruby Server SDK for MCP UI
|
131
|
+
test_files: []
|