You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit ebd0dbc
Browse filesBrowse the repository at this point in the historyBrowse files
test(mcp,storage): pin read_file and create_upload_link on a booted showcase, docs and changeset (#30892)
The dogfood file drives both tools over /api/v1/mcp as a member and the
admin on the showcase's invoice graph (owner RLS, controlled_by_parent,
a receipt file field, enable.files attachments), spends links through the
real PUT route, and pins access, the binding, single use, expiry and the
upload limit. Docs list the two tools and the two routes; one changeset
for the three published packages.
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4cyqd3A7fbCWrqM3KCz
feat(mcp): an agent can read a record's files and attach a file to a record — `read_file` and `create_upload_link` over `/api/v1/mcp`, and the single-use upload link they hand out (#30892)
8
+
9
+
Clause-②: yes (widening) — two MCP tools join the HTTP surface where the storage service is installed (`read_file` under `data:read`, `create_upload_link` under `data:write`), and two public routes join the storage family: `GET` and `PUT /api/v1/storage/upload-links/:token`. Nothing that answered before answers differently.
10
+
Metadata-protocol: none
11
+
12
+
-**`read_file`** takes `objectName`, `recordId` and a file field (with `fileId` when the field holds several files) or an `attachmentId`. With neither, it lists the record's files. It answers the file's facts (id, name, type, size, the read cap that applies), a short-lived download link, and the content: a PDF's or Word document's text (extracted on the server, capped in pages and characters), an image as an MCP image block, a text file as text. A file over the cap (10 MiB) answers its facts and the download link, without content. Access follows the record: a record the caller cannot read answers `404 RECORD_NOT_FOUND`, as `get_record` does, and a file the download door would refuse answers that door's `403`.
13
+
-**`create_upload_link`** takes a record the caller may edit plus a file field or `attachments: true`. It answers a link that accepts exactly one upload for ten minutes, bound to that record and that field (or the record's attachments). The upload lands as the caller would land it: the file field is written through the record's own update door, so the field's `accept` and `maxSize`, the record's edit access and the upload size limit (`storage.max_upload_mb`, `413` before the link is spent) all apply. It writes the same `sys_file` row, size recorded, and the same `storage.upload` as any other upload, so whatever a host counts toward quota counts it. A used, expired or unknown link answers the same `404`.
14
+
-**`@objectstack/mcp`** loads `unpdf` (pinned 1.9.1) by dynamic import on the first PDF read, with pdf.js eval off; a Word document is read with `node:zlib` under a bounded inflate.
Copy file name to clipboardExpand all lines: content/docs/ai/connect-mcp.mdx
+35-2Lines changed: 35 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -244,7 +244,8 @@ decision.
244
244
245
245
## What the agent gets
246
246
247
-
Twelve tools, generated from your metadata:
247
+
Twelve tools, generated from your metadata, and two more for files where the
248
+
storage service is installed:
248
249
249
250
| Tool | What it does |
250
251
|:---|:---|
@@ -255,6 +256,38 @@ Twelve tools, generated from your metadata:
255
256
|`create_record` / `update_record` / `delete_record`| Write data |
256
257
|`list_actions` / `run_action`| Discover and invoke your business actions by name |
257
258
|`resume_run`| Finish a flow action that paused on a screen, by submitting the screen's values |
259
+
|`read_file`| Read a file a record carries: its text (text files, PDF, Word `.docx`) or the image itself, with its size, the byte cap and a short-lived download link |
260
+
|`create_upload_link`| Hand the user a single-use link that uploads one file onto a record's file field or its attachments |
261
+
262
+
### Files: `read_file` and `create_upload_link`
263
+
264
+
Both tools are registered only where the storage service is installed (they
265
+
read and write through it), `read_file` with the `data:read` scope and
266
+
`create_upload_link` with `data:write`. Neither judges access on its own: the
267
+
record is read first, exactly as `get_record` reads it, so **no access to the
268
+
record means no file**, refused with the same `RECORD_NOT_FOUND`.
0 commit comments