Skip to content

docs(resources): document static properties as a first-class MCP/OpenAPI schema source#605

Open
kylebernhardy wants to merge 5 commits into
mainfrom
docs/static-properties-mcp-openapi-1923
Open

docs(resources): document static properties as a first-class MCP/OpenAPI schema source#605
kylebernhardy wants to merge 5 commits into
mainfrom
docs/static-properties-mcp-openapi-1923

Conversation

@kylebernhardy

@kylebernhardy kylebernhardy commented Jul 24, 2026

Copy link
Copy Markdown
Member

Companion docs for harper#1921 — feat(mcp/openapi): drive schemas from a programmatic Resource's static properties and harper#1933 — fix(resources): URL attribute-suffix routing for programmatic static-properties Resources. Closes HarperFast/harper#1923.

What changed in core

static properties has been the canonical class-level metadata API since #1167, and the docs already described it — but only table-backed Resources actually got rich schemas from it. The MCP tool builder and the OpenAPI generator both read the internal attributes Array, so a programmatic Resource declaring a bare static properties literal produced an empty property set. 5.2.0 fixes that, and also resolves GET /Resource/id.property against static properties.

So this is less "document a new API" than "the documented API now works for the case the docs implied it did" — plus the vocabulary, constraint, and per-surface details an author actually needs.

Changes

  • reference/resources/resource-api.md — the bulk of it. Under static properties: which surfaces derive from it (and which explicitly don't), a JSON Schema vocabulary subsection, a table of every fragment key Harper reads with its per-surface behavior, resolution notes for unions / item-less arrays / optional properties / static primaryKey, and a nested-object + array-of-object example.
  • learn/developers/mcp-and-openapi-metadata.mdx — Path B gets the emitted tools/list JSON, a vocabulary warning, a Path-B authoring rubric, and debugging guidance.
  • reference/mcp/tool-metadata.md, reference/mcp/tools-and-resources.md, reference/mcp/overview.md — the three places that said input schemas come from Table.attributes.
  • reference/rest/overview.md, reference/rest/querying.md — the GET /MyTable/123.propertyName rule, in both places it's stated.

Review history worth knowing about

The first commit (7fea797) was fact-checked against the implementation and 15 of its claims were wrong; 6cfe041 and 556106b correct them. If you reviewed the first commit, please re-read rather than diffing. The corrections that matter most, because each is a per-surface asymmetry a reader would not guess:

  • harper://schema/{db}/{table} is keyed by database/table, so a table-less programmatic Resource never appears there. It is not one of the surfaces.
  • Only get_* has a record-shaped outputSchema. search_* has none; the write verbs advertise fixed {id} / {ok} / {deleted} envelopes. update_* and patch_* are mutually exclusive.
  • Nullability: MCP re-expands to type: ["string","null"] and never emits a nullable keyword; OpenAPI 3.0.3 drops it at the top level and emits it only inside nested objects.
  • enum / format / const and per-property hidden are honored at the top level only; inside a nested object or an array's items, MCP keeps them and OpenAPI drops them.
  • The OpenAPI path parameter and the verb-description sentence read the class-level static primaryKey (default 'id'), not the fragment's primaryKey: true. Both examples now declare both.
  • Capitalized GraphQL type names (String, Int) do map correctly; the real hazard is a name in neither vocabulary ('Text'), where MCP coerces to string and OpenAPI emits {}.

Open items for a human

  • Two pre-existing docs claims were false and are corrected here, which widens this PR beyond #1923. mcp/overview.md and the metadata guide both said attribute_permissions narrows MCP input schemas per user — every derivation call site passes undefined; schemas are built once at registration and are caller-agnostic (RBAC filters the tool list, and is enforced at call time). Shout if you'd rather split those out.
  • <VersionBadge version="v5.2.0" /> is the first v5.2.0 badge in the repo, and there's no release-notes/v5-lincoln/5.2.md yet — I didn't create one for a single entry. Happy to add it once that page exists.
  • Three core behaviors documented here look like bugs, not intent — filed separately if the team agrees: verb tools for table-less Resources are listed only to super-users (makeVisibleTo returns false without a db/table), hidden on a nested sub-property isn't suppressed (OpenAPI even emits hidden: true as a schema key), and the two surfaces disagree on unknown type names. The docs describe current behavior; if any of these get fixed, these pages need a follow-up.

Verified with npm run build (clean; the one reported broken anchor is pre-existing on release-notes/v5-lincoln/5.1) and npm run format:check.

PR description generated by kAIle (Claude Opus 4.8).

…API schema source

Harper 5.2.0 makes a programmatic Resource's `static properties` drive the
MCP tool schemas, the OpenAPI document, and `harper://schema` — previously
those surfaces read only the internal `attributes` Array, so a bare
`static properties` declaration produced a skeletal `{ type: 'object' }`.
5.2.0 also resolves `GET /Resource/id.property` against `static properties`.

Documents the authoring path: the JSON Schema vocabulary (lowercase types,
distinct from the capitalized GraphQL names), the full fragment key list,
how unions/item-less arrays/optional properties resolve, and a nested +
array-of-object example.

Companion to HarperFast/harper#1921 and #1933; closes HarperFast/harper#1923.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188G62J9fZQg4J9rVuqLzjy

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the documentation across several files to reflect changes in version v5.2.0, specifically detailing how programmatic Resources declaring static properties now receive rich schema generation across MCP, OpenAPI, and schema introspection surfaces. It adds comprehensive explanations of JSON Schema vocabulary, an authoring rubric, and examples. Feedback on the changes suggests updating the get method in the OrderSummary example to be a static method to maintain consistency with Harper Resource documentation standards.

Comment thread reference/resources/resource-api.md Outdated
@github-actions
github-actions Bot temporarily deployed to pr-605 July 24, 2026 21:23 Inactive
@github-actions

Copy link
Copy Markdown

🚀 Preview Deployment

Your preview deployment is ready!

🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-605

This preview will update automatically when you push new commits.

nizzlenitz and others added 2 commits July 24, 2026 15:43
Adversarial fact-check of every technical claim in the previous commit
against harper core turned up 15 defects. Corrections:

- harper://schema is keyed by database/table, so a table-less programmatic
  Resource never appears there; dropped it from the surface list.
- Only get_* has a record-shaped outputSchema. search_* has none; the write
  verbs advertise fixed {id}/{ok}/{deleted} envelopes. update_*/patch_* are
  mutually exclusive.
- Nullability: MCP re-expands to type:['string','null'] and never emits a
  `nullable` keyword; OpenAPI drops it at the top level and emits it only
  inside nested objects.
- enum/format/const and per-property hidden are honored at the top level;
  nested/items keep them on MCP but not OpenAPI. Timestamp keys are MCP-only.
- The OpenAPI path parameter and the verb-description sentence read the
  class-level `static primaryKey`, not the fragment flag — document both.
- Capitalized GraphQL type names DO map correctly; the real hazard is a name
  in neither vocabulary, where the two surfaces disagree.
- Corrected the get_ProductInventory sample to the actual emitted shape
  (pk description override, get_attributes description, required +
  additionalProperties on the output schema).
- Added the super-user-only listing caveat for table-less Resources.
- Fixed a pre-existing claim that attribute_permissions filters tool schemas
  per-user; schemas are built once at registration and are user-agnostic.
- REST: .json/.cbor/.msgpack/.csv take precedence over a same-named property.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188G62J9fZQg4J9rVuqLzjy
…contradicted it

Conventions/consistency pass found three pages stating the old rule, none of
which the first pass touched:

- reference/mcp/overview.md said input schemas are "narrowed by your role's
  attribute_permissions" — they are not; schemas are built once at
  registration and are caller-agnostic. Corrected, plus the static properties
  source.
- reference/mcp/tools-and-resources.md said input schemas come from
  Table.attributes only.
- reference/rest/querying.md is the canonical page for the `id.property` URL
  form and still said "declared in the schema"; rest/overview.md restated the
  same rule 26 lines above the one this PR had updated.

Also: moved the Path B version badge off the (unchanged) section heading onto
the paragraph it describes, and gave the ProductInventory example the
`static primaryKey` its sample output already assumed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188G62J9fZQg4J9rVuqLzjy
@kylebernhardy

Copy link
Copy Markdown
Member Author

The three core-behavior gaps flagged in the description are now filed against v5.2:

These pages document current behavior, so each of those fixes will need a small follow-up edit here — the per-surface caveats in the fragment-key table collapse back to single rows once #1942 lands, the "top-level only" wording goes away with #1941, and the listing-visibility note changes with #1940. Each issue names the specific docs text to revisit.

Comment generated by kAIle (Claude Opus 4.8).

@github-actions
github-actions Bot temporarily deployed to pr-605 July 24, 2026 21:49 Inactive
@github-actions

Copy link
Copy Markdown

🚀 Preview Deployment

Your preview deployment is ready!

🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-605

This preview will update automatically when you push new commits.

… examples

The examples in this section defined `get` as an instance method while the
page's own primary examples (and the Resource Static Methods section above)
use `static get(target)` — the documented override point. Converts all five,
not just the one added by this PR.

Flagged by gemini-code-assist on documentation#605.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188G62J9fZQg4J9rVuqLzjy
@github-actions
github-actions Bot temporarily deployed to pr-605 July 24, 2026 21:57 Inactive
@github-actions

Copy link
Copy Markdown

🚀 Preview Deployment

Your preview deployment is ready!

🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-605

This preview will update automatically when you push new commits.

…ing instance

Verified by booting Harper with a programmatic static-properties component and
hitting the endpoints:

- The route is `/openapi`, not `/openapi.json` — the latter 404s. Four
  references corrected, two of which this PR introduced. The integration suite
  uses `/openapi`, which is the authority.
- "OpenAPI is typically exposed to anyone reachable on the HTTP port" overstates
  it: a default `prod` install returns 403 for an unauthenticated GET. What is
  actually true, and what matters for the @hidden guidance, is that the document
  is global — no per-user filtering — so any authorized caller sees every
  docstring regardless of their own read permissions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188G62J9fZQg4J9rVuqLzjy
@github-actions

Copy link
Copy Markdown

🚀 Preview Deployment

Your preview deployment is ready!

🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-605

This preview will update automatically when you push new commits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[docs] Document programmatic Resource static properties for MCP/OpenAPI schema authoring

2 participants