studio 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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +46 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +140 -0
- data/Rakefile +12 -0
- data/exe/studio +6 -0
- data/lib/studio/blueprint.rb +153 -0
- data/lib/studio/tool.rb +36 -0
- data/lib/studio/version.rb +5 -0
- data/lib/studio.rb +34 -0
- data/sig/studio.rbs +4 -0
- metadata +73 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0ac4b3ac6f6fbeebc3dbf0aafcc521035ee7ae5be4333819a84fd43f40c5c998
|
4
|
+
data.tar.gz: 31f702dc105efcb28df1070f2b22b46c2b88d914989ab2599b575d929294ab6d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 16ca24292c16c4a6a9a6412e205305eb0f051ee236c4603570666638115984dfcee13b3cc7c9e0dbc2e6fef6132c2105ee57502e7bb4446d7a4a94143954fc6b
|
7
|
+
data.tar.gz: 1302ac921460a521c225296fc08bc36e4d764dd57990a1e75bb438c36501ad46eed26375aca4f7ff34a9467083175e8ab1e33b25eba4cb1703174c6fd5a16b5a
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
plugins:
|
2
|
+
- rubocop-performance
|
3
|
+
- rubocop-rspec
|
4
|
+
- rubocop-rake
|
5
|
+
|
6
|
+
AllCops:
|
7
|
+
TargetRubyVersion: 3.2
|
8
|
+
NewCops: enable
|
9
|
+
|
10
|
+
Style/StringLiterals:
|
11
|
+
EnforcedStyle: double_quotes
|
12
|
+
|
13
|
+
Style/StringLiteralsInInterpolation:
|
14
|
+
EnforcedStyle: double_quotes
|
15
|
+
|
16
|
+
Metrics/BlockLength:
|
17
|
+
Exclude:
|
18
|
+
- "spec/**/*"
|
19
|
+
- "*.gemspec"
|
20
|
+
|
21
|
+
Metrics/MethodLength:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Metrics/ModuleLength:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Metrics/ClassLength:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Metrics/AbcSize:
|
31
|
+
Exclude:
|
32
|
+
- "spec/**/*"
|
33
|
+
|
34
|
+
Metrics/CyclomaticComplexity:
|
35
|
+
Exclude:
|
36
|
+
- "spec/**/*"
|
37
|
+
|
38
|
+
Metrics/PerceivedComplexity:
|
39
|
+
Exclude:
|
40
|
+
- "spec/**/*"
|
41
|
+
|
42
|
+
RSpec/MultipleExpectations:
|
43
|
+
Enabled: false
|
44
|
+
|
45
|
+
RSpec/ExampleLength:
|
46
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 Martin Emde
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
# Studio
|
2
|
+
|
3
|
+
Make any CLI into a single tool MCP server.
|
4
|
+
|
5
|
+
> Bright Studio Apt – No Walls, No Rules – 800/mo OBO.
|
6
|
+
>
|
7
|
+
> Wired and wild. No questions, no backups. rm -rf compatible. Cash only. Basement-ish. BYO everything. Just enough, nothing more.
|
8
|
+
>
|
9
|
+
> Text 404 to /dev/null for more details!
|
10
|
+
|
11
|
+
## Install
|
12
|
+
|
13
|
+
```sh
|
14
|
+
$ gem install studio
|
15
|
+
```
|
16
|
+
|
17
|
+
### Claude Code
|
18
|
+
|
19
|
+
```sh
|
20
|
+
# <serv-name> studio <cmd> <arguments or blueprints>
|
21
|
+
$ claude mcp add echo-server studio echo "{{text#What do you want to say?}}"
|
22
|
+
```
|
23
|
+
|
24
|
+
```sh
|
25
|
+
$ claude mcp add git-log studio git log --one-line -n 20 --ref "{{branch}}"
|
26
|
+
```
|
27
|
+
|
28
|
+
### Cursor
|
29
|
+
|
30
|
+
Make sure `studio` is installed, then
|
31
|
+
|
32
|
+
[](https://cursor.com/install-mcp?name=echo&config=eyJjb21tYW5kIjoic3R1ZGlvIHt7dGV4dCNXaGF0IGRvIHlvdSB3YW50IHRvIHNheT99fSJ9)
|
33
|
+
|
34
|
+
Add to your `.cursor/mcp.json` manually:
|
35
|
+
|
36
|
+
```json
|
37
|
+
{
|
38
|
+
"mcpServers": {
|
39
|
+
"echo": {
|
40
|
+
"command": "studio",
|
41
|
+
"args": ["echo", "{{text#What do you want to say?}}"]
|
42
|
+
},
|
43
|
+
"rails": {
|
44
|
+
"command": "studio",
|
45
|
+
"args": ["rails", "generate", "{{generator # A valid rails generator like scaffold, model}}"]
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
```
|
50
|
+
|
51
|
+
### VSCode
|
52
|
+
|
53
|
+
_Pay attention, one says `stdio`, the other says `studio`! (uh oh, what have I done?)_
|
54
|
+
|
55
|
+
```json
|
56
|
+
"mcp": {
|
57
|
+
"servers": {
|
58
|
+
"echo": {
|
59
|
+
"type": "stdio",
|
60
|
+
"command": "studio",
|
61
|
+
"args": ["echo", "{{text#What do you want to say?}}"]
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
65
|
+
```
|
66
|
+
|
67
|
+
### Do you speak MCP?
|
68
|
+
|
69
|
+
```bash
|
70
|
+
$ studio echo
|
71
|
+
```
|
72
|
+
|
73
|
+
Now you're speaking to the mcp server, so say something smart.
|
74
|
+
|
75
|
+
**ping**
|
76
|
+
|
77
|
+
```json
|
78
|
+
{"jsonrpc":"2.0","id":"1","method":"ping"}
|
79
|
+
```
|
80
|
+
|
81
|
+
or not... just play ping pong if you wish.
|
82
|
+
|
83
|
+
**initialize**
|
84
|
+
|
85
|
+
```json
|
86
|
+
{"jsonrpc": "2.0","id": "2","method": "initialize","params": {"protocolVersion": "2024-11-05","capabilities": {},"clientInfo": {"name": "test-client","version": "1.0.0"}}}
|
87
|
+
```
|
88
|
+
|
89
|
+
that's better...
|
90
|
+
|
91
|
+
**list tools**
|
92
|
+
|
93
|
+
```json
|
94
|
+
{"jsonrpc":"2.0","id":"3","method":"tools/list"}
|
95
|
+
```
|
96
|
+
|
97
|
+
Oh look, a hammer...
|
98
|
+
|
99
|
+
**use tool**
|
100
|
+
|
101
|
+
```json
|
102
|
+
{"jsonrpc": "2.0","id": "4","method": "tools/call","params": {"name": "echo","arguments": {"args": ["hello", "world"]}}}
|
103
|
+
```
|
104
|
+
|
105
|
+
### Blueprints
|
106
|
+
|
107
|
+
Use blueprints (templates) to keep your studio tidy.
|
108
|
+
|
109
|
+
```bash
|
110
|
+
studio rails generate "{{generator # A valid rails generator like scaffold, model}}"
|
111
|
+
```
|
112
|
+
|
113
|
+
This creates an Studio server with one argument: `generator`.
|
114
|
+
Everything after the `#` will be used as the description of the argument.
|
115
|
+
|
116
|
+
Blueprints use the format: `{{name # description}}`.
|
117
|
+
|
118
|
+
- `name`: The argument name that will be exposed in the MCP tool schema
|
119
|
+
- `description`: A description of what the argument should contain. May contain spaces.
|
120
|
+
|
121
|
+
This is a simple studio, not one of those fancy 1 bedroom flats. Blueprint types, flags, arrays? The landlord will upgrade the place for free eventually, right? Right?
|
122
|
+
|
123
|
+
## Development
|
124
|
+
|
125
|
+
**Keys to the City**. Run `bin/setup` to move in. Run `bin/rake` for a quick inspection.
|
126
|
+
|
127
|
+
Bump `version.rb`, tag it `vX.Y.Z`, push to GitHub. Trusted publishing will be happy to receive your rent check.
|
128
|
+
|
129
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
130
|
+
|
131
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, tag the release vX.Y.Z, then push the tag to github. Trusted publishing will do the rest.
|
132
|
+
|
133
|
+
## Home Is Where You Make It
|
134
|
+
|
135
|
+
This is your studio too. Bugs, features, ideas? Swing by the repo:
|
136
|
+
🏠 https://github.com/martinemde/studio
|
137
|
+
|
138
|
+
## Lease Terms: MIT
|
139
|
+
|
140
|
+
Move in under the standard terms, no fine print. Full text here: [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/exe/studio
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "shellwords"
|
4
|
+
|
5
|
+
module Studio
|
6
|
+
class Blueprint # rubocop:todo Style/Documentation
|
7
|
+
attr_reader :argv, :base_command, :blueprint_args
|
8
|
+
|
9
|
+
def self.argv(argv)
|
10
|
+
new(argv)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(argv)
|
14
|
+
@argv = argv.dup
|
15
|
+
@base_command = @argv.shift
|
16
|
+
@blueprint_args = parse_blueprint_args(@argv)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Execute the command with provided arguments
|
20
|
+
def execute(args = [])
|
21
|
+
full_command = build_command(args)
|
22
|
+
|
23
|
+
# Spawn the process and capture output
|
24
|
+
stdout, stderr, status = Open3.capture3(*full_command)
|
25
|
+
|
26
|
+
{
|
27
|
+
stdout: stdout,
|
28
|
+
stderr: stderr,
|
29
|
+
exit_code: status.exitstatus,
|
30
|
+
success: status.success?
|
31
|
+
}
|
32
|
+
rescue StandardError => e
|
33
|
+
{
|
34
|
+
stdout: "",
|
35
|
+
stderr: "Studio error: #{e.message}",
|
36
|
+
exit_code: 1,
|
37
|
+
success: false
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
# Get the tool name for MCP
|
42
|
+
def tool_name
|
43
|
+
@base_command.gsub(/[^a-zA-Z0-9_]/, "_")
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get the tool description for MCP
|
47
|
+
def tool_description
|
48
|
+
if @blueprint_args.any?
|
49
|
+
blueprint_desc = @blueprint_args.map { |arg| "{{#{arg[:name]}}}" }.join(" ")
|
50
|
+
"Run the shell command `#{@base_command} #{blueprint_desc}`"
|
51
|
+
else
|
52
|
+
"Run the shell command `#{@base_command} [args]`"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Get the input schema for MCP tool
|
57
|
+
def input_schema
|
58
|
+
schema = {
|
59
|
+
type: "object",
|
60
|
+
properties: {}
|
61
|
+
}
|
62
|
+
|
63
|
+
if @blueprint_args.empty?
|
64
|
+
schema[:properties][:args] = {
|
65
|
+
type: "array",
|
66
|
+
items: { type: "string" },
|
67
|
+
description: "Additional command line arguments"
|
68
|
+
}
|
69
|
+
else
|
70
|
+
# Add blueprint arguments to schema
|
71
|
+
@blueprint_args.each do |blueprint_arg|
|
72
|
+
schema[:properties][blueprint_arg[:name].to_sym] = {
|
73
|
+
type: "string",
|
74
|
+
description: blueprint_arg[:description]
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
schema
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def parse_blueprint_args(args)
|
85
|
+
blueprint_args = []
|
86
|
+
|
87
|
+
args.each do |arg|
|
88
|
+
# First check for templates with descriptions: {{name#description}}
|
89
|
+
arg.scan(/\{\{(\w+)\s*#\s*(.+?)\}\}/) do |name, description|
|
90
|
+
blueprint_args << { name: name, description: description.strip }
|
91
|
+
end
|
92
|
+
|
93
|
+
# Then check for templates without descriptions: {{name}}
|
94
|
+
# Only add if we haven't already found this name with a description
|
95
|
+
arg.scan(/\{\{(\w+)\}\}/) do |name|
|
96
|
+
name = name.first
|
97
|
+
unless blueprint_args.any? { |ba| ba[:name] == name }
|
98
|
+
blueprint_args << { name: name, description: "the {{#{name}}} arg" }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
blueprint_args
|
104
|
+
end
|
105
|
+
|
106
|
+
# rubocop:todo Metrics/PerceivedComplexity
|
107
|
+
# rubocop:todo Metrics/AbcSize
|
108
|
+
# rubocop:todo Metrics/CyclomaticComplexity
|
109
|
+
def build_command(args) # rubocop:todo Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
|
110
|
+
command_parts = [@base_command]
|
111
|
+
|
112
|
+
# Add blueprint arguments if they exist
|
113
|
+
if @blueprint_args.any?
|
114
|
+
@argv.each do |arg|
|
115
|
+
# Check if this argument contains any blueprint patterns
|
116
|
+
processed_arg = arg.dup
|
117
|
+
|
118
|
+
# Replace templates with descriptions: {{name#description}}
|
119
|
+
processed_arg.gsub!(/\{\{(\w+)\s*#\s*.+?\}\}/) do |_match|
|
120
|
+
name = ::Regexp.last_match(1)
|
121
|
+
value = args[name.to_s] || args[name.to_sym]
|
122
|
+
value.to_s # Convert to string in case of nil
|
123
|
+
end
|
124
|
+
|
125
|
+
# Replace templates without descriptions: {{name}}
|
126
|
+
processed_arg.gsub!(/\{\{(\w+)\}\}/) do |_match|
|
127
|
+
name = ::Regexp.last_match(1)
|
128
|
+
value = args[name.to_s] || args[name.to_sym]
|
129
|
+
value.to_s # Convert to string in case of nil
|
130
|
+
end
|
131
|
+
|
132
|
+
command_parts << processed_arg
|
133
|
+
end
|
134
|
+
else
|
135
|
+
# Add original blueprint arguments
|
136
|
+
command_parts.concat(@argv)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Add additional args
|
140
|
+
if args.is_a?(Hash) && (args["args"] || args[:args])
|
141
|
+
additional_args = args["args"] || args[:args]
|
142
|
+
command_parts.concat(additional_args) if additional_args
|
143
|
+
elsif args.is_a?(Array)
|
144
|
+
command_parts.concat(args)
|
145
|
+
end
|
146
|
+
|
147
|
+
command_parts.compact
|
148
|
+
end
|
149
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
150
|
+
# rubocop:enable Metrics/AbcSize
|
151
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
152
|
+
end
|
153
|
+
end
|
data/lib/studio/tool.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mcp"
|
4
|
+
|
5
|
+
module Studio
|
6
|
+
class Tool # rubocop:todo Style/Documentation
|
7
|
+
# rubocop:todo Metrics/AbcSize
|
8
|
+
def self.for_blueprint(blueprint) # rubocop:todo Metrics/AbcSize
|
9
|
+
Class.new(MCP::Tool) do
|
10
|
+
tool_name blueprint.tool_name
|
11
|
+
description blueprint.tool_description
|
12
|
+
input_schema blueprint.input_schema
|
13
|
+
|
14
|
+
define_singleton_method :call do |server_context:, **arguments| # rubocop:todo Lint/UnusedBlockArgument
|
15
|
+
result = blueprint.execute(arguments)
|
16
|
+
|
17
|
+
# Log stderr if present
|
18
|
+
warn "[Studio] #{result[:stderr]}" unless result[:stderr].empty?
|
19
|
+
|
20
|
+
if result[:success]
|
21
|
+
MCP::Tool::Response.new([{
|
22
|
+
type: "text",
|
23
|
+
text: result[:stdout]
|
24
|
+
}])
|
25
|
+
else
|
26
|
+
MCP::Tool::Response.new([{
|
27
|
+
type: "text",
|
28
|
+
text: result[:stdout]
|
29
|
+
}], true)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
# rubocop:enable Metrics/AbcSize
|
35
|
+
end
|
36
|
+
end
|
data/lib/studio.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "open3"
|
4
|
+
require "mcp"
|
5
|
+
require "mcp/transports/stdio"
|
6
|
+
|
7
|
+
require_relative "studio/version"
|
8
|
+
require_relative "studio/blueprint"
|
9
|
+
require_relative "studio/tool"
|
10
|
+
|
11
|
+
module Studio # rubocop:todo Style/Documentation
|
12
|
+
class Error < StandardError; end
|
13
|
+
|
14
|
+
def self.serve(argv)
|
15
|
+
if argv.empty?
|
16
|
+
warn "Usage: studio <command> [blueprint_args...]"
|
17
|
+
exit 1
|
18
|
+
end
|
19
|
+
|
20
|
+
blueprint = Blueprint.new(argv)
|
21
|
+
tool_class = Tool.for_blueprint(blueprint)
|
22
|
+
|
23
|
+
server = MCP::Server.new(
|
24
|
+
name: "studio_server",
|
25
|
+
tools: [tool_class]
|
26
|
+
)
|
27
|
+
|
28
|
+
transport = MCP::Transports::StdioTransport.new(server)
|
29
|
+
transport.open
|
30
|
+
rescue StandardError => e
|
31
|
+
warn "Studio error: #{e.message}"
|
32
|
+
exit 1
|
33
|
+
end
|
34
|
+
end
|
data/sig/studio.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: studio
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Emde
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: mcp
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0.1'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0.1'
|
26
|
+
description: Studio transforms any CLI command into a StdIO Model Context Protocol
|
27
|
+
(MCP) server. It exposes templated command-line tools so they can be executed more
|
28
|
+
easily and safely than free inputs.
|
29
|
+
email:
|
30
|
+
- me@martinemde.com
|
31
|
+
executables:
|
32
|
+
- studio
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- ".rspec"
|
37
|
+
- ".rubocop.yml"
|
38
|
+
- CHANGELOG.md
|
39
|
+
- LICENSE.txt
|
40
|
+
- README.md
|
41
|
+
- Rakefile
|
42
|
+
- exe/studio
|
43
|
+
- lib/studio.rb
|
44
|
+
- lib/studio/blueprint.rb
|
45
|
+
- lib/studio/tool.rb
|
46
|
+
- lib/studio/version.rb
|
47
|
+
- sig/studio.rbs
|
48
|
+
homepage: https://github.com/martinemde/studio
|
49
|
+
licenses:
|
50
|
+
- MIT
|
51
|
+
metadata:
|
52
|
+
homepage_uri: https://github.com/martinemde/studio
|
53
|
+
source_code_uri: https://github.com/martinemde/studio
|
54
|
+
changelog_uri: https://github.com/martinemde/studio/blob/main/CHANGELOG.md
|
55
|
+
rubygems_mfa_required: 'true'
|
56
|
+
rdoc_options: []
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 3.2.0
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
requirements: []
|
70
|
+
rubygems_version: 3.6.7
|
71
|
+
specification_version: 4
|
72
|
+
summary: Make any CLI command an MCP server
|
73
|
+
test_files: []
|