Skip to content

[MCP/OpenAPI] hidden is not honored on nested sub-properties, and leaks as a schema key in OpenAPI #1941

Description

@kylebernhardy

Follow-up from the docs pass for #1923 (documentation#605).

Problem

@hidden / hidden: true is documented as the way to keep a field out of the MCP tool descriptors and the OpenAPI document — the latter matters because /openapi.json has no per-user filtering, so anything reaching it is visible to anyone who can hit the HTTP port. That suppression is implemented only at the top level:

MCPattributeVisible is consulted in the top-level loops (components/mcp/tools/schemas/derive.ts:166, :208, :319), but attributeToProperty recurses into attr.properties with no visibility check, so a hidden sub-property is emitted with its description.

OpenAPIresources/openApi.ts:142 does if (hidden) continue; for top-level attributes only. The nested-object branch calls attributeToFragment, which does not suppress and instead emits the flag as a schema key:

// resources/jsonSchemaTypes.ts:119
if (attr.hidden) fragment.hidden = true;

So a hidden nested sub-property appears in the public OpenAPI document along with its description and a "hidden": true marker advertising that it was meant to be suppressed.

Why it matters

The docs tell authors to use @hidden for fields that should not surface publicly ("don't put secrets, internal-only commentary, or speculative prose in docstrings; use @hidden to suppress"). For a nested field that guidance currently does not hold. This is narrow — it needs a nested object whose sub-field is marked hidden — but the failure is silent and in the direction of disclosure.

Note the exposure is bounded: table-backed GraphQL nested types usually take the $ref branch in openApi.ts, so this mainly affects programmatic static properties objects and any table path that reaches the attr.properties branch.

Suggested fix

  1. Filter in the recursion: skip hidden sub-attributes in attributeToProperty (derive.ts) and in attributeToFragment's properties loop (jsonSchemaTypes.ts), rather than only in the top-level loops.
  2. Stop emitting hidden as a JSON Schema key at all — it is a Harper directive, not JSON Schema vocabulary, and it does not belong in an output document. (Check consumers first: Table.properties round-trips through fragmentToAttribute, which reads it back, so the key may need to stay in the canonical projection while being stripped on the MCP/OpenAPI emit paths.)
  3. Add unit coverage for a hidden sub-property on both surfaces — the existing tests only assert top-level suppression.

Once fixed, the "top-level only" caveats in reference/resources/resource-api.md need to come back out.

Issue generated by kAIle (Claude Opus 4.8).

Metadata

Metadata

Assignees

Labels

area:mcpModel Context Protocol (MCP) server: protocol, profiles, stdio CLI

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions