From 11c52ca5bf9ec189677d93848c879f7c72128bd2 Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Thu, 7 May 2026 08:50:42 -0700 Subject: [PATCH 1/4] feat(cli/sdk/mcp): add workspaces update for renaming Expose workspace updates across all three client surfaces. The cloud v2Workspace.update procedure already supports name/branch/hostId; only name is wired through here since branch/host moves require host-side git orchestration that isn't safe to drive from the cloud directly. - CLI: superset workspaces update --name "..." - SDK: client.workspaces.update(id, { name }) - MCP-v2: workspaces_update tool --- .../src/commands/workspaces/update/command.ts | 31 ++++++++++++++ packages/mcp-v2/src/tools/register.ts | 2 + .../mcp-v2/src/tools/workspaces/update.ts | 20 +++++++++ packages/sdk/src/resources/workspaces.ts | 41 +++++++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 packages/cli/src/commands/workspaces/update/command.ts create mode 100644 packages/mcp-v2/src/tools/workspaces/update.ts diff --git a/packages/cli/src/commands/workspaces/update/command.ts b/packages/cli/src/commands/workspaces/update/command.ts new file mode 100644 index 00000000000..8889c956e21 --- /dev/null +++ b/packages/cli/src/commands/workspaces/update/command.ts @@ -0,0 +1,31 @@ +import { CLIError, positional, string } from "@superset/cli-framework"; +import { command } from "../../../lib/command"; + +export default command({ + description: "Update a workspace", + args: [positional("id").required().desc("Workspace UUID")], + options: { + name: string().desc("Workspace name"), + }, + run: async ({ ctx, args, options }) => { + const id = args.id as string; + const organizationId = ctx.config.organizationId; + if (!organizationId) { + throw new CLIError("No active organization", "Run: superset auth login"); + } + + if (options.name === undefined) { + throw new CLIError("No fields to update", "Pass --name "); + } + + const updated = await ctx.api.v2Workspace.update.mutate({ + id, + name: options.name, + }); + + return { + data: updated, + message: `Updated workspace ${id}`, + }; + }, +}); diff --git a/packages/mcp-v2/src/tools/register.ts b/packages/mcp-v2/src/tools/register.ts index 555d4c624b6..3d40aa6081a 100644 --- a/packages/mcp-v2/src/tools/register.ts +++ b/packages/mcp-v2/src/tools/register.ts @@ -27,6 +27,7 @@ import * as tasksUpdate from "./tasks/update"; import * as workspacesCreate from "./workspaces/create"; import * as workspacesDelete from "./workspaces/delete"; import * as workspacesList from "./workspaces/list"; +import * as workspacesUpdate from "./workspaces/update"; const REGISTRARS = [ tasksList, @@ -47,6 +48,7 @@ const REGISTRARS = [ automationsLogs, workspacesList, workspacesCreate, + workspacesUpdate, workspacesDelete, agentsRun, agentsList, diff --git a/packages/mcp-v2/src/tools/workspaces/update.ts b/packages/mcp-v2/src/tools/workspaces/update.ts new file mode 100644 index 00000000000..11572deb8e8 --- /dev/null +++ b/packages/mcp-v2/src/tools/workspaces/update.ts @@ -0,0 +1,20 @@ +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import { createMcpCaller } from "../../caller"; +import { defineTool } from "../../define-tool"; + +export function register(server: McpServer): void { + defineTool(server, { + name: "workspaces_update", + description: + "Update fields on an existing workspace (cloud-index metadata). Only the fields you pass are changed; omitted fields preserve their current value.", + inputSchema: { + id: z.string().uuid().describe("Workspace UUID."), + name: z.string().min(1).optional().describe("New workspace name."), + }, + handler: async (input, ctx) => { + const caller = createMcpCaller(ctx); + return caller.v2Workspace.update(input); + }, + }); +} diff --git a/packages/sdk/src/resources/workspaces.ts b/packages/sdk/src/resources/workspaces.ts index fb29d323bf8..fc65690e41e 100644 --- a/packages/sdk/src/resources/workspaces.ts +++ b/packages/sdk/src/resources/workspaces.ts @@ -58,6 +58,25 @@ export class Workspaces extends APIResource { ); } + /** + * Update fields on a workspace (cloud-index metadata). Currently only the + * `name` is exposed — git branch and host moves require host-side + * orchestration and aren't safe to set through the cloud directly. + * + * Mirrors `superset workspaces update`. + */ + update( + id: string, + params: WorkspaceUpdateParams, + options?: RequestOptions, + ): APIPromise { + return this._client.mutation( + "v2Workspace.update", + { id, ...params }, + options, + ); + } + /** * Delete a workspace by id. Looks up the host the workspace lives on (via * the cloud index) and routes the delete to that host's service through @@ -179,6 +198,26 @@ export interface WorkspaceCreateResult { alreadyExists: boolean; } +export interface WorkspaceUpdateParams { + /** New workspace name. */ + name?: string; +} + +export interface WorkspaceUpdateResult { + id: string; + name: string; + branch: string; + organizationId: string; + projectId: string; + hostId: string; + type: "main" | "worktree"; + createdByUserId: string | null; + taskId: string | null; + createdAt: Date; + updatedAt: Date; + txid: number; +} + export interface WorkspaceDeleteResult { [key: string]: unknown; } @@ -193,6 +232,8 @@ export declare namespace Workspaces { WorkspaceAgentLaunch, WorkspaceCreateAgentResult, WorkspaceCreateResult, + WorkspaceUpdateParams, + WorkspaceUpdateResult, WorkspaceDeleteResult, }; } From 7fc930c5da3a48e070b186d41d61ac6b3e00af18 Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Thu, 7 May 2026 08:54:49 -0700 Subject: [PATCH 2/4] docs(skill): add workspaces update to superset CLI reference --- skills/superset/SKILL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/skills/superset/SKILL.md b/skills/superset/SKILL.md index f46ea9e514d..02ef88b2914 100644 --- a/skills/superset/SKILL.md +++ b/skills/superset/SKILL.md @@ -32,6 +32,7 @@ If `$SUPERSET_WORKSPACE_ID` is unset, you're not inside a Superset workspace — superset workspaces create --project --host --name "..." --branch superset workspaces create --project --local --name "..." --pr superset workspaces list [--host | --local] +superset workspaces update --name "..." superset workspaces delete [...] ``` From 30df2ecf9ea6efe14e141b080171046b81f5628e Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Thu, 7 May 2026 09:04:10 -0700 Subject: [PATCH 3/4] fix(mcp-v2): pre-flight guard against id-only update payload The CLI throws "No fields to update" client-side when --name is omitted. The MCP tool was forwarding {id} to the server and surfacing an opaque BAD_REQUEST. Mirror the CLI's behaviour with an explicit error and sharpen the tool description so an LLM caller knows at-least-one-field is required. --- packages/mcp-v2/src/tools/workspaces/update.ts | 8 +++++++- packages/sdk/src/resources/workspaces.ts | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/mcp-v2/src/tools/workspaces/update.ts b/packages/mcp-v2/src/tools/workspaces/update.ts index 11572deb8e8..1571e6f3068 100644 --- a/packages/mcp-v2/src/tools/workspaces/update.ts +++ b/packages/mcp-v2/src/tools/workspaces/update.ts @@ -7,12 +7,18 @@ export function register(server: McpServer): void { defineTool(server, { name: "workspaces_update", description: - "Update fields on an existing workspace (cloud-index metadata). Only the fields you pass are changed; omitted fields preserve their current value.", + "Update fields on an existing workspace. At least one field is required.", inputSchema: { id: z.string().uuid().describe("Workspace UUID."), name: z.string().min(1).optional().describe("New workspace name."), }, handler: async (input, ctx) => { + const { id: _id, ...fields } = input; + if (Object.keys(fields).length === 0) { + throw new Error( + "No fields to update. Pass at least one field such as --name.", + ); + } const caller = createMcpCaller(ctx); return caller.v2Workspace.update(input); }, diff --git a/packages/sdk/src/resources/workspaces.ts b/packages/sdk/src/resources/workspaces.ts index fc65690e41e..8013a1a7437 100644 --- a/packages/sdk/src/resources/workspaces.ts +++ b/packages/sdk/src/resources/workspaces.ts @@ -59,9 +59,9 @@ export class Workspaces extends APIResource { } /** - * Update fields on a workspace (cloud-index metadata). Currently only the - * `name` is exposed — git branch and host moves require host-side - * orchestration and aren't safe to set through the cloud directly. + * Update fields on a workspace. At least one field is required. Currently + * only `name` is exposed — branch and host moves require host-side + * orchestration and aren't safe to set directly. * * Mirrors `superset workspaces update`. */ From d3f10b84c87e80ec254ebb28edf79c3dde169248 Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Thu, 7 May 2026 09:09:15 -0700 Subject: [PATCH 4/4] revert(mcp-v2): drop pre-flight guard on workspaces_update Description already states at-least-one-field; client-side guard adds boilerplate without saving a round-trip. --- packages/mcp-v2/src/tools/workspaces/update.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/mcp-v2/src/tools/workspaces/update.ts b/packages/mcp-v2/src/tools/workspaces/update.ts index 1571e6f3068..283f7ab54d7 100644 --- a/packages/mcp-v2/src/tools/workspaces/update.ts +++ b/packages/mcp-v2/src/tools/workspaces/update.ts @@ -13,12 +13,6 @@ export function register(server: McpServer): void { name: z.string().min(1).optional().describe("New workspace name."), }, handler: async (input, ctx) => { - const { id: _id, ...fields } = input; - if (Object.keys(fields).length === 0) { - throw new Error( - "No fields to update. Pass at least one field such as --name.", - ); - } const caller = createMcpCaller(ctx); return caller.v2Workspace.update(input); },