Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion docs/internal/slack-local-setup-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ oauth_config:
- openid
- email
- profile
- canvases:read
settings:
event_subscriptions:
request_url: https://<you>-posthog.ngrok.dev/slack/event-callback
Expand Down Expand Up @@ -145,6 +146,8 @@ Django must be up at that moment.
> locally. Neither is behind a feature flag: the App Home tab renders for every install, and the
> identity link appears only when the install holds the `users:read` and `users:read.email` scopes.

> The `canvases:read` user scope lets the Slack MCP connection read canvases that the authorizing user can access.

> `reaction_added` + `reactions:read` power thumbs-reaction feedback on agent replies. Without
> them a 👍/👎 reaction on a reply records nothing, again with no error.

Expand Down Expand Up @@ -282,9 +285,11 @@ It does not move bot events to production or change the production Slack app's c
The existing Slack MCP entry becomes **Slack via PostHog (dev)** and uses the separate credentials.
It activates only after the shared-client probe passes.
Existing Slack MCP installations block a change of OAuth app; do not disconnect them without their owners' approval.
6. In an allowed project, connect **Slack via PostHog (dev)** from the MCP store, finish Slack authorization, and confirm that a channel search returns results.
6. In an allowed project, connect **Slack via PostHog (dev)** from the MCP store, finish Slack authorization, and confirm that a channel search returns results and an accessible canvas can be read.
Comment thread
dmarticus marked this conversation as resolved.
Confirm that another project cannot list or authorize this entry.

Reconnect an installation that was authorized before a new reviewed scope was synced. Existing OAuth tokens do not gain the new scope automatically.

The connection keeps the catalog's reviewed MCP scopes; it does not request all scopes available to the bot.
The project restriction applies to authorization, token exchange, token refresh, and upstream requests.
Removing a project from the allowlist blocks its existing connection, even before catalog sync runs.
Expand Down
3 changes: 2 additions & 1 deletion products/mcp_store/backend/catalog_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@ def _entry_for_environment(entry: CatalogEntry, template: MCPServerTemplate | No
return replace(
entry,
name="Slack via PostHog (dev)",
description="Search public Slack channels with the internal PostHog development app.",
description="Search public Slack channels and read messages, user profiles, and canvases you can access.",
oauth_scope_allowlist=(entry.oauth_scope_allowlist or ()) + ("canvases:read",),
oauth_credentials_source="slack_dev_app",
disabled=not dev_enabled,
)
Expand Down
12 changes: 7 additions & 5 deletions products/mcp_store/backend/test/test_catalog_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,21 @@ def test_slack_dev_uses_separate_credentials_and_resets_after_disable(self, prob
counts = sync_mcp_catalog(entries=[entry])
template = MCPServerTemplate.objects.get(url=entry.url)
assert template.name == "Slack via PostHog (dev)"
assert template.description == "Search public Slack channels with the internal PostHog development app."
assert template.description == (
"Search public Slack channels and read messages, user profiles, and canvases you can access."
)
assert template.oauth_credentials_source == "slack_dev_app"
assert template.oauth_credentials == {}
assert entry.oauth_scope_allowlist is not None
assert template.oauth_scope_allowlist == list(entry.oauth_scope_allowlist)
assert "canvases:read" not in entry.oauth_scope_allowlist
expected_scopes = (*entry.oauth_scope_allowlist, "canvases:read")
assert template.oauth_scope_allowlist == list(expected_scopes)
assert counts.activated == 1
assert MCPServerTemplate.available_for_team(42).filter(id=template.id).exists()
assert not MCPServerTemplate.available_for_team(43).filter(id=template.id).exists()

instance_settings.assert_called_with(["SLACK_DEV_APP_CLIENT_ID", "SLACK_DEV_APP_CLIENT_SECRET"])
probe.assert_called_once_with(
entry.url, scope_allowlist=entry.oauth_scope_allowlist, shared_client_id="dev-client"
)
probe.assert_called_once_with(entry.url, scope_allowlist=expected_scopes, shared_client_id="dev-client")

with self.settings(MCP_STORE_SLACK_DEV_ALLOWED_TEAM_IDS=[]):
sync_mcp_catalog(entries=[entry])
Expand Down
Loading