mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 19:23:24 +00:00
feat(mcp)!: retire the full schema profiles and declare schema facts as typed annotations (#10693)
* test(mcp): check advertised schemas against a portable dialect in every profile * fix(mcp): re-emit advertised patterns in a portable regex grammar * fix(mcp): present discover_tools input through the selected profile The hot-cold catalog registered discover_tools directly with the SDK, so the default compact input profile still advertised its canonical schema, including page-size defaults clients apply on their own (#5322). Route every listed input schema, transport discovery included, through one server-owned presentation, and pass the selected presentation to the deferred server, which serves recorded tool schemas but not discovery. * fix(mcp): keep the Evidence union on described borrowed inputs borrowEvidenceInput installed its advertised projection on one schema instance. A call site's .describe() rebuilds the schema from its definition, so match_native_functions advertised left and right as unconstrained values in every input profile. Install the projection in the borrowed parser's constructor so derived schemas keep it, for both borrowed inputs and borrowed arrays. * fix(mcp): validate advertised dialect by selected profile * fix(mcp): preserve schema widening through references * feat(mcp): project compact input schemas into the portable advertised dialect One projection (advertisedDialectProjection.ts) owns the compact input advertisement: one type per node, items on every array, string literals as enums, oneOf widened to anyOf, allOf merged, tuples as element unions, exclusive bounds made inclusive, and every keyword outside the dialect dropped. Each rewrite is exact or widening; server validation is unchanged. The root description and sentences for dropped cross-field constraints move to the tool description, which Codex and Zed keep. The compact baseline falls to nullable values only (29), which the dialect cannot express without narrowing an input. The gateway shape repairs (typeArrayItemUnions, hasGatewayIncompatibleShape), the second dependentRequired-to-prose copy and the strict-gateway test denylist are removed; the dialect checker gates the inline presentation instead. * feat(mcp): omit output schemas from tools/list by default No surveyed model API reads an output schema, MCP client validators repeat REA's own result validation, and complete output schemas were about 92% of the default tools/list. REA_MCP_OUTPUT_SCHEMA_PROFILE now resolves to off through one owner (resolveMcpOutputSchemaProfile); compact and full remain selectable. structuredContent is still delivered, and the deferred server serves both off and full from the recorded catalog with the matching recorded identity. Tests and artifact verifiers that validate results against complete advertised output contracts select full explicitly. * docs: describe omitted output schemas as the default in README translations * fix(mcp): select schemas in verification workflows * fix(mcp): refuse zero-padded repeat bounds and record host-specific path patterns * fix(mcp): keep projected schemas permissive * fix(mcp): keep references valid when presentation widens their targets * fix(mcp): keep a single union branch's closure scoped to its properties * fix(mcp): select complete output schemas in the Electron verifier * fix(mcp): select complete output schemas in standalone verifier probes * fix(mcp): keep an allOf member's tuple tail with its own positions * fix(mcp): relax references into negations and conditionals presentation may remove * refactor(contracts): declare root discriminators as typed schema annotations * test(mcp): select the borrowed Evidence contract from its family * fix(mcp): keep astral literals and dynamic references widening in every mode * fix(mcp): omit astral literals and relax untracked references when presentation widens * fix(mcp): select complete output schemas in every verifier transport * fix(mcp): read reference pointers by schema position, not segment spelling * fix(mcp): protect references into collapsing boolean unions and keep referencing branches out of the collapse * fix(mcp): keep reduced schemas open when their root admits names by pattern * test(mcp): keep the reduced-schema pattern case outside the size-limited block * fix(mcp): omit patterns whose classes name astral characters * fix(mcp): decide union collapse and pointer protection from canonical branches * docs(mcp): document non-u astral matching and pin BMP agreement across V8 modes * fix(mcp): keep prototype-named properties as own properties in the dialect projection * test(mcp): select full output schemas in packet and signal schema checks * test(mcp): expect the advertised base64 schema to omit the unbounded pad-bit probe * fix(mcp): protect references into unevaluated keywords lost evaluation removes * fix(mcp): validate the doctor probe reply against the canonical output contract (#10655) * fix(mcp): validate the doctor probe reply against the canonical output contract * test(mcp): replay doctor probe validation under every output profile * test(mcp): accept typed-branch nullable unions in the advertised dialect (#10649) * test(mcp): select full output schemas in HTTP session workflows * fix(mcp): emit stack-safe classes with non-ASCII members and protect draft-07 reference siblings * fix(mcp): protect references into literal siblings of relaxed draft-07 references * feat(mcp): omit output schemas from tools/list by default (#10646) * feat(mcp): omit output schemas from tools/list by default No surveyed model API reads an output schema, MCP client validators repeat REA's own result validation, and complete output schemas were about 92% of the default tools/list. REA_MCP_OUTPUT_SCHEMA_PROFILE now resolves to off through one owner (resolveMcpOutputSchemaProfile); compact and full remain selectable. structuredContent is still delivered, and the deferred server serves both off and full from the recorded catalog with the matching recorded identity. Tests and artifact verifiers that validate results against complete advertised output contracts select full explicitly. * fix(mcp): select schemas in verification workflows * fix(mcp): select complete output schemas in the Electron verifier * fix(mcp): select complete output schemas in standalone verifier probes * fix(mcp): select complete output schemas in every verifier transport * test(mcp): select full output schemas in packet and signal schema checks * fix(mcp): validate the doctor probe reply against the canonical output contract (#10655) * fix(mcp): validate the doctor probe reply against the canonical output contract * test(mcp): replay doctor probe validation under every output profile * test(mcp): accept typed-branch nullable unions in the advertised dialect (#10649) * test(mcp): select full output schemas in HTTP session workflows * fix(mcp): keep the Evidence union on described borrowed inputs (#10597) * fix(mcp): keep the Evidence union on described borrowed inputs borrowEvidenceInput installed its advertised projection on one schema instance. A call site's .describe() rebuilds the schema from its definition, so match_native_functions advertised left and right as unconstrained values in every input profile. Install the projection in the borrowed parser's constructor so derived schemas keep it, for both borrowed inputs and borrowed arrays. * test(mcp): select the borrowed Evidence contract from its family * docs(mcp): admit baseline growth only for restored contract declarations * fix(mcp): state reduced multi-type properties as typed branches, not type lists The reduced fallback derived a property's types and emitted them as a type list, which Gemini, Moonshot and Ark reject. A property whose canonical schema is anyOf string and number therefore traded a typed union for a rejected construct when the inline projection fell back. Emit one type, or anyOf typed branches, so [T, null] becomes the dialect's nullable union. * feat(mcp)!: retire the full schema profiles and declare complete observations as a typed annotation (#10718) * refactor(mcp): declare complete observations as a typed annotation applied only on the wire * test(mcp): compare complete-observation declarations with their owning text * refactor(schema): couple Zod checks with their JSON Schema constraint keywords (#10725) * refactor(schema): couple Zod checks with their JSON Schema constraint keywords * refactor(schema): declare pattern approximations and object output roots as typed annotations (#10754) * refactor(schema): declare pattern approximations and object output roots as typed annotations * fix(schema): refuse pattern declarations JSON Schema cannot state * test(schema): select guided_workflows from its contract family * feat(mcp)!: retire the full input and output schema profiles (#10841) * feat(mcp)!: retire the full input and output schema profiles The compact input presentation is now the only advertised input schema, and output schemas are either omitted (`off`, the default) or bounded (`compact`). Catalog identity, the build-recorded digests and the recorded startup catalog collapse to those presentations: two digest profiles instead of six, one recorded catalog instead of two, and no input dialect baseline. Tests and verifiers that validated replies against listed full output schemas now validate against the canonical output contract, which REA already enforces server-side; tests that asserted canonical input restrictions read the canonical input schema instead of a full listing. BREAKING CHANGE: `REA_MCP_INPUT_SCHEMA_PROFILE=full` and `REA_MCP_OUTPUT_SCHEMA_PROFILE=full` are refused at startup and by `rea setup` with a configuration error naming the setting. Remove the setting from the client registration (or rerun setup with `REA_MCP_INPUT_SCHEMA_PROFILE=compact` / `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact`). `REA_MCP_INPUT_SCHEMA_PROFILE` still accepts `compact`. Server-side validation is unchanged. * test(mcp): validate replies against canonical output contracts at every listed-schema reader tools/list omits output schemas by default, so fixtures, verifiers and the Android analysis runners that read a listed outputSchema now bind the canonical contract instead. connectLocalToolsMcp and advertisedMcpToolSchemas hand the SDK a definition carrying the canonical output schema, so the SDK client and caller-supplied validators check every successful reply. * test(mcp): resolve Android runners' output contracts from the selected repository The Android efficiency runners imported the canonical output-schema helper from the harness checkout while --repository selects the revision under test. Import it from the selected repository, as the runners already do for the SDK and Ajv. * test(mcp): let Android runners compare revisions before the canonical output helper A selected revision without scripts/lib/canonical-output-schema.mjs could not start the Android runners. Choose the output contracts in one harness helper: the selected revision's canonical contracts when it has them, otherwise its listed schemas under the full output profile it still supports. * fix(scripts): keep caller launch settings when the real Hopper verifier builds its server environment The verifier deleted REA_VERIFY_SERVER_COMMAND and REA_VERIFY_SERVER_ARGS_JSON from process.env itself before reading them, so a caller selecting an installed REA command silently ran the checkout's dist/main.js. Copy the environment, as main does, so only the child's environment changes. * test(mcp): validate whole replies against canonical output contracts wherever a listed definition is held tools/list omits output schemas by default and the full output profile is gone, so tests that handed the SDK a listed definition no longer checked complete replies. Attach the canonical output schema with withCanonicalOutputSchema at every held REA tool definition. Environment-served tools (not REA contracts), and the deferred-runtime and doctor tests that exercise per-profile listings, keep their listed definitions. * test(mcp): record the advertised catalog summary without the retired full profiles Retiring the full input and output profiles leaves only the compact input and compact output listings, so the summary drops the full-profile digests and every pattern only the full listings carried. Regenerated with npm run verify:advertised-schemas -- --update-summary.
This commit is contained in:
1 parent
14b6345347
commit
5c53bcaed8
304 files changed
+2780
-4523
No files matched your search
@@ -96,17 +96,15 @@ Setup supports Claude Code, Codex, Cursor, Gemini CLI, Grok Build and
|
||||
manual MCP registration, Claude Code/Codex plugins, and the Cursor Marketplace
|
||||
plugin.
|
||||
|
||||
MCP uses compact input schemas by default to reduce context overhead. Set
|
||||
`REA_MCP_INPUT_SCHEMA_PROFILE=full` in your MCP registration when you need the
|
||||
complete advertised field guidance and nested validation details. Both profiles
|
||||
keep the full tool set and canonical server validation. See
|
||||
"Compact input schema profile" in the MCP contracts guide.
|
||||
MCP advertises compact input schemas to reduce context overhead; guidance a
|
||||
compact schema omits follows the tool description. The full tool set and
|
||||
canonical server validation are unchanged. See "Compact input schema profile"
|
||||
in the MCP contracts guide.
|
||||
|
||||
Output schemas are omitted from `tools/list` by default: no model API reads
|
||||
them, and REA validates every result itself. Set
|
||||
`REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` to advertise each output schema within
|
||||
a 4,096-byte budget, or `full` for complete output schemas (for MCP clients
|
||||
that validate results). All settings keep canonical server-side validation and
|
||||
a 4,096-byte budget. Both settings keep canonical server-side validation and
|
||||
complete structured results. See "Output schemas" in the MCP contracts guide.
|
||||
|
||||
### Ask your agent
|
||||
|
||||
@@ -30,12 +30,10 @@ declaration renderers rendered the full `compare_web_captures` input as
|
||||
`args: unknown`: its generated TypeScript type exceeds Pi's 16,000-character
|
||||
default. REA keeps the full advertisement's fields by inlining references when
|
||||
the shared form would exceed Pi's 32-reference expansion limit; retaining that
|
||||
shared form would leave later fields unknown instead. The existing
|
||||
`REA_MCP_INPUT_SCHEMA_PROFILE=compact` profile lets callers stay below
|
||||
its character limit by advertising reduced nested validation structure, while
|
||||
REA continues to validate calls against the complete canonical input schema.
|
||||
Compact is the default for the server's advertised inputs; an explicit `full`
|
||||
choice is preserved. See the [compact input schema profile](../reference/mcp-contracts.md#compact-input-schema-profile)
|
||||
shared form would leave later fields unknown instead. The compact
|
||||
input presentation keeps advertised inputs below its character limit by
|
||||
reducing nested validation structure, while REA continues to validate calls
|
||||
against the complete canonical input schema. See the [compact input schema profile](../reference/mcp-contracts.md#compact-input-schema-profile)
|
||||
for details of its reduced forms.
|
||||
|
||||
Custom renderer hosts can raise `inputMaxChars` through `renderToolSignature` or
|
||||
@@ -208,39 +206,30 @@ ID). Every actual HTTP request's model is checked and recorded separately.
|
||||
Both `HOME` and `USERPROFILE` point to the disposable account; native `skill add`
|
||||
still selects the installed skill directory explicitly.
|
||||
|
||||
The same native client passed full-profile chat and analysis with the actual
|
||||
wire model `gpt-4.1` under a distinct BYOK model ID:
|
||||
The same native client passed chat and analysis under REA's former full input
|
||||
profile with the actual wire model `gpt-4.1` under a distinct BYOK model ID:
|
||||
|
||||
```bash
|
||||
REA_VERIFY_COPILOT_MODEL=rea-gpt-4.1 REA_VERIFY_COPILOT_WIRE_MODEL=gpt-4.1 npm run verify:copilot-client -- chat
|
||||
REA_VERIFY_COPILOT_MODEL=rea-gpt-4.1 REA_VERIFY_COPILOT_WIRE_MODEL=gpt-4.1 npm run verify:copilot-client -- call
|
||||
```
|
||||
|
||||
These runs retain full canonical input/output schemas, all tools, installed-skill
|
||||
loading and complete Evidence readback. They use the fixture's requested
|
||||
one-million-token BYOK prompt setting; effective capacity and live API acceptance
|
||||
remain unknown. With the built-in `gpt-4.1` model ID, the verified client still
|
||||
blocks the full input-schema profile before HTTP with
|
||||
`compaction_static_context_blocked`, even when requesting a larger BYOK prompt
|
||||
capacity. The effective capacity is unknown. The default verifier preserves
|
||||
the complete catalog and full schemas. To verify the existing compact profile:
|
||||
Those runs used the fixture's requested one-million-token BYOK prompt setting;
|
||||
effective capacity and live API acceptance remain unknown. With the built-in
|
||||
`gpt-4.1` model ID, the verified client blocked the former full input-schema
|
||||
profile before HTTP with `compaction_static_context_blocked`. That profile was
|
||||
removed, so the verifier now compares the input schemas forwarded by Copilot
|
||||
with REA's compact advertisement:
|
||||
|
||||
```bash
|
||||
REA_VERIFY_COPILOT_MODEL=gpt-4.1 REA_VERIFY_COPILOT_SCHEMA_PROFILE=compact npm run verify:copilot-client -- chat
|
||||
REA_VERIFY_COPILOT_MODEL=gpt-4.1 REA_VERIFY_COPILOT_SCHEMA_PROFILE=compact npm run verify:copilot-client -- call
|
||||
REA_VERIFY_COPILOT_MODEL=gpt-4.1 npm run verify:copilot-client -- chat
|
||||
REA_VERIFY_COPILOT_MODEL=gpt-4.1 npm run verify:copilot-client -- call
|
||||
```
|
||||
|
||||
The verifier explicitly writes either selected profile into its disposable
|
||||
registration and compares the input schemas forwarded by Copilot with that
|
||||
profile's advertised schemas. A run must not infer `full` from an unset setting:
|
||||
REA's runtime default is now `compact`. The recorded client results above remain
|
||||
scoped to their executed revisions; this verifier correction is not a new client run.
|
||||
|
||||
Both native workflows passed with the complete tool inventory. The verifier selects the
|
||||
profile only in its disposable Copilot registration and records it in the
|
||||
receipt. Compact advertisements can reduce nested validation structure; the
|
||||
server still validates complete canonical inputs. This is a tested alternate
|
||||
workflow, not a fix for the full-profile client/model limit tracked in #1554.
|
||||
The recorded client results above remain scoped to their executed revisions;
|
||||
this verifier change is not a new client run. Compact native workflows passed
|
||||
with the complete tool inventory. Compact advertisements can reduce nested
|
||||
validation structure; the server still validates complete canonical inputs.
|
||||
Live model APIs, native Windows and default OS-home skill discovery remain
|
||||
unverified.
|
||||
|
||||
|
||||
+14
-14
@@ -111,9 +111,9 @@ API dependency, and independent fixture compiler oracles keep their existing
|
||||
TypeScript versions.
|
||||
|
||||
`npm run build:cached` compiles the CLI/MCP runtime and its packaged skill.
|
||||
The build also records `dist/mcp-catalog.json` (compact inputs) and
|
||||
`dist/mcp-catalog-full.json` from the canonical SDK registrations and binds
|
||||
each to the runtime with its own SHA-256 digest. These are
|
||||
The build also records `dist/mcp-catalog.json` (compact inputs) from the
|
||||
canonical SDK registrations and binds it to the runtime with its SHA-256
|
||||
digest. These are
|
||||
runtime artifacts, separate from the test catalog below. Default target-free
|
||||
MCP startup serves its schemas without constructing analysis validators;
|
||||
the first tool or guided-workflow request materializes the canonical runtime
|
||||
@@ -294,12 +294,10 @@ profile. Meta-schema validity does not establish client acceptance or useful
|
||||
rendered declarations. `src/contracts/advertisedDialect.ts` owns the portable
|
||||
advertised dialect: the keyword allowlist, size and depth limits, and the regex
|
||||
grammar that compiles in V8, Python, RE2 and Rust. `advertisedDialect.test.ts`
|
||||
lists every input, output and catalog profile combination and compares the
|
||||
violation counts with `advertisedDialectBaseline.ts`, which may only shrink,
|
||||
except when a runtime analysis setting adds its `update_analysis_settings`
|
||||
branch, or a fix restores a contract declaration the presentation had lost and
|
||||
the PR names it; the packaged catalog check applies the same rules to the
|
||||
default profiles.
|
||||
lists every output and catalog profile combination and requires zero dialect
|
||||
violations; there is no baseline, so a construct outside the dialect fails the
|
||||
test by name. The packaged catalog check applies the same rule to the default
|
||||
profiles.
|
||||
Advertised patterns, input and output, are enforced at zero:
|
||||
`portablePattern.ts` re-emits each one exactly, widens it, or omits it, and the
|
||||
presentation drops any enclosing `not` or `if` and turns `oneOf` into `anyOf`
|
||||
@@ -760,12 +758,14 @@ to reach their original memory, depth, cycle or representation boundary; retain
|
||||
those stronger assertions.
|
||||
|
||||
When a matrix already holds a named tool definition from live `tools/list`,
|
||||
pass that definition through the SDK's `toolDefinition` call option. This keeps
|
||||
the selected output-schema validation without eagerly compiling unrelated
|
||||
catalog schemas. Re-read definitions if the server advertises a changed list.
|
||||
pass that definition through the SDK's `toolDefinition` call option, with the
|
||||
canonical output schema attached (`tools/list` omits output schemas by default).
|
||||
This keeps output-schema validation without eagerly compiling unrelated catalog
|
||||
schemas. Re-read definitions if the server advertises a changed list.
|
||||
|
||||
For exact nested input constraints, select the full profile explicitly or use
|
||||
the full-SDK conformance suite that owns the field. Compact workflow tests
|
||||
For exact nested input constraints, assert the canonical input schema
|
||||
(`advertisedInputJsonSchema`) or use the full-SDK conformance suite that owns
|
||||
the field. Compact workflow tests
|
||||
should assert canonical failure categories and provider-call controls where
|
||||
rejection before provider work matters. Do not make today's omission of a
|
||||
constraint a compact-profile invariant.
|
||||
|
||||
@@ -62,26 +62,26 @@ the registration's file does not carry as a
|
||||
|
||||
## Setting launch variables in PowerShell
|
||||
|
||||
Shell examples that prefix a command with `REA_MCP_INPUT_SCHEMA_PROFILE=compact`
|
||||
Shell examples that prefix a command with `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact`
|
||||
use POSIX shell syntax. In PowerShell, set the process environment before
|
||||
launching REA:
|
||||
|
||||
```powershell
|
||||
$env:REA_MCP_INPUT_SCHEMA_PROFILE = "compact"
|
||||
$env:REA_MCP_OUTPUT_SCHEMA_PROFILE = "compact"
|
||||
rea mcp doctor --json
|
||||
```
|
||||
|
||||
The assignment applies to the current PowerShell process and commands it launches.
|
||||
Restore the previous value when finished, or use
|
||||
`Remove-Item Env:REA_MCP_INPUT_SCHEMA_PROFILE` if it was previously unset. It does
|
||||
`Remove-Item Env:REA_MCP_OUTPUT_SCHEMA_PROFILE` if it was previously unset. It does
|
||||
not update an already running MCP server or desktop client.
|
||||
|
||||
To select this profile for an agent, set `REA_MCP_INPUT_SCHEMA_PROFILE` in that
|
||||
To select this profile for an agent, set `REA_MCP_OUTPUT_SCHEMA_PROFILE` in that
|
||||
client's existing REA registration `env` object (`environment` for OpenCode) and
|
||||
restart the client, preserving its other settings. A successful terminal doctor run does not verify the client's
|
||||
launch environment. See [launch-environment verification](../development/client-verification.md#launch-environment-verification)
|
||||
and the client-specific instructions below. REA setup forwards supported selections,
|
||||
including `REA_MCP_INPUT_SCHEMA_PROFILE`, from its environment into the registration;
|
||||
including `REA_MCP_OUTPUT_SCHEMA_PROFILE`, from its environment into the registration;
|
||||
it does not copy unrelated environment values. Restoring the shell's previous
|
||||
value does not undo a selection already saved by setup.
|
||||
|
||||
@@ -179,7 +179,6 @@ during `tools/list`, merge these settings into the **REA server process's**
|
||||
environment, then reconnect:
|
||||
|
||||
```text
|
||||
REA_MCP_INPUT_SCHEMA_PROFILE=compact
|
||||
REA_MCP_OUTPUT_SCHEMA_PROFILE=compact
|
||||
REA_MCP_TOOL_CATALOG_PROFILE=full
|
||||
```
|
||||
@@ -232,23 +231,20 @@ aligned configuration; doctor does not widen it to expose every REA tool. See
|
||||
[Copilot's MCP configuration](https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers)
|
||||
for tool-selection settings.
|
||||
|
||||
For GitHub Copilot CLI 1.0.95, `gpt-4.1` model metadata can block ordinary
|
||||
chat with REA's full input-schema profile before sending a model request
|
||||
For GitHub Copilot CLI 1.0.95, `gpt-4.1` model metadata blocked ordinary
|
||||
chat with REA's former full input-schema profile before sending a model request
|
||||
(`compaction_static_context_blocked`; [#1554](https://github.com/morluto/rea/issues/1554)).
|
||||
For the native offline OpenAI-compatible BYOK adapter, full-profile chat and
|
||||
analysis also passed with a distinct `COPILOT_PROVIDER_MODEL_ID` such as
|
||||
That profile was removed. For the native offline OpenAI-compatible BYOK adapter,
|
||||
chat and analysis with it also passed with a distinct `COPILOT_PROVIDER_MODEL_ID` such as
|
||||
`rea-gpt-4.1`, `COPILOT_PROVIDER_WIRE_MODEL=gpt-4.1`, and that distinct ID selected
|
||||
through `copilot --model rea-gpt-4.1`. The local fixture requested
|
||||
`COPILOT_PROVIDER_MAX_PROMPT_TOKENS=1000000`; configure the limit supported by
|
||||
your backend. This keeps every full schema and the actual wire model intact.
|
||||
It does not establish the effective client budget or fix the built-in-ID path.
|
||||
The existing compact profile passed native chat and analysis with the complete
|
||||
tool inventory on Linux using a loopback model fixture. After setup, add
|
||||
`"REA_MCP_INPUT_SCHEMA_PROFILE": "compact"` to the `env` object of the
|
||||
`mcpServers.rea` entry in Copilot's `mcp-config.json`, preserving its other
|
||||
settings, then restart the client. Compact advertisements can reduce nested
|
||||
schema constraints; REA still validates complete canonical inputs. Full-profile
|
||||
`gpt-5.4` metadata is also verified locally. Live model API acceptance and
|
||||
The compact input schemas passed native chat and analysis with the complete
|
||||
tool inventory on Linux using a loopback model fixture. Compact advertisements
|
||||
can reduce nested schema constraints; REA still validates complete canonical
|
||||
inputs. Live model API acceptance and
|
||||
Windows remain unverified; see [native client verification](../development/testing.md).
|
||||
|
||||
## Claude Desktop
|
||||
@@ -354,11 +350,6 @@ and Claude Code reports that autocompact is thrashing. To avoid this:
|
||||
to restore deferred loading. DeepSeek's Anthropic API guide does not list
|
||||
`tool_reference` among its supported content blocks, so this setting may cause
|
||||
its requests to fail.
|
||||
- To shrink the upfront catalog, add
|
||||
`"REA_MCP_INPUT_SCHEMA_PROFILE": "compact"` to the `env` object of the
|
||||
`mcpServers.rea` entry in `~/.claude.json` (`$CLAUDE_CONFIG_DIR/.claude.json`
|
||||
when that variable is set), then restart Claude Code. Setup keeps this
|
||||
override.
|
||||
- To remove entire tool families from the upfront catalog, run
|
||||
`rea setup --client claude_code --tool-families <ids>` with the families the
|
||||
workflow needs (see [the advertising scope contract](../reference/mcp-contracts.md#tool-family-advertising-scope)).
|
||||
@@ -372,13 +363,9 @@ The sizes above come from REA's own `tools/list`. A live Claude Code session
|
||||
against DeepSeek's API is unverified.
|
||||
|
||||
Output schemas are omitted by default (`"REA_MCP_OUTPUT_SCHEMA_PROFILE": "off"`).
|
||||
If you selected `"full"` and a client's stdio reader rejects the complete
|
||||
catalog frame, return to the default or select `"compact"`, which bounds every
|
||||
advertised output schema, in REA's `env` object, then restart the client.
|
||||
Every profile keeps canonical result validation and complete structured
|
||||
results; `compact` keeps a widened root shape and `off` advertises nothing. On catalogs large
|
||||
enough that full input schemas alone approach the frame limit, also keep the
|
||||
input profile at its `compact` default. See [MCP output schema
|
||||
`"compact"` bounds every advertised output schema. Both profiles keep canonical
|
||||
result validation and complete structured results; `compact` keeps a widened
|
||||
root shape and `off` advertises nothing. See [MCP output schema
|
||||
profiles](../reference/mcp-contracts.md#output-schemas).
|
||||
|
||||
## Kimi Code
|
||||
@@ -429,12 +416,10 @@ 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.
|
||||
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
|
||||
The server advertises compact input schemas, which avoids a Moonshot Kimi Code
|
||||
2.2.0 failure observed with uncompacted schemas: 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.
|
||||
provider-specific and not an MCP protocol 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.
|
||||
@@ -450,8 +435,7 @@ 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; 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
|
||||
compact input schemas keep each advertised tool within a bounded size. 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.
|
||||
|
||||
@@ -640,8 +624,8 @@ workload needs more time. Devin honors `XDG_CONFIG_HOME` for its
|
||||
|
||||
An older REA input containing acyclic local references was rejected by a
|
||||
Responses-compatible Kimi gateway as “infinite recursion”; expanding those
|
||||
references succeeded. A separate full-profile input failed the provider's
|
||||
schema-size limit. These are distinct reported failures, not proof that the
|
||||
references succeeded. A separate input under REA's former full input profile
|
||||
failed the provider's schema-size limit. These are distinct reported failures, not proof that the
|
||||
canonical schema is recursive. Current REA inputs are already self-contained;
|
||||
see [schema advertisement](../reference/mcp-contracts.md#schema-advertisement).
|
||||
|
||||
@@ -649,17 +633,10 @@ For this failure, inspect the active MCP launch command and refresh the REA
|
||||
installation that command actually runs. A configured package selector or the
|
||||
installed skill version alone does not identify a cached or still-running
|
||||
server. Follow [installation and update guidance](installation.md#diagnose-update-and-remove).
|
||||
Then merge this setting into the REA entry in `$CODEX_HOME/config.toml`
|
||||
(`~/.codex/config.toml` by default), preserving its launcher and other settings:
|
||||
|
||||
```toml
|
||||
[mcp_servers.rea.env]
|
||||
REA_MCP_INPUT_SCHEMA_PROFILE = "compact"
|
||||
```
|
||||
|
||||
If that table already exists, add or update the key there. Restart/reconnect
|
||||
Codex so REA reads the new environment. Compact is now REA's default, but an
|
||||
explicit `full` override remains effective until changed. It preserves the
|
||||
REA advertises compact input schemas. A registration that still sets
|
||||
`REA_MCP_INPUT_SCHEMA_PROFILE = "full"` in `$CODEX_HOME/config.toml`
|
||||
(`~/.codex/config.toml` by default) fails at startup, so remove that key and
|
||||
restart/reconnect Codex. Compact advertisement preserves the
|
||||
tool inventory and canonical server validation while reducing advertised input
|
||||
detail. The probes in [#5160](https://github.com/morluto/rea/issues/5160) covered
|
||||
individual tools through one gateway, not the entire current catalog or a
|
||||
|
||||
@@ -98,7 +98,7 @@ approve replacing it. A different existing installation path is foreign to the
|
||||
current entrypoint; a path that cannot be resolved remains unknown. Cordis uses
|
||||
its stable REA entry ID instead of launcher identity.
|
||||
Setup also retains valid environment overrides in REA's existing server entry,
|
||||
such as `REA_MCP_INPUT_SCHEMA_PROFILE`, `REA_MCP_OUTPUT_SCHEMA_PROFILE`, and
|
||||
such as `REA_MCP_OUTPUT_SCHEMA_PROFILE`,
|
||||
`REA_MCP_MAX_RESPONSE_BYTES`, and `REA_MCP_TOOL_FAMILIES`.
|
||||
Detected provider settings replace only matching environment keys.
|
||||
Setup manages REA's launcher, selected provider settings, and recommended timeout
|
||||
@@ -352,7 +352,7 @@ remove it with `claude mcp remove --scope local rea` from the same directory.
|
||||
`rea update` doesn't refresh it either, so it continues using the selected installation.
|
||||
To move it to a newer release, first back up the project entry in
|
||||
`~/.claude.json` and record its environment and other metadata. In particular,
|
||||
preserve caller-selected overrides such as `REA_MCP_INPUT_SCHEMA_PROFILE` and
|
||||
preserve caller-selected overrides such as `REA_MCP_OUTPUT_SCHEMA_PROFILE` and
|
||||
`REA_MCP_MAX_RESPONSE_BYTES`, and `REA_MCP_TOOL_FAMILIES`. Run `claude mcp remove --scope local rea` from the
|
||||
same directory, then recreate it with the platform-appropriate command above
|
||||
and the new version. Reapply every recorded environment value with Claude
|
||||
|
||||
@@ -63,10 +63,6 @@ invalidate that profile even when the model still emits the expected marker.
|
||||
Useful controls:
|
||||
|
||||
```sh
|
||||
# Compare full input guidance with the default compact input presentation.
|
||||
npm run eval:mcp-catalog -- --model gpt-6.1-sol \
|
||||
--output "$HOME/.cache/rea-catalog-full" --input-profile full --repetitions 3
|
||||
|
||||
# Choose exact tool names in the working host mode.
|
||||
npm run eval:mcp-catalog -- --model gpt-6.1-sol \
|
||||
--output "$HOME/.cache/rea-catalog-selected" --modes client-code \
|
||||
|
||||
@@ -82,11 +82,11 @@ npx rea-agents setup
|
||||
|
||||
يدعم الإعداد Claude Code وCodex وCursor وGemini CLI وGrok Build و[وكلاء آخرين](../getting-started/installation.md#supported-agents). راجع [التثبيت والإعداد](../getting-started/installation.md) لتهيئة موفّري التحليل وتسجيل MCP يدويًا.
|
||||
|
||||
يستخدم MCP مخططات إدخال مختصرة افتراضيًا. للحصول على إرشادات الحقول الكاملة وتفاصيل التحقق المتداخل، اضبط `REA_MCP_INPUT_SCHEMA_PROFILE=full` في تسجيل MCP. يحتفظ الخياران بجميع الأدوات والتحقق القياسي نفسه على الخادم؛ راجع دليل عقود MCP.
|
||||
يستخدم MCP مخططات إدخال مختصرة افتراضيًا. تنتقل الإرشادات التي يحذفها المخطط المختصر إلى وصف الأداة. تبقى جميع الأدوات والتحقق القياسي على الخادم دون تغيير؛ راجع دليل عقود MCP.
|
||||
|
||||
يسجّل الإعداد بيئة التشغيل المحددة وملف دخول REA مباشرةً. عند استخدام مشغّل حزم، احتفظ بإدخال ذاكرة التخزين المؤقت وبيئة التشغيل؛ ونفّذ تنزيل الحزمة من دليل موثوق خارج المشروع المستهدف. راجع دليل التثبيت والإعداد.
|
||||
|
||||
تُحذف مخططات الإخراج من `tools/list` افتراضيًا: لا تقرؤها أي واجهة برمجة لنموذج، ويتحقق REA من كل نتيجة بنفسه. اضبط `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` في تسجيل MCP لإعلان كل مخطط إخراج ضمن حد 4096 بايت، أو `full` لمخططات الإخراج الكاملة (لعملاء MCP الذين يتحققون من النتائج)؛ يظل التحقق والخروج المنظّم كاملين على الخادم في كل الإعدادات. راجع قسم مخططات الإخراج في دليل عقود MCP.
|
||||
تُحذف مخططات الإخراج من `tools/list` افتراضيًا: لا تقرؤها أي واجهة برمجة لنموذج، ويتحقق REA من كل نتيجة بنفسه. اضبط `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` في تسجيل MCP لإعلان كل مخطط إخراج ضمن حد 4096 بايت؛ يظل التحقق والخروج المنظّم كاملين على الخادم في كل الإعدادات. راجع قسم مخططات الإخراج في دليل عقود MCP.
|
||||
|
||||
### اطلب من وكيلك
|
||||
|
||||
|
||||
@@ -99,18 +99,16 @@ runner), не изтривайте записа му в кеша. Изтегля
|
||||
доставчици на анализ, ръчна регистрация на MCP и инсталиране на плъгина за
|
||||
Claude Code/Codex.
|
||||
|
||||
По подразбиране MCP използва компактни входни схеми, за да заема по-малко от
|
||||
контекста. Задайте `REA_MCP_INPUT_SCHEMA_PROFILE=full` в MCP регистрацията си,
|
||||
когато са ви нужни пълните описания на полетата и подробностите за вложената
|
||||
валидация. И двата профила запазват пълния набор от инструменти и каноничната
|
||||
валидация на сървъра. Вижте „Compact input schema profile“ в ръководството за MCP
|
||||
MCP обявява компактни входни схеми, за да заема по-малко от контекста;
|
||||
указанията, които компактната схема пропуска, следват описанието на
|
||||
инструмента. Пълният набор от инструменти и каноничната валидация на сървъра
|
||||
остават непроменени. Вижте „Compact input schema profile“ в ръководството за MCP
|
||||
договорите.
|
||||
|
||||
По подразбиране изходните схеми се пропускат от `tools/list`: никой API на
|
||||
модел не ги чете, а REA сам валидира всеки резултат. Задайте
|
||||
`REA_MCP_OUTPUT_SCHEMA_PROFILE=compact`, за да обявите всяка изходна схема в
|
||||
рамките на 4096 байта, или `full` за пълни изходни схеми (за MCP клиенти, които
|
||||
валидират резултатите). Всички настройки запазват каноничната валидация от
|
||||
рамките на 4096 байта. Всички настройки запазват каноничната валидация от
|
||||
страна на сървъра и пълните структурирани резултати. Вижте „Output schemas“ в
|
||||
ръководството за MCP договорите.
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ Wähle deine Agenten aus, prüfe die geplanten Änderungen und bestätige sie. D
|
||||
|
||||
Die Einrichtung unterstützt Claude Code, Codex, Cursor, Gemini CLI, Grok Build und [weitere Agenten](../getting-started/installation.md#supported-agents). Informationen zur Provider-Konfiguration und manuellen MCP-Registrierung findest du unter [Installation und Einrichtung](../getting-started/installation.md).
|
||||
|
||||
MCP verwendet standardmäßig kompakte Eingabeschemas. Für vollständige Feldhinweise und Details zur verschachtelten Validierung setze `REA_MCP_INPUT_SCHEMA_PROFILE=full` in der MCP-Registrierung. Beide Profile behalten alle Tools und dieselbe kanonische Servervalidierung; siehe MCP-Vertragsleitfaden.
|
||||
MCP verwendet standardmäßig kompakte Eingabeschemas. Hinweise, die ein kompaktes Schema auslässt, folgen der Tool-Beschreibung. Alle Tools und die kanonische Servervalidierung bleiben unverändert; siehe MCP-Vertragsleitfaden.
|
||||
|
||||
Die Einrichtung registriert die gewählte Laufzeit und das REA-Einstiegsskript direkt. Bewahre bei einem Paket-Runner dessen Cache-Eintrag und die Laufzeit auf; lade das Paket aus einem vertrauenswürdigen Verzeichnis außerhalb des Zielprojekts. Siehe die Installationsanleitung.
|
||||
|
||||
Ausgabeschemas werden standardmäßig nicht in `tools/list` angekündigt: Keine Modell-API liest sie, und REA validiert jedes Ergebnis selbst. Setze in der MCP-Registrierung `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact`, um jedes Ausgabeschema innerhalb von 4096 Byte anzukündigen, oder `full` für vollständige Ausgabeschemas (für MCP-Clients, die Ergebnisse validieren). Servervalidierung und strukturierte Ergebnisse bleiben in jeder Einstellung vollständig. Siehe „Output schemas“ im MCP-Vertragsleitfaden.
|
||||
Ausgabeschemas werden standardmäßig nicht in `tools/list` angekündigt: Keine Modell-API liest sie, und REA validiert jedes Ergebnis selbst. Setze in der MCP-Registrierung `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact`, um jedes Ausgabeschema innerhalb von 4096 Byte anzukündigen. Servervalidierung und strukturierte Ergebnisse bleiben in jeder Einstellung vollständig. Siehe „Output schemas“ im MCP-Vertragsleitfaden.
|
||||
|
||||
### Deinen Agenten fragen
|
||||
|
||||
|
||||
@@ -84,11 +84,11 @@ npx rea-agents setup
|
||||
|
||||
Υποστηρίζονται τα Claude Code, Codex, Cursor, Gemini CLI, Grok Build και [άλλοι agents](../getting-started/installation.md#supported-agents). Για τη ρύθμιση των παρόχων ανάλυσης και τη μη αυτόματη καταχώριση MCP, δες τον οδηγό [εγκατάστασης και ρύθμισης](../getting-started/installation.md).
|
||||
|
||||
Το MCP χρησιμοποιεί συμπαγή σχήματα εισόδου από προεπιλογή. Για πλήρεις οδηγίες πεδίων και ένθετης επικύρωσης, όρισε `REA_MCP_INPUT_SCHEMA_PROFILE=full` στην καταχώριση MCP. Και τα δύο προφίλ διατηρούν όλα τα εργαλεία και την ίδια κανονική επικύρωση διακομιστή· δες τον οδηγό συμβολαίων MCP.
|
||||
Το MCP χρησιμοποιεί συμπαγή σχήματα εισόδου από προεπιλογή. Οι οδηγίες που παραλείπει ένα συμπαγές σχήμα ακολουθούν την περιγραφή του εργαλείου. Όλα τα εργαλεία και η κανονική επικύρωση διακομιστή παραμένουν ίδια· δες τον οδηγό συμβολαίων MCP.
|
||||
|
||||
Η εγκατάσταση καταχωρίζει απευθείας τον επιλεγμένο runtime και το αρχικό script του REA. Αν χρησιμοποιείς package runner, διατήρησε την καταχώριση cache και τον runtime· απόκτησε το πακέτο από έμπιστο κατάλογο έξω από το έργο-στόχο. Δες τον οδηγό εγκατάστασης.
|
||||
|
||||
Τα σχήματα εξόδου παραλείπονται από το `tools/list` από προεπιλογή: κανένα API μοντέλου δεν τα διαβάζει και το REA επικυρώνει μόνο του κάθε αποτέλεσμα. Όρισε `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` στην καταχώριση MCP για να ανακοινώνεται κάθε σχήμα εξόδου έως 4096 byte, ή `full` για πλήρη σχήματα εξόδου (για clients MCP που επικυρώνουν αποτελέσματα). Η επικύρωση του διακομιστή και τα δομημένα αποτελέσματα παραμένουν πλήρη σε κάθε ρύθμιση. Δες το «Output schemas» στον οδηγό συμβάσεων MCP.
|
||||
Τα σχήματα εξόδου παραλείπονται από το `tools/list` από προεπιλογή: κανένα API μοντέλου δεν τα διαβάζει και το REA επικυρώνει μόνο του κάθε αποτέλεσμα. Όρισε `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` στην καταχώριση MCP για να ανακοινώνεται κάθε σχήμα εξόδου έως 4096 byte. Η επικύρωση του διακομιστή και τα δομημένα αποτελέσματα παραμένουν πλήρη σε κάθε ρύθμιση. Δες το «Output schemas» στον οδηγό συμβάσεων MCP.
|
||||
|
||||
### Ζήτησε από τον agent σου
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ Elige tus agentes, revisa los cambios propuestos y apruébalos. La configuració
|
||||
|
||||
La configuración admite Claude Code, Codex, Cursor, Gemini CLI, Grok Build y [otros agentes](../getting-started/installation.md#supported-agents). Consulta [instalación y configuración](../getting-started/installation.md) para configurar proveedores y registrar MCP manualmente.
|
||||
|
||||
MCP usa esquemas de entrada compactos por defecto. Para ver todas las instrucciones de campos y la validación anidada, establece `REA_MCP_INPUT_SCHEMA_PROFILE=full` en el registro MCP. Ambos perfiles conservan todas las herramientas y la misma validación canónica del servidor; consulta la guía de contratos MCP.
|
||||
MCP usa esquemas de entrada compactos por defecto. Las indicaciones que omite un esquema compacto siguen a la descripción de la herramienta. Todas las herramientas y la validación canónica del servidor no cambian; consulta la guía de contratos MCP.
|
||||
|
||||
La configuración registra directamente el entorno de ejecución elegido y el script de entrada de REA. Si usas un ejecutor de paquetes, conserva su entrada de caché y el entorno de ejecución; descarga el paquete desde un directorio de confianza fuera del proyecto objetivo. Consulta la guía de instalación.
|
||||
|
||||
Los esquemas de salida se omiten de `tools/list` por defecto: ninguna API de modelos los lee y REA valida cada resultado por sí mismo. Establece `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` en el registro MCP para anunciar cada esquema de salida dentro de 4096 bytes, o `full` para los esquemas de salida completos (para clientes MCP que validan resultados). En todos los casos, la validación del servidor y los resultados estructurados siguen siendo completos. Consulta «Output schemas» en la guía de contratos MCP.
|
||||
Los esquemas de salida se omiten de `tools/list` por defecto: ninguna API de modelos los lee y REA valida cada resultado por sí mismo. Establece `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` en el registro MCP para anunciar cada esquema de salida dentro de 4096 bytes. En todos los casos, la validación del servidor y los resultados estructurados siguen siendo completos. Consulta «Output schemas» en la guía de contratos MCP.
|
||||
|
||||
### Pregunta a tu agente
|
||||
|
||||
|
||||
@@ -74,11 +74,11 @@ npx rea-agents setup
|
||||
|
||||
راهاندازی از Claude Code، Codex، Cursor، Gemini CLI، Grok Build و [ایجنتهای دیگر](../getting-started/installation.md#supported-agents) پشتیبانی میکند. برای پیکربندی ارائهدهنده و ثبت دستی MCP، [نصب و راهاندازی](../getting-started/installation.md) را ببینید.
|
||||
|
||||
MCP بهطور پیشفرض از schema ورودی فشرده استفاده میکند. برای راهنمای کامل فیلدها و اعتبارسنجی تودرتو، در ثبت MCP مقدار `REA_MCP_INPUT_SCHEMA_PROFILE=full` را تنظیم کنید. هر دو حالت همهٔ ابزارها و اعتبارسنجی استاندارد یکسان سرور را نگه میدارند؛ راهنمای قراردادهای MCP را ببینید.
|
||||
MCP بهطور پیشفرض از schema ورودی فشرده استفاده میکند. راهنماییهایی که schema فشرده حذف میکند پس از توضیح ابزار میآیند. همهٔ ابزارها و اعتبارسنجی استاندارد سرور بدون تغییر میمانند؛ راهنمای قراردادهای MCP را ببینید.
|
||||
|
||||
راهاندازی، محیط اجرای انتخابشده و اسکریپت ورودی REA را مستقیماً ثبت میکند. اگر از اجراکنندهٔ بسته استفاده میکنید، ورودی حافظهٔ پنهان و محیط اجرا را حفظ کنید؛ بسته را از پوشهای قابل اعتماد خارج از پروژهٔ هدف دریافت کنید. راهنمای نصب را ببینید.
|
||||
|
||||
طرحهای خروجی بهطور پیشفرض از `tools/list` حذف میشوند: هیچ API مدلی آنها را نمیخواند و REA خودش هر نتیجه را اعتبارسنجی میکند. در ثبت MCP مقدار `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` را برای اعلام هر طرح خروجی در سقف 4096 بایت، یا `full` را برای طرحهای خروجی کامل (برای کلاینتهای MCP که نتایج را اعتبارسنجی میکنند) تنظیم کنید. در همهٔ تنظیمات، اعتبارسنجی سرور و نتایج ساختیافته کامل میمانند. بخش «Output schemas» در راهنمای قراردادهای MCP را ببینید.
|
||||
طرحهای خروجی بهطور پیشفرض از `tools/list` حذف میشوند: هیچ API مدلی آنها را نمیخواند و REA خودش هر نتیجه را اعتبارسنجی میکند. در ثبت MCP مقدار `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` را برای اعلام هر طرح خروجی در سقف 4096 بایت تنظیم کنید. در همهٔ تنظیمات، اعتبارسنجی سرور و نتایج ساختیافته کامل میمانند. بخش «Output schemas» در راهنمای قراردادهای MCP را ببینید.
|
||||
|
||||
### از ایجنت خود بپرسید
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ Choisissez vos agents, vérifiez les modifications proposées et approuvez-les.
|
||||
|
||||
Setup prend en charge Claude Code, Codex, Cursor, Gemini CLI, Grok Build et [d'autres agents](../getting-started/installation.md#supported-agents). Consultez [Installation et configuration](../getting-started/installation.md) pour la configuration des fournisseurs et l'enregistrement MCP manuel.
|
||||
|
||||
MCP utilise par défaut des schémas d’entrée compacts. Pour les descriptions complètes des champs et de la validation imbriquée, définissez `REA_MCP_INPUT_SCHEMA_PROFILE=full` dans l’enregistrement MCP. Les deux profils conservent tous les outils et la même validation canonique du serveur ; voir le guide des contrats MCP.
|
||||
MCP utilise par défaut des schémas d’entrée compacts. Les indications qu’un schéma compact omet suivent la description de l’outil. Tous les outils et la validation canonique du serveur restent inchangés ; voir le guide des contrats MCP.
|
||||
|
||||
La configuration enregistre directement le runtime choisi et le script d’entrée de REA. Avec un exécuteur de paquets, conservez son entrée de cache et le runtime ; récupérez le paquet depuis un répertoire de confiance hors du projet cible. Consultez le guide d’installation.
|
||||
|
||||
Les schémas de sortie sont omis de `tools/list` par défaut : aucune API de modèle ne les lit, et REA valide lui-même chaque résultat. Définissez `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` dans l’enregistrement MCP pour annoncer chaque schéma de sortie dans un budget de 4096 octets, ou `full` pour des schémas de sortie complets (pour les clients MCP qui valident les résultats). Dans tous les cas, la validation du serveur et les résultats structurés restent complets. Consultez « Output schemas » dans le guide des contrats MCP.
|
||||
Les schémas de sortie sont omis de `tools/list` par défaut : aucune API de modèle ne les lit, et REA valide lui-même chaque résultat. Définissez `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` dans l’enregistrement MCP pour annoncer chaque schéma de sortie dans un budget de 4096 octets. Dans tous les cas, la validation du serveur et les résultats structurés restent complets. Consultez « Output schemas » dans le guide des contrats MCP.
|
||||
|
||||
### Interroger votre agent
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ Pilih agen yang ingin digunakan, tinjau perubahan yang diusulkan, lalu setujui p
|
||||
|
||||
Penyiapan mendukung Claude Code, Codex, Cursor, Gemini CLI, Grok Build, dan [agen lainnya](../getting-started/installation.md#supported-agents). Lihat [instalasi dan penyiapan](../getting-started/installation.md) untuk konfigurasi penyedia dan pendaftaran MCP secara manual.
|
||||
|
||||
MCP menggunakan skema input ringkas secara default. Untuk petunjuk bidang lengkap dan validasi bertingkat, atur `REA_MCP_INPUT_SCHEMA_PROFILE=full` pada pendaftaran MCP. Kedua profil mempertahankan semua alat dan validasi server kanonis yang sama; lihat panduan kontrak MCP.
|
||||
MCP menggunakan skema input ringkas secara default. Petunjuk yang dihilangkan skema ringkas mengikuti deskripsi alat. Semua alat dan validasi server kanonis tidak berubah; lihat panduan kontrak MCP.
|
||||
|
||||
Setup mencatat runtime yang dipilih dan skrip masuk REA secara langsung. Jika memakai package runner, pertahankan entri cache dan runtime tersebut; unduh paket dari direktori tepercaya di luar proyek target. Lihat panduan instalasi.
|
||||
|
||||
Skema keluaran tidak disertakan dalam `tools/list` secara default: tidak ada API model yang membacanya, dan REA memvalidasi setiap hasil sendiri. Atur `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` dalam registrasi MCP untuk mengumumkan setiap skema keluaran dalam batas 4096 byte, atau `full` untuk skema keluaran lengkap (bagi klien MCP yang memvalidasi hasil). Di semua pengaturan, validasi server dan hasil terstruktur tetap lengkap. Lihat «Output schemas» di panduan kontrak MCP.
|
||||
Skema keluaran tidak disertakan dalam `tools/list` secara default: tidak ada API model yang membacanya, dan REA memvalidasi setiap hasil sendiri. Atur `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` dalam registrasi MCP untuk mengumumkan setiap skema keluaran dalam batas 4096 byte. Di semua pengaturan, validasi server dan hasil terstruktur tetap lengkap. Lihat «Output schemas» di panduan kontrak MCP.
|
||||
|
||||
### Minta agen Anda
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ Scegli i tuoi agenti, rivedi le modifiche proposte e approvale. La configurazion
|
||||
|
||||
La configurazione supporta Claude Code, Codex, Cursor, Gemini CLI, Grok Build e [altri agenti](../getting-started/installation.md#supported-agents). Vedi [installazione e configurazione](../getting-started/installation.md) per la configurazione dei provider e la registrazione manuale MCP.
|
||||
|
||||
MCP usa schemi di input compatti per impostazione predefinita. Per tutte le indicazioni sui campi e la validazione annidata, imposta `REA_MCP_INPUT_SCHEMA_PROFILE=full` nella registrazione MCP. Entrambi i profili mantengono tutti gli strumenti e la stessa validazione canonica del server; vedi la guida ai contratti MCP.
|
||||
MCP usa schemi di input compatti per impostazione predefinita. Le indicazioni omesse da uno schema compatto seguono la descrizione dello strumento. Tutti gli strumenti e la validazione canonica del server restano invariati; vedi la guida ai contratti MCP.
|
||||
|
||||
La configurazione registra direttamente il runtime scelto e lo script di avvio di REA. Se usi un package runner, conserva la sua voce nella cache e il runtime; scarica il pacchetto da una directory attendibile fuori dal progetto di destinazione. Consulta la guida di installazione.
|
||||
|
||||
Gli schemi di output sono omessi da `tools/list` per impostazione predefinita: nessuna API di modelli li legge e REA valida ogni risultato da sé. Imposta `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` nella registrazione MCP per dichiarare ogni schema di output entro 4096 byte, oppure `full` per gli schemi di output completi (per i client MCP che validano i risultati). In ogni caso la validazione del server e i risultati strutturati restano completi. Consulta «Output schemas» nella guida ai contratti MCP.
|
||||
Gli schemi di output sono omessi da `tools/list` per impostazione predefinita: nessuna API di modelli li legge e REA valida ogni risultato da sé. Imposta `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` nella registrazione MCP per dichiarare ogni schema di output entro 4096 byte. In ogni caso la validazione del server e i risultati strutturati restano completi. Consulta «Output schemas» nella guida ai contratti MCP.
|
||||
|
||||
### Chiedi al tuo agente
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ npx rea-agents setup
|
||||
|
||||
セットアップは Claude Code、Codex、Cursor、Gemini CLI、Grok Build、および[その他のエージェント](../getting-started/installation.md#supported-agents)に対応しています。プロバイダーの設定と MCP の手動登録については、[インストールとセットアップ](../getting-started/installation.md)を参照してください。
|
||||
|
||||
MCP は既定でコンパクトな入力スキーマを使用します。完全なフィールド説明と入れ子の検証情報が必要な場合は、MCP 登録に `REA_MCP_INPUT_SCHEMA_PROFILE=full` を設定してください。どちらも全ツールと同じ正規サーバー検証を維持します。詳細は MCP 契約ガイドを参照してください。
|
||||
MCP は既定でコンパクトな入力スキーマを使用します。コンパクトなスキーマが省略する説明はツールの説明の後に続きます。全ツールと正規のサーバー検証は変わりません。詳細は MCP 契約ガイドを参照してください。
|
||||
|
||||
セットアップは選択したランタイムと REA のエントリースクリプトを直接登録します。パッケージランナーを使う場合は、そのキャッシュ項目とランタイムを残し、対象プロジェクト外の信頼できるディレクトリからパッケージを取得してください。 インストールガイドを参照してください。
|
||||
|
||||
出力スキーマはデフォルトで `tools/list` から省略されます。モデル API は出力スキーマを読まず、REA 自身がすべての結果を検証するためです。MCP 登録で `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` を設定すると各出力スキーマを 4096 バイト以内で公開し、`full` を設定すると完全な出力スキーマを公開します(結果を検証する MCP クライアント向け)。どの設定でもサーバー側の検証と構造化結果は完全なままです。 MCP 契約ガイドの「Output schemas」を参照してください。
|
||||
出力スキーマはデフォルトで `tools/list` から省略されます。モデル API は出力スキーマを読まず、REA 自身がすべての結果を検証するためです。MCP 登録で `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` を設定すると各出力スキーマを 4096 バイト以内で公開します。どの設定でもサーバー側の検証と構造化結果は完全なままです。 MCP 契約ガイドの「Output schemas」を参照してください。
|
||||
|
||||
### エージェントに依頼する
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ npx rea-agents setup
|
||||
|
||||
설정은 Claude Code, Codex, Cursor, Gemini CLI, Grok Build 및 [다른 에이전트](../getting-started/installation.md#supported-agents)를 지원합니다. 제공자 설정과 수동 MCP 등록은 [설치 및 설정](../getting-started/installation.md)을 참고하세요.
|
||||
|
||||
MCP는 기본적으로 간결한 입력 스키마를 사용합니다. 전체 필드 안내와 중첩 검증 정보가 필요하면 MCP 등록에서 `REA_MCP_INPUT_SCHEMA_PROFILE=full`을 설정하세요. 두 프로필 모두 전체 도구와 동일한 정규 서버 검증을 유지합니다. MCP 계약 가이드를 참고하세요.
|
||||
MCP는 기본적으로 간결한 입력 스키마를 사용합니다. 간결한 스키마가 생략한 안내는 도구 설명 뒤에 이어집니다. 전체 도구와 정규 서버 검증은 그대로 유지됩니다. MCP 계약 가이드를 참고하세요.
|
||||
|
||||
설정은 선택한 런타임과 REA 진입 스크립트를 직접 등록합니다. 패키지 실행기를 사용한다면 해당 캐시 항목과 런타임을 유지하고, 대상 프로젝트 밖의 신뢰할 수 있는 디렉터리에서 패키지를 받으세요. 설치 가이드를 참고하세요.
|
||||
|
||||
기본적으로 출력 스키마는 `tools/list`에서 생략됩니다. 모델 API는 출력 스키마를 읽지 않으며 REA가 모든 결과를 직접 검증합니다. MCP 등록에서 `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact`로 설정하면 각 출력 스키마를 4096바이트 이내로 제공하고, `full`로 설정하면 완전한 출력 스키마를 제공합니다(결과를 검증하는 MCP 클라이언트용). 어떤 설정에서도 서버 검증과 구조화된 결과는 완전하게 유지됩니다. MCP 계약 가이드의 «Output schemas»를 참고하세요.
|
||||
기본적으로 출력 스키마는 `tools/list`에서 생략됩니다. 모델 API는 출력 스키마를 읽지 않으며 REA가 모든 결과를 직접 검증합니다. MCP 등록에서 `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact`로 설정하면 각 출력 스키마를 4096바이트 이내로 제공합니다. 어떤 설정에서도 서버 검증과 구조화된 결과는 완전하게 유지됩니다. MCP 계약 가이드의 «Output schemas»를 참고하세요.
|
||||
|
||||
### 에이전트에게 요청
|
||||
|
||||
|
||||
@@ -84,11 +84,11 @@ npx rea-agents setup
|
||||
|
||||
Claude Code, Codex, Cursor, Gemini CLI, Grok Build, കൂടാതെ [മറ്റ് agents](../getting-started/installation.md#supported-agents) എന്നിവയെ setup പിന്തുണയ്ക്കുന്നു. provider configuration-നും manual MCP registration-നും [installation and setup](../getting-started/installation.md) കാണുക.
|
||||
|
||||
MCP സാധാരണയായി compact input schemas ഉപയോഗിക്കുന്നു. പൂർണ്ണ field guidance-ഉം nested validation details-ഉം വേണമെങ്കിൽ MCP registration-ൽ `REA_MCP_INPUT_SCHEMA_PROFILE=full` സജ്ജമാക്കുക. രണ്ടു profiles-ലും എല്ലാ tools-ഉം ഒരേ canonical server validation-ഉം നിലനിൽക്കും; MCP contracts guide കാണുക.
|
||||
MCP സാധാരണയായി compact input schemas ഉപയോഗിക്കുന്നു. Compact schema ഒഴിവാക്കുന്ന guidance tool description-ന് ശേഷം വരും. എല്ലാ tools-ഉം canonical server validation-ഉം മാറ്റമില്ലാതെ നിലനിൽക്കും; MCP contracts guide കാണുക.
|
||||
|
||||
തിരഞ്ഞെടുത്ത runtime-ഉം REA entry script-ഉം setup നേരിട്ട് രജിസ്റ്റർ ചെയ്യുന്നു. Package runner ഉപയോഗിക്കുമ്പോൾ അതിന്റെ cache entry-യും runtime-ഉം നിലനിർത്തുക; target project-ന് പുറത്തുള്ള വിശ്വസനീയമായ ഡയറക്ടറിയിൽ നിന്ന് package നേടുക. ഇൻസ്റ്റലേഷൻ ഗൈഡ് കാണുക.
|
||||
|
||||
സ്ഥിരസ്ഥിതിയായി output schema-കൾ `tools/list`-ൽ നിന്ന് ഒഴിവാക്കുന്നു: ഒരു model API-യും അവ വായിക്കുന്നില്ല, ഓരോ result-ഉം REA തന്നെ validate ചെയ്യുന്നു. ഓരോ output schema-യും 4096 byte പരിധിക്കുള്ളിൽ പരസ്യപ്പെടുത്താൻ MCP registration-ൽ `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact`, പൂർണ്ണ output schema-കൾക്ക് `full` (result-കൾ validate ചെയ്യുന്ന MCP client-കൾക്ക്) സജ്ജമാക്കുക. എല്ലാ ക്രമീകരണങ്ങളിലും Server validation-ഉം structured result-കളും പൂർണ്ണമായി നിലനിൽക്കും. MCP കരാർ ഗൈഡിലെ «Output schemas» കാണുക.
|
||||
സ്ഥിരസ്ഥിതിയായി output schema-കൾ `tools/list`-ൽ നിന്ന് ഒഴിവാക്കുന്നു: ഒരു model API-യും അവ വായിക്കുന്നില്ല, ഓരോ result-ഉം REA തന്നെ validate ചെയ്യുന്നു. ഓരോ output schema-യും 4096 byte പരിധിക്കുള്ളിൽ പരസ്യപ്പെടുത്താൻ MCP registration-ൽ `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` സജ്ജമാക്കുക. എല്ലാ ക്രമീകരണങ്ങളിലും Server validation-ഉം structured result-കളും പൂർണ്ണമായി നിലനിൽക്കും. MCP കരാർ ഗൈഡിലെ «Output schemas» കാണുക.
|
||||
|
||||
### നിങ്ങളുടെ agent-നോട് ആവശ്യപ്പെടുക
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ Wybierz swoich agentów, sprawdź proponowane zmiany i zatwierdź je. Konfigurat
|
||||
|
||||
Konfigurator obsługuje Claude Code, Codex, Cursor, Gemini CLI, Grok Build i [innych agentów](../getting-started/installation.md#supported-agents). Konfigurację dostawców analizy i ręczną rejestrację MCP opisuje przewodnik [instalacji i konfiguracji](../getting-started/installation.md).
|
||||
|
||||
MCP domyślnie używa zwartych schematów wejściowych. Pełne opisy pól i zagnieżdżonej walidacji włącza `REA_MCP_INPUT_SCHEMA_PROFILE=full` w rejestracji MCP. Oba profile zachowują wszystkie narzędzia i tę samą kanoniczną walidację serwera; zobacz przewodnik kontraktów MCP.
|
||||
MCP domyślnie używa zwartych schematów wejściowych. Wskazówki pominięte w zwartym schemacie trafiają do opisu narzędzia. Wszystkie narzędzia i kanoniczna walidacja serwera pozostają bez zmian; zobacz przewodnik kontraktów MCP.
|
||||
|
||||
Konfiguracja zapisuje bezpośrednio wybrane środowisko uruchomieniowe i skrypt wejściowy REA. Przy użyciu narzędzia uruchamiającego pakiety zachowaj jego wpis w pamięci podręcznej i środowisko; pobierz pakiet z zaufanego katalogu poza projektem docelowym. Zobacz instrukcję instalacji.
|
||||
|
||||
Schematy wyjściowe są domyślnie pomijane w `tools/list`: żadne API modelu ich nie czyta, a REA samo waliduje każdy wynik. Ustaw `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` w rejestracji MCP, aby udostępniać każdy schemat wyjściowy w limicie 4096 bajtów, lub `full`, aby udostępniać pełne schematy wyjściowe (dla klientów MCP, które walidują wyniki). W każdym ustawieniu walidacja serwera i wyniki strukturalne pozostają pełne. Zobacz „Output schemas” w przewodniku po kontraktach MCP.
|
||||
Schematy wyjściowe są domyślnie pomijane w `tools/list`: żadne API modelu ich nie czyta, a REA samo waliduje każdy wynik. Ustaw `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` w rejestracji MCP, aby udostępniać każdy schemat wyjściowy w limicie 4096 bajtów. W każdym ustawieniu walidacja serwera i wyniki strukturalne pozostają pełne. Zobacz „Output schemas” w przewodniku po kontraktach MCP.
|
||||
|
||||
### Zapytaj agenta
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ Escolha seus agentes, revise as alterações propostas e aprove-as. A configura
|
||||
|
||||
A configuração oferece suporte ao Claude Code, Codex, Cursor, Gemini CLI, Grok Build e [outros agentes](../getting-started/installation.md#supported-agents). Consulte [instalação e configuração](../getting-started/installation.md) para configurar provedores e registrar o MCP manualmente.
|
||||
|
||||
O MCP usa esquemas de entrada compactos por padrão. Para obter todas as orientações de campos e validação aninhada, defina `REA_MCP_INPUT_SCHEMA_PROFILE=full` no registro MCP. Ambos os perfis mantêm todas as ferramentas e a mesma validação canônica do servidor; veja o guia dos contratos MCP.
|
||||
O MCP usa esquemas de entrada compactos por padrão. As orientações que um esquema compacto omite seguem a descrição da ferramenta. Todas as ferramentas e a validação canônica do servidor permanecem iguais; veja o guia dos contratos MCP.
|
||||
|
||||
A configuração registra diretamente o runtime escolhido e o script de entrada do REA. Ao usar um executor de pacotes, mantenha sua entrada de cache e o runtime; obtenha o pacote em um diretório confiável fora do projeto alvo. Consulte o guia de instalação.
|
||||
|
||||
Os esquemas de saída são omitidos de `tools/list` por padrão: nenhuma API de modelo os lê, e o REA valida cada resultado por conta própria. Defina `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` no registro MCP para anunciar cada esquema de saída dentro de 4096 bytes, ou `full` para esquemas de saída completos (para clientes MCP que validam resultados). Em todas as configurações, a validação do servidor e os resultados estruturados continuam completos. Consulte “Output schemas” no guia de contratos MCP.
|
||||
Os esquemas de saída são omitidos de `tools/list` por padrão: nenhuma API de modelo os lê, e o REA valida cada resultado por conta própria. Defina `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` no registro MCP para anunciar cada esquema de saída dentro de 4096 bytes. Em todas as configurações, a validação do servidor e os resultados estruturados continuam completos. Consulte “Output schemas” no guia de contratos MCP.
|
||||
|
||||
### Peça ao seu agente
|
||||
|
||||
|
||||
@@ -92,16 +92,14 @@ Programul de configurare acceptă Claude Code, Codex, Cursor, Gemini CLI, Grok B
|
||||
[instalarea și configurarea](../getting-started/installation.md) pentru configurarea motoarelor
|
||||
de analiză și înregistrarea manuală a serverului MCP.
|
||||
|
||||
MCP folosește implicit scheme de intrare compacte, pentru a reduce spațiul ocupat
|
||||
în context. Setează `REA_MCP_INPUT_SCHEMA_PROFILE=full` în înregistrarea MCP când
|
||||
ai nevoie de îndrumările complete pentru câmpurile expuse și de detaliile
|
||||
validării structurilor imbricate. Ambele profiluri păstrează toate uneltele și
|
||||
validarea canonică a serverului. Consultă secțiunea „Compact input schema profile”
|
||||
MCP anunță scheme de intrare compacte, pentru a reduce spațiul ocupat în
|
||||
context; îndrumările omise de o schemă compactă urmează descrierea uneltei.
|
||||
Toate uneltele și validarea canonică a serverului rămân neschimbate. Consultă secțiunea „Compact input schema profile”
|
||||
din ghidul contractelor MCP.
|
||||
|
||||
Configurarea înregistrează direct mediul de execuție ales și scriptul de intrare REA. Dacă folosești un executor de pachete, păstrează intrarea sa din cache și mediul de execuție; obține pachetul dintr-un director de încredere din afara proiectului țintă. Consultă ghidul de instalare.
|
||||
|
||||
Schemele de ieșire sunt omise implicit din `tools/list`: niciun API de model nu le citește, iar REA validează singur fiecare rezultat. Setează `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` în înregistrarea MCP pentru a anunța fiecare schemă de ieșire în limita a 4096 de octeți, sau `full` pentru scheme de ieșire complete (pentru clienții MCP care validează rezultatele). În toate setările, validarea serverului și rezultatele structurate rămân complete. Consultă „Output schemas” din ghidul contractelor MCP.
|
||||
Schemele de ieșire sunt omise implicit din `tools/list`: niciun API de model nu le citește, iar REA validează singur fiecare rezultat. Setează `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` în înregistrarea MCP pentru a anunța fiecare schemă de ieșire în limita a 4096 de octeți. În toate setările, validarea serverului și rezultatele structurate rămân complete. Consultă „Output schemas” din ghidul contractelor MCP.
|
||||
|
||||
### Cere-i agentului
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ npx rea-agents setup
|
||||
|
||||
Настройка поддерживает Claude Code, Codex, Cursor, Gemini CLI, Grok Build и [других агентов](../getting-started/installation.md#supported-agents). Настройка провайдеров и ручная регистрация MCP описаны в разделе [установка и настройка](../getting-started/installation.md).
|
||||
|
||||
MCP по умолчанию использует компактные входные схемы. Для полного описания полей и вложенной валидации задайте `REA_MCP_INPUT_SCHEMA_PROFILE=full` в регистрации MCP. Оба профиля сохраняют все инструменты и одинаковую каноническую проверку на сервере; см. руководство по контрактам MCP.
|
||||
MCP по умолчанию использует компактные входные схемы. Указания, которые компактная схема опускает, следуют за описанием инструмента. Все инструменты и каноническая проверка на сервере не меняются; см. руководство по контрактам MCP.
|
||||
|
||||
Настройка напрямую регистрирует выбранную среду выполнения и входной скрипт REA. При использовании средства запуска пакетов сохраняйте его запись в кэше и среду выполнения; получайте пакет из доверенного каталога вне целевого проекта. См. руководство по установке.
|
||||
|
||||
По умолчанию схемы вывода не включаются в `tools/list`: ни один API моделей их не читает, а REA сам проверяет каждый результат. Задайте `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` в регистрации MCP, чтобы объявлять каждую схему вывода в пределах 4096 байт, или `full` для полных схем вывода (для MCP-клиентов, которые проверяют результаты). При любой настройке проверка на сервере и структурированные результаты остаются полными. См. раздел «Output schemas» в руководстве по контрактам MCP.
|
||||
По умолчанию схемы вывода не включаются в `tools/list`: ни один API моделей их не читает, а REA сам проверяет каждый результат. Задайте `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` в регистрации MCP, чтобы объявлять каждую схему вывода в пределах 4096 байт. При любой настройке проверка на сервере и структурированные результаты остаются полными. См. раздел «Output schemas» в руководстве по контрактам MCP.
|
||||
|
||||
### Задайте вопрос агенту
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ npx rea-agents setup
|
||||
|
||||
ขั้นตอนตั้งค่ารองรับ Claude Code, Codex, Cursor, Gemini CLI, Grok Build และ[เอเจนต์อื่น ๆ](../getting-started/installation.md#supported-agents) ดู[การติดตั้งและตั้งค่า](../getting-started/installation.md) สำหรับการตั้งค่าผู้ให้บริการวิเคราะห์และการลงทะเบียน MCP ด้วยตนเอง
|
||||
|
||||
MCP ใช้สคีมาอินพุตแบบกระชับโดยปริยาย หากต้องการคำอธิบายฟิลด์และการตรวจสอบแบบซ้อนทั้งหมด ให้ตั้ง `REA_MCP_INPUT_SCHEMA_PROFILE=full` ในการลงทะเบียน MCP ทั้งสองแบบคงเครื่องมือครบและใช้การตรวจสอบมาตรฐานฝั่งเซิร์ฟเวอร์เดียวกัน ดูคู่มือสัญญา MCP
|
||||
MCP ใช้สคีมาอินพุตแบบกระชับโดยปริยาย คำแนะนำที่สคีมาแบบกระชับละไว้จะต่อท้ายคำอธิบายเครื่องมือ เครื่องมือทั้งหมดและการตรวจสอบมาตรฐานฝั่งเซิร์ฟเวอร์ยังคงเหมือนเดิม ดูคู่มือสัญญา MCP
|
||||
|
||||
การตั้งค่าจะลงทะเบียนรันไทม์ที่เลือกและสคริปต์เริ่มต้นของ REA โดยตรง หากใช้ตัวรันแพ็กเกจ ให้เก็บรายการแคชและรันไทม์ไว้ และรับแพ็กเกจจากไดเรกทอรีที่เชื่อถือได้นอกโปรเจกต์เป้าหมาย ดูคู่มือการติดตั้ง
|
||||
|
||||
ตามค่าเริ่มต้น ระบบจะไม่ใส่สคีมาเอาต์พุตใน `tools/list` เพราะไม่มี API ของโมเดลใดอ่านสคีมาเหล่านี้ และ REA ตรวจสอบผลลัพธ์ทุกรายการเอง ตั้ง `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` ในการลงทะเบียน MCP เพื่อประกาศสคีมาเอาต์พุตแต่ละรายการภายใน 4096 ไบต์ หรือ `full` เพื่อประกาศสคีมาเอาต์พุตฉบับเต็ม (สำหรับไคลเอนต์ MCP ที่ตรวจสอบผลลัพธ์) ทุกการตั้งค่ายังคงการตรวจสอบฝั่งเซิร์ฟเวอร์และผลลัพธ์แบบมีโครงสร้างครบถ้วน ดูหัวข้อ “Output schemas” ในคู่มือสัญญา MCP
|
||||
ตามค่าเริ่มต้น ระบบจะไม่ใส่สคีมาเอาต์พุตใน `tools/list` เพราะไม่มี API ของโมเดลใดอ่านสคีมาเหล่านี้ และ REA ตรวจสอบผลลัพธ์ทุกรายการเอง ตั้ง `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` ในการลงทะเบียน MCP เพื่อประกาศสคีมาเอาต์พุตแต่ละรายการภายใน 4096 ไบต์ ทุกการตั้งค่ายังคงการตรวจสอบฝั่งเซิร์ฟเวอร์และผลลัพธ์แบบมีโครงสร้างครบถ้วน ดูหัวข้อ “Output schemas” ในคู่มือสัญญา MCP
|
||||
|
||||
### ถามเอเจนต์
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ Ajanlarınızı seçin, önerilen değişiklikleri inceleyin ve onaylayın. Kuru
|
||||
|
||||
Kurulum Claude Code, Codex, Cursor, Gemini CLI, Grok Build ve [diğer ajanları](../getting-started/installation.md#supported-agents) destekler. Sağlayıcı yapılandırması ve elle MCP kaydı için [yükleme ve kurulum](../getting-started/installation.md) belgesine bakın.
|
||||
|
||||
MCP varsayılan olarak kompakt girdi şemaları kullanır. Tam alan açıklamaları ve iç içe doğrulama ayrıntıları için MCP kaydında `REA_MCP_INPUT_SCHEMA_PROFILE=full` ayarlayın. Her iki profil tüm araçları ve aynı kanonik sunucu doğrulamasını korur; MCP sözleşme rehberine bakın.
|
||||
MCP varsayılan olarak kompakt girdi şemaları kullanır. Kompakt şemanın atladığı açıklamalar araç açıklamasının ardından gelir. Tüm araçlar ve kanonik sunucu doğrulaması değişmez; MCP sözleşme rehberine bakın.
|
||||
|
||||
Kurulum, seçilen çalışma zamanını ve REA giriş betiğini doğrudan kaydeder. Paket çalıştırıcısı kullanıyorsanız önbellek girdisini ve çalışma zamanını koruyun; paketi hedef projenin dışında güvenilir bir dizinden edinin. Kurulum rehberine bakın.
|
||||
|
||||
Çıktı şemaları varsayılan olarak `tools/list` içinde yer almaz: hiçbir model API'si bunları okumaz ve REA her sonucu kendisi doğrular. Her çıktı şemasını 4096 baytlık sınır içinde duyurmak için MCP kaydında `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact`, tam çıktı şemaları için `full` (sonuçları doğrulayan MCP istemcileri için) ayarlayın. Tüm ayarlarda sunucu doğrulaması ve yapılandırılmış sonuçlar tam kalır. MCP sözleşmeleri rehberindeki “Output schemas” bölümüne bakın.
|
||||
Çıktı şemaları varsayılan olarak `tools/list` içinde yer almaz: hiçbir model API'si bunları okumaz ve REA her sonucu kendisi doğrular. Her çıktı şemasını 4096 baytlık sınır içinde duyurmak için MCP kaydında `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` ayarlayın. Tüm ayarlarda sunucu doğrulaması ve yapılandırılmış sonuçlar tam kalır. MCP sözleşmeleri rehberindeki “Output schemas” bölümüne bakın.
|
||||
|
||||
### Ajanınıza sorun
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ npx rea-agents setup
|
||||
|
||||
Налаштування підтримує Claude Code, Codex, Cursor, Gemini CLI, Grok Build та [інших агентів](../getting-started/installation.md#supported-agents). Конфігурацію провайдерів і ручну реєстрацію MCP описано в розділі [встановлення й налаштування](../getting-started/installation.md).
|
||||
|
||||
MCP типово використовує компактні вхідні схеми. Для повного опису полів і вкладеної перевірки задайте `REA_MCP_INPUT_SCHEMA_PROFILE=full` у реєстрації MCP. Обидва профілі зберігають усі інструменти й однакову канонічну перевірку сервера; див. посібник контрактів MCP.
|
||||
MCP типово використовує компактні вхідні схеми. Вказівки, які компактна схема пропускає, йдуть після опису інструмента. Усі інструменти й канонічна перевірка сервера не змінюються; див. посібник контрактів MCP.
|
||||
|
||||
Налаштування безпосередньо реєструє вибране середовище виконання та стартовий скрипт REA. Якщо ви використовуєте засіб запуску пакетів, збережіть його запис у кеші та середовище виконання; отримуйте пакет із довіреного каталогу поза цільовим проєктом. Див. посібник зі встановлення.
|
||||
|
||||
За замовчуванням схеми виводу не включаються до `tools/list`: жоден API моделей їх не читає, а REA сам перевіряє кожен результат. Задайте `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` у реєстрації MCP, щоб оголошувати кожну схему виводу в межах 4096 байтів, або `full` для повних схем виводу (для MCP-клієнтів, які перевіряють результати). За будь-якого налаштування перевірка сервера та структуровані результати залишаються повними. Див. розділ «Output schemas» у посібнику з контрактів MCP.
|
||||
За замовчуванням схеми виводу не включаються до `tools/list`: жоден API моделей їх не читає, а REA сам перевіряє кожен результат. Задайте `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` у реєстрації MCP, щоб оголошувати кожну схему виводу в межах 4096 байтів. За будь-якого налаштування перевірка сервера та структуровані результати залишаються повними. Див. розділ «Output schemas» у посібнику з контрактів MCP.
|
||||
|
||||
### Запитайте агента
|
||||
|
||||
|
||||
@@ -92,16 +92,15 @@ Setup Claude Code, Codex, Cursor, Gemini CLI, Grok Build aur
|
||||
configuration aur manual MCP registration ke liye
|
||||
[installation aur setup](../getting-started/installation.md) dekhein.
|
||||
|
||||
Context overhead kam rakhne ke liye MCP default mein compact input schemas use
|
||||
karta hai. Agar tamam advertised fields ki guidance aur nested validation details
|
||||
chahiye hon to apni MCP registration mein `REA_MCP_INPUT_SCHEMA_PROFILE=full`
|
||||
set karein. Dono profiles mein tamam tools aur canonical server validation
|
||||
maujood rehte hain. MCP contracts guide ka "Compact input schema profile"
|
||||
Context overhead kam rakhne ke liye MCP compact input schemas advertise karta
|
||||
hai; jo guidance compact schema chhor deta hai woh tool description ke baad aati
|
||||
hai. Tamam tools aur canonical server validation baghair tabdeeli ke maujood
|
||||
rehte hain. MCP contracts guide ka "Compact input schema profile"
|
||||
section dekhein.
|
||||
|
||||
Setup chuna hua runtime aur REA entry script seedha register karta hai. Package runner use karne par uski cache entry aur runtime barqarar rakhein; package target project se bahar kisi bharosemand directory se hasil karein. Installation guide dekhein.
|
||||
|
||||
Default mein output schemas `tools/list` se nikal diye jate hain: koi model API inhein nahin parhti, aur REA har result ko khud validate karta hai. Har output schema ko 4096 bytes ki had mein advertise karne ke liye MCP registration mein `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact`, ya mukammal output schemas ke liye `full` (results validate karne wale MCP clients ke liye) set karein. Har setting mein server validation aur structured results mukammal rehte hain. MCP contracts guide ka “Output schemas” section dekhein.
|
||||
Default mein output schemas `tools/list` se nikal diye jate hain: koi model API inhein nahin parhti, aur REA har result ko khud validate karta hai. Har output schema ko 4096 bytes ki had mein advertise karne ke liye MCP registration mein `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` set karein. Har setting mein server validation aur structured results mukammal rehte hain. MCP contracts guide ka “Output schemas” section dekhein.
|
||||
|
||||
### Apne agent se kahein
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ Chọn các tác nhân, xem lại những thay đổi được đề xuất và
|
||||
|
||||
Quá trình thiết lập hỗ trợ Claude Code, Codex, Cursor, Gemini CLI, Grok Build và [các tác nhân khác](../getting-started/installation.md#supported-agents). Xem [cài đặt và thiết lập](../getting-started/installation.md) để cấu hình trình phân tích và đăng ký MCP thủ công.
|
||||
|
||||
MCP mặc định dùng schema đầu vào gọn. Để có hướng dẫn đầy đủ về trường và xác thực lồng nhau, đặt `REA_MCP_INPUT_SCHEMA_PROFILE=full` trong đăng ký MCP. Cả hai cấu hình giữ toàn bộ công cụ và cùng xác thực chuẩn ở máy chủ; xem hướng dẫn hợp đồng MCP.
|
||||
MCP mặc định dùng schema đầu vào gọn. Hướng dẫn mà schema gọn lược bỏ được nối sau mô tả công cụ. Toàn bộ công cụ và xác thực chuẩn ở máy chủ không thay đổi; xem hướng dẫn hợp đồng MCP.
|
||||
|
||||
Thiết lập đăng ký trực tiếp runtime đã chọn và script khởi chạy REA. Khi dùng trình chạy gói, hãy giữ mục cache và runtime đó; tải gói từ một thư mục đáng tin cậy bên ngoài dự án đích. Xem hướng dẫn cài đặt.
|
||||
|
||||
Theo mặc định, schema đầu ra được lược khỏi `tools/list`: không API mô hình nào đọc chúng, và REA tự xác thực mọi kết quả. Đặt `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` trong cấu hình MCP để công bố từng schema đầu ra trong giới hạn 4096 byte, hoặc `full` để công bố schema đầu ra đầy đủ (cho các client MCP có xác thực kết quả). Với mọi thiết lập, xác thực phía server và kết quả có cấu trúc vẫn đầy đủ. Xem mục “Output schemas” trong hướng dẫn hợp đồng MCP.
|
||||
Theo mặc định, schema đầu ra được lược khỏi `tools/list`: không API mô hình nào đọc chúng, và REA tự xác thực mọi kết quả. Đặt `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` trong cấu hình MCP để công bố từng schema đầu ra trong giới hạn 4096 byte. Với mọi thiết lập, xác thực phía server và kết quả có cấu trúc vẫn đầy đủ. Xem mục “Output schemas” trong hướng dẫn hợp đồng MCP.
|
||||
|
||||
### Hỏi tác nhân
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ npx rea-agents setup
|
||||
|
||||
設定流程支援 Claude Code、Codex、Cursor、Gemini CLI、Grok Build 和[其他 AI 代理](../getting-started/installation.md#supported-agents)。提供者組態和手動註冊 MCP 的方法見[安裝與設定](../getting-started/installation.md)。
|
||||
|
||||
MCP 預設使用精簡輸入 Schema,以減少上下文開銷。需要完整欄位說明和巢狀驗證細節時,請在 MCP 註冊中設定 `REA_MCP_INPUT_SCHEMA_PROFILE=full`。兩種設定都保留完整工具集和標準的伺服器驗證;見 MCP 合約指南。
|
||||
MCP 預設使用精簡輸入 Schema,以減少上下文開銷。精簡 Schema 省略的說明會接在工具描述之後。完整工具集和標準的伺服器驗證維持不變;見 MCP 合約指南。
|
||||
|
||||
設定會直接登記所選執行環境和 REA 入口指令碼。使用套件執行器時,請保留其快取項目及執行環境,並從目標專案以外的可信任目錄取得套件。 請參閱安裝指南。
|
||||
|
||||
預設會從 `tools/list` 省略輸出 Schema:沒有任何模型 API 會讀取它們,且 REA 會自行驗證每個結果。在 MCP 登記中設定 `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` 可在 4096 位元組內公布每個輸出 Schema,設定 `full` 則公布完整輸出 Schema(供會驗證結果的 MCP 用戶端使用)。所有設定下,伺服器驗證和結構化結果仍保持完整。 請參閱 MCP 合約指南中的「Output schemas」。
|
||||
預設會從 `tools/list` 省略輸出 Schema:沒有任何模型 API 會讀取它們,且 REA 會自行驗證每個結果。在 MCP 登記中設定 `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` 可在 4096 位元組內公布每個輸出 Schema。所有設定下,伺服器驗證和結構化結果仍保持完整。 請參閱 MCP 合約指南中的「Output schemas」。
|
||||
|
||||
### 詢問 AI 代理
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ npx rea-agents setup
|
||||
|
||||
设置流程支持 Claude Code、Codex、Cursor、Gemini CLI、Grok Build 和[其他智能体](../getting-started/installation.md#supported-agents)。提供方配置和手动注册 MCP 的方法见[安装与设置](../getting-started/installation.md)。
|
||||
|
||||
MCP 默认使用紧凑输入 Schema 来减少上下文开销。需要完整字段说明和嵌套验证细节时,在 MCP 注册中设置 `REA_MCP_INPUT_SCHEMA_PROFILE=full`。两种配置都保留完整工具集和规范的服务端验证;见 MCP 合约指南。
|
||||
MCP 默认使用紧凑输入 Schema 来减少上下文开销。紧凑 Schema 省略的说明会接在工具描述之后。完整工具集和规范的服务端验证保持不变;见 MCP 合约指南。
|
||||
|
||||
设置会直接注册所选运行时和 REA 入口脚本。使用包运行器时,请保留其缓存条目和运行时,并从目标项目以外的可信目录获取包。 请参阅安装指南。
|
||||
|
||||
默认从 `tools/list` 中省略输出 Schema:没有任何模型 API 读取它们,REA 会自行验证每个结果。在 MCP 注册中设置 `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` 可在 4096 字节内公布每个输出 Schema,设置 `full` 则公布完整输出 Schema(供验证结果的 MCP 客户端使用)。所有设置下,服务端验证和结构化结果仍保持完整。 请参阅 MCP 合约指南中的“Output schemas”。
|
||||
默认从 `tools/list` 中省略输出 Schema:没有任何模型 API 读取它们,REA 会自行验证每个结果。在 MCP 注册中设置 `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` 可在 4096 字节内公布每个输出 Schema。所有设置下,服务端验证和结构化结果仍保持完整。 请参阅 MCP 合约指南中的“Output schemas”。
|
||||
|
||||
### 询问智能体
|
||||
|
||||
|
||||
@@ -89,10 +89,9 @@ managed method page with
|
||||
`view: {"kind":"managed-members","collection":"methods","offset":0,"limit":64}`.
|
||||
`view: {"kind":"summary"}` has no facet. These are examples, not all supported
|
||||
families; use the producer and its guide for collection-specific fields and
|
||||
selection. The full input schema exposes accepted variants, while the default
|
||||
compact profile can omit nested shapes and parameter descriptions. Refreshing
|
||||
the same compact catalog does not restore those missing shapes; do not invent
|
||||
selectors from an opaque object declaration.
|
||||
selection. The compact input schema can omit nested shapes and parameter
|
||||
descriptions. Refreshing the catalog does not restore those missing shapes; do
|
||||
not invent selectors from an opaque object declaration.
|
||||
|
||||
Text pages use UTF-16 code units; row pages count the selected retained
|
||||
collection. Source coverage and retained-page coverage remain distinct. A
|
||||
@@ -2084,33 +2083,32 @@ rejects. The MCP handler still validates every call with the canonical
|
||||
contract. The Gemini CLI verifier checks the outgoing
|
||||
`parametersJsonSchema` and canonical contract examples against that declaration
|
||||
using a loopback model fixture; it does not establish live Google API or
|
||||
OpenCode client behavior. Set `REA_VERIFY_GEMINI_SCHEMA_PROFILE=compact` to
|
||||
verify the compact advertisement through the same client boundary.
|
||||
OpenCode client behavior.
|
||||
|
||||
### Compact input schema profile
|
||||
|
||||
Some function-calling providers reject a request while any single tool's
|
||||
`parameters` schema exceeds a literal serialized size cap; Moonshot clients
|
||||
such as kimi-cli have reported failures with the full catalog attached
|
||||
([#1484](https://github.com/morluto/rea/issues/1484)). Setting the server's
|
||||
`REA_MCP_INPUT_SCHEMA_PROFILE` environment variable to `compact` (the default)
|
||||
renders every advertised input schema within a 13,312-byte budget, measured to sit below
|
||||
([#1484](https://github.com/morluto/rea/issues/1484)). REA therefore renders
|
||||
every advertised input schema within a 13,312-byte budget, measured to sit below
|
||||
Moonshot's observed cap (community measurements: a 13.7 KB schema is accepted
|
||||
while a ~15 KB one is rejected). The setting is read at startup; restart the
|
||||
server to apply changes, as with `REA_MCP_MAX_RESPONSE_BYTES`. Explicitly select
|
||||
`full` to retain complete advertised input guidance and nested validation detail.
|
||||
Existing explicit profile choices are preserved. Compact presentation
|
||||
while a ~15 KB one is rejected). This compact presentation is the only input
|
||||
advertisement. `REA_MCP_INPUT_SCHEMA_PROFILE` accepts only `compact`; the former
|
||||
`full` profile was removed, and a registration that still selects it fails at
|
||||
startup with a configuration error naming the setting. Remove the setting, or
|
||||
set it to `compact`. Compact presentation
|
||||
also reduces schemas whose expanded JSON tree exceeds ten levels, the limit
|
||||
reported by strict-depth MCP clients in [#2813](https://github.com/morluto/rea/issues/2813).
|
||||
WorkBuddy/CodeBuddy gateways have also rejected full REA catalogs with an
|
||||
opaque `11133` error while accepting the compact profile
|
||||
WorkBuddy/CodeBuddy gateways have also rejected uncompacted REA catalogs with
|
||||
an opaque `11133` error while accepting the compact profile
|
||||
([#2999](https://github.com/morluto/rea/issues/2999)); compatibility depends on
|
||||
the selected gateway and model. That report used ZCode through a gateway to a
|
||||
Kimi-K3 backend, not the WorkBuddy MCP client. Set the profile on REA's
|
||||
registration in the actual MCP host; the host's name alone does not identify
|
||||
the downstream model or its schema constraints.
|
||||
|
||||
The compact profile also projects every advertised input schema into the
|
||||
The compact presentation also projects every advertised input schema into the
|
||||
portable advertised dialect declared in `src/contracts/advertisedDialect.ts`:
|
||||
the subset of JSON Schema that the clients and gateways REA has met accept,
|
||||
with one rule per observed rejection (for example strict gateways in
|
||||
@@ -2170,11 +2168,10 @@ dialect checker, keep them valid JSON Schema that still accepts every
|
||||
canonical example. Client verification
|
||||
also checks provider-facing sizes and canonical examples after conversion by
|
||||
the actual Kimi Code CLI; local captures do not establish Moonshot acceptance.
|
||||
The SDK catalog test checks that full-profile schemas retain nested ADB extra,
|
||||
managed selection, and WASM timeout and detail restrictions. Path-contract tests
|
||||
check full-profile local-path rules. Compact MCP workflow tests verify canonical
|
||||
rejection of invalid inputs before provider work, independent of how much detail
|
||||
the selected advertisement retains.
|
||||
The SDK catalog test checks that the canonical input schemas retain nested ADB
|
||||
extra, managed selection, and WASM timeout and detail restrictions. Compact MCP
|
||||
workflow tests verify canonical rejection of invalid inputs before provider
|
||||
work, independent of how much detail the advertisement retains.
|
||||
|
||||
<a id="hotcold-catalog-discovery"></a>
|
||||
|
||||
@@ -2307,8 +2304,8 @@ preserved. This keeps every tool and validation rule in discovery and does not
|
||||
change the SDK's catalog invalidation or availability checks.
|
||||
|
||||
The server identity's tool-schema digest includes the selected output-schema
|
||||
profile and the advertised form that profile produces. `full`, `compact`, and
|
||||
`off` therefore have distinct identities. The `compact` digest covers the
|
||||
profile and the advertised form that profile produces. `compact` and `off`
|
||||
therefore have distinct identities. The `compact` digest covers the
|
||||
bounded schemas clients receive; `off` keeps the canonical output contracts in
|
||||
the digest while advertising nothing.
|
||||
|
||||
@@ -2319,9 +2316,9 @@ validation, and complete output schemas were about 92% of a default
|
||||
`tools/list` (4.3 of 4.6 MB). Some clients also reject a large `tools/list`
|
||||
frame or HTTP SSE event before they load any tools (the measured pi-web-ui
|
||||
failure is in [#2512](https://github.com/morluto/rea/issues/2512)). Select
|
||||
`compact` to bound every advertised output schema to 4,096 bytes, or `full`
|
||||
for complete output schemas when an MCP client should validate results. Every
|
||||
profile keeps the same named canonical tool contract for server-side result
|
||||
`compact` to bound every advertised output schema to 4,096 bytes. The former
|
||||
`full` output profile was removed; a registration that still selects it fails
|
||||
at startup with a configuration error naming the setting. Both profiles keep the same named canonical tool contract for server-side result
|
||||
validation and still returns complete `structuredContent` and text results;
|
||||
the MCP specification does not require an output schema for
|
||||
`structuredContent`.
|
||||
@@ -2339,12 +2336,7 @@ within the budget, so the advertised form is self-contained and omits the
|
||||
canonical contract's content-bound `$id`. The projection only widens: a valid
|
||||
canonical result still satisfies the advertised schema, but a client validating
|
||||
against it can accept a result the canonical contract rejects. REA still
|
||||
validates every result before delivery. On catalogs large enough that full
|
||||
input schemas alone approach the frame limit, keep the input profile at its
|
||||
`compact` default as well; with full input schemas the advertisement is
|
||||
dominated by the input side.
|
||||
The full input profile has no one-megabyte catalog-frame guarantee, including
|
||||
when output schemas are omitted.
|
||||
validates every result before delivery.
|
||||
|
||||
Only client-side schema discovery and validation are reduced or omitted, so
|
||||
hosts using these profiles should rely on REA's validation and retain their
|
||||
|
||||
@@ -23,7 +23,7 @@ const current = (
|
||||
await readFile(join(sourceRoot, "SKILL.md"), "utf8")
|
||||
).replaceAll("\r\n", "\n");
|
||||
const cacheBust = String(Date.now());
|
||||
const { FULL_CATALOG_IDENTITY } = await import(
|
||||
const { DEFAULT_CATALOG_IDENTITY } = await import(
|
||||
`${pathToFileURL(join(root, "dist/catalogIdentity.js")).href}?${cacheBust}`
|
||||
);
|
||||
const { PRODUCT_IDENTITY } = await import(
|
||||
@@ -34,7 +34,7 @@ const latestSpecifier = PRODUCT_IDENTITY.packageSpecifier;
|
||||
const pinnedSpecifier = PRODUCT_IDENTITY.registrationPackageSpecifier;
|
||||
const generated = addSkillManifestToolCount(
|
||||
current,
|
||||
FULL_CATALOG_IDENTITY.counts.mcp_tools,
|
||||
DEFAULT_CATALOG_IDENTITY.counts.mcp_tools,
|
||||
);
|
||||
if (!generated.ok) throw generated.error;
|
||||
const source = generated.value.replaceAll(latestSpecifier, pinnedSpecifier);
|
||||
|
||||
@@ -4,10 +4,8 @@ import { Client } from "@modelcontextprotocol/client";
|
||||
import { createVerifierCommands } from "./verify-commands.mjs";
|
||||
import { createOwnedMcpStdioTransport } from "./owned-mcp-stdio.mjs";
|
||||
import { withVerifierCleanup } from "./verifier-cleanup.mjs";
|
||||
import {
|
||||
INLINE_EVIDENCE_MCP_ENVIRONMENT,
|
||||
schemaVerifierEnvironment,
|
||||
} from "./mcp-verification-environment.mjs";
|
||||
import { INLINE_EVIDENCE_MCP_ENVIRONMENT } from "./mcp-verification-environment.mjs";
|
||||
import { withCanonicalOutputSchema } from "./canonical-output-schema.mjs";
|
||||
|
||||
// Bind parsed definitions to this connection. Hot/cold catalogs may add tools;
|
||||
// refresh a missing name once without throwing away already validated bindings.
|
||||
@@ -59,7 +57,7 @@ const advertisedTool = async (client, name) => {
|
||||
return tool;
|
||||
};
|
||||
|
||||
/** Call an advertised tool with SDK output validation of its actual schema. */
|
||||
/** Call an advertised tool with SDK output validation against its canonical contract. */
|
||||
export async function artifactMcpCall(client, parameters, options = {}) {
|
||||
options.signal?.throwIfAborted();
|
||||
assert.equal(
|
||||
@@ -67,11 +65,9 @@ export async function artifactMcpCall(client, parameters, options = {}) {
|
||||
undefined,
|
||||
"Artifact verification owns the advertised schema; pass request options only",
|
||||
);
|
||||
const toolDefinition = await advertisedTool(client, parameters.name);
|
||||
assert.notEqual(
|
||||
toolDefinition.outputSchema,
|
||||
undefined,
|
||||
`MCP tool omitted its output schema: ${parameters.name}`,
|
||||
// tools/list omits output schemas by default; bind the canonical contract.
|
||||
const toolDefinition = withCanonicalOutputSchema(
|
||||
await advertisedTool(client, parameters.name),
|
||||
);
|
||||
options.signal?.throwIfAborted();
|
||||
return client.callTool(parameters, { ...options, toolDefinition });
|
||||
@@ -98,7 +94,7 @@ const environment = () => {
|
||||
const compileCache =
|
||||
process.env.NODE_COMPILE_CACHE ??
|
||||
process.env.REA_ARTIFACT_NODE_COMPILE_CACHE;
|
||||
return schemaVerifierEnvironment({
|
||||
return {
|
||||
PATH: process.env.PATH ?? "/usr/bin:/bin",
|
||||
...(compileCache &&
|
||||
process.env.NODE_V8_COVERAGE === undefined &&
|
||||
@@ -109,7 +105,7 @@ const environment = () => {
|
||||
REA_ANALYSIS_PROVIDER: "auto",
|
||||
// Artifact workflows have no deep-provider prerequisite or GUI authority.
|
||||
HOPPER_LAUNCHER_PATH: "/rea-unconfigured-deep-provider/hopper",
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/** Execute the compiled CLI with production providers and no test adapters. */
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Client } from "@modelcontextprotocol/client";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
import { artifactCliEvidence, artifactMcpResult } from "./artifact-e2e.mjs";
|
||||
import { webSourceMapsSchema } from "../../dist/domain/webBundleAnalysis.js";
|
||||
import { schemaVerifierEnvironment } from "./mcp-verification-environment.mjs";
|
||||
|
||||
const ANNOTATION_BYTES = 2 * 1024 * 1024;
|
||||
const SCRIPT_COUNT = 5;
|
||||
@@ -107,10 +106,10 @@ export async function verifyBrowserCaptureMetadataBudget(endpoint) {
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [fileURLToPath(new URL("../rea.mjs", import.meta.url)), "mcp"],
|
||||
env: schemaVerifierEnvironment({
|
||||
env: {
|
||||
PATH: process.env.PATH ?? "",
|
||||
REA_LOG_LEVEL: "silent",
|
||||
}),
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
const client = new Client({ name: "browser-metadata-e2e", version: "1" });
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { schemaVerifierEnvironment } from "./mcp-verification-environment.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import { requireMcpEvidenceBundle } from "./mcp-verifier-results.mjs";
|
||||
import { createHash } from "node:crypto";
|
||||
@@ -138,7 +137,7 @@ export async function verifyScenarioStorageFingerprints(executable, origin) {
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [fileURLToPath(new URL("../rea.mjs", import.meta.url)), "mcp"],
|
||||
env: schemaVerifierEnvironment(process.env),
|
||||
env: process.env,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const client = new Client({
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { Tool } from "@modelcontextprotocol/client";
|
||||
export function canonicalOutputSchema(name: string): Record<string, unknown>;
|
||||
export function withCanonicalOutputSchema<T extends Pick<Tool, "name">>(
|
||||
tool: T,
|
||||
): T & { outputSchema: Record<string, unknown> };
|
||||
@@ -0,0 +1,24 @@
|
||||
import { z } from "zod";
|
||||
import { DISCOVER_TOOLS } from "../../dist/catalogDiscovery.js";
|
||||
import { TOOL_CONTRACTS } from "../../dist/contracts/toolContracts.js";
|
||||
import { advertisedOutputJsonSchema } from "../../dist/contracts/toolSchemaMetadata.js";
|
||||
|
||||
/**
|
||||
* The canonical output JSON Schema for one tool. `tools/list` omits output
|
||||
* schemas by default (or bounds them), so verifiers validate replies against
|
||||
* the compiled contract instead of a listed definition.
|
||||
*/
|
||||
export const canonicalOutputSchema = (name) => {
|
||||
// Transport discovery is registered from its own schema, not a tool contract.
|
||||
if (name === DISCOVER_TOOLS.name)
|
||||
return z.toJSONSchema(DISCOVER_TOOLS.outputSchema, { io: "output" });
|
||||
const contract = TOOL_CONTRACTS.find((candidate) => candidate.name === name);
|
||||
if (contract === undefined) throw new Error(`No tool contract named ${name}`);
|
||||
return advertisedOutputJsonSchema(contract);
|
||||
};
|
||||
|
||||
/** A listed tool definition whose output schema the SDK client validates against. */
|
||||
export const withCanonicalOutputSchema = (tool) => ({
|
||||
...tool,
|
||||
outputSchema: canonicalOutputSchema(tool.name),
|
||||
});
|
||||
@@ -40,7 +40,7 @@ export function toolFamilyCatalog(sources: {
|
||||
readonly TOOL_CONTRACTS: readonly unknown[];
|
||||
};
|
||||
readonly catalogIdentity: {
|
||||
readonly FULL_CATALOG_IDENTITY: {
|
||||
readonly DEFAULT_CATALOG_IDENTITY: {
|
||||
readonly counts: { readonly mcp_tools: number };
|
||||
};
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ export const toolFamilyCatalog = (sources) => {
|
||||
if (
|
||||
familyTotal !== sources.toolContracts.TOOL_CONTRACTS.length ||
|
||||
familyTotal !==
|
||||
sources.catalogIdentity.FULL_CATALOG_IDENTITY.counts.mcp_tools
|
||||
sources.catalogIdentity.DEFAULT_CATALOG_IDENTITY.counts.mcp_tools
|
||||
)
|
||||
throw new Error("Tool family inventory does not match TOOL_CONTRACTS");
|
||||
return { total: familyTotal, families };
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
export const INLINE_EVIDENCE_MCP_ENVIRONMENT: {
|
||||
readonly REA_MCP_MAX_INPUT_ELEMENTS: "65536";
|
||||
};
|
||||
|
||||
/** Select complete output contracts when validating advertised schemas. */
|
||||
export function schemaVerifierEnvironment<
|
||||
Environment extends Record<string, string | undefined>,
|
||||
>(
|
||||
environment: Environment,
|
||||
): Environment & { REA_MCP_OUTPUT_SCHEMA_PROFILE: "full" };
|
||||
@@ -4,9 +4,3 @@
|
||||
export const INLINE_EVIDENCE_MCP_ENVIRONMENT = Object.freeze({
|
||||
REA_MCP_MAX_INPUT_ELEMENTS: "65536",
|
||||
});
|
||||
|
||||
/** Select complete output contracts when validating advertised schemas. */
|
||||
export const schemaVerifierEnvironment = (environment) => ({
|
||||
...environment,
|
||||
REA_MCP_OUTPUT_SCHEMA_PROFILE: "full",
|
||||
});
|
||||
@@ -1,4 +1,3 @@
|
||||
import { schemaVerifierEnvironment } from "./mcp-verification-environment.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdir, mkdtemp, readdir, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
@@ -237,7 +236,7 @@ async function verifyNativeValueAdapters(
|
||||
const transport = await createOwnedMcpStdioTransport({
|
||||
command: process.execPath,
|
||||
arguments: [entrypoint, "mcp"],
|
||||
hostEnvironment: schemaVerifierEnvironment(env),
|
||||
hostEnvironment: env,
|
||||
cwd: process.cwd(),
|
||||
expectedCommand: null,
|
||||
sweepTokenOwnedProcesses: true,
|
||||
|
||||
@@ -43,7 +43,7 @@ export const createProductCatalog = async (root) => {
|
||||
aliases: cli.aliases,
|
||||
},
|
||||
runtime_catalog: {
|
||||
counts: sources.catalogIdentity.FULL_CATALOG_IDENTITY.counts,
|
||||
counts: sources.catalogIdentity.DEFAULT_CATALOG_IDENTITY.counts,
|
||||
digests: {
|
||||
// Full runtime schema digests belong to server identity, not this facts projection.
|
||||
providers_sha256: digest(providers),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { canonicalOutputSchema } from "./canonical-output-schema.mjs";
|
||||
import {
|
||||
requireMcpToolError,
|
||||
mcpTextValue,
|
||||
@@ -43,15 +44,19 @@ export async function verifyGhidraBoundaries(
|
||||
addFormats(ajv);
|
||||
const validators = new Map();
|
||||
for (const tool of catalog.tools) {
|
||||
for (const key of ["inputSchema", "outputSchema"])
|
||||
const outputSchema = canonicalOutputSchema(tool.name);
|
||||
for (const [key, schema] of [
|
||||
["inputSchema", tool.inputSchema],
|
||||
["outputSchema", outputSchema],
|
||||
])
|
||||
assert.ok(
|
||||
ajv.validateSchema(tool[key]),
|
||||
ajv.validateSchema(schema),
|
||||
`${tool.name} ${key}: ${JSON.stringify(ajv.errors)}`,
|
||||
);
|
||||
validators.set(
|
||||
tool.name,
|
||||
(tool.outputSchema?.$id && ajv.getSchema(tool.outputSchema.$id)) ||
|
||||
ajv.compile(tool.outputSchema),
|
||||
(outputSchema.$id && ajv.getSchema(outputSchema.$id)) ||
|
||||
ajv.compile(outputSchema),
|
||||
);
|
||||
}
|
||||
let successfulCalls = 0;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { canonicalOutputSchema } from "./canonical-output-schema.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import { execFile } from "node:child_process";
|
||||
import { copyFile, mkdtemp, readFile, realpath, rm } from "node:fs/promises";
|
||||
@@ -40,15 +41,19 @@ export async function verifyHopperBoundaryContracts(
|
||||
ajv.validateSchema(tool.inputSchema),
|
||||
`${tool.name} input schema`,
|
||||
);
|
||||
assert.ok(tool.outputSchema, `${tool.name} omitted its output schema`);
|
||||
assert.ok(
|
||||
ajv.validateSchema(tool.outputSchema),
|
||||
canonicalOutputSchema(tool.name),
|
||||
`${tool.name} omitted its output schema`,
|
||||
);
|
||||
assert.ok(
|
||||
ajv.validateSchema(canonicalOutputSchema(tool.name)),
|
||||
`${tool.name} output schema`,
|
||||
);
|
||||
validators.set(
|
||||
tool.name,
|
||||
(tool.outputSchema?.$id && ajv.getSchema(tool.outputSchema.$id)) ||
|
||||
ajv.compile(tool.outputSchema),
|
||||
(canonicalOutputSchema(tool.name)?.$id &&
|
||||
ajv.getSchema(canonicalOutputSchema(tool.name).$id)) ||
|
||||
ajv.compile(canonicalOutputSchema(tool.name)),
|
||||
);
|
||||
}
|
||||
let successfulCalls = 0;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { withVerifierCleanup } from "./verifier-cleanup.mjs";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { FULL_CATALOG_IDENTITY } from "../../dist/catalogIdentity.js";
|
||||
import { DEFAULT_CATALOG_IDENTITY } from "../../dist/catalogIdentity.js";
|
||||
import { verifyCompleteToolCatalog } from "./verify-package-core.mjs";
|
||||
|
||||
/** Install the selected archive or registry specifier with an owned ten-minute deadline. */
|
||||
@@ -79,14 +79,10 @@ export async function verifyInstalledCanaryPackage(
|
||||
return withVerifierCleanup(async () => {
|
||||
try {
|
||||
await owner.open(client, transport);
|
||||
const toolNames = await verifyCompleteToolCatalog(
|
||||
client,
|
||||
undefined,
|
||||
environment,
|
||||
);
|
||||
const toolNames = await verifyCompleteToolCatalog(client);
|
||||
return {
|
||||
mcpTools: toolNames.length,
|
||||
canonicalMcpTools: FULL_CATALOG_IDENTITY.counts.mcp_tools,
|
||||
canonicalMcpTools: DEFAULT_CATALOG_IDENTITY.counts.mcp_tools,
|
||||
};
|
||||
} catch (cause) {
|
||||
throw new Error(`Installed package MCP verification failed: ${stderr}`, {
|
||||
|
||||
@@ -2,6 +2,5 @@ import type { Client } from "@modelcontextprotocol/client";
|
||||
export function verifyCompleteToolCatalog(
|
||||
client: Pick<Client, "listTools" | "callTool">,
|
||||
options?: Parameters<Client["listTools"]>[1],
|
||||
environment?: Readonly<NodeJS.ProcessEnv>,
|
||||
): Promise<string[]>;
|
||||
export function pathExists(path: string): Promise<boolean>;
|
||||
@@ -5,23 +5,15 @@ import { Ajv2020 } from "ajv/dist/2020.js";
|
||||
* Verify canonical names, converted JSON Schemas, and session catalog metadata.
|
||||
* Only this operation needs built contracts; path checks remain source-only.
|
||||
*/
|
||||
export const verifyCompleteToolCatalog = async (
|
||||
client,
|
||||
options,
|
||||
environment = {},
|
||||
) => {
|
||||
export const verifyCompleteToolCatalog = async (client, options) => {
|
||||
const [
|
||||
{ requireMcpLifecycleResult, sortVerifierNames },
|
||||
{ TOOL_CONTRACTS },
|
||||
dialect,
|
||||
{ ADVERTISED_INPUT_DIALECT_BASELINE },
|
||||
{ mcpInputSchemaProfileSchema },
|
||||
] = await Promise.all([
|
||||
import("./mcp-verifier-results.mjs"),
|
||||
import("../../dist/contracts/toolContracts.js"),
|
||||
import("../../dist/contracts/advertisedDialect.js"),
|
||||
import("../../dist/contracts/advertisedDialectBaseline.js"),
|
||||
import("../../dist/config/mcpInputSchemaProfile.js"),
|
||||
]);
|
||||
const listed = await client.listTools(undefined, options);
|
||||
const canonical = sortVerifierNames(TOOL_CONTRACTS.map(({ name }) => name));
|
||||
@@ -48,16 +40,13 @@ export const verifyCompleteToolCatalog = async (
|
||||
);
|
||||
}
|
||||
}
|
||||
// Compare the advertisement with the profiles actually passed to the server.
|
||||
const inputProfile = mcpInputSchemaProfileSchema.parse(
|
||||
environment.REA_MCP_INPUT_SCHEMA_PROFILE ?? "compact",
|
||||
);
|
||||
// The compact input presentation is enforced at zero, like every output pattern.
|
||||
const regressions = [
|
||||
...dialect.advertisedDialectRegressions(
|
||||
dialect.countAdvertisedDialectViolations(
|
||||
listed.tools.flatMap(dialect.advertisedInputDialectViolations),
|
||||
),
|
||||
ADVERTISED_INPUT_DIALECT_BASELINE[inputProfile],
|
||||
{},
|
||||
),
|
||||
...dialect.advertisedDialectRegressions(
|
||||
dialect.countAdvertisedDialectViolations(
|
||||
|
||||
@@ -13,16 +13,11 @@ const { createCatalogDigestProfiles } = await import(
|
||||
pathToFileURL(join(compiledRoot, "catalogIdentity.js")).href
|
||||
);
|
||||
// Retain only hashes, with every shared record frozen. Schemas remain owned by
|
||||
// the compiled contracts and are converted once for all six identities.
|
||||
// the compiled contracts and are converted once for both identities.
|
||||
const profiles = Object.entries(createCatalogDigestProfiles())
|
||||
.map(
|
||||
([input, outputs]) =>
|
||||
`${JSON.stringify(input)}: Object.freeze({${Object.entries(outputs)
|
||||
.map(
|
||||
([output, digests]) =>
|
||||
`${JSON.stringify(output)}: Object.freeze(${JSON.stringify(digests)})`,
|
||||
)
|
||||
.join(",")}})`,
|
||||
([output, digests]) =>
|
||||
`${JSON.stringify(output)}: Object.freeze(${JSON.stringify(digests)})`,
|
||||
)
|
||||
.join(",");
|
||||
await writeFile(
|
||||
|
||||
@@ -28,11 +28,10 @@ const [
|
||||
read("domain/evidenceInputContract.js"),
|
||||
read("contracts/toolSchemaMetadata.js"),
|
||||
]);
|
||||
const recordCatalog = async (profile) => {
|
||||
const recordCatalog = async () => {
|
||||
// Catalog generation is independent of the host account and user configuration.
|
||||
const home = await mkdtemp(join(tmpdir(), "rea-mcp-catalog-"));
|
||||
const environment = {
|
||||
REA_MCP_INPUT_SCHEMA_PROFILE: profile,
|
||||
HOME: home,
|
||||
USERPROFILE: home,
|
||||
XDG_CONFIG_HOME: join(home, "config"),
|
||||
@@ -97,13 +96,7 @@ const recordCatalog = async (profile) => {
|
||||
};
|
||||
const encoded = JSON.stringify(catalog);
|
||||
const digest = createHash("sha256").update(encoded).digest("hex");
|
||||
await writeFile(
|
||||
join(
|
||||
compiled,
|
||||
profile === "full" ? "mcp-catalog-full.json" : "mcp-catalog.json",
|
||||
),
|
||||
encoded,
|
||||
);
|
||||
await writeFile(join(compiled, "mcp-catalog.json"), encoded);
|
||||
return digest;
|
||||
} catch (cause) {
|
||||
producerFailure = { cause };
|
||||
@@ -132,10 +125,8 @@ const recordCatalog = async (profile) => {
|
||||
}
|
||||
};
|
||||
|
||||
const digests = {};
|
||||
for (const profile of ["compact", "full"])
|
||||
digests[profile] = await recordCatalog(profile);
|
||||
const digest = await recordCatalog();
|
||||
await writeFile(
|
||||
join(compiled, "recordedMcpCatalog.js"),
|
||||
`/** Recorded from the compiled runtime; do not edit. */\nexport const RECORDED_MCP_CATALOG_SHA256 = ${JSON.stringify(digests)};\n`,
|
||||
`/** Recorded from the compiled runtime; do not edit. */\nexport const RECORDED_MCP_CATALOG_SHA256 = ${JSON.stringify(digest)};\n`,
|
||||
);
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mjs";
|
||||
import { canonicalOutputSchema } from "./lib/canonical-output-schema.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import { execFile } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
@@ -224,7 +224,7 @@ const client = new Client({ name: "apk-signature-verifier", version: "1" });
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [entrypoint, "mcp"],
|
||||
env: schemaVerifierEnvironment(environment),
|
||||
env: environment,
|
||||
stderr: "pipe",
|
||||
});
|
||||
// The MCP server child reads files under the workspace until it has exited.
|
||||
@@ -238,11 +238,11 @@ try {
|
||||
const advertised = (await client.listTools()).tools.find(
|
||||
({ name }) => name === "inspect_apk_signature",
|
||||
);
|
||||
assert.ok(advertised?.outputSchema, "tool must advertise an output schema");
|
||||
assert.ok(advertised, "tool must advertise an output schema");
|
||||
const ajv = new Ajv2020({ strict: false, validateFormats: false });
|
||||
assert.ok(ajv.validateSchema(advertised.inputSchema), "input schema invalid");
|
||||
assert.ok(
|
||||
ajv.validateSchema(advertised.outputSchema),
|
||||
ajv.validateSchema(canonicalOutputSchema(advertised.name)),
|
||||
"output schema invalid",
|
||||
);
|
||||
|
||||
@@ -264,7 +264,10 @@ try {
|
||||
);
|
||||
assert.notEqual(reply.isError, true, mcpTextValue(reply));
|
||||
assert.ok(
|
||||
ajv.validate(advertised.outputSchema, reply.structuredContent),
|
||||
ajv.validate(
|
||||
canonicalOutputSchema(advertised.name),
|
||||
reply.structuredContent,
|
||||
),
|
||||
JSON.stringify(ajv.errors),
|
||||
);
|
||||
const mcp = parseEvidence(requireMcpOutput(reply, "inspect_apk_signature"));
|
||||
|
||||
@@ -5,10 +5,6 @@ import { isAbsolute, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import Ajv2020 from "ajv/dist/2020.js";
|
||||
import { createCatalogIdentity } from "../dist/catalogIdentity.js";
|
||||
import {
|
||||
compactPresentationForMcpInputSchemaProfile,
|
||||
parseMcpInputSchemaProfile,
|
||||
} from "../dist/config/mcpInputSchemaProfile.js";
|
||||
import {
|
||||
parseMcpOutputSchemaProfile,
|
||||
resolveMcpOutputSchemaProfile,
|
||||
@@ -191,26 +187,18 @@ await withVerifierCleanup(async () => {
|
||||
...parsedStatus,
|
||||
result: expandMcpSessionStatus(parsedStatus.result),
|
||||
};
|
||||
const inputProfile = parseMcpInputSchemaProfile(
|
||||
environment.REA_MCP_INPUT_SCHEMA_PROFILE,
|
||||
);
|
||||
const outputProfile = parseMcpOutputSchemaProfile(
|
||||
environment.REA_MCP_OUTPUT_SCHEMA_PROFILE,
|
||||
);
|
||||
assert.ok(inputProfile.ok);
|
||||
assert.ok(outputProfile.ok);
|
||||
assert.deepEqual(
|
||||
status.result.server_identity.catalog,
|
||||
createCatalogIdentity({
|
||||
compactPresentation:
|
||||
compactPresentationForMcpInputSchemaProfile(
|
||||
inputProfile.value,
|
||||
),
|
||||
outputSchemaProfile: resolveMcpOutputSchemaProfile(
|
||||
outputProfile.value,
|
||||
),
|
||||
}),
|
||||
"binary_session must return the selected input/output profile's catalog identity",
|
||||
"binary_session must return the selected output profile's catalog identity",
|
||||
);
|
||||
assert.equal(
|
||||
Object.hasOwn(status.result.server_identity.catalog, "tools"),
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from "../dist/contracts/toolContracts.js";
|
||||
import { advertisedInputJsonSchema } from "../dist/contracts/toolSchemaMetadata.js";
|
||||
import { presentAdvertisedInputSchema } from "../dist/contracts/compactInputSchema.js";
|
||||
import { compactPresentationForMcpInputSchemaProfile } from "../dist/config/mcpInputSchemaProfile.js";
|
||||
import { COMPACT_INPUT_SCHEMA_PRESENTATION } from "../dist/config/mcpInputSchemaProfile.js";
|
||||
import { isPathWithinRoot } from "../dist/domain/localPath.js";
|
||||
import { createVerifierCommands } from "./lib/verify-commands.mjs";
|
||||
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
|
||||
@@ -37,12 +37,6 @@ const command = process.env.REA_VERIFY_COPILOT_COMMAND ?? "copilot";
|
||||
const mode = process.argv[2] ?? "call";
|
||||
const model = process.env.REA_VERIFY_COPILOT_MODEL ?? "gpt-5.4";
|
||||
const wireModel = process.env.REA_VERIFY_COPILOT_WIRE_MODEL ?? model;
|
||||
const inputSchemaProfile =
|
||||
process.env.REA_VERIFY_COPILOT_SCHEMA_PROFILE ?? "full";
|
||||
assert(
|
||||
["full", "compact"].includes(inputSchemaProfile),
|
||||
"REA_VERIFY_COPILOT_SCHEMA_PROFILE must be full or compact",
|
||||
);
|
||||
assert(
|
||||
["chat", "call"].includes(mode),
|
||||
"Usage: verify-copilot-client.mjs [chat|call]",
|
||||
@@ -176,9 +170,9 @@ await withVerifierCleanup(async () => {
|
||||
advertisedInputJsonSchema(
|
||||
toolContract(tool.name.slice(prefix.length)),
|
||||
),
|
||||
compactPresentationForMcpInputSchemaProfile(inputSchemaProfile),
|
||||
COMPACT_INPUT_SCHEMA_PRESENTATION,
|
||||
),
|
||||
`${tool.name}: Copilot must forward the selected ${inputSchemaProfile} input schema`,
|
||||
`${tool.name}: Copilot must forward the compact input schema`,
|
||||
);
|
||||
schemasChecked++;
|
||||
}
|
||||
@@ -329,15 +323,6 @@ await withVerifierCleanup(async () => {
|
||||
assert.deepEqual(JSON.parse(repeated.stdout).appliedActions, []);
|
||||
assert.equal(await readFile(configPath, "utf8"), configured);
|
||||
assert.equal(await readFile(`${configPath}.rea.backup`, "utf8"), original);
|
||||
// Bind both choices explicitly: the server's default can change independently
|
||||
// of the profile this verifier promises to exercise and records in its receipt.
|
||||
const customized = JSON.parse(configured);
|
||||
assert(customized.mcpServers.rea);
|
||||
customized.mcpServers.rea.env = {
|
||||
...customized.mcpServers.rea.env,
|
||||
REA_MCP_INPUT_SCHEMA_PROFILE: inputSchemaProfile,
|
||||
};
|
||||
await writeFile(configPath, JSON.stringify(customized, null, 2));
|
||||
const installedSkill = await readFile(
|
||||
join(skillDirectory, "SKILL.md"),
|
||||
"utf8",
|
||||
@@ -442,7 +427,6 @@ await withVerifierCleanup(async () => {
|
||||
mode,
|
||||
model,
|
||||
wireModel,
|
||||
inputSchemaProfile,
|
||||
passed,
|
||||
artifacts: lab,
|
||||
runtimeRoot,
|
||||
@@ -456,9 +440,7 @@ await withVerifierCleanup(async () => {
|
||||
skillDiscovery:
|
||||
"native COPILOT_HOME MCP configuration and caller-added isolated skill directory; default OS-home discovery unverified",
|
||||
capacityBoundary:
|
||||
inputSchemaProfile === "compact"
|
||||
? "Explicit compact profile: complete inventory and canonical server validation; advertised schemas may be reduced. This does not fix the full-profile gpt-4.1 context boundary."
|
||||
: "Full advertised input schemas, checked at the client request boundary. Client model ID and wire model are recorded separately; effective token capacity and live provider compatibility remain unknown.",
|
||||
"Compact input schemas, checked at the client request boundary: complete inventory and canonical server validation; advertised schemas may be reduced. Client model ID and wire model are recorded separately; effective token capacity and live provider compatibility remain unknown.",
|
||||
catalogSize,
|
||||
schemasChecked,
|
||||
spillReadRequested,
|
||||
|
||||
@@ -34,8 +34,6 @@ const repo = fileURLToPath(new URL("..", import.meta.url));
|
||||
const runtimeRoot = process.env.REA_VERIFY_RUNTIME_ROOT ?? repo;
|
||||
const command = process.env.REA_VERIFY_GEMINI_COMMAND ?? "gemini";
|
||||
const mode = process.argv[2] ?? "call";
|
||||
const schemaProfile = process.env.REA_VERIFY_GEMINI_SCHEMA_PROFILE ?? "full";
|
||||
assert(["full", "compact"].includes(schemaProfile));
|
||||
// npm installs of Gemini CLI provide a JavaScript entry point; on Windows only a gemini.cmd shim reaches
|
||||
// PATH, which Node does not spawn without a shell, so a selected .js entry runs under this Node. It is
|
||||
// resolved once, because the preflight runs from the caller's directory and the client from the workspace.
|
||||
@@ -99,7 +97,6 @@ await withVerifierCleanup(async () => {
|
||||
GEMINI_CLI_NO_RELAUNCH: "1",
|
||||
GEMINI_CLI_TRUST_WORKSPACE: "true",
|
||||
REA_PROCESS_RUN_ID: verifier.run_id,
|
||||
REA_MCP_INPUT_SCHEMA_PROFILE: schemaProfile,
|
||||
};
|
||||
try {
|
||||
clientVersion = (
|
||||
@@ -417,7 +414,6 @@ await withVerifierCleanup(async () => {
|
||||
client: "Gemini CLI",
|
||||
clientVersion,
|
||||
mode,
|
||||
schemaProfile,
|
||||
schemasChecked,
|
||||
examplesChecked,
|
||||
passed,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import { isUtf8 } from "node:buffer";
|
||||
import { createHash } from "node:crypto";
|
||||
@@ -82,7 +81,7 @@ try {
|
||||
transport = await createOwnedMcpStdioTransport({
|
||||
command: process.execPath,
|
||||
arguments: [entrypoint, "mcp"],
|
||||
hostEnvironment: schemaVerifierEnvironment(environment),
|
||||
hostEnvironment: environment,
|
||||
cwd: process.cwd(),
|
||||
expectedCommand: null,
|
||||
sweepTokenOwnedProcesses: true,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mjs";
|
||||
import { canonicalOutputSchema } from "./lib/canonical-output-schema.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFile, writeFile } from "node:fs/promises";
|
||||
@@ -65,7 +65,7 @@ const client = new Client({
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [entrypoint, "mcp"],
|
||||
env: schemaVerifierEnvironment(environment),
|
||||
env: environment,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const contract = toolContract("inspect_web_network_capture");
|
||||
@@ -213,11 +213,11 @@ try {
|
||||
const advertised = (await client.listTools()).tools.find(
|
||||
(tool) => tool.name === contract.name,
|
||||
);
|
||||
assert.ok(advertised?.outputSchema, "Missing named advertised schema");
|
||||
assert.ok(advertised, "Missing named advertised schema");
|
||||
const ajv = new Ajv2020({ strict: false, validateFormats: false });
|
||||
assert.ok(ajv.validateSchema(advertised.inputSchema));
|
||||
assert.ok(ajv.validateSchema(advertised.outputSchema));
|
||||
validateOutput = ajv.compile(advertised.outputSchema);
|
||||
assert.ok(ajv.validateSchema(canonicalOutputSchema(advertised.name)));
|
||||
validateOutput = ajv.compile(canonicalOutputSchema(advertised.name));
|
||||
const inputValidator = ajv.compile(advertised.inputSchema);
|
||||
assert.ok(
|
||||
inputValidator({
|
||||
|
||||
@@ -17,8 +17,8 @@ import * as prompts from "./verify-package-prompts.mjs";
|
||||
import { verifyCompleteToolCatalog } from "./lib/verify-package-core.mjs";
|
||||
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
|
||||
|
||||
const verifyMcpToolsAndPrompts = async (client, mcpOptions, environment) => {
|
||||
await verifyCompleteToolCatalog(client, mcpOptions, environment);
|
||||
const verifyMcpToolsAndPrompts = async (client, mcpOptions) => {
|
||||
await verifyCompleteToolCatalog(client, mcpOptions);
|
||||
await prompts.verifyPromptCatalog(client, mcpOptions, prompts.names);
|
||||
await prompts.verifyPromptCompletion(client, mcpOptions, []);
|
||||
};
|
||||
@@ -283,7 +283,7 @@ export async function verifyPackageMcp({
|
||||
try {
|
||||
await client.connect(transport);
|
||||
const mcpOptions = { timeout: 15_000 };
|
||||
await verifyMcpToolsAndPrompts(client, mcpOptions, environment);
|
||||
await verifyMcpToolsAndPrompts(client, mcpOptions);
|
||||
await verifyMcpTargetFree(client, mcpOptions);
|
||||
await verifyMcpUnknownProvider(client, mcpOptions);
|
||||
await verifyMcpBinaryLifecycle(client, mcpOptions);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { importVerifierSource } from "./lib/verifier-source-loader.mjs";
|
||||
|
||||
import { access, copyFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
||||
import { FULL_CATALOG_IDENTITY } from "../dist/catalogIdentity.js";
|
||||
import { DEFAULT_CATALOG_IDENTITY } from "../dist/catalogIdentity.js";
|
||||
|
||||
import { parseTarListing } from "./lib/verify-package-listing.mjs";
|
||||
import { verifyPackedBridge } from "./verify-packed-bridge.mjs";
|
||||
@@ -213,7 +213,8 @@ export async function verifyPackagePack({ root, workspace, commands }) {
|
||||
const skillMetadata = parseSkillManifestMetadata(skill);
|
||||
if (
|
||||
!skillMetadata.ok ||
|
||||
skillMetadata.value.toolCount !== FULL_CATALOG_IDENTITY.counts.mcp_tools ||
|
||||
skillMetadata.value.toolCount !==
|
||||
DEFAULT_CATALOG_IDENTITY.counts.mcp_tools ||
|
||||
skillMetadata.value.hasCatalogDigest
|
||||
)
|
||||
throw new Error(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mjs";
|
||||
import { canonicalOutputSchema } from "./lib/canonical-output-schema.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
requireMcpEvidenceBundle,
|
||||
@@ -46,7 +46,7 @@ const client = new Client({ name: "packet-capture-verifier", version: "1" });
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [entrypoint, "mcp"],
|
||||
env: schemaVerifierEnvironment(environment),
|
||||
env: environment,
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
@@ -120,17 +120,20 @@ await withVerifierCleanup(async () => {
|
||||
const tool = (await client.listTools()).tools.find(
|
||||
({ name }) => name === "inspect_packet_capture",
|
||||
);
|
||||
assert.ok(tool?.outputSchema);
|
||||
assert.ok(tool);
|
||||
const ajv = new Ajv2020({ strict: false, validateFormats: false });
|
||||
assert.ok(ajv.validateSchema(tool.inputSchema));
|
||||
assert.ok(ajv.validateSchema(tool.outputSchema));
|
||||
assert.ok(ajv.validateSchema(canonicalOutputSchema(tool.name)));
|
||||
const inspect = async (input) => {
|
||||
const response = await client.callTool({
|
||||
name: tool.name,
|
||||
arguments: input,
|
||||
});
|
||||
assert.ok(
|
||||
ajv.validate(tool.outputSchema, response.structuredContent),
|
||||
ajv.validate(
|
||||
canonicalOutputSchema(tool.name),
|
||||
response.structuredContent,
|
||||
),
|
||||
JSON.stringify(ajv.errors),
|
||||
);
|
||||
const evidence = parseEvidence(
|
||||
@@ -276,10 +279,10 @@ await withVerifierCleanup(async () => {
|
||||
const pcapTool = (await client.listTools()).tools.find(
|
||||
(t) => t.name === "inspect_pcap_capture",
|
||||
);
|
||||
assert.ok(pcapTool?.outputSchema);
|
||||
assert.ok(pcapTool);
|
||||
const pcapAjv = new Ajv2020({ strict: false, validateFormats: false });
|
||||
assert.ok(pcapAjv.validateSchema(pcapTool.inputSchema));
|
||||
assert.ok(pcapAjv.validateSchema(pcapTool.outputSchema));
|
||||
assert.ok(pcapAjv.validateSchema(canonicalOutputSchema(pcapTool.name)));
|
||||
for (const capturePath of [path, bluetooth]) {
|
||||
const capinfosOutput = (
|
||||
await execute(
|
||||
@@ -316,7 +319,10 @@ await withVerifierCleanup(async () => {
|
||||
arguments: { path: capturePath, detail: "summary" },
|
||||
});
|
||||
assert.ok(
|
||||
pcapAjv.validate(pcapTool.outputSchema, response.structuredContent),
|
||||
pcapAjv.validate(
|
||||
canonicalOutputSchema(pcapTool.name),
|
||||
response.structuredContent,
|
||||
),
|
||||
pcapAjv.errorsText(),
|
||||
);
|
||||
const summary = parseEvidence(
|
||||
@@ -393,9 +399,9 @@ await withVerifierCleanup(async () => {
|
||||
const discovery = (await client.listTools()).tools.find(
|
||||
({ name }) => name === "inspect_packet_fields",
|
||||
);
|
||||
assert.ok(discovery?.outputSchema);
|
||||
assert.ok(discovery);
|
||||
assert.ok(ajv.validateSchema(discovery.inputSchema));
|
||||
assert.ok(ajv.validateSchema(discovery.outputSchema));
|
||||
assert.ok(ajv.validateSchema(canonicalOutputSchema(discovery.name)));
|
||||
const nativeProfile = join(root.path, "native-profile");
|
||||
await mkdir(nativeProfile);
|
||||
const native = (
|
||||
@@ -422,7 +428,10 @@ await withVerifierCleanup(async () => {
|
||||
arguments: input,
|
||||
});
|
||||
assert.ok(
|
||||
ajv.validate(discovery.outputSchema, response.structuredContent),
|
||||
ajv.validate(
|
||||
canonicalOutputSchema(discovery.name),
|
||||
response.structuredContent,
|
||||
),
|
||||
JSON.stringify(ajv.errors),
|
||||
);
|
||||
const evidence = parseEvidence(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mjs";
|
||||
import { canonicalOutputSchema } from "./lib/canonical-output-schema.mjs";
|
||||
import {
|
||||
closeVerifierResources,
|
||||
withVerifierCleanup,
|
||||
@@ -41,7 +41,7 @@ const environment = {
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [entrypoint, "mcp"],
|
||||
env: schemaVerifierEnvironment(environment),
|
||||
env: environment,
|
||||
stderr: "pipe",
|
||||
});
|
||||
let diagnostics = "";
|
||||
@@ -60,10 +60,10 @@ const report = await withVerifierCleanup(async () => {
|
||||
const tool = (await client.listTools()).tools.find(
|
||||
(tool) => tool.name === "inspect_web_network_capture",
|
||||
);
|
||||
assert.ok(tool?.outputSchema);
|
||||
for (const schema of [tool.inputSchema, tool.outputSchema])
|
||||
assert.ok(tool);
|
||||
for (const schema of [tool.inputSchema, canonicalOutputSchema(tool.name)])
|
||||
assert.ok(ajv.validateSchema(schema));
|
||||
const validateOutput = ajv.compile(tool.outputSchema);
|
||||
const validateOutput = ajv.compile(canonicalOutputSchema(tool.name));
|
||||
for (const [lane, path] of [
|
||||
["control", controlPath],
|
||||
["loss", lossPath],
|
||||
|
||||
@@ -51,7 +51,6 @@ import { verifyScenarioStorage } from "./lib/browser-scenario-storage-e2e.mjs";
|
||||
import { verifyScenarioStorageFingerprints } from "./lib/browser-scenario-storage-fingerprints-e2e.mjs";
|
||||
import { verifyScenarioServiceWorkers } from "./lib/browser-scenario-service-workers-e2e.mjs";
|
||||
import { verifyBrowserUrlUserInfo } from "./lib/browser-url-userinfo-e2e.mjs";
|
||||
import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mjs";
|
||||
|
||||
const SCENARIO_SECRET_VALUE = "rea-browser-verifier-secret";
|
||||
const SCENARIO_URL_SECRET_VALUE = "rea-browser-url-verifier-secret";
|
||||
@@ -448,10 +447,10 @@ async function verifyPublicBundle(endpoint, target, origin) {
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [fileURLToPath(new URL("./rea.mjs", import.meta.url)), "mcp"],
|
||||
env: schemaVerifierEnvironment({
|
||||
env: {
|
||||
PATH: process.env.PATH ?? "",
|
||||
REA_LOG_LEVEL: "silent",
|
||||
}),
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
const client = new Client({ name: "browser-bundle-e2e", version: "1" });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mjs";
|
||||
import { canonicalOutputSchema } from "./lib/canonical-output-schema.mjs";
|
||||
import { closeVerifierResources } from "./lib/verifier-cleanup.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
@@ -92,7 +92,7 @@ const env = {
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [entrypoint, "mcp"],
|
||||
env: schemaVerifierEnvironment(env),
|
||||
env: env,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const client = new Client({ name: "rea-real-com-proof", version: "1" });
|
||||
@@ -215,11 +215,17 @@ await withVerifierFixture(workspaceFixture, async () => {
|
||||
const advertised = tools.find(
|
||||
(tool) => tool.name === "inspect_native_instruction",
|
||||
);
|
||||
assert.ok(advertised?.outputSchema);
|
||||
assert.ok(advertised);
|
||||
const ajv = new Ajv2020({ strict: false, validateFormats: false });
|
||||
assert.equal(ajv.validateSchema(advertised.outputSchema), true);
|
||||
assert.equal(
|
||||
ajv.validate(advertised.outputSchema, unlistedResponse.structuredContent),
|
||||
ajv.validateSchema(canonicalOutputSchema(advertised.name)),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
ajv.validate(
|
||||
canonicalOutputSchema(advertised.name),
|
||||
unlistedResponse.structuredContent,
|
||||
),
|
||||
true,
|
||||
JSON.stringify(ajv.errors),
|
||||
);
|
||||
@@ -234,7 +240,7 @@ await withVerifierFixture(workspaceFixture, async () => {
|
||||
const contextDefinition = tools.find(
|
||||
(tool) => tool.name === "inspect_address_context",
|
||||
);
|
||||
assert.ok(contextDefinition?.outputSchema);
|
||||
assert.ok(contextDefinition);
|
||||
const documents = await call("list_documents");
|
||||
assert.equal(documents.length, 1);
|
||||
const document = documents[0];
|
||||
@@ -314,7 +320,7 @@ await withVerifierFixture(workspaceFixture, async () => {
|
||||
// Ghidra, rather than assuming the provider exposes direct comment methods.
|
||||
const contextCall = async (name, args) => {
|
||||
const definition = tools.find((tool) => tool.name === name);
|
||||
assert.ok(definition?.outputSchema, `${name} must be advertised`);
|
||||
assert.ok(definition, `${name} must be advertised`);
|
||||
const response = await client.callTool(
|
||||
{ name, arguments: args },
|
||||
{ timeout: 240000, toolDefinition: definition },
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mjs";
|
||||
import { withCanonicalOutputSchema } from "./lib/canonical-output-schema.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import { execFile } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
@@ -97,7 +97,7 @@ const env = {
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [entrypoint, "mcp"],
|
||||
env: schemaVerifierEnvironment(env),
|
||||
env: env,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const client = new Client({ name: "rea-real-raw-proof", version: "1" });
|
||||
@@ -135,10 +135,11 @@ await withVerifierFixture(workspaceFixture, async () => {
|
||||
try {
|
||||
await client.connect(transport);
|
||||
connected = true;
|
||||
searchDefinition = (await client.listTools()).tools.find(
|
||||
const listedSearch = (await client.listTools()).tools.find(
|
||||
(tool) => tool.name === "search_bytes",
|
||||
);
|
||||
assert.ok(searchDefinition?.outputSchema);
|
||||
assert.ok(listedSearch);
|
||||
searchDefinition = withCanonicalOutputSchema(listedSearch);
|
||||
const profiles = [];
|
||||
const sessions = [];
|
||||
for (const entryPoint of ["0x08000000", "0x8000000", "0x09000000"]) {
|
||||
@@ -734,7 +735,7 @@ async function verifyCrossBlockProject(target, targetSha256) {
|
||||
command: process.execPath,
|
||||
args: [entrypoint, "mcp"],
|
||||
env: {
|
||||
...schemaVerifierEnvironment(env),
|
||||
...env,
|
||||
REA_GHIDRA_PROJECT_PATH: join(projectDirectory, "Boundary.gpr"),
|
||||
},
|
||||
stderr: "pipe",
|
||||
@@ -748,7 +749,7 @@ async function verifyCrossBlockProject(target, targetSha256) {
|
||||
const definition = (await projectClient.listTools()).tools.find(
|
||||
(tool) => tool.name === "search_bytes",
|
||||
);
|
||||
assert.ok(definition?.outputSchema);
|
||||
assert.ok(definition);
|
||||
const projectCall = async (name, args) => {
|
||||
const reply = await projectClient.callTool(
|
||||
{ name, arguments: args },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mjs";
|
||||
import { canonicalOutputSchema } from "./lib/canonical-output-schema.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import { execFile } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
@@ -90,7 +90,7 @@ for (const key of ["GHIDRA_HEADLESS_MAXMEM", "GHIDRA_MAXMEM"])
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [entrypoint, "mcp"],
|
||||
env: schemaVerifierEnvironment(env),
|
||||
env: env,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const client = new Client({ name: "rea-real-riscv-proof", version: "1" });
|
||||
@@ -132,14 +132,17 @@ try {
|
||||
const validators = new Map();
|
||||
for (const tool of catalog.tools) {
|
||||
assert.ok(
|
||||
ajv.validateSchema(tool.outputSchema),
|
||||
ajv.validateSchema(canonicalOutputSchema(tool.name)),
|
||||
`${tool.name}: invalid output schema`,
|
||||
);
|
||||
const existing =
|
||||
typeof tool.outputSchema.$id === "string"
|
||||
? ajv.getSchema(tool.outputSchema.$id)
|
||||
typeof canonicalOutputSchema(tool.name).$id === "string"
|
||||
? ajv.getSchema(canonicalOutputSchema(tool.name).$id)
|
||||
: undefined;
|
||||
validators.set(tool.name, existing ?? ajv.compile(tool.outputSchema));
|
||||
validators.set(
|
||||
tool.name,
|
||||
existing ?? ajv.compile(canonicalOutputSchema(tool.name)),
|
||||
);
|
||||
}
|
||||
|
||||
const typedSoft = await buildRiscvFixture(workspace, 32, "soft");
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mjs";
|
||||
// Run from the repository root after `npm run build:cached` on macOS arm64,
|
||||
// with Hopper and the Xcode command-line tools installed. No target is executed.
|
||||
import assert from "node:assert/strict";
|
||||
@@ -68,12 +67,12 @@ const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: ["dist/main.js"],
|
||||
cwd: root,
|
||||
env: schemaVerifierEnvironment({
|
||||
env: {
|
||||
...process.env,
|
||||
REA_ANALYSIS_PROVIDER: "hopper",
|
||||
REA_LOG_LEVEL: "silent",
|
||||
REA_HOPPER_CALLER_SCAN_BUDGET_MS: "5000",
|
||||
}),
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
let stderr = "";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mjs";
|
||||
import { canonicalOutputSchema } from "./lib/canonical-output-schema.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import { createHash } from "node:crypto";
|
||||
import { execFile } from "node:child_process";
|
||||
@@ -82,11 +82,11 @@ const client = new Client({ name: "hopper-fat-verifier", version: "1" });
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: ["dist/main.js"],
|
||||
env: schemaVerifierEnvironment({
|
||||
env: {
|
||||
...process.env,
|
||||
REA_ANALYSIS_PROVIDER: "hopper",
|
||||
HOPPER_LOADER_ARGS_JSON: "[]",
|
||||
}),
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
transport.stderr?.on("data", (chunk) => process.stderr.write(chunk));
|
||||
@@ -259,10 +259,11 @@ await withVerifierFixture(workspace, async () => {
|
||||
{ timeout: 180_000 },
|
||||
);
|
||||
const result = requireMcpOperationResult(reply, name);
|
||||
const schema = catalog.tools.find(
|
||||
(tool) => tool.name === name,
|
||||
)?.outputSchema;
|
||||
assert.ok(schema, `${name} omitted its output schema`);
|
||||
assert.ok(
|
||||
catalog.tools.some((tool) => tool.name === name),
|
||||
`${name} is not advertised`,
|
||||
);
|
||||
const schema = canonicalOutputSchema(name);
|
||||
assert.equal(
|
||||
ajv.validate(schema, reply.structuredContent),
|
||||
true,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mjs";
|
||||
import { execFile } from "node:child_process";
|
||||
import { tmpdir } from "node:os";
|
||||
import { promisify } from "node:util";
|
||||
@@ -234,7 +233,7 @@ const targetB = fixtureTargets.secondary.path;
|
||||
const largeTarget = fixtureTargets.large.path;
|
||||
const targetHashA = fixtureTargets.primary.sha256;
|
||||
const targetHashB = fixtureTargets.secondary.sha256;
|
||||
const serverEnvironment = schemaVerifierEnvironment(process.env);
|
||||
const serverEnvironment = { ...process.env };
|
||||
serverEnvironment.REA_ANALYSIS_PROVIDER = "auto";
|
||||
delete serverEnvironment.HOPPER_TARGET_PATH;
|
||||
delete serverEnvironment.HOPPER_SECOND_TARGET_PATH;
|
||||
|
||||
@@ -96,13 +96,9 @@ try {
|
||||
advertised.filter((tool) => tool.name === "inspect_recorded_crash").length,
|
||||
1,
|
||||
);
|
||||
const catalog = await verifyCompleteToolCatalog(
|
||||
basic.client,
|
||||
{
|
||||
timeout: 90_000,
|
||||
},
|
||||
environment,
|
||||
);
|
||||
const catalog = await verifyCompleteToolCatalog(basic.client, {
|
||||
timeout: 90_000,
|
||||
});
|
||||
for (const name of [
|
||||
"inspect_binary_layout",
|
||||
"inspect_recorded_crash",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mjs";
|
||||
import { canonicalOutputSchema } from "./lib/canonical-output-schema.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
requireMcpEvidenceBundle,
|
||||
@@ -80,7 +80,7 @@ const assertRejectedSelector = (
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [entrypoint, "mcp"],
|
||||
env: schemaVerifierEnvironment(environment),
|
||||
env: environment,
|
||||
stderr: "pipe",
|
||||
});
|
||||
// The MCP server child reads files under the root until it has exited.
|
||||
@@ -119,10 +119,10 @@ await withVerifierFixture(root, async () => {
|
||||
const advertised = (await client.listTools()).tools.find(
|
||||
({ name }) => name === "decode_signal_recording",
|
||||
);
|
||||
assert.ok(advertised?.outputSchema);
|
||||
assert.ok(advertised);
|
||||
const ajv = new Ajv2020({ strict: false, validateFormats: false });
|
||||
assert.ok(ajv.validateSchema(advertised.inputSchema));
|
||||
assert.ok(ajv.validateSchema(advertised.outputSchema));
|
||||
assert.ok(ajv.validateSchema(canonicalOutputSchema(advertised.name)));
|
||||
for (const extra of [
|
||||
{ input_format: "vcd", annotations: "uart=rx-data" },
|
||||
{},
|
||||
@@ -133,7 +133,10 @@ await withVerifierFixture(root, async () => {
|
||||
arguments: input,
|
||||
});
|
||||
assert.ok(
|
||||
ajv.validate(advertised.outputSchema, response.structuredContent),
|
||||
ajv.validate(
|
||||
canonicalOutputSchema(advertised.name),
|
||||
response.structuredContent,
|
||||
),
|
||||
JSON.stringify(ajv.errors),
|
||||
);
|
||||
const evidence = parseEvidence(
|
||||
@@ -249,7 +252,10 @@ await withVerifierFixture(root, async () => {
|
||||
arguments: input,
|
||||
});
|
||||
assert.ok(
|
||||
ajv.validate(advertised.outputSchema, response.structuredContent),
|
||||
ajv.validate(
|
||||
canonicalOutputSchema(advertised.name),
|
||||
response.structuredContent,
|
||||
),
|
||||
JSON.stringify(ajv.errors),
|
||||
);
|
||||
const evidence = parseEvidence(
|
||||
@@ -325,9 +331,9 @@ await withVerifierFixture(root, async () => {
|
||||
const discoveryTool = (await client.listTools()).tools.find(
|
||||
({ name }) => name === "inspect_signal_decoders",
|
||||
);
|
||||
assert.ok(discoveryTool?.outputSchema);
|
||||
assert.ok(discoveryTool);
|
||||
assert.ok(ajv.validateSchema(discoveryTool.inputSchema));
|
||||
assert.ok(ajv.validateSchema(discoveryTool.outputSchema));
|
||||
assert.ok(ajv.validateSchema(canonicalOutputSchema(discoveryTool.name)));
|
||||
const nativeHelp = (
|
||||
await execute(
|
||||
process.env.REA_SIGROK_COMMAND,
|
||||
@@ -379,7 +385,10 @@ await withVerifierFixture(root, async () => {
|
||||
arguments: input,
|
||||
});
|
||||
assert.ok(
|
||||
ajv.validate(discoveryTool.outputSchema, response.structuredContent),
|
||||
ajv.validate(
|
||||
canonicalOutputSchema(discoveryTool.name),
|
||||
response.structuredContent,
|
||||
),
|
||||
JSON.stringify(ajv.errors),
|
||||
);
|
||||
const evidence = parseEvidence(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mjs";
|
||||
import { canonicalOutputSchema } from "./lib/canonical-output-schema.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import { execFile } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
@@ -90,12 +90,12 @@ const callTool = (request) =>
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [entrypoint, "mcp"],
|
||||
env: schemaVerifierEnvironment({
|
||||
env: {
|
||||
...environment,
|
||||
TMPDIR: childTempRoot,
|
||||
TMP: childTempRoot,
|
||||
TEMP: childTempRoot,
|
||||
}),
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
let cases = 0;
|
||||
@@ -110,10 +110,10 @@ try {
|
||||
const advertised = (await client.listTools()).tools.find(
|
||||
({ name }) => name === "inspect_wasm_artifact",
|
||||
);
|
||||
assert.ok(advertised?.outputSchema, "WASM tool must advertise output schema");
|
||||
assert.ok(advertised, "WASM tool must advertise output schema");
|
||||
const ajv = new Ajv2020({ strict: false, validateFormats: false });
|
||||
assert.ok(ajv.validateSchema(advertised.inputSchema));
|
||||
assert.ok(ajv.validateSchema(advertised.outputSchema));
|
||||
assert.ok(ajv.validateSchema(canonicalOutputSchema(advertised.name)));
|
||||
const inspectCli = async (input) => {
|
||||
try {
|
||||
return JSON.parse(
|
||||
@@ -280,7 +280,10 @@ try {
|
||||
});
|
||||
assert.notEqual(reply.isError, true, mcpTextValue(reply));
|
||||
assert.ok(
|
||||
ajv.validate(advertised.outputSchema, reply.structuredContent),
|
||||
ajv.validate(
|
||||
canonicalOutputSchema(advertised.name),
|
||||
reply.structuredContent,
|
||||
),
|
||||
JSON.stringify(ajv.errors),
|
||||
);
|
||||
const mcp = parseEvidence(requireMcpOutput(reply, "inspect_wasm_artifact"));
|
||||
@@ -521,7 +524,10 @@ try {
|
||||
});
|
||||
assert.notEqual(reply.isError, true, mcpTextValue(reply));
|
||||
assert.ok(
|
||||
ajv.validate(advertised.outputSchema, reply.structuredContent),
|
||||
ajv.validate(
|
||||
canonicalOutputSchema(advertised.name),
|
||||
reply.structuredContent,
|
||||
),
|
||||
JSON.stringify(ajv.errors),
|
||||
);
|
||||
const mcp = parseEvidence(requireMcpOutput(reply, "inspect_wasm_artifact"));
|
||||
@@ -587,7 +593,10 @@ try {
|
||||
});
|
||||
assert.notEqual(reply.isError, true, mcpTextValue(reply));
|
||||
assert.ok(
|
||||
ajv.validate(advertised.outputSchema, reply.structuredContent),
|
||||
ajv.validate(
|
||||
canonicalOutputSchema(advertised.name),
|
||||
reply.structuredContent,
|
||||
),
|
||||
JSON.stringify(ajv.errors),
|
||||
);
|
||||
const mcp = parseEvidence(requireMcpOutput(reply, "inspect_wasm_artifact"));
|
||||
|
||||
@@ -105,9 +105,7 @@ try {
|
||||
let toolCount = 0;
|
||||
await withVerifierCleanup(async () => {
|
||||
await client.connect(server);
|
||||
toolCount = (
|
||||
await verifyCompleteToolCatalog(client, undefined, environment)
|
||||
).length;
|
||||
toolCount = (await verifyCompleteToolCatalog(client)).length;
|
||||
}, [
|
||||
async () => {
|
||||
try {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -5,10 +5,7 @@ import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
import { parseEvidence } from "../../../dist/domain/evidence.js";
|
||||
import { artifactMcpCall } from "../../lib/artifact-e2e.mjs";
|
||||
import { withVerifierCleanup } from "../../lib/verifier-cleanup.mjs";
|
||||
import {
|
||||
INLINE_EVIDENCE_MCP_ENVIRONMENT,
|
||||
schemaVerifierEnvironment,
|
||||
} from "../../lib/mcp-verification-environment.mjs";
|
||||
import { INLINE_EVIDENCE_MCP_ENVIRONMENT } from "../../lib/mcp-verification-environment.mjs";
|
||||
import {
|
||||
requireMcpOutput,
|
||||
requireMcpToolError,
|
||||
@@ -107,10 +104,10 @@ const verifyApk = async (options, path, dexEntry) => {
|
||||
command: process.execPath,
|
||||
args: [options.entrypoint, "mcp"],
|
||||
cwd: options.repository,
|
||||
env: schemaVerifierEnvironment({
|
||||
env: {
|
||||
...options.environment,
|
||||
...INLINE_EVIDENCE_MCP_ENVIRONMENT,
|
||||
}),
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
const client = new Client({
|
||||
@@ -256,10 +253,10 @@ const verifySiteBudget = async (options, path, control) => {
|
||||
command: process.execPath,
|
||||
args: [options.entrypoint, "mcp"],
|
||||
cwd: options.repository,
|
||||
env: schemaVerifierEnvironment({
|
||||
env: {
|
||||
...options.environment,
|
||||
...INLINE_EVIDENCE_MCP_ENVIRONMENT,
|
||||
}),
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
const client = new Client({ name: "rea-external-site-budget", version: "1" });
|
||||
|
||||
@@ -155,6 +155,10 @@
|
||||
"src/pcap/PcapDnsWire.ts": 5,
|
||||
"src/v8/V8CodeCacheParser.ts": 1
|
||||
},
|
||||
"raw-json-schema-meta": {
|
||||
"src/cli/jsonInputArgument.ts": 2,
|
||||
"src/cli/utilityCommands.ts": 1
|
||||
},
|
||||
"range-error-interpretation": {
|
||||
"src/application/JsonInputFile.ts": 1,
|
||||
"src/artifacts/nodeSea/NodeSeaBlob.ts": 1,
|
||||
|
||||
@@ -535,6 +535,20 @@ checkCounted(
|
||||
[],
|
||||
);
|
||||
|
||||
// Raw JSON Schema keywords and `x-rea-*` markers in `.meta()` bypass the
|
||||
// canonical Zod checks and reach every JSON Schema projection, including the
|
||||
// advertised one. Presentation facts are typed annotations
|
||||
// (src/domain/schemaAnnotations); constraints belong in Zod checks, or in a
|
||||
// src/domain/schemaConstraints helper that adds the check and its keyword
|
||||
// together.
|
||||
checkCounted(
|
||||
"raw JSON Schema keyword or x-rea marker in Zod .meta() (use a typed annotation from src/domain/schemaAnnotations, a Zod check, or a src/domain/schemaConstraints helper)",
|
||||
"raw-json-schema-meta",
|
||||
files,
|
||||
/\.meta\(\s*\{\s*"?(?:x-rea-[\w-]+|\$ref|not|allOf|anyOf|oneOf|if|then|else|dependentRequired|dependentSchemas|dependencies|uniqueItems|(?:min|max)(?:Length|Items|Properties|imum)|exclusive(?:Min|Max)imum|multipleOf|type|const|enum|format|pattern|patternProperties|properties|required|additionalProperties|items|prefixItems|contains|content(?:MediaType|Encoding|Schema)|propertyNames)"?\s*:/gu,
|
||||
["src/domain/schemaConstraints.ts"],
|
||||
);
|
||||
|
||||
// A RangeError can mean a stack overflow, a host string or buffer limit, a
|
||||
// raw read or write past a buffer, or whatever its thrower intended. Only
|
||||
// known host limits are classified; parsers throw a ParseFailureError.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { canonicalOutputSchema } from "../../lib/canonical-output-schema.mjs";
|
||||
import { withVerifierCleanup } from "../../lib/verifier-cleanup.mjs";
|
||||
import {
|
||||
VerifierFixture,
|
||||
@@ -5,7 +6,6 @@ import {
|
||||
withVerifierFixture,
|
||||
} from "../../lib/verifier-fixture.mjs";
|
||||
import { requireMcpToolError } from "../../lib/mcp-verifier-results.mjs";
|
||||
import { schemaVerifierEnvironment } from "../../lib/mcp-verification-environment.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import { execFile, spawn } from "node:child_process";
|
||||
import { randomUUID } from "node:crypto";
|
||||
@@ -83,7 +83,7 @@ bridge.exposeInMainWorld("fixture", {
|
||||
command: process.execPath,
|
||||
args: [entrypoint, "mcp"],
|
||||
// Results are validated against the listed complete output contracts.
|
||||
env: schemaVerifierEnvironment({
|
||||
env: {
|
||||
...Object.fromEntries(
|
||||
["DISPLAY", "XAUTHORITY", "WAYLAND_DISPLAY"].flatMap((name) =>
|
||||
process.env[name] === undefined ? [] : [[name, process.env[name]]],
|
||||
@@ -92,7 +92,7 @@ bridge.exposeInMainWorld("fixture", {
|
||||
PATH: process.env.PATH,
|
||||
REA_LOG_LEVEL: "silent",
|
||||
HOPPER_LAUNCHER_PATH: "/rea-unconfigured/hopper",
|
||||
}),
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
const client = new Client({ name: "electron-real-e2e", version: "1" });
|
||||
@@ -146,10 +146,10 @@ bridge.exposeInMainWorld("fixture", {
|
||||
ajv.compile(tool.inputSchema);
|
||||
return [
|
||||
tool.name,
|
||||
tool.outputSchema
|
||||
? (tool.outputSchema.$id &&
|
||||
ajv.getSchema(tool.outputSchema.$id)) ||
|
||||
ajv.compile(tool.outputSchema)
|
||||
canonicalOutputSchema(tool.name)
|
||||
? (canonicalOutputSchema(tool.name).$id &&
|
||||
ajv.getSchema(canonicalOutputSchema(tool.name).$id)) ||
|
||||
ajv.compile(canonicalOutputSchema(tool.name))
|
||||
: undefined,
|
||||
];
|
||||
}),
|
||||
|
||||
@@ -9,7 +9,7 @@ import { sqliteDatabaseCapability } from "./sqlite/SqliteDatabaseCapability.js";
|
||||
import { CURRENT_ENTRYPOINT_PATH } from "../process/CurrentEntrypointPath.js";
|
||||
import { catalogIdentityForToolFamilies } from "../catalogIdentity.js";
|
||||
import { progressiveCatalogIdentity } from "../catalogDiscovery.js";
|
||||
import { compactPresentationForMcpInputSchemaProfile } from "../config/mcpInputSchemaProfile.js";
|
||||
import { COMPACT_INPUT_SCHEMA_PRESENTATION } from "../config/mcpInputSchemaProfile.js";
|
||||
import { resolveMcpOutputSchemaProfile } from "../config/mcpOutputSchemaProfile.js";
|
||||
import {
|
||||
directAnalysisCleanupFailure,
|
||||
@@ -34,9 +34,7 @@ export const runSessionStatus = async (
|
||||
async () => {
|
||||
const status = jsonObjectSchema.parse(await session.status());
|
||||
const catalog = catalogIdentityForToolFamilies(
|
||||
compactPresentationForMcpInputSchemaProfile(
|
||||
config.value.mcpInputSchemaProfile,
|
||||
),
|
||||
COMPACT_INPUT_SCHEMA_PRESENTATION,
|
||||
resolveMcpOutputSchemaProfile(config.value.mcpOutputSchemaProfile),
|
||||
config.value.mcpToolFamilies === undefined
|
||||
? undefined
|
||||
|
||||
+55
-92
@@ -19,21 +19,13 @@ import {
|
||||
presentAdvertisedInput,
|
||||
type CompactInputSchemaPresentation,
|
||||
} from "./contracts/compactInputSchema.js";
|
||||
import { presentAdvertisedOutputSchema } from "./contracts/compactOutputSchema.js";
|
||||
import {
|
||||
presentAdvertisedOutputSchema,
|
||||
type CompactOutputSchemaPresentation,
|
||||
} from "./contracts/compactOutputSchema.js";
|
||||
import {
|
||||
COMPACT_OUTPUT_SCHEMA_BUDGET_BYTES,
|
||||
compactPresentationForMcpOutputSchemaProfile,
|
||||
COMPACT_OUTPUT_SCHEMA_PRESENTATION,
|
||||
resolveMcpOutputSchemaProfile,
|
||||
type McpOutputSchemaProfile,
|
||||
} from "./config/mcpOutputSchemaProfile.js";
|
||||
import {
|
||||
COMPACT_INPUT_SCHEMA_BUDGET_BYTES,
|
||||
compactPresentationForMcpInputSchemaProfile,
|
||||
type McpInputSchemaProfile,
|
||||
} from "./config/mcpInputSchemaProfile.js";
|
||||
import { COMPACT_INPUT_SCHEMA_PRESENTATION } from "./config/mcpInputSchemaProfile.js";
|
||||
import { RECORDED_CATALOG_DIGEST_PROFILES } from "./recordedCatalogDigests.js";
|
||||
|
||||
/**
|
||||
@@ -68,27 +60,29 @@ const presentedInput = (
|
||||
};
|
||||
};
|
||||
|
||||
/** Advertised input for every tool; output stays canonical unless bounded. */
|
||||
const presentToolCatalog = (
|
||||
catalog: ReturnType<typeof createToolCatalog>,
|
||||
presentation: CompactInputSchemaPresentation | undefined,
|
||||
outputPresentation: CompactOutputSchemaPresentation | undefined = undefined,
|
||||
presentation: CompactInputSchemaPresentation,
|
||||
) =>
|
||||
presentation === undefined && outputPresentation === undefined
|
||||
? catalog
|
||||
: catalog.map((tool) => ({
|
||||
catalog.map((tool) => ({
|
||||
...tool,
|
||||
...presentedInput(tool.description, tool.input_schema, presentation),
|
||||
}));
|
||||
|
||||
const presentOutputSchemas = (
|
||||
catalog: ReturnType<typeof presentToolCatalog>,
|
||||
outputSchemaProfile: McpOutputSchemaProfile,
|
||||
) =>
|
||||
outputSchemaProfile === "compact"
|
||||
? catalog.map((tool) => ({
|
||||
...tool,
|
||||
...(presentation === undefined
|
||||
? {}
|
||||
: presentedInput(tool.description, tool.input_schema, presentation)),
|
||||
...(outputPresentation === undefined
|
||||
? {}
|
||||
: {
|
||||
output_schema: presentAdvertisedOutputSchema(
|
||||
tool.output_schema,
|
||||
outputPresentation,
|
||||
),
|
||||
}),
|
||||
}));
|
||||
output_schema: presentAdvertisedOutputSchema(
|
||||
tool.output_schema,
|
||||
COMPACT_OUTPUT_SCHEMA_PRESENTATION,
|
||||
),
|
||||
}))
|
||||
: catalog;
|
||||
|
||||
const createPromptCatalog = (contracts: readonly PromptContract[]) =>
|
||||
contracts
|
||||
@@ -117,13 +111,13 @@ const digest = (value: unknown): string => {
|
||||
};
|
||||
|
||||
const digestCatalogs = (
|
||||
catalog: ReturnType<typeof createToolCatalog>,
|
||||
catalog: ReturnType<typeof presentToolCatalog>,
|
||||
prompts: ReturnType<typeof createPromptCatalog>,
|
||||
outputSchemaProfile: McpOutputSchemaProfile,
|
||||
) => {
|
||||
const tools = {
|
||||
output_schema_profile: outputSchemaProfile,
|
||||
catalog,
|
||||
catalog: presentOutputSchemas(catalog, outputSchemaProfile),
|
||||
};
|
||||
return {
|
||||
tools_sha256: digest(tools),
|
||||
@@ -139,19 +133,20 @@ const digestCatalogs = (
|
||||
/**
|
||||
* Catalog digests over the given tool contracts. Exported so tests can prove
|
||||
* which contract fields the identity covers; production uses
|
||||
* {@link FULL_CATALOG_IDENTITY}.
|
||||
* {@link DEFAULT_CATALOG_IDENTITY}.
|
||||
*/
|
||||
export const createCatalogDigests = (
|
||||
contracts: readonly ToolContract[] = TOOL_CONTRACTS,
|
||||
promptContracts: readonly PromptContract[] = PROMPT_CONTRACTS,
|
||||
compactPresentation: CompactInputSchemaPresentation | undefined = undefined,
|
||||
outputSchemaProfile: McpOutputSchemaProfile = "full",
|
||||
compactPresentation: CompactInputSchemaPresentation = COMPACT_INPUT_SCHEMA_PRESENTATION,
|
||||
outputSchemaProfile: McpOutputSchemaProfile = resolveMcpOutputSchemaProfile(
|
||||
undefined,
|
||||
),
|
||||
) =>
|
||||
digestCatalogs(
|
||||
presentToolCatalog(
|
||||
createToolCatalog(sortByName(contracts)),
|
||||
compactPresentation,
|
||||
compactPresentationForMcpOutputSchemaProfile(outputSchemaProfile),
|
||||
),
|
||||
createPromptCatalog(promptContracts),
|
||||
outputSchemaProfile,
|
||||
@@ -159,35 +154,23 @@ export const createCatalogDigests = (
|
||||
|
||||
export type CatalogDigests = ReturnType<typeof digestCatalogs>;
|
||||
export type CatalogDigestProfiles = Readonly<
|
||||
Record<
|
||||
McpInputSchemaProfile,
|
||||
Readonly<Record<McpOutputSchemaProfile, CatalogDigests>>
|
||||
>
|
||||
Record<McpOutputSchemaProfile, CatalogDigests>
|
||||
>;
|
||||
|
||||
/** Build-time identities share schema conversion across the six supported profiles. */
|
||||
/** Build-time identities share schema conversion across both output profiles. */
|
||||
export const createCatalogDigestProfiles = (
|
||||
contracts: readonly ToolContract[] = TOOL_CONTRACTS,
|
||||
promptContracts: readonly PromptContract[] = PROMPT_CONTRACTS,
|
||||
): CatalogDigestProfiles => {
|
||||
const full = createToolCatalog(sortByName(contracts));
|
||||
const compact = presentToolCatalog(full, {
|
||||
budgetBytes: COMPACT_INPUT_SCHEMA_BUDGET_BYTES,
|
||||
});
|
||||
const catalog = presentToolCatalog(
|
||||
createToolCatalog(sortByName(contracts)),
|
||||
COMPACT_INPUT_SCHEMA_PRESENTATION,
|
||||
);
|
||||
const prompts = createPromptCatalog(promptContracts);
|
||||
const compactOutput: CompactOutputSchemaPresentation = {
|
||||
budgetBytes: COMPACT_OUTPUT_SCHEMA_BUDGET_BYTES,
|
||||
};
|
||||
const outputProfiles = (catalog: typeof full) => ({
|
||||
full: digestCatalogs(catalog, prompts, "full"),
|
||||
compact: digestCatalogs(
|
||||
presentToolCatalog(catalog, undefined, compactOutput),
|
||||
prompts,
|
||||
"compact",
|
||||
),
|
||||
return {
|
||||
off: digestCatalogs(catalog, prompts, "off"),
|
||||
});
|
||||
return { full: outputProfiles(full), compact: outputProfiles(compact) };
|
||||
compact: digestCatalogs(catalog, prompts, "compact"),
|
||||
};
|
||||
};
|
||||
|
||||
/** One catalog identity: advertised presentation, contract and prompt scope, output profile. */
|
||||
@@ -200,31 +183,22 @@ export interface CatalogIdentityOptions {
|
||||
|
||||
export const createCatalogIdentity = (options: CatalogIdentityOptions = {}) => {
|
||||
const {
|
||||
compactPresentation,
|
||||
compactPresentation = COMPACT_INPUT_SCHEMA_PRESENTATION,
|
||||
contracts = TOOL_CONTRACTS,
|
||||
outputSchemaProfile = "full",
|
||||
outputSchemaProfile = resolveMcpOutputSchemaProfile(undefined),
|
||||
promptContracts = PROMPT_CONTRACTS,
|
||||
} = options;
|
||||
const selectedPresentation =
|
||||
compactPresentation === undefined
|
||||
? undefined
|
||||
: { budgetBytes: compactPresentation.budgetBytes };
|
||||
// Only the exact built-in presentations have build-time identities. Source
|
||||
// runs and custom budgets still compute from their selected schemas lazily.
|
||||
const inputProfile =
|
||||
selectedPresentation === undefined
|
||||
? "full"
|
||||
: selectedPresentation.budgetBytes === COMPACT_INPUT_SCHEMA_BUDGET_BYTES
|
||||
? "compact"
|
||||
: undefined;
|
||||
// Recorded digests describe the complete default catalog only; a scoped tool
|
||||
// or prompt set always computes from its own inputs.
|
||||
const selectedPresentation = { budgetBytes: compactPresentation.budgetBytes };
|
||||
// Recorded digests describe the complete catalog at the built-in budget
|
||||
// only; a scoped tool or prompt set, or a custom budget, computes from its
|
||||
// own inputs on first use.
|
||||
let catalogDigests: CatalogDigests | undefined =
|
||||
inputProfile === undefined ||
|
||||
selectedPresentation.budgetBytes !==
|
||||
COMPACT_INPUT_SCHEMA_PRESENTATION.budgetBytes ||
|
||||
contracts !== TOOL_CONTRACTS ||
|
||||
promptContracts !== PROMPT_CONTRACTS
|
||||
? undefined
|
||||
: RECORDED_CATALOG_DIGEST_PROFILES?.[inputProfile][outputSchemaProfile];
|
||||
: RECORDED_CATALOG_DIGEST_PROFILES?.[outputSchemaProfile];
|
||||
return {
|
||||
counts: {
|
||||
cli_commands: CLI_COMMAND_NAMES.length,
|
||||
@@ -242,17 +216,8 @@ export const createCatalogIdentity = (options: CatalogIdentityOptions = {}) => {
|
||||
} as const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Full input/output advertisement identity. Digests are recorded at build time
|
||||
* or computed on first use; this is independent of the configured default.
|
||||
*/
|
||||
export const FULL_CATALOG_IDENTITY = createCatalogIdentity();
|
||||
|
||||
/** Default MCP advertisement; explicit full/custom selections have their own identity. */
|
||||
export const DEFAULT_CATALOG_IDENTITY = createCatalogIdentity({
|
||||
compactPresentation: compactPresentationForMcpInputSchemaProfile(undefined),
|
||||
outputSchemaProfile: resolveMcpOutputSchemaProfile(undefined),
|
||||
});
|
||||
/** Default MCP advertisement: compact input, output schemas omitted. */
|
||||
export const DEFAULT_CATALOG_IDENTITY = createCatalogIdentity();
|
||||
|
||||
/**
|
||||
* Identity whose digests cover exactly the contracts a registration advertises.
|
||||
@@ -260,19 +225,17 @@ export const DEFAULT_CATALOG_IDENTITY = createCatalogIdentity({
|
||||
* paths derive the same value from the same scope.
|
||||
*/
|
||||
export const catalogIdentityForToolFamilies = (
|
||||
compactPresentation: CompactInputSchemaPresentation | undefined,
|
||||
compactPresentation: CompactInputSchemaPresentation,
|
||||
outputSchemaProfile: McpOutputSchemaProfile,
|
||||
advertisedToolFamilies: ReadonlySet<string> | undefined,
|
||||
): ReturnType<typeof createCatalogIdentity> =>
|
||||
compactPresentation === undefined &&
|
||||
outputSchemaProfile === "full" &&
|
||||
compactPresentation.budgetBytes ===
|
||||
COMPACT_INPUT_SCHEMA_PRESENTATION.budgetBytes &&
|
||||
outputSchemaProfile === resolveMcpOutputSchemaProfile(undefined) &&
|
||||
advertisedToolFamilies === undefined
|
||||
? FULL_CATALOG_IDENTITY
|
||||
? DEFAULT_CATALOG_IDENTITY
|
||||
: createCatalogIdentity({
|
||||
compactPresentation:
|
||||
compactPresentation === undefined
|
||||
? undefined
|
||||
: { budgetBytes: compactPresentation.budgetBytes },
|
||||
compactPresentation: { budgetBytes: compactPresentation.budgetBytes },
|
||||
contracts: contractsForToolFamilies(advertisedToolFamilies),
|
||||
outputSchemaProfile,
|
||||
promptContracts: promptsForToolFamilies(advertisedToolFamilies),
|
||||
|
||||
@@ -1,30 +1,32 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ADVERTISED_INPUT_SCHEMA_MAX_BYTES } from "../contracts/advertisedDialect.js";
|
||||
import type { CompactInputSchemaPresentation } from "../contracts/compactInputSchema.js";
|
||||
import { ConfigurationError } from "../domain/configurationErrors.js";
|
||||
import { err, ok, type Result } from "../domain/result.js";
|
||||
|
||||
/** The compact profile fits the portable dialect's per-tool size cap. */
|
||||
/** The compact presentation fits the portable dialect's per-tool size cap. */
|
||||
export const COMPACT_INPUT_SCHEMA_BUDGET_BYTES =
|
||||
ADVERTISED_INPUT_SCHEMA_MAX_BYTES;
|
||||
|
||||
export const mcpInputSchemaProfileSchema = z.enum(["full", "compact"]);
|
||||
|
||||
/** Resolve the selected advertisement at the configuration boundary. */
|
||||
export const resolveMcpInputSchemaProfile = (
|
||||
profile: McpInputSchemaProfile | undefined,
|
||||
): McpInputSchemaProfile => profile ?? "compact";
|
||||
|
||||
/** Select the default compact advertisement, preserving an explicit full choice. */
|
||||
export const compactPresentationForMcpInputSchemaProfile = (
|
||||
profile: "full" | "compact" | undefined,
|
||||
): { readonly budgetBytes: number } | undefined =>
|
||||
resolveMcpInputSchemaProfile(profile) === "full"
|
||||
? undefined
|
||||
: { budgetBytes: COMPACT_INPUT_SCHEMA_BUDGET_BYTES };
|
||||
/**
|
||||
* The one advertised input presentation. Every server, identity and recorded
|
||||
* catalog presents input schemas through it; canonical validators still
|
||||
* check every call.
|
||||
*/
|
||||
export const COMPACT_INPUT_SCHEMA_PRESENTATION: CompactInputSchemaPresentation =
|
||||
Object.freeze({ budgetBytes: COMPACT_INPUT_SCHEMA_BUDGET_BYTES });
|
||||
|
||||
const profileConstraint =
|
||||
'REA_MCP_INPUT_SCHEMA_PROFILE must be "compact" (default) or "full". The compact profile advertises bounded input schemas with fewer nested annotations; full preserves complete advertised input guidance.';
|
||||
'REA_MCP_INPUT_SCHEMA_PROFILE accepts only "compact", the default. The "full" input profile was removed: remove the setting from the client registration, or set it to "compact". Canonical server-side validation still checks every call.';
|
||||
|
||||
/**
|
||||
* Only `compact` remains so registrations that spell the default keep
|
||||
* starting; any other value, including the removed `full`, fails closed.
|
||||
*/
|
||||
export const mcpInputSchemaProfileSchema = z.enum(["compact"], {
|
||||
error: profileConstraint,
|
||||
});
|
||||
|
||||
export type McpInputSchemaProfile = z.infer<typeof mcpInputSchemaProfileSchema>;
|
||||
|
||||
|
||||
@@ -15,8 +15,13 @@ import { err, ok, type Result } from "../domain/result.js";
|
||||
*/
|
||||
export const COMPACT_OUTPUT_SCHEMA_BUDGET_BYTES = 4 * 1024;
|
||||
|
||||
/** Select whether tools/list carries the large output contracts. */
|
||||
export const mcpOutputSchemaProfileSchema = z.enum(["full", "compact", "off"]);
|
||||
const profileConstraint =
|
||||
'REA_MCP_OUTPUT_SCHEMA_PROFILE must be "off" (default) or "compact". The off profile omits output schemas; compact advertises every output schema within a 4,096-byte budget. The "full" output profile was removed: remove the setting or select "compact". Both keep canonical server-side validation and complete structured content.';
|
||||
|
||||
/** Select whether tools/list carries bounded output contracts. */
|
||||
export const mcpOutputSchemaProfileSchema = z.enum(["off", "compact"], {
|
||||
error: profileConstraint,
|
||||
});
|
||||
|
||||
export type McpOutputSchemaProfile = z.infer<
|
||||
typeof mcpOutputSchemaProfileSchema
|
||||
@@ -26,22 +31,16 @@ export type McpOutputSchemaProfile = z.infer<
|
||||
* Resolve the selected advertisement at the configuration boundary. No model
|
||||
* API reads an output schema; MCP client validators re-check what REA already
|
||||
* validates, and complete output schemas were about 92% of `tools/list`. They
|
||||
* are therefore omitted unless a caller selects `compact` or `full`.
|
||||
* are therefore omitted unless a caller selects `compact`.
|
||||
*/
|
||||
export const resolveMcpOutputSchemaProfile = (
|
||||
profile: McpOutputSchemaProfile | undefined,
|
||||
): McpOutputSchemaProfile => profile ?? "off";
|
||||
|
||||
/** Resolve the compact output advertisement, preserving every other choice. */
|
||||
export const compactPresentationForMcpOutputSchemaProfile = (
|
||||
profile: McpOutputSchemaProfile | undefined,
|
||||
): { readonly budgetBytes: number } | undefined =>
|
||||
profile === "compact"
|
||||
? { budgetBytes: COMPACT_OUTPUT_SCHEMA_BUDGET_BYTES }
|
||||
: undefined;
|
||||
|
||||
const profileConstraint =
|
||||
'REA_MCP_OUTPUT_SCHEMA_PROFILE must be "off" (default), "compact", or "full". The off profile omits output schemas; compact advertises every output schema within a 4,096-byte budget; full advertises complete output schemas. All keep canonical server-side validation and complete structured content.';
|
||||
/** The one bounded output advertisement, selected by `compact`. */
|
||||
export const COMPACT_OUTPUT_SCHEMA_PRESENTATION: {
|
||||
readonly budgetBytes: number;
|
||||
} = Object.freeze({ budgetBytes: COMPACT_OUTPUT_SCHEMA_BUDGET_BYTES });
|
||||
|
||||
/** Parse the output schema advertisement selection at server composition. */
|
||||
export const parseMcpOutputSchemaProfile = (
|
||||
|
||||
@@ -152,9 +152,6 @@ export const parseConfig = (
|
||||
...(env.REA_MCP_MAX_INPUT_ELEMENTS === undefined
|
||||
? {}
|
||||
: { mcpMaxInputElements: env.REA_MCP_MAX_INPUT_ELEMENTS }),
|
||||
...(env.REA_MCP_INPUT_SCHEMA_PROFILE === undefined
|
||||
? {}
|
||||
: { mcpInputSchemaProfile: env.REA_MCP_INPUT_SCHEMA_PROFILE }),
|
||||
...(env.REA_MCP_OUTPUT_SCHEMA_PROFILE === undefined
|
||||
? {}
|
||||
: { mcpOutputSchemaProfile: env.REA_MCP_OUTPUT_SCHEMA_PROFILE }),
|
||||
|
||||
@@ -5,7 +5,6 @@ import type { ReferenceSourcePolicy } from "../domain/referenceSourcePolicy.js";
|
||||
import type { AnalysisProviderSelector } from "../domain/providerSelection.js";
|
||||
|
||||
import type { GhidraLanguageOverride } from "./ghidraLanguageOverride.js";
|
||||
import type { McpInputSchemaProfile } from "./mcpInputSchemaProfile.js";
|
||||
import type { McpOutputSchemaProfile } from "./mcpOutputSchemaProfile.js";
|
||||
import type { McpToolCatalogProfile } from "./mcpToolCatalogProfile.js";
|
||||
import type { BridgeMaxRequestBytes } from "./bridgeMaxRequestBytes.js";
|
||||
@@ -20,7 +19,6 @@ export interface AppConfig {
|
||||
readonly mcpToolFamilies?: readonly McpToolFamilies[number][];
|
||||
readonly mcpMaxResponseBytes?: number;
|
||||
readonly mcpMaxInputElements?: number;
|
||||
readonly mcpInputSchemaProfile?: McpInputSchemaProfile;
|
||||
readonly mcpOutputSchemaProfile?: McpOutputSchemaProfile;
|
||||
readonly mcpToolCatalogProfile?: McpToolCatalogProfile;
|
||||
readonly analysisProvider: AnalysisProviderSelector;
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
// Advertised constructs outside the portable dialect that the presentation
|
||||
// does not yet project away, counted per profile over every distinct tool
|
||||
// definition the server can list (all catalog and output profiles). Counts may
|
||||
// only shrink: the catalog test requires an exact match, so removing a
|
||||
// construct lowers its count here, and any key not listed is enforced at zero.
|
||||
// Two exceptions grow them. Each runtime analysis setting adds a branch to
|
||||
// update_analysis_settings with two `const` members (`setting`, `action`), and
|
||||
// the full profile also counts the Evidence bundle's required `evidence_format`
|
||||
// literal (src/domain/evidenceFormat.ts) in the inputs that carry a whole
|
||||
// bundle. A fix that restores a contract declaration the presentation had lost
|
||||
// (for example a borrowed Evidence union advertised as an unconstrained value)
|
||||
// counts its constructs again, and the PR names the restored declaration. A
|
||||
// new contract or presentation construct never raises a count.
|
||||
|
||||
// Every advertised pattern, input and output, is enforced at zero: the
|
||||
// presentation re-emits each one in the portable grammar.
|
||||
|
||||
/** Input-side counts, keyed by `REA_MCP_INPUT_SCHEMA_PROFILE`. */
|
||||
export const ADVERTISED_INPUT_DIALECT_BASELINE: Readonly<
|
||||
Record<"compact" | "full", Readonly<Record<string, number>>>
|
||||
> = {
|
||||
// The compact projection (`advertisedDialectProjection.ts`) leaves nothing
|
||||
// outside the dialect.
|
||||
compact: {},
|
||||
full: {
|
||||
"input any-of fewer-than-two-branches": 176,
|
||||
"input any-of untyped-branch": 142,
|
||||
"input boolean-schema": 1,
|
||||
"input bound maxItems": 2,
|
||||
"input bound maxLength": 30,
|
||||
"input description-length": 1,
|
||||
"input enum not-on-string-type": 1,
|
||||
"input input-bytes": 23,
|
||||
"input input-depth": 33,
|
||||
"input keyword $schema": 256,
|
||||
"input keyword allOf": 99,
|
||||
"input keyword const": 1917,
|
||||
"input keyword default": 803,
|
||||
"input keyword else": 6,
|
||||
"input keyword examples": 253,
|
||||
"input keyword exclusiveMinimum": 335,
|
||||
"input keyword format": 22,
|
||||
"input keyword if": 7,
|
||||
"input keyword minProperties": 12,
|
||||
"input keyword not": 10,
|
||||
"input keyword oneOf": 225,
|
||||
"input keyword prefixItems": 267,
|
||||
"input keyword propertyNames": 55,
|
||||
"input keyword then": 7,
|
||||
"input keyword uniqueItems": 5,
|
||||
"input type type-list": 53,
|
||||
"input type type:null": 6,
|
||||
"input type untyped": 1256,
|
||||
},
|
||||
};
|
||||
@@ -7,6 +7,7 @@ import type { ToolContract } from "./toolContractTypes.js";
|
||||
import { artifactOutputSchemas } from "./toolOutputSchemaGroups.js";
|
||||
import { artifactIntegrityPolicySchema } from "../domain/artifactIntegrityPolicy.js";
|
||||
import { jsonValueSchema } from "../domain/jsonValue.js";
|
||||
import { distinctItems } from "../domain/schemaConstraints.js";
|
||||
import { toolContractMetadata } from "./toolEffects.js";
|
||||
import { appleAssetCatalogInputSchema } from "../domain/apple/appleAssetCatalog.js";
|
||||
import { dylibResolutionInputSchema } from "../domain/apple/dylibResolution.js";
|
||||
@@ -44,14 +45,10 @@ export const artifactExtractionInputSchema = z.strictObject({
|
||||
.describe(
|
||||
"Absolute destination path on the REA host. It must not exist and its parent must exist. Omit to use a fresh temporary directory. Successful output persists after the call.",
|
||||
),
|
||||
occurrence_ids: z
|
||||
.array(artifactOccurrenceSchema.shape.occurrence_id)
|
||||
.min(1)
|
||||
.refine(
|
||||
(ids) => new Set(ids).size === ids.length,
|
||||
"Occurrence IDs must be unique",
|
||||
)
|
||||
.meta({ uniqueItems: true })
|
||||
occurrence_ids: distinctItems(
|
||||
z.array(artifactOccurrenceSchema.shape.occurrence_id).min(1),
|
||||
"Occurrence IDs must be unique",
|
||||
)
|
||||
.exactOptional()
|
||||
.describe(
|
||||
"Exact regular-file or slice occurrence IDs from the current inventory. Omit to extract all regular entries exposed by the active reader. Directories, links, the root and members inside nested archives cannot be selected.",
|
||||
|
||||
@@ -42,15 +42,17 @@ import {
|
||||
} from "../domain/webScreenshot.js";
|
||||
import { toolContractMetadata } from "./toolEffects.js";
|
||||
import { evidenceResultOf } from "./toolOutputSchemaPrimitives.js";
|
||||
import { presentRootDiscriminator } from "../domain/schemaAnnotations.js";
|
||||
|
||||
const listOutputSchema = evidenceResultOf(browserTargetListSchema);
|
||||
const inspectionOutputSchema = evidenceResultOf(webPageInspectionSchema);
|
||||
export const analyzeWebBundleRequestSchema = z
|
||||
.discriminatedUnion("detail", [
|
||||
export const analyzeWebBundleRequestSchema = presentRootDiscriminator(
|
||||
z.discriminatedUnion("detail", [
|
||||
analyzeWebBundleInputSchema.extend(javaScriptRoutesCompleteDeliveryShape),
|
||||
analyzeWebBundleInputSchema.extend(webRoutesSummaryDeliveryShape),
|
||||
])
|
||||
.meta({ "x-rea-root-discriminator": "detail" });
|
||||
]),
|
||||
"detail",
|
||||
);
|
||||
const bundleOutputSchema = evidenceResultOf(
|
||||
z.union([
|
||||
webBundleAnalysisSchema,
|
||||
|
||||
@@ -80,8 +80,10 @@ it.each(
|
||||
: new Ajv({ strict: false, validateFormats: false });
|
||||
expect(ajv.validateSchema(projected)).toBe(true);
|
||||
expect(projected).toMatchObject({ type: "object" });
|
||||
for (const key of ["anyOf", "oneOf", "allOf", "x-rea-root-discriminator"])
|
||||
for (const key of ["anyOf", "oneOf", "allOf"])
|
||||
expect(projected).not.toHaveProperty(key);
|
||||
// The typed root discriminator selects the alternative.
|
||||
expect(projected).toMatchObject({ if: { required: ["detail"] } });
|
||||
const validate = ajv.compile(projected);
|
||||
for (const [delivery, valid] of [
|
||||
[{}, true],
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
stripAnnotations,
|
||||
} from "./compactJsonSchema.js";
|
||||
|
||||
// The compact input profile: every advertised input schema is self-contained
|
||||
// The compact input presentation: every advertised input schema is self-contained
|
||||
// (clients such as Kimi expand references before sending tools to Moonshot),
|
||||
// inside the per-tool byte budget, and in the portable advertised dialect.
|
||||
// Canonical server-side validation remains unchanged.
|
||||
@@ -112,9 +112,8 @@ const compacted = new WeakMap<object, Map<number, CompactedInputSchema>>();
|
||||
/** The advertised input for the selected presentation, with its guidance. */
|
||||
export const presentAdvertisedInput = (
|
||||
schema: Record<string, unknown>,
|
||||
compactPresentation: CompactInputSchemaPresentation | undefined,
|
||||
compactPresentation: CompactInputSchemaPresentation,
|
||||
): Pick<CompactedInputSchema, "schema" | "guidance"> => {
|
||||
if (compactPresentation === undefined) return { schema, guidance: [] };
|
||||
const { budgetBytes } = compactPresentation;
|
||||
let byBudget = compacted.get(schema);
|
||||
if (byBudget === undefined) compacted.set(schema, (byBudget = new Map()));
|
||||
@@ -130,7 +129,7 @@ export const presentAdvertisedInput = (
|
||||
/** Return the JSON schema clients receive for the selected input presentation. */
|
||||
export const presentAdvertisedInputSchema = (
|
||||
schema: Record<string, unknown>,
|
||||
compactPresentation: CompactInputSchemaPresentation | undefined,
|
||||
compactPresentation: CompactInputSchemaPresentation,
|
||||
): Record<string, unknown> =>
|
||||
presentAdvertisedInput(schema, compactPresentation).schema;
|
||||
|
||||
|
||||
@@ -14,14 +14,16 @@ import {
|
||||
erlangBeamCompleteDeliveryShape,
|
||||
erlangBeamSummaryDeliveryShape,
|
||||
} from "../../domain/analysisView/erlangBeamView.js";
|
||||
import { presentRootDiscriminator } from "../../domain/schemaAnnotations.js";
|
||||
|
||||
/** Delivery selection does not change provider inputs or complete Evidence identity. */
|
||||
export const inspectErlangBeamRequestSchema = z
|
||||
.discriminatedUnion("detail", [
|
||||
export const inspectErlangBeamRequestSchema = presentRootDiscriminator(
|
||||
z.discriminatedUnion("detail", [
|
||||
inspectErlangBeamInputSchema.extend(erlangBeamCompleteDeliveryShape),
|
||||
inspectErlangBeamInputSchema.extend(erlangBeamSummaryDeliveryShape),
|
||||
])
|
||||
.meta({ "x-rea-root-discriminator": "detail" });
|
||||
]),
|
||||
"detail",
|
||||
);
|
||||
|
||||
/** Static BEAM inspection is independent of active native-analysis providers. */
|
||||
export const ERLANG_BEAM_TOOL_CONTRACTS = [
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { presentObjectRoot } from "../domain/schemaAnnotations.js";
|
||||
|
||||
/** Transport-independent request grammar; rendering never executes a workflow. */
|
||||
export const guidedWorkflowsInputSchema = z.discriminatedUnion("action", [
|
||||
z.strictObject({ action: z.literal("list") }),
|
||||
@@ -28,9 +30,9 @@ const guidedWorkflowItemSchema = z.strictObject({
|
||||
}),
|
||||
),
|
||||
});
|
||||
// Every alternative is an object; declare that root for older MCP codecs too.
|
||||
export const guidedWorkflowsOutputSchema = z
|
||||
.discriminatedUnion("action", [
|
||||
// Every alternative is an object; the advertised root says so for older MCP codecs.
|
||||
export const guidedWorkflowsOutputSchema = presentObjectRoot(
|
||||
z.discriminatedUnion("action", [
|
||||
z.strictObject({
|
||||
action: z.literal("list"),
|
||||
workflows: z.array(guidedWorkflowItemSchema),
|
||||
@@ -40,6 +42,6 @@ export const guidedWorkflowsOutputSchema = z
|
||||
workflow_id: z.string(),
|
||||
rendered_prompt: z.string(),
|
||||
}),
|
||||
])
|
||||
.meta({ type: "object" });
|
||||
]),
|
||||
);
|
||||
export type GuidedWorkflowsResult = z.infer<typeof guidedWorkflowsOutputSchema>;
|
||||
@@ -2,72 +2,77 @@ import { Ajv } from "ajv";
|
||||
import { Ajv2020 } from "ajv/dist/2020.js";
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import { presentInputJsonSchema } from "./inputSchemaPresentation.js";
|
||||
import { z } from "zod";
|
||||
|
||||
it.each(["draft-2020-12", "draft-07"] as const)(
|
||||
"summarizes only declared observation schemas, preserving literal data in %s",
|
||||
(target) => {
|
||||
const literal = {
|
||||
type: "object",
|
||||
"x-rea-complete-observation": true,
|
||||
properties: { producer_data: { type: "string" } },
|
||||
};
|
||||
const controls = {
|
||||
type: "object",
|
||||
properties: { match: { type: "string", minLength: 1 } },
|
||||
required: ["match"],
|
||||
additionalProperties: false,
|
||||
};
|
||||
const canonical = {
|
||||
$schema:
|
||||
target === "draft-07"
|
||||
? "http://json-schema.org/draft-07/schema#"
|
||||
: "https://json-schema.org/draft/2020-12/schema",
|
||||
type: "object",
|
||||
properties: {
|
||||
observation: {
|
||||
type: "object",
|
||||
description: "Complete result from the producer.",
|
||||
"x-rea-complete-observation": true,
|
||||
properties: { id: { type: "string" } },
|
||||
required: ["id"],
|
||||
},
|
||||
controls,
|
||||
literal: { const: literal },
|
||||
},
|
||||
required: ["observation", "controls", "literal"],
|
||||
additionalProperties: false,
|
||||
};
|
||||
const presented = presentInputJsonSchema(canonical, () => undefined);
|
||||
expect(presented).toHaveProperty("properties.observation", {
|
||||
type: "object",
|
||||
description: expect.stringContaining("complete canonical observation"),
|
||||
});
|
||||
expect(presented).toHaveProperty("properties.controls", controls);
|
||||
expect(presented).toHaveProperty("properties.literal.const", literal);
|
||||
const ajv =
|
||||
target === "draft-07"
|
||||
? new Ajv({ strict: false })
|
||||
: new Ajv2020({ strict: false });
|
||||
const validate = ajv.compile(presented);
|
||||
expect(
|
||||
validate({ observation: {}, controls: { match: "x" }, literal }),
|
||||
).toBe(true);
|
||||
expect(
|
||||
validate({ observation: "{}", controls: { match: "x" }, literal }),
|
||||
).toBe(false);
|
||||
expect(
|
||||
validate({ observation: {}, controls: { match: "" }, literal }),
|
||||
).toBe(false);
|
||||
expect(
|
||||
ajv.compile(canonical)({
|
||||
observation: {},
|
||||
controls: { match: "x" },
|
||||
literal,
|
||||
}),
|
||||
).toBe(false);
|
||||
},
|
||||
);
|
||||
import { BROWSER_TOOL_CONTRACTS } from "./browserToolContracts.js";
|
||||
import { presentInputJsonSchema } from "./inputSchemaPresentation.js";
|
||||
import {
|
||||
advertisedInputJsonSchema,
|
||||
COMPLETE_OBSERVATION_GUIDANCE,
|
||||
toolOutputSchemaWithMetadata,
|
||||
} from "./toolSchemaMetadata.js";
|
||||
import { compareBrowserScenariosInputSchema } from "../domain/browserScenarioDiffValues.js";
|
||||
import { captureSnapshotSchema } from "../domain/webCaptureDiffSchemas.js";
|
||||
|
||||
const browserContract = (name: string) => {
|
||||
const found = BROWSER_TOOL_CONTRACTS.find(
|
||||
(candidate) => candidate.name === name,
|
||||
);
|
||||
if (found === undefined) throw new Error(`Missing ${name}`);
|
||||
return found;
|
||||
};
|
||||
|
||||
it("advertises complete observations as opaque objects only on the wire", () => {
|
||||
const comparison = browserContract("compare_web_captures");
|
||||
const advertised = advertisedInputJsonSchema(comparison);
|
||||
// Each opaque declaration is the field's own description, if any, followed
|
||||
// by the shared instruction.
|
||||
const declared = (description: string | undefined) => ({
|
||||
type: "object",
|
||||
description: [description, COMPLETE_OBSERVATION_GUIDANCE]
|
||||
.filter((text) => text !== undefined)
|
||||
.join(" "),
|
||||
});
|
||||
expect(advertised).toHaveProperty(
|
||||
"properties.before.properties.inspection",
|
||||
declared(captureSnapshotSchema.shape.inspection.description),
|
||||
);
|
||||
expect(advertised).toHaveProperty(
|
||||
"properties.before.properties.webmcp.anyOf.0",
|
||||
declared(undefined),
|
||||
);
|
||||
expect(advertised).toHaveProperty(
|
||||
"properties.before_scenario",
|
||||
declared(
|
||||
compareBrowserScenariosInputSchema.shape.before_scenario.description,
|
||||
),
|
||||
);
|
||||
// Analyst-authored controls stay explicit.
|
||||
expect(advertised).toHaveProperty(
|
||||
"properties.normalization.properties.rules.items.required",
|
||||
["rule_id", "artifacts", "match", "replacement"],
|
||||
);
|
||||
const validate = new Ajv2020({ strict: false }).compile(advertised);
|
||||
expect(validate({ before_scenario: {}, after_scenario: {} })).toBe(true);
|
||||
expect(validate({ before_scenario: "{}", after_scenario: {} })).toBe(false);
|
||||
|
||||
// Every other projection of the same schemas stays canonical: the plain
|
||||
// conversion behind `--schema`, and the producer's own output schema.
|
||||
const plain = JSON.stringify(
|
||||
z.toJSONSchema(comparison.inputSchema, { io: "input" }),
|
||||
);
|
||||
expect(plain).not.toContain(COMPLETE_OBSERVATION_GUIDANCE);
|
||||
expect(plain).not.toContain("x-rea-");
|
||||
// The full scenario capture declaration, including its step list.
|
||||
expect(plain).toContain('"steps":{');
|
||||
expect(
|
||||
JSON.stringify(
|
||||
toolOutputSchemaWithMetadata(browserContract("inspect_web_page"))[
|
||||
"~standard"
|
||||
].jsonSchema.output({ target: "draft-2020-12" }),
|
||||
),
|
||||
).not.toContain(COMPLETE_OBSERVATION_GUIDANCE);
|
||||
});
|
||||
|
||||
const referenceWideningCases: readonly {
|
||||
name: string;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { isDeepStrictEqual } from "node:util";
|
||||
import { advertisedSchemaPatterns } from "./advertisedDialect.js";
|
||||
import { dependencyGuidance } from "./advertisedDialectProjection.js";
|
||||
import { projectPortablePattern } from "./portablePattern.js";
|
||||
import type { SchemaAnnotations } from "../domain/schemaAnnotations.js";
|
||||
import {
|
||||
schemaArrays,
|
||||
schemaChildren,
|
||||
@@ -50,22 +51,6 @@ const describeSchema = (
|
||||
propertyDescription?: string,
|
||||
): unknown => {
|
||||
if (!isObject(schema)) return schema;
|
||||
// These operands are complete producer results, not fields an agent builds.
|
||||
// Their owner keeps full canonical validation; only their wire declaration
|
||||
// omits the repeated result schema. Analyst-authored controls stay explicit.
|
||||
if (schema.type === "object" && schema["x-rea-complete-observation"] === true)
|
||||
return {
|
||||
type: "object",
|
||||
description: [
|
||||
propertyDescription ??
|
||||
(typeof schema.description === "string"
|
||||
? schema.description
|
||||
: undefined),
|
||||
"Pass the complete returned normalized_result unchanged. Its fields are omitted from this declaration; the server still validates the complete canonical observation.",
|
||||
]
|
||||
.filter((text) => text !== undefined)
|
||||
.join(" "),
|
||||
};
|
||||
const scope = references.enter(schema, inherited);
|
||||
const described = Object.fromEntries(
|
||||
Object.entries(schema).map(([key, value]) => {
|
||||
@@ -895,13 +880,12 @@ const flattenRootObjectUnion = (
|
||||
|
||||
/** Preserve opt-in discriminator relationships without a root union. */
|
||||
const presentDiscriminatedRoot = (
|
||||
root: Record<string, unknown>,
|
||||
canonical: Record<string, unknown>,
|
||||
discriminator: string | undefined,
|
||||
): Record<string, unknown> => {
|
||||
const { "x-rea-root-discriminator": discriminator, ...canonical } = root;
|
||||
const references = new JsonSchemaReferences(canonical);
|
||||
const flattened = flattenRootObjectUnion(canonical, references);
|
||||
if (typeof discriminator !== "string" || flattened === canonical)
|
||||
return flattened;
|
||||
if (discriminator === undefined || flattened === canonical) return flattened;
|
||||
const alternatives = canonical.anyOf ?? canonical.oneOf;
|
||||
if (!Array.isArray(alternatives) || alternatives.length !== 2)
|
||||
throw new TypeError(
|
||||
@@ -1000,8 +984,12 @@ const presentDiscriminatedRoot = (
|
||||
export const presentInputJsonSchema = (
|
||||
root: Record<string, unknown>,
|
||||
describeProperty: (property: string) => string | undefined,
|
||||
annotations: Pick<SchemaAnnotations, "rootDiscriminator"> = {},
|
||||
): Record<string, unknown> => {
|
||||
const flattened = presentDiscriminatedRoot(root);
|
||||
const flattened = presentDiscriminatedRoot(
|
||||
root,
|
||||
annotations.rootDiscriminator,
|
||||
);
|
||||
const references = new JsonSchemaReferences(flattened);
|
||||
const described = describeSchema(
|
||||
flattened,
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
import { javascriptRuntimeReconciliationResultSchema } from "../../domain/javascript/javascriptRuntimeReconciliationSchemas.js";
|
||||
import { reconcileJavaScriptRuntimeRequestSchema } from "./applicationWorkflowInputContracts.js";
|
||||
import { JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE } from "./javascriptRuntimeReconciliationExample.js";
|
||||
import { presentRootDiscriminator } from "../../domain/schemaAnnotations.js";
|
||||
|
||||
const listOutputSchema = evidenceResultOf(electronTargetListSchema);
|
||||
const inspectionOutputSchema = evidenceResultOf(electronPageInspectionSchema);
|
||||
@@ -41,16 +42,18 @@ const javascriptApplicationMcpInputSchema =
|
||||
),
|
||||
});
|
||||
/** Caller-selected delivery of a completed JavaScript application analysis. */
|
||||
export const analyzeJavaScriptApplicationRequestSchema = z
|
||||
.discriminatedUnion("detail", [
|
||||
javascriptApplicationMcpInputSchema.extend(
|
||||
javaScriptRoutesCompleteDeliveryShape,
|
||||
),
|
||||
javascriptApplicationMcpInputSchema.extend(
|
||||
javaScriptRoutesSummaryDeliveryShape,
|
||||
),
|
||||
])
|
||||
.meta({ "x-rea-root-discriminator": "detail" });
|
||||
export const analyzeJavaScriptApplicationRequestSchema =
|
||||
presentRootDiscriminator(
|
||||
z.discriminatedUnion("detail", [
|
||||
javascriptApplicationMcpInputSchema.extend(
|
||||
javaScriptRoutesCompleteDeliveryShape,
|
||||
),
|
||||
javascriptApplicationMcpInputSchema.extend(
|
||||
javaScriptRoutesSummaryDeliveryShape,
|
||||
),
|
||||
]),
|
||||
"detail",
|
||||
);
|
||||
const applicationOutputSchema = analysisDetailOutputSchemaOf(
|
||||
javascriptApplicationAnalysisResultSchema,
|
||||
z.union([
|
||||
|
||||
@@ -35,7 +35,8 @@ describe.each(cases)("%s delivery contract", (name, base) => {
|
||||
expect(projected).not.toHaveProperty("anyOf");
|
||||
expect(projected).not.toHaveProperty("oneOf");
|
||||
expect(projected).not.toHaveProperty("allOf");
|
||||
expect(projected).not.toHaveProperty("x-rea-root-discriminator");
|
||||
// The typed root discriminator selects the alternative.
|
||||
expect(projected).toMatchObject({ if: { required: ["detail"] } });
|
||||
const inputs = [
|
||||
[{}, true],
|
||||
[{ detail: "complete" }, true],
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { ToolContract } from "../toolContractTypes.js";
|
||||
import { managedOutputSchemas } from "../toolOutputSchemaGroups.js";
|
||||
import { toolContractMetadata } from "../toolEffects.js";
|
||||
import { absoluteLocalPathStringSchema } from "../../domain/localPath.js";
|
||||
import { distinctItems } from "../../domain/schemaConstraints.js";
|
||||
import { analysisDetailSchema } from "../analysisDetail.js";
|
||||
import { managedInspectionBudgetShape } from "../../domain/managed/managedInspectionLimits.js";
|
||||
|
||||
@@ -25,20 +26,19 @@ export const managedArtifactInputSchema = managedTargetInputSchema.extend({
|
||||
|
||||
/** Member inspection may select declaring types before members are decoded. */
|
||||
export const managedMembersInputSchema = managedArtifactInputSchema.extend({
|
||||
type_names: z
|
||||
.array(
|
||||
z
|
||||
.string()
|
||||
.min(1)
|
||||
.describe(
|
||||
"Exact declaring-type full_name: namespace-qualified for top-level types (System.Threading.Monitor); nested types report their bare name, so it selects every nested type of that name",
|
||||
),
|
||||
)
|
||||
.min(1)
|
||||
.refine((names) => new Set(names).size === names.length, {
|
||||
message: "type_names must not repeat a name",
|
||||
})
|
||||
.meta({ uniqueItems: true })
|
||||
type_names: distinctItems(
|
||||
z
|
||||
.array(
|
||||
z
|
||||
.string()
|
||||
.min(1)
|
||||
.describe(
|
||||
"Exact declaring-type full_name: namespace-qualified for top-level types (System.Threading.Monitor); nested types report their bare name, so it selects every nested type of that name",
|
||||
),
|
||||
)
|
||||
.min(1),
|
||||
"type_names must not repeat a name",
|
||||
)
|
||||
.describe(
|
||||
"Inspect only the fields, methods, call edges, and field accesses declared by these exact type full names, and the user strings their method bodies load; omit to inspect every type. Unmatched names are reported in selection.unmatched_type_names.",
|
||||
)
|
||||
|
||||
@@ -14,14 +14,16 @@ import {
|
||||
pythonPycCompleteDeliveryShape,
|
||||
pythonPycSummaryDeliveryShape,
|
||||
} from "../../domain/analysisView/pythonPycView.js";
|
||||
import { presentRootDiscriminator } from "../../domain/schemaAnnotations.js";
|
||||
|
||||
/** Delivery selection does not change provider inputs or complete Evidence identity. */
|
||||
export const inspectPythonPycRequestSchema = z
|
||||
.discriminatedUnion("detail", [
|
||||
export const inspectPythonPycRequestSchema = presentRootDiscriminator(
|
||||
z.discriminatedUnion("detail", [
|
||||
inspectPythonPycInputSchema.extend(pythonPycCompleteDeliveryShape),
|
||||
inspectPythonPycInputSchema.extend(pythonPycSummaryDeliveryShape),
|
||||
])
|
||||
.meta({ "x-rea-root-discriminator": "detail" });
|
||||
]),
|
||||
"detail",
|
||||
);
|
||||
|
||||
/** Static CPython pyc inspection is independent of active native-analysis providers. */
|
||||
export const PYTHON_PYC_TOOL_CONTRACTS = [
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { expect, it } from "vitest";
|
||||
import { z } from "zod";
|
||||
|
||||
import { ELECTRON_TOOL_CONTRACTS } from "./javascript/electronToolContracts.js";
|
||||
import {
|
||||
toolInputSchemaWithMetadata,
|
||||
toolOutputSchemaWithMetadata,
|
||||
} from "./toolSchemaMetadata.js";
|
||||
import { APPLICATION_TOOL_CONTRACTS } from "./applicationToolContracts.js";
|
||||
|
||||
// Each MCP server registers every contract. The SDK reprojects registered
|
||||
// schemas on every tools/list, so a wrapper rebuilt per server would repeat
|
||||
@@ -30,3 +32,17 @@ it("shares one advertised wrapper and projection per contract across registratio
|
||||
firstOutput["~standard"].jsonSchema.output(options),
|
||||
);
|
||||
});
|
||||
|
||||
it("declares an object root only on the advertised output of an object union", () => {
|
||||
const contract = APPLICATION_TOOL_CONTRACTS.find(
|
||||
({ name }) => name === "guided_workflows",
|
||||
);
|
||||
if (contract === undefined) throw new Error("Missing guided_workflows");
|
||||
const advertised = toolOutputSchemaWithMetadata(contract)[
|
||||
"~standard"
|
||||
].jsonSchema.output({ target: "draft-2020-12" });
|
||||
expect(advertised).toMatchObject({ type: "object" });
|
||||
expect(advertised).toHaveProperty("oneOf");
|
||||
// The canonical projection keeps only what parsing enforces.
|
||||
expect(z.toJSONSchema(contract.outputSchema)).not.toHaveProperty("type");
|
||||
});
|
||||
@@ -20,6 +20,7 @@ import { digestCanonicalValue } from "../domain/canonicalDigest.js";
|
||||
import { shareOutputDefinitions } from "./outputSchemaDefinitions.js";
|
||||
import { getEvidenceInputContract } from "../domain/evidenceInputContract.js";
|
||||
import { isRecord } from "../domain/isRecord.js";
|
||||
import { schemaAnnotations } from "../domain/schemaAnnotations.js";
|
||||
|
||||
const PROPERTY_DESCRIPTIONS: Readonly<Record<string, string>> = {
|
||||
addresses: "Ordered provider-normalized procedure addresses to analyze.",
|
||||
@@ -89,6 +90,49 @@ const oncePerContract = <
|
||||
return built;
|
||||
};
|
||||
|
||||
/** The instruction an advertised complete observation carries after its description. */
|
||||
export const COMPLETE_OBSERVATION_GUIDANCE =
|
||||
"Pass the complete returned normalized_result unchanged. Its fields are omitted from this declaration; the server still validates the complete canonical observation.";
|
||||
|
||||
/**
|
||||
* Apply typed presentation annotations while Zod projects an advertised input.
|
||||
* Running inside this projection only keeps `--schema`, output schemas and
|
||||
* every other JSON Schema conversion of the same Zod schema canonical.
|
||||
*/
|
||||
const presentAnnotatedInput = ({
|
||||
zodSchema,
|
||||
jsonSchema,
|
||||
}: {
|
||||
readonly zodSchema: z.core.$ZodType;
|
||||
readonly jsonSchema: Record<string, unknown>;
|
||||
}): void => {
|
||||
if (schemaAnnotations(zodSchema)?.completeObservation !== true) return;
|
||||
// These operands are complete producer results, not fields an agent builds,
|
||||
// so the wire declaration omits the repeated result schema.
|
||||
if (jsonSchema.type !== "object")
|
||||
throw new TypeError(
|
||||
"A complete observation annotation requires an object schema",
|
||||
);
|
||||
const description = [jsonSchema.description, COMPLETE_OBSERVATION_GUIDANCE]
|
||||
.filter((text) => typeof text === "string")
|
||||
.join(" ");
|
||||
for (const key of Object.keys(jsonSchema)) delete jsonSchema[key];
|
||||
Object.assign(jsonSchema, { type: "object", description });
|
||||
};
|
||||
|
||||
/**
|
||||
* Declare `type: "object"` on a union root whose alternatives are all objects,
|
||||
* after its combinator, where Zod places root metadata.
|
||||
*/
|
||||
const objectRoot = ({
|
||||
$defs,
|
||||
...root
|
||||
}: Record<string, unknown>): Record<string, unknown> => ({
|
||||
...root,
|
||||
type: "object",
|
||||
...($defs === undefined ? {} : { $defs }),
|
||||
});
|
||||
|
||||
/** Attach caller guidance to a canonical schema for the SDK wire projection. */
|
||||
export const toolInputSchemaWithMetadata = <Contract extends ToolContract>(
|
||||
contract: Contract,
|
||||
@@ -100,12 +144,17 @@ export const toolInputSchemaWithMetadata = <Contract extends ToolContract>(
|
||||
(project) => (options) => {
|
||||
const inline = project({
|
||||
...options,
|
||||
libraryOptions: { ...options.libraryOptions, reused: "inline" },
|
||||
libraryOptions: {
|
||||
...options.libraryOptions,
|
||||
reused: "inline",
|
||||
override: presentAnnotatedInput,
|
||||
},
|
||||
});
|
||||
const examples = advertisedInputExamples(contract);
|
||||
const presented = presentInputJsonSchema(
|
||||
inline,
|
||||
fallbackPropertyDescription,
|
||||
schemaAnnotations(contract.inputSchema),
|
||||
);
|
||||
const sourceGuidance = getEvidenceInputContract(contract.inputSchema)
|
||||
.hasEvidence
|
||||
@@ -146,12 +195,15 @@ export const toolOutputSchemaWithMetadata = <Contract extends ToolContract>(
|
||||
contract.outputSchema,
|
||||
"output",
|
||||
(project) => (options) => {
|
||||
const projected = project({
|
||||
...options,
|
||||
libraryOptions: { reused: "ref", ...options.libraryOptions },
|
||||
});
|
||||
const shared = shareOutputDefinitions(
|
||||
presentOutputJsonSchema(
|
||||
project({
|
||||
...options,
|
||||
libraryOptions: { reused: "ref", ...options.libraryOptions },
|
||||
}),
|
||||
schemaAnnotations(contract.outputSchema)?.objectRoot === true
|
||||
? objectRoot(projected)
|
||||
: projected,
|
||||
),
|
||||
);
|
||||
// An explicit ID owns the reference base. Relative external references
|
||||
|
||||
@@ -24,13 +24,15 @@ import {
|
||||
webSourceLocationInputSchema,
|
||||
webSourceLocationResultSchema,
|
||||
} from "../domain/webSourceLocation.js";
|
||||
import { presentRootDiscriminator } from "../domain/schemaAnnotations.js";
|
||||
|
||||
export const webSourceLocationRequestSchema = z
|
||||
.discriminatedUnion("detail", [
|
||||
export const webSourceLocationRequestSchema = presentRootDiscriminator(
|
||||
z.discriminatedUnion("detail", [
|
||||
webSourceLocationInputSchema.extend(webSourceLocationCompleteDeliveryShape),
|
||||
webSourceLocationInputSchema.extend(webSourceLocationSummaryDeliveryShape),
|
||||
])
|
||||
.meta({ "x-rea-root-discriminator": "detail" });
|
||||
]),
|
||||
"detail",
|
||||
);
|
||||
|
||||
/** Local captured-script publication contracts shared by CLI and MCP. */
|
||||
export const WEB_SCRIPT_TOOL_CONTRACTS = [
|
||||
|
||||
@@ -4,6 +4,7 @@ import { AnalysisUnsupportedTargetError } from "../analysisErrorCore.js";
|
||||
import type { AnalysisError } from "../analysisErrorBase.js";
|
||||
import { uniqueSorted } from "../canonicalOrdering.js";
|
||||
import { err, ok, type Result } from "../result.js";
|
||||
import { distinctItems } from "../schemaConstraints.js";
|
||||
import type { AnalysisViewParent } from "./analysisViewParent.js";
|
||||
import { ownedView, type AdmittedView } from "./analysisViewShape.js";
|
||||
import { EnvironmentManifestAccumulator } from "./EnvironmentManifestAccumulator.js";
|
||||
@@ -19,13 +20,10 @@ import {
|
||||
type ScenarioResponseBodyRetention,
|
||||
} from "./scenarioCaptureRecords.js";
|
||||
/** Opt-in template rules, each listed at most once; shared by every view that groups tools. */
|
||||
export const templateRulesSchema = z
|
||||
.array(z.enum(TEMPLATE_RULES))
|
||||
.refine((rules) => new Set(rules).size === rules.length, {
|
||||
message: "template_rules must not repeat a rule",
|
||||
})
|
||||
.meta({ uniqueItems: true })
|
||||
.default([]);
|
||||
export const templateRulesSchema = distinctItems(
|
||||
z.array(z.enum(TEMPLATE_RULES)),
|
||||
"template_rules must not repeat a rule",
|
||||
).default([]);
|
||||
|
||||
export const environmentManifestViewRequestSchema = z.strictObject({
|
||||
kind: z.literal("environment-manifest"),
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
apiContractOperationSchema,
|
||||
apiContractPathReferenceSchema,
|
||||
} from "./apiContract.js";
|
||||
import { presentRootDiscriminator } from "../schemaAnnotations.js";
|
||||
|
||||
const page = {
|
||||
kind: z.literal("api-contract"),
|
||||
@@ -67,8 +68,8 @@ const commonInput = {
|
||||
};
|
||||
const selectionDescription =
|
||||
"Return this selected page immediately; complete Evidence is retained and view takes precedence over detail";
|
||||
export const inspectApiContractInputSchema = z
|
||||
.union([
|
||||
export const inspectApiContractInputSchema = presentRootDiscriminator(
|
||||
z.union([
|
||||
z.strictObject({
|
||||
...commonInput,
|
||||
format: z.literal("openapi-json").default("openapi-json"),
|
||||
@@ -88,8 +89,9 @@ export const inspectApiContractInputSchema = z
|
||||
.optional()
|
||||
.describe(selectionDescription),
|
||||
}),
|
||||
])
|
||||
.meta({ "x-rea-root-discriminator": "format" });
|
||||
]),
|
||||
"format",
|
||||
);
|
||||
export type InspectApiContractInput = z.output<
|
||||
typeof inspectApiContractInputSchema
|
||||
>;
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { retainedBrowserScenarioCaptureSchema } from "./browserScenarioCapture.js";
|
||||
import { presentCompleteObservation } from "./schemaAnnotations.js";
|
||||
|
||||
/** Scenario artifact families compared after step alignment. */
|
||||
export const browserScenarioArtifactKindSchema = z.enum([
|
||||
@@ -74,14 +75,16 @@ const normalizationInputSchema = z
|
||||
|
||||
/** Input for step-aligned comparison of two browser scenario captures. */
|
||||
export const compareBrowserScenariosInputSchema = z.strictObject({
|
||||
before_scenario: retainedBrowserScenarioCaptureSchema
|
||||
.describe(
|
||||
before_scenario: presentCompleteObservation(
|
||||
retainedBrowserScenarioCaptureSchema.describe(
|
||||
"Earlier complete normalized_result from capture_browser_scenario.",
|
||||
)
|
||||
.meta({ "x-rea-complete-observation": true }),
|
||||
after_scenario: retainedBrowserScenarioCaptureSchema
|
||||
.describe("Later complete normalized_result from capture_browser_scenario.")
|
||||
.meta({ "x-rea-complete-observation": true }),
|
||||
),
|
||||
),
|
||||
after_scenario: presentCompleteObservation(
|
||||
retainedBrowserScenarioCaptureSchema.describe(
|
||||
"Later complete normalized_result from capture_browser_scenario.",
|
||||
),
|
||||
),
|
||||
normalization: normalizationInputSchema
|
||||
.default({ rules: [] })
|
||||
.describe("Exact-literal rules committed in the comparison result."),
|
||||
|
||||
Loaded 100 of 304 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user