MCP Ruby SDK - Server Development Guide
Build Model Context Protocol servers in Ruby using the official mcp gem (maintained by Anthropic and Shopify).
Design Philosophy
Information Provider, Not Analyzer
MCP servers provide structured data; LLMs do the reasoning. Return comprehensive frameworks and raw information—let the client perform analysis and context-dependent decisions.
"The MCP server's job is to be the world's best research assistant, not a competing analyst." — Matt Adams
Context Preservation
Agents have limited context windows. Every byte returned that wasn't requested is a byte that could have held useful context. Treat context preservation as a first-class design constraint.
Principles:
- Never return data that wasn't explicitly requested
- Mutations are quiet—return confirmations, not data dumps
- Explicit over implicit—associations only when asked
- Filter large datasets before returning (10,000 rows → 5 relevant rows)
Domain-Aligned Vocabulary
Tools should speak the language of your domain, not database/CRUD terminology. Agents are collaborators in your domain process, not database clients.
Example: A visual novel asset server uses create_image, make_sprite, place_character, explore_variations, compare_images—not generate, remove_background, composite, batch_generate, get_diff.
Tool Budget Management
Too many tools overwhelm agents and increase costs. Design toolsets around clear use cases, not API endpoint mirrors.
- Group related functionality intelligently
- Use lazy loading for large tool sets (150K tokens → 2K via on-demand discovery)
- Tool names ≤64 characters, descriptions narrow and unambiguous
Security: The Lethal Trifecta
Three capabilities that, when combined, create vulnerabilities (Simon Willison):
- Access to private data
- Exposure to untrusted content
- External communication capabilities
Required: Explicit user consent before tool invocation, clear UI showing exposed tools, alerts when tool descriptions change.
Domain Components
| Component |
Purpose |
Reference |
| Tools |
Define callable functions with input/output schemas |
references/tools.md |
| Prompts |
Template-based message generators |
references/prompts.md |
| Resources |
Static and dynamic file/data registration |
references/resources.md |
| Server |
Core server initialization and configuration |
references/server.md |
| Transport |
STDIO and HTTP transport options |
references/transport.md |
| Gotchas |
Tricky behaviors and error handling |
references/gotchas.md |
Key Concepts
| Concept |
Purpose |
MCP::Tool |
Base class for defining callable tools |
MCP::Prompt |
Base class for prompt templates |
MCP::Resource |
Static resource registration |
MCP::ResourceTemplate |
Dynamic URI-based resources |
server_context |
Request-scoped data passed to handlers |
MCP::Tool::Response |
Structured tool return value |
Tool Definition Patterns
| Pattern |
Use Case |
Class-based (< MCP::Tool) |
Reusable tools with complex logic |
Block-based (MCP::Tool.define) |
Inline, simple tools |
Dynamic (server.define_tool) |
Runtime tool registration |
Transport Decision Tree
What environment?
├── CLI tool / Local server
│ └── Use STDIO transport
└── Web server / Production
└── Need sessions and notifications?
├── YES → Use Streamable HTTP (stateful)
└── NO → Use Streamable HTTP (stateless)
Quick Comparison
| Transport |
Sessions |
Notifications |
Use For |
| STDIO |
N/A |
Yes |
CLI tools, local dev |
| HTTP (stateful) |
Yes |
Yes |
Web apps, long-lived connections |
| HTTP (stateless) |
No |
No |
Simple request/response APIs |
Protocol Version Features
| Feature |
Minimum Version |
description |
2025-11-25 |
instructions |
2025-03-26 |
annotations |
2025-03-26 |
output_schema |
2025-03-26 |
Best Practices
Do
- Use
tool_name for namespaced classes to avoid conflicts
- Use
additionalProperties: false for strict schema validation
- Use mutex for shared state in HTTP transport (thread safety)
- Return error responses for business errors (
Response.new([...], error: true))
- Check protocol version before using newer features
- Use
server_context for request-scoped data (user_id, env)
Don't
- Don't use
$ref in schemas (raises ArgumentError, inline only)
- Don't assume extra args are rejected (
additionalProperties defaults to allowing extras)
- Don't use
rpc. prefix (reserved for protocol methods)
- Don't send notifications in stateless mode (raises RuntimeError)
- Don't rely on validation order (required args checked before JSON Schema)
Anti-Patterns Quick List
| Anti-Pattern |
Solution |
Missing additionalProperties: false |
Add to schema for strict validation |
Using $ref in schemas |
Inline all definitions |
| Notifications in stateless mode |
Use stateful transport or skip notifications |
| Hardcoded server_context |
Pass dynamically based on request |
| Ignoring protocol version |
Check version before using gated features |
| Blocking in tool handlers |
Use async patterns for long operations |
Key Points
- Validation is multi-layered - Required args checked first, then JSON Schema validation
- Notifications are fire-and-forget - Errors reported but don't propagate
- Protocol version matters - Features are gated by version
- Server context is opt-in - Detected from method signature (must include
server_context: parameter)
- Schemas are immutable - Validated at class load time, not runtime
Additional Resources
Reference Files
For detailed DSL syntax by domain:
references/tools.md - Tool definition, responses, schemas, annotations
references/prompts.md - Prompt definition, arguments, content types
references/resources.md - Resource registration, templates, read handlers
references/server.md - Server initialization, configuration, custom methods
references/transport.md - Transport config, protocol methods, sessions
references/gotchas.md - Tricky behaviors, error handling, edge cases
Example Files
Working examples in examples/:
examples/stdio_server.rb - Complete STDIO server with tools, prompts, resources
examples/http_server.rb - HTTP server with Rack and logging
examples/rails_integration.rb - Rails controller, routes, and initializer
examples/file_manager_tool.rb - Sandboxed file operations with security patterns
examples/dynamic_tools.rb - Runtime tool registration with notifications
examples/http_client.rb - HTTP client connecting to MCP server
examples/streaming_client.rb - SSE streaming client for real-time notifications
External Links
1---2name: mcp-server-ruby3description: This skill should be used when the user asks to "create an MCP server", "build MCP tools", "define MCP prompts", "register MCP resources", "implement Model Context Protocol", or mentions the mcp gem, MCP::Server, MCP::Tool, JSON-RPC transport, stdio transport, or streamable HTTP transport. Should also be used when editing MCP server files, working with tool/prompt/resource definitions, or discussing LLM tool integrations in Ruby.4---56# MCP Ruby SDK - Server Development Guide78Build Model Context Protocol servers in Ruby using the official `mcp` gem (maintained by Anthropic and Shopify).910## Design Philosophy1112### Information Provider, Not Analyzer1314MCP servers provide structured data; LLMs do the reasoning. Return comprehensive frameworks and raw information—let the client perform analysis and context-dependent decisions.1516> "The MCP server's job is to be the world's best research assistant, not a competing analyst." — Matt Adams1718### Context Preservation1920Agents have limited context windows. Every byte returned that wasn't requested is a byte that could have held useful context. Treat context preservation as a first-class design constraint.2122**Principles:**23- Never return data that wasn't explicitly requested24- Mutations are quiet—return confirmations, not data dumps25- Explicit over implicit—associations only when asked26- Filter large datasets before returning (10,000 rows → 5 relevant rows)2728### Domain-Aligned Vocabulary2930Tools should speak the language of your domain, not database/CRUD terminology. Agents are collaborators in your domain process, not database clients.3132**Example:** A visual novel asset server uses `create_image`, `make_sprite`, `place_character`, `explore_variations`, `compare_images`—not `generate`, `remove_background`, `composite`, `batch_generate`, `get_diff`.3334### Tool Budget Management3536Too many tools overwhelm agents and increase costs. Design toolsets around clear use cases, not API endpoint mirrors.3738- Group related functionality intelligently39- Use lazy loading for large tool sets (150K tokens → 2K via on-demand discovery)40- Tool names ≤64 characters, descriptions narrow and unambiguous4142### Security: The Lethal Trifecta4344Three capabilities that, when combined, create vulnerabilities (Simon Willison):451. Access to private data462. Exposure to untrusted content473. External communication capabilities4849**Required:** Explicit user consent before tool invocation, clear UI showing exposed tools, alerts when tool descriptions change.5051## Domain Components5253| Component | Purpose | Reference |54|-----------|---------|-----------|55| **Tools** | Define callable functions with input/output schemas | [`references/tools.md`](references/tools.md) |56| **Prompts** | Template-based message generators | [`references/prompts.md`](references/prompts.md) |57| **Resources** | Static and dynamic file/data registration | [`references/resources.md`](references/resources.md) |58| **Server** | Core server initialization and configuration | [`references/server.md`](references/server.md) |59| **Transport** | STDIO and HTTP transport options | [`references/transport.md`](references/transport.md) |60| **Gotchas** | Tricky behaviors and error handling | [`references/gotchas.md`](references/gotchas.md) |6162## Key Concepts6364| Concept | Purpose |65|---------|---------|66| `MCP::Tool` | Base class for defining callable tools |67| `MCP::Prompt` | Base class for prompt templates |68| `MCP::Resource` | Static resource registration |69| `MCP::ResourceTemplate` | Dynamic URI-based resources |70| `server_context` | Request-scoped data passed to handlers |71| `MCP::Tool::Response` | Structured tool return value |7273## Tool Definition Patterns7475| Pattern | Use Case |76|---------|----------|77| Class-based (`< MCP::Tool`) | Reusable tools with complex logic |78| Block-based (`MCP::Tool.define`) | Inline, simple tools |79| Dynamic (`server.define_tool`) | Runtime tool registration |8081## Transport Decision Tree8283```84What environment?85├── CLI tool / Local server86│ └── Use STDIO transport87└── Web server / Production88 └── Need sessions and notifications?89 ├── YES → Use Streamable HTTP (stateful)90 └── NO → Use Streamable HTTP (stateless)91```9293### Quick Comparison9495| Transport | Sessions | Notifications | Use For |96|-----------|----------|---------------|---------|97| STDIO | N/A | Yes | CLI tools, local dev |98| HTTP (stateful) | Yes | Yes | Web apps, long-lived connections |99| HTTP (stateless) | No | No | Simple request/response APIs |100101## Protocol Version Features102103| Feature | Minimum Version |104|---------|-----------------|105| `description` | 2025-11-25 |106| `instructions` | 2025-03-26 |107| `annotations` | 2025-03-26 |108| `output_schema` | 2025-03-26 |109110## Best Practices111112### Do113114- Use `tool_name` for namespaced classes to avoid conflicts115- Use `additionalProperties: false` for strict schema validation116- Use mutex for shared state in HTTP transport (thread safety)117- Return error responses for business errors (`Response.new([...], error: true)`)118- Check protocol version before using newer features119- Use `server_context` for request-scoped data (user_id, env)120121### Don't122123- Don't use `$ref` in schemas (raises ArgumentError, inline only)124- Don't assume extra args are rejected (`additionalProperties` defaults to allowing extras)125- Don't use `rpc.` prefix (reserved for protocol methods)126- Don't send notifications in stateless mode (raises RuntimeError)127- Don't rely on validation order (required args checked before JSON Schema)128129## Anti-Patterns Quick List130131| Anti-Pattern | Solution |132|--------------|----------|133| Missing `additionalProperties: false` | Add to schema for strict validation |134| Using `$ref` in schemas | Inline all definitions |135| Notifications in stateless mode | Use stateful transport or skip notifications |136| Hardcoded server_context | Pass dynamically based on request |137| Ignoring protocol version | Check version before using gated features |138| Blocking in tool handlers | Use async patterns for long operations |139140## Key Points1411421. **Validation is multi-layered** - Required args checked first, then JSON Schema validation1432. **Notifications are fire-and-forget** - Errors reported but don't propagate1443. **Protocol version matters** - Features are gated by version1454. **Server context is opt-in** - Detected from method signature (must include `server_context:` parameter)1465. **Schemas are immutable** - Validated at class load time, not runtime147148## Additional Resources149150### Reference Files151152For detailed DSL syntax by domain:153154- **`references/tools.md`** - Tool definition, responses, schemas, annotations155- **`references/prompts.md`** - Prompt definition, arguments, content types156- **`references/resources.md`** - Resource registration, templates, read handlers157- **`references/server.md`** - Server initialization, configuration, custom methods158- **`references/transport.md`** - Transport config, protocol methods, sessions159- **`references/gotchas.md`** - Tricky behaviors, error handling, edge cases160161### Example Files162163Working examples in `examples/`:164165- **`examples/stdio_server.rb`** - Complete STDIO server with tools, prompts, resources166- **`examples/http_server.rb`** - HTTP server with Rack and logging167- **`examples/rails_integration.rb`** - Rails controller, routes, and initializer168- **`examples/file_manager_tool.rb`** - Sandboxed file operations with security patterns169- **`examples/dynamic_tools.rb`** - Runtime tool registration with notifications170- **`examples/http_client.rb`** - HTTP client connecting to MCP server171- **`examples/streaming_client.rb`** - SSE streaming client for real-time notifications172173### External Links174175- [MCP Ruby SDK on GitHub](https://github.com/modelcontextprotocol/ruby-sdk)176- [MCP Protocol Specification](https://modelcontextprotocol.io)177- [RubyDoc API Reference](https://rubydoc.info/gems/mcp)