Skip to content

Commit f93fd19

Browse files
authored
feat(cli): add MCP server for AI agent integration (#112)
* feat(cli): add MCP server for AI agent integration Expose the CLI as an MCP (Model Context Protocol) server so AI agents (Claude Code, Cursor, VS Code, etc.) can scaffold and modify projects programmatically via stdio transport. 7 tools: bfs_get_guidance, bfs_get_schema, bfs_check_compatibility, bfs_plan_project, bfs_create_project, bfs_plan_addition, bfs_add_feature 3 resources: compatibility-rules, stack-options, getting-started Usage: create-better-fullstack mcp Register: claude mcp add --transport stdio better-fullstack -- npx create-better-fullstack mcp * docs: add feature roadmap and competitive analysis Detailed expansion plans for Rust, Go, Python, TypeScript ecosystems, new categories (GraphQL, i18n, rate limiting, desktop), platform features (MCP server, add command, cross-ecosystem stacks), and competitive analysis vs better-t-stack. * fix(templates): Kysely empty Database interface and Drizzle missing NextAuth export Kysely: when auth != better-auth, the Database interface was empty, making the entire DB layer non-functional. Now generates an ExampleTable as a starter schema. Drizzle: barrel file only exported auth schema for better-auth but not NextAuth, which also generates Drizzle schema files. Added NextAuth to the conditional export. * fix(cli): propagate addon setup warnings instead of silent failures setupAddons() now returns a warnings array. addHandler() includes warnings in AddResult so callers (including MCP) know when MCP/Skills addon setup failed. Previously returned success: true with no indication of failure. * fix(templates): trailing newlines, tab-to-space in payments, prompt text - Add trailing newlines to 52 .hbs template files (POSIX compliance) - Convert tabs to 2-space indent in 4 payment TypeScript templates - Change "Choose web" prompt to "Select web framework" - Add codebase issues tracking doc * fix(mcp): address code review issues from PR #112 - Prevent path traversal in bfs_create_project by calling sanitizePath and rejecting '..' path components - Fix mcp --help hang by only intercepting bare 'mcp' subcommand - Run setupAddons after writing files in bfs_create_project - Add missing types field to ./mcp package.json export - Fix misleading self-* notation in COMPATIBILITY_RULES_MD - Fix summarizeTree type mismatch with VirtualFileTree * fix: update template snapshots to match current templates * ci: retrigger CI * fix(mcp): unify plan/create config defaults via shared buildProjectConfig bfs_plan_project used createVirtual which had different defaults (api=trpc, uiLibrary=shadcn-ui, testing=vitest, etc.) than bfs_create_project (all "none"). This caused plan previews to show more files than create would actually generate. Extract buildProjectConfig() as single source of truth for both tools, and share the Zod schema via planCreateSchema.
1 parent c6920e5 commit f93fd19

78 files changed

Lines changed: 2858 additions & 508 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/cli/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@
8585
"./virtual": {
8686
"types": "./dist/virtual.d.mts",
8787
"import": "./dist/virtual.mjs"
88+
},
89+
"./mcp": {
90+
"types": "./dist/mcp-entry.d.mts",
91+
"import": "./dist/mcp-entry.mjs"
8892
}
8993
},
9094
"publishConfig": {
@@ -123,6 +127,7 @@
123127
"oxfmt": "^0.19.0",
124128
"picocolors": "^1.1.1",
125129
"tinyglobby": "^0.2.15",
130+
"@modelcontextprotocol/sdk": "^1.12.1",
126131
"trpc-cli": "^0.12.1",
127132
"ts-morph": "^27.0.2",
128133
"yaml": "^2.8.3",

apps/cli/src/cli.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1-
import { createBtsCli } from "./index";
2-
3-
createBtsCli().run();
1+
const firstArg = process.argv[2];
2+
if (firstArg === "mcp" && process.argv.length === 3) {
3+
import("./mcp.js").then((m) => m.startMcpServer());
4+
} else {
5+
import("./index.js").then((m) => m.createBtsCli().run());
6+
}

apps/cli/src/helpers/addons/addons-setup.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import { setupTui } from "./tui-setup";
1515
import { setupUltracite } from "./ultracite-setup";
1616
import { setupWxt } from "./wxt-setup";
1717

18-
export async function setupAddons(config: ProjectConfig) {
18+
export async function setupAddons(config: ProjectConfig): Promise<string[]> {
19+
const warnings: string[] = [];
1920
const { addons, frontend, projectDir } = config;
2021
const hasReactWebFrontend =
2122
frontend.includes("react-router") ||
@@ -95,12 +96,22 @@ export async function setupAddons(config: ProjectConfig) {
9596
}
9697

9798
if (addons.includes("mcp")) {
98-
await setupMcp(config);
99+
try {
100+
await setupMcp(config);
101+
} catch (error) {
102+
warnings.push(`MCP setup failed: ${error instanceof Error ? error.message : String(error)}`);
103+
}
99104
}
100105

101106
if (addons.includes("skills")) {
102-
await setupSkills(config);
107+
try {
108+
await setupSkills(config);
109+
} catch (error) {
110+
warnings.push(`Skills setup failed: ${error instanceof Error ? error.message : String(error)}`);
111+
}
103112
}
113+
114+
return warnings;
104115
}
105116

106117
export async function setupBiome(projectDir: string) {

apps/cli/src/helpers/core/add-handler.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export interface AddResult {
3131
addedAddons: Addons[];
3232
projectDir: string;
3333
error?: string;
34+
setupWarnings?: string[];
3435
}
3536

3637
export async function addHandler(
@@ -169,7 +170,7 @@ async function addHandlerInternal(input: AddInput): Promise<AddResult> {
169170

170171
await writeTreeToFilesystem(tree, projectDir);
171172

172-
await setupAddons(config);
173+
const setupWarnings = await setupAddons(config);
173174
await applyDependencyVersionChannel(projectDir, config.versionChannel);
174175

175176
const updatedAddons = [...new Set([...existingAddons, ...addonsToAdd])];
@@ -197,6 +198,9 @@ async function addHandlerInternal(input: AddInput): Promise<AddResult> {
197198

198199
if (!isSilent()) {
199200
log.success(pc.green(`Successfully added: ${addonsToAdd.join(", ")}`));
201+
for (const warning of setupWarnings) {
202+
log.warn(pc.yellow(warning));
203+
}
200204
if (!input.install) {
201205
const installCmd =
202206
config.packageManager === "npm" ? "npm install" : `${config.packageManager} install`;
@@ -209,6 +213,7 @@ async function addHandlerInternal(input: AddInput): Promise<AddResult> {
209213
success: true,
210214
addedAddons: addonsToAdd,
211215
projectDir,
216+
setupWarnings: setupWarnings.length > 0 ? setupWarnings : undefined,
212217
};
213218
}
214219

apps/cli/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,12 @@ export const router = os.router({
364364
ecosystem: input.ecosystem,
365365
});
366366
}),
367+
mcp: os
368+
.meta({ description: "Start MCP server for AI agent integration (stdio transport)" })
369+
.handler(async () => {
370+
log.message("MCP server is started via the 'mcp' subcommand intercepted in cli.ts.");
371+
log.message("Run: create-better-fullstack mcp");
372+
}),
367373
});
368374

369375
const caller = createRouterClient(router, { context: {} });

apps/cli/src/mcp-entry.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { startMcpServer } from "./mcp.js";
2+
3+
startMcpServer();

0 commit comments

Comments
 (0)