cf-mcp 0.19.0 → 0.20.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8503e2b733dd80a28ab4e44b5435ce8ca334ba25a0bd34a3751c2b986beea8dc
4
- data.tar.gz: f4b986ddb96f77b7bc97c5b56384b6aaae88ee73f1f030a184409e64c78cd317
3
+ metadata.gz: aa1835b909429755f2b06a50b6db8d973c68445f879413974b7b28863b6bce06
4
+ data.tar.gz: 6a110d101e27e125763506715bed2a92b973e9cecd709bd17a69c7088fb5e55f
5
5
  SHA512:
6
- metadata.gz: e408e0273973b658e8eb883946d350b25a899d901e5f0940bbb4e9cec4e64e3d21287461fdf17141ba21b33e9b03016d4375635115b490b143476c0a5e142554
7
- data.tar.gz: 7878980a0bb69c7038055106a803ee68032840ffb9fb37c684ddc99353797c133133c1485405f8342ae98052d974b7771d2e17258cf1474e27df1b90592a6dcf
6
+ metadata.gz: e450154e426288754d99ee28a63e4b3fa99d2394600ab8519df3f0d584844b2428f64a1c62d106053f3f3826ace1d4e4beeadd6d9f45e0100b2d7bcc3717db69
7
+ data.tar.gz: 7fd889d1f4ae36abed48cb84048efa8debf22a4c97d198c2d12745ad095589a3ab02feeb5c2c1d4d3221f33a38069c363214f3142b7c56bcbf0c30e04d790f54
data/CHANGELOG.md CHANGED
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [0.20.0] - 2026-09-19
11
+
12
+ ### Added
13
+
14
+ - `CF_MCP_ALLOWED_HOSTS` environment variable (comma-separated) to allow extra `Host` header values on the HTTP endpoint; set to `cf-mcp.fly.dev` in `fly.toml`
15
+ - Support MCP protocol version 2026-07-28: stateless requests carrying their version in `_meta`, and the `server/discover` method
16
+ - Allow the `MCP-Protocol-Version`, `Mcp-Method` and `Mcp-Name` headers in CORS preflight responses
17
+
18
+ ### Changed
19
+
20
+ - **BREAKING:** Raised the minimum supported Ruby from 3.2 to 3.3 (`required_ruby_version` and the Standard Ruby target); 3.2 reached end of life on 2026-03-31; CI now also runs against Ruby 3.3
21
+ - Updated `mcp` from 0.25.0 to 1.5.1
22
+ - Updated `puma` from 7.2.1 to 8.0.2 (the gemspec constraint is now `~> 8.0`) and `rubyzip` from 3.6.0 to 3.7.0
23
+ - Removed the `2025-03-26` protocol pin; clients using the `initialize` handshake now negotiate up to 2025-11-25
24
+ - The web UI now calls tools through the stateless 2026-07-28 protocol
25
+ - The web UI shows both the modern and the legacy handshake protocol versions
26
+ - Bump Ruby to 4.0.7 in `.ruby-version`, the Dockerfile, and the CI matrix
27
+ - Bumped GitHub Actions: `actions/checkout` to v7 and `actions/cache` to v6
28
+ - Added least-privilege permissions, PR concurrency cancellation and a job timeout to the CI workflow, and Dependabot for GitHub Actions and Bundler
29
+
30
+ ### Fixed
31
+
32
+ - HTTP endpoint rejected all non-loopback requests with `403 Invalid Host header` since `mcp` 0.25 enabled DNS-rebinding protection, breaking the hosted server
33
+
8
34
  ## [0.19.0] - 2026-09-15
9
35
 
10
36
  ### Added
@@ -306,6 +332,8 @@ This version was prepared but never released. Changes rolled into 0.17.2.
306
332
  - `cf_list_category` - List items by category
307
333
  - `cf_get_details` - Get full documentation by name
308
334
 
335
+ [Unreleased]: https://github.com/pusewicz/cf-mcp/compare/v0.20.0...HEAD
336
+ [0.20.0]: https://github.com/pusewicz/cf-mcp/compare/v0.19.0...v0.20.0
309
337
  [0.19.0]: https://github.com/pusewicz/cf-mcp/compare/v0.18.0...v0.19.0
310
338
  [0.17.2]: https://github.com/pusewicz/cf-mcp/compare/v0.17.0...v0.17.2
311
339
  [0.17.1]: https://github.com/pusewicz/cf-mcp/compare/v0.17.0...v0.17.1
data/README.md CHANGED
@@ -63,6 +63,12 @@ Then launch the inspector and connect using Streamable HTTP transport to:
63
63
  http://localhost:9292/http
64
64
  ```
65
65
 
66
+ When serving under a non-loopback hostname, allow it via `CF_MCP_ALLOWED_HOSTS` (comma-separated), otherwise requests are rejected with `403 Invalid Host header`:
67
+
68
+ ```bash
69
+ CF_MCP_ALLOWED_HOSTS=mcp.example.com cf-mcp http --root /path/to/cute_framework
70
+ ```
71
+
66
72
  ## Development
67
73
 
68
74
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/lib/cf/mcp/server.rb CHANGED
@@ -12,7 +12,7 @@ module CF
12
12
  CORS_HEADERS = {
13
13
  "access-control-allow-origin" => "*",
14
14
  "access-control-allow-methods" => "GET, POST, DELETE, OPTIONS",
15
- "access-control-allow-headers" => "Content-Type, Accept, Mcp-Session-Id",
15
+ "access-control-allow-headers" => "Content-Type, Accept, MCP-Protocol-Version, Mcp-Method, Mcp-Name, Mcp-Session-Id",
16
16
  "access-control-expose-headers" => "Mcp-Session-Id"
17
17
  }.freeze
18
18
 
@@ -37,18 +37,27 @@ module CF
37
37
  new(index, revision: builder.revision).rack_app
38
38
  end
39
39
 
40
- PROTOCOL_VERSION = "2025-03-26"
40
+ # Modern clients carry their version on every request (no handshake); legacy
41
+ # clients negotiate through `initialize`. Both track the SDK rather than pinning.
42
+ MODERN_PROTOCOL_VERSION = ::MCP::Configuration::LATEST_MODERN_PROTOCOL_VERSION
43
+ LEGACY_PROTOCOL_VERSION = ::MCP::Configuration::LATEST_HANDSHAKE_PROTOCOL_VERSION
41
44
  WEBSITE_URL = ENV.fetch("FLY_APP_NAME", nil) ? "https://#{ENV["FLY_APP_NAME"]}.fly.dev" : "https://cf-mcp.fly.dev"
42
45
  PUBLIC_DIR = File.join(__dir__, "public")
46
+ ALLOWED_HOSTS_ENV = "CF_MCP_ALLOWED_HOSTS"
43
47
 
44
- def initialize(index, revision: nil)
48
+ # Comma-separated list of extra Host header values the HTTP transport accepts
49
+ # (the MCP SDK only allows loopback hosts by default).
50
+ def self.allowed_hosts_from_env(env = ENV)
51
+ env.fetch(ALLOWED_HOSTS_ENV, "").split(",").map(&:strip).reject(&:empty?)
52
+ end
53
+
54
+ def initialize(index, revision: nil, allowed_hosts: self.class.allowed_hosts_from_env)
45
55
  @index = index
46
56
  @revision = revision
57
+ @allowed_hosts = allowed_hosts
47
58
 
48
- configuration = ::MCP::Configuration.new(protocol_version: PROTOCOL_VERSION)
49
59
  @server = ::MCP::Server.new(
50
60
  name: "cf-mcp",
51
- configuration:,
52
61
  version: CF::MCP::VERSION,
53
62
  icons: [
54
63
  ::MCP::Icon.new(src: "#{WEBSITE_URL}/favicon.svg", mime_type: "image/svg+xml", sizes: ["any"]),
@@ -82,7 +91,7 @@ module CF
82
91
  def rack_app
83
92
  require "rack"
84
93
 
85
- http_transport = ::MCP::Server::Transports::StreamableHTTPTransport.new(@server, stateless: true)
94
+ http_transport = ::MCP::Server::Transports::StreamableHTTPTransport.new(@server, stateless: true, allowed_hosts: @allowed_hosts)
86
95
  @server.transport = http_transport
87
96
 
88
97
  landing_page = build_landing_page
@@ -193,7 +202,8 @@ module CF
193
202
  ->(index, tool_classes) {
194
203
  context = TemplateContext.new(
195
204
  version: CF::MCP::VERSION,
196
- protocol_version: PROTOCOL_VERSION,
205
+ modern_protocol_version: MODERN_PROTOCOL_VERSION,
206
+ legacy_protocol_version: LEGACY_PROTOCOL_VERSION,
197
207
  revision: @revision,
198
208
  revision_url: @revision && format(CUTE_FRAMEWORK_COMMIT_URL_TEMPLATE, revision: @revision),
199
209
  stats: index.stats,
@@ -219,11 +229,12 @@ module CF
219
229
  class TemplateContext
220
230
  TEMPLATES_DIR = File.join(__dir__, "templates")
221
231
 
222
- attr_reader :version, :protocol_version, :revision, :revision_url, :stats, :categories, :topics, :tools, :tool_schemas_json
232
+ attr_reader :version, :modern_protocol_version, :legacy_protocol_version, :revision, :revision_url, :stats, :categories, :topics, :tools, :tool_schemas_json
223
233
 
224
- def initialize(version:, protocol_version:, revision:, revision_url:, stats:, categories:, topics:, tools:, tool_schemas_json:)
234
+ def initialize(version:, modern_protocol_version:, legacy_protocol_version:, revision:, revision_url:, stats:, categories:, topics:, tools:, tool_schemas_json:)
225
235
  @version = version
226
- @protocol_version = protocol_version
236
+ @modern_protocol_version = modern_protocol_version
237
+ @legacy_protocol_version = legacy_protocol_version
227
238
  @revision = revision
228
239
  @revision_url = revision_url
229
240
  @stats = stats
@@ -256,7 +267,10 @@ module CF
256
267
 
257
268
  def js_content
258
269
  js = File.read(File.join(TEMPLATES_DIR, "script.js"))
259
- js.sub("TOOL_SCHEMAS_PLACEHOLDER", @tool_schemas_json)
270
+ # Substitute the scalar placeholders first so injected content can't collide with them
271
+ js.sub("PROTOCOL_VERSION_PLACEHOLDER", @modern_protocol_version.to_json)
272
+ .sub("CLIENT_VERSION_PLACEHOLDER", @version.to_json)
273
+ .sub("TOOL_SCHEMAS_PLACEHOLDER", @tool_schemas_json)
260
274
  .sub("CATEGORIES_PLACEHOLDER", categories_json)
261
275
  .sub("TOPICS_PLACEHOLDER", topics_json)
262
276
  .sub("CHANGELOG_PLACEHOLDER", changelog_json)
@@ -53,7 +53,7 @@
53
53
  </tr>
54
54
  <tr>
55
55
  <th>Protocol Version</th>
56
- <td><%= protocol_version %></td>
56
+ <td><%= modern_protocol_version %> <small>(legacy handshake up to <%= legacy_protocol_version %>)</small></td>
57
57
  </tr>
58
58
  <% if revision %>
59
59
  <tr>
@@ -8,6 +8,8 @@ const TOOLS = TOOL_SCHEMAS_PLACEHOLDER;
8
8
  const CATEGORIES = CATEGORIES_PLACEHOLDER;
9
9
  const TOPICS = TOPICS_PLACEHOLDER;
10
10
  const CHANGELOG = CHANGELOG_PLACEHOLDER;
11
+ const PROTOCOL_VERSION = PROTOCOL_VERSION_PLACEHOLDER;
12
+ const CLIENT_VERSION = CLIENT_VERSION_PLACEHOLDER;
11
13
 
12
14
  // Helper to render topic options (used in multiple places)
13
15
  const renderTopicOptions = () =>
@@ -38,13 +40,24 @@ function useMcpToolCall() {
38
40
  method: 'POST',
39
41
  headers: {
40
42
  'Content-Type': 'application/json',
41
- 'Accept': 'application/json, text/event-stream'
43
+ 'Accept': 'application/json, text/event-stream',
44
+ 'MCP-Protocol-Version': PROTOCOL_VERSION,
45
+ 'Mcp-Method': 'tools/call',
46
+ 'Mcp-Name': toolName
42
47
  },
43
48
  body: JSON.stringify({
44
49
  jsonrpc: '2.0',
45
50
  id: Date.now(),
46
51
  method: 'tools/call',
47
- params: { name: toolName, arguments: args }
52
+ params: {
53
+ name: toolName,
54
+ arguments: args,
55
+ _meta: {
56
+ 'io.modelcontextprotocol/protocolVersion': PROTOCOL_VERSION,
57
+ 'io.modelcontextprotocol/clientCapabilities': {},
58
+ 'io.modelcontextprotocol/clientInfo': { name: 'cf-mcp-dashboard', version: CLIENT_VERSION }
59
+ }
60
+ }
48
61
  })
49
62
  });
50
63
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module CF
4
4
  module MCP
5
- VERSION = "0.19.0"
5
+ VERSION = "0.20.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cf-mcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Usewicz
@@ -29,28 +29,28 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '0.25'
32
+ version: '1.5'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '0.25'
39
+ version: '1.5'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: puma
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '7.1'
46
+ version: '8.0'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '7.1'
53
+ version: '8.0'
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: rack
56
56
  requirement: !ruby/object:Gem::Requirement
@@ -161,14 +161,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
161
161
  requirements:
162
162
  - - ">="
163
163
  - !ruby/object:Gem::Version
164
- version: 3.2.0
164
+ version: 3.3.0
165
165
  required_rubygems_version: !ruby/object:Gem::Requirement
166
166
  requirements:
167
167
  - - ">="
168
168
  - !ruby/object:Gem::Version
169
169
  version: '0'
170
170
  requirements: []
171
- rubygems_version: 4.0.12
171
+ rubygems_version: 4.0.20
172
172
  specification_version: 4
173
173
  summary: MCP server providing documentation tools for Cute Framework
174
174
  test_files: []