mcp 0.8.0 → 0.9.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +174 -5
  3. data/lib/mcp/client/stdio.rb +222 -0
  4. data/lib/mcp/client.rb +12 -2
  5. data/lib/mcp/progress.rb +21 -0
  6. data/lib/mcp/prompt.rb +4 -0
  7. data/lib/mcp/resource.rb +3 -0
  8. data/lib/mcp/server/transports/stdio_transport.rb +1 -1
  9. data/lib/mcp/server/transports/streamable_http_transport.rb +7 -19
  10. data/lib/mcp/server/transports.rb +10 -0
  11. data/lib/mcp/server.rb +40 -4
  12. data/lib/mcp/server_context.rb +26 -0
  13. data/lib/mcp/tool.rb +5 -0
  14. data/lib/mcp/version.rb +1 -1
  15. data/lib/mcp.rb +10 -24
  16. metadata +7 -36
  17. data/.gitattributes +0 -4
  18. data/.github/dependabot.yml +0 -6
  19. data/.github/workflows/ci.yml +0 -54
  20. data/.github/workflows/conformance.yml +0 -29
  21. data/.github/workflows/release.yml +0 -57
  22. data/.gitignore +0 -11
  23. data/.rubocop.yml +0 -15
  24. data/AGENTS.md +0 -107
  25. data/CHANGELOG.md +0 -168
  26. data/CODE_OF_CONDUCT.md +0 -74
  27. data/Gemfile +0 -29
  28. data/RELEASE.md +0 -12
  29. data/Rakefile +0 -56
  30. data/SECURITY.md +0 -21
  31. data/bin/console +0 -15
  32. data/bin/generate-gh-pages.sh +0 -119
  33. data/bin/rake +0 -31
  34. data/bin/setup +0 -8
  35. data/conformance/README.md +0 -103
  36. data/conformance/expected_failures.yml +0 -9
  37. data/conformance/runner.rb +0 -101
  38. data/conformance/server.rb +0 -547
  39. data/dev.yml +0 -30
  40. data/docs/_config.yml +0 -6
  41. data/docs/index.md +0 -7
  42. data/docs/latest/index.html +0 -19
  43. data/examples/README.md +0 -197
  44. data/examples/http_client.rb +0 -184
  45. data/examples/http_server.rb +0 -169
  46. data/examples/stdio_server.rb +0 -94
  47. data/examples/streamable_http_client.rb +0 -207
  48. data/examples/streamable_http_server.rb +0 -172
  49. data/mcp.gemspec +0 -35
@@ -1,101 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Starts the conformance server and runs `npx @modelcontextprotocol/conformance` against it.
4
- require "English"
5
- require "net/http"
6
- require_relative "server"
7
-
8
- module Conformance
9
- class Runner
10
- # Timeout for waiting for the Puma server to start.
11
- SERVER_START_TIMEOUT = 20
12
- SERVER_POLL_INTERVAL = 0.5
13
- SERVER_HEALTH_CHECK_RETRIES = (SERVER_START_TIMEOUT / SERVER_POLL_INTERVAL).to_i
14
-
15
- def initialize(port: Server::DEFAULT_PORT, scenario: nil, spec_version: nil, verbose: false)
16
- @port = port
17
- @scenario = scenario
18
- @spec_version = spec_version
19
- @verbose = verbose
20
- end
21
-
22
- def run
23
- command = build_command
24
- server_pid = start_server
25
-
26
- run_conformance(command, server_pid: server_pid)
27
- end
28
-
29
- private
30
-
31
- def build_command
32
- expected_failures_yml = File.expand_path("expected_failures.yml", __dir__)
33
-
34
- npx_command = ["npx", "--yes", "@modelcontextprotocol/conformance", "server", "--url", "http://localhost:#{@port}/mcp"]
35
- npx_command += ["--scenario", @scenario] if @scenario
36
- npx_command += ["--spec-version", @spec_version] if @spec_version
37
- npx_command += ["--verbose"] if @verbose
38
- npx_command += ["--expected-failures", expected_failures_yml]
39
- npx_command
40
- end
41
-
42
- def start_server
43
- puts "Starting conformance server on port #{@port}..."
44
-
45
- server_pid = fork do
46
- Conformance::Server.new(port: @port).start
47
- end
48
-
49
- health_url = URI("http://localhost:#{@port}/health")
50
- ready = false
51
- SERVER_HEALTH_CHECK_RETRIES.times do
52
- begin
53
- response = Net::HTTP.get_response(health_url)
54
- if response.code == "200"
55
- ready = true
56
- break
57
- end
58
- rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Net::ReadTimeout
59
- # not ready yet
60
- end
61
- sleep(SERVER_POLL_INTERVAL)
62
- end
63
-
64
- unless ready
65
- warn("ERROR: Conformance server did not start within #{SERVER_START_TIMEOUT} seconds")
66
- terminate_server(server_pid)
67
- exit(1)
68
- end
69
-
70
- puts "Server ready. Running conformance tests..."
71
-
72
- server_pid
73
- end
74
-
75
- def run_conformance(command, server_pid:)
76
- puts "Command: #{command.join(" ")}\n\n"
77
-
78
- conformance_exit_code = nil
79
- begin
80
- system(*command)
81
- conformance_exit_code = $CHILD_STATUS.exitstatus
82
- ensure
83
- terminate_server(server_pid)
84
- end
85
-
86
- exit(conformance_exit_code || 1)
87
- end
88
-
89
- def terminate_server(pid)
90
- Process.kill("TERM", pid)
91
- rescue Errno::ESRCH
92
- # process already exited
93
- ensure
94
- begin
95
- Process.wait(pid)
96
- rescue Errno::ECHILD
97
- # already reaped
98
- end
99
- end
100
- end
101
- end
@@ -1,547 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rackup"
4
- require "json"
5
- require "uri"
6
- require_relative "../lib/mcp"
7
-
8
- module Conformance
9
- # 1x1 red PNG pixel (matches TypeScript SDK and Python SDK)
10
- BASE64_1X1_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg=="
11
-
12
- # Minimal WAV file (matches TypeScript SDK and Python SDK)
13
- BASE64_MINIMAL_WAV = "UklGRiYAAABXQVZFZm10IBAAAAABAAEAQB8AAAB9AAACABAAZGF0YQIAAAA="
14
-
15
- module Tools
16
- class TestSimpleText < MCP::Tool
17
- tool_name "test_simple_text"
18
- description "A tool that returns simple text content"
19
-
20
- class << self
21
- def call(**_args)
22
- MCP::Tool::Response.new([MCP::Content::Text.new("This is a simple text response for testing.").to_h])
23
- end
24
- end
25
- end
26
-
27
- class TestImageContent < MCP::Tool
28
- tool_name "test_image_content"
29
- description "A tool that returns image content"
30
-
31
- class << self
32
- def call(**_args)
33
- MCP::Tool::Response.new([MCP::Content::Image.new(BASE64_1X1_PNG, "image/png").to_h])
34
- end
35
- end
36
- end
37
-
38
- class TestAudioContent < MCP::Tool
39
- tool_name "test_audio_content"
40
- description "A tool that returns audio content"
41
-
42
- class << self
43
- def call(**_args)
44
- MCP::Tool::Response.new([MCP::Content::Audio.new(BASE64_MINIMAL_WAV, "audio/wav").to_h])
45
- end
46
- end
47
- end
48
-
49
- class TestEmbeddedResource < MCP::Tool
50
- tool_name "test_embedded_resource"
51
- description "A tool that returns embedded resource content"
52
-
53
- class << self
54
- def call(**_args)
55
- text_contents = MCP::Resource::TextContents.new(
56
- uri: "test://embedded-resource",
57
- mime_type: "text/plain",
58
- text: "This is an embedded resource content.",
59
- )
60
- MCP::Tool::Response.new([MCP::Content::EmbeddedResource.new(text_contents).to_h])
61
- end
62
- end
63
- end
64
-
65
- class TestMultipleContentTypes < MCP::Tool
66
- tool_name "test_multiple_content_types"
67
- description "A tool that returns multiple content types"
68
-
69
- class << self
70
- def call(**_args)
71
- MCP::Tool::Response.new([
72
- MCP::Content::Text.new("Multiple content types test:").to_h,
73
- MCP::Content::Image.new(BASE64_1X1_PNG, "image/png").to_h,
74
- MCP::Content::EmbeddedResource.new(
75
- MCP::Resource::TextContents.new(
76
- uri: "test://mixed-content-resource",
77
- mime_type: "application/json",
78
- text: '{"test":"data","value":123}',
79
- ),
80
- ).to_h,
81
- ])
82
- end
83
- end
84
- end
85
-
86
- class TestErrorHandling < MCP::Tool
87
- tool_name "test_error_handling"
88
- description "A tool that intentionally returns an error for testing"
89
-
90
- class << self
91
- def call(**_args)
92
- MCP::Tool::Response.new(
93
- [MCP::Content::Text.new("This tool intentionally returns an error for testing").to_h],
94
- error: true,
95
- )
96
- end
97
- end
98
- end
99
-
100
- class JsonSchema202012Tool < MCP::Tool
101
- tool_name "json_schema_2020_12_tool"
102
- description "Tool with JSON Schema 2020-12 features"
103
- input_schema(
104
- "$schema": "https://json-schema.org/draft/2020-12/schema",
105
- "$defs": {
106
- address: {
107
- type: "object",
108
- properties: {
109
- street: { type: "string" },
110
- city: { type: "string" },
111
- },
112
- },
113
- },
114
- properties: {
115
- name: { type: "string" },
116
- address: { "$ref": "#/$defs/address" },
117
- },
118
- additionalProperties: false,
119
- )
120
-
121
- class << self
122
- def call(**_args)
123
- MCP::Tool::Response.new([MCP::Content::Text.new("Processed with JSON Schema 2020-12").to_h])
124
- end
125
- end
126
- end
127
-
128
- class TestToolWithLogging < MCP::Tool
129
- tool_name "test_tool_with_logging"
130
- description "A tool that sends log messages during execution"
131
-
132
- class << self
133
- def call(server_context:, **_args)
134
- server_context.notify_log_message(data: "Tool execution started", level: "info", logger: "test_logger")
135
- sleep(0.05) # Required by the conformance test to verify clients handle interleaved notifications (same as TypeScript SDK).
136
- server_context.notify_log_message(data: "Tool processing data", level: "info", logger: "test_logger")
137
- sleep(0.05) # Same as above.
138
- server_context.notify_log_message(data: "Tool execution completed", level: "info", logger: "test_logger")
139
- MCP::Tool::Response.new([MCP::Content::Text.new("Logging complete (3 messages sent)").to_h])
140
- end
141
- end
142
- end
143
-
144
- # test_tool_with_progress: the actual progress dispatch is in `tools_call_handler`
145
- class TestToolWithProgress < MCP::Tool
146
- tool_name "test_tool_with_progress"
147
- description "A tool that reports progress notifications"
148
-
149
- class << self
150
- def call(**_args)
151
- MCP::Tool::Response.new([MCP::Content::Text.new("Progress complete").to_h])
152
- end
153
- end
154
- end
155
-
156
- # TODO: Implement when `Transport` supports server-to-client requests.
157
- class TestSampling < MCP::Tool
158
- tool_name "test_sampling"
159
- description "A tool that requests LLM sampling from the client"
160
- input_schema(
161
- properties: { prompt: { type: "string" } },
162
- required: ["prompt"],
163
- )
164
-
165
- class << self
166
- def call(prompt:)
167
- MCP::Tool::Response.new(
168
- [MCP::Content::Text.new("Sampling not supported in this SDK version").to_h],
169
- error: true,
170
- )
171
- end
172
- end
173
- end
174
-
175
- # TODO: Implement when `Transport` supports server-to-client requests.
176
- class TestElicitation < MCP::Tool
177
- tool_name "test_elicitation"
178
- description "A tool that requests user input from the client"
179
- input_schema(
180
- properties: { message: { type: "string" } },
181
- required: ["message"],
182
- )
183
-
184
- class << self
185
- def call(message:)
186
- MCP::Tool::Response.new(
187
- [MCP::Content::Text.new("Elicitation not supported in this SDK version").to_h],
188
- error: true,
189
- )
190
- end
191
- end
192
- end
193
-
194
- # TODO: Implement when `Transport` supports server-to-client requests.
195
- class TestElicitationSep1034Defaults < MCP::Tool
196
- tool_name "test_elicitation_sep1034_defaults"
197
- description "A tool that tests elicitation with default values"
198
-
199
- class << self
200
- def call(**_args)
201
- MCP::Tool::Response.new(
202
- [MCP::Content::Text.new("Elicitation not supported in this SDK version").to_h],
203
- error: true,
204
- )
205
- end
206
- end
207
- end
208
-
209
- # TODO: Implement when `Transport` supports server-to-client requests.
210
- class TestElicitationSep1330Enums < MCP::Tool
211
- tool_name "test_elicitation_sep1330_enums"
212
- description "A tool that tests elicitation with enum schemas"
213
-
214
- class << self
215
- def call(**_args)
216
- MCP::Tool::Response.new(
217
- [MCP::Content::Text.new("Elicitation not supported in this SDK version").to_h],
218
- error: true,
219
- )
220
- end
221
- end
222
- end
223
-
224
- class TestReconnection < MCP::Tool
225
- tool_name "test_reconnection"
226
- description "A tool that triggers SSE stream closure to test client reconnection behavior"
227
-
228
- class << self
229
- def call(**_args)
230
- MCP::Tool::Response.new([MCP::Content::Text.new("Reconnection test completed").to_h])
231
- end
232
- end
233
- end
234
- end
235
-
236
- module Prompts
237
- class TestSimplePrompt < MCP::Prompt
238
- prompt_name "test_simple_prompt"
239
- description "A simple prompt for testing with no arguments"
240
-
241
- class << self
242
- def template(_args, server_context: nil)
243
- MCP::Prompt::Result.new(
244
- messages: [
245
- MCP::Prompt::Message.new(
246
- role: "user",
247
- content: MCP::Content::Text.new("This is a simple prompt for testing."),
248
- ),
249
- ],
250
- )
251
- end
252
- end
253
- end
254
-
255
- class TestPromptWithArguments < MCP::Prompt
256
- prompt_name "test_prompt_with_arguments"
257
- description "A prompt with required arguments for testing"
258
- arguments [
259
- MCP::Prompt::Argument.new(name: "arg1", description: "First test argument", required: true),
260
- MCP::Prompt::Argument.new(name: "arg2", description: "Second test argument", required: true),
261
- ]
262
-
263
- class << self
264
- def template(args, server_context: nil)
265
- arg1 = args.dig(:arg1) || args.dig("arg1") || ""
266
- arg2 = args.dig(:arg2) || args.dig("arg2") || ""
267
- MCP::Prompt::Result.new(
268
- messages: [
269
- MCP::Prompt::Message.new(
270
- role: "user",
271
- content: MCP::Content::Text.new("Prompt with arguments: arg1='#{arg1}', arg2='#{arg2}'"),
272
- ),
273
- ],
274
- )
275
- end
276
- end
277
- end
278
-
279
- class TestPromptWithEmbeddedResource < MCP::Prompt
280
- prompt_name "test_prompt_with_embedded_resource"
281
- description "A prompt with an embedded resource for testing"
282
- arguments [
283
- MCP::Prompt::Argument.new(name: "resourceUri", description: "URI of the resource to embed", required: true),
284
- ]
285
-
286
- class << self
287
- def template(args, server_context: nil)
288
- resource_uri = args.dig(:resourceUri) || args.dig("resourceUri") || "test://example-resource"
289
- MCP::Prompt::Result.new(
290
- messages: [
291
- MCP::Prompt::Message.new(
292
- role: "user",
293
- content: MCP::Content::EmbeddedResource.new(
294
- MCP::Resource::TextContents.new(
295
- uri: resource_uri,
296
- mime_type: "text/plain",
297
- text: "Embedded resource content for testing.",
298
- ),
299
- ),
300
- ),
301
- MCP::Prompt::Message.new(
302
- role: "user",
303
- content: MCP::Content::Text.new("Please process the embedded resource above."),
304
- ),
305
- ],
306
- )
307
- end
308
- end
309
- end
310
-
311
- class TestPromptWithImage < MCP::Prompt
312
- prompt_name "test_prompt_with_image"
313
- description "A prompt with image content for testing"
314
-
315
- class << self
316
- def template(_args, server_context: nil)
317
- MCP::Prompt::Result.new(
318
- messages: [
319
- MCP::Prompt::Message.new(
320
- role: "user",
321
- content: MCP::Content::Image.new(BASE64_1X1_PNG, "image/png"),
322
- ),
323
- MCP::Prompt::Message.new(
324
- role: "user",
325
- content: MCP::Content::Text.new("Please analyze the image above."),
326
- ),
327
- ],
328
- )
329
- end
330
- end
331
- end
332
- end
333
-
334
- class Server
335
- DEFAULT_PORT = 9292
336
-
337
- class DnsRebindingProtection
338
- LOCALHOST_PATTERNS = /\A(localhost|127\.0\.0\.1|\[::1\]|::1)(:\d+)?\z/i.freeze
339
-
340
- def initialize(app)
341
- @app = app
342
- end
343
-
344
- def call(env)
345
- host = env["HTTP_HOST"] || env["SERVER_NAME"] || ""
346
-
347
- unless localhost?(host)
348
- return [
349
- 403,
350
- { "Content-Type" => "application/json" },
351
- [{ error: "Forbidden: DNS rebinding protection - invalid Host header '#{host}'" }.to_json],
352
- ]
353
- end
354
-
355
- origin = env["HTTP_ORIGIN"]
356
- if origin && !origin.empty?
357
- begin
358
- origin_host = URI.parse(origin).host.to_s
359
- unless localhost?(origin_host)
360
- return [
361
- 403,
362
- { "Content-Type" => "application/json" },
363
- [{ error: "Forbidden: DNS rebinding protection - invalid Origin '#{origin}'" }.to_json],
364
- ]
365
- end
366
- rescue URI::InvalidURIError
367
- return [
368
- 403,
369
- { "Content-Type" => "application/json" },
370
- [{ error: "Forbidden: invalid Origin header" }.to_json],
371
- ]
372
- end
373
- end
374
-
375
- @app.call(env)
376
- end
377
-
378
- private
379
-
380
- def localhost?(host)
381
- host.empty? || host.match?(LOCALHOST_PATTERNS)
382
- end
383
- end
384
-
385
- def initialize(port: DEFAULT_PORT)
386
- @port = port
387
- end
388
-
389
- def start
390
- server = build_server
391
- transport = build_transport(server)
392
- configure_handlers(server)
393
- rack_app = build_rack_app(transport)
394
-
395
- puts <<~MESSAGE
396
- MCP Conformance Server starting on http://localhost:#{@port}/mcp
397
- Use Ctrl-C to stop
398
- MESSAGE
399
-
400
- Rackup::Handler.get("puma").run(rack_app, Port: @port, Host: "localhost", Silent: true)
401
- end
402
-
403
- private
404
-
405
- def build_server
406
- MCP::Server.new(
407
- name: "ruby-sdk-conformance-server",
408
- version: MCP::VERSION,
409
- tools: [
410
- Tools::TestSimpleText,
411
- Tools::TestImageContent,
412
- Tools::TestAudioContent,
413
- Tools::TestEmbeddedResource,
414
- Tools::TestMultipleContentTypes,
415
- Tools::TestErrorHandling,
416
- Tools::JsonSchema202012Tool,
417
- Tools::TestToolWithLogging,
418
- Tools::TestToolWithProgress,
419
- Tools::TestSampling,
420
- Tools::TestElicitation,
421
- Tools::TestElicitationSep1034Defaults,
422
- Tools::TestElicitationSep1330Enums,
423
- Tools::TestReconnection,
424
- ],
425
- prompts: [
426
- Prompts::TestSimplePrompt,
427
- Prompts::TestPromptWithArguments,
428
- Prompts::TestPromptWithEmbeddedResource,
429
- Prompts::TestPromptWithImage,
430
- ],
431
- resources: resources,
432
- resource_templates: resource_templates,
433
- capabilities: {
434
- tools: { listChanged: true },
435
- prompts: { listChanged: true },
436
- resources: { listChanged: true, subscribe: true },
437
- logging: {},
438
- completions: {},
439
- },
440
- )
441
- end
442
-
443
- def resources
444
- [
445
- MCP::Resource.new(
446
- uri: "test://static-text",
447
- name: "static-text",
448
- description: "A static text resource for testing",
449
- mime_type: "text/plain",
450
- ),
451
- MCP::Resource.new(
452
- uri: "test://static-binary",
453
- name: "static-binary",
454
- description: "A static binary (PNG) resource for testing",
455
- mime_type: "image/png",
456
- ),
457
- MCP::Resource.new(
458
- uri: "test://watched-resource",
459
- name: "watched-resource",
460
- description: "A resource for subscription testing",
461
- mime_type: "text/plain",
462
- ),
463
- ]
464
- end
465
-
466
- def resource_templates
467
- [
468
- MCP::ResourceTemplate.new(
469
- uri_template: "test://template/{id}/data",
470
- name: "template-resource",
471
- description: "A parameterized resource template for testing",
472
- mime_type: "application/json",
473
- ),
474
- ]
475
- end
476
-
477
- def build_transport(server)
478
- transport = MCP::Server::Transports::StreamableHTTPTransport.new(server)
479
- server.transport = transport
480
- transport
481
- end
482
-
483
- def configure_handlers(server)
484
- server.logging_message_notification = MCP::LoggingMessageNotification.new(level: "debug")
485
- server.server_context = server
486
-
487
- configure_resources_read_handler(server)
488
- end
489
-
490
- def configure_resources_read_handler(server)
491
- server.resources_read_handler do |params|
492
- uri = params[:uri].to_s
493
-
494
- case uri
495
- when "test://static-text"
496
- [
497
- MCP::Resource::TextContents.new(
498
- text: "This is the content of the static text resource.",
499
- uri: uri,
500
- mime_type: "text/plain",
501
- ).to_h,
502
- ]
503
- when "test://static-binary"
504
- [
505
- MCP::Resource::BlobContents.new(
506
- data: BASE64_1X1_PNG,
507
- uri: uri,
508
- mime_type: "image/png",
509
- ).to_h,
510
- ]
511
- when %r{\Atest://template/(.+)/data\z}
512
- id = Regexp.last_match(1)
513
- content = { id: id, templateTest: true, data: "Data for ID: #{id}" }.to_json
514
-
515
- [
516
- MCP::Resource::TextContents.new(
517
- text: content,
518
- uri: uri,
519
- mime_type: "application/json",
520
- ).to_h,
521
- ]
522
- else
523
- []
524
- end
525
- end
526
- end
527
-
528
- def build_rack_app(transport)
529
- mcp_app = proc do |env|
530
- request = Rack::Request.new(env)
531
-
532
- if request.path_info == "/health"
533
- [200, { "Content-Type" => "application/json" }, ['{"status":"ok"}']]
534
- elsif request.path_info == "/mcp" || request.path_info == "/"
535
- transport.handle_request(request)
536
- else
537
- [404, { "Content-Type" => "application/json" }, ['{"error":"Not found"}']]
538
- end
539
- end
540
-
541
- Rack::Builder.new do
542
- use(DnsRebindingProtection)
543
- run(mcp_app)
544
- end
545
- end
546
- end
547
- end
data/dev.yml DELETED
@@ -1,30 +0,0 @@
1
- name: mcp-ruby
2
-
3
- type: ruby
4
-
5
- up:
6
- - ruby
7
- - bundler
8
-
9
- commands:
10
- console:
11
- desc: Open console with the gem loaded
12
- run: bin/console
13
- build:
14
- desc: Build the gem using rake build
15
- run: bin/rake build
16
- test:
17
- desc: Run tests
18
- syntax:
19
- argument: file
20
- optional: args...
21
- run: |
22
- if [[ $# -eq 0 ]]; then
23
- bin/rake test
24
- else
25
- bin/rake -I test "$@"
26
- fi
27
- style:
28
- desc: Run rubocop
29
- aliases: [rubocop, lint]
30
- run: bin/rake rubocop
data/docs/_config.yml DELETED
@@ -1,6 +0,0 @@
1
- # Use package name as site title
2
- title: "MCP Ruby SDK"
3
-
4
- # Include generated files and directories which may start with underscores
5
- include:
6
- - "_*"
data/docs/index.md DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- # Empty Jekyll front matter to enable Liquid templating (see {{ ... }} below)
3
- ---
4
-
5
- {% for version in site.data.versions -%}
6
- - [v{{ version }}](https://rubydoc.info/gems/mcp/{{ version }})
7
- {% endfor %}