Skip to content

content(blog): agent-operable apps — every object and action an MCP tool under the user's permissions (post 4, slot 2026-10-20) - #237

Open
objectstack-fleet[bot] wants to merge 4 commits into
mainfrom
claude/pm-dispatch-objectos-ju9td1-issue-230
Open

objectstack-fleet[bot] wants to merge 4 commits into
mainfrom
claude/pm-dispatch-objectos-ju9td1-issue-230

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #230

Re-slotted to Tue 2026-10-20 (head d010082). Daily cadence switched on 2026-10-17 (ruling B, #267), and the queued posts were re-dated onto consecutive days from 10-18. The re-date changes the post's date and the back-linked updated stamps, with zh-Hant regenerated. Gates are green on the new head. Slot dates further down this body are superseded by this line. Merge on 2026-10-20 only.

Post 4 of the positioning wave (#209), slot Tuesday 2026-10-27. Merge on the slot day and no earlier: www AGENTS.md says "merge time is publish time".

Re-slot. The post was slotted for Tue 10-20. The wave slipped one week when post 1 missed 10-08 and landed on 10-15 as #212, so post 4 moved to the next free slot. Commit 7f1c50b moves date to 2026-10-27 in all five locale files, and moves the sibling's updated to 2026-10-27T10:00:00+08:00. 6 lines change. Gates on that commit all exit 0: content:lint, check, build (893 pages, tree clean) and seo:smoke.

Branch. This branch holds three commits replayed onto main @ 51147d6. The replay is patch-equal to lin/post-base..lin/issue-230, with byte-identical diffs. Head 813f3de.

What this PR adds

New post content/blog/agent-operable-apps-mcp-tools-user-permissions/ in en, zh-Hans, zh-Hant (generated by pnpm gen:zh-hant), ja and ko, date: 2026-10-27, no updated, topic: ai-agents, audience: developer. Title as ruled: Agent-Operable Apps: When Every Object and Action Is an MCP Tool Under the User's Permissions. Promise: Agent-operable.

  • Ownable concept: same list, different answers — the tool list is identical for every user; the permission decision is made on every call. The number behind it: twelve tools, identical for both users; three refusals, each decided at the call.
  • Proof artifact (rule 5), real and reproducible: a scaffolded expense desk (npm create objectstack@latest expense-desk, Create ObjectStack v17.7.0, published packages, Node 22), one object, one operation: 'update' action gated by a capability, two permission sets, two users created through the documented admin route, each holding an API key minted on their own account, a JSON-RPC probe against /api/v1/mcp. Output quoted trimmed, not rewritten.
  • Honesty (rule 7): a section steelmanning hand-built tool APIs, and a section on what this does not solve: prompt injection, agent judgment, the cost of a wide tool surface, plus the docs' warning that action bodies run with the app's authority.
  • ObjectOS appears once, as T4_STANDALONE + E1 verbatim from src/lib/positioning.ts, per locale.
  • CTA: the README's agent path (npm create objectstack@latest, the agent, npx os dev --ui, then the README's own claude mcp add line). os start appears 0 times. No token figures. No competitor claims.
  • Assets: cover-en.svg / cover.svg (1200×675), same-list-two-answers-en.svg / .svg (1200×700), action-gate-en.svg / .svg (1200×560). ja and ko use the English set, zh-Hans/zh-Hant the Chinese pair. Root tag closes within the first 460–750 bytes of each file, inside Astro's 1 KB probe window.

Updated post content/blog/ontology-mcp-agent-tools/index.mdx (en only; the post has no other locale):

  • updated: 2026-10-27T10:00:00+08:00; slug and date unchanged.
  • The closing section is rewritten to the README's renamed section, "Agents are the first users", quoting its sentences rather than the old "generated from it rather than hand-written per agent … approval steps … included" paraphrase, and links the new post.
  • The CTA "Point your coding agent at the ObjectStack spec" becomes the README agent path, with the claude mcp add line.
  • One sentence corrected for the positioning boundary: "The semantic layer was an internal asset that applications consumed" → "The ontology was an internal asset that applications consumed" (the sentence is about the ontology; the site no longer calls the ontology a semantic layer).

Claims and their sources

Claim in the post Source
"Objects are tools, actions are tools, permissions decide what an agent may call, and audit records what it did"; MCP server at /api/v1/mcp, on by default; "under the same permissions and RLS as a human"; each deployment is its own OAuth server; API key for headless; objects exposed automatically, actions opt in with ai: { exposed: true } objectstack README.md:148-164 at 8caa131e, quoted whole as a blockquote
T1–T4 (T4 as T4_STANDALONE), E1 src/lib/positioning.ts
"Every call runs as the caller. The MCP bridge resolves the same ExecutionContext as a REST request…"; OAuth scopes data:read / data:write / actions:execute narrow the toolset; API-key and session callers get the full set; the "fixed ~10-tool spine with the object name as a parameter"; twelve tools; sys_* blocked fail-closed; "default exposure policy (v1)", "not a ceiling", #3167; stdio advice "mint the key on a user with exactly the access it should have"; client-side confirmation; POST /api/v1/keys; the anonymous probe returns 401 objectstack content/docs/ai/connect-mcp.mdx at 8caa131e
The five-step action gate (principal, ai.exposed fail-closed, requiredPermissions, record under RLS, body runs with the app's authority and is audit-logged); "Action bodies run as trusted code"; "the boundary is therefore at invoke time, not inside the body"; "ai.exposed is the security decision"; ai.description of at least 40 characters objectstack content/docs/ai/actions-as-tools.mdx at 8caa131e
requiredPermissions is a dual-surface gate; the REST route answers 403 content/docs/ui/actions.mdx
POST /api/v1/auth/admin/create-user, set-user-password; grants as sys_user_permission_set rows content/docs/permissions/authentication.mdx, permissions/permission-sets.mdx
Seeded dev admin, banner text, --compile, os dev behaviour observed output, consistent with content/docs/deployment/cli.mdx
Everything in a text code block the run's own output (scratchpad/issue-230/proof/*.log, server*.log, validate*.log, scaffold.log)

Claims the post explicitly marks as the docs' and not measured: the audit-log step (5) and the OAuth-scope narrowing of the tool list (the run used API keys).

The run, in order (what the transcript actually did)

  1. npm create objectstack@latest expense-desk (an earlier agent-desk scaffold was discarded for the name). npm run validate: 49 rules, passed, one advisory about owner-only read for the submitter set (quoted in the post).
  2. npx objectstack dev --no-watch -p 3230, first boot, no ai block. Anonymous probe 401 with www-authenticate naming the deployment's /.well-known/oauth-protected-resource; that document names /api/v1/auth as the authorization server with scopes data:read data:write actions:execute offline_access.
  3. Admin sign-in; self-registration refused (SELF_REGISTRATION_CLOSED, posture invite_only); Ada and Sam created via admin/create-user, passwords set without the forced change, expense_desk_approver granted to Ada and expense_desk_submitter to Sam as sys_user_permission_set rows; each user minted an osk_ key via POST /api/v1/keys.
  4. As Ada: initialize (serverInfo objectstack 17.7.0), tools/list (12), list_objects (2), list_actions → [].
  5. ai block added, validate passed, restart without --compile: the runtime warned the artifact was stale and served the old one; run_action answered "is not exposed to AI — the app author must opt it in…" for both users. Recorded as what happened; the post's "before the opt-in" block uses the clean ablation in step 7, whose text is identical.
  6. Restart with --compile (exposed present in dist/objectstack.json): Sam create_record → id 9vBXoTWICx4MQupC; Sam/Ada query_records → 1 row each; Ada list_actions → approve_claim; Sam list_actions → []; Sam run_action → "requires capability [approve_claims] — caller is missing [approve_claims]"; Ada run_action → ok, status: "approved", updated_by Ada, owner_id Sam; Sam delete_record → "You do not have permission to perform this action…"; Sam get_record → his record, approved. A refused call is HTTP 200 with isError: true (checked with -w %{http_code}).
  7. Ablation: ai block removed (grep count 0), restart with --compile (exposed count 0 in dist) → Ada list_actions [], Ada run_action "not exposed"; block restored (count 1), restart with --compile (count 2) → Ada list_actions lists approve_claim. Server stopped by PID afterwards; no process of this run left.

Gates, on b567417 (all through scripts/pm/os-verify-lock.sh, slot www-230, VERDICT command-exit 0, held 121 s, waited 0 s, shared-box seconds)

Gate Exit Verdict line
pnpm content:lint 0 ✓ content lint passed (352 files, 44 glossary terms checked)
pnpm content:lint --published 0 same line, same advisories
pnpm check 0 Result (152 files): 0 errors, 0 warnings, 0 hints
pnpm build 0 ✓ zh-Hant: generated 48, kept 0 hand-maintained · svg-text-fit: OK (124 SVGs, 2722 text boxes…) · [build] 893 page(s) built in 72.33s · ✓ content lint --dist passed (491 built blog pages checked) · CJK line breaks, quotation marks and links passed · tree clean afterwards
pnpm seo:smoke 0 SEO smoke test passed (883 HTML pages checked; 582 sitemap URLs against 301 noindexed pages; 5526 canonical/hreflang annotations; 16704 prose blocks language-checked; 94702 internal links redirect-checked)
pnpm svg:lint 0 svg-text-fit: OK — … (124 SVGs, 2722 text boxes, 185 filtered elements, tolerance 2px, 1 allowlisted)

Advisories, verbatim (all expected for a slot-day post with a ruled title): Date is in the future: Tue Oct 20 2026 … ×5 (now Oct 27 after the re-slot); Title is 93 columns wide (93 chars); search results show about 60, so the end is cut (en; zh-Hans 63, zh-Hant 65, ja 111, ko 78); Description is 207 columns wide (207 chars); search results show about 160… (en; zh-Hans 161, zh-Hant 165, ja 225, ko 204 — all ≤ 230 chars); content/blog/ontology-mcp-agent-tools/index.mdx: Updated is in the future: 2026-10-20.

Browser check (final build, dist/ served by astro preview, Playwright Chromium at /opt/pw-browsers/chromium)

scratchpad/issue-230/shots.mjs: 413 of 413 assertions pass (shots/results.json). The article in en, zh-Hans, ja and ko at 1366×625, 1440×750, 1512×830, 1920×945, 390×844 and 360×800, light and dark (prefers-color-scheme, which the site honours), lazy images scrolled into view: HTTP 200, scrollWidth equal to the viewport, no element past the viewport outside a scroll container, cover renders, every body image loads, no repeated image. The updated post in en at 1440 and 390 (it has no zh-Hans). The en blog list at 1440 and 390 links the new post. Element crops of the table, both diagrams and the first code block at 360 and 1366 in en and zh-Hans. The six served SVGs opened at native size: lowest text contrast 5.22:1 (en and zh table diagrams, "ExecutionContext → RBAC · RLS · FLS"), 5.42:1 (covers), 0 text nodes on a dark fill.

The check found one defect, fixed in b567417: the "not attempted" / "未尝试" cells of the two table diagrams read 3.74:1; they now use the diagram's small-text grey, 6.2:1 on white. Looked at: the folds at every width in en, zh-Hans at 1440 and 360 dark, ja at 390, ko at 1920 dark and 390 dark, the phone table (horizontal scroll container, as the site's phone rule gives every post), the diagrams at 328 px (the known site-wide phone size), the covers and diagrams at native size, and the updated post at 1440 and 390 with its "Updated Oct 20, 2026" line.

Deviations and notes

  • updated on the sibling is 2026-10-27T10:00:00+08:00, the publish day, as ruled.
  • The card asked for the updated post in zh-Hans too; it exists only in en, so only en was checked.
  • The run's dev server was the scaffold's own os dev on port 3230 (the README's step says 3000; the port is a container choice and the post prints what the banner printed).
  • Interactive OAuth sign-in was not exercised (no browser-based MCP client here); the post shows the 401 challenge and the discovery documents instead and says the run stayed on API keys.
  • The zh-Hans source avoids one locative 里 (四个承诺中的第三个) because the zh-Hant generator's whitelist did not claim it and the whitelist is site code, out of scope.
  • Commit trailers are the repo's model-free pair, as on the sibling commits.

Acceptance notes (observations, not filed)


Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr

objectstack-fleet Bot and others added 3 commits October 15, 2026 11:46
…ool under the user's permissions (slot 2026-10-20)

Post 4 of the positioning wave (#230). New post in en, zh-Hans, zh-Hant
(generated), ja and ko, with two covers and two diagrams in English and
Chinese. The proof artifact is a real run on the published 17.7.0 packages:
a scaffolded expense desk, two users with their own API keys, the same
twelve tools, one action refused for the submitter and permitted for the
approver, and the action absent until its author opts it in.

Also in this PR: ontology-mcp-agent-tools gets updated: 2026-10-20, its
closing aligned with the README's renamed "Agents are the first users"
section and the README agent path, and one sentence that called the
ontology a semantic layer corrected. Slug and date unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
The "not attempted" / "未尝试" cells used #7c8498 on white, 3.74:1 in the
browser check; #5b6478, the small-text grey the rest of the diagram uses,
reads 6.2:1.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
The positioning wave's slots slipped by one week: post 1 missed its
Thu 2026-10-08 slot and moves to Thu 2026-10-15, post 2 to Tue
2026-10-20 and post 3 to Thu 2026-10-22. Under the publishing cadence
(two slots a week, one post per slot, an agent's `date` is its planned
slot, never two posts on one day) post 4 can no longer hold Tue
2026-10-20, which post 2 now takes, so it moves to the next Tuesday.

The post is unpublished, so its first-publication `date` may still
change: `date: 2026-10-20` becomes `date: 2026-10-27` in the en, ja,
ko and zh-Hans files, and zh-Hant is regenerated with
`pnpm gen:zh-hant`. The back-linked ontology-mcp-agent-tools post gets
its revision on the day the link goes live, so its `updated` moves
from 2026-10-20T10:00:00+08:00 to 2026-10-27T10:00:00+08:00. No other
changes.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 15, 2026 11:51
This was referenced Oct 15, 2026
…y cadence, #267)

Ruling B on #267 switches on now that seven approved posts are queued,
and the seat starts the daily run on 2026-10-18. Post 4 is unpublished,
so its date moves from Tue 2026-10-27 to Tue 2026-10-20 in every
authored locale (en, ja, ko, zh-Hans), and the updated stamp it writes
on the back-linked published post ontology-mcp-agent-tools (en) moves
with it. zh-Hant is regenerated with pnpm gen:zh-hant. No published
post's date changes.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
@objectstack-fleet objectstack-fleet Bot changed the title content(blog): agent-operable apps — every object and action an MCP tool under the user's permissions (post 4, slot 2026-10-27) content(blog): agent-operable apps — every object and action an MCP tool under the user's permissions (post 4, slot 2026-10-20) Oct 17, 2026
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.

Post (slot Tue 2026-10-20): Agent-Operable Apps: When Every Object and Action Is an MCP Tool Under the User's Permissions

2 participants