Skip to content

feat(ui): add option to create a correctly-configured Slack app during Slack provider setup - #2585

Merged
simonwhitaker merged 3 commits into
mainfrom
sw/slack-manifest
Oct 21, 2026
Merged

simonwhitaker merged 3 commits into
mainfrom
sw/slack-manifest

Conversation

@simonwhitaker

@simonwhitaker simonwhitaker commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adding the Slack provider now shows a Create a Slack app link below the client ID field. It opens Slack's app creation page with Coral's app manifest filled in, including this instance's callback URL. You no longer enable MCP or register the redirect URL by hand.

 apps/reef/recipes/slack/recipe.yaml
     oauth:
       method: oauth_discovery
+      client_config_hint_generator: slack_app_create_link
 apps/reef/app/lib/provider-recipes/
 ├── recipe-format.ts          # + oauth_discovery.client_config_hint_generator: enum
 ├── configuration.server.ts   # recipeConfiguration(recipe, setup, { callbackUrl }) + clientConfigHintMarkdown
+├── client-config-hints.ts    # generator name → (context) => Markdown
+└── slack-app-manifest.ts     # the manifest as a typed constant, + slackAppCreateLink(callbackUrl)
 apps/reef/app/wax/components/markdown/
 └── markdown.tsx              # + color: 'secondary' (default) | 'tertiary'

A recipe names a hint generator; it never carries Reef code or files. A generator takes a context (today just the callback URL) and returns Markdown. The generator names are an enum in the recipe format, and the registry is typed against it, so a missing generator fails typecheck.

provider-new loader
  recipeConfiguration(chosen, setup, { callbackUrl })
    option the recipe fills in (its only one, or its default)
    CLIENT_CONFIG_HINT_GENERATORS.slack_app_create_link(context)
      → RecipeConfigurationView.clientConfigHintMarkdown =
        "[Create a Slack app](https://api.slack.com/apps?new_app=1&manifest_yaml=…), then copy its client ID and client secret here."
<StepperPage recipeConfiguration>
  <ConfigurationStep recipe>
    <OAuthApplicationBlock clientConfigHintMarkdown>  # generic: Wax <Markdown color="tertiary"> below the client ID

The hint travels with the rest of what the recipe fills in, so it needs no props of its own through the stepper. OAuthApplicationBlock has no Slack logic. It renders the hint with Wax's Markdown: links open in a new tab, and it renders no HTML or remote images. Markdown gains a color prop so the hint matches the tertiary help text above it; other uses keep the secondary default.

The manifest has only user scopes, copied from Slack's MCP tool table, and no bot user, because Slack's MCP server acts with the signed-in person's token. It sets is_mcp_enabled: true, no token rotation and no PKCE. A scope change is a one-line diff in slack-app-manifest.ts.

Evidence

  • Demo: video. It was recorded before the refactors, so it shows an earlier green button rather than the inline link.
  • Before: the recipe told you to "Enable MCP and register the callback URL" in Slack's settings yourself.
    After: the link carries a manifest whose only redirect URL is this instance's callback URL. The link is about 1.1 KB.
  • client-config-hints.test.ts: the Slack hint links to api.slack.com/apps?new_app=1, and its decoded manifest has redirect_urls: [callbackUrl] and is_mcp_enabled: true.
  • recipes.server.test.ts: recipeConfiguration carries exactly the generator's hint when the option names one, and no hint when it doesn't.
  • Story ConfigurationOAuthClientHint checks that the hint renders as a link that opens in a new tab.
  • Not yet done: a run against a real Slack workspace, confirming the app comes up with MCP on and the redirect URL filled in, and that Coral sign-in works.

Merge Danger

Door: two-way

Blast Radius: small

The hint shows only during new Slack provider setup, and only before the first confirmation, like everything else a recipe fills in. The edit and update pages don't know a provider's recipe, so they don't show it. Recipes are bundled at build time, so the new recipe field can still be reshaped later. The Markdown change is additive; existing uses render as before.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 16 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread apps/reef/app/lib/provider-recipes/library.server.ts Outdated
Comment thread apps/reef/app/lib/provider-recipes/library.server.ts Outdated
Comment thread apps/reef/app/lib/provider-recipes/library.server.ts Outdated
Comment thread apps/reef/recipes/slack/recipe.yaml Outdated
Comment thread apps/reef/app/views/providers/oauth-application.tsx Outdated
Comment thread apps/reef/app/lib/provider-recipes/recipes.server.test.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 10 files (changes from recent commits).

Requires human review: Auto-approval blocked because this review re-detected 1 unresolved issue already reported by Cubic.

Re-trigger cubic

Comment thread apps/reef/app/views/providers/oauth-application.tsx Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 12 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread apps/reef/app/lib/provider-recipes/recipes.server.test.ts
Comment thread apps/reef/app/lib/provider-recipes/recipes.server.test.ts
@simonwhitaker simonwhitaker changed the title feat(reef): create a Slack app from a manifest during provider setup feat(ui): create a Slack app from a manifest during provider setup Oct 21, 2026
The Slack recipe names a client config hint generator,
slack_app_create_link. Reef runs it on the add provider page to link to
Slack's app creation page with Coral's app manifest and this instance's
callback URL, shown as Markdown below the client ID field.
Move the Slack app link onto RecipeConfigurationView instead of passing
it through the stepper as its own prop. Give Wax's Markdown a color
variant so the hint matches the tertiary text above it, rename
slackAppUrl to slackAppCreateLink, and test the hint once at its owner.
@simonwhitaker simonwhitaker changed the title feat(ui): create a Slack app from a manifest during provider setup feat(ui): add option to create a correctly-configured Slack app during Slack provider setup Oct 21, 2026
@simonwhitaker
simonwhitaker merged commit 8c3a883 into main Oct 21, 2026
26 checks passed
@simonwhitaker
simonwhitaker deleted the sw/slack-manifest branch October 21, 2026 19:21
simonwhitaker pushed a commit that referenced this pull request Oct 22, 2026
🤖 I have created a release *beep* *boop*
---


## [1.2.0](v1.1.0...v1.2.0)
(2026-10-22)


### Features

* **activity:** record authored source locations for calls and outputs
([#2535](#2535))
([2d735ca](2d735ca))
* **activity:** show persistent Program source beside selected records
([#2569](#2569))
([f403819](f403819))
* add the Claude recipe and read gzipped OpenAPI documents
([#2513](#2513))
([07c27a2](07c27a2))
* **catalog:** share named TypeScript types across a Provider
([#2482](#2482))
([256e37d](256e37d))
* **desktop:** run the runtime on a Postgres database named in
desktop.json ([#2485](#2485))
([96465a0](96465a0))
* **functions:** type coral.providers, coral.workspace and
coral.functions at Saved Function save
([#2526](#2526))
([a465850](a465850))
* **provider:** create the Coral Built-in Provider at coral.workspace in
every new Workspace
([#2349](#2349))
([06e65ca](06e65ca))
* **provider:** store an agent's createProvider request on its Setup
([#2593](#2593))
([92847d2](92847d2))
* **provider:** update every Workspace's Built-in Provider catalog at
boot ([#2355](#2355))
([24615d9](24615d9))
* **reef:** add NextDNS provider recipe
([#2509](#2509))
([e44880e](e44880e))
* **reef:** calmer providers table and a flush app shell
([#2462](#2462))
([ffe279c](ffe279c))
* **reef:** improve the UI on small screens
([#2531](#2531))
([75e020a](75e020a))
* **reef:** make the providers rail vertical tabs with an All providers
entry ([#2552](#2552))
([08d007a](08d007a))
* **reef:** providers page UI tweaks
([#2562](#2562))
([7ca218e](7ca218e))
* **reef:** put the list pages' main action back in the top bar as a
primary button ([#2546](#2546))
([761ea6d](761ea6d))
* **reef:** read ^, $ and * in every filter field
([#2520](#2520))
([d322356](d322356))
* **reef:** refuse a provider display name another provider uses
([#2518](#2518))
([fdcd473](fdcd473))
* **reef:** review provider configuration changes in a dialog
([#2579](#2579))
([24417e9](24417e9))
* **reef:** show search indexing failures on a Provider and its
Operations ([#2504](#2504))
([6cffed0](6cffed0))
* **reef:** show your recent activity summary on the workspace overview
([#2574](#2574))
([0d1dba5](0d1dba5))
* **reef:** title list pages in their toolbar, with the main action
beside it ([#2525](#2525))
([fd44b72](fd44b72))
* **reef:** UI tweaks to provider's page
([#2566](#2566))
([62204ca](62204ca))
* **reef:** unify Activity detail panels and action reviews
([#2544](#2544))
([409d062](409d062))
* return only declared successes from Operation calls
([#2517](#2517))
([0270bf1](0270bf1))
* **run:** enforce the Fan-out Limit inside the Run Owner
([#2571](#2571))
([cb53419](cb53419))
* **run:** introduce the Run Owner for Program requests
([#2524](#2524))
([245d19d](245d19d))
* **run:** let coral.workspace.createProvider configure and publish the
Provider itself
([#2352](#2352))
([088faaf](088faaf))
* **run:** let createProvider configure sign-in and link to Reef's
sign-in step ([#2353](#2353))
([8d5495c](8d5495c))
* **run:** list a workspace's providers with
coral.workspace.listProviders
([#2374](#2374))
([179c43a](179c43a))
* **run:** make the Run Owner survive uncertain commits and lost leases
([#2541](#2541))
([3a1fad1](3a1fad1))
* **run:** name Tasks and Runs with four-word identifiers
([#2558](#2558))
([c82259a](c82259a))
* **run:** prepare a Provider from a Program with
coral.workspace.createProvider
([#2351](#2351))
([e7e56fd](e7e56fd))
* **run:** reshape the Dispatch Fence into one batched check per chunk
commit ([#2550](#2550))
([120c9d3](120c9d3))
* **run:** route approval decisions and cancellation through the Run
Owner ([#2577](#2577))
([77bcbf5](77bcbf5))
* **run:** summarize your recent activity in a Workspace
([#2573](#2573))
([bf4c75e](bf4c75e))
* **search:** show a Saved Function's result fields in its signature
line ([#2522](#2522))
([41365f6](41365f6))
* **search:** trace each reconcile pass and its steps
([#2456](#2456))
([0594094](0594094))
* **ui:** add option to create a correctly-configured Slack app during
Slack provider setup
([#2585](#2585))
([8c3a883](8c3a883))
* **ui:** move Add provider to the providers toolbar
([#2591](#2591))
([e1ee4cb](e1ee4cb))


### Bug Fixes

* **app:** sweep device authorization leases every 15 s
([#2452](#2452))
([1a4e4ae](1a4e4ae))
* **auth:** drop the unverified client name note from the approval page
([#2578](#2578))
([ddf5aa4](ddf5aa4))
* **catalog:** derive each Built-in Operation's contract from the types
Coral parses ([#2582](#2582))
([534e636](534e636))
* **catalog:** import OpenAPI Providers whose Operations request
different OAuth scopes
([#2401](#2401))
([3acfe55](3acfe55))
* **catalog:** keep keyword names in Operation Call Paths
([#2506](#2506))
([b20795b](b20795b))
* **catalog:** never make an Operation unavailable for search material
([#2465](#2465))
([f83c909](f83c909))
* **catalog:** stop rejecting createProvider request headers by name
([#2589](#2589))
([39e54b9](39e54b9))
* **provider:** ask for an OAuth client ID when Coral cannot register
one ([#2580](#2580))
([baefe9e](baefe9e))
* **provider:** bind each Setup to its starter and hide Coral from
Members ([#2480](#2480))
([6011a64](6011a64))
* **reef:** align the catalog table with the first rail entry
([#2484](#2484))
([fb59758](fb59758))
* **reef:** calm the Activity badge and keep it while the stream
reconnects ([#2468](#2468))
([1b66639](1b66639))
* **reef:** frame the providers table again and lower the top bar
([#2472](#2472))
([87e41ee](87e41ee))
* **reef:** pin third-party recipe OpenAPI documents to a commit
([#2510](#2510))
([b4e9280](b4e9280))
* **reef:** replace loose help text with info tips and banners
([#2461](#2461))
([695500a](695500a))
* **reef:** round the page's corners beside the sidebar
([#2503](#2503))
([a1fef64](a1fef64))
* **reef:** shorten OAuth scope chips that share a URL prefix
([#2455](#2455))
([5ae67ec](5ae67ec))
* **reef:** suggest a provider's namespace from its document name, not
its host ([#2565](#2565))
([8d77893](8d77893))
* **run:** name every argument to correct in an invalid_input failure
([#2354](#2354))
([07bd727](07bd727))
* **run:** reject a credential beside createProvider's bearer method and
report unreachable sources as upstream_incomplete
([#2542](#2542))
([590eab7](590eab7))
* **run:** report an MCP JSON-RPC rejection as invalid_input or
upstream_rejected
([#2402](#2402))
([749770b](749770b))
* **search:** load each Generation's schema graph once per search
([#2448](#2448))
([445d945](445d945))
* **search:** retry a failed Saved Function only after one interval
([#2460](#2460))
([a61ab3a](a61ab3a))
* **search:** return no hits for a query without a letter or digit
([#2561](#2561))
([bb0a4f0](bb0a4f0))
* **search:** start the write limit at the lock and build rows before
the write ([#2575](#2575))
([06ad55d](06ad55d))
* **search:** stop a self-referential Promise result from overflowing
the stack ([#2543](#2543))
([1d4f257](1d4f257))
* **search:** stop retrying an index write that fails the same way every
time ([#2548](#2548))
([e3b6d15](e3b6d15))


### Performance Improvements

* **catalog:** dedup credential destinations once per build, not per
Operation ([#2555](#2555))
([0afbcf5](0afbcf5))
* **catalog:** project each named TypeScript type once per Provider
([#2443](#2443))
([153c273](153c273))
* **search:** drop schema text from the phrase test
([#2559](#2559))
([cfdbafa](cfdbafa))
* **search:** index each word of schema text once
([#2547](#2547))
([d1123fb](d1123fb))
* **search:** prepare search entries in the indexer after commit
([#2467](#2467))
([1ab6765](1ab6765))
* **search:** recount only Workspaces whose rows changed
([#2457](#2457))
([777cfb3](777cfb3))
* **search:** skip unchanged rows when an index write runs again
([#2459](#2459))
([43b5188](43b5188))
* **search:** write only the statistics rows that changed
([#2458](#2458))
([8e2832c](8e2832c))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: coral-release-bot[bot] <269256696+coral-release-bot[bot]@users.noreply.github.com>
Co-authored-by: release-please[bot] <release-please[bot]@users.noreply.github.com>
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