feat(slack): add slack-mcp-v2 flag + align mcp-v2/cli/sdk surface#4197
feat(slack): add slack-mcp-v2 flag + align mcp-v2/cli/sdk surface#4197saddlepaddle merged 3 commits intomainfrom
Conversation
Routes the Slack agent to the v2 MCP server via per-organization PostHog flag (`slack-mcp-v2`); off keeps existing v1 path. Adds nested `organization.members` and `task.statuses` to tRPC, the mcp-v2 server (now tRPC-only — no DB), the CLI, and the SDK so all four surfaces share the same shape.
|
ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds MCP v2 support for Slack agents (feature-flagged), new SDK Organization/Members and TaskStatuses resources, tRPC routers and procedures, MCP v2 tool registrations, in-memory client option plumbing, CLI list commands, and branching logic in the Slack agent to select V1 vs V2 flows. ChangesMCP V2 Slack Agent with Organization & Task Status APIs
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR adds a
Confidence Score: 4/5Safe to merge after fixing the misleading error message in the remove mutation. The flag routing, context preloading, deny-lists, and SDK types are all correct; v1 is fully preserved as the fallback. The sole defect is in the new packages/trpc/src/router/organization/members.ts — specifically the error-branch ordering in the
|
| Filename | Overview |
|---|---|
| packages/trpc/src/router/organization/members.ts | New tRPC sub-router for organization member management; contains an error-message ordering bug where sole-owner self-removal shows a misleading error, and an API surface inconsistency where list uses the active org but add/remove accept an explicit organizationId. |
| apps/api/src/app/api/integrations/slack/events/utils/run-agent/run-agent.ts | Adds PostHog flag check to route the Slack agent to mcp-v2, with correct safe fallback to v1 on error; adds v2 tool name mappings, dual deny-lists, and v2/v1 response parsing for hosts. Logic is sound. |
| apps/api/src/app/api/integrations/slack/events/utils/run-agent/mcp-clients.ts | Adds createSupersetMcpV2Client alongside the existing v1 client; wires onToolCall for PostHog analytics. Clean separation, no issues. |
| packages/trpc/src/router/task/statuses.ts | New tRPC sub-router returning task statuses for the active org; correctly uses requireActiveOrgMembership and orders by position. No issues. |
| packages/mcp-v2/src/tools/organization/members/list.ts | New mcp-v2 tool wrapping the tRPC organization.members.list via createMcpCaller; returns { members: rows } compatible with fetchAgentContext's parsing. No issues. |
| packages/mcp-v2/src/tools/tasks/statuses/list.ts | New mcp-v2 tool for listing task statuses; no inputSchema is intentional (consistent with hosts_list), as defineTool defaults to {}. Returns { statuses: rows }. No issues. |
| packages/mcp-v2/src/in-memory.ts | Adds optional onToolCall emitter param to InMemoryClientOptions and threads it into createMcpServer. Minimal, safe change. |
| packages/sdk/src/resources/organization.ts | New SDK resource for organization.members with list, add, remove methods; types are well-defined and match the tRPC schema. No issues. |
| packages/sdk/src/resources/tasks.ts | Adds TaskStatuses nested resource and TaskStatus/TaskStatusListResponse types to the Tasks SDK resource. No issues. |
| packages/shared/src/constants.ts | Adds SLACK_MCP_V2: "slack-mcp-v2" to FEATURE_FLAGS. Straightforward addition. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Slack @mention -> runSlackAgent] --> B{PostHog: slack-mcp-v2 flag}
B -- off/error --> C[createSupersetMcpClient v1]
B -- on --> D[createSupersetMcpV2Client v2]
C --> E[fetchAgentContext useV2=false]
D --> F[fetchAgentContext useV2=true]
E --> G[list_members / list_task_statuses / list_devices]
F --> H[organization_members_list / tasks_statuses_list / hosts_list]
G --> I[Agent loop - DENIED_SUPERSET_TOOLS_V1 filtered out]
H --> J[Agent loop - DENIED_SUPERSET_TOOLS_V2 filtered out]
I --> K[getActionFromToolResult - v1 wire shape]
J --> L[getActionFromToolResult - v2 wire shape]
K --> M[SlackBlocks / PostHog events]
L --> M
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
packages/trpc/src/router/organization/members.ts:107-124
**Last-owner self-removal shows wrong error message**
The `isTargetSelf` guard (line 108) fires before the last-owner guard (line 114). When a sole owner tries to remove themselves, both conditions are true and the `isTargetSelf` branch short-circuits first, surfacing "Cannot remove yourself" instead of the far more actionable "Cannot remove the last owner. Transfer ownership first." The last-owner message is effectively unreachable for self-removal cases. Fix by evaluating the last-owner condition first.
### Issue 2 of 2
packages/trpc/src/router/organization/members.ts:47-65
**`add` and `remove` accept caller-supplied `organizationId`; `list` uses active org**
`list` resolves the org via `requireActiveOrgMembership(ctx)` (session's active org), while `add` and `remove` accept an explicit `organizationId` in their input. SDK callers who call `organization.members.list()` and then `organization.members.add({ organizationId: someOtherOrg, ... })` will silently operate on different organizations. Consider either (a) making `add`/`remove` also default to the active org, or (b) adding an `organizationId` parameter to `list` so all three are consistent.
Reviews (1): Last reviewed commit: "feat(slack): add slack-mcp-v2 flag + ali..." | Re-trigger Greptile
| if (!canRemove) { | ||
| if (isTargetSelf) { | ||
| throw new TRPCError({ | ||
| code: "FORBIDDEN", | ||
| message: "Cannot remove yourself", | ||
| }); | ||
| } | ||
| if (targetMember.role === "owner" && ownerCount === 1) { | ||
| throw new TRPCError({ | ||
| code: "FORBIDDEN", | ||
| message: "Cannot remove the last owner. Transfer ownership first.", | ||
| }); | ||
| } | ||
| throw new TRPCError({ | ||
| code: "FORBIDDEN", | ||
| message: "You don't have permission to remove this member", | ||
| }); | ||
| } |
There was a problem hiding this comment.
Last-owner self-removal shows wrong error message
The isTargetSelf guard (line 108) fires before the last-owner guard (line 114). When a sole owner tries to remove themselves, both conditions are true and the isTargetSelf branch short-circuits first, surfacing "Cannot remove yourself" instead of the far more actionable "Cannot remove the last owner. Transfer ownership first." The last-owner message is effectively unreachable for self-removal cases. Fix by evaluating the last-owner condition first.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/trpc/src/router/organization/members.ts
Line: 107-124
Comment:
**Last-owner self-removal shows wrong error message**
The `isTargetSelf` guard (line 108) fires before the last-owner guard (line 114). When a sole owner tries to remove themselves, both conditions are true and the `isTargetSelf` branch short-circuits first, surfacing "Cannot remove yourself" instead of the far more actionable "Cannot remove the last owner. Transfer ownership first." The last-owner message is effectively unreachable for self-removal cases. Fix by evaluating the last-owner condition first.
How can I resolve this? If you propose a fix, please make it concise.| add: protectedProcedure | ||
| .input( | ||
| z.object({ | ||
| organizationId: z.string().uuid(), | ||
| userId: z.string().uuid(), | ||
| role: z.enum(["member", "admin", "owner"]).default("member"), | ||
| }), | ||
| ) | ||
| .mutation(async ({ ctx, input }) => { | ||
| await verifyOrgAdmin(ctx.session.user.id, input.organizationId); | ||
| return ctx.auth.api.addMember({ | ||
| body: { | ||
| organizationId: input.organizationId, | ||
| userId: input.userId, | ||
| role: input.role, | ||
| }, | ||
| headers: ctx.headers, | ||
| }); | ||
| }), |
There was a problem hiding this comment.
add and remove accept caller-supplied organizationId; list uses active org
list resolves the org via requireActiveOrgMembership(ctx) (session's active org), while add and remove accept an explicit organizationId in their input. SDK callers who call organization.members.list() and then organization.members.add({ organizationId: someOtherOrg, ... }) will silently operate on different organizations. Consider either (a) making add/remove also default to the active org, or (b) adding an organizationId parameter to list so all three are consistent.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/trpc/src/router/organization/members.ts
Line: 47-65
Comment:
**`add` and `remove` accept caller-supplied `organizationId`; `list` uses active org**
`list` resolves the org via `requireActiveOrgMembership(ctx)` (session's active org), while `add` and `remove` accept an explicit `organizationId` in their input. SDK callers who call `organization.members.list()` and then `organization.members.add({ organizationId: someOtherOrg, ... })` will silently operate on different organizations. Consider either (a) making `add`/`remove` also default to the active org, or (b) adding an `organizationId` parameter to `list` so all three are consistent.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/trpc/src/router/organization/members.ts`:
- Around line 107-124: The error handling in the remove flow currently checks
isTargetSelf before verifying the last-owner condition, causing the "Cannot
remove yourself" message to mask the more actionable "Cannot remove the last
owner" when the sole owner tries to remove themselves; update the logic in the
remove handler (the block that examines canRemove, isTargetSelf, targetMember,
and ownerCount) to check the last-owner condition (targetMember.role === "owner"
&& ownerCount === 1) before the isTargetSelf check and throw the TRPCError with
message "Cannot remove the last owner. Transfer ownership first." so callers
receive the correct, actionable error.
- Around line 47-65: The add procedure currently lets any caller who passes
verifyOrgAdmin assign role "owner", causing privilege escalation; update the
protectedProcedure handling in the add member endpoint (the add mutation in
members.ts) to disallow assigning "owner" for callers who only pass
verifyOrgAdmin by either narrowing the accepted roles to ["member","admin"] or
adding a runtime check that if input.role === "owner" then require
verifyOrgOwner (or throw an unauthorized error); mirror the same enforcement
approach used in updateRole in organization.ts to ensure admins cannot grant
owner.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e9a34392-06a5-4df2-9ce0-41a68583c6e1
📒 Files selected for processing (19)
apps/api/src/app/api/integrations/slack/events/utils/run-agent/mcp-clients.tsapps/api/src/app/api/integrations/slack/events/utils/run-agent/run-agent.tspackages/cli/src/commands/organization/members/list/command.tspackages/cli/src/commands/organization/members/meta.tspackages/cli/src/commands/tasks/statuses/list/command.tspackages/cli/src/commands/tasks/statuses/meta.tspackages/mcp-v2/src/in-memory.tspackages/mcp-v2/src/tools/organization/members/list.tspackages/mcp-v2/src/tools/register.tspackages/mcp-v2/src/tools/tasks/statuses/list.tspackages/sdk/src/client.tspackages/sdk/src/resources/index.tspackages/sdk/src/resources/organization.tspackages/sdk/src/resources/tasks.tspackages/shared/src/constants.tspackages/trpc/src/router/organization/members.tspackages/trpc/src/router/organization/organization.tspackages/trpc/src/router/task/statuses.tspackages/trpc/src/router/task/task.ts
| if (!canRemove) { | ||
| if (isTargetSelf) { | ||
| throw new TRPCError({ | ||
| code: "FORBIDDEN", | ||
| message: "Cannot remove yourself", | ||
| }); | ||
| } | ||
| if (targetMember.role === "owner" && ownerCount === 1) { | ||
| throw new TRPCError({ | ||
| code: "FORBIDDEN", | ||
| message: "Cannot remove the last owner. Transfer ownership first.", | ||
| }); | ||
| } | ||
| throw new TRPCError({ | ||
| code: "FORBIDDEN", | ||
| message: "You don't have permission to remove this member", | ||
| }); | ||
| } |
There was a problem hiding this comment.
remove error-message ordering masks the last-owner reason.
When the sole owner tries to remove themselves, isTargetSelf is true and targetMember.role === "owner" && ownerCount === 1 is also true. Because the isTargetSelf branch fires first, the caller receives "Cannot remove yourself" instead of the actionable "Cannot remove the last owner. Transfer ownership first." The second branch is unreachable in this scenario.
🐛 Proposed fix — check last-owner before self
if (!canRemove) {
+ if (targetMember.role === "owner" && ownerCount === 1) {
+ throw new TRPCError({
+ code: "FORBIDDEN",
+ message: "Cannot remove the last owner. Transfer ownership first.",
+ });
+ }
if (isTargetSelf) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Cannot remove yourself",
});
}
- if (targetMember.role === "owner" && ownerCount === 1) {
- throw new TRPCError({
- code: "FORBIDDEN",
- message: "Cannot remove the last owner. Transfer ownership first.",
- });
- }
throw new TRPCError({🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/trpc/src/router/organization/members.ts` around lines 107 - 124, The
error handling in the remove flow currently checks isTargetSelf before verifying
the last-owner condition, causing the "Cannot remove yourself" message to mask
the more actionable "Cannot remove the last owner" when the sole owner tries to
remove themselves; update the logic in the remove handler (the block that
examines canRemove, isTargetSelf, targetMember, and ownerCount) to check the
last-owner condition (targetMember.role === "owner" && ownerCount === 1) before
the isTargetSelf check and throw the TRPCError with message "Cannot remove the
last owner. Transfer ownership first." so callers receive the correct,
actionable error.
🧹 Preview Cleanup CompleteThe following preview resources have been cleaned up:
Thank you for your contribution! 🎉 |
There was a problem hiding this comment.
1 issue found across 19 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/trpc/src/router/organization/members.ts">
<violation number="1" location="packages/trpc/src/router/organization/members.ts:52">
P1: Privilege escalation: an admin can assign the `"owner"` role via this procedure because `verifyOrgAdmin` permits both admin and owner callers. Restrict the enum to `["member", "admin"]` here, or add a separate `verifyOrgOwner` guard when `role === "owner"`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Aligns with the existing All Access cohort pattern (person-level email match) used by cloud-access. The flag now keys off the linking Superset user's id so the cohort-based gating actually applies.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/api/src/app/api/integrations/slack/events/utils/run-agent/run-agent.ts">
<violation number="1" location="apps/api/src/app/api/integrations/slack/events/utils/run-agent/run-agent.ts:556">
P1: Evaluate `slack-mcp-v2` using organization scope, not user scope, to preserve the intended per-org rollout behavior.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| let useV2 = false; | ||
| try { | ||
| useV2 = Boolean( | ||
| await posthog.getFeatureFlag(FEATURE_FLAGS.SLACK_MCP_V2, params.userId), |
There was a problem hiding this comment.
P1: Evaluate slack-mcp-v2 using organization scope, not user scope, to preserve the intended per-org rollout behavior.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/app/api/integrations/slack/events/utils/run-agent/run-agent.ts, line 556:
<comment>Evaluate `slack-mcp-v2` using organization scope, not user scope, to preserve the intended per-org rollout behavior.</comment>
<file context>
@@ -553,11 +553,7 @@ export async function runSlackAgent(
- params.organizationId,
- { groups: { organization: params.organizationId } },
- ),
+ await posthog.getFeatureFlag(FEATURE_FLAGS.SLACK_MCP_V2, params.userId),
);
} catch (error) {
</file context>
| await posthog.getFeatureFlag(FEATURE_FLAGS.SLACK_MCP_V2, params.userId), | |
| await posthog.getFeatureFlag( | |
| FEATURE_FLAGS.SLACK_MCP_V2, | |
| params.organizationId, | |
| { groups: { organization: params.organizationId } }, | |
| ), |
Member add/remove flows have UI-side concerns (invite emails,
ownership transfer, audit) that don't belong in a programmatic
API yet. Keep the read-only `list` endpoint; add/remove stay
in the app via existing organization.{addMember,removeMember}
procedures.
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/sdk/src/resources/organization.ts">
<violation number="1" location="packages/sdk/src/resources/organization.ts:26">
P1: This change makes `organization.members` read-only in the SDK by removing `add/remove`, which is a breaking API change for existing SDK users.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
| export class Organization extends APIResource { | ||
| /** | ||
| * Member listing for the active organization. Member add/remove | ||
| * intentionally lives in the app — the programmatic API is read-only. |
There was a problem hiding this comment.
P1: This change makes organization.members read-only in the SDK by removing add/remove, which is a breaking API change for existing SDK users.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/sdk/src/resources/organization.ts, line 26:
<comment>This change makes `organization.members` read-only in the SDK by removing `add/remove`, which is a breaking API change for existing SDK users.</comment>
<file context>
@@ -18,37 +18,12 @@ export class Members extends APIResource {
/**
- * Member management for the active organization.
+ * Member listing for the active organization. Member add/remove
+ * intentionally lives in the app — the programmatic API is read-only.
*/
members: Members = new Members(this._client);
</file context>
Tip: Review your code locally with the cubic CLI to iterate faster.
Changes since v0.2.11: - workspaces: add `superset workspaces update <id> --name "..."` for renaming. Branch/host moves stay desktop-only — they require host-side git orchestration that isn't safe to drive from the cloud directly. (#4189) - organization: new `superset organization members list` command (also exposed via SDK + mcp-v2). Read-only — add/remove keep their existing UI flows. (#4197) - tasks: new `superset tasks statuses list` command surfacing the organization's configured task statuses. (#4197) - host-service: paste a closed/merged PR URL or `#N` into the v2 new-workspace modal and the dropdown actually returns the PR instead of "No open pull requests found." Direct lookups now ignore the open-only state filter that bounds text-search results. (#4190) Push cli-v0.2.12 after this lands to fire the release pipeline.
Changes since alpha.8:
- workspaces.update accepts `{ name }` for renaming. Branch/host moves
stay desktop-only — they require host-side git orchestration that
isn't safe to drive from the cloud. (#4189)
- organization.members.list — read-only listing of org members.
add/remove deliberately not exposed; they have UI-side concerns
(invites, ownership transfer, audit) that aren't ready for a
programmatic API. (#4197)
- task.statuses.list — list configured task statuses for the org.
(#4197)
After merge: `cd packages/sdk && bun run build && cd dist && npm publish --access public`.
Recorded as integrated via -s ours after batch PRs #455-#464. Taken via individual PRs: - PR 1 (#455): v2 polish 前半 safe set (9 commits) - PR 2 (#456): v2/host-service polish 中盤 (12 commits) - PR 3 (#457): sidebar polish + jwt API (5 commits) - PR 4 (#458): host-service tRPC retry/cache/timeout (3 commits) - PR 5 (#459): v2 diff pane / file pane polish (2 commits) - PR 7 (#462): host-service v2 canonical workspace.create + attachment store (PR1 superset-sh#3893 + PR2 superset-sh#3916) - PR 11 (#463): agents API + onboarding (7 commits + 1 cleanup) - PR 12 (#464): v1→v2 import flow rewrite (11 commits + 2 follow-ups) - PR 13 (#460): host-service shell env probe + typo (2 commits) - PR 16 (#461): marketplace 19 themes (1 commit) Skipped / deferred (recorded as integrated for behind=0): - PR 6: CLI v1 launch (superset-sh#3898 + 30+ CLI/SDK followups) — defer to dedicated migration - PR 9: v2 PR3 (superset-sh#3940) + revert (superset-sh#4017) — net-zero pair - PR 10: pty-daemon (superset-sh#3896, superset-sh#3971, superset-sh#4054) — fork keeps its terminal-host - PR 14: Slack MCP-v2 (superset-sh#4197, superset-sh#4208) — depends on mcp-v2/sdk divergence - PR 15: onboarding remaining (superset-sh#4115, superset-sh#4125, superset-sh#4214, superset-sh#4213, superset-sh#4222, superset-sh#4225) — depends on fork's deleted setup pages Behind: 0 after this merge.
Summary
slack-mcp-v2PostHog flag (evaluated per-organization) routes the Slack agent to@superset/mcp-v2instead of@superset/mcp. Off → v1, on → v2. Two MCP clients constructed inmcp-clients.ts; the agent picks one inrunSlackAgent.organization_members_list,tasks_statuses_list) to callcreateMcpCaller(ctx). No more direct DB imports. Matches how every other v2 tool works (tasks_*,workspaces_*, etc).organization.members.list/add/remove—add/removeare thin wrappers over the existingorganization.addMember/removeMemberlogic (kept in place for the existing dashboard callers).task.statuses.list— new sub-router ontaskRouter.New surface
organization.members.{list,add,remove},task.statuses.listorganization_members_list,tasks_statuses_listsuperset organization members list,superset tasks statuses listclient.organization.members.{list,add,remove},client.tasks.statuses.listSlack agent specifics
organization_members_list+tasks_statuses_list+hosts_list(matches v1'slist_members/list_task_statuses/list_devices).getActionFromToolResulthandles both wire shapes (v1{ created/updated/deleted }, v2{ task, txid }/{ workspace, ... }).TOOL_PROGRESS_STATUSand a per-version deny list cover both naming conventions.onToolCallso PostHogmcp_tool_calledevents fire on the v2 Slack path too.Test plan
bun run lintclean,bun run typecheck29/29, trpcbun test44 pass).@mentionsstill work end-to-end (v1 path, no behavior change expected).slack-mcp-v2on for one test org in PostHog →@mentionshould still work, withmcp_server: superset-v2properties onmcp_tool_calledevents.superset organization members list,superset tasks statuses list.client.organization.members.list()andclient.tasks.statuses.list()against staging.Summary by cubic
Adds a per-user
slack-mcp-v2flag to route the Slack agent to@superset/mcp-v2, and aligns nested APIs across tRPC/mcp‑v2/CLI/SDK for organization members (list-only) and task statuses. Default stays on v1 for a safe rollout with consistent tooling and telemetry.New Features
slack-mcp-v2(person-level; off → v1, on → v2).@superset/mcp-v2is tRPC‑only; adds toolsorganization_members_listandtasks_statuses_list; in‑memory client emitsmcp_tool_called.organization.members.listandtask.statuses.list; CLIsuperset organization members listandsuperset tasks statuses list; SDKclient.organization.members.list()(read‑only) andclient.tasks.statuses.list().Migration
slack-mcp-v2for a test user in PostHog. Organization member add/remove remain app-only.Written for commit 4e397f4. Summary will update on new commits.
Summary by CodeRabbit
New Features
Enhancements