feat(mcp): expose current environment diagnostics#8726

closed
t3code/codex-turn-mapping
agents/mcp-environment/read
openedAug 30, 2026byJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 4d ago

Problem

Agents could not inspect the identity or safe operational state of the T3 environment backing their current conversation without relying on unrelated settings payloads.

Change

Add t3_environment_read, backed by the existing environment, provider registry, and settings services. It returns a paged provider-health summary, explicit safe capabilities, and a narrow server-owned preference projection.

Behavior

The tool is current-environment only, read-only, and closed-world. It reads cached provider state without refreshing, installing, or authenticating providers. Human-readable diagnostics are Unicode-safe and bounded; credentials, provider configuration, model inventories, paths, raw diagnostics, and administrative fields never enter the result. Environment mismatch is checked before provider/settings reads, and finite public failure codes do not expose internal causes.

Focused validation

  • allowlisted service projection, capability/mismatch denial, unavailable-service, and Unicode-bound tests
  • production HTTP tools/list root-object and annotation coverage
  • Claude read-only allowlist and shared presentation coverage
  • 6 focused files / 18 tests passed across the complete native stack
  • server and contracts scoped typechecks, targeted formatting, and git diff --check

Dependency

Bottom layer of native stack 8782, based on t3code/codex-turn-mapping at the pinned rollout target. #8728 adds the dependent preference mutation.

Implemented by GPT-5.6-Sol via Codex in T3 Code.

Synced from GitHub

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

โš™๏ธ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fce18fe1-806e-416c-b85d-d5ae444adbf7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ๐Ÿ” Trigger review

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


Comment @coderabbitai help to get the list of available commands.

MmacroscopeappBotreviewed ยท 2 inline threadsAug 30, 2026
Synced from GitHub

Reviewed the new EnvironmentMcpService, its toolkit wiring, and the environmentMcp contract against the Effect service conventions. Module layout (namespace imports of Effect subpaths, inline Context.Service interface, make then layer, dependencies acquired via yield*) looks right; two findings on the error model.

Posted via Macroscope โ€” Effect Service Conventions

packages/contracts/src/environmentMcp.tslines 141โ€“148Outdated ยท 5a4a6afOpen thread
โ€ฆโ€ฆ@@ -0,0 +1,150 @@
145+ "settings_unavailable",
146+ "operation_failed",
147+ ]),
148+ message: Schema.String,
MmacroscopeappBoton lines 141โ€“148Aug 30, 2026

src/environmentMcp.ts:148

EnvironmentMcpFailure carries an unstructured message as its only payload beyond code, so the message is not derived from structural attributes and there is nowhere to preserve the underlying failure.

Consider dropping the message field, adding cause: Schema.optional(Schema.Defect()), and deriving message in an override get message() from code. Note that environment_unavailable is currently used for two genuinely different situations in EnvironmentMcpService.read (descriptor unavailable vs. credential/environment mismatch) with different caller-visible text โ€” those should become distinct codes so each keeps its own derived message.

No longer relevant as of 0a9b7e711700353973e94c053c88f3f78cc1e628

Addressed in 0a9b7e7117. EnvironmentMcpFailure now carries only a finite structural code; its safe message is derived from that code. Credential/environment mismatch has its own environment_mismatch code, distinct from descriptor/service unavailability. I intentionally did not add Schema.Defect or a raw cause to this public MCP contract because those values can expose stacks, paths, and configuration.

apps/server/src/mcp/EnvironmentMcpService.tslines 32โ€“46Outdated ยท 5a4a6afOpen thread
โ€ฆโ€ฆ@@ -0,0 +1,217 @@
43+ ? Effect.failCause(cause).pipe(Effect.orDie)
44+ : Effect.fail(failure(code, message)),
45+ ),
46+ );
MmacroscopeappBoton lines 32โ€“46Aug 30, 2026

failure is a helper whose only behavior is (...args) => new EnvironmentMcpFailure({ ...args }), which the conventions ask us not to add โ€” constructing the error at each failure boundary keeps its attributes visible. Separately, unavailable discards the wrapped failure: the caught cause never reaches the returned error, so the real underlying error and stack are lost for every *_unavailable code.

Consider inlining new EnvironmentMcpFailure({ ... }) at the failure sites and threading the caught cause through (e.g. Cause.squash(cause)) as a cause attribute once the contract carries one (see the comment on packages/contracts/src/environmentMcp.ts).

Posted via Macroscope โ€” Effect Service Conventions

Addressed in 0a9b7e7117. The constructor-only failure helper was removed and each boundary now constructs EnvironmentMcpFailure with its visible structural code. The remaining unavailable helper is the common interruption-preserving redaction boundary: interruption is re-raised, while dependency failures map to a finite public reason without serializing internal stacks, paths, or configuration onto the MCP wire.

Synced from GitHub

Thread transfer impact

โš ๏ธ The latest CI run did not produce a thread transfer result for 512391d.

This comment will update automatically after the next completed run.

Synced from GitHub

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable โ€” This PR introduces and registers a new authenticated MCP capability that exposes environment, provider health, authentication summaries, and server-owned preferences to provider sessions. Its read-only and allowlisted design limits risk, but the new production data boundary and session-tool availability require human review.

Notes:

  • Diff unchanged. Approvability was decided on eligibility alone.

No code changes detected at 512391d. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

apps/server/src/mcp/EnvironmentMcpService.tsline 124Outdated ยท 26f239aOpen thread
โ€ฆโ€ฆ@@ -0,0 +1,196 @@
121+ if (!scope.capabilities.has("orchestration")) {
122+ return yield* new EnvironmentMcpFailure({ code: "capability_denied" });
123+ }
124+ const [descriptor, providers, settings] = yield* Effect.all([
MmacroscopeappBoton line 124Aug 30, 2026

๐ŸŸก Medium mcp/EnvironmentMcpService.ts:124

A credential with a mismatched scope.environmentId receives provider_registry_unavailable or settings_unavailable when either dependency fails, instead of environment_mismatch, disclosing operational health outside its environment. Effect.all fetches provider and settings data before the binding check; validate the descriptor and environment binding before accessing those dependencies.

๐Ÿš€ Reply "fix it for me" or copy this AI Prompt for your agent:
text
1In file @apps/server/src/mcp/EnvironmentMcpService.ts around line 124:
2
3A credential with a mismatched `scope.environmentId` receives `provider_registry_unavailable` or `settings_unavailable` when either dependency fails, instead of `environment_mismatch`, disclosing operational health outside its environment. `Effect.all` fetches provider and settings data before the binding check; validate the descriptor and environment binding before accessing those dependencies.

Fixed at c24bb8ce3264322a05c17cb3c3408c0ad5178854. The service now loads and validates the running environment descriptor before starting provider-registry or settings reads. The focused mismatch test uses read counters and proves both scoped dependencies remain unread when the credential environment differs; its partial services are typed Layer.mock fixtures. EnvironmentMcpService.test.ts passes 4/4 and the scoped server typecheck exits successfully.

MmacroscopeappBotreviewed ยท 1 inline threadAug 30, 2026
Synced from GitHub

One retained finding: EnvironmentMcpFailure still drops the underlying failure when the service wraps a real error. See the inline comment.

Posted via Macroscope โ€” Effect Service Conventions

apps/server/src/mcp/EnvironmentMcpService.tslines 37โ€“41Outdated ยท 26f239aOpen thread
โ€ฆโ€ฆ@@ -0,0 +1,196 @@
38+ (cause): Effect.Effect<never, EnvironmentMcpFailure> =>
39+ Cause.hasInterrupts(cause)
40+ ? Effect.failCause(cause).pipe(Effect.orDie)
41+ : Effect.fail(new EnvironmentMcpFailure({ code })),
MmacroscopeappBoton lines 37โ€“41Aug 30, 2026

unavailable still discards the caught cause: for environment_unavailable, provider_registry_unavailable, and settings_unavailable the wrapped error and its stack are lost, leaving only the code. When wrapping a real failure, the immediate underlying error should be preserved as cause alongside the structural fields.

Consider adding an optional cause: Schema.optional(Schema.Defect()) to EnvironmentMcpFailure in packages/contracts/src/environmentMcp.ts (optional because capability_denied and environment_mismatch legitimately have no underlying failure), and passing Cause.squash(cause) here: Effect.fail(new EnvironmentMcpFailure({ code, cause: Cause.squash(cause) })). Keep the message getter derived from code only, so no raw defect text reaches the wire.

Posted via Macroscope โ€” Effect Service Conventions

Declining this public-contract change. EnvironmentMcpFailure intentionally exposes only a finite code and code-derived safe message; adding Schema.Defect or a squashed cause would serialize arbitrary stack, path, provider, or configuration details across MCP. The unavailable helper preserves interruption semantics while redacting operational causes at this trust boundary. Server-side diagnostics can remain internal without widening the wire schema.

CcursorBotreviewed ยท 1 inline threadSep 5, 2026
Synced from GitHub

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit 000d34359ce5fc78b4c6d73dcbae3b9bb102d9fd. Configure here.

apps/server/src/mcp/EnvironmentMcpService.test.tsline 21Outdated ยท 000d343Open thread
โ€ฆโ€ฆ@@ -0,0 +1,279 @@
18+import { ProviderRegistry } from "../provider/Services/ProviderRegistry.ts";
19+import { makeProviderRegistryLayer } from "../provider/testUtils/providerRegistryMock.ts";
20+import * as ServerSettings from "../serverSettings.ts";
21+import { EnvironmentMcpService, layer } from "./EnvironmentMcpService.ts";
CcursorBoton line 21Sep 5, 2026

Test named-imports service layer

Low Severity

The new EnvironmentMcpService test named-imports layer and stubs ProviderRegistry with as never. Server service tags are namespace-imported so callers use EnvironmentMcpService.layer and typed Layer.mock fixtures rather than a bare layer binding or an untyped stub.

Additional Locations (1)

Triggered by learned rule: Effect services: namespace import, not named import of the Tag

Reviewed by Cursor Bugbot for commit 000d34359ce5fc78b4c6d73dcbae3b9bb102d9fd. Configure here.

Fixed in 09826107612029802ec06bacfb22d8102eac9513. The test now namespace-imports both EnvironmentMcpService and ProviderRegistry, and the two cast-away service objects are typed Layer.mock fixtures. Focused diagnostics/preferences tests pass 7/7 after upper restack; canonical t3 typecheck exits 0.

Synced from GitHub

Bugbot is paused โ€” on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

Superseded as the active MCP proposal by #10560 in the rebuilt MCP stack, following the maintainer request to review thin service wrappers separately from service hardening.

This is not a full feature-parity replacement. Updates require a live full-access/default caller under the existing ThreadCommandExecutor. Provider configuration, secrets and admin controls are not exposed. No new settings store or lock service.

Closing this mixed proposal without merging. The remote branch agents/mcp-environment/read and exact head 512391db91759dc9f4a7b1e93ca3075fe1e90dea are retained so the service changes can be extracted for separate review later.