create-basalt 1.2.2 → 1.3.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.
- package/README.md +3 -0
- package/dist/cli.js +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/templates.d.ts +8 -0
- package/dist/templates.js +79 -4
- package/dist/wizard.d.ts +2 -1
- package/dist/wizard.js +4 -2
- package/package.json +1 -1
- package/dist/chunk-XUW6ZAPV.js +0 -1342
package/README.md
CHANGED
|
@@ -46,6 +46,7 @@ Multi-tenancy? (Y/n) y
|
|
|
46
46
|
Authentication? (Y/n) y
|
|
47
47
|
Subscriptions / billing? (y/N) n
|
|
48
48
|
Web UI (React + shadcn)? (y/N) n
|
|
49
|
+
MCP server (routes as AI-agent tools)? (y/N) n
|
|
49
50
|
'basalt' CLI (code generators)? (y/N) n
|
|
50
51
|
Install dependencies now? (y/N) y
|
|
51
52
|
Initialize a git repository? (y/N) y
|
|
@@ -89,6 +90,7 @@ Usage: npm create basalt <name> [options]
|
|
|
89
90
|
| `--billing` | off | Includes subscriptions/plans (`@basaltkit/subscriptions`, example `free` and `pro` plans) |
|
|
90
91
|
| `--ui` | off | Generates the `web/` frontend (React + shadcn via `@basaltkit/admin-shadcn` + `@basaltkit/sdk`). **Forces pnpm** — see note below |
|
|
91
92
|
| `--cli` | off | Generates the `basalt` CLI (`bin/basalt.ts`, `pnpm basalt` script, `make:*` generators from `@basaltkit/generator`) |
|
|
93
|
+
| `--mcp` | off | Exposes read-only routes as MCP tools (`@basaltkit/mcp`) over HTTP at `POST /mcp` — the overview and health endpoints are opted in via `meta.mcp` |
|
|
92
94
|
| `--install` | off | Installs dependencies at the end (with the detected/chosen manager) |
|
|
93
95
|
| `--git` | off | Runs `git init` + first commit ("Initial commit from create-basalt") |
|
|
94
96
|
| `--pm=<manager>` | autodetect | Package manager: `pnpm` \| `npm` \| `yarn` \| `bun` |
|
|
@@ -202,6 +204,7 @@ Exported from `create-basalt` (in addition to the `create-basalt` executable):
|
|
|
202
204
|
| `billing` | `boolean` | No | `false` | Include subscriptions |
|
|
203
205
|
| `ui` | `boolean` | No | `false` | Generate the `web/` frontend |
|
|
204
206
|
| `cli` | `boolean` | No | `false` | Generate the `basalt` CLI |
|
|
207
|
+
| `mcp` | `boolean` | No | `false` | Expose read-only routes as MCP tools at `/mcp` |
|
|
205
208
|
|
|
206
209
|
`CreateProjectResult`:
|
|
207
210
|
|
package/dist/cli.js
CHANGED
|
@@ -11,6 +11,7 @@ Options:
|
|
|
11
11
|
--billing Include subscriptions/billing
|
|
12
12
|
--ui Scaffold a web/ frontend (React + shadcn + SDK)
|
|
13
13
|
--cli Scaffold the 'basalt' CLI (code generators + commands)
|
|
14
|
+
--mcp Expose read-only routes as MCP tools at /mcp
|
|
14
15
|
--install Install dependencies after scaffolding
|
|
15
16
|
--git Initialize a git repository with a first commit
|
|
16
17
|
--pm=<manager> Package manager: pnpm | npm | yarn | bun (default: auto-detect)
|
|
@@ -26,6 +27,7 @@ function parseArgs(argv) {
|
|
|
26
27
|
billing: false,
|
|
27
28
|
ui: false,
|
|
28
29
|
cli: false,
|
|
30
|
+
mcp: false,
|
|
29
31
|
install: false,
|
|
30
32
|
git: false,
|
|
31
33
|
yes: false,
|
|
@@ -41,6 +43,8 @@ function parseArgs(argv) {
|
|
|
41
43
|
flags.ui = true;
|
|
42
44
|
else if (token === '--cli')
|
|
43
45
|
flags.cli = true;
|
|
46
|
+
else if (token === '--mcp')
|
|
47
|
+
flags.mcp = true;
|
|
44
48
|
else if (token === '--install')
|
|
45
49
|
flags.install = true;
|
|
46
50
|
else if (token === '--git')
|
|
@@ -91,6 +95,7 @@ if (!flags.yes && flags.name === undefined && stdin.isTTY) {
|
|
|
91
95
|
flags.billing = result.billing;
|
|
92
96
|
flags.ui = result.ui;
|
|
93
97
|
flags.cli = result.cli;
|
|
98
|
+
flags.mcp = result.mcp;
|
|
94
99
|
flags.install = result.install;
|
|
95
100
|
flags.git = result.git;
|
|
96
101
|
flags.pm = result.pm;
|
|
@@ -121,6 +126,7 @@ try {
|
|
|
121
126
|
billing: flags.billing,
|
|
122
127
|
ui: flags.ui,
|
|
123
128
|
cli: flags.cli,
|
|
129
|
+
mcp: flags.mcp,
|
|
124
130
|
});
|
|
125
131
|
console.log(`\nCreated ${result.options.name} in ${result.dir}\n`);
|
|
126
132
|
for (const file of result.files)
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface CreateProjectInput {
|
|
|
11
11
|
ui?: boolean;
|
|
12
12
|
/** Scaffold the `basalt` CLI entrypoint (code generators + commands). Default: false. */
|
|
13
13
|
cli?: boolean;
|
|
14
|
+
/** Expose opted-in routes as MCP tools over HTTP at `/mcp`. Default: false. */
|
|
15
|
+
mcp?: boolean;
|
|
14
16
|
}
|
|
15
17
|
export interface CreateProjectResult {
|
|
16
18
|
dir: string;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdir, readdir, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname, join, resolve } from 'node:path';
|
|
3
|
-
import { appTest, appTs, envExample, envTs, gitignore, basaltBin, packageJson, pnpmWorkspaceYaml, readme, routesTs, serverTs, tsconfigJson, } from './templates.js';
|
|
3
|
+
import { appTest, appTs, envExample, envTs, gitignore, mcpJson, basaltBin, packageJson, pnpmWorkspaceYaml, readme, routesTs, serverTs, tsconfigJson, } from './templates.js';
|
|
4
4
|
import { uiFiles } from './templates-ui.js';
|
|
5
5
|
export class TargetNotEmptyError extends Error {
|
|
6
6
|
constructor(dir) {
|
|
@@ -27,6 +27,7 @@ export async function createProject(input) {
|
|
|
27
27
|
billing: input.billing ?? false,
|
|
28
28
|
ui: input.ui ?? false,
|
|
29
29
|
cli: input.cli ?? false,
|
|
30
|
+
mcp: input.mcp ?? false,
|
|
30
31
|
};
|
|
31
32
|
const dir = resolve(input.dir ?? input.name);
|
|
32
33
|
const existing = await readdir(dir).catch(() => null);
|
|
@@ -45,6 +46,7 @@ export async function createProject(input) {
|
|
|
45
46
|
'src/server.ts': serverTs(),
|
|
46
47
|
'tests/app.test.ts': appTest(options),
|
|
47
48
|
...(options.cli ? { 'bin/basalt.ts': basaltBin() } : {}),
|
|
49
|
+
...(options.mcp ? { '.mcp.json': mcpJson(options) } : {}),
|
|
48
50
|
...(options.ui ? uiFiles(options) : {}),
|
|
49
51
|
};
|
|
50
52
|
for (const [path, content] of Object.entries(files)) {
|
package/dist/templates.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface ProjectOptions {
|
|
|
7
7
|
ui: boolean;
|
|
8
8
|
/** Scaffold the `basalt` CLI entrypoint (code generators + built-in commands). */
|
|
9
9
|
cli: boolean;
|
|
10
|
+
/** Expose opted-in routes as MCP tools (@basaltkit/mcp) over HTTP at `/mcp`. */
|
|
11
|
+
mcp: boolean;
|
|
10
12
|
}
|
|
11
13
|
/** The dependency range for a @basalt package (override, else the base line). */
|
|
12
14
|
export declare const versionOf: (pkg: string) => string;
|
|
@@ -20,6 +22,12 @@ export declare function serverTs(): string;
|
|
|
20
22
|
export declare function basaltBin(): string;
|
|
21
23
|
export declare function appTest(options: ProjectOptions): string;
|
|
22
24
|
export declare function readme(options: ProjectOptions): string;
|
|
25
|
+
/**
|
|
26
|
+
* A Claude Code / generic MCP client config that registers the dev-only
|
|
27
|
+
* `basalt-ai-mcp` bridge for this project. Claude Code reads `.mcp.json` at the
|
|
28
|
+
* project root automatically; Claude Desktop uses the same server entry.
|
|
29
|
+
*/
|
|
30
|
+
export declare function mcpJson(_options: ProjectOptions): string;
|
|
23
31
|
export declare function gitignore(): string;
|
|
24
32
|
/**
|
|
25
33
|
* pnpm settings: esbuild's build script is required by tsx;
|
package/dist/templates.js
CHANGED
|
@@ -5,7 +5,10 @@ const BASALT_VERSION = '^1.0.0';
|
|
|
5
5
|
* package that advances past the base line needs its own range or a fresh app
|
|
6
6
|
* can't install it. Add an entry when a package crosses a minor.
|
|
7
7
|
*/
|
|
8
|
-
const VERSIONS = {
|
|
8
|
+
const VERSIONS = {
|
|
9
|
+
// ai-mcp is on the 0.x line (published separately from the 1.x framework base).
|
|
10
|
+
'@basaltkit/ai-mcp': '^0.1.0',
|
|
11
|
+
};
|
|
9
12
|
/** The dependency range for a @basalt package (override, else the base line). */
|
|
10
13
|
export const versionOf = (pkg) => VERSIONS[pkg] ?? BASALT_VERSION;
|
|
11
14
|
export function packageJson(options) {
|
|
@@ -24,6 +27,8 @@ export function packageJson(options) {
|
|
|
24
27
|
dependencies['@basaltkit/auth'] = BASALT_VERSION;
|
|
25
28
|
if (options.billing)
|
|
26
29
|
dependencies['@basaltkit/subscriptions'] = BASALT_VERSION;
|
|
30
|
+
if (options.mcp)
|
|
31
|
+
dependencies['@basaltkit/mcp'] = BASALT_VERSION;
|
|
27
32
|
if (options.cli) {
|
|
28
33
|
// Runtime deps: app.ts uses commandsPlugin (@basaltkit/cli); prisma powers
|
|
29
34
|
// prismaPlugin + `basalt prisma:sync`. @basaltkit/generator is dev-only (below).
|
|
@@ -50,6 +55,12 @@ export function packageJson(options) {
|
|
|
50
55
|
// never does, so the app runs completely without the codegen/AI layer.
|
|
51
56
|
devDependencies['@basaltkit/generator'] = versionOf('@basaltkit/generator');
|
|
52
57
|
}
|
|
58
|
+
if (options.mcp) {
|
|
59
|
+
// Dev-only AI bridge: exposes analyze/plan/make to MCP clients (Claude
|
|
60
|
+
// Code/Desktop) via the `basalt-ai-mcp` bin. NEVER a runtime dependency — the
|
|
61
|
+
// app ships and runs without it (see `.mcp.json` for the client config).
|
|
62
|
+
devDependencies['@basaltkit/ai-mcp'] = versionOf('@basaltkit/ai-mcp');
|
|
63
|
+
}
|
|
53
64
|
return `${JSON.stringify({
|
|
54
65
|
name: options.name,
|
|
55
66
|
version: '0.1.0',
|
|
@@ -166,7 +177,19 @@ export function appTs(options) {
|
|
|
166
177
|
imports.push(`import { commandsPlugin, type CommandDefinition } from '@basaltkit/cli'`);
|
|
167
178
|
plugins.push(`...(options.commands && options.commands.length > 0 ? [commandsPlugin(options.commands)] : [])`);
|
|
168
179
|
}
|
|
169
|
-
|
|
180
|
+
let adapterRoutesExpr = routesExpression;
|
|
181
|
+
if (options.mcp) {
|
|
182
|
+
imports.push(`import { mcpPlugin, mcpRoutes } from '@basaltkit/mcp'`);
|
|
183
|
+
// The MCP server scans the SAME app routes for `meta.mcp` (see routes.ts); the
|
|
184
|
+
// `/mcp` transport is added to the adapter so agents POST JSON-RPC to it. Tool
|
|
185
|
+
// calls run through the neutral pipeline, so tenancy/auth apply unchanged.
|
|
186
|
+
plugins.push(`mcpPlugin({ routes: ${routesExpression}, serverInfo: { name: '${options.name}', version: '0.1.0' } })`);
|
|
187
|
+
adapterRoutesExpr =
|
|
188
|
+
routesExpression === 'appRoutes'
|
|
189
|
+
? '[...appRoutes, ...mcpRoutes()]'
|
|
190
|
+
: routesExpression.replace(/]$/, ', ...mcpRoutes()]');
|
|
191
|
+
}
|
|
192
|
+
plugins.push(`fastifyPlugin({ routes: ${adapterRoutesExpr} })`);
|
|
170
193
|
const commandsField = options.cli
|
|
171
194
|
? `\n /** Dev/CLI commands (make:*, ai:*, prisma:sync) — passed ONLY by bin/basalt.ts. */\n commands?: CommandDefinition[]`
|
|
172
195
|
: '';
|
|
@@ -201,6 +224,14 @@ export function routesTs(options) {
|
|
|
201
224
|
]
|
|
202
225
|
: []),
|
|
203
226
|
];
|
|
227
|
+
// Opt these read-only routes in as MCP tools when MCP is enabled. Only safe,
|
|
228
|
+
// non-mutating endpoints are exposed — auth routes are deliberately left out.
|
|
229
|
+
const overviewMeta = options.mcp
|
|
230
|
+
? `\n meta: { mcp: { name: 'overview', description: 'List what this API exposes: name, status and available endpoints.' } },`
|
|
231
|
+
: '';
|
|
232
|
+
const healthMeta = options.mcp
|
|
233
|
+
? `\n meta: { mcp: { name: 'health', description: 'Liveness check — returns ok and the current request id.' } },`
|
|
234
|
+
: '';
|
|
204
235
|
return `import { ctx } from '@basaltkit/core'
|
|
205
236
|
import { route } from '@basaltkit/fastify'
|
|
206
237
|
|
|
@@ -208,7 +239,7 @@ export const appRoutes = [
|
|
|
208
239
|
// Friendly index so \`GET /\` is never a bare 404 — lists what the API exposes.
|
|
209
240
|
route({
|
|
210
241
|
method: 'GET',
|
|
211
|
-
url: '/'
|
|
242
|
+
url: '/',${overviewMeta}
|
|
212
243
|
async handler() {
|
|
213
244
|
return {
|
|
214
245
|
name: '${options.name}',
|
|
@@ -219,7 +250,7 @@ export const appRoutes = [
|
|
|
219
250
|
}),
|
|
220
251
|
route({
|
|
221
252
|
method: 'GET',
|
|
222
|
-
url: '/health'
|
|
253
|
+
url: '/health',${healthMeta}
|
|
223
254
|
async handler() {
|
|
224
255
|
return { ok: true, requestId: ctx().requestId${options.tenancy ? ', tenant: ctx().tenant?.id ?? null' : ''} }
|
|
225
256
|
},
|
|
@@ -313,6 +344,7 @@ export function readme(options) {
|
|
|
313
344
|
...(options.billing ? ['subscriptions with plans and feature limits'] : []),
|
|
314
345
|
...(options.ui ? ['web UI (React + shadcn/ui on @basaltkit/admin-shadcn + @basaltkit/sdk)'] : []),
|
|
315
346
|
...(options.cli ? ['`basalt` CLI with code generators (`make:*`) and built-in commands'] : []),
|
|
347
|
+
...(options.mcp ? ['MCP server — read-only routes exposed as AI-agent tools at `/mcp`'] : []),
|
|
316
348
|
];
|
|
317
349
|
return `# ${options.name}
|
|
318
350
|
|
|
@@ -352,9 +384,52 @@ pnpm --filter ${options.name}-web dev # terminal 2 — UI on http://localhost:
|
|
|
352
384
|
|
|
353
385
|
Open <http://localhost:5180>. The Vite dev server proxies \`/api\` to the API,
|
|
354
386
|
so there is no CORS to configure${options.auth ? '. Register, then sign in' : ''}.
|
|
387
|
+
`
|
|
388
|
+
: ''}${options.mcp
|
|
389
|
+
? `
|
|
390
|
+
## MCP server
|
|
391
|
+
|
|
392
|
+
Read-only routes marked with \`meta.mcp\` in \`src/routes.ts\` (the overview and
|
|
393
|
+
health endpoints) are exposed as [Model Context Protocol](https://modelcontextprotocol.io)
|
|
394
|
+
tools over HTTP at \`POST /mcp\`. Point any MCP client at it:
|
|
395
|
+
|
|
396
|
+
\`\`\`bash
|
|
397
|
+
# list the tools
|
|
398
|
+
curl -s localhost:3000/mcp -H 'content-type: application/json' \\
|
|
399
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
|
|
400
|
+
\`\`\`
|
|
401
|
+
|
|
402
|
+
Tool calls run through the same neutral pipeline as HTTP, so validation, tenancy
|
|
403
|
+
and auth apply unchanged. Add \`meta: { mcp: true }\` to any route to expose it —
|
|
404
|
+
keep mutations and auth flows off unless an agent should really call them.
|
|
405
|
+
|
|
406
|
+
### AI dev tools
|
|
407
|
+
|
|
408
|
+
This project is also wired for AI-assisted development. \`.mcp.json\` registers the
|
|
409
|
+
**dev-only** \`basalt-ai-mcp\` bridge (a \`devDependency\` — never shipped in your
|
|
410
|
+
app's runtime) with MCP clients such as Claude Code and Claude Desktop. It exposes
|
|
411
|
+
\`basalt_analyze\`, \`basalt_doctor\`, \`basalt_plan\`, \`basalt_review\` and
|
|
412
|
+
\`basalt_make\` (preview by default, writes confined to this project) plus workflow
|
|
413
|
+
prompts. Claude Code picks up \`.mcp.json\` automatically; for Claude Desktop add the
|
|
414
|
+
same server to its config with \`--cwd=<absolute project path>\`.
|
|
355
415
|
`
|
|
356
416
|
: ''}`;
|
|
357
417
|
}
|
|
418
|
+
/**
|
|
419
|
+
* A Claude Code / generic MCP client config that registers the dev-only
|
|
420
|
+
* `basalt-ai-mcp` bridge for this project. Claude Code reads `.mcp.json` at the
|
|
421
|
+
* project root automatically; Claude Desktop uses the same server entry.
|
|
422
|
+
*/
|
|
423
|
+
export function mcpJson(_options) {
|
|
424
|
+
return `${JSON.stringify({
|
|
425
|
+
mcpServers: {
|
|
426
|
+
'basalt-ai': {
|
|
427
|
+
command: 'npx',
|
|
428
|
+
args: ['-y', '@basaltkit/ai-mcp', '--cwd=.'],
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
}, null, 2)}\n`;
|
|
432
|
+
}
|
|
358
433
|
export function gitignore() {
|
|
359
434
|
return `node_modules/
|
|
360
435
|
dist/
|
package/dist/wizard.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PackageManager } from './index.js';
|
|
2
2
|
import { type Choice, type Prompter } from './prompt.js';
|
|
3
|
-
export type FeatureKey = 'tenancy' | 'auth' | 'billing' | 'ui' | 'cli';
|
|
3
|
+
export type FeatureKey = 'tenancy' | 'auth' | 'billing' | 'ui' | 'cli' | 'mcp';
|
|
4
4
|
/** The feature toggles offered in the "custom" preset (and shown in the summary). */
|
|
5
5
|
export declare const FEATURES: Choice<FeatureKey>[];
|
|
6
6
|
interface Preset {
|
|
@@ -20,6 +20,7 @@ export interface WizardResult {
|
|
|
20
20
|
billing: boolean;
|
|
21
21
|
ui: boolean;
|
|
22
22
|
cli: boolean;
|
|
23
|
+
mcp: boolean;
|
|
23
24
|
pm: PackageManager;
|
|
24
25
|
install: boolean;
|
|
25
26
|
git: boolean;
|
package/dist/wizard.js
CHANGED
|
@@ -6,11 +6,12 @@ export const FEATURES = [
|
|
|
6
6
|
{ value: 'billing', label: 'Subscriptions / billing', hint: 'Stripe, Paddle, Lemon Squeezy' },
|
|
7
7
|
{ value: 'ui', label: 'Web UI', hint: 'React + shadcn + SDK (pnpm workspace)' },
|
|
8
8
|
{ value: 'cli', label: 'basalt CLI', hint: 'code generators + commands' },
|
|
9
|
+
{ value: 'mcp', label: 'MCP server', hint: 'expose routes as AI-agent tools (/mcp)' },
|
|
9
10
|
];
|
|
10
11
|
export const PRESETS = [
|
|
11
12
|
{ value: 'saas', label: 'SaaS starter', hint: 'tenancy + auth + billing + CLI', features: ['tenancy', 'auth', 'billing', 'cli'] },
|
|
12
|
-
{ value: 'api', label: 'API only', hint: 'auth, no tenancy or UI', features: ['auth'] },
|
|
13
|
-
{ value: 'full', label: 'Full stack', hint: 'everything + web UI', features: ['tenancy', 'auth', 'billing', 'ui', 'cli'] },
|
|
13
|
+
{ value: 'api', label: 'API only', hint: 'auth + MCP, no tenancy or UI', features: ['auth', 'mcp'] },
|
|
14
|
+
{ value: 'full', label: 'Full stack', hint: 'everything + web UI', features: ['tenancy', 'auth', 'billing', 'ui', 'cli', 'mcp'] },
|
|
14
15
|
{ value: 'minimal', label: 'Minimal', hint: 'no batteries, add them later', features: [] },
|
|
15
16
|
{ value: 'custom', label: 'Custom', hint: 'pick features yourself', features: null },
|
|
16
17
|
];
|
|
@@ -88,6 +89,7 @@ export async function runWizard(prompter, options = {}) {
|
|
|
88
89
|
billing: featureSet.has('billing'),
|
|
89
90
|
ui: featureSet.has('ui'),
|
|
90
91
|
cli: featureSet.has('cli'),
|
|
92
|
+
mcp: featureSet.has('mcp'),
|
|
91
93
|
pm,
|
|
92
94
|
install,
|
|
93
95
|
git,
|
package/package.json
CHANGED