Skip to content

Commit bc830f5

Browse files
committed
fix(mcp): share one constSchema and prove ownership both ways
constSchema was hand-copied into src/commands/system/index.ts, drifting from the identical private helper in command-output-schemas.ts. Export it once from command-input.ts, next to the other schema-primitive builders, and import it at both sites. The ownership test for the projected family map only checked that each of the module's own entries names the module as owner; it did not check the inverse, so a system-family command the registry attributes to this module but missing from SYSTEM_COMMAND_OUTPUT_SCHEMAS would still pass. Compare the two sets directly.
1 parent 3d89d21 commit bc830f5

4 files changed

Lines changed: 25 additions & 11 deletions

File tree

‎src/commands/command-input.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ export function enumSchema(values: readonly string[], description?: string): Jso
106106
return { type: 'string', enum: values, ...(description ? { description } : {}) };
107107
}
108108

109+
/** A single-valued string schema, for a discriminant field an output shape always fixes. */
110+
export function constSchema(value: string): JsonSchema {
111+
return { type: 'string', const: value };
112+
}
113+
109114
export function stringSchema(description?: string): JsonSchema {
110115
return { type: 'string', ...(description ? { description } : {}) };
111116
}

‎src/commands/system/index.ts‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { CommandResultMap } from '@agent-device/command-registry/command-re
33
import type { ClipboardCommandOptions } from '@agent-device/contracts/client';
44
import {
55
type FoldKeyframe,
6+
FOLD_SCREEN_COORDINATE_SPACE,
67
MAX_FOLD_DURATION_MS,
78
MAX_FOLD_KEYFRAMES,
89
parseFoldInput,
@@ -12,7 +13,6 @@ import {
1213
FOLD_POSE_USAGE,
1314
parseDeviceRotation,
1415
} from '@agent-device/contracts/device';
15-
import { FOLD_SCREEN_COORDINATE_SPACE } from '@agent-device/contracts/fold-runtime';
1616
import { type BackMode, BACK_MODES } from '@agent-device/contracts/back-mode';
1717
import {
1818
TV_REMOTE_BUTTONS,
@@ -41,6 +41,7 @@ import {
4141
jsonSchemaField,
4242
readFieldInput,
4343
booleanSchema,
44+
constSchema,
4445
enumSchema,
4546
numberSchema,
4647
objectSchema,
@@ -74,10 +75,6 @@ const TV_REMOTE_LONGPRESS_PRESET_MS = 500;
7475
const CLIPBOARD_ACTION_VALUES = ['read', 'write'] as const;
7576
const KEYBOARD_METADATA_ACTION_VALUES = ['status', 'dismiss', 'enter', 'return'] as const;
7677

77-
function constSchema(value: string): JsonSchema {
78-
return { type: 'string', const: value };
79-
}
80-
8178
/**
8279
* This family's advertised MCP `outputSchema`s, keyed by daemon command name and projected into
8380
* the command map by `src/mcp/command-output-schemas.ts`. Non-strict like every other entry: no

‎src/mcp/__tests__/command-tools-system-schemas.test.ts‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import assert from 'node:assert/strict';
22
import { test } from 'vitest';
3-
import { ownerFilesForCommand } from '@agent-device/command-registry/owner-files';
3+
import {
4+
COMMAND_OWNER_FILES,
5+
ownerFilesForCommand,
6+
} from '@agent-device/command-registry/owner-files';
47
import { commandSupportsSettleObservation } from '@agent-device/command-registry/registry';
58
import { SYSTEM_COMMAND_OUTPUT_SCHEMAS } from '../../commands/system/index.ts';
69
import { COMMAND_OUTPUT_SCHEMAS } from '../command-output-schemas.ts';
@@ -39,13 +42,25 @@ test('a settle-derived system entry still grafts onto the module object, not a f
3942
);
4043
});
4144

42-
test('the projected family map declares exactly the commands its module owns', () => {
45+
test('every system-owned command claims this module, and the module claims nothing else', () => {
4346
for (const command of SYSTEM_COMMANDS) {
4447
assert.ok(
4548
ownerFilesForCommand(command).includes('src/commands/system/index.ts'),
4649
`${command} projects its output schema from this module but does not name it as its owner`,
4750
);
4851
}
52+
53+
const commandsOwnedByThisModule = (
54+
Object.entries(COMMAND_OWNER_FILES) as Array<[string, readonly string[]]>
55+
)
56+
.filter(([, ownerFiles]) => ownerFiles.includes('src/commands/system/index.ts'))
57+
.map(([command]) => command)
58+
.sort();
59+
assert.deepEqual(
60+
commandsOwnedByThisModule,
61+
[...SYSTEM_COMMANDS].sort(),
62+
'a command the registry attributes to this module is missing from SYSTEM_COMMAND_OUTPUT_SCHEMAS (or vice versa)',
63+
);
4964
});
5065

5166
// `appstate` on iOS answers from the session record, and from a live runner when one can read the

‎src/mcp/command-output-schemas.ts‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { CommandResultMap } from '@agent-device/command-registry/command-re
33
import { commandSupportsSettleObservation } from '@agent-device/command-registry/registry';
44
import {
55
booleanSchema,
6+
constSchema,
67
enumSchema,
78
looseObjectSchema,
89
numberSchema,
@@ -47,10 +48,6 @@ import { DEVICE_TARGETS, PUBLIC_PLATFORMS } from '@agent-device/kernel/device';
4748

4849
export const DEVICE_KINDS = ['simulator', 'emulator', 'device'] as const;
4950

50-
function constSchema(value: string): JsonSchema {
51-
return { type: 'string', const: value };
52-
}
53-
5451
function nullableStringSchema(description?: string): JsonSchema {
5552
return { type: ['string', 'null'], ...(description ? { description } : {}) };
5653
}

0 commit comments

Comments
 (0)