Skip to content

Commit 8e8cfc6

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-22270-print-record-door
2 parents d3a78e5 + 7d6ec91 commit 8e8cfc6

4 files changed

Lines changed: 384 additions & 2 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@objectstack/cli': patch
3+
---
4+
5+
Clause-②: no
6+
7+
fix(cli): `os start` with the stdio MCP transport answers the `initialize` an MCP client writes when it spawns the server, and boots the configured app
8+
9+
- **What was wrong.** An MCP client writes `initialize` to the server's stdin as soon as it spawns it. `os start` hands its stdin down to the processes it starts, and the CLI's argument parser fills an omitted positional argument from a pending stdin line. So the client's `initialize` became a config path. In a project with no `dist/objectstack.json`, the automatic compile step failed with `Config file not found: …/{"jsonrpc":"2.0",…}` and `os start` exited 1. In a project with a compiled artifact, the server printed `No objectstack.config.ts found — booting from artifact`, booted without the config's own plugins, and never answered `initialize`.
10+
- **What changed.** The automatic compile step now gets no stdin, and `os start` names the config path when it starts `os serve` (the same `objectstack.config.ts` in the working directory that `os serve` used by default). The client's first message stays in the pipe for the stdio transport, and the config is loaded.
11+
- **Unchanged:** the flags and environment variables of `os start`, `os serve` and `os compile`, and which config and artifact a boot uses.

‎packages/cli/src/commands/start.ts‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,19 @@ export default class Start extends Command {
240240
const outputPath = path.resolve(cwd, CONVENTIONAL_ARTIFACT_RELATIVE_PATH);
241241
printStep('Compiling objectstack.config.ts → dist/objectstack.json...');
242242
const binPath = process.argv[1];
243+
// ── stdin belongs to the protocol, never to the compile step (#24002) ──
244+
// This command's stdin is the stdio MCP transport's input once the
245+
// `serve` child below starts it, and an MCP client writes `initialize`
246+
// the moment it spawns `os start` — before this one-shot child runs.
247+
// Inherited, the child's argument parser read that pending line as its
248+
// missing `config` positional (oclif's `tryStdin`), so the compile failed
249+
// on "Config file not found: …/{"jsonrpc":"2.0",…}" and `initialize` was
250+
// consumed before any transport existed. The compile reads no input, so
251+
// it gets none: `'ignore'` keeps every byte in the pipe for `serve`.
243252
const compileResult = spawnSync(
244253
process.execPath,
245254
[binPath, 'compile', '--output', outputPath],
246-
{ stdio: 'inherit', env: process.env },
255+
{ stdio: ['ignore', 'inherit', 'inherit'], env: process.env },
247256
);
248257
if (compileResult.status !== 0) {
249258
printError('Compile failed — fix errors above before starting the server');
@@ -487,11 +496,25 @@ export default class Start extends Command {
487496
// `const child = spawn(` is the anchor
488497
// `utils/port-contract-single-source.test.ts` reads to hold the port
489498
// door above AHEAD of this spawn — keep the spelling.
499+
//
500+
// ── The config positional is NAMED, never left to stdin (#24002) ──
501+
// stdio stays inherited: the child's stdio MCP transport reads this
502+
// process's stdin. But oclif fills an omitted positional from a pending
503+
// stdin line (`tryStdin`), and `serve`'s `config` is one — so with no
504+
// positional here, an MCP client's `initialize`, written at spawn,
505+
// became "the config path": the child booted without the config the
506+
// banner above names ("No objectstack.config.ts found") and the
507+
// transport never saw `initialize`. A supplied positional skips that
508+
// read. `projectConfigPath` is the path `serve` would default to
509+
// (`objectstack.config.ts` under this same cwd), and the same one the
510+
// `Config:` row was judged by, so whether it joins the boot is still
511+
// the child's own `cwdConfigJoinsBoot` reading — exactly as before.
490512
const child = spawn(
491513
process.execPath,
492514
[
493515
binPath,
494516
'serve',
517+
projectConfigPath,
495518
flags.ui ? '--ui' : '--no-ui',
496519
...(flags.verbose ? ['--verbose'] : []),
497520
...(flags['log-level'] ? ['--log-level', flags['log-level']] : []),

0 commit comments

Comments
 (0)