[GH-ISSUE #7580] [Bug] Schema discovery for mcp add applies an Amp registration #4515

Closed
opened 2026-10-31 00:50:25 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 27, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/7580

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

CLI

Observed behavior

node scripts/rea.mjs mcp add --agent amp --schema exits 0, announces that REA was registered, and creates $HOME/.config/amp/settings.json. Its output contains no argument schema. I reproduced this twice with separate empty homes. Adding --json also writes the registration and returns registration information rather than the requested schema.

The corresponding --help and --llms requests return discovery output without creating client settings.

Reproduction

  1. Use the already-built source baseline below with its locked dependencies and Node.js v24.22.1 on PATH. These checks used the prepared runtime without rebuilding it.

  2. From the checkout root, execute this isolated replay. Each request receives a new home and XDG directories. The preload prevents external process launches and network connections; Amp selection uses the direct settings writer, so the stored npx command is never executed.

    python3 -I - <<'PY'
    import json, os, pathlib, shutil, subprocess, tempfile
    
    node = shutil.which("node")
    assert node, "Put Node.js v24.22.1 on PATH first"
    with tempfile.TemporaryDirectory() as work:
        root = pathlib.Path(work)
        guard = root / "guard.mjs"
        guard.write_text('''
    import cp from 'node:child_process';
    import net from 'node:net';
    import tls from 'node:tls';
    import http from 'node:http';
    import https from 'node:https';
    import { syncBuiltinESMExports } from 'node:module';
    const deny = () => { throw new Error('Verifier blocked process/network'); };
    for (const key of ['exec','execFile','execSync','execFileSync','spawn','spawnSync','fork']) cp[key] = deny;
    net.Socket.prototype.connect = deny;
    net.connect = deny; net.createConnection = deny; tls.connect = deny;
    http.request = deny; http.get = deny; https.request = deny; https.get = deny;
    globalThis.fetch = deny;
    syncBuiltinESMExports();
    ''')
        for case, flag in [("schema-1", "--schema"),
                           ("schema-2", "--schema"), ("help", "--help")]:
            env = {"PATH": str(pathlib.Path(node).parent), "NO_COLOR": "1",
                   "NODE_OPTIONS": "--import=" + str(guard)}
            for key, part in {
                "HOME": "home", "XDG_CONFIG_HOME": "config",
                "XDG_DATA_HOME": "data", "XDG_CACHE_HOME": "cache",
                "XDG_STATE_HOME": "state", "TMPDIR": "tmp",
                "npm_config_prefix": "npm-global",
            }.items():
                directory = root / case / part
                directory.mkdir(parents=True, exist_ok=True)
                env[key] = str(directory)
            result = subprocess.run(
                [node, "scripts/rea.mjs", "mcp", "add", "--agent", "amp", flag],
                env=env, text=True, capture_output=True, timeout=30,
            )
            settings = pathlib.Path(env["HOME"]) / ".config/amp/settings.json"
            print(case, "exit=" + str(result.returncode),
                  "settings=" + str(settings.exists()))
            print(result.stdout)
            print("stderr=" + repr(result.stderr))
            if settings.exists():
                print(json.dumps(json.loads(settings.read_text())))
    PY
    
  3. Both schema cases report exit=0 settings=True; the help control reports exit=0 settings=False. The script removes the disposable settings after inspection.

Expected behavior

Root help advertises --schema as a Global Option that will Show JSON Schema for command. The manifest footer directs callers to rea <command> --schema for argument details. A schema request should therefore return discovery information without registering a server. If this builtin cannot expose a schema, it should explicitly refuse before changing client configuration.

A separate executed control, node scripts/rea.mjs inspect-devicetree --schema --json, returns its argument and option schemas without a target or client configuration write.

Impact and scope

P2: a documented discovery request produces a persistent settings change and misleading success. Verification observed one new Amp settings file per affected run, with an REA launcher entry; it did not exercise an existing configuration or start an Amp session. mcp add --help is a directly verified way to inspect the declared options without applying registration. Other clients and platforms were not tested, and no data loss was observed.

REA version

Exact node scripts/rea.mjs --version output: 6.8.0 followed by a newline.
Source baseline: 65f1c447344a423fa2785847dbcea1e9a9e2454a.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1; Darwin arm64. Locked Incur 0.6.1 and the prepared compiled runtime.

Target and artifact context

No analysis target. The fixture is an empty home and isolated XDG directories. The affected artifact is Amp settings containing a stored REA MCP launcher. No client application, provider, external runner, or network service was started.

Interface

CLI

Earliest failure stage

Request parsing or validation

Relevant output or evidence

Both executed --schema requests exited 0 with empty stderr and included:

Registering MCP server...
✓ Registered rea as MCP server
  Agents: Amp

Agents can now use rea tools.

The created settings, reduced to one line, were:

{"amp.mcpServers":{"rea":{"command":"npx","args":["-y","rea-agents@6.8.0","mcp"]}}}

The output also contains a terminal clear-line escape before the success message. --schema --json separately reproduced the settings write and printed an object with name, command, and agents, rather than a schema. --help printed the declared registration options; --llms printed the manifest. Neither created client files.

Source-derived reachability: scripts/rea.mjs:9-11,31-40 preserves the builtin registration route, and src/cli.ts:169-180 forwards normalized input. The pinned dispatcher executes its MCP builtin at node_modules/incur/src/Cli.ts:1239-1334, before its schema branch at :1509 onward. The Amp-only path skips external execution at node_modules/incur/src/SyncMcp.ts:21-33 and writes the settings directly at :51-82.

Typed outcome or recovery expected

Return the command schema, or an explicit unsupported-discovery error, with no client settings created or changed. Keep actual registration requests working.

Analysis or suspected cause

Source-derived: the builtin checks help before registration, then returns after running the registration handler. The schema flag is handled later in the dispatcher. This ordering explains why a recognized discovery flag fails to prevent the write. No proposed repair was implemented or tested.

Additional context

The same Amp invocation with --version also printed registration success and wrote settings; root --version returned only 6.8.0. This corroborates the builtin dispatch ordering issue.

Open #7375 and PR #7453 concern surplus positional arguments. This reproduction uses a documented global discovery switch and needs discovery or refusal, rather than positional rejection. The Amp reports #7535, #7536, and #7538 concern registration scope, false success, and preservation of existing settings respectively. #5811 and PR #6347 concern Windows external-runner startup. Open/closed issue, all-state PR, and comment searches found no report with this same discovery contract and closure signal. The first affected release is unknown.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Repeated schema-discovery requests created persistent Amp registration settings and returned registration success; the observed effect was bounded to disposable local settings, with no severe loss.
  • Workaround status: available
  • Workaround basis: The same command with --help displayed its declared options and created no client settings; callers can use that discovery path while avoiding --schema on this builtin.
Originally created by @coygeek on GitHub (Oct 27, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7580 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area CLI ### Observed behavior `node scripts/rea.mjs mcp add --agent amp --schema` exits 0, announces that REA was registered, and creates `$HOME/.config/amp/settings.json`. Its output contains no argument schema. I reproduced this twice with separate empty homes. Adding `--json` also writes the registration and returns registration information rather than the requested schema. The corresponding `--help` and `--llms` requests return discovery output without creating client settings. ### Reproduction 1. Use the already-built source baseline below with its locked dependencies and Node.js v24.22.1 on PATH. These checks used the prepared runtime without rebuilding it. 2. From the checkout root, execute this isolated replay. Each request receives a new home and XDG directories. The preload prevents external process launches and network connections; Amp selection uses the direct settings writer, so the stored `npx` command is never executed. ```sh python3 -I - <<'PY' import json, os, pathlib, shutil, subprocess, tempfile node = shutil.which("node") assert node, "Put Node.js v24.22.1 on PATH first" with tempfile.TemporaryDirectory() as work: root = pathlib.Path(work) guard = root / "guard.mjs" guard.write_text(''' import cp from 'node:child_process'; import net from 'node:net'; import tls from 'node:tls'; import http from 'node:http'; import https from 'node:https'; import { syncBuiltinESMExports } from 'node:module'; const deny = () => { throw new Error('Verifier blocked process/network'); }; for (const key of ['exec','execFile','execSync','execFileSync','spawn','spawnSync','fork']) cp[key] = deny; net.Socket.prototype.connect = deny; net.connect = deny; net.createConnection = deny; tls.connect = deny; http.request = deny; http.get = deny; https.request = deny; https.get = deny; globalThis.fetch = deny; syncBuiltinESMExports(); ''') for case, flag in [("schema-1", "--schema"), ("schema-2", "--schema"), ("help", "--help")]: env = {"PATH": str(pathlib.Path(node).parent), "NO_COLOR": "1", "NODE_OPTIONS": "--import=" + str(guard)} for key, part in { "HOME": "home", "XDG_CONFIG_HOME": "config", "XDG_DATA_HOME": "data", "XDG_CACHE_HOME": "cache", "XDG_STATE_HOME": "state", "TMPDIR": "tmp", "npm_config_prefix": "npm-global", }.items(): directory = root / case / part directory.mkdir(parents=True, exist_ok=True) env[key] = str(directory) result = subprocess.run( [node, "scripts/rea.mjs", "mcp", "add", "--agent", "amp", flag], env=env, text=True, capture_output=True, timeout=30, ) settings = pathlib.Path(env["HOME"]) / ".config/amp/settings.json" print(case, "exit=" + str(result.returncode), "settings=" + str(settings.exists())) print(result.stdout) print("stderr=" + repr(result.stderr)) if settings.exists(): print(json.dumps(json.loads(settings.read_text()))) PY ``` 3. Both schema cases report `exit=0 settings=True`; the help control reports `exit=0 settings=False`. The script removes the disposable settings after inspection. ### Expected behavior Root help advertises `--schema` as a Global Option that will `Show JSON Schema for command`. The manifest footer directs callers to `rea <command> --schema` for argument details. A schema request should therefore return discovery information without registering a server. If this builtin cannot expose a schema, it should explicitly refuse before changing client configuration. A separate executed control, `node scripts/rea.mjs inspect-devicetree --schema --json`, returns its argument and option schemas without a target or client configuration write. ### Impact and scope P2: a documented discovery request produces a persistent settings change and misleading success. Verification observed one new Amp settings file per affected run, with an REA launcher entry; it did not exercise an existing configuration or start an Amp session. `mcp add --help` is a directly verified way to inspect the declared options without applying registration. Other clients and platforms were not tested, and no data loss was observed. ### REA version Exact `node scripts/rea.mjs --version` output: `6.8.0` followed by a newline. Source baseline: `65f1c447344a423fa2785847dbcea1e9a9e2454a`. ### Runtime and platform Node.js v24.22.1; macOS 27.0.1; Darwin arm64. Locked Incur 0.6.1 and the prepared compiled runtime. ### Target and artifact context No analysis target. The fixture is an empty home and isolated XDG directories. The affected artifact is Amp settings containing a stored REA MCP launcher. No client application, provider, external runner, or network service was started. ### Interface CLI ### Earliest failure stage Request parsing or validation ### Relevant output or evidence Both executed `--schema` requests exited 0 with empty stderr and included: ```text Registering MCP server... ✓ Registered rea as MCP server Agents: Amp Agents can now use rea tools. ``` The created settings, reduced to one line, were: ```json {"amp.mcpServers":{"rea":{"command":"npx","args":["-y","rea-agents@6.8.0","mcp"]}}} ``` The output also contains a terminal clear-line escape before the success message. `--schema --json` separately reproduced the settings write and printed an object with `name`, `command`, and `agents`, rather than a schema. `--help` printed the declared registration options; `--llms` printed the manifest. Neither created client files. Source-derived reachability: `scripts/rea.mjs:9-11,31-40` preserves the builtin registration route, and `src/cli.ts:169-180` forwards normalized input. The pinned dispatcher executes its MCP builtin at `node_modules/incur/src/Cli.ts:1239-1334`, before its schema branch at `:1509` onward. The Amp-only path skips external execution at `node_modules/incur/src/SyncMcp.ts:21-33` and writes the settings directly at `:51-82`. ### Typed outcome or recovery expected Return the command schema, or an explicit unsupported-discovery error, with no client settings created or changed. Keep actual registration requests working. ### Analysis or suspected cause Source-derived: the builtin checks help before registration, then returns after running the registration handler. The schema flag is handled later in the dispatcher. This ordering explains why a recognized discovery flag fails to prevent the write. No proposed repair was implemented or tested. ### Additional context The same Amp invocation with `--version` also printed registration success and wrote settings; root `--version` returned only `6.8.0`. This corroborates the builtin dispatch ordering issue. Open [#7375](https://github.com/morluto/rea/issues/7375) and [PR #7453](https://github.com/morluto/rea/pull/7453) concern surplus positional arguments. This reproduction uses a documented global discovery switch and needs discovery or refusal, rather than positional rejection. The Amp reports [#7535](https://github.com/morluto/rea/issues/7535), [#7536](https://github.com/morluto/rea/issues/7536), and [#7538](https://github.com/morluto/rea/issues/7538) concern registration scope, false success, and preservation of existing settings respectively. [#5811](https://github.com/morluto/rea/issues/5811) and [PR #6347](https://github.com/morluto/rea/pull/6347) concern Windows external-runner startup. Open/closed issue, all-state PR, and comment searches found no report with this same discovery contract and closure signal. The first affected release is unknown. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Repeated schema-discovery requests created persistent Amp registration settings and returned registration success; the observed effect was bounded to disposable local settings, with no severe loss. - Workaround status: available - Workaround basis: The same command with --help displayed its declared options and created no client settings; callers can use that discovery path while avoiding --schema on this builtin.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4515
No description provided.