micro_mcp 0.1.0 → 0.1.4
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/.devcontainer/devcontainer.json +2 -2
- data/.tool-versions +1 -0
- data/AGENTS.md +5 -1
- data/CHANGELOG.md +12 -0
- data/Cargo.lock +278 -558
- data/README.md +39 -8
- data/docs/changes/ERGONOMIC_IMPROVEMENTS.md +133 -0
- data/ext/micro_mcp/AGENTS.md +66 -0
- data/ext/micro_mcp/Cargo.toml +3 -1
- data/ext/micro_mcp/src/lib.rs +21 -5
- data/ext/micro_mcp/src/server.rs +470 -50
- data/ext/micro_mcp/src/utils.rs +1 -1
- data/lib/micro_mcp/runtime_helpers.rb +104 -0
- data/lib/micro_mcp/schema.rb +125 -0
- data/lib/micro_mcp/server.rb +2 -2
- data/lib/micro_mcp/tool_registry.rb +65 -2
- data/lib/micro_mcp/validation_helpers.rb +59 -0
- data/lib/micro_mcp/version.rb +1 -1
- data/lib/micro_mcp.rb +13 -1
- metadata +9 -4
- data/sig/micro_mcp.rbs +0 -4
data/README.md
CHANGED
@@ -1,28 +1,59 @@
|
|
1
1
|
# MicroMcp
|
2
2
|
|
3
|
-
|
3
|
+
MicroMcp is a tiny framework for building [MCP](https://github.com/openai/AIAPI-Protocol) servers in Ruby. It ships with a Rust extension that handles the low level protocol while your Ruby code focuses on registering tools.
|
4
4
|
|
5
|
-
|
5
|
+
The gem is available on [RubyGems](https://rubygems.org/gems/micro_mcp).
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
9
|
-
|
9
|
+
Add the gem to your application's Gemfile:
|
10
10
|
|
11
|
-
|
11
|
+
```ruby
|
12
|
+
gem "micro_mcp"
|
13
|
+
```
|
14
|
+
|
15
|
+
Then execute:
|
12
16
|
|
13
17
|
```bash
|
14
|
-
bundle
|
18
|
+
bundle install
|
15
19
|
```
|
16
20
|
|
17
|
-
|
21
|
+
Or install it directly with RubyGems:
|
18
22
|
|
19
23
|
```bash
|
20
|
-
gem install
|
24
|
+
gem install micro_mcp
|
21
25
|
```
|
22
26
|
|
23
27
|
## Usage
|
24
28
|
|
25
|
-
|
29
|
+
Define one or more tools and start the server:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require "micro_mcp"
|
33
|
+
|
34
|
+
MicroMcp::ToolRegistry.register_tool(name: "say_hello") do
|
35
|
+
"Hello World!"
|
36
|
+
end
|
37
|
+
|
38
|
+
# Tools can also accept arguments defined using JSON Schema.
|
39
|
+
# The arguments hash is provided as the first block parameter.
|
40
|
+
MicroMcp::ToolRegistry.register_tool(
|
41
|
+
name: "add_numbers",
|
42
|
+
description: "Adds two integers",
|
43
|
+
arguments: {
|
44
|
+
"type" => "object",
|
45
|
+
"properties" => {
|
46
|
+
"a" => {"type" => "integer"},
|
47
|
+
"b" => {"type" => "integer"}
|
48
|
+
},
|
49
|
+
"required" => ["a", "b"]
|
50
|
+
}
|
51
|
+
) do |args, _runtime|
|
52
|
+
(args["a"] + args["b"]).to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
MicroMcp.start_server
|
56
|
+
```
|
26
57
|
|
27
58
|
## Development
|
28
59
|
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# MicroMCP Ergonomic Improvements
|
2
|
+
|
3
|
+
## Problem
|
4
|
+
|
5
|
+
The original MCP tool creation was error-prone due to several issues:
|
6
|
+
|
7
|
+
1. **Missing return value handling** - Easy to forget `result["content"]["text"]`
|
8
|
+
2. **Cryptic error messages** - "no implicit conversion of Hash into String"
|
9
|
+
3. **Symbol vs String key confusion** - JSON requires string keys
|
10
|
+
4. **Repetitive boilerplate** - Same patterns repeated in every tool
|
11
|
+
5. **No validation** - Hard to debug parameter issues
|
12
|
+
|
13
|
+
## Solution
|
14
|
+
|
15
|
+
We've added several layers of improvements to make tool creation more ergonomic and less error-prone:
|
16
|
+
|
17
|
+
### 1. Helper Methods (`runtime_helpers.rb`)
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
# Simple question-answering
|
21
|
+
runtime.ask_assistant("What is the capital of France?")
|
22
|
+
|
23
|
+
# Multi-message conversations
|
24
|
+
runtime.chat_with_assistant([
|
25
|
+
"Let's discuss Ruby",
|
26
|
+
{"role" => "assistant", "content" => {"type" => "text", "text" => "I'd love to help!"}},
|
27
|
+
"What are the key features?"
|
28
|
+
])
|
29
|
+
|
30
|
+
# Safe wrapper with validation
|
31
|
+
runtime.safe_create_message(params)
|
32
|
+
```
|
33
|
+
|
34
|
+
### 2. Enhanced Tool Registration (`tool_registry.rb`)
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# One-liner for simple Q&A tools
|
38
|
+
TR.register_qa_tool(
|
39
|
+
name: "ask",
|
40
|
+
description: "Ask a question"
|
41
|
+
)
|
42
|
+
|
43
|
+
# Enhanced registration with error handling
|
44
|
+
TR.register_assistant_tool(name: "custom", description: "Custom tool") do |args, runtime|
|
45
|
+
runtime.ask_assistant(args["question"])
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
### 3. Validation System (`validation_helpers.rb`)
|
50
|
+
|
51
|
+
- Pre-flight validation of `create_message` parameters
|
52
|
+
- Clear error messages for common mistakes
|
53
|
+
- Automatic symbol-to-string key conversion
|
54
|
+
- Structural validation of message format
|
55
|
+
|
56
|
+
## Response Format Handling
|
57
|
+
|
58
|
+
The system now properly handles the MCP response format:
|
59
|
+
|
60
|
+
```json
|
61
|
+
{
|
62
|
+
"role": "assistant",
|
63
|
+
"content": {
|
64
|
+
"type": "text",
|
65
|
+
"text": "The actual response text"
|
66
|
+
},
|
67
|
+
"model": "o4-mini",
|
68
|
+
"stopReason": "endTurn"
|
69
|
+
}
|
70
|
+
```
|
71
|
+
|
72
|
+
Helper methods automatically extract `result["content"]["text"]` with proper error handling.
|
73
|
+
|
74
|
+
## Before vs After
|
75
|
+
|
76
|
+
### Before (Error-prone)
|
77
|
+
```ruby
|
78
|
+
TR.register_tool(
|
79
|
+
name: "ask",
|
80
|
+
arguments: S.object(question: S.string.required)
|
81
|
+
) do |args, runtime|
|
82
|
+
result = runtime.create_message({
|
83
|
+
"messages" => [
|
84
|
+
{"role" => "user", "content" => {"type" => "text", "text" => args["question"]}}
|
85
|
+
],
|
86
|
+
"modelPreferences" => {
|
87
|
+
"hints" => [{"name" => "o4-mini"}],
|
88
|
+
"intelligencePriority" => 0.8,
|
89
|
+
"speedPriority" => 0.5
|
90
|
+
},
|
91
|
+
"systemPrompt" => "You are a helpful assistant.",
|
92
|
+
"maxTokens" => 100
|
93
|
+
})
|
94
|
+
result["content"]["text"] # Easy to forget!
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
### After (Simple)
|
99
|
+
```ruby
|
100
|
+
# One-liner approach
|
101
|
+
TR.register_qa_tool(name: "ask", description: "Ask a question")
|
102
|
+
|
103
|
+
# Or with customization
|
104
|
+
TR.register_assistant_tool(name: "ask", description: "Ask a question") do |args, runtime|
|
105
|
+
runtime.ask_assistant(args["question"])
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
## Error Handling
|
110
|
+
|
111
|
+
The new system provides:
|
112
|
+
|
113
|
+
1. **Pre-validation** - Catches errors before sending to MCP
|
114
|
+
2. **Clear error messages** - Explains what went wrong
|
115
|
+
3. **Automatic error recovery** - Handles common formatting issues
|
116
|
+
4. **Debug mode** - Set `ENV['MCP_DEBUG']` for detailed logging
|
117
|
+
|
118
|
+
## Backward Compatibility
|
119
|
+
|
120
|
+
All existing tools continue to work unchanged. The improvements are additive.
|
121
|
+
|
122
|
+
## Usage Examples
|
123
|
+
|
124
|
+
See `test/support/ergonomic_test.rb` and `test/support/ergonomic_examples.rb` for comprehensive examples.
|
125
|
+
|
126
|
+
## Key Benefits
|
127
|
+
|
128
|
+
1. ✅ **Reduced boilerplate** - Simple tools are one-liners
|
129
|
+
2. ✅ **Better error messages** - Clear validation feedback
|
130
|
+
3. ✅ **Automatic result extraction** - No more forgetting return values
|
131
|
+
4. ✅ **Symbol key safety** - Automatic string conversion
|
132
|
+
5. ✅ **Validation** - Catch issues before they cause problems
|
133
|
+
6. ✅ **Backward compatible** - Existing code keeps working
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Rust Development Agent Instructions
|
2
|
+
|
3
|
+
This directory contains Rust code that interfaces with Ruby via FFI. These instructions help agents navigate Rust source code efficiently.
|
4
|
+
|
5
|
+
## Environment Setup
|
6
|
+
|
7
|
+
The following environment variables should be exported for Rust source navigation:
|
8
|
+
|
9
|
+
```bash
|
10
|
+
# Standard library source (requires `rustup component add rust-src`)
|
11
|
+
export RUST_STD_SRC="$(rustc --print sysroot)/lib/rustlib/src/rust/library"
|
12
|
+
|
13
|
+
# Crates.io tarball cache (downloaded by `cargo fetch`)
|
14
|
+
export RUST_CRATE_SRC="${CARGO_HOME:-$HOME/.cargo}/registry/src"
|
15
|
+
|
16
|
+
# Git checkouts for dependencies using `git = "..."`
|
17
|
+
export RUST_GIT_SRC="${CARGO_HOME:-$HOME/.cargo}/git/checkouts"
|
18
|
+
```
|
19
|
+
|
20
|
+
## Helper Commands
|
21
|
+
|
22
|
+
### Locating Standard Library Items
|
23
|
+
|
24
|
+
```bash
|
25
|
+
rg "pub .* <TypeOrFnName>" "$RUST_STD_SRC"
|
26
|
+
```
|
27
|
+
|
28
|
+
### Finding Versioned Crates
|
29
|
+
|
30
|
+
```bash
|
31
|
+
# Example: finding serde 1.0.200
|
32
|
+
rg --files "$RUST_CRATE_SRC" | grep '/serde-1\.0\.200/' | head -n1
|
33
|
+
# Then search inside that path
|
34
|
+
```
|
35
|
+
|
36
|
+
### Locating Git Dependencies
|
37
|
+
|
38
|
+
```bash
|
39
|
+
# Example: finding a crate directory containing 'mycrate'
|
40
|
+
find "$RUST_GIT_SRC" -maxdepth 3 -type d -name '*mycrate*' | head
|
41
|
+
```
|
42
|
+
|
43
|
+
### Quick Jump to Crate Root
|
44
|
+
|
45
|
+
```bash
|
46
|
+
cargo metadata --format-version 1 --no-deps \
|
47
|
+
| jq -r '.packages[] | select(.name=="tokio") | .manifest_path' \
|
48
|
+
| xargs dirname
|
49
|
+
```
|
50
|
+
|
51
|
+
## Agent Rules
|
52
|
+
|
53
|
+
1. **Source Code Lookup Order**: Always search in this order:
|
54
|
+
- `$RUST_STD_SRC` (standard library)
|
55
|
+
- `$RUST_CRATE_SRC` (crates.io dependencies)
|
56
|
+
- `$RUST_GIT_SRC` (git dependencies)
|
57
|
+
|
58
|
+
2. **Exact Signatures**: Copy the **exact** function/type signature you find in the source. If no match is found, report:
|
59
|
+
- Which symbol is missing
|
60
|
+
- Which directory was searched
|
61
|
+
- Stop and ask for clarification
|
62
|
+
|
63
|
+
3. **FFI Considerations**: This project uses Ruby FFI, so pay attention to:
|
64
|
+
- Memory management between Rust and Ruby
|
65
|
+
- Proper error handling across language boundaries
|
66
|
+
- Thread safety considerations
|
data/ext/micro_mcp/Cargo.toml
CHANGED
@@ -15,7 +15,9 @@ magnus = { version = "0.7", features = ["rb-sys"] }
|
|
15
15
|
rb-sys = { version = "*", default-features = false, features = [
|
16
16
|
"stable-api-compiled-fallback",
|
17
17
|
] }
|
18
|
-
rust-mcp-sdk = "0.4.3"
|
18
|
+
rust-mcp-sdk = { version = "0.4.3", default-features = false, features = [
|
19
|
+
"server", "client", "2025_03_26"
|
20
|
+
]}
|
19
21
|
serde = "1.0.219"
|
20
22
|
serde_json = "1.0.140"
|
21
23
|
tokio = "1.45.1"
|
data/ext/micro_mcp/src/lib.rs
CHANGED
@@ -1,12 +1,28 @@
|
|
1
|
-
mod utils;
|
2
1
|
mod server;
|
2
|
+
mod utils;
|
3
3
|
|
4
|
-
use magnus::{function, prelude::*, Error, Ruby};
|
4
|
+
use magnus::{function, method, prelude::*, Error, Ruby};
|
5
5
|
|
6
6
|
#[magnus::init]
|
7
7
|
fn init(ruby: &Ruby) -> Result<(), Error> {
|
8
|
-
let
|
9
|
-
|
10
|
-
|
8
|
+
let native = ruby.define_module("MicroMcpNative")?;
|
9
|
+
native.define_singleton_method("start_server", function!(server::start_server, 0))?;
|
10
|
+
native.define_singleton_method("shutdown_server", function!(server::shutdown_server, 0))?;
|
11
|
+
native.define_singleton_method("register_tool", function!(server::register_tool, 4))?;
|
12
|
+
|
13
|
+
let parent = ruby.define_module("MicroMcp")?;
|
14
|
+
let class = parent.define_class("Runtime", ruby.class_object())?;
|
15
|
+
class.define_method(
|
16
|
+
"is_initialized",
|
17
|
+
method!(server::RubyMcpServer::is_initialized, 0),
|
18
|
+
)?;
|
19
|
+
class.define_method(
|
20
|
+
"client_supports_sampling",
|
21
|
+
method!(server::RubyMcpServer::client_supports_sampling, 0),
|
22
|
+
)?;
|
23
|
+
class.define_method(
|
24
|
+
"create_message",
|
25
|
+
method!(server::RubyMcpServer::create_message, 1),
|
26
|
+
)?;
|
11
27
|
Ok(())
|
12
28
|
}
|