Skip to content

feat(host-cloudflare): attribute MCP-session runs to a trigger actor - #22

Merged
aryasaatvik merged 1 commit into
devfrom
feat-mcp-session-actor
Jun 14, 2026
Merged

feat(host-cloudflare): attribute MCP-session runs to a trigger actor#22
aryasaatvik merged 1 commit into
devfrom
feat-mcp-session-actor

Conversation

@aryasaatvik

Copy link
Copy Markdown
Owner

What

Threads the run actor through the Cloudflare MCP session Durable Object so MCP-triggered runs carry a {kind:"mcp", actor} trigger — the MCP half of run actor attribution (HTTP runs already got one).

Changes

  • McpSessionInit gains an optional actor; the worker dispatcher stamps it from the gate's resolved principal at session create.
  • The shared DO base carries actor onto the persisted SessionMeta (alongside webOrigin), so a cold isolate rebuilds with the same attribution.
  • Each host's buildMcpServer (apps/host-cloudflare) stamps the trigger, falling back to a user actor keyed by the session user when the principal supplied none.

Generic plumbing only — it threads whatever actor the principal carries (no service-token specifics here). Pairs with the actor contract from #20.

Verification

  • typecheck 41/41 · lint + format clean on all changed files

(Build/Deploy-preview checks fail on the fork's missing R2 secrets, as on prior PRs — unrelated.)

Thread the run actor through the Durable-Object session: the worker stamps
McpSessionInit.actor from the gate's principal, the DO base carries it onto the
persisted SessionMeta, and each host's buildMcpServer stamps it as the
{kind:"mcp", actor} trigger (falling back to a user actor keyed by the session
user). This is the MCP half of run actor attribution — HTTP runs already carry a
trigger; now MCP-session runs do too, so the Runs page attributes and filters
them by actor.
@greptile-apps

greptile-apps Bot commented Jun 14, 2026

Copy link
Copy Markdown

Greptile Summary

This PR threads a run actor through the Cloudflare MCP session Durable Object so that every run spawned from an MCP connection carries a {kind: "mcp", actor} trigger. The plumbing is session-stable: the actor is stamped once at session create, persisted in SessionMeta alongside webOrigin, and correctly reinstated on cold-isolate restore.

  • McpSessionInit and SessionMeta gain an optional actor: ExecutionActor field; the shared base class merges it from the McpSessionInit token after host-supplied resolveSessionMeta runs, so subclasses stay identity-only.
  • makeDurableObjectMcpSessionStore extracts principal.actor at dispatch time and forwards it to createSession; sessions created before this deploy (where actor would be absent in storage) are handled gracefully by the ?? { kind: "user", ... } fallback in buildMcpServer.
  • apps/cloud's buildMcpServer does not yet pass a trigger to createExecutorMcpServer — this appears intentional per the PR description ("Pairs with the actor contract from feat(execution-history): attribute runs to a trigger actor + filter by actor #20").

Confidence Score: 5/5

Safe to merge — the change is purely additive plumbing with no modifications to existing auth, session validation, or storage layout that could break existing sessions.

All four changed files are consistent: the actor flows from principal → McpSessionInit → SessionMeta (persisted) → buildMcpServer trigger without gaps. The conditional-spread pattern avoids writing actor: undefined into DO storage, and the ?? { kind:"user", ... } fallback in buildMcpServer correctly handles both pre-deploy sessions (no actor in storage) and sessions where the principal carried no explicit actor.

No files require special attention. The cloud DO's buildMcpServer (not in this diff) does not yet consume sessionMeta.actor, but this is intentional and noted in the PR description.

Important Files Changed

Filename Overview
packages/hosts/cloudflare/src/mcp/seams.ts Adds optional actor?: ExecutionActor to McpSessionInit; change is additive and backward-compatible.
packages/hosts/cloudflare/src/mcp/session-durable-object.ts Adds actor?: ExecutionActor to SessionMeta and updates resolveAndStoreSessionMeta to conditionally spread the actor from the token onto the persisted meta; spread ordering is correct and avoids writing actor: undefined into storage.
packages/hosts/cloudflare/src/mcp/session-store.ts Threads principal.actor into createSession; the new actor parameter is typed as `ExecutionActor
apps/host-cloudflare/src/mcp/session-durable-object.ts Stamps { kind: "mcp", actor } as the run trigger in buildMcpServer, with a safe fallback to a user-kind actor keyed by sessionMeta.userId when no explicit actor was set at session create.

Sequence Diagram

sequenceDiagram
    participant W as Worker (session-store)
    participant DO as McpSessionDOBase
    participant Host as host-cf buildMcpServer
    participant MCP as createExecutorMcpServer

    W->>W: authenticate → principal.actor
    W->>DO: "stub.init({ userId, orgId, webOrigin, actor })"
    DO->>DO: resolveSessionMeta(token) → base SessionMeta
    DO->>DO: merge token.webOrigin + token.actor → SessionMeta
    DO->>DO: saveSessionMeta (ctx.storage)
    DO->>Host: buildMcpServer(sessionMeta, dbHandle)
    Host->>Host: "actor = sessionMeta.actor ?? { kind:user, id:userId, label:null }"
    Host->>MCP: "createExecutorMcpServer({ engine, trigger:{ kind:mcp, actor } })"
    MCP-->>Host: McpServer
    Host-->>DO: "{ mcpServer, engine }"

    Note over DO: Cold-isolate restore path
    DO->>DO: loadSessionMeta() from ctx.storage
    DO->>Host: buildMcpServer(restoredSessionMeta, dbHandle)
    Host->>MCP: "createExecutorMcpServer({ trigger:{ kind:mcp, actor } })"
Loading

Reviews (1): Last reviewed commit: "feat(host-cloudflare): attribute MCP-ses..." | Re-trigger Greptile

@aryasaatvik
aryasaatvik merged commit 7c77922 into dev Jun 14, 2026
10 of 13 checks passed
@aryasaatvik
aryasaatvik deleted the feat-mcp-session-actor branch June 14, 2026 21:16
aryasaatvik added a commit that referenced this pull request Jun 23, 2026
…22)

Threads the run **actor** through the Cloudflare MCP session Durable
Object so MCP-triggered runs carry a `{kind:"mcp", actor}` trigger — the
MCP half of run actor attribution (HTTP runs already got one).

- `McpSessionInit` gains an optional `actor`; the worker dispatcher
stamps it from the gate's resolved principal at session create.
- The shared DO base carries `actor` onto the persisted `SessionMeta`
(alongside `webOrigin`), so a cold isolate rebuilds with the same
attribution.
- Each host's `buildMcpServer` (`apps/host-cloudflare`) stamps the
trigger, falling back to a `user` actor keyed by the session user when
the principal supplied none.

Generic plumbing only — it threads whatever actor the principal carries
(no service-token specifics here). Pairs with the actor contract from

- `typecheck` 41/41 · lint + format clean on all changed files

(Build/Deploy-preview checks fail on the fork's missing R2 secrets, as
on prior PRs — unrelated.)
aryasaatvik added a commit that referenced this pull request Jun 23, 2026
…22)

Threads the run **actor** through the Cloudflare MCP session Durable
Object so MCP-triggered runs carry a `{kind:"mcp", actor}` trigger — the
MCP half of run actor attribution (HTTP runs already got one).

- `McpSessionInit` gains an optional `actor`; the worker dispatcher
stamps it from the gate's resolved principal at session create.
- The shared DO base carries `actor` onto the persisted `SessionMeta`
(alongside `webOrigin`), so a cold isolate rebuilds with the same
attribution.
- Each host's `buildMcpServer` (`apps/host-cloudflare`) stamps the
trigger, falling back to a `user` actor keyed by the session user when
the principal supplied none.

Generic plumbing only — it threads whatever actor the principal carries
(no service-token specifics here). Pairs with the actor contract from

- `typecheck` 41/41 · lint + format clean on all changed files

(Build/Deploy-preview checks fail on the fork's missing R2 secrets, as
on prior PRs — unrelated.)
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.

1 participant