Merge branch 'v2/main' into v2/fix/2067-proxy-undici
One conflict, in `clients/tui/__tests__/App.test.tsx`: both sides added an `opts` capture to the test harness's `FakeClient` for different fields — `defaultMetadata` on v2/main (#1910), `environment.fetch` here (#2067) — so the resolution is the union of the two, keeping both rationales and both tests. Everything else auto-merged. `npm run ci` passes on the result. Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
commit
531cf84e60
1 changed files with 3739 additions and 691 deletions
|
|
@ -25,6 +25,8 @@ import {
|
|||
parseHeaderPair,
|
||||
} from "@inspector/core/mcp/node/index.js";
|
||||
import type { JsonValue } from "@inspector/core/mcp/index.js";
|
||||
import type { StrictJsonValue } from "@inspector/core/json/jsonUtils.js";
|
||||
import { isSerializableJson } from "@inspector/core/json/jsonUtils.js";
|
||||
import {
|
||||
canonicalUrlHost,
|
||||
isAllInterfacesHost,
|
||||
|
|
@ -34,7 +36,6 @@ import { consumeMethodOutcome } from "./handlers/consume-outcome.js";
|
|||
import { runMethod } from "./handlers/run-method.js";
|
||||
import {
|
||||
isOneShotMethod,
|
||||
metaValueToString,
|
||||
ONE_SHOT_METHODS,
|
||||
type MethodArgs,
|
||||
} from "./handlers/method-types.js";
|
||||
|
|
@ -480,8 +481,8 @@ function buildHandoff(
|
|||
|
||||
function parseKeyValuePair(
|
||||
value: string,
|
||||
previous: Record<string, JsonValue> = {},
|
||||
): Record<string, JsonValue> {
|
||||
previous: Record<string, StrictJsonValue> = {},
|
||||
): Record<string, StrictJsonValue> {
|
||||
const parts = value.split("=");
|
||||
const key = parts[0];
|
||||
const val = parts.slice(1).join("=");
|
||||
|
|
@ -492,13 +493,31 @@ function parseKeyValuePair(
|
|||
);
|
||||
}
|
||||
|
||||
let parsedValue: JsonValue;
|
||||
// `StrictJsonValue`: `JSON.parse` cannot produce `undefined`, and these values
|
||||
// become `_meta`, which must reach the wire exactly as written (#1910).
|
||||
let parsedValue: StrictJsonValue;
|
||||
try {
|
||||
parsedValue = JSON.parse(val) as JsonValue;
|
||||
parsedValue = JSON.parse(val) as StrictJsonValue;
|
||||
} catch {
|
||||
// Not JSON at all — a bare word or an unquoted string. Sent as a string,
|
||||
// which is what the user plainly meant.
|
||||
parsedValue = val;
|
||||
}
|
||||
|
||||
// Valid JSON syntax is not the same as sendable JSON: `1e400` parses to
|
||||
// `Infinity`, which `JSON.stringify` writes as `null`. Rejecting is better
|
||||
// than accepting the flag and silently transmitting a different value —
|
||||
// and better than falling back to the literal string, which would also not
|
||||
// be what was asked for.
|
||||
if (!isSerializableJson(parsedValue)) {
|
||||
// Names the key, never the value: the pair can carry a credential
|
||||
// (`credentials={"accessToken":"…","n":1e400}`) and this message lands in
|
||||
// stderr and CI logs.
|
||||
throw new Error(
|
||||
`Invalid value for "${key}": numbers must be finite (a literal like 1e400 overflows to Infinity and cannot be sent).`,
|
||||
);
|
||||
}
|
||||
|
||||
return { ...previous, [key as string]: parsedValue };
|
||||
}
|
||||
|
||||
|
|
@ -742,8 +761,8 @@ async function parseArgs(argv?: string[]): Promise<ParseResult> {
|
|||
promptName?: string;
|
||||
promptArgs?: Record<string, JsonValue>;
|
||||
logLevel?: LoggingLevel;
|
||||
metadata?: Record<string, JsonValue>;
|
||||
toolMetadata?: Record<string, JsonValue>;
|
||||
metadata?: Record<string, StrictJsonValue>;
|
||||
toolMetadata?: Record<string, StrictJsonValue>;
|
||||
cwd?: string;
|
||||
transport?: "sse" | "http" | "stdio";
|
||||
serverUrl?: string;
|
||||
|
|
@ -1005,22 +1024,12 @@ async function parseArgs(argv?: string[]): Promise<ParseResult> {
|
|||
promptName: options.promptName,
|
||||
promptArgs: options.promptArgs,
|
||||
logLevel: options.logLevel,
|
||||
metadata: options.metadata
|
||||
? Object.fromEntries(
|
||||
Object.entries(options.metadata).map(([key, value]) => [
|
||||
key,
|
||||
metaValueToString(value),
|
||||
]),
|
||||
)
|
||||
: undefined,
|
||||
toolMeta: options.toolMetadata
|
||||
? Object.fromEntries(
|
||||
Object.entries(options.toolMetadata).map(([key, value]) => [
|
||||
key,
|
||||
metaValueToString(value),
|
||||
]),
|
||||
)
|
||||
: undefined,
|
||||
// `--metadata`/`--tool-metadata` values are parsed as JSON, and `_meta`
|
||||
// takes any JSON — so they go through unflattened (#1910). They used to be
|
||||
// squeezed through `metaValueToString`, which sent `{"a":1}` as the
|
||||
// *string* `'{"a":1}'`.
|
||||
metadata: options.metadata,
|
||||
toolMeta: options.toolMetadata,
|
||||
appInfo: options.appInfo === true,
|
||||
format: options.format,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue