Skip to content

Commit a1fd3d4

Browse files
committed
Document non-text content types, binary resources, and elicitation schema options
## Motivation and Context The SDK tiering system (SEP-1730) requires Tier 1 SDKs to document all non-experimental features with examples. A documentation coverage audit against the canonical feature list found that the README.md only showed `type: "text"` content everywhere, leaving several implemented features invisible to users: - Tool responses with image, audio, and embedded resource content (`MCP::Content::Image` / `Audio` / `EmbeddedResource`) - Prompt messages carrying image or embedded resource content - Reading binary resources via `MCP::Resource::BlobContents` - Reading resources through a registered resource template URI - Elicitation `requested_schema` flat-object restrictions and client-side validation behavior - Elicitation default values (SEP-1034) and enum values with `enumNames` (SEP-1330) All of these are existing SDK capabilities; the conformance suite already exercises them. This change documents each with prose and a runnable-style example, closing every documentation gap that does not require new code. Legacy SSE transport remains undocumented because the SDK intentionally does not implement it. ## How Has This Been Tested? Documentation-only change. Every code example was written against the current public API in lib/ (`MCP::Content`, `MCP::Resource::TextContents` / `BlobContents`, `MCP::Prompt::Message`, `create_form_elicitation`) and cross-checked with the working implementations in conformance/server.rb, which pass the conformance suite. ## Breaking Changes None. This only adds sections to README.md.
1 parent 565f4de commit a1fd3d4

1 file changed

Lines changed: 177 additions & 0 deletions

File tree

README.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,53 @@ class WeatherTool < MCP::Tool
872872
end
873873
```
874874

875+
### Tool Responses with Image, Audio, and Embedded Resources
876+
877+
Tool responses are not limited to text. The `MCP::Content` module provides `Image`, `Audio`, and `EmbeddedResource` content types,
878+
which serialize to the `image`, `audio`, and `resource` content blocks defined by the MCP spec. Image and audio data is passed as
879+
a base64-encoded string together with its MIME type:
880+
881+
```ruby
882+
class ChartTool < MCP::Tool
883+
description "Render a chart as a PNG image"
884+
885+
def self.call(server_context:)
886+
MCP::Tool::Response.new([
887+
MCP::Content::Text.new("Here is the rendered chart:").to_h,
888+
MCP::Content::Image.new(Base64.strict_encode64(render_chart_png), "image/png").to_h,
889+
])
890+
end
891+
end
892+
893+
class SpeechTool < MCP::Tool
894+
description "Synthesize speech audio"
895+
896+
def self.call(server_context:)
897+
MCP::Tool::Response.new([
898+
MCP::Content::Audio.new(Base64.strict_encode64(synthesize_wav), "audio/wav").to_h,
899+
])
900+
end
901+
end
902+
```
903+
904+
An embedded resource wraps `MCP::Resource::TextContents` or `MCP::Resource::BlobContents`, allowing a tool to return resource contents inline:
905+
906+
```ruby
907+
class ReportTool < MCP::Tool
908+
description "Return a report as an embedded resource"
909+
910+
def self.call(server_context:)
911+
contents = MCP::Resource::TextContents.new(
912+
uri: "report://monthly",
913+
mime_type: "application/json",
914+
text: { total: 42 }.to_json,
915+
)
916+
917+
MCP::Tool::Response.new([MCP::Content::EmbeddedResource.new(contents).to_h])
918+
end
919+
end
920+
```
921+
875922
### Prompts
876923

877924
MCP spec includes [Prompts](https://modelcontextprotocol.io/specification/latest/server/prompts), which enable servers to define reusable prompt templates and workflows that clients can easily surface to users and LLMs.
@@ -1010,6 +1057,49 @@ The server will handle prompt listing and execution through the MCP protocol met
10101057
- `prompts/list` - Lists all registered prompts and their schemas
10111058
- `prompts/get` - Retrieves and executes a specific prompt with arguments
10121059

1060+
### Prompts with Image and Embedded Resource Content
1061+
1062+
Prompt messages are not limited to text. The same `MCP::Content` types used in tool responses can be used as message content,
1063+
letting a prompt template include images or inline resource contents. Unlike tool responses, the content object is passed directly rather than as a hash;
1064+
`MCP::Prompt::Message` serializes it when the prompt result is returned:
1065+
1066+
```ruby
1067+
class CodeReviewPrompt < MCP::Prompt
1068+
prompt_name "code_review"
1069+
description "Review a source file with an accompanying diagram"
1070+
arguments [
1071+
MCP::Prompt::Argument.new(name: "file_uri", description: "URI of the file to review", required: true),
1072+
]
1073+
1074+
class << self
1075+
def template(args, server_context:)
1076+
MCP::Prompt::Result.new(
1077+
messages: [
1078+
MCP::Prompt::Message.new(
1079+
role: "user",
1080+
content: MCP::Content::EmbeddedResource.new(
1081+
MCP::Resource::TextContents.new(
1082+
uri: args["file_uri"],
1083+
mime_type: "text/x-ruby",
1084+
text: read_source(args["file_uri"]),
1085+
),
1086+
),
1087+
),
1088+
MCP::Prompt::Message.new(
1089+
role: "user",
1090+
content: MCP::Content::Image.new(architecture_diagram_base64, "image/png"),
1091+
),
1092+
MCP::Prompt::Message.new(
1093+
role: "user",
1094+
content: MCP::Content::Text.new("Please review the code above, using the diagram for context."),
1095+
),
1096+
],
1097+
)
1098+
end
1099+
end
1100+
end
1101+
```
1102+
10131103
### Resources
10141104

10151105
MCP spec includes [Resources](https://modelcontextprotocol.io/specification/latest/server/resources).
@@ -1126,6 +1216,34 @@ server.resources_read_handler do |params|
11261216
end
11271217
```
11281218

1219+
### Reading Binary Resources
1220+
1221+
For binary resources, respond with a base64-encoded `blob` field instead of `text`.
1222+
The `MCP::Resource::TextContents` and `MCP::Resource::BlobContents` classes build the two contents shapes defined by the spec:
1223+
1224+
```ruby
1225+
server.resources_read_handler do |params|
1226+
case params[:uri]
1227+
when "file:///logo.png"
1228+
[
1229+
MCP::Resource::BlobContents.new(
1230+
uri: params[:uri],
1231+
mime_type: "image/png",
1232+
data: Base64.strict_encode64(File.binread("logo.png")),
1233+
).to_h,
1234+
]
1235+
else
1236+
[
1237+
MCP::Resource::TextContents.new(
1238+
uri: params[:uri],
1239+
mime_type: "text/plain",
1240+
text: "Hello from example resource!",
1241+
).to_h,
1242+
]
1243+
end
1244+
end
1245+
```
1246+
11291247
### Resource Templates
11301248

11311249
Resource templates follow the same pattern. Class-based templates declare a `uri_template` and
@@ -1201,6 +1319,31 @@ server = MCP::Server.new(
12011319
)
12021320
```
12031321

1322+
Registered templates are listed through the `resources/templates/list` protocol method.
1323+
To serve reads for URIs that match a template, extract the variable parts of the URI in your `resources_read_handler`:
1324+
1325+
```ruby
1326+
resource_template = MCP::ResourceTemplate.new(
1327+
uri_template: "file:///items/{item_id}",
1328+
name: "item",
1329+
mime_type: "application/json",
1330+
)
1331+
1332+
server = MCP::Server.new(name: "my_server", resource_templates: [resource_template])
1333+
1334+
server.resources_read_handler do |params|
1335+
if (match = params[:uri].match(%r{\Afile:///items/(?<item_id>[^/]+)\z}))
1336+
[{
1337+
uri: params[:uri],
1338+
mimeType: "application/json",
1339+
text: { id: match[:item_id] }.to_json,
1340+
}]
1341+
else
1342+
raise MCP::Server::ResourceNotFoundError.new(params[:uri], params)
1343+
end
1344+
end
1345+
```
1346+
12041347
### Roots
12051348

12061349
The Model Context Protocol allows servers to request filesystem roots from clients through the `roots/list` method.
@@ -1793,6 +1936,40 @@ server.define_tool(name: "collect_contact", description: "Collect contact info")
17931936
end
17941937
```
17951938

1939+
The `requested_schema` must be a flat object schema: a top-level `type: "object"` whose `properties` are limited to
1940+
primitive types (`string`, `number`, `integer`, `boolean`). Nested objects and arrays are not allowed, which keeps
1941+
the schema simple enough for clients to render as a form. Per the MCP specification, the client validates
1942+
the user's input against this schema before returning it, so the `content` of an `accept` response matches the requested shape.
1943+
1944+
#### Default Values and Enums
1945+
1946+
Properties may declare a `default` value (SEP-1034), which clients use to pre-fill the form.
1947+
String properties may declare `enum` values, optionally with human-readable `enumNames` (SEP-1330), which clients render as a choice list:
1948+
1949+
```ruby
1950+
server.define_tool(name: "configure_deploy", description: "Configure a deployment") do |server_context:|
1951+
result = server_context.create_form_elicitation(
1952+
message: "Configure the deployment",
1953+
requested_schema: {
1954+
type: "object",
1955+
properties: {
1956+
replicas: { type: "integer", default: 3 },
1957+
verbose: { type: "boolean", default: false },
1958+
environment: {
1959+
type: "string",
1960+
enum: ["dev", "staging", "prod"],
1961+
enumNames: ["Development", "Staging", "Production"],
1962+
default: "dev",
1963+
},
1964+
},
1965+
required: ["environment"],
1966+
},
1967+
)
1968+
1969+
MCP::Tool::Response.new([{ type: "text", text: "Deploying to #{result[:content][:environment]}" }])
1970+
end
1971+
```
1972+
17961973
#### URL Mode
17971974

17981975
URL mode directs the user to an external URL for out-of-band interactions such as OAuth flows:

0 commit comments

Comments
 (0)