[GH-ISSUE #8950] [Bug] read-adb-setting CLI schema omits enforced setting-key constraints #7868

Open
opened 2026-11-05 02:37:35 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Nov 5, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/8950

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 read-adb-setting --schema --json advertises key as a nonempty string with no maximum or identifier pattern. Ajv accepts key with space, key!, and a 257-character key against that emitted args schema. Executing the corresponding CLI requests exits 1 with invalid_request / invalid_input, naming the key format or maximum 256.

The actual stdio MCP read_adb_setting schema includes those constraints and rejects all three inputs. This is a CLI discovery mismatch. Runtime admission correctly refuses these requests before invoking ADB, and retains useful field correction details.

Reproduction

Use Node.js 24.22.1. Start at the source baseline below with locked dependencies installed and the runtime built (npm ci and npm run build:cached are the repository setup commands; verification used the provided existing build). Run the following probe from the checkout root. It isolates state and selects an inert ADB executable that records invocation and exits 97, without returning invented Android data or contacting a device.

node --input-type=module <<'JS'
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { mkdtempSync, mkdirSync, writeFileSync, existsSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve, dirname } from 'node:path';
import { createRequire } from 'node:module';
const require = createRequire(resolve('package.json'));
const { default: Ajv2020 } = require('ajv/dist/2020.js');
const entry = resolve('scripts/rea.mjs');
const work = mkdtempSync(join(tmpdir(), 'rea-setting-schema-'));
const env = { PATH: `${dirname(process.execPath)}:/usr/bin:/bin`, REA_LOG_LEVEL: 'silent', NO_COLOR: '1' };
for (const key of ['HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_CACHE_HOME', 'XDG_STATE_HOME', 'TMPDIR']) {
  env[key] = join(work, key); mkdirSync(env[key]);
}
const calls = join(work, 'adb-called');
env.REA_ADB_PATH = join(work, 'adb');
writeFileSync(env.REA_ADB_PATH, `#!/bin/sh\nprintf 'called\\n' >> '${calls}'\nexit 97\n`, { mode: 0o700 });
const run = args => {
  const p = spawnSync(process.execPath, [entry, ...args], { env, cwd: work, encoding: 'utf8', timeout: 30000 });
  assert.ifError(p.error); assert.equal(p.stderr, '');
  return { exit: p.status, body: JSON.parse(p.stdout) };
};
try {
  const schema = run(['read-adb-setting', '--schema', '--json']).body;
  console.log(JSON.stringify({ keySchema: schema.args.properties.key }));
  const validate = new Ajv2020({ strict: false }).compile(schema.args);
  for (const [name, key] of [['space', 'key with space'], ['punctuation', 'key!'], ['long', 'a'.repeat(257)], ['empty', ''], ['dotted', 'a.b'], ['max', 'a'.repeat(256)]]) {
    rmSync(calls, { force: true });
    const schemaAccepts = validate({ serial: 'emulator-5554', namespace: 'secure', key });
    const result = run(['read-adb-setting', 'emulator-5554', 'secure', key, '--json']);
    const adbInvoked = existsSync(calls);
    console.log(JSON.stringify({ name, schemaAccepts, exit: result.exit, code: result.body.code, issues: result.body.details?.issues, adbInvoked }));
    assert.equal(schemaAccepts, name !== 'empty');
    assert.equal(result.exit, 1);
    assert.equal(adbInvoked, ['dotted', 'max'].includes(name));
    if (['space', 'punctuation', 'long'].includes(name)) assert.equal(result.body.code, 'invalid_request');
  }
} finally { rmSync(work, { recursive: true, force: true }); }
JS

This complete probe ran once and passed every assertion on the source baseline. A separate production-boundary probe ran once on the baseline and once on the latest published 6.8.0 package, with the same result; it also collected actual stdio MCP schemas and sent a malformed tool call. No project code was changed.

Expected behavior

CLI discovery should expose the existing setting-key maximum and identifier grammar so schema consumers reject malformed input before invocation. Preserve the current canonical refusal, field-specific correction information, and admitted key values. No specific CLI error-code spelling is required.

The repository's CLI translation policy requires canonical semantic fields and schema/parser parity checks. Exact references appear under Relevant output or evidence.

Impact and scope

P3: the observed defect is misleading discovery metadata for malformed input already refused with useful diagnostics. A schema-driven wrapper can accept a key that the CLI refuses. No valid-request failure, wrong successful result, target mutation, data loss, or consequential recovery failure was demonstrated.

Verified scope is read-adb-setting key discovery on one macOS arm64 host, on the source baseline and published 6.8.0. Empty keys are correctly excluded. Dotted keys and exactly 256 identifier characters pass admission and reach the inert client's version check; successful real-device analysis was not tested. Callers can prevalidate against the existing MCP constraints or correct the field using the runtime diagnostic before retrying.

REA version

6.8.0 from both the baseline launcher and the separately installed latest published package. Source baseline: c3647ade1e557c4179a8b825a37e0c2193cd2526.

The registry returned 6.8.0 for npm view rea-agents version. Current main at 71f680d7c1aa7a45ba0325fe87b104744b0c105f retains the same declaration and unchanged affected admission/contract paths by source comparison; main was not rebuilt or runtime-tested.

Runtime and platform

Node.js v24.22.1; macOS 27.2; arm64. Baseline locked dependencies; published package installed separately with npm lifecycle scripts disabled. No real ADB or Android device version applies to this pre-execution validation check.

Target and artifact context

Inert device serial emulator-5554 and namespace secure. HOME, XDG paths and TMPDIR are isolated. The selected executable records invocation and refuses all commands; malformed cases never invoke it. Positive admission controls reach only its version check and receive the expected unusable-client error.

Interface

CLI

Earliest failure stage

Request parsing or validation

Relevant output or evidence

Observed CLI key schema:

{"type":"string","minLength":1,"description":"Setting key to read"}

Observed actual MCP key schema:

{"type":"string","minLength":1,"maxLength":256,"pattern":"^[A-Za-z0-9._\\x2d]+$"}

Observed reduced reproduction output:

{"name":"space","schemaAccepts":true,"exit":1,"code":"invalid_request","issues":[{"path":["key"],"reason":"invalid_format","message":"Setting keys are restricted to identifiers","expected":"regex"}],"adbInvoked":false}
{"name":"punctuation","schemaAccepts":true,"exit":1,"code":"invalid_request","issues":[{"path":["key"],"reason":"invalid_format","message":"Setting keys are restricted to identifiers","expected":"regex"}],"adbInvoked":false}
{"name":"long","schemaAccepts":true,"exit":1,"code":"invalid_request","issues":[{"path":["key"],"reason":"out_of_range","maximum":256}],"adbInvoked":false}
{"name":"empty","schemaAccepts":false,"exit":1,"code":"VALIDATION_ERROR","adbInvoked":false}
{"name":"dotted","schemaAccepts":true,"exit":1,"code":"capability_unavailable","adbInvoked":true}
{"name":"max","schemaAccepts":true,"exit":1,"code":"capability_unavailable","adbInvoked":true}

A real stdio MCP call with key: "key with space" returned isError:true and invalid_request, with the message The arguments for MCP tool read_adb_setting do not match its advertised input schema. Its details retained the same key-format issue shown above. Ajv rejected space, punctuation, overlong and empty keys against the MCP schema, and accepted both positive controls.

Source references at the stated baseline:

  • docs/tool-design.md:519-526: canonical CLI semantic fields and joint emitted-schema/parser checks.
  • src/cli/adbCommands.ts:244-259: independent nonempty-only key declaration and command dispatch.
  • src/domain/adb/adbDeviceAnalysis.ts:52-53,249-259: maximum 256 and identifier grammar.
  • src/contracts/adb/adbToolContracts.ts:236-242: named MCP contract uses the canonical schema.
  • src/application/adb/AdbDeviceAnalysisService.ts:17-33 and src/application/ProviderOperationExecution.ts:60-73: shared validation before provider execution.

Typed outcome or recovery expected

Expose the canonical maximum and identifier grammar in the CLI schema and enforce them during CLI parsing. Keep the existing field name, safe format/maximum metadata, and refusal before device work.

Analysis or suspected cause

Source-derived: the CLI independently declares a weaker string than adbInputSchemas.read_adb_setting.shape.key. The CLI's schema reflects that declaration, while shared workflow admission enforces the complete canonical constraint before the provider. Reusing the owning semantic field would address the mismatch; no fix was implemented or verified. An introducing commit and last-known-good release were not established.

Additional context

Closed #7676 and merged PR #7722 addressed the same kind of discovery mismatch for serial, package and activity selectors. Their scope omitted this setting-key field, which remains weaker in both the baseline and current main.

Open #7677 and PR #7789 concern Android/browser selectors and mapping paths, rather than this ADB argument. Closed #1581 and merged PR #1580 repaired MCP regex dialect compatibility; the current portable pattern is present and working here.

All-state issue/PR searches covered command names, the MCP alias, setting-key wording, the exact identifier diagnostic, and ADB CLI schema/constraint terms. Related bodies, comments and the ADB closure/fix relationship were checked; no current open exact duplicate was found.

Originally created by @coygeek on GitHub (Nov 5, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8950 ### 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 read-adb-setting --schema --json` advertises `key` as a nonempty string with no maximum or identifier pattern. Ajv accepts `key with space`, `key!`, and a 257-character key against that emitted args schema. Executing the corresponding CLI requests exits 1 with `invalid_request` / `invalid_input`, naming the key format or maximum 256. The actual stdio MCP `read_adb_setting` schema includes those constraints and rejects all three inputs. This is a CLI discovery mismatch. Runtime admission correctly refuses these requests before invoking ADB, and retains useful field correction details. ### Reproduction Use Node.js 24.22.1. Start at the source baseline below with locked dependencies installed and the runtime built (`npm ci` and `npm run build:cached` are the repository setup commands; verification used the provided existing build). Run the following probe from the checkout root. It isolates state and selects an inert ADB executable that records invocation and exits 97, without returning invented Android data or contacting a device. ```sh node --input-type=module <<'JS' import assert from 'node:assert/strict'; import { spawnSync } from 'node:child_process'; import { mkdtempSync, mkdirSync, writeFileSync, existsSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join, resolve, dirname } from 'node:path'; import { createRequire } from 'node:module'; const require = createRequire(resolve('package.json')); const { default: Ajv2020 } = require('ajv/dist/2020.js'); const entry = resolve('scripts/rea.mjs'); const work = mkdtempSync(join(tmpdir(), 'rea-setting-schema-')); const env = { PATH: `${dirname(process.execPath)}:/usr/bin:/bin`, REA_LOG_LEVEL: 'silent', NO_COLOR: '1' }; for (const key of ['HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_CACHE_HOME', 'XDG_STATE_HOME', 'TMPDIR']) { env[key] = join(work, key); mkdirSync(env[key]); } const calls = join(work, 'adb-called'); env.REA_ADB_PATH = join(work, 'adb'); writeFileSync(env.REA_ADB_PATH, `#!/bin/sh\nprintf 'called\\n' >> '${calls}'\nexit 97\n`, { mode: 0o700 }); const run = args => { const p = spawnSync(process.execPath, [entry, ...args], { env, cwd: work, encoding: 'utf8', timeout: 30000 }); assert.ifError(p.error); assert.equal(p.stderr, ''); return { exit: p.status, body: JSON.parse(p.stdout) }; }; try { const schema = run(['read-adb-setting', '--schema', '--json']).body; console.log(JSON.stringify({ keySchema: schema.args.properties.key })); const validate = new Ajv2020({ strict: false }).compile(schema.args); for (const [name, key] of [['space', 'key with space'], ['punctuation', 'key!'], ['long', 'a'.repeat(257)], ['empty', ''], ['dotted', 'a.b'], ['max', 'a'.repeat(256)]]) { rmSync(calls, { force: true }); const schemaAccepts = validate({ serial: 'emulator-5554', namespace: 'secure', key }); const result = run(['read-adb-setting', 'emulator-5554', 'secure', key, '--json']); const adbInvoked = existsSync(calls); console.log(JSON.stringify({ name, schemaAccepts, exit: result.exit, code: result.body.code, issues: result.body.details?.issues, adbInvoked })); assert.equal(schemaAccepts, name !== 'empty'); assert.equal(result.exit, 1); assert.equal(adbInvoked, ['dotted', 'max'].includes(name)); if (['space', 'punctuation', 'long'].includes(name)) assert.equal(result.body.code, 'invalid_request'); } } finally { rmSync(work, { recursive: true, force: true }); } JS ``` This complete probe ran once and passed every assertion on the source baseline. A separate production-boundary probe ran once on the baseline and once on the latest published 6.8.0 package, with the same result; it also collected actual stdio MCP schemas and sent a malformed tool call. No project code was changed. ### Expected behavior CLI discovery should expose the existing setting-key maximum and identifier grammar so schema consumers reject malformed input before invocation. Preserve the current canonical refusal, field-specific correction information, and admitted key values. No specific CLI error-code spelling is required. The repository's CLI translation policy requires canonical semantic fields and schema/parser parity checks. Exact references appear under Relevant output or evidence. ### Impact and scope P3: the observed defect is misleading discovery metadata for malformed input already refused with useful diagnostics. A schema-driven wrapper can accept a key that the CLI refuses. No valid-request failure, wrong successful result, target mutation, data loss, or consequential recovery failure was demonstrated. Verified scope is `read-adb-setting` key discovery on one macOS arm64 host, on the source baseline and published 6.8.0. Empty keys are correctly excluded. Dotted keys and exactly 256 identifier characters pass admission and reach the inert client's version check; successful real-device analysis was not tested. Callers can prevalidate against the existing MCP constraints or correct the field using the runtime diagnostic before retrying. ### REA version `6.8.0` from both the baseline launcher and the separately installed latest published package. Source baseline: `c3647ade1e557c4179a8b825a37e0c2193cd2526`. The registry returned `6.8.0` for `npm view rea-agents version`. Current main at `71f680d7c1aa7a45ba0325fe87b104744b0c105f` retains the same declaration and unchanged affected admission/contract paths by source comparison; main was not rebuilt or runtime-tested. ### Runtime and platform Node.js v24.22.1; macOS 27.2; arm64. Baseline locked dependencies; published package installed separately with npm lifecycle scripts disabled. No real ADB or Android device version applies to this pre-execution validation check. ### Target and artifact context Inert device serial `emulator-5554` and namespace `secure`. HOME, XDG paths and TMPDIR are isolated. The selected executable records invocation and refuses all commands; malformed cases never invoke it. Positive admission controls reach only its version check and receive the expected unusable-client error. ### Interface CLI ### Earliest failure stage Request parsing or validation ### Relevant output or evidence Observed CLI key schema: ```json {"type":"string","minLength":1,"description":"Setting key to read"} ``` Observed actual MCP key schema: ```json {"type":"string","minLength":1,"maxLength":256,"pattern":"^[A-Za-z0-9._\\x2d]+$"} ``` Observed reduced reproduction output: ```json {"name":"space","schemaAccepts":true,"exit":1,"code":"invalid_request","issues":[{"path":["key"],"reason":"invalid_format","message":"Setting keys are restricted to identifiers","expected":"regex"}],"adbInvoked":false} {"name":"punctuation","schemaAccepts":true,"exit":1,"code":"invalid_request","issues":[{"path":["key"],"reason":"invalid_format","message":"Setting keys are restricted to identifiers","expected":"regex"}],"adbInvoked":false} {"name":"long","schemaAccepts":true,"exit":1,"code":"invalid_request","issues":[{"path":["key"],"reason":"out_of_range","maximum":256}],"adbInvoked":false} {"name":"empty","schemaAccepts":false,"exit":1,"code":"VALIDATION_ERROR","adbInvoked":false} {"name":"dotted","schemaAccepts":true,"exit":1,"code":"capability_unavailable","adbInvoked":true} {"name":"max","schemaAccepts":true,"exit":1,"code":"capability_unavailable","adbInvoked":true} ``` A real stdio MCP call with `key: "key with space"` returned `isError:true` and `invalid_request`, with the message `The arguments for MCP tool read_adb_setting do not match its advertised input schema.` Its details retained the same key-format issue shown above. Ajv rejected space, punctuation, overlong and empty keys against the MCP schema, and accepted both positive controls. Source references at the stated baseline: - `docs/tool-design.md:519-526`: canonical CLI semantic fields and joint emitted-schema/parser checks. - `src/cli/adbCommands.ts:244-259`: independent nonempty-only key declaration and command dispatch. - `src/domain/adb/adbDeviceAnalysis.ts:52-53,249-259`: maximum 256 and identifier grammar. - `src/contracts/adb/adbToolContracts.ts:236-242`: named MCP contract uses the canonical schema. - `src/application/adb/AdbDeviceAnalysisService.ts:17-33` and `src/application/ProviderOperationExecution.ts:60-73`: shared validation before provider execution. ### Typed outcome or recovery expected Expose the canonical maximum and identifier grammar in the CLI schema and enforce them during CLI parsing. Keep the existing field name, safe format/maximum metadata, and refusal before device work. ### Analysis or suspected cause Source-derived: the CLI independently declares a weaker string than `adbInputSchemas.read_adb_setting.shape.key`. The CLI's schema reflects that declaration, while shared workflow admission enforces the complete canonical constraint before the provider. Reusing the owning semantic field would address the mismatch; no fix was implemented or verified. An introducing commit and last-known-good release were not established. ### Additional context Closed [#7676](https://github.com/morluto/rea/issues/7676) and merged [PR #7722](https://github.com/morluto/rea/pull/7722) addressed the same kind of discovery mismatch for serial, package and activity selectors. Their scope omitted this setting-key field, which remains weaker in both the baseline and current main. Open [#7677](https://github.com/morluto/rea/issues/7677) and [PR #7789](https://github.com/morluto/rea/pull/7789) concern Android/browser selectors and mapping paths, rather than this ADB argument. Closed [#1581](https://github.com/morluto/rea/issues/1581) and merged [PR #1580](https://github.com/morluto/rea/pull/1580) repaired MCP regex dialect compatibility; the current portable pattern is present and working here. All-state issue/PR searches covered command names, the MCP alias, setting-key wording, the exact identifier diagnostic, and ADB CLI schema/constraint terms. Related bodies, comments and the ADB closure/fix relationship were checked; no current open exact duplicate was found.
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#7868
No description provided.