Skip to content

Commit 9cd068c

Browse files
authored
feat(cli): canonicalize lucli stdio mcp, deprecate in-dev-server http mcp (#2140)
* feat(cli): canonicalize lucli stdio mcp in templates and cli setup Updates all four .mcp.json / .opencode.json templates shipped with the Wheels CLI to the stdio configuration: {"mcpServers":{"wheels":{"command":"wheels","args":["mcp","wheels"]}}} Templates touched: - cli/src/templates/McpConfig.json (wheels mcp setup output) - app/snippets/McpConfig.json (new-app scaffold snippet) - tools/build/base/.mcp.json (base build template) - tools/build/base/.opencode.json (base build OpenCode template) Also simplifies 'wheels mcp setup' — port detection, noAutoStart, and {PORT} template interpolation are no longer needed for stdio transport. Users can now run 'wheels mcp setup' or 'wheels mcp setup --force' and get a working config with no runtime state inspection. Module.cfc's mcp() subcommand now shows the 'wheels' binary name (not 'lucli') in the sample config, matching how end users actually invoke the CLI. * chore(config): deprecate in-dev-server mcp endpoint The HTTP MCP endpoint at /wheels/mcp (mounted from vendor/wheels/public/views/mcp.cfm backed by McpServer.cfc) is superseded by the LuCLI stdio MCP (wheels mcp wheels). Keep it alive for this release as a compatibility shim but signal deprecation: - mcp.cfm logs a one-time per-JVM deprecation warning to application wheels_mcp log on first request - McpServer.cfc serverInfo now advertises deprecated=true and a deprecationNotice string so MCP clients that inspect serverInfo can surface a migration banner to the user Removal is scheduled for a future release once 'wheels mcp setup' has landed in a released Wheels CLI and users have had time to migrate. * docs(docs): describe lucli stdio mcp as canonical in claude.md and guides CLAUDE.md MCP Server section now documents the stdio config as the canonical surface with the HTTP endpoint marked deprecated. Adds the full auto-discovered tool inventory and links to the configuration guide for deeper docs. mcp-setup.md rewritten to reflect the new 'wheels mcp setup' behavior (stdio transport, no port needed). Lists the 16 MCP-exposed tools and notes the 7 CLI-only tools hidden via mcpHiddenTools(). Adds a migration note pointing pre-4.0 HTTP users at 'wheels mcp setup --force'. mcp-configuration-guide.md: Overview + Quick Start rewritten around the stdio transport. Legacy HTTP section retained (still works, with deprecation warning) for users not yet ready to migrate. * docs(docs): changelog entries for plan c mcp deprecation and canonicalization
1 parent 95eb507 commit 9cd068c

12 files changed

Lines changed: 172 additions & 347 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,14 @@ All historical references to "CFWheels" in this changelog have been preserved fo
120120
- CSRF cookie encryption key auto-generated when empty (apps should still set their own for stable cross-deploy cookies) (#2054)
121121
- CI engine testing restructured: 42 jobs reduced to 8 via engine-grouped testing (#1939)
122122
- `wheels mcp wheels` MCP surface curated — 7 CLI-only commands (`mcp`, `d`, `new`, `console`, `start`, `stop`, `browser`) hidden from MCP `tools/list` via the `mcpHiddenTools()` convention (requires LuCLI 0.3.4+). All remain reachable as CLI subcommands. Tool count drops from 23 to 16 for agent consumers.
123+
- LuCLI stdio MCP (`wheels mcp wheels`) is now the canonical AI-agent surface for Wheels. `wheels mcp setup` generates `.mcp.json` and `.opencode.json` pointing at the stdio transport. No port or running dev server required. Updated templates: `cli/src/templates/McpConfig.json`, `app/snippets/McpConfig.json`, `tools/build/base/.mcp.json`, `tools/build/base/.opencode.json`.
123124

124125
### Deprecated
125126

126127
- Legacy `plugins/` folder — superseded by the new `packages/``vendor/` activation model. Plugins still load, with a deprecation warning. (#1995)
127128
- RocketUnit test style for new tests — BDD syntax (via WheelsTest) is required going forward. Existing RocketUnit specs continue to run. (#1925)
128129
- `wheels.Test` test base class — extend `wheels.WheelsTest` instead (#1889)
130+
- In-dev-server HTTP MCP endpoint at `/wheels/mcp` — superseded by the LuCLI stdio MCP server (`wheels mcp wheels`). Emits a deprecation warning to the `wheels_mcp` log on first request and advertises `deprecated: true` in the `serverInfo` handshake. Scheduled for removal in a future release. Migrate existing projects with `wheels mcp setup --force`.
129131

130132
### Removed
131133

CLAUDE.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,16 @@ The project name is **Wheels** (not "CFWheels"). The rebrand happened at v3.0. A
857857

858858
## MCP Server
859859

860-
Endpoint: `/wheels/mcp` (routes must come before `.wildcard()` in routes.cfm).
860+
**Canonical surface (Wheels 4.0+):** LuCLI stdio MCP at `wheels mcp wheels`. Configure your AI IDE with:
861861

862-
Tools: `wheels_generate`, `wheels_migrate`, `wheels_test`, `wheels_server`, `wheels_reload`, `wheels_analyze`, `wheels_validate`.
862+
```json
863+
{"mcpServers":{"wheels":{"command":"wheels","args":["mcp","wheels"]}}}
864+
```
865+
866+
Or run `wheels mcp setup` to generate `.mcp.json` + `.opencode.json` automatically.
867+
868+
Tools are auto-discovered from `cli/lucli/Module.cfc` public functions, prefixed with the module name (`wheels_generate`, `wheels_migrate`, `wheels_test`, `wheels_reload`, `wheels_seed`, `wheels_analyze`, `wheels_validate`, `wheels_routes`, `wheels_info`, `wheels_destroy`, `wheels_doctor`, `wheels_stats`, `wheels_notes`, `wheels_db`, `wheels_upgrade`, `wheels_create`). CLI-only tools (`mcp`, `d`, `new`, `console`, `start`, `stop`, `browser`) are hidden from MCP `tools/list` via `mcpHiddenTools()`.
869+
870+
Workflow orchestration (multi-step planning, feature development) is not a framework concern — use your preferred Claude Code plugin (Superpowers, feature-dev, etc.). The framework ships deterministic Wheels operations via MCP; the model orchestrates.
871+
872+
**Deprecated:** The in-dev-server HTTP endpoint at `/wheels/mcp` (routed from `vendor/wheels/public/views/mcp.cfm`). Emits a deprecation notice and warning log on first request. Scheduled for removal in a future release — migrate to the stdio surface. See `docs/command-line-tools/commands/mcp/mcp-configuration-guide.md`.

app/snippets/McpConfig.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"mcpServers": {
33
"wheels": {
4-
"url": "http://localhost:{PORT}/wheels/mcp",
5-
"type": "http"
4+
"command": "wheels",
5+
"args": ["mcp", "wheels"]
66
},
77
"browsermcp": {
88
"command": "npx",
@@ -11,4 +11,4 @@
1111
]
1212
}
1313
}
14-
}
14+
}

cli/lucli/Module.cfc

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -615,14 +615,19 @@ component extends="modules.BaseModule" {
615615
* hint: Show MCP server configuration instructions
616616
*/
617617
public string function mcp() {
618-
out("MCP is built into LuCLI. Run:", "bold");
619-
out(" lucli mcp wheels");
618+
out("MCP is built into the Wheels CLI. Run:", "bold");
619+
out(" wheels mcp wheels");
620620
out("");
621-
out("Configure in Claude Code (.claude/claude_project_config.json):", "bold");
622-
out(' {"mcpServers":{"wheels":{"command":"lucli","args":["mcp","wheels"]}}}');
621+
out("Configure in Claude Code (.mcp.json):", "bold");
622+
out(' {"mcpServers":{"wheels":{"command":"wheels","args":["mcp","wheels"]}}}');
623+
out("");
624+
out("For OpenCode, Cursor, and other AI IDEs, see:");
625+
out(" docs/command-line-tools/commands/mcp/mcp-configuration-guide.md");
623626
out("");
624627
out("All public commands in this module are auto-discovered as MCP tools.");
625628
out("Tools are prefixed with the module name: wheels_generate, wheels_migrate, etc.");
629+
out("Stateful/interactive commands (start, stop, new, console, ...) are hidden");
630+
out("from MCP tools/list via mcpHiddenTools() — they remain CLI-only.");
626631
return "";
627632
}
628633

Lines changed: 27 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,57 @@
11
/**
2-
* Set up MCP (Model Context Protocol) integration for AI IDE support
3-
* Creates .mcp.json and .opencode.json configuration files in your project root
4-
* Uses the native CFML MCP server built into Wheels (no Node.js required)
2+
* Set up MCP (Model Context Protocol) integration for AI IDE support.
3+
*
4+
* Writes .mcp.json and .opencode.json configuration files in the project
5+
* root. Both point your AI IDE at the LuCLI stdio MCP server (`wheels mcp
6+
* wheels`) — the canonical Wheels MCP surface as of 4.0. No port or running
7+
* dev server required; the IDE launches the stdio subprocess on demand.
58
*
69
* Examples:
710
* {code:bash}
811
* wheels mcp setup
9-
* wheels mcp setup --port=8080
1012
* wheels mcp setup --force
1113
* {code}
1214
**/
1315
component extends="../base" {
1416

1517
/**
16-
* @port Port number for Wheels server (auto-detected if not provided)
1718
* @force Overwrite existing configuration files
18-
* @noAutoStart Don't automatically start server if not running
1919
**/
20-
function run(
21-
numeric port = 0,
22-
boolean force = false,
23-
boolean noAutoStart = false
24-
) {
20+
function run(boolean force = false) {
2521
print.line();
2622
print.boldYellowLine("🤖 Setting up MCP Integration for Wheels");
2723
print.line("=" .repeatString(50));
2824
print.line();
2925

30-
// Check if we're in a Wheels application
3126
if (!isWheelsApp()) {
3227
print.redLine("❌ This doesn't appear to be a Wheels application directory.");
3328
print.line(" Looking for /vendor/wheels, /config, and /app folders");
3429
print.line();
3530
print.yellowLine(" Run this command from your Wheels project root directory.");
3631
return;
37-
} else {
38-
print.greenLine("✅ Wheels application detected");
3932
}
33+
print.greenLine("✅ Wheels application detected");
34+
print.greenLine("✅ Using LuCLI stdio MCP (transport: stdio, no port needed)");
4035

41-
print.greenLine("✅ Using native CFML MCP server (no Node.js required)");
42-
43-
// Detect or use provided port
44-
var serverPort = detectServerPort(arguments.port, arguments.noAutoStart);
4536
print.line();
46-
47-
// Create configuration files
4837
print.boldLine("Creating MCP configuration files...");
4938

5039
try {
5140
var projectRoot = getCWD();
5241

53-
// Create .mcp.json
5442
var mcpConfigPath = projectRoot & "/.mcp.json";
5543
if (!fileExists(mcpConfigPath) || arguments.force) {
5644
var mcpTemplate = fileRead(expandPath("/wheels-cli/templates/McpConfig.json"));
57-
var mcpContent = replace(mcpTemplate, "{PORT}", serverPort, "ALL");
58-
fileWrite(mcpConfigPath, mcpContent);
45+
fileWrite(mcpConfigPath, mcpTemplate);
5946
print.greenLine("✅ Created .mcp.json");
6047
} else {
6148
print.yellowLine("⚠️ .mcp.json already exists (use --force to overwrite)");
6249
}
6350

64-
// Create .opencode.json
6551
var opencodeConfigPath = projectRoot & "/.opencode.json";
6652
if (!fileExists(opencodeConfigPath) || arguments.force) {
6753
var opencodeTemplate = fileRead(expandPath("/wheels-cli/templates/OpenCodeConfig.json"));
68-
var opencodeContent = replace(opencodeTemplate, "{PORT}", serverPort, "ALL");
69-
fileWrite(opencodeConfigPath, opencodeContent);
54+
fileWrite(opencodeConfigPath, opencodeTemplate);
7055
print.greenLine("✅ Created .opencode.json");
7156
} else {
7257
print.yellowLine("⚠️ .opencode.json already exists (use --force to overwrite)");
@@ -77,21 +62,20 @@ component extends="../base" {
7762
print.redLine("❌ Configuration failed: " & e.message);
7863
return;
7964
}
80-
print.line();
8165

82-
// Summary
66+
print.line();
8367
print.boldGreenLine("✨ MCP Integration Setup Complete!");
8468
print.line();
8569
print.boldLine("Configuration Summary:");
86-
print.indentedLine("Wheels MCP Endpoint: http://localhost:" & serverPort & "/wheels/mcp");
87-
print.indentedLine("Server Type: Native CFML (no Node.js required)");
88-
print.indentedLine("Files Created: .mcp.json, .opencode.json");
70+
print.indentedLine("MCP transport: stdio (launched on demand by your AI IDE)");
71+
print.indentedLine('Command: wheels mcp wheels');
72+
print.indentedLine("Files created: .mcp.json, .opencode.json");
8973
print.line();
9074

9175
print.boldLine("Next Steps:");
92-
print.indentedLine("1. Ensure your Wheels server is running on port " & serverPort);
93-
print.indentedLine("2. Configure your AI IDE to use the generated configuration files");
94-
print.indentedLine("3. Test the connection: wheels mcp test");
76+
print.indentedLine("1. Ensure the 'wheels' CLI binary is on PATH (brew install wheels)");
77+
print.indentedLine("2. Restart your AI IDE so it re-reads .mcp.json / .opencode.json");
78+
print.indentedLine("3. Verify tool discovery in the IDE's MCP panel — 16 tools should appear");
9579
print.line();
9680

9781
print.boldLine("Available MCP Commands:");
@@ -100,116 +84,15 @@ component extends="../base" {
10084
print.indentedLine("wheels mcp remove - Remove MCP integration");
10185
print.line();
10286

103-
print.yellowLine("💡 The generated files provide AI assistants with:");
104-
print.indentedLine("• Real-time access to your Wheels project structure");
105-
print.indentedLine("• Complete API documentation and guides");
106-
print.indentedLine("• Ability to generate models, controllers, and migrations");
107-
print.indentedLine("• Direct execution of tests and server commands");
108-
print.indentedLine("• Browser automation capabilities (via Browser MCP)");
87+
print.yellowLine("💡 The generated files give AI assistants:");
88+
print.indentedLine("• Code generation (generate, destroy, seed, ...)");
89+
print.indentedLine("• Migration + database operations (migrate, db, ...)");
90+
print.indentedLine("• Project introspection (routes, info, analyze, doctor, stats, notes)");
91+
print.indentedLine("• Test runner (test)");
92+
print.indentedLine("• Browser automation (via Browser MCP)");
93+
print.line();
94+
print.indentedLine("Full guide: docs/command-line-tools/commands/mcp/mcp-configuration-guide.md");
10995
print.line();
11096
}
11197

112-
private function detectServerPort(port, noAutoStart) {
113-
var serverPort = 0;
114-
115-
if (!isNull(port) && port > 0) {
116-
serverPort = port;
117-
print.line("Using specified port: " & serverPort);
118-
} else {
119-
print.line("Detecting Wheels server port...");
120-
121-
// Use CommandBox serverService to get server info
122-
try {
123-
var cwd = getCWD();
124-
var serverInfo = {};
125-
126-
// Try current directory first
127-
serverInfo = serverService.getServerInfoByWebroot(cwd);
128-
129-
// If not found, try with /public subdirectory (common Wheels setup)
130-
if (serverInfo.isEmpty() && directoryExists(cwd & "/public")) {
131-
serverInfo = serverService.getServerInfoByWebroot(cwd & "/public");
132-
}
133-
134-
// If still not found, try parent directory (in case we're in /public)
135-
if (serverInfo.isEmpty() && getFileFromPath(cwd) == "public") {
136-
var parentDir = getDirectoryFromPath(cwd.substring(1, cwd.length() - 1));
137-
serverInfo = serverService.getServerInfoByWebroot(parentDir);
138-
}
139-
140-
if (!serverInfo.isEmpty()) {
141-
// Check if server is running
142-
if (serverInfo.status == "running") {
143-
// Get the port from the running server
144-
if (structKeyExists(serverInfo, "port")) {
145-
serverPort = serverInfo.port;
146-
print.greenLine("✅ Detected running server on port " & serverPort);
147-
} else if (structKeyExists(serverInfo, "web") &&
148-
structKeyExists(serverInfo.web, "http") &&
149-
structKeyExists(serverInfo.web.http, "port")) {
150-
serverPort = serverInfo.web.http.port;
151-
print.greenLine("✅ Detected running server on port " & serverPort);
152-
}
153-
} else if (serverInfo.status == "stopped") {
154-
// Server exists but is stopped
155-
print.yellowLine("⚠️ Server is configured but not running");
156-
157-
// Try to get configured port even if stopped
158-
if (structKeyExists(serverInfo, "port")) {
159-
serverPort = serverInfo.port;
160-
print.line(" Using configured port: " & serverPort);
161-
}
162-
}
163-
}
164-
} catch (any e) {
165-
// Fallback if serverService fails
166-
print.yellowLine("⚠️ Could not detect server via CommandBox service");
167-
}
168-
}
169-
170-
// If no server detected and auto-start enabled, try to start server
171-
if (serverPort == 0 && !noAutoStart) {
172-
print.line("No running server detected. Attempting to start server...");
173-
174-
try {
175-
// Start the server using serverService
176-
var serverInfo = serverService.start(
177-
name = "", // Use default name for current directory
178-
directory = getCWD(),
179-
saveSettings = true
180-
);
181-
182-
sleep(3000); // Wait for server to fully start
183-
184-
// Get the server info again after starting
185-
serverInfo = serverService.getServerInfoByWebroot(getCWD());
186-
187-
if (!serverInfo.isEmpty() && serverInfo.status == "running") {
188-
if (structKeyExists(serverInfo, "port")) {
189-
serverPort = serverInfo.port;
190-
print.greenLine("✅ Started server on port " & serverPort);
191-
}
192-
}
193-
} catch (any e) {
194-
print.yellowLine("⚠️ Could not start server automatically: " & e.message);
195-
}
196-
}
197-
198-
if (serverPort == 0) {
199-
print.yellowLine("⚠️ Could not detect server port.");
200-
print.line();
201-
print.line("Options:");
202-
print.indentedLine("1. Start your server: wheels server start");
203-
print.indentedLine("2. Specify port manually: wheels mcp setup --port=60000");
204-
print.indentedLine("3. Run with --noAutoStart to disable server auto-start");
205-
print.line();
206-
207-
// Ask for port
208-
var userPort = ask("Enter your Wheels server port (or press Enter to use 60000): ");
209-
serverPort = len(trim(userPort)) ? userPort : 60000;
210-
}
211-
212-
return serverPort;
213-
}
214-
215-
}
98+
}

cli/src/templates/McpConfig.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"mcpServers": {
33
"wheels": {
4-
"url": "http://localhost:{PORT}/wheels/mcp",
5-
"type": "http"
4+
"command": "wheels",
5+
"args": ["mcp", "wheels"]
66
},
77
"browsermcp": {
88
"command": "npx",
@@ -11,4 +11,4 @@
1111
]
1212
}
1313
}
14-
}
14+
}

0 commit comments

Comments
 (0)