HarperFast / HarperFast/harper
[MCP] #1095 follow-ups: programmatic static-properties schema wiring + MCP/convergence test coverage
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 205
Description
Follow-up to #1095, which was delivered by #1167 and closed. The production functionality (GraphQL docstrings → MCP + OpenAPI descriptions, the operations catalog, `@hidden`, `outputSchema`, per-attribute descriptions, OpenAPI parity) is live. This issue tracks the remainder from the #1095 acceptance list that #1167 did not fully cover — see the audit comment on #1095 for the full checkbox-by-checkbox verdict.
## 1. Programmatic-Resource `static properties` → MCP input schema
A bare `static properties = {…}` literal on a Resource class is **not** read by MCP or OpenAPI schema derivation — both read `.attributes` (`components/mcp/tools/application.ts:1359`, `resources/openApi.ts:112`). `static properties` only enriches schemas once compiled through `defineResource`/`withSchema` (`resources/defineResource.ts:607-610`). Consequences:
- A pure programmatic Resource declaring only `static properties` (no attributes, no contract) yields a skeletal MCP `inputSchema` — the "skeletal schema for programmatic Resources" gap #1095 set out to close, still open for the bare-class path.
- There is **no lazy `Resource.properties` getter**; the attributes→properties projection is eager at `Table` construction (`resources/Table.ts:278`, `resources/databases.ts:1157`). Back-compat co-population works and is tested, but item "`Resource.properties` getter projects from `Resource.attributes` lazily" (acceptance) is unmet as written.
- No `static properties` **inheritance / spread-override** fixture (kriszyp's `CustomProduct` example). Static-field inheritance works natively in JS, but nothing verifies it, and it's moot until `static properties` feeds MCP derivation.
**Decide:** is the intended authoring path `defineResource`/`withSchema` only (in which case document that and drop the bare-literal acceptance items), or should a bare `static properties` literal also drive MCP/OpenAPI schemas? kriszyp locked in `static properties` as the canonical public API in the #1095 discussion, which points at the latter.
## 2. MCP-side + convergence test coverage
The OpenAPI side and the GraphQL parser are tested; the MCP assertions and the cross-surface convergence tests were never written:
- **Missing** — `tools/list` against a docstring-carrying fixture asserting the tool `description` includes the docstring **and** `inputSchema.properties[*].description` carry attribute descriptions. (`unitTests/components/mcp/tools/application.test.js` asserts only names/verbs; its `.description` assertions are for custom `mcpTools`.) No fixture under `integrationTests/fixtures` contains `"""` docstrings.
- **Missing** — convergence test: a Resource with descriptions surfaces them in **both** MCP `tools/list` **and** OpenAPI `/openapi.json`.
- **Missing** — a `@table` Resource with docstrings (no override) produces identical MCP + OpenAPI output to one with explicit `static description` + `static properties` (both paths converge).
- **Ineffective** — the idempotent guard (`unitTests/components/mcp/tools/operations.test.js:386-391`) only checks `!('idempotentHint' in OPERATION_DESCRIPTIONS)` (a string map); it never inspects an emitted `add_user`/`create_*` tool's `annotations`. Behavior is correct today (`IDEMPOTENT_OPERATIONS` ships empty), but nothing would catch a future mis-population — the exact safety this check was meant to provide. Rewrite it to assert the real emitted annotation is absent.
- **Partial** — operations-profile test asserts description *coverage* but not that a specific curated string lands on the right tool; back-compat is tested per-surface but not as a unified golden assertion.
## Out of scope
- Search/list return envelope → tracked in #1107.
_Issue generated by kAIle (Claude Opus 4.8)._
Contributor guide
Assessment
This issue has not been assessed yet.