feat: add Harness File Store MCP support with multipart uploads#211
feat: add Harness File Store MCP support with multipart uploads#211ritek01 wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Stale comment
Found a few architecture-standard issues on the live PR head (
c9e36c6).
file_store.list_childrenbreaks the sharedharness_executecontract because the action ignores the resource's primaryresource_idmapping.- Invalid
content_base64does not fail loud and can silently upload corrupted bytes.- The README count bump documents larger totals but still omits the new File Store resource/toolset from the discoverability tables.
- The File Store delete description points agents to
filters.force_delete, butharness_deleteonly acceptsparams.Assumptions / notes:
- Reviewed against the live PR head, not just the trigger snapshot.
- CI is green, and I also ran local verification after installing from the existing lockfile.
Verification:
pnpm test tests/client/harness-client.test.ts tests/registry/file-store-multipart.test.tspnpm typecheckpnpm buildpnpm docs:checkOne nuance from the docs check: it passes here because
scripts/generate-docs.jsonly patches numeric counts, so it does not catch the missing File Store entries in the README tables.Sent by Cursor Automation: Sunil On Demand Architecture Review
| if (rawBody !== undefined && typeof rawBody === "object" && rawBody !== null && !Array.isArray(rawBody)) { | ||
| return rawBody; | ||
| } | ||
| const id = input.folder_identifier ?? input.identifier; |
There was a problem hiding this comment.
file_store registers identifierFields: ["file_store_id"], so the generic harness_execute(resource_id=...) path maps the primary ID onto input.file_store_id before dispatch. This helper only checks folder_identifier / identifier, which means the shared execute contract is broken for list_children: a normal resource_id call is ignored unless the caller redundantly provides an undocumented alias.
Please read input.file_store_id here (or adjust the action contract / identifierFields) and add a tool-level regression test for harness_execute(..., resource_id=...).
| (typeof b.file_name === "string" && b.file_name) || | ||
| name; | ||
| if (typeof b.content_base64 === "string" && b.content_base64 !== "") { | ||
| const buf = Buffer.from(b.content_base64, "base64"); |
There was a problem hiding this comment.
Buffer.from(str, "base64") is permissive and will happily turn malformed input into garbage bytes instead of throwing. I verified locally that buildFileStoreMultipartBody(..., { content_base64: "not-base64" }) still produces a non-empty Blob, so this write path can succeed with corrupted content instead of failing loud.
Given the repo's validation standard, please validate content_base64 (for example via a canonical base64/round-trip check) before building the multipart body, and add a negative test.
| ## Resource Types | ||
|
|
||
| 169 resource types organized across 31 toolsets. Each resource type supports a subset of CRUD operations and optional execute actions. | ||
| 170 resource types organized across 32 toolsets. Each resource type supports a subset of CRUD operations and optional execute actions. |
There was a problem hiding this comment.
The numeric totals were updated here, but the README still never documents the new surface area: there is no File Store section in the Resource Types catalog below, and there is still no file_store row in the "Available toolset names" table.
pnpm docs:check won't catch this because scripts/generate-docs.js only patches counts. Please either document the new resource/toolset in the README tables or strengthen the docs parity check so discoverability stays consistent with the registry.
Expose list/get/create/update/delete and folder list_children via the registry; teach HarnessClient to post FormData without forcing JSON. Co-authored-by: Cursor <cursoragent@cursor.com>
c9e36c6 to
3451df9
Compare
There was a problem hiding this comment.
- Critical —
src/registry/toolsets/file-store.ts:51-60,src/registry/index.ts:691-695,tests/registry/file-store-multipart.test.ts:55-68:buildFileStoreMultipartBody()silently falls back to"Root"whenparent_identifieris omitted, even forupdate. Because FormData bodies now bypass the generic required-field validation, a rename/content-only update turns into a move-to-root request instead of failing loud. The new tests only cover the explicit-parent case, so this regression is currently unguarded. - Important —
src/registry/toolsets/file-store.ts:259-260,src/tools/harness-delete.ts:20-27: the new delete description tells callers to usefilters.force_delete, butharness_deletedoes not expose afiltersargument; onlyparamsis accepted. Agents followingharness_describewill generate an invalid tool call for the one File Store delete override this PR adds. - Important —
README.md:955-957,README.md:1403-1475,src/registry/index.ts:147-158: the README increments the counts to 170 resource types / 33 toolsets, but it still never documents the actual newfile_storetoolset or resource name inAvailable toolset namesor under## Resource Types. That leaves the docs internally inconsistent: users can see the counts changed, but still cannot discover the new toolset/resource name to enable, filter, or query it.
Sent by Cursor Automation: Sunil On Demand Architecture Review
There was a problem hiding this comment.
High
src/registry/toolsets/file-store.ts:list_childrenbreaks the genericresource_idcontract.harness_executemapsresource_idtofile_store_id, but the shorthand folder builder ignores that field and only acceptsfolder_identifier/identifier, so the primary execute path fails unless callers duplicate the folder id inparams.src/registry/toolsets/file-store.ts: update requests silently fall backparent_identifierto"Root". The File Store update API requires the actual parent identifier, so a rename or content replace without that field can move the node to the root instead of failing fast.src/registry/toolsets/file-store.ts: malformedcontent_base64is accepted and decoded into garbage bytes.Buffer.from(..., "base64")does not validate input, so this violates the server’s fail-loudly standard and can corrupt uploaded content.
Assumptions
- Reviewed the live PR head
3451df9. - CI is green.
- I couldn’t rerun local
vitest/tscin this workspace becausenode_modulesis not present.
Change summary
- The multipart upload plumbing and new
file_storeregistry entry fit the existing direction, but the three issues above still keep it short of Sunil’s shared-contract / fail-loudly standards.
Sent by Cursor Automation: Sunil On Demand Architecture Review
| } | ||
|
|
||
| const parentIdentifier = | ||
| (b.parent_identifier ?? b.parentIdentifier ?? "Root") as unknown; |
There was a problem hiding this comment.
Defaulting a missing parent_identifier to "Root" is risky for updates. The File Store update API requires the real parentIdentifier, so a caller that only wants to rename a file or replace its content will silently move the node to the root instead of getting a validation error. This should fail loud on update when the parent id is missing, rather than substituting a different location.
| (typeof b.file_name === "string" && b.file_name) || | ||
| name; | ||
| if (typeof b.content_base64 === "string" && b.content_base64 !== "") { | ||
| const buf = Buffer.from(b.content_base64, "base64"); |
There was a problem hiding this comment.
Buffer.from(value, "base64") does not validate that the input is actually valid base64; malformed strings decode into garbage bytes instead of throwing. That means content_base64 can succeed with corrupted uploads, which cuts against the server’s fail-loudly standard. Please validate the encoding before constructing the blob and add a focused regression test for the malformed-input case.
| if (rawBody !== undefined && typeof rawBody === "object" && rawBody !== null && !Array.isArray(rawBody)) { | ||
| return rawBody; | ||
| } | ||
| const id = input.folder_identifier ?? input.identifier; |
There was a problem hiding this comment.
This shorthand path ignores the resource’s primary identifier field. harness_execute maps resource_id to file_store_id for file_store, but buildFolderNodesBody() only looks at folder_identifier / identifier, so harness_execute(resource_type="file_store", action="list_children", resource_id="...") still fails unless the caller redundantly passes the folder id again in params. That breaks the shared generic-tool contract; this builder should honor input.file_store_id as well.
There was a problem hiding this comment.
Findings
src/registry/toolsets/file-store.ts:buildFileStoreMultipartBody()still defaults a missingparent_identifiertoRootonupdate. The Harness File Store update API expects the real parent identifier, so a rename/content-only update can silently move a node to the root instead of failing loudly.src/registry/toolsets/file-store.ts:content_base64is still not validated beforeBuffer.from(..., "base64"). Node accepts malformed input here; I reproducedcontent_base64: "not-base64"producing a 7-byte blob, so corrupted uploads can succeed instead of surfacing an error.src/registry/toolsets/file-store.ts:list_childrenstill breaks the shared generic-tool contract.harness_execute(resource_id=...)maps the primary ID tofile_store_id, butbuildFolderNodesBody()only readsfolder_identifier/identifier, so the execute path fails unless callers redundantly pass the folder ID again inparams.README.md: the counts were updated, but the newfile_storeresource/toolset still is not documented in the Resource Types or Available toolset tables, so README discoverability is out of sync with the registry.
Assumption
I checked the Harness File Store docs while reviewing this; they describe parentIdentifier as required on update, which is why I’m treating the Root fallback as behaviorally unsafe rather than just a docs mismatch.
Change Summary
The multipart plumbing in src/client/harness-client.ts and src/registry/index.ts looks good. The remaining gaps are in the file_store toolset contract plus README parity.
Verification
pnpm typecheck
pnpm exec vitest run tests/registry/file-store-multipart.test.ts tests/client/harness-client.test.ts
pnpm exec vitest run tests/tools/tool-handlers.test.ts -t "harness_execute"
Sent by Cursor Automation: Sunil On Demand Architecture Review


Expose list/get/create/update/delete and folder list_children via the registry; teach HarnessClient to post FormData without forcing JSON.
This pull request adds full support for Harness File Store operations, including robust handling of multipart/form-data requests, and introduces a new
file_storetoolset for managing files and folders. The changes ensure that multipart bodies are properly detected and handled throughout the client, avoiding incorrect content-type headers and unnecessary JSON serialization. Validation and body injection logic are also updated to account for FormData bodies. Comprehensive tests are added to verify correct behavior.Harness Client: Multipart/form-data Handling
isFormDataBodyutility and updated all request logic inHarnessClientto detectFormDatabodies, avoid forcing JSON or YAML content-type, and ensure multipart requests are sent correctly. Also updated logging to redact multipart bodies and fetch logic to use the correct body type. [1] [2] [3] [4] [5] [6] [7]Registry: File Store Toolset and Multipart-aware Logic
file_storetoolset in the registry, with resource definitions, operations for list/get/create/update/delete, and action for listing folder children. Includes body builders for multipart requests and comprehensive field schemas. [1] [2] [3] [4]Tests
Description
Type of Change
Checklist