Skip to content

feat(docs): Add MCP improvements / improve MCP local command flakiness#1608

Merged
saddlepaddle merged 5 commits into
mainfrom
mcp-auth-flaky
Feb 22, 2026
Merged

feat(docs): Add MCP improvements / improve MCP local command flakiness#1608
saddlepaddle merged 5 commits into
mainfrom
mcp-auth-flaky

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented Feb 19, 2026

Summary

  • MCP improvements: added get_workspace_details tool, unified start_claude_session/start_claude_subagent into a single tool with paneId parameter
  • ResourceCard fix: internal links no longer open in new tabs

Test plan

  • bun run lint:fix passes
  • bun run typecheck passes (17/17 packages)
  • bun test passes (1230 pass, 0 fail)
  • bunx sherif --fix passes
  • bun run --filter @superset/docs build produces 18 static pages
  • MCP tools tested via Claude Code: list_devices, list_members, list_tasks, list_projects, list_workspaces, get_app_context, get_workspace_details, get_task, update_task all respond correctly

Summary by CodeRabbit

  • New Features

    • Workspace details tool provides visibility into workspace structure, tabs, and panes
    • Claude sessions support optional terminal pane creation in existing tabs (consolidated subagent flow)
    • Command status updates now persist with automatic retry logic for improved reliability
    • External documentation links now open in a new tab for safety
  • Documentation

    • New guides: Parallel Agents, Task Management, Workflow
    • Revamped overview and reorganized docs navigation and grouping

…parallel agents pages

Inspired by Conductor's documentation architecture:
- Add workflow.mdx, task-management.mdx, parallel-agents.mdx
- Rewrite overview as card-grid welcome/landing page
- Reorganize navigation into 5 sections (Get Started, Core Features, Guides, Tips, Help)
- Fix ResourceCard to support internal links
- Update MCP docs with get_workspace_details and unified start_claude_session
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 19, 2026

📝 Walkthrough

Walkthrough

Adds server-persisted command status with retrying TRPC calls, consolidates Claude subagent into a pane-aware start session tool, introduces workspace-introspection tools for desktop and MCP, removes collection onUpdate syncing, and updates documentation, navigation, and a ResourceCard link behavior.

Changes

Cohort / File(s) Summary
Command persistence
apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/persistCommandStatus.ts, apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/useCommandWatcher.ts
New persistCommandStatus uses a TRPC httpBatchLink client with superjson and retry logic (attempts: 4; delays: 500ms, 1s, 2s). Integrated into command lifecycle (completed/failed/timeout) to persist status; local optimistic updates remain.
Workspace introspection tools
apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/get-workspace-details.ts, packages/mcp/src/tools/devices/get-workspace-details/...
Adds desktop and MCP implementations of get_workspace_details returning workspace metadata (workspace, activeTabId, tabs, panes) for discovering pane IDs.
Claude session / subagent consolidation
apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/start-claude-session.ts, apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/start-claude-subagent.ts (removed), packages/mcp/src/tools/devices/start-claude-session/start-claude-session.ts
Removes dedicated subagent file and extends start_claude_session schema to accept optional paneId; when provided, tools create/use a pane instead of initializing a new workspace.
Tool registry updates
apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/index.ts, packages/mcp/src/tools/index.ts
Registers new get_workspace_details, removes local start_claude_subagent entry, and adds MCP registration for workspace-details in the MCP tools list.
Collections / sync removal
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts
Removed onUpdate callback for agent_commands collection (removes automatic server mutation path that called apiClient.agent.updateCommand).
Docs and navigation
apps/docs/content/docs/*.mdx, apps/docs/content/docs/meta.json, apps/docs/next.config.mjs
Major docs additions and restructuring (new pages: parallel-agents, workflow, task-management; overview rewrite), meta.json reorganization, and redirects updated to /overview.
UI tweak
apps/docs/src/components/ResourceCard/ResourceCard.tsx
Detects external links and conditionally adds target="_blank" and rel="noopener noreferrer" for external hrefs.
MCP tool text/exports
packages/mcp/src/tools/devices/*
Adds/exported register for get-workspace-details; updates tool descriptions to clarify device ownership and pane behavior; updates tools index to include the new register.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant AgentWatcher as AgentWatcher
participant TabsStore as TabsStore
participant TRPCClient as TRPC Client
participant API as MCP/API Server
participant DB as Database
AgentWatcher->>TabsStore: read pane/tab for paneId (optional)
AgentWatcher->>TRPCClient: persistCommandStatus(params)
TRPCClient->>API: HTTP batch request (Bearer token)
API->>DB: update command row
DB-->>API: ack
API-->>TRPCClient: response
TRPCClient-->>AgentWatcher: success / error
alt retry on failure
TRPCClient->>API: retry attempts (500ms,1s,2s)
end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through panes and saved each state,
I nibbled retries when networks wait,
Folded subagents into cozy tabs,
Peeked at workspaces, listed their labs,
Docs now bloom — small changes, giant gates.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title mentions 'MCP improvements' and 'improve MCP local command flakiness', but the changeset includes substantial documentation additions (7 new/modified MDX files, meta.json reorganization), feature changes (command persistence, tool consolidation), and ResourceCard fixes. The title incompletely captures the full scope.
Description check ✅ Passed The PR description provides a clear summary of changes and comprehensive test results covering linting, type checking, unit tests, and manual MCP tool validation, though it doesn't follow the repository's template structure with sections like 'Related Issues' or 'Type of Change'.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mcp-auth-flaky

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@saddlepaddle saddlepaddle changed the title feat(docs): restructure docs and add MCP improvements feat(docs): Add MCP improvements Feb 19, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 19, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ✅ Electric Fly.io app

Thank you for your contribution! 🎉

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts (1)

209-222: ⚠️ Potential issue | 🟡 Minor

Fix the unawaited persistCommandStatus call in the timeout case.

The agentCommands collection updates are intentionally optimistic-only for intermediate statuses (claimed, executing), with server persistence happening only through explicit persistCommandStatus() calls for final states. This design is sound and documented in the code comment.

However, there is an inconsistency: the processCommand handler awaits persistCommandStatus() for both "completed" and "failed" states, but the timeout handling at line 190 does not await the call. This creates a reliability gap — if the device crashes or unmounts before the retry logic completes, the timeout persistence may be lost.

Timeout case (not awaited)
persistCommandStatus({
    id: cmd.id,
    status: "timeout",
    error: "Command expired before execution",
});

Add await to match the pattern in the handler:

await persistCommandStatus({
    id: cmd.id,
    status: "timeout",
    error: "Command expired before execution",
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts`
around lines 209 - 222, The timeout branch currently calls
persistCommandStatus(...) without awaiting it, causing potential loss of the
final "timeout" state; update the timeout handling inside the processCommand
flow to await persistCommandStatus({ id: cmd.id, status: "timeout", error:
"Command expired before execution" }) so it matches the awaited calls for
"completed" and "failed" states and ensures the server persistence completes
before proceeding; locate the timeout logic near the processCommand handler that
updates the agentCommands collection and change the call to use await
persistCommandStatus(...) so persistence is reliable.
🧹 Nitpick comments (7)
packages/mcp/src/tools/devices/switch-workspace/switch-workspace.ts (1)

26-28: Redundant as casts — consider removing them.

Zod already infers the correct types from the schema (string, string | undefined), so the explicit as casts add noise without providing any type safety benefit.

♻️ Proposed cleanup
-		const deviceId = args.deviceId as string;
-		const workspaceId = args.workspaceId as string | undefined;
-		const workspaceName = args.workspaceName as string | undefined;
+		const { deviceId, workspaceId, workspaceName } = args;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mcp/src/tools/devices/switch-workspace/switch-workspace.ts` around
lines 26 - 28, Redundant explicit type assertions are used when extracting args
(deviceId, workspaceId, workspaceName); remove the unnecessary "as string" and
"as string | undefined" casts in the switchWorkspace handler so the variables
rely on Zod-inferred types, i.e., change assignments that reference
args.deviceId, args.workspaceId, and args.workspaceName to plain variable
bindings without "as" casts and ensure any downstream usages rely on their
inferred types.
apps/docs/src/components/ResourceCard/ResourceCard.tsx (1)

30-35: Use next/link <Link> for internal routes to enable client-side navigation and prefetching.

ResourceCard is a Next.js MDX component that currently renders both internal and external links as plain <a> tags. Internal routes should use Next.js <Link> for client-side navigation, prefetching, and scroll restoration. This pattern is already established elsewhere in the docs app—NavigationBar's NavLink helper (lines 103–125) demonstrates the exact approach: conditionally rendering Link for internal hrefs and <a target="_blank"> for external links.

♻️ Suggested refactor

Add "use client" directive and import at the top:

+"use client";
+
 import { ArrowUpRight } from "lucide-react";
 import { cn } from "@/lib/cn";
+import Link from "next/link";

Then swap the link element:

-				<a
-					href={href}
-					{...(isExternal
-						? { target: "_blank", rel: "noopener noreferrer" }
-						: {})}
-				>
-					<h3 className="font-semibold text-md tracking-tight no-underline">
-						{title}
-					</h3>
-				</a>
+				{isExternal ? (
+					<a href={href} target="_blank" rel="noopener noreferrer">
+						<h3 className="font-semibold text-md tracking-tight no-underline">
+							{title}
+						</h3>
+					</a>
+				) : (
+					<Link href={href}>
+						<h3 className="font-semibold text-md tracking-tight no-underline">
+							{title}
+						</h3>
+					</Link>
+				)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/docs/src/components/ResourceCard/ResourceCard.tsx` around lines 30 - 35,
ResourceCard currently renders all links as raw <a> tags; change it to use
Next.js client-side Link for internal routes: add the "use client" directive at
the top of ResourceCard and import Link from "next/link", then in the JSX
replace the unconditional <a href={href} ...> with a conditional render that
uses <Link href={href}>...</Link> when isExternal is false and the existing <a
target="_blank" rel="noopener noreferrer"> when isExternal is true (follow the
NavLink pattern used elsewhere for reference). Ensure href and isExternal props
used by ResourceCard are passed into the Link correctly and that external links
keep the security attributes.
packages/mcp/src/tools/devices/get-workspace-details/get-workspace-details.ts (1)

11-13: Consider adding .uuid() validation to workspaceId.

workspaceId should be validated as a UUID for consistency with other workspace-modifying tools. update-workspace.ts uses z.string().uuid() and delete-workspace.ts validates workspaceIds as UUIDs. If workspace IDs are always UUIDs in the system, adding this validation would catch malformed IDs earlier.

Proposed change
 inputSchema: {
 	deviceId: z.string().describe("Target device ID"),
-	workspaceId: z.string().describe("Workspace ID to get details for"),
+	workspaceId: z.string().uuid().describe("Workspace ID to get details for"),
 },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/mcp/src/tools/devices/get-workspace-details/get-workspace-details.ts`
around lines 11 - 13, The input schema in get-workspace-details.ts currently
defines workspaceId as z.string(); update the inputSchema entry for workspaceId
to use z.string().uuid() (matching other handlers like update-workspace.ts and
delete-workspace.ts) so workspace IDs are validated as UUIDs; locate the
inputSchema object in the getWorkspaceDetails handler (the inputSchema with
deviceId and workspaceId) and change the workspaceId validator to .uuid() to
enforce consistent ID format.
apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/useCommandWatcher.ts (1)

194-198: Floating promise from persistCommandStatus inside .filter() callback.

persistCommandStatus returns a Promise<void> but is called without await inside the synchronous .filter(). While the function handles its own errors internally, this is still a floating promise. Consider moving timeout handling into its own loop before the filter, or using void persistCommandStatus(...) to signal intentional fire-and-forget.

Minimal signal of intent
-			persistCommandStatus({
+			void persistCommandStatus({
 				id: cmd.id,
 				status: "timeout",
 				error: "Command expired before execution",
 			});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/useCommandWatcher.ts`
around lines 194 - 198, In useCommandWatcher, the call to persistCommandStatus
inside the .filter() callback creates a floating Promise; either handle timeouts
in a separate loop before calling .filter() or explicitly mark the
fire-and-forget intent (e.g., use void persistCommandStatus(...)) so the Promise
is not silently leaked; update the .filter() callback around
persistCommandStatus({ id: cmd.id, status: "timeout", error: "Command expired
before execution" }) to either await the call in an async timeout-handling loop
or prefix it with void to signal intentional non-awaiting.
apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/persistCommandStatus.ts (1)

21-52: Silent swallowing of persistence failures after retries.

After all 4 attempts exhaust, the function logs the error and returns void — callers have no way to detect that persistence failed. Since local state was already optimistically updated, the server and client will silently diverge. This is acceptable if eventual consistency is guaranteed elsewhere (e.g., a background sync), but worth documenting or surfacing as a metric/alert.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/persistCommandStatus.ts`
around lines 21 - 52, The function persistCommandStatus currently swallows
failures after retrying; change it so callers can detect failure by re-throwing
the final error (lastError) or returning a failure indicator instead of silently
returning void — update persistCommandStatus to either throw lastError after the
retries or change its return type to a Result/boolean and return false on
exhaustion; ensure the thrown/returned value includes the original error from
apiClient.agent.updateCommand.mutate and keep the existing
console.error/console.warn logging (and optionally emit a metric or call a
telemetry helper) so callers like any code invoking persistCommandStatus can
react to persistence failure.
packages/mcp/src/tools/devices/start-claude-session/start-claude-session.ts (1)

42-54: Type assertions rely on upstream Zod validation.

validateArgs uses as string casts without its own runtime type checks. This is safe because the MCP framework validates inputSchema (Zod) before the handler runs, but it makes validateArgs fragile if called from a different context.

Alternatively, reuse the Zod schema for validation
-function validateArgs(args: Record<string, unknown>): {
-	deviceId: string;
-	taskId: string;
-	workspaceId: string;
-	paneId?: string;
-} | null {
-	const deviceId = args.deviceId as string;
-	const taskId = args.taskId as string;
-	const workspaceId = args.workspaceId as string;
-	const paneId = args.paneId as string | undefined;
-	if (!deviceId || !taskId || !workspaceId) return null;
-	return { deviceId, taskId, workspaceId, ...(paneId ? { paneId } : {}) };
-}
+const argsSchema = z.object({
+	deviceId: z.string().min(1),
+	taskId: z.string().min(1),
+	workspaceId: z.string().min(1),
+	paneId: z.string().optional(),
+});
+
+function validateArgs(args: Record<string, unknown>) {
+	const result = argsSchema.safeParse(args);
+	return result.success ? result.data : null;
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mcp/src/tools/devices/start-claude-session/start-claude-session.ts`
around lines 42 - 54, validateArgs currently uses "as string" casts without
runtime checks, making it fragile if called outside the MCP-validated path; fix
it by either (A) performing runtime type checks inside validateArgs (use typeof
on args.deviceId, args.taskId, args.workspaceId and allow paneId only if typeof
=== 'string', return null on any mismatch) or (B) reuse the existing Zod input
schema (e.g., inputSchema.safeParse(args) or inputSchema.parse within a
try/catch) to validate and extract deviceId, taskId, workspaceId, and optional
paneId, then return the typed object or null on failure. Ensure you reference
the validateArgs function and the inputSchema symbol so the implementation
replaces the unsafe "as string" casts with real validation.
apps/docs/content/docs/task-management.mdx (1)

39-45: Clarify that the fenced block contains example agent prompts, not executable code.

The unlabeled code fence may read as a generic snippet. A short lead-in sentence makes intent explicit.

✏️ Suggested copy tweak
 AI agents can manage tasks programmatically via the [MCP server](/mcp):
 
+For example, you can tell an agent:
+

"Create a task for fixing the login validation bug"
"List all my high-priority tasks"
"Update the auth task status to done"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/docs/content/docs/task-management.mdx` around lines 39 - 45, Add a
one-line lead-in before the fenced block clarifying these are example AI agent
prompts (not executable code) so readers understand intent; update the copy
immediately above the fence that contains the three sample prompts ("Create a
task for fixing the login validation bug", "List all my high-priority tasks",
"Update the auth task status to done") to a sentence like “Example agent
prompts:” (or similar) to make the snippet explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/persistCommandStatus.ts`:
- Around line 8-19: Replace the duplicated tRPC client in
persistCommandStatus.ts by importing and using the existing apiTrpcClient
(instead of creating a new apiClient via createTRPCProxyClient) so all calls
reuse the centralized configuration from renderer/lib/api-trpc-client.ts; also
update the persistence calls made by the functions in persistCommandStatus (the
methods that call the backend to persist command state) to include the local
claimedAt value that useCommandWatcher.ts sets on the command, ensuring
claimedAt is sent with the payload so server state matches the local command
state.

In
`@apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/useCommandWatcher.ts`:
- Around line 110-116: The local optimistic update sets draft.claimedAt = new
Date() but that timestamp is never sent to the backend; capture that timestamp
into a claimedAt variable when you set draft.claimedAt and include claimedAt in
every persistCommandStatus call (the calls inside useCommandWatcher that pass
id/status/claimedBy/result/executedAt) so the server receives the claim time for
completed, failed and cancelled branches; update the persistCommandStatus
payloads (the calls that reference commandId, deviceInfo?.deviceId,
result/executedAt/etc.) to include this claimedAt value.

In `@apps/docs/content/docs/parallel-agents.mdx`:
- Around line 20-28: Supported Agents table in parallel-agents.mdx lists Gemini
CLI and "Any CLI agent" but agent-integration.mdx only lists Claude Code, Codex,
and OpenCode; update the agent lists to be consistent by either adding Gemini
and a note about generic CLI agents to agent-integration.mdx or removing those
entries from the Supported Agents table in parallel-agents.mdx so both pages
mention the same agents (reference the "Supported Agents" table and the
agent-integration.mdx agent list entries like Claude Code, Codex, OpenCode,
Gemini CLI, and Any CLI agent when making the change).

---

Outside diff comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts`:
- Around line 209-222: The timeout branch currently calls
persistCommandStatus(...) without awaiting it, causing potential loss of the
final "timeout" state; update the timeout handling inside the processCommand
flow to await persistCommandStatus({ id: cmd.id, status: "timeout", error:
"Command expired before execution" }) so it matches the awaited calls for
"completed" and "failed" states and ensures the server persistence completes
before proceeding; locate the timeout logic near the processCommand handler that
updates the agentCommands collection and change the call to use await
persistCommandStatus(...) so persistence is reliable.

---

Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/persistCommandStatus.ts`:
- Around line 21-52: The function persistCommandStatus currently swallows
failures after retrying; change it so callers can detect failure by re-throwing
the final error (lastError) or returning a failure indicator instead of silently
returning void — update persistCommandStatus to either throw lastError after the
retries or change its return type to a Result/boolean and return false on
exhaustion; ensure the thrown/returned value includes the original error from
apiClient.agent.updateCommand.mutate and keep the existing
console.error/console.warn logging (and optionally emit a metric or call a
telemetry helper) so callers like any code invoking persistCommandStatus can
react to persistence failure.

In
`@apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/useCommandWatcher.ts`:
- Around line 194-198: In useCommandWatcher, the call to persistCommandStatus
inside the .filter() callback creates a floating Promise; either handle timeouts
in a separate loop before calling .filter() or explicitly mark the
fire-and-forget intent (e.g., use void persistCommandStatus(...)) so the Promise
is not silently leaked; update the .filter() callback around
persistCommandStatus({ id: cmd.id, status: "timeout", error: "Command expired
before execution" }) to either await the call in an async timeout-handling loop
or prefix it with void to signal intentional non-awaiting.

In `@apps/docs/content/docs/task-management.mdx`:
- Around line 39-45: Add a one-line lead-in before the fenced block clarifying
these are example AI agent prompts (not executable code) so readers understand
intent; update the copy immediately above the fence that contains the three
sample prompts ("Create a task for fixing the login validation bug", "List all
my high-priority tasks", "Update the auth task status to done") to a sentence
like “Example agent prompts:” (or similar) to make the snippet explicit.

In `@apps/docs/src/components/ResourceCard/ResourceCard.tsx`:
- Around line 30-35: ResourceCard currently renders all links as raw <a> tags;
change it to use Next.js client-side Link for internal routes: add the "use
client" directive at the top of ResourceCard and import Link from "next/link",
then in the JSX replace the unconditional <a href={href} ...> with a conditional
render that uses <Link href={href}>...</Link> when isExternal is false and the
existing <a target="_blank" rel="noopener noreferrer"> when isExternal is true
(follow the NavLink pattern used elsewhere for reference). Ensure href and
isExternal props used by ResourceCard are passed into the Link correctly and
that external links keep the security attributes.

In
`@packages/mcp/src/tools/devices/get-workspace-details/get-workspace-details.ts`:
- Around line 11-13: The input schema in get-workspace-details.ts currently
defines workspaceId as z.string(); update the inputSchema entry for workspaceId
to use z.string().uuid() (matching other handlers like update-workspace.ts and
delete-workspace.ts) so workspace IDs are validated as UUIDs; locate the
inputSchema object in the getWorkspaceDetails handler (the inputSchema with
deviceId and workspaceId) and change the workspaceId validator to .uuid() to
enforce consistent ID format.

In `@packages/mcp/src/tools/devices/start-claude-session/start-claude-session.ts`:
- Around line 42-54: validateArgs currently uses "as string" casts without
runtime checks, making it fragile if called outside the MCP-validated path; fix
it by either (A) performing runtime type checks inside validateArgs (use typeof
on args.deviceId, args.taskId, args.workspaceId and allow paneId only if typeof
=== 'string', return null on any mismatch) or (B) reuse the existing Zod input
schema (e.g., inputSchema.safeParse(args) or inputSchema.parse within a
try/catch) to validate and extract deviceId, taskId, workspaceId, and optional
paneId, then return the typed object or null on failure. Ensure you reference
the validateArgs function and the inputSchema symbol so the implementation
replaces the unsafe "as string" casts with real validation.

In `@packages/mcp/src/tools/devices/switch-workspace/switch-workspace.ts`:
- Around line 26-28: Redundant explicit type assertions are used when extracting
args (deviceId, workspaceId, workspaceName); remove the unnecessary "as string"
and "as string | undefined" casts in the switchWorkspace handler so the
variables rely on Zod-inferred types, i.e., change assignments that reference
args.deviceId, args.workspaceId, and args.workspaceName to plain variable
bindings without "as" casts and ensure any downstream usages rely on their
inferred types.

Comment on lines +8 to +19
const apiClient = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: `${env.NEXT_PUBLIC_API_URL}/api/trpc`,
headers: () => {
const token = getAuthToken();
return token ? { Authorization: `Bearer ${token}` } : {};
},
transformer: superjson,
}),
],
});
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for existing tRPC clients in the desktop app
rg -n "createTRPCProxyClient|httpBatchLink" --type=ts --type=tsx -C3 apps/desktop/src/

Repository: superset-sh/superset

Length of output: 91


🏁 Script executed:

# Check the structure of src/lib/trpc and what it exports
find apps/desktop/src -path "*lib/trpc*" -type f | head -20

Repository: superset-sh/superset

Length of output: 1203


🏁 Script executed:

# Check if there's a shared API client pattern
rg -n "tRPC.*client|API.*client" --type=ts --type=tsx apps/desktop/src/ -C2 | head -50

Repository: superset-sh/superset

Length of output: 91


🏁 Script executed:

# Look at the file structure to understand the app organization
tree -L 3 apps/desktop/src/ -d 2>/dev/null || find apps/desktop/src -type d | head -20

Repository: superset-sh/superset

Length of output: 1897


🏁 Script executed:

# Check what src/lib/trpc exports
cat -n apps/desktop/src/lib/trpc/index.ts

Repository: superset-sh/superset

Length of output: 1642


🏁 Script executed:

# Check the ElectronTRPCProvider to understand how tRPC is used
find apps/desktop/src/renderer -name "*ElectronTRPC*" -o -name "*electron-trpc*" | head -10

Repository: superset-sh/superset

Length of output: 249


🏁 Script executed:

# Look for API client patterns in the renderer
rg -l "apiClient|api\..*\(" apps/desktop/src/renderer --max-count=20

Repository: superset-sh/superset

Length of output: 317


🏁 Script executed:

# Check the persistCommandStatus file itself
cat -n apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/persistCommandStatus.ts

Repository: superset-sh/superset

Length of output: 1831


🏁 Script executed:

# Check ElectronTRPCProvider to see how it's structured
cat -n apps/desktop/src/renderer/providers/ElectronTRPCProvider/ElectronTRPCProvider.tsx

Repository: superset-sh/superset

Length of output: 1272


🏁 Script executed:

# Check the electron-trpc.ts file
cat -n apps/desktop/src/renderer/lib/electron-trpc.ts

Repository: superset-sh/superset

Length of output: 615


🏁 Script executed:

# Look for other HTTP API client creations in the renderer
rg "httpBatchLink|createTRPCProxyClient|NEXT_PUBLIC_API_URL" apps/desktop/src/renderer --max-count=30

Repository: superset-sh/superset

Length of output: 3845


🏁 Script executed:

# Check useCommandWatcher to understand the context and claimedAt usage
find apps/desktop/src/renderer -name "*useCommandWatcher*" -type f

Repository: superset-sh/superset

Length of output: 178


🏁 Script executed:

# Check the existing api-trpc-client.ts
cat -n apps/desktop/src/renderer/lib/api-trpc-client.ts

Repository: superset-sh/superset

Length of output: 1025


🏁 Script executed:

# Check useCommandWatcher.ts to see how persistCommandStatus is used
cat -n apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/useCommandWatcher.ts

Repository: superset-sh/superset

Length of output: 8548


🏁 Script executed:

# Check collections.ts to see if it also has the same duplicate pattern
cat -n apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts | head -50

Repository: superset-sh/superset

Length of output: 2058


Use the existing apiTrpcClient instead of duplicating the tRPC client configuration.

An identical HTTP tRPC client already exists in renderer/lib/api-trpc-client.ts with the same URL, authentication headers, and transformer. Creating a new instance here duplicates configuration and risks divergence if the API client setup changes. Import and use apiTrpcClient from that module instead.

Additionally, the local command state sets claimedAt (line 94 in useCommandWatcher.ts), but it's not included in the persistence calls—this creates a data inconsistency between local and server state.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/persistCommandStatus.ts`
around lines 8 - 19, Replace the duplicated tRPC client in
persistCommandStatus.ts by importing and using the existing apiTrpcClient
(instead of creating a new apiClient via createTRPCProxyClient) so all calls
reuse the centralized configuration from renderer/lib/api-trpc-client.ts; also
update the persistence calls made by the functions in persistCommandStatus (the
methods that call the backend to persist command state) to include the local
claimedAt value that useCommandWatcher.ts sets on the command, ensuring
claimedAt is sent with the payload so server state matches the local command
state.

Comment on lines +20 to +28
## Supported Agents

| Agent | Command |
|-------|---------|
| **Claude Code** | `claude` |
| **Codex** | `codex` |
| **Gemini CLI** | `gemini` |
| **OpenCode** | `opencode` |
| **Any CLI agent** | Works in the terminal |
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.

⚠️ Potential issue | 🟡 Minor

Minor inconsistency: agent-integration.mdx lists only three agents.

The Supported Agents table here includes Gemini CLI (line 26) and "Any CLI agent" (line 28), but agent-integration.mdx (lines 14–16) only lists Claude Code, Codex, and OpenCode. Consider keeping both pages in sync to avoid confusion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/docs/content/docs/parallel-agents.mdx` around lines 20 - 28, Supported
Agents table in parallel-agents.mdx lists Gemini CLI and "Any CLI agent" but
agent-integration.mdx only lists Claude Code, Codex, and OpenCode; update the
agent lists to be consistent by either adding Gemini and a note about generic
CLI agents to agent-integration.mdx or removing those entries from the Supported
Agents table in parallel-agents.mdx so both pages mention the same agents
(reference the "Supported Agents" table and the agent-integration.mdx agent list
entries like Claude Code, Codex, OpenCode, Gemini CLI, and Any CLI agent when
making the change).

@saddlepaddle saddlepaddle changed the title feat(docs): Add MCP improvements feat(docs): Add MCP improvements / improve MCP local command flakiness Feb 19, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/useCommandWatcher.ts (1)

192-203: ⚠️ Potential issue | 🟡 Minor

Timed-out commands should be added to handledCommands before returning.

When a command times out, handledCommands.add(cmd.id) is never called. Because the useEffect dependency array includes pendingCommands, if the effect re-fires before the optimistic "timeout" status update propagates out of the live-query (a plausible timing edge given React batching and useLiveQuery internals), the same command will pass the filter again — triggering a second collections.agentCommands.update and a second fire-and-forget persistCommandStatus call.

persistCommandStatus is idempotent and the local update is harmless, so there's no correctness breakage, but the extra retry loop (up to ~3.5 s) could fire unnecessarily. Adding the guard eliminates the edge case entirely and keeps the pattern consistent with the rest of the watcher:

🛡️ Proposed fix
 		if (cmd.timeoutAt && new Date(cmd.timeoutAt) < now) {
+			handledCommands.add(cmd.id);
 			collections.agentCommands.update(cmd.id, (draft) => {
 				draft.status = "timeout";
 				draft.error = "Command expired before execution";
 			});
 			persistCommandStatus({
 				id: cmd.id,
 				status: "timeout",
 				error: "Command expired before execution",
 			});
 			return false;
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/useCommandWatcher.ts`
around lines 192 - 203, When a command times out in the timeout handling block
inside useCommandWatcher, add the command id to handledCommands before returning
to prevent the effect from re-processing it; specifically, in the branch that
calls collections.agentCommands.update(...) and persistCommandStatus({ id:
cmd.id, status: "timeout", ... }), also call handledCommands.add(cmd.id) prior
to the return false so the command is marked handled (consistent with how other
branches use handledCommands and avoids duplicate retries from useEffect with
pendingCommands).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/useCommandWatcher.ts`:
- Around line 192-203: When a command times out in the timeout handling block
inside useCommandWatcher, add the command id to handledCommands before returning
to prevent the effect from re-processing it; specifically, in the branch that
calls collections.agentCommands.update(...) and persistCommandStatus({ id:
cmd.id, status: "timeout", ... }), also call handledCommands.add(cmd.id) prior
to the return false so the command is marked handled (consistent with how other
branches use handledCommands and avoids duplicate retries from useEffect with
pendingCommands).

@saddlepaddle saddlepaddle merged commit 23c61b7 into main Feb 22, 2026
14 checks passed
saddlepaddle added a commit that referenced this pull request Feb 22, 2026
The docs restructure (new MDX pages, overview rewrite, meta.json
reorganization, redirect changes) was part of a separate docs task
that was accidentally included in the MCP auth flakiness PR (#1608).

This reverts only the docs-task-specific changes:
- Remove workflow.mdx, task-management.mdx, parallel-agents.mdx
- Restore overview.mdx, meta.json, agent-integration.mdx to original
- Restore next.config.mjs redirect to /installation
- Restore ResourceCard to use target="_blank" for all links

MCP-related changes (mcp.mdx, get_workspace_details, etc.) are kept.
saddlepaddle added a commit that referenced this pull request Feb 22, 2026
The docs restructure (new MDX pages, overview rewrite, meta.json
reorganization, redirect changes) was part of a separate docs task
that was accidentally included in the MCP auth flakiness PR (#1608).

This reverts only the docs-task-specific changes:
- Remove workflow.mdx, task-management.mdx, parallel-agents.mdx
- Restore overview.mdx, meta.json, agent-integration.mdx to original
- Restore next.config.mjs redirect to /installation
- Restore ResourceCard to use target="_blank" for all links

MCP-related changes (mcp.mdx, get_workspace_details, etc.) are kept.
@Kitenite Kitenite deleted the mcp-auth-flaky branch February 27, 2026 09:29
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