ask-mcp 0.2.0 → 0.3.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 +4 -4
- data/CHANGELOG.md +18 -21
- data/README.md +73 -23
- data/lib/ask/mcp/adapters/{ask_tool_server.rb → tool_server.rb} +16 -11
- data/lib/ask/mcp/server/stdio.rb +1 -1
- data/lib/ask/mcp/version.rb +1 -1
- data/lib/ask/mcp.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 90e04370e4d5b1136abb513220b68beb46a5ca2c74d8f7ca694fa59297f8f32e
|
|
4
|
+
data.tar.gz: 17f2accea77cefc0ea51fedcc3879e219fff47760999b0ba39e0e168d30dbfe1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d8a481b9467e8f4d6d4d305da61346855f4d27ac94de77f109ac528e704599c18aee5a9c5deb64f7edb71a71b4e446cc1801149df21891d15eefdcb441eb1d09
|
|
7
|
+
data.tar.gz: b78ccc9a910fbfbeaf2e273ce6169279e4195624871f89356d6564ab6cd46380dd876951544e90b2f3b3b57328ae00180c6d218e1d66afa5416090c5703e1560
|
data/CHANGELOG.md
CHANGED
|
@@ -1,40 +1,37 @@
|
|
|
1
|
+
## [0.3.0] - 2026-06-26
|
|
2
|
+
|
|
3
|
+
### Changed
|
|
4
|
+
- Renamed `Adapters::AskToolServer` → `Adapters::ToolServer` — the adapter is
|
|
5
|
+
now duck-typed and works with any Ruby object, not just Ask::Tool instances.
|
|
6
|
+
- README fully rewritten: leads with a general-purpose duck-typed server example
|
|
7
|
+
instead of ask-tools-specific code. ask-rb integration is now a sub-section.
|
|
8
|
+
- Gemspec description updated to reflect general-purpose positioning.
|
|
9
|
+
|
|
1
10
|
## [0.2.0] - 2026-06-26
|
|
2
11
|
|
|
3
12
|
### Added
|
|
4
13
|
- Server Runtime: `Ask::MCP::Server::Stdio` — run as an MCP server over stdio
|
|
5
14
|
with full initialize handshake, tool discovery, and tool call dispatch.
|
|
6
|
-
- `
|
|
7
|
-
tool definitions and dispatches calls, enabling any Ask::Tool to be exposed
|
|
8
|
-
via MCP without modification.
|
|
15
|
+
- `Adapters::AskToolServer` — converts tools to MCP definitions and dispatches calls.
|
|
9
16
|
- `Ask::MCP::Server.start_stdio` entry point for easy one-line server setup.
|
|
10
|
-
- `examples/` — reference usage in README "Running as an MCP Server" section.
|
|
11
17
|
|
|
12
18
|
### Changed
|
|
13
|
-
-
|
|
14
|
-
and start_stdio entry point (170 total, up from 114).
|
|
15
|
-
- `ask-mcp` gem description updated to reflect client + server capabilities.
|
|
16
|
-
- Architecture section updated to include server/ and adapters/ask_tool_server.rb.
|
|
19
|
+
- 56 new tests across adapter, server stdio, integration, and start_stdio (170 total).
|
|
17
20
|
|
|
18
21
|
## [0.1.1] - 2026-06-25
|
|
19
22
|
|
|
20
23
|
### Changed
|
|
21
|
-
- Major test expansion: Transport tests
|
|
24
|
+
- Major test expansion: Transport tests, MessagesParser, OAuth, Client, Server, Tool, Messages, integration. Rubocop, overcommit, CI matrix, SimpleCov.
|
|
22
25
|
|
|
23
26
|
## [0.1.0] - 2026-06-10
|
|
24
27
|
|
|
25
28
|
### Added
|
|
26
29
|
- Core MCP client with full JSON-RPC 2.0 message layer
|
|
27
|
-
- stdio
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
- Tool calling, resource reading, and prompt retrieval
|
|
33
|
-
- Token-based authentication (Bearer/Basic)
|
|
34
|
-
- OAuth 2.1 authentication (client credentials + authorization code flows)
|
|
35
|
-
- Ask::Tool adapter for integration with ask-agent
|
|
30
|
+
- stdio, SSE, and Streamable HTTP transports
|
|
31
|
+
- Tool, Resource, and Prompt data models
|
|
32
|
+
- Client lifecycle, tool calling, resource reading, prompt retrieval
|
|
33
|
+
- Token-based and OAuth 2.1 authentication
|
|
34
|
+
- Ask::Tool adapter for ask-agent integration
|
|
36
35
|
- Thread-safe request/response matching with configurable timeouts
|
|
37
|
-
- Server-side notifications handling
|
|
38
|
-
- Comprehensive test suite with mock MCP server
|
|
36
|
+
- Server-side notifications handling
|
|
39
37
|
- Factory methods: `from_stdio`, `from_sse`, `from_http`, `connect`
|
|
40
|
-
- Ability to cache or bypass caching for tools/resources/prompts
|
data/README.md
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/rb/ask-mcp)
|
|
4
4
|
|
|
5
|
-
**Model Context Protocol (MCP) client for Ruby.** Connect to MCP
|
|
6
|
-
stdio, SSE, or Streamable HTTP transports.
|
|
7
|
-
|
|
5
|
+
**Model Context Protocol (MCP) client and server for Ruby.** Connect to MCP
|
|
6
|
+
servers via stdio, SSE, or Streamable HTTP transports. Run as an MCP server
|
|
7
|
+
to expose your own tools to any MCP client. No framework lock-in — just
|
|
8
|
+
implement a couple of duck-typed methods and you're done.
|
|
8
9
|
|
|
9
10
|
MCP is the industry standard for LLM tool discovery — the same protocol used by
|
|
10
11
|
Claude Code, Codex, Cursor, and GitHub Copilot.
|
|
@@ -21,44 +22,49 @@ Or add to your Gemfile:
|
|
|
21
22
|
gem "ask-mcp", "~> 0.1.0"
|
|
22
23
|
```
|
|
23
24
|
|
|
24
|
-
## Quick Start
|
|
25
|
+
## Quick Start — Client
|
|
26
|
+
|
|
27
|
+
Connect to any MCP server and call its tools:
|
|
25
28
|
|
|
26
29
|
```ruby
|
|
27
30
|
require "ask/mcp"
|
|
28
31
|
|
|
29
|
-
# Connect to a local MCP server via stdio
|
|
30
32
|
client = Ask::MCP.from_stdio("npx", ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"])
|
|
31
33
|
client.start
|
|
32
34
|
|
|
33
|
-
# List available tools
|
|
34
35
|
client.tools.each { |name, tool| puts "#{name}: #{tool.description}" }
|
|
35
36
|
|
|
36
|
-
# Call a tool
|
|
37
37
|
result = client.call_tool("read_file", path: "/tmp/test.txt")
|
|
38
38
|
puts result
|
|
39
39
|
|
|
40
|
-
# Clean up
|
|
41
40
|
client.stop
|
|
42
41
|
```
|
|
43
42
|
|
|
43
|
+
## Quick Start — Server
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
`
|
|
48
|
-
`Ask::Tool` instances to MCP-compatible clients (Codex Desktop, Claude Code, etc.).
|
|
45
|
+
Run as a standalone MCP server exposing your own tools to any MCP client
|
|
46
|
+
(Codex, Claude Code, Cursor, etc.). Any object that responds to `name`,
|
|
47
|
+
`description`, `params_schema`, and `call(args)` will work:
|
|
49
48
|
|
|
50
49
|
```ruby
|
|
51
50
|
require "ask/mcp"
|
|
52
|
-
require "ask-tools-shell"
|
|
53
|
-
require "ask-web-search"
|
|
54
51
|
|
|
55
|
-
tools
|
|
56
|
-
|
|
52
|
+
# Define your tools — no base class needed, just duck typing
|
|
53
|
+
class Greeter
|
|
54
|
+
def name; "greet" end
|
|
55
|
+
def description; "Greets someone by name" end
|
|
56
|
+
def params_schema
|
|
57
|
+
{ type: "object", properties: { "name" => { "type" => "string" } }, required: ["name"] }
|
|
58
|
+
end
|
|
59
|
+
def call(args = {})
|
|
60
|
+
"Hello, #{args['name']}!"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
57
63
|
|
|
64
|
+
# Start the server (blocking — runs until stdin closes)
|
|
58
65
|
Ask::MCP::Server.start_stdio(
|
|
59
66
|
name: "my-server",
|
|
60
|
-
tools:
|
|
61
|
-
capabilities: { tools: {} }
|
|
67
|
+
tools: [Greeter.new]
|
|
62
68
|
)
|
|
63
69
|
```
|
|
64
70
|
|
|
@@ -69,14 +75,58 @@ Configure your MCP client:
|
|
|
69
75
|
"mcpServers": {
|
|
70
76
|
"my-server": {
|
|
71
77
|
"command": "ruby",
|
|
72
|
-
"args": ["path/to/your/
|
|
78
|
+
"args": ["/path/to/your/server.rb"]
|
|
73
79
|
}
|
|
74
80
|
}
|
|
75
81
|
}
|
|
76
82
|
```
|
|
77
83
|
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
Tools can return any value the client can use. The server automatically wraps
|
|
85
|
+
the result into MCP's `content` array format.
|
|
86
|
+
|
|
87
|
+
### What about complex tools?
|
|
88
|
+
|
|
89
|
+
The result object from `call(args)` should respond to `ok?` (or `ok`) and
|
|
90
|
+
`output` / `error_message`. Use `OpenStruct` for simple cases:
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
require "ostruct"
|
|
94
|
+
|
|
95
|
+
class BashTool
|
|
96
|
+
def name; "bash" end
|
|
97
|
+
def description; "Run a shell command" end
|
|
98
|
+
def params_schema
|
|
99
|
+
{ type: "object", properties: { "command" => { "type" => "string" } }, required: ["command"] }
|
|
100
|
+
end
|
|
101
|
+
def call(args = {})
|
|
102
|
+
output = `#{args['command']} 2>&1`
|
|
103
|
+
OpenStruct.new(ok?: true, output: output)
|
|
104
|
+
rescue => e
|
|
105
|
+
OpenStruct.new(ok?: false, error_message: e.message)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
For a production example with shell tools, file ops, and web search,
|
|
111
|
+
see [llm-proxy](https://github.com/ask-rb/llm-proxy).
|
|
112
|
+
|
|
113
|
+
### Using with ask-tools
|
|
114
|
+
|
|
115
|
+
If you use the ask-rb ecosystem, you can expose Ask::Tool subclasses directly:
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
require "ask/mcp"
|
|
119
|
+
require "ask-tools-shell"
|
|
120
|
+
require "ask-web-search"
|
|
121
|
+
|
|
122
|
+
tools = Ask::Tools::Shell::TOOLS.map(&:new) + [Ask::Tools::WebSearch.new]
|
|
123
|
+
|
|
124
|
+
Ask::MCP::Server.start_stdio(
|
|
125
|
+
name: "my-server",
|
|
126
|
+
tools: tools,
|
|
127
|
+
capabilities: { tools: {} }
|
|
128
|
+
)
|
|
129
|
+
```
|
|
80
130
|
|
|
81
131
|
## Transports
|
|
82
132
|
|
|
@@ -183,7 +233,6 @@ wrapped = Ask::MCP::Adapters::AskTool.wrap(client.tools)
|
|
|
183
233
|
wrapped.each { |name, adapter| agent.register_tool(adapter.to_ask_tool) }
|
|
184
234
|
```
|
|
185
235
|
|
|
186
|
-
|
|
187
236
|
## Architecture
|
|
188
237
|
|
|
189
238
|
```
|
|
@@ -205,8 +254,9 @@ ask-mcp/
|
|
|
205
254
|
│ └── token.rb # Token-based auth
|
|
206
255
|
└── lib/ask/mcp/adapters/
|
|
207
256
|
├── ask_tool.rb # MCP::Tool → Ask::Tool adapter
|
|
208
|
-
└──
|
|
257
|
+
└── tool_server.rb # Duck-typed tools → MCP server adapter
|
|
209
258
|
```
|
|
259
|
+
|
|
210
260
|
## Development
|
|
211
261
|
|
|
212
262
|
```bash
|
|
@@ -3,19 +3,26 @@
|
|
|
3
3
|
module Ask
|
|
4
4
|
module MCP
|
|
5
5
|
module Adapters
|
|
6
|
-
# Converts
|
|
7
|
-
# calls. This is the server-direction adapter — it takes
|
|
8
|
-
#
|
|
6
|
+
# Converts duck-typed tool objects into MCP tool definitions and dispatches
|
|
7
|
+
# calls. This is the server-direction adapter — it takes any objects that
|
|
8
|
+
# respond to +name+, +description+, +params_schema+, and +call(args)+ and
|
|
9
|
+
# exposes them over MCP.
|
|
9
10
|
#
|
|
10
11
|
# Usage:
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
|
|
12
|
+
# class MyTool
|
|
13
|
+
# def name; "hello" end
|
|
14
|
+
# def description; "Says hello" end
|
|
15
|
+
# def params_schema; nil end
|
|
16
|
+
# def call(args = {}); OpenStruct.new(ok?: true, output: "Hello!") end
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# adapter = ToolServer.new([MyTool.new])
|
|
20
|
+
# adapter.definitions # => [{ name: "hello", ... }]
|
|
21
|
+
# adapter.call("hello", {}) # => { content: [...], isError: false }
|
|
22
|
+
class ToolServer
|
|
15
23
|
attr_reader :tools
|
|
16
24
|
|
|
17
|
-
# @param tools [Array<#call, #name, #description, #params_schema>] tool instances to expose
|
|
18
|
-
# Each tool must respond to name, description, params_schema, and call(args).
|
|
25
|
+
# @param tools [Array<#call, #name, #description, #params_schema>] tool instances to expose
|
|
19
26
|
def initialize(tools = [])
|
|
20
27
|
@tools = tools
|
|
21
28
|
@tool_map = tools.each_with_object({}) { |t, h| h[t.name] = t }
|
|
@@ -44,12 +51,10 @@ module Ask
|
|
|
44
51
|
return error_result("Tool not found: #{name}")
|
|
45
52
|
end
|
|
46
53
|
|
|
47
|
-
# Stringify keys — Ask::Tool subclasses expect string keys
|
|
48
54
|
normalized = deep_stringify_keys(arguments)
|
|
49
55
|
result = tool.call(normalized)
|
|
50
56
|
wrap_result(result)
|
|
51
57
|
rescue StandardError => e
|
|
52
|
-
# If the tool raised Ask::Tool::Halt, treat it as a success
|
|
53
58
|
if defined?(Ask::Tool::Halt) && e.is_a?(Ask::Tool::Halt)
|
|
54
59
|
return { content: [{ type: "text", text: e.content.to_s }], isError: false }
|
|
55
60
|
end
|
data/lib/ask/mcp/server/stdio.rb
CHANGED
data/lib/ask/mcp/version.rb
CHANGED
data/lib/ask/mcp.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ask-mcp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kaka Ruto
|
|
@@ -80,8 +80,8 @@ dependencies:
|
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '13.0'
|
|
82
82
|
description: Connect to MCP servers via stdio, SSE, and Streamable HTTP transports.
|
|
83
|
-
Run as an MCP server
|
|
84
|
-
prompts. OAuth 2.1 authentication.
|
|
83
|
+
Run as an MCP server exposing any Ruby object as a tool. Discover tools, resources,
|
|
84
|
+
and prompts. OAuth 2.1 authentication.
|
|
85
85
|
email:
|
|
86
86
|
- kaka@myrrlabs.com
|
|
87
87
|
executables: []
|
|
@@ -95,7 +95,7 @@ files:
|
|
|
95
95
|
- lib/ask-mcp.rb
|
|
96
96
|
- lib/ask/mcp.rb
|
|
97
97
|
- lib/ask/mcp/adapters/ask_tool.rb
|
|
98
|
-
- lib/ask/mcp/adapters/
|
|
98
|
+
- lib/ask/mcp/adapters/tool_server.rb
|
|
99
99
|
- lib/ask/mcp/auth/oauth.rb
|
|
100
100
|
- lib/ask/mcp/auth/token.rb
|
|
101
101
|
- lib/ask/mcp/client.rb
|