Skip to content

RFC: a real screen at / — path = context, and the home-org model for a personal layer #4326

Description

@vibegui

TL;DR

It's agents all the way down. There's always a chat; the URL says who it is. The logo takes you to yourself.

  • Today / is a redirect, not a screen. Studio has no surface for the user — you're bumped into an org before anything happens.
  • Proposal: path = context. A topbar breadcrumb (deco > org > agent) is the product's only navigation and the chat's scope selector. Same rule the API already follows (/api/:org/...) — surfaced for humans.
  • Everything is an agent; only the zoom changes. Root = you across all your orgs · org = its pilot · deeper = one agent or connection. Same primitive at every level.
  • Your deco is an org. The personal home is a home org (every user already gets one at signup) that has memory about you and aggregates your other orgs as connections/agents — no new backend primitives, no god-scope.
  • Share at any level mints an MCP URL for that scope — your whole deco, one org, or one agent — connectable from any external client with the caller's own role (fix(mcp-oauth): let external OAuth clients use aggregate/virtual MCP endpoints #4263).
  • Pilotable this week with zero core changes: hand-wire one org with org-connections + reporting agents. Smallest core PR after: kill the redirect, root screen, breadcrumb.

Problem

studio.decocms.com/ is not a screen. The root route immediately redirects into an org (apps/mesh/src/web/index.tsxlastOrgSlug → first org → onboarding). There is no surface that represents the user: no place to see all your orgs at a glance, no chat that isn't already inside an org, no cross-org status.

For single-org users this is invisible. For anyone with several orgs (agencies, platform operators, most internal users) the product forces an org choice before any value, and there is no way to ask a question that spans orgs ("what needs me this morning?").

Design principle: path = context

The backend already has this rule: all org-scoped API routes are /api/:org/... and the org is resolved from the path (resolveOrgFromPath, and #4263 extends path-derived org resolution to MCP OAuth sessions). The proposal is to surface the same rule for humans:

URL path              chat scope                     tool surface
/                     you (all your orgs)            fan-out across memberships
/:org                 the org's pilot (decopilot)    /api/:org/mcp aggregate
/:org/agents/:id      that agent                     its virtual MCP
/:org/connections/:id that connection                its tools + management tools

Concretely: a topbar breadcrumb (logo = / = home, then org, then entity) that is also the context selector for a chat that is always available. You never navigate to a chat; you navigate to a scope and speak. Every breadcrumb level corresponds to an MCP endpoint, so what the web UI shows at a path is what an external MCP client attached to that same path would get.

Properties that fall out:

Share = mint an MCP URL for this scope

Every breadcrumb level carries a Share action that produces the MCP URL for exactly that scope, ready to paste into any external client (Claude Desktop/Code, Cursor, etc.):

  • Share at an agent → that agent's virtual-MCP URL.
  • Share at an org → the org's aggregate (/api/:org/mcp).
  • Share at the rootyour entire deco: the home scope's aggregate, i.e. all your orgs behind one URL.

Sharing exposes an endpoint, not access — the client still authenticates via OAuth (#4263) and lands with the caller's own role in each org. This makes the breadcrumb, the chat scope, and the connectable surface the same object at every zoom level: the UI is one client of a scope; Share hands the same scope to any other client.

Implementation option A: home org (composition over construction)

Instead of building a new "personal layer" with new primitives, represent the personal home as an org the user elects as home — and plug the user's other orgs into it:

  • Every user already has a candidate: signup creates a default org per user.
  • The home org holds memory about the user. It's not just an aggregator: as an org it already has memory/context primitives, so the personal scope accumulates who you are, what you're working on, and how you like reports — the thing none of the individual orgs should own.
  • Org-as-connection: an org's aggregate endpoint (/api/:org/mcp) is an MCP resource. Once fix(mcp-oauth): let external OAuth clients use aggregate/virtual MCP endpoints #4263 lands, it supports external OAuth clients with DCR — which includes another Studio org's connection. With auth_mode: per_user, the connection holds the member's own token for the target org, so actions execute with that user's real role there. No god-scope, no shared identity.
  • Org-as-agent: wrap each org-connection in a virtual MCP with reporting instructions ("represent org X: report status, surface findings, escalate what needs attention"). The home org's chat then addresses orgs as sub-agents.
  • Home view as an MCP app: the actual home screen (org cards, needs-attention queue, morning brief) ships as an App UI inside the home org — iterable without touching studio core routing.
  • Scheduled reporting: the event bus already supports cron (EVENT_PUBLISH with cron); a recurring event in the home org can fan out "brief me" to each org-agent and post a digest.

This is pilotable with zero core changes: hand-wire one org with org-connections + reporting agents, evaluate, then productize (a "home org" setting, a first-class "Studio org" connection type, / rendering the home org, the breadcrumb topbar).

Implementation option B: dedicated personal layer (/api/me/* fan-out)

The alternative explored in #4038: a person-scoped route layer above orgs that fans out over listOrganizations(user) and calls each org's existing scoped endpoints, aggregating client-side first and server-side later. Never a god-scope — it only ever reads the caller's own memberships.

Tradeoffs

Dimension A: home org B: /api/me/* fan-out
New primitives None — orgs, connections, virtual MCPs, apps, event bus New route family + aggregator, personal thread storage
Time to pilot Days (hand-wireable today with API keys; per-user OAuth after #4263) Weeks (new backend surface before anything is real)
Identity Per-user OAuth per org-connection → correct attribution; API-key interim loses per-member attribution Session identity throughout
Isolation Unchanged — home org has exactly the powers of its member's memberships Unchanged by design, but a new cross-org code path to review
Freshness Pull (tool calls) + cron digests; no live push without extra wiring Same problem; aggregator could cache/poll centrally
Failure modes Aggregation-of-aggregations: double tool-name prefixing (mitigated by the short namespace codes in #4263) and possible connection cycles (org A ↔ org B) — needs a guard before productizing Fan-out latency/error handling in one place; N sequential org calls on landing
Org proliferation Blesses the existing default org rather than adding one Neutral
UX coherence Home is "just an org", so all existing chrome works; risk: home inherits org UI that doesn't fit a personal surface Purpose-built screen; risk: a second UI system to maintain
Long-term Doubles as Studio-to-Studio federation (orgs consuming orgs), useful beyond the home Purpose-built; doesn't generalize

Shared risks regardless of option:

  • Digest fatigue: daily reports are compelling for a week and then ignored. The home surface should show deltas and needs-attention items that can be dismissed, not a recurring report.
  • Breadcrumb ≠ toolbar: the topbar must stay a single line of path; if it accretes actions it recreates the toolbox this is meant to remove.
  • Mid-thread context switches: changing scope inside a running thread should either start a new thread or be an explicit re-scope action — silent context swaps would make thread history unreliable.
  • Single-org users must lose nothing: for them / should feel like today's org home with one extra crumb, not an empty cockpit.

Relationship to existing work

Suggested sequence

  1. Pilot (no core changes): one org hand-wired with org-connections + per-org reporting agents + a minimal home App UI. Validate the interaction model.
  2. Land fix(mcp-oauth): let external OAuth clients use aggregate/virtual MCP endpoints #4263 (currently blocked on a missing module — see review notes there).
  3. Smallest core PR: stop the / redirect; render a root screen (org cards via client-side membership fan-out + composer scoped by an org chip); topbar breadcrumb derived from the route tree.
  4. Productize option A if the pilot holds: "home org" setting, "Studio org" connection type with per-user OAuth, cycle guard for org-to-org connections, scheduled digests via event bus cron.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions