mcp 0.8.0 → 0.10.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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +176 -5
  3. data/lib/mcp/client/stdio.rb +222 -0
  4. data/lib/mcp/client.rb +21 -3
  5. data/lib/mcp/progress.rb +22 -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 +6 -4
  9. data/lib/mcp/server/transports/streamable_http_transport.rb +140 -31
  10. data/lib/mcp/server/transports.rb +10 -0
  11. data/lib/mcp/server.rb +71 -39
  12. data/lib/mcp/server_context.rb +44 -0
  13. data/lib/mcp/server_session.rb +79 -0
  14. data/lib/mcp/tool.rb +5 -0
  15. data/lib/mcp/transport.rb +2 -2
  16. data/lib/mcp/version.rb +1 -1
  17. data/lib/mcp.rb +11 -24
  18. metadata +8 -36
  19. data/.gitattributes +0 -4
  20. data/.github/dependabot.yml +0 -6
  21. data/.github/workflows/ci.yml +0 -54
  22. data/.github/workflows/conformance.yml +0 -29
  23. data/.github/workflows/release.yml +0 -57
  24. data/.gitignore +0 -11
  25. data/.rubocop.yml +0 -15
  26. data/AGENTS.md +0 -107
  27. data/CHANGELOG.md +0 -168
  28. data/CODE_OF_CONDUCT.md +0 -74
  29. data/Gemfile +0 -29
  30. data/RELEASE.md +0 -12
  31. data/Rakefile +0 -56
  32. data/SECURITY.md +0 -21
  33. data/bin/console +0 -15
  34. data/bin/generate-gh-pages.sh +0 -119
  35. data/bin/rake +0 -31
  36. data/bin/setup +0 -8
  37. data/conformance/README.md +0 -103
  38. data/conformance/expected_failures.yml +0 -9
  39. data/conformance/runner.rb +0 -101
  40. data/conformance/server.rb +0 -547
  41. data/dev.yml +0 -30
  42. data/docs/_config.yml +0 -6
  43. data/docs/index.md +0 -7
  44. data/docs/latest/index.html +0 -19
  45. data/examples/README.md +0 -197
  46. data/examples/http_client.rb +0 -184
  47. data/examples/http_server.rb +0 -169
  48. data/examples/stdio_server.rb +0 -94
  49. data/examples/streamable_http_client.rb +0 -207
  50. data/examples/streamable_http_server.rb +0 -172
  51. data/mcp.gemspec +0 -35
data/CODE_OF_CONDUCT.md DELETED
@@ -1,74 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- In the interest of fostering an open and welcoming environment, we as
6
- contributors and maintainers pledge to making participation in our project and
7
- our community a harassment-free experience for everyone, regardless of age, body
8
- size, disability, ethnicity, gender identity and expression, level of experience,
9
- nationality, personal appearance, race, religion, or sexual identity and
10
- orientation.
11
-
12
- ## Our Standards
13
-
14
- Examples of behavior that contributes to creating a positive environment
15
- include:
16
-
17
- * Using welcoming and inclusive language
18
- * Being respectful of differing viewpoints and experiences
19
- * Gracefully accepting constructive criticism
20
- * Focusing on what is best for the community
21
- * Showing empathy towards other community members
22
-
23
- Examples of unacceptable behavior by participants include:
24
-
25
- * The use of sexualized language or imagery and unwelcome sexual attention or
26
- advances
27
- * Trolling, insulting/derogatory comments, and personal or political attacks
28
- * Public or private harassment
29
- * Publishing others' private information, such as a physical or electronic
30
- address, without explicit permission
31
- * Other conduct which could reasonably be considered inappropriate in a
32
- professional setting
33
-
34
- ## Our Responsibilities
35
-
36
- Project maintainers are responsible for clarifying the standards of acceptable
37
- behavior and are expected to take appropriate and fair corrective action in
38
- response to any instances of unacceptable behavior.
39
-
40
- Project maintainers have the right and responsibility to remove, edit, or
41
- reject comments, commits, code, wiki edits, issues, and other contributions
42
- that are not aligned to this Code of Conduct, or to ban temporarily or
43
- permanently any contributor for other behaviors that they deem inappropriate,
44
- threatening, offensive, or harmful.
45
-
46
- ## Scope
47
-
48
- This Code of Conduct applies both within project spaces and in public spaces
49
- when an individual is representing the project or its community. Examples of
50
- representing a project or community include using an official project e-mail
51
- address, posting via an official social media account, or acting as an appointed
52
- representative at an online or offline event. Representation of a project may be
53
- further defined and clarified by project maintainers.
54
-
55
- ## Enforcement
56
-
57
- Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
- reported by contacting the project team at topher.bullock@shopify.com. All
59
- complaints will be reviewed and investigated and will result in a response that
60
- is deemed necessary and appropriate to the circumstances. The project team is
61
- obligated to maintain confidentiality with regard to the reporter of an incident.
62
- Further details of specific enforcement policies may be posted separately.
63
-
64
- Project maintainers who do not follow or enforce the Code of Conduct in good
65
- faith may face temporary or permanent repercussions as determined by other
66
- members of the project's leadership.
67
-
68
- ## Attribution
69
-
70
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
- available at [http://contributor-covenant.org/version/1/4][version]
72
-
73
- [homepage]: http://contributor-covenant.org
74
- [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile DELETED
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source "https://rubygems.org"
4
-
5
- # Specify runtime dependencies in the gemspec
6
- gemspec
7
-
8
- # Specify development dependencies below
9
- gem "rubocop-minitest", require: false
10
- gem "rubocop-rake", require: false
11
- gem "rubocop-shopify", ">= 2.18", require: false if RUBY_VERSION >= "3.1"
12
-
13
- gem "puma", ">= 5.0.0"
14
- gem "rackup", ">= 2.1.0"
15
-
16
- gem "activesupport"
17
- # Fix io-console install error when Ruby 3.0.
18
- gem "debug" if RUBY_VERSION >= "3.1"
19
- gem "rake", "~> 13.0"
20
- gem "sorbet-static-and-runtime" if RUBY_VERSION >= "3.0"
21
- gem "yard", "~> 0.9"
22
- gem "yard-sorbet", "~> 0.9" if RUBY_VERSION >= "3.1"
23
-
24
- group :test do
25
- gem "faraday", ">= 2.0"
26
- gem "minitest", "~> 5.1", require: false
27
- gem "mocha"
28
- gem "webmock"
29
- end
data/RELEASE.md DELETED
@@ -1,12 +0,0 @@
1
- ## Releases
2
-
3
- This gem is published to [RubyGems.org](https://rubygems.org/gems/mcp)
4
-
5
- Releases are triggered by PRs to the `main` branch updating the version number in `lib/mcp/version.rb`.
6
-
7
- 1. **Update the version number** in `lib/mcp/version.rb`, following [semver](https://semver.org/)
8
- 2. **Update CHANGELOG.md**, backfilling the changes since the last release if necessary, and adding a new section for the new version, clearing out the Unreleased section
9
- 3. **Create a PR and get approval from a maintainer**
10
- 4. **Merge your PR to the main branch** - This will automatically trigger the release workflow via GitHub Actions
11
-
12
- When changes are merged to the `main` branch, the GitHub Actions workflow (`.github/workflows/release.yml`) is triggered and the gem is published to RubyGems.
data/Rakefile DELETED
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rake/testtask"
5
-
6
- Rake::TestTask.new(:test) do |t|
7
- t.ruby_opts = ["-W0", "-W:deprecated"]
8
- t.libs << "test"
9
- t.libs << "lib"
10
- t.test_files = FileList["test/**/*_test.rb"]
11
- end
12
-
13
- require "rubocop/rake_task"
14
-
15
- RuboCop::RakeTask.new
16
-
17
- task default: [:rubocop, :test, :conformance]
18
-
19
- desc "Run MCP conformance tests (PORT, SCENARIO, SPEC_VERSION, VERBOSE)"
20
- task :conformance do |t|
21
- next unless npx_available?(t.name)
22
-
23
- require_relative "conformance/runner"
24
-
25
- options = {}
26
- options[:port] = Integer(ENV["PORT"]) if ENV["PORT"]
27
- options[:scenario] = ENV["SCENARIO"] if ENV["SCENARIO"]
28
- options[:spec_version] = ENV["SPEC_VERSION"] if ENV["SPEC_VERSION"]
29
- options[:verbose] = true if ENV["VERBOSE"]
30
-
31
- Conformance::Runner.new(**options).run
32
- end
33
-
34
- desc "List available conformance scenarios"
35
- task :conformance_list do |t|
36
- next unless npx_available?(t.name)
37
-
38
- system("npx", "--yes", "@modelcontextprotocol/conformance", "list", "--server")
39
- end
40
-
41
- desc "Start the conformance server (PORT)"
42
- task :conformance_server do
43
- require_relative "conformance/server"
44
-
45
- options = {}
46
- options[:port] = Integer(ENV["PORT"]) if ENV["PORT"]
47
-
48
- Conformance::Server.new(**options).start
49
- end
50
-
51
- def npx_available?(task_name)
52
- return true if system("which", "npx", out: File::NULL, err: File::NULL)
53
-
54
- warn("Skipping #{task_name}: npx is not installed. Install Node.js to run this task: https://nodejs.org/")
55
- false
56
- end
data/SECURITY.md DELETED
@@ -1,21 +0,0 @@
1
- # Security Policy
2
-
3
- Thank you for helping keep the Model Context Protocol and its ecosystem secure.
4
-
5
- ## Reporting Security Issues
6
-
7
- If you discover a security vulnerability in this repository, please report it through
8
- the [GitHub Security Advisory process](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability)
9
- for this repository.
10
-
11
- Please **do not** report security vulnerabilities through public GitHub issues, discussions,
12
- or pull requests.
13
-
14
- ## What to Include
15
-
16
- To help us triage and respond quickly, please include:
17
-
18
- - A description of the vulnerability
19
- - Steps to reproduce the issue
20
- - The potential impact
21
- - Any suggested fixes (optional)
data/bin/console DELETED
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "bundler/setup"
5
- require "mcp"
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require "irb"
15
- IRB.start(__FILE__)
@@ -1,119 +0,0 @@
1
- #!/bin/bash
2
- set -e
3
-
4
- # Generates versioned documentation links and commits to gh-pages branch
5
- #
6
- # PURPOSE:
7
- # This script generates a landing page with links to API documentation on
8
- # RubyDoc.info for a specific version tag. This script is invoked by the
9
- # publish-gh-pages job in the GitHub Actions workflow
10
- # (.github/workflows/release.yml) when a release is published.
11
- #
12
- # HOW IT WORKS:
13
- # - Creates isolated git worktrees for the specified tag and gh-pages branch
14
- # - Copies static Jekyll template files from docs/
15
- # - Generates _data/versions.yml with list of versions
16
- # - Commits changes to gh-pages (does not push automatically)
17
- #
18
- # WORKFLOW:
19
- # 1. Run this script with a tag name: `generate-gh-pages.sh v1.2.3`
20
- # 2. Script generates docs and commits to local gh-pages branch
21
- # 3. Push gh-pages branch to deploy: `git push origin gh-pages`
22
-
23
- # Parse semantic version from tag name (ignoring arbitrary prefixes)
24
- if [[ "${1}" =~ ([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?)$ ]]; then
25
- VERSION="v${BASH_REMATCH[1]}"
26
- else
27
- echo "Error: Must specify a tag name that contains a valid semantic version"
28
- echo "Usage: ${0} <tag-name>"
29
- echo "Examples:"
30
- echo " ${0} 1.2.3"
31
- echo " ${0} v2.0.0-rc.1"
32
- exit 1
33
- fi
34
-
35
- TAG_NAME="${1}"
36
- REPO_ROOT="$(git rev-parse --show-toplevel)"
37
-
38
- echo "Generating documentation for tag: ${TAG_NAME}"
39
-
40
- # Create temporary directories for both worktrees
41
- WORKTREE_DIR=$(mktemp -d)
42
- GHPAGES_WORKTREE_DIR=$(mktemp -d)
43
-
44
- # Set up trap to clean up both worktrees on exit
45
- trap 'git worktree remove --force "${WORKTREE_DIR}" 2>/dev/null || true; \
46
- git worktree remove --force "${GHPAGES_WORKTREE_DIR}" 2>/dev/null || true' EXIT
47
-
48
- echo "Creating worktree for ${TAG_NAME}..."
49
- git worktree add --quiet "${WORKTREE_DIR}" "${TAG_NAME}"
50
-
51
- # Check if gh-pages branch exists
52
- if git show-ref --verify --quiet refs/heads/gh-pages; then
53
- echo "Creating worktree for existing gh-pages branch..."
54
- git worktree add --quiet "${GHPAGES_WORKTREE_DIR}" gh-pages
55
- elif git ls-remote --exit-code --heads origin gh-pages > /dev/null 2>&1; then
56
- echo "Creating worktree for gh-pages branch from remote..."
57
- git worktree add --quiet "${GHPAGES_WORKTREE_DIR}" -b gh-pages origin/gh-pages
58
- else
59
- echo "Creating worktree for new orphan gh-pages branch..."
60
- git worktree add --quiet --detach "${GHPAGES_WORKTREE_DIR}"
61
- git -C "${GHPAGES_WORKTREE_DIR}" checkout --orphan gh-pages
62
- git -C "${GHPAGES_WORKTREE_DIR}" rm -rf . > /dev/null 2>&1 || true
63
- fi
64
-
65
- # Change to gh-pages worktree
66
- cd "${GHPAGES_WORKTREE_DIR}"
67
-
68
- # Determine if this tag is the latest version
69
- echo "Determining if ${VERSION} is the latest version..."
70
-
71
- # Get all existing version tags from the repository (reverse sorted, newest first)
72
- ALL_VERSIONS=$(
73
- git -C "${REPO_ROOT}" tag --list | \
74
- sed -nE 's/^[^0-9]*([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?)$/v\1/p' | \
75
- sort -Vr
76
- )
77
-
78
- # Get the latest version from all version tags
79
- LATEST_VERSION=$(echo "${ALL_VERSIONS}" | head -n 1)
80
-
81
- if [ "${VERSION}" = "${LATEST_VERSION}" ]; then
82
- echo "${VERSION} is the latest version"
83
- else
84
- echo "${VERSION} is not the latest version (latest is ${LATEST_VERSION})"
85
- fi
86
-
87
- # Update custom documentation for latest version
88
- if [ "${VERSION}" = "${LATEST_VERSION}" ]; then
89
- echo "Updating custom documentation..."
90
-
91
- # Clean up old custom docs from gh-pages root
92
- echo "Cleaning gh-pages root..."
93
- git ls-tree --name-only HEAD | xargs -r git rm -rf
94
-
95
- # Copy custom docs from docs/ directory
96
- echo "Copying custom docs from ${WORKTREE_DIR}/docs/..."
97
- cp -r "${WORKTREE_DIR}/docs/." "${GHPAGES_WORKTREE_DIR}/"
98
- fi
99
-
100
- # Generate version data for Jekyll
101
- echo "Generating _data/versions.yml..."
102
- mkdir -p _data
103
- echo "${ALL_VERSIONS}" | sed 's/^v/- /' > _data/versions.yml
104
-
105
- # Stage all changes
106
- git add .
107
-
108
- # Commit if there are changes
109
- if git diff --staged --quiet; then
110
- echo "No changes to commit"
111
- else
112
- echo "Committing documentation for ${VERSION}..."
113
- git commit -m "Add ${VERSION} docs"
114
-
115
- echo "Documentation committed to gh-pages branch!"
116
- echo "Push to remote to deploy to GitHub Pages"
117
- fi
118
-
119
- echo "Done!"
data/bin/rake DELETED
@@ -1,31 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- #
5
- # This file was generated by Bundler.
6
- #
7
- # The application 'rake' is installed as part of a gem, and
8
- # this file is here to facilitate running it.
9
- #
10
-
11
- require "pathname"
12
- ENV["BUNDLE_GEMFILE"] ||= File.expand_path(
13
- "../../Gemfile",
14
- Pathname.new(__FILE__).realpath,
15
- )
16
-
17
- bundle_binstub = File.expand_path("../bundle", __FILE__)
18
-
19
- if File.file?(bundle_binstub)
20
- if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
21
- load(bundle_binstub)
22
- else
23
- abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
24
- Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
25
- end
26
- end
27
-
28
- require "rubygems"
29
- require "bundler/setup"
30
-
31
- load Gem.bin_path("rake", "rake")
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
@@ -1,103 +0,0 @@
1
- # MCP Conformance Tests
2
-
3
- Validates the Ruby SDK's conformance to the MCP specification using [`@modelcontextprotocol/conformance`](https://github.com/modelcontextprotocol/conformance).
4
-
5
- ## Prerequisites
6
-
7
- - Node.js (for `npx`)
8
- - `bundle install` completed
9
-
10
- ## Running the Tests
11
-
12
- ### Run all scenarios
13
-
14
- ```bash
15
- bundle exec rake conformance
16
- ```
17
-
18
- Starts the conformance server, runs all active scenarios against it, prints a pass/fail
19
- summary for each scenario, and exits with a non-zero status code if any unexpected failures
20
- are detected. Scenarios listed in `expected_failures.yml` are allowed to fail without
21
- affecting the exit code.
22
-
23
- ### Environment variables
24
-
25
- | Variable | Description | Default |
26
- |----------------|--------------------------------------|---------|
27
- | `PORT` | Server port | `9292` |
28
- | `SCENARIO` | Run a single scenario by name | (all) |
29
- | `SPEC_VERSION` | Filter scenarios by spec version | (all) |
30
- | `VERBOSE` | Show raw JSON output when set | (off) |
31
-
32
- ```bash
33
- # Run a single scenario
34
- bundle exec rake conformance SCENARIO=ping
35
-
36
- # Use a different port with verbose output
37
- bundle exec rake conformance PORT=3000 VERBOSE=1
38
-
39
- # Start the server on a specific port
40
- bundle exec rake conformance_server PORT=3000
41
- ```
42
-
43
- ### Start the server and test separately
44
-
45
- ```bash
46
- # Terminal 1: start the server
47
- bundle exec rake conformance_server
48
-
49
- # Terminal 2: run all scenarios
50
- npx @modelcontextprotocol/conformance server --url http://localhost:9292/mcp
51
-
52
- # Terminal 2: run a single scenario
53
- npx @modelcontextprotocol/conformance server --url http://localhost:9292/mcp --scenario ping
54
- ```
55
-
56
- Keeps the server alive between test runs, which avoids the startup overhead when iterating
57
- on a single scenario. Stop the server with Ctrl+C when done.
58
-
59
- ### List available scenarios
60
-
61
- ```bash
62
- bundle exec rake conformance_list
63
- ```
64
-
65
- Prints all scenario names that can be passed to `SCENARIO`.
66
-
67
- ## SDK Tier Report
68
-
69
- The [MCP SDK Tier system](https://modelcontextprotocol.io/community/sdk-tiers) requires SDK
70
- maintainers to self-assess and report results to the SDK Working Group via
71
- [modelcontextprotocol/modelcontextprotocol issues](https://github.com/modelcontextprotocol/modelcontextprotocol/issues).
72
-
73
- To generate a full tier assessment report, use the `/mcp-sdk-tier-audit` slash command from
74
- the [modelcontextprotocol/conformance](https://github.com/modelcontextprotocol/conformance)
75
- repository with the conformance server running:
76
-
77
- ```bash
78
- # Terminal 1 (this repository): start the conformance server
79
- bundle exec rake conformance_server
80
-
81
- # Terminal 2 (conformance repository): run the tier audit skill as a slash command in Claude Code
82
- /mcp-sdk-tier-audit /path/to/modelcontextprotocol/ruby-sdk http://localhost:9292/mcp
83
- ```
84
-
85
- The skill evaluates conformance pass rate, issue label taxonomy, triage metrics, documentation
86
- coverage, and policy compliance, then produces a markdown report suitable for tier advancement
87
- submissions.
88
-
89
- ## File Structure
90
-
91
- ```
92
- conformance/
93
- server.rb # Conformance server (Rack + Puma, default port 9292)
94
- runner.rb # Starts the server, runs npx conformance, exits with result code
95
- expected_failures.yml # Baseline of known-failing scenarios
96
- README.md # This file
97
- ```
98
-
99
- ## Known Limitations
100
-
101
- Known-failing scenarios are registered in `conformance/expected_failures.yml`. They are allowed to
102
- fail without affecting the exit code and are tracked to catch regressions.
103
- These are shown in the output of `bundle exec rake conformance`.
@@ -1,9 +0,0 @@
1
- server:
2
- # TODO: Server-to-client requests (sampling/createMessage, elicitation/create) are not implemented.
3
- # `Transport#send_request` does not exist in the current SDK.
4
- - tools-call-sampling
5
- - tools-call-elicitation
6
- - elicitation-sep1034-defaults
7
- - elicitation-sep1330-enums
8
- # TODO: The SDK does not extract `_meta.progressToken` from tool call requests or deliver `notifications/progress` to tools.
9
- - tools-call-with-progress
@@ -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