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 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module McpUiServer
4
+ VERSION = '0.1.0'
5
+ end
@@ -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: []