From 92c33f313c6e1428e2b05a0cc84c14179b04727a Mon Sep 17 00:00:00 2001 From: mhenrixon Date: Sat, 4 Jul 2026 10:16:37 +0200 Subject: [PATCH] fix(generator): don't re-inject the MCP route scaffold over a site's live routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add_mcp_route drew the commented /mcp scaffold with plain Thor `route`, which only skips byte-identical lines — a site that opted in has LIVE routes in its own style (`, as: :mcp`, single quotes) that never match, so every --sync run re-injected the commented noise. Guard on the endpoint via route_present? instead (matches docs_kit/mcp#create in any form, live or commented). Found dogfooding 1.0.3 into pgbus + phlex-reactive — both hit it. Claude-Session: https://claude.ai/code/session_01FPQb6z3YwcKRMbvoJhdxnX --- .../docs_kit/install/install_generator.rb | 10 ++++++++++ spec/generators/install_generator_spec.rb | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/generators/docs_kit/install/install_generator.rb b/lib/generators/docs_kit/install/install_generator.rb index 054fa39..d8771e7 100644 --- a/lib/generators/docs_kit/install/install_generator.rb +++ b/lib/generators/docs_kit/install/install_generator.rb @@ -150,7 +150,17 @@ def add_routes # bundled. A site opts in by adding `gem "mcp"` and uncommenting these. POST # speaks JSON-RPC; GET/DELETE 405 (read-only, stateless — no SSE session). # `route` prepends, so drawing `match` before `post` leaves `post` on top. + # + # Guarded on the endpoint (route_present?), NOT on Thor's byte-identical + # skip: a site that opted in has LIVE routes in its own style (an + # `, as: :mcp` suffix, single quotes) which never byte-match the commented + # template — plain `route` would re-inject the scaffold on every --sync. + # Found dogfooding 1.0.3 into pgbus + phlex-reactive. def add_mcp_route + if route_present?(%(post "/mcp" => "docs_kit/mcp#create")) + return say_status(:identical, "route /mcp (already drawn or scaffolded)", :blue) + end + route %(# match "/mcp" => "docs_kit/mcp#method_not_allowed", via: %i[get delete]) route %(# post "/mcp" => "docs_kit/mcp#create") route %(# Add your docs to an agent over MCP (needs `gem "mcp"`):) diff --git a/spec/generators/install_generator_spec.rb b/spec/generators/install_generator_spec.rb index f7b9914..47fde59 100644 --- a/spec/generators/install_generator_spec.rb +++ b/spec/generators/install_generator_spec.rb @@ -488,6 +488,25 @@ def capture_stream expect(routes).to include(%(# match "/mcp" => "docs_kit/mcp#method_not_allowed", via: %i[get delete])) end + it "does not re-inject the commented MCP scaffold when the site has LIVE /mcp routes" do + # A site that opted in (uncommented, in its own style — an `, as: :mcp` + # suffix) must not accumulate the commented scaffold on every --sync run. + # Found dogfooding 1.0.3 into pgbus + phlex-reactive. + write("config/routes.rb", <<~ROUTES) + Rails.application.routes.draw do + post "/mcp" => "docs_kit/mcp#create", as: :mcp + match "/mcp" => "docs_kit/mcp#method_not_allowed", via: %i[get delete] + end + ROUTES + + run_generator(sync: true) + + routes = read("config/routes.rb") + expect(routes).not_to include(%(# post "/mcp")) + expect(routes).not_to include(%(# match "/mcp")) + expect(routes.scan(%r{docs_kit/mcp#create}).size).to eq(1) + end + it "draws /docs/search ABOVE docs/:doc so it isn't swallowed as :doc" do routes = read("config/routes.rb")