Skip to content

MCP consent: origin-thread Slack reply (populate ConsentPrompt.Origin) #365

Description

@initializ-mk

Follow-up to #343 (Forge-driven Slack delivery of MCP consent prompts), which shipped in #345. The core delivery is done and functional; this issue tracks the one deferred item from #343's acceptance list.

What is deferred

#343 called this out explicitly: "the Slack adapter stashes ChannelOrigin{channel, thread_ts, user_id} on the request context at inbound … the one net-new plumbing item … If deferred, v1 falls back to DM-by-email — fully functional, just not in-thread."

It was deferred. Today, when a type: user MCP call parks on the auth-required gate, Forge always DMs the consent link by email rather than replying in the Slack thread the user asked from — even when the request came in over Slack.

Current state (what's already built)

The receiving side is complete — only the population of Origin is missing:

  • forge-core/channels/plugin.goConsentPrompt.Origin *ChannelOrigin and ChannelOrigin{Adapter, Channel, ThreadTS, UserID} exist.
  • forge-plugins/channels/slack/consent.goconsentTarget() already routes to req.Origin.Channel / Origin.ThreadTS when Origin != nil, else falls back to DM-by-email. No change needed here.

What's missing:

  • No WithChannelOrigin / ChannelOriginFromContext context helpers.
  • Nothing stashes a ChannelOrigin on the request context at Slack inbound.
  • run.go's SetConsentDeliverer closure builds the ConsentPrompt without Origin (no originFromContext(ctx)), so req.Origin is always nil in production. ChannelOrigin currently appears only in consent_test.go.

Scope

  1. Add context helpers (e.g. channels.WithChannelOrigin(ctx, ChannelOrigin) / ChannelOriginFromContext(ctx)), or an equivalent in forge-cli/runtime.
  2. At Slack inbound (where the task is dispatched from a Slack message), stash ChannelOrigin{Adapter:"slack", Channel, ThreadTS, UserID} on the context that flows to mcpAuthGate.Await.
  3. In run.go's consent closure, set Origin: originFromContext(ctx) on the ConsentPrompt.

Acceptance

  • A type: user call that arrived over Slack delivers the "Connect " link in the origin thread/DM, not a fresh email-resolved DM.
  • Non-Slack-origin (or unresolvable origin) still falls back to DM-by-email — no regression.
  • The clicker-identity guard on Cancel still holds in a shared channel (a non-subject click must not cancel a peer's parked call).

Out of scope (unchanged from #343)

  • MS Teams / other adapters implementing ConsentDeliverer — separate per-adapter follow-up.
  • Message-lifecycle edits ("Connected / expired") — v1.1.

Related

Delivery layer #343 (#345) · standalone loop #332 · gate #330 · epic #317 · design-tool-registry.md §18.4.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestplatformInitializ Platform (governance control plane) responsibility

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions