From 6c062c9c2ae603a58a8d74ee1d07bc4e03906e5c Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sun, 15 Nov 2026 08:01:15 +0800 Subject: [PATCH] fix(setup): stop writing the default compact input profile for Kimi and LM Studio (#10559) * fix(setup): stop writing the default compact input profile for Kimi and LM Studio The server already advertises compact input schemas when REA_MCP_INPUT_SCHEMA_PROFILE is unset, so setup no longer adds that value to Kimi Code and LM Studio registrations, and doctor/status no longer report a registration without it as stale. Explicit stored or setup-environment profiles are still preserved. Refs #10514 * fix(setup): validate LM Studio registration environment * test(hopper): retry cleanup after confirmed application exit --- docs/getting-started/client-configuration.md | 25 +++++++------------ src/application/ClientRegistrationStatus.ts | 22 +++++----------- src/application/SetupClientConfiguration.ts | 13 +--------- src/application/SupportedClients.ts | 6 ----- .../clientRegistrationStatus.test.ts | 24 ++++++++++++++++++ .../kimiClientConfiguration.test.ts | 18 +++++++------ .../lmStudioClientConfiguration.test.ts | 12 +++++---- 7 files changed, 57 insertions(+), 63 deletions(-) diff --git a/docs/getting-started/client-configuration.md b/docs/getting-started/client-configuration.md index 33b6dcf385..bb05075eb3 100644 --- a/docs/getting-started/client-configuration.md +++ b/docs/getting-started/client-configuration.md @@ -427,18 +427,12 @@ client environment overrides remain intact unless setup supplies the same key. You can edit `startupTimeoutMs` and `toolTimeoutMs` there; doctor keeps positive caller-selected values aligned and reports a non-blocking advisory when a tool timeout is shorter than the known provider allowance. -Setup selects the compact input-schema advertisement for new or refreshed Kimi -registrations unless their stored configuration or setup environment explicitly -selects `REA_MCP_INPUT_SCHEMA_PROFILE=full` or `compact`. This keeps REA's -canonical tool validation unchanged while avoiding a Moonshot Kimi Code 2.2.0 -failure observed with the full catalog: its request was rejected because a -single tool schema exceeded the provider's size limit. The limit is -provider-specific and not an MCP protocol limit. `rea doctor --client kimi` -reports an older registration with no explicit profile as stale. To restore the -compact profile, run `rea setup --client kimi` or add -`REA_MCP_INPUT_SCHEMA_PROFILE=compact` to the registration's `env`, then restart -Kimi Code. An explicit `full` profile remains respected and can still exceed -Moonshot's limit. +The server advertises compact input schemas by default, which avoids a Moonshot +Kimi Code 2.2.0 failure observed with the full catalog: its request was rejected +because a single tool schema exceeded the provider's size limit. The limit is +provider-specific and not an MCP protocol limit. Setup preserves an explicit +`REA_MCP_INPUT_SCHEMA_PROFILE` from the stored registration or the setup +environment; an explicit `full` profile can still exceed Moonshot's limit. Kimi Code also discovers personal skills in `~/.agents/skills` and project-local `.agents/skills`, so no separate skill root is required; setup still installs and verifies the matching skill files through the normal shared-skill flow. @@ -453,10 +447,9 @@ LM Studio does: the path stored in `~/.lmstudio-home-pointer`, otherwise the pointer file. LM Studio parses `mcp.json` as strict JSON, so setup rejects a file with comments, trailing commas, or a byte order mark without a backup or a write. The entry carries `command`, `args`, and `env`, and setup keeps -every other server. Local models often have small context windows, so setup -selects `REA_MCP_INPUT_SCHEMA_PROFILE=compact` unless the stored entry or the -setup environment selects a profile explicitly. Doctor reports an entry with no -explicit profile as stale, as for Kimi Code. LM Studio 0.4.26 on macOS loaded +every other server. Local models often have small context windows; the default +compact input-schema profile applies unless the stored entry or the setup +environment selects `REA_MCP_INPUT_SCHEMA_PROFILE` explicitly. LM Studio 0.4.26 on macOS loaded this registration, and a local model called an REA tool through it. Linux and Windows remain unverified. diff --git a/src/application/ClientRegistrationStatus.ts b/src/application/ClientRegistrationStatus.ts index a55ea4f4ae..eb477e90a6 100644 --- a/src/application/ClientRegistrationStatus.ts +++ b/src/application/ClientRegistrationStatus.ts @@ -11,7 +11,6 @@ import { lstat, stat } from "node:fs/promises"; import { err, ok, type Result } from "../domain/result.js"; import { PRODUCT_IDENTITY } from "../identity.js"; -import { snapshotEnvironment } from "../process/snapshotEnvironment.js"; import { CURRENT_ENTRYPOINT_PATH } from "../process/CurrentEntrypointPath.js"; import { MCP_STARTUP_POLICY, @@ -28,7 +27,6 @@ import { import { clientEvidencePaths, clientOwnershipEvidencePaths, - compactInputSchemaByDefault, manualRegistrationRemediation, supportedClients, } from "./SupportedClients.js"; @@ -337,15 +335,17 @@ export const readClientRegistrationInspections = async ( continue; } const registrationEnvironment = - compactInputSchemaByDefault(client) || client.name === "codex" + client.name === "kimi" || + client.name === "codex" || + client.name === "lm_studio" ? await clientRegistrationEnvironmentDetailsWithBudget( parsed, client, {}, "effective", - client.name === "kimi" || client.name === "codex" - ? options.resolveProviderRequestBudget - : undefined, + client.name === "lm_studio" + ? undefined + : options.resolveProviderRequestBudget, options.platform, ) : undefined; @@ -375,12 +375,6 @@ export const readClientRegistrationInspections = async ( registration, client: effectiveClient, commandFacts, - inputSchemaProfileSelected: - registrationEnvironment?.ok === true && - snapshotEnvironment( - registrationEnvironment.value.values, - options.platform, - ).REA_MCP_INPUT_SCHEMA_PROFILE !== undefined, }); record( configuredStatus( @@ -510,12 +504,10 @@ const registrationAligned = ({ registration, client, commandFacts, - inputSchemaProfileSelected, }: { readonly registration: ClientRegistration; readonly client: SetupClient; readonly commandFacts: ClientRegistrationCommandFacts; - readonly inputSchemaProfileSelected: boolean; }): "aligned" | "stale" | "unknown" => { const { identity } = commandFacts; if (identity === "unknown") return "unknown"; @@ -537,8 +529,6 @@ const registrationAligned = ({ registration.toolTimeoutMs === undefined) ) return "stale"; - if (compactInputSchemaByDefault(client) && !inputSchemaProfileSelected) - return "stale"; // Native transport is admitted separately; the Harness route must name REA. if (client.format === "dsh" && registration.serverName !== "rea") return "stale"; diff --git a/src/application/SetupClientConfiguration.ts b/src/application/SetupClientConfiguration.ts index 7cddd994ba..f220f35224 100644 --- a/src/application/SetupClientConfiguration.ts +++ b/src/application/SetupClientConfiguration.ts @@ -47,7 +47,6 @@ import { } from "./ClientRegistration.js"; import { PRODUCT_IDENTITY } from "../identity.js"; -import { snapshotEnvironment } from "../process/snapshotEnvironment.js"; import { currentClientRegistrationCommand } from "./ClientRegistrationIdentity.js"; import { resolveClientConfigTransactionPath } from "./ClientConfigPath.js"; import { packageReadTargetChanged } from "./MsixPackageStorage.js"; @@ -67,7 +66,6 @@ import type { SetupProviderEnvironment, } from "./SetupTypes.js"; import { - compactInputSchemaByDefault, registrationWriteBlocker, type SetupClient, } from "./SupportedClients.js"; @@ -940,20 +938,11 @@ const clientConfigurationDesired = async ( "fragment", ); if (!environment.ok) throw environment.error; - const registrationEnvironment = - compactInputSchemaByDefault(client) && - snapshotEnvironment(environment.value).REA_MCP_INPUT_SCHEMA_PROFILE === - undefined - ? { - ...environment.value, - REA_MCP_INPUT_SCHEMA_PROFILE: "compact", - } - : environment.value; const registration = refreshClientRegistration( client, format, launcher, - registrationEnvironment, + environment.value, parsed === undefined ? undefined : effectiveClientServer(parsed, PRODUCT_IDENTITY.mcpServerKey), diff --git a/src/application/SupportedClients.ts b/src/application/SupportedClients.ts index 08456e0ec6..b271ba0869 100644 --- a/src/application/SupportedClients.ts +++ b/src/application/SupportedClients.ts @@ -190,12 +190,6 @@ const codexDirectory = ({ home, env }: ClientPathContext): string => const kimiDirectory = ({ home, env }: ClientPathContext): string => env.KIMI_CODE_HOME || join(home, ".kimi-code"); -/** Clients registered with the compact input-schema profile unless one is selected. */ -export const compactInputSchemaByDefault = ({ - name, -}: Pick): boolean => - name === "kimi" || name === "lm_studio"; - /** * LM Studio's findLMStudioHome, read-only: a home pointer file wins, then an * existing ~/.cache/lm-studio, then ~/.lmstudio. Setup never writes the pointer. diff --git a/tests/boundary/filesystem/clientRegistrationStatus.test.ts b/tests/boundary/filesystem/clientRegistrationStatus.test.ts index 7c7daa9202..2774f14210 100644 --- a/tests/boundary/filesystem/clientRegistrationStatus.test.ts +++ b/tests/boundary/filesystem/clientRegistrationStatus.test.ts @@ -136,6 +136,30 @@ describe("client registration status", () => { }); describe("client registration environment validation", () => { + it("reports invalid LM Studio runtime settings without requiring an explicit profile", async () => { + const home = await createTestTempDirectory("rea-lm-studio-env-"); + await mkdir(join(home, ".lmstudio")); + await writeFile( + join(home, ".lmstudio/mcp.json"), + JSON.stringify({ + mcpServers: { + rea: { + command: "npx", + args: ["-y", PRODUCT_IDENTITY.registrationPackageSpecifier, "mcp"], + env: { REA_MCP_INPUT_SCHEMA_PROFILE: "invalid" }, + }, + }, + }), + ); + const statuses = await readClientRegistrationStatuses(home, "/current/rea"); + expect(statuses).toContainEqual( + expect.objectContaining({ + client: "lm_studio", + state: "invalid", + remediation: expect.stringContaining("REA_MCP_INPUT_SCHEMA_PROFILE"), + }), + ); + }); it("reports an otherwise aligned registration invalid for a bad tool-family scope", async () => { const home = await createTestTempDirectory("rea-registration-env-"); await mkdir(join(home, ".cursor")); diff --git a/tests/boundary/filesystem/kimiClientConfiguration.test.ts b/tests/boundary/filesystem/kimiClientConfiguration.test.ts index 925e5212ad..3912e5db71 100644 --- a/tests/boundary/filesystem/kimiClientConfiguration.test.ts +++ b/tests/boundary/filesystem/kimiClientConfiguration.test.ts @@ -38,12 +38,14 @@ const exists = async (path: string): Promise => { const expectedRegistration = ( launcher: readonly string[], - profile = "compact", + profile?: "full" | "compact", toolTimeoutMs = 450_000, ) => ({ command: launcher[0], args: launcher.slice(1), - env: { REA_MCP_INPUT_SCHEMA_PROFILE: profile }, + ...(profile === undefined + ? {} + : { env: { REA_MCP_INPUT_SCHEMA_PROFILE: profile } }), startupTimeoutMs: MCP_STARTUP_POLICY.kimiStartupTimeoutMs, toolTimeoutMs, }); @@ -318,7 +320,7 @@ describe("Kimi Code input-schema profile selection", () => { }, ); - it("reports an omitted-profile registration stale until setup adds compact", async () => { + it("reports an omitted-profile registration aligned because compact is the server default", async () => { const { home, client } = await kimiClient(); await writeFile( client.configPath, @@ -340,13 +342,13 @@ describe("Kimi Code input-schema profile selection", () => { environment: {}, }) ).find(({ client: name }) => name === "kimi"), - ).toMatchObject({ state: "stale", command }); - expect( - (await configureClientConfiguration(client, {}, command)).status, - ).toBe("configured"); + ).toMatchObject({ state: "aligned", command }); + expect(await configureClientConfiguration(client, {}, command)).toEqual({ + status: "unchanged", + }); expect( JSON.parse(await readFile(client.configPath, "utf8")).mcpServers.rea, - ).toEqual(expectedRegistration(command, "compact", 120_000)); + ).toEqual(expectedRegistration(command, undefined, 120_000)); }); }); diff --git a/tests/boundary/filesystem/lmStudioClientConfiguration.test.ts b/tests/boundary/filesystem/lmStudioClientConfiguration.test.ts index 97518ec0c3..c132bbe49c 100644 --- a/tests/boundary/filesystem/lmStudioClientConfiguration.test.ts +++ b/tests/boundary/filesystem/lmStudioClientConfiguration.test.ts @@ -30,10 +30,12 @@ const exists = async (path: string): Promise => { } }; -const expectedRegistration = (profile = "compact") => ({ +const expectedRegistration = (profile?: "full" | "compact") => ({ command: command[0], args: command.slice(1), - env: { REA_MCP_INPUT_SCHEMA_PROFILE: profile }, + ...(profile === undefined + ? {} + : { env: { REA_MCP_INPUT_SCHEMA_PROFILE: profile } }), }); const doctorState = async (home: string) => @@ -89,7 +91,7 @@ describe("LM Studio home resolution", () => { describe("LM Studio registration lifecycle", () => { it.each(["default", "pointer", "cache"] as const)( - "registers rea with the compact profile in the %s home", + "registers rea with the default input profile in the %s home", async (layout) => { const { home, directory, client } = await installedHome(layout); const pointer = join(home, ".lmstudio-home-pointer"); @@ -155,7 +157,7 @@ describe("LM Studio registration lifecycle", () => { }); }); - it("keeps an explicit full profile and reports an omitted profile stale", async () => { + it("keeps an explicit full profile and reports an omitted profile aligned", async () => { const { home, client } = await installedHome("default"); await writeFile( client.configPath, @@ -163,7 +165,7 @@ describe("LM Studio registration lifecycle", () => { mcpServers: { rea: { command: command[0], args: command.slice(1) } }, }), ); - expect(await doctorState(home)).toMatchObject({ state: "stale" }); + expect(await doctorState(home)).toMatchObject({ state: "aligned" }); expect( (