Skip to content
Draft
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
1 change: 0 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ DEVSPACE_ALLOWED_ROOTS=/home/waishnav/personal,/home/waishnav/work
# Advanced escape hatch. `*` disables Host header allowlist protection.
# DEVSPACE_ALLOWED_HOSTS=localhost,127.0.0.1,your-public-host.example.com
DEVSPACE_TOOL_MODE=full
DEVSPACE_TOOL_NAMING=legacy
# off | changes | full. Defaults to changes.
# changes creates one aggregate review widget via review_changes instead of per-tool iframes.
DEVSPACE_WIDGETS=changes
Expand Down
10 changes: 1 addition & 9 deletions docs/chatgpt-coding-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Set `DEVSPACE_SKILLS=0` to hide skills from workspace output.

## Tool Names

Short names are the default:
DevSpace exposes these tool names:

- `open_workspace`
- `read`
Expand All @@ -109,14 +109,6 @@ By default, DevSpace also runs in `DEVSPACE_TOOL_MODE=minimal`, so dedicated
`grep`, `glob`, and `ls` tools are hidden. Use `bash` with command-line tools
such as `rg`, `find`, and `ls` for search and directory inspection.

Legacy names are available with `DEVSPACE_TOOL_NAMING=legacy`:

- `open_workspace`
- `read_file`
- `write_file`
- `edit_file`
- `run_shell`

Use `DEVSPACE_TOOL_MODE=full` to restore dedicated search and directory tools.

## Show Changes
Expand Down
8 changes: 0 additions & 8 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,6 @@ MCP clients discover metadata from:

## Tool Modes

`DEVSPACE_TOOL_NAMING` controls tool names.

| Value | Behavior |
| --- | --- |
| `short` | Default. Uses `read`, `edit`, `bash`, and related names. |
| `legacy` | Uses `read_file`, `edit_file`, `run_shell`, and related names. |

`DEVSPACE_TOOL_MODE` controls the tool surface.

| Value | Behavior |
Expand Down Expand Up @@ -123,7 +116,6 @@ DEVSPACE_ALLOWED_ROOTS="$HOME/personal,$HOME/work" \
DEVSPACE_PUBLIC_BASE_URL="https://devspace.example.com" \
DEVSPACE_WORKTREE_ROOT="$HOME/.devspace/worktrees" \
DEVSPACE_TOOL_MODE="minimal" \
DEVSPACE_TOOL_NAMING="short" \
DEVSPACE_WIDGETS="full" \
npx @waishnav/devspace serve
```
Expand Down
7 changes: 0 additions & 7 deletions src/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ assert.equal(loadConfig(baseEnv).widgets, "full");
assert.equal(loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "changes" }).widgets, "changes");
assert.equal(loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "full" }).widgets, "full");
assert.equal(loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "off" }).widgets, "off");
assert.equal(loadConfig(baseEnv).toolNaming, "short");
assert.equal(loadConfig({ ...baseEnv, DEVSPACE_TOOL_NAMING: "short" }).toolNaming, "short");
assert.equal(loadConfig({ ...baseEnv, DEVSPACE_TOOL_NAMING: "legacy" }).toolNaming, "legacy");
assert.equal(loadConfig(baseEnv).minimalTools, true);
assert.equal(loadConfig({ ...baseEnv, DEVSPACE_TOOL_MODE: "minimal" }).minimalTools, true);
assert.equal(loadConfig({ ...baseEnv, DEVSPACE_TOOL_MODE: "full" }).minimalTools, false);
Expand All @@ -43,10 +40,6 @@ assert.throws(
() => loadConfig({ ...baseEnv, DEVSPACE_TOOL_MODE: "invalid" }),
/Invalid DEVSPACE_TOOL_MODE: invalid/,
);
assert.throws(
() => loadConfig({ ...baseEnv, DEVSPACE_TOOL_NAMING: "invalid" }),
/Invalid DEVSPACE_TOOL_NAMING: invalid/,
);

assert.deepEqual(loadConfig(baseEnv).logging, {
level: "info",
Expand Down
10 changes: 0 additions & 10 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type { LoggingConfig, LogFormat, LogLevel } from "./logger.js";
import type { OAuthConfig } from "./oauth-provider.js";
import { loadDevspaceFiles } from "./user-config.js";

export type ToolNamingMode = "legacy" | "short";
export type WidgetMode = "off" | "changes" | "full";
const DEFAULT_OAUTH_ACCESS_TOKEN_TTL_SECONDS = 60 * 60;
const DEFAULT_OAUTH_REFRESH_TOKEN_TTL_SECONDS = 30 * 24 * 60 * 60;
Expand All @@ -18,7 +17,6 @@ export interface ServerConfig {
allowedHosts: string[];
publicBaseUrl: string;
minimalTools: boolean;
toolNaming: ToolNamingMode;
widgets: WidgetMode;
stateDir: string;
worktreeRoot: string;
Expand Down Expand Up @@ -133,13 +131,6 @@ function parsePositiveInteger(value: string | undefined, fallback: number, name:
return parsed;
}

function parseToolNaming(value: string | undefined): ToolNamingMode {
if (!value || value === "short") return "short";
if (value === "legacy") return "legacy";

throw new Error(`Invalid DEVSPACE_TOOL_NAMING: ${value}`);
}

function parseLoggingConfig(env: NodeJS.ProcessEnv): LoggingConfig {
return {
level: parseLogLevel(env.DEVSPACE_LOG_LEVEL),
Expand Down Expand Up @@ -228,7 +219,6 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig {
allowedHosts: parseAllowedHosts(env.DEVSPACE_ALLOWED_HOSTS, derivedAllowedHosts),
publicBaseUrl,
minimalTools: parseMinimalTools(env),
toolNaming: parseToolNaming(env.DEVSPACE_TOOL_NAMING),
widgets: parseWidgetMode(env.DEVSPACE_WIDGETS),
stateDir: resolve(expandHomePath(env.DEVSPACE_STATE_DIR ?? files.config.stateDir ?? defaultStateDir())),
worktreeRoot: resolve(expandHomePath(env.DEVSPACE_WORKTREE_ROOT ?? files.config.worktreeRoot ?? defaultWorktreeRoot())),
Expand Down
57 changes: 16 additions & 41 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,16 @@ function toolWidgetDescriptorMeta(
};
}

interface ToolNames {
openWorkspace: "open_workspace";
read: "read_file" | "read";
write: "write_file" | "write";
edit: "edit_file" | "edit";
grep: "grep_files" | "grep";
glob: "find_files" | "glob";
ls: "list_directory" | "ls";
shell: "run_shell" | "bash";
}
const toolNames = {
openWorkspace: "open_workspace",
read: "read",
write: "write",
edit: "edit",
grep: "grep",
glob: "glob",
ls: "ls",
shell: "bash",
} as const;

interface ToolLogFields {
tool: string;
Expand All @@ -160,31 +160,7 @@ interface ToolLogFields {
error?: string;
}

function toolNamesFor(config: ServerConfig): ToolNames {
return config.toolNaming === "short"
? {
openWorkspace: "open_workspace",
read: "read",
write: "write",
edit: "edit",
grep: "grep",
glob: "glob",
ls: "ls",
shell: "bash",
}
: {
openWorkspace: "open_workspace",
read: "read_file",
write: "write_file",
edit: "edit_file",
grep: "grep_files",
glob: "find_files",
ls: "list_directory",
shell: "run_shell",
};
}

function serverInstructions(config: ServerConfig, toolNames: ToolNames): string {
function serverInstructions(config: ServerConfig): string {
const inspection = config.minimalTools
? `In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use ${toolNames.shell} with command-line tools such as grep, rg, find, ls, and tree for search and directory inspection. `
: `Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. `;
Expand Down Expand Up @@ -457,7 +433,6 @@ function createMcpServer(
workspaces: WorkspaceRegistry,
reviewCheckpoints: ReturnType<typeof createReviewCheckpointManager>,
): McpServer {
const toolNames = toolNamesFor(config);
const server = new McpServer(
{
name: "devspace",
Expand All @@ -467,7 +442,7 @@ function createMcpServer(
"Secure local coding workspace for MCP clients. Provides workspace-scoped file, search, edit, write, and shell tools.",
},
{
instructions: serverInstructions(config, toolNames),
instructions: serverInstructions(config),
},
);

Expand Down Expand Up @@ -966,7 +941,7 @@ function createMcpServer(
server,
toolNames.grep,
{
title: config.toolNaming === "short" ? "Grep" : "Grep files",
title: "Grep",
description:
"Search file contents inside an open workspace. Use this before broad reads when looking for symbols, text, or usage sites. Respects project ignore rules. Call open_workspace first and pass workspaceId.",
inputSchema: {
Expand Down Expand Up @@ -1039,7 +1014,7 @@ function createMcpServer(
server,
toolNames.glob,
{
title: config.toolNaming === "short" ? "Glob" : "Find files",
title: "Glob",
description:
"Find files by glob pattern inside an open workspace. Use this to discover filenames or narrow file sets before reading. Respects project ignore rules. Call open_workspace first and pass workspaceId.",
inputSchema: {
Expand Down Expand Up @@ -1109,7 +1084,7 @@ function createMcpServer(
server,
toolNames.ls,
{
title: config.toolNaming === "short" ? "Ls" : "List directory",
title: "Ls",
description:
"List a directory inside an open workspace. Use this for directory inspection before reading files. Call open_workspace first and pass workspaceId.",
inputSchema: {
Expand Down Expand Up @@ -1176,7 +1151,7 @@ function createMcpServer(
server,
toolNames.shell,
{
title: config.toolNaming === "short" ? "Bash" : "Run shell",
title: "Bash",
description: config.minimalTools
? `Run a shell command inside an open workspace. Use only for tests, builds, git inspection, package scripts, search, file discovery, and directory inspection. In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use command-line tools such as grep, rg, find, ls, and tree for those read-only inspection actions. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read} for direct file reads. Call open_workspace first and pass workspaceId. This is powerful local execution and should only be exposed behind strong authentication.`
: `Run a shell command inside an open workspace. Use only for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. Call open_workspace first and pass workspaceId. This is powerful local execution and should only be exposed behind strong authentication.`,
Expand Down
24 changes: 5 additions & 19 deletions src/ui/card-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,6 @@ import type { App } from "@modelcontextprotocol/ext-apps";

export type ToolName =
| "open_workspace"
| "read_file"
| "write_file"
| "edit_file"
| "grep_files"
| "find_files"
| "list_directory"
| "run_shell"
| "show_changes"
| "read"
| "write"
Expand Down Expand Up @@ -67,13 +60,6 @@ export interface ToolPayload {
export function isToolName(value: unknown): value is ToolName {
return (
value === "open_workspace" ||
value === "read_file" ||
value === "write_file" ||
value === "edit_file" ||
value === "grep_files" ||
value === "find_files" ||
value === "list_directory" ||
value === "run_shell" ||
value === "show_changes" ||
value === "read" ||
value === "write" ||
Expand All @@ -86,23 +72,23 @@ export function isToolName(value: unknown): value is ToolName {
}

export function isReadTool(tool: ToolName): boolean {
return tool === "read_file" || tool === "read";
return tool === "read";
}

export function isWriteTool(tool: ToolName): boolean {
return tool === "write_file" || tool === "write";
return tool === "write";
}

export function isEditTool(tool: ToolName): boolean {
return tool === "edit_file" || tool === "edit";
return tool === "edit";
}

export function isSearchTool(tool: ToolName): boolean {
return tool === "grep_files" || tool === "find_files" || tool === "grep" || tool === "glob";
return tool === "grep" || tool === "glob";
}

export function isShellTool(tool: ToolName): boolean {
return tool === "run_shell" || tool === "bash";
return tool === "bash";
}

export function isReviewTool(tool: ToolName): boolean {
Expand Down
1 change: 0 additions & 1 deletion src/ui/workspace-app.css
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,6 @@ body {
word-break: break-word;
}

.text-payload.run_shell,
.text-payload.bash {
color: var(--color-text-primary, #f5f5f6);
background: var(--color-background-primary, #101114);
Expand Down
7 changes: 0 additions & 7 deletions src/ui/workspace-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -492,25 +492,18 @@ function getToolDisplay(card: ToolResultCard): ToolDisplay {
switch (card.tool) {
case "open_workspace":
return { icon: folderIcon(), title: "Workspace", label, tone: "workspace" };
case "read_file":
case "read":
return { icon: fileIcon(), title: "Read File", label, tone: "read" };
case "write_file":
case "write":
return { icon: filePlusIcon(), title: "Write File", label, tone: "write" };
case "edit_file":
case "edit":
return { icon: editIcon(), title: "Edit File", label, tone: "edit" };
case "grep_files":
case "grep":
return { icon: searchIcon(), title: "Grep", label, tone: "search" };
case "find_files":
case "glob":
return { icon: filesIcon(), title: "Glob", label, tone: "search" };
case "list_directory":
case "ls":
return { icon: listIcon(), title: "List Directory", label, tone: "directory" };
case "run_shell":
case "bash":
return { icon: terminalIcon(), title: "Bash", label, tone: "shell" };
case "show_changes":
Expand Down
Loading