Skip to content

feat(agent): add process MCP custom servers#989

Open
Nikhil (shadowfax92) wants to merge 1 commit into
devfrom
polecat/jasper/bosmain-c08@moxnf1b3
Open

feat(agent): add process MCP custom servers#989
Nikhil (shadowfax92) wants to merge 1 commit into
devfrom
polecat/jasper/bosmain-c08@moxnf1b3

Conversation

@shadowfax92
Copy link
Copy Markdown
Contributor

Summary

  • Add process-based custom MCP server specs alongside existing URL-only custom MCP entries.
  • Add an AnythingLLM preset with env var support for launching the official MCP server.
  • Carry custom MCP process specs through browser context and build stdio MCP clients on the server.

Fixes #281

Test plan

  • git diff --check origin/dev...HEAD
  • bun --env-file=apps/server/.env.development test apps/agent/lib/mcp/customMcpServerPayload.test.ts apps/server/tests/agent/mcp-builder.test.ts (agent tests passed locally; server test blocked locally by existing @browseros/shared workspace resolution issue tracked in bosmain-bjx)
  • bunx biome check on touched files
  • bun run typecheck from packages/browseros-agent/apps/server (blocked locally by existing tsc resolution issue tracked in bosmain-woi)
  • bun run typecheck from packages/browseros-agent/apps/agent (blocked locally by existing wxt script resolution issue tracked in bosmain-090)
  • bun run build from packages/browseros-agent/apps/agent (blocked locally by existing graphql-codegen script resolution issue tracked in bosmain-4xe)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 9, 2026

✅ Tests passed — 1238/1242

Suite Passed Failed Skipped
agent 82/82 0 0
build 9/9 0 0
eval 93/93 0 0
server-agent 269/269 0 0
server-api 205/205 0 0
server-browser 4/4 0 0
server-integration 9/10 0 1
server-lib 245/245 0 0
server-root 60/63 0 3
server-skills 31/31 0 0
server-tools 231/231 0 0

View workflow run

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 9, 2026

Greptile Summary

This PR extends the custom MCP server system to support local process-based (stdio) servers alongside the existing HTTP/SSE URL-based servers, and ships an AnythingLLM preset as the first concrete use case. The change touches the full stack: a new form UI with command/args/env/cwd fields, shared Zod schema extensions, a payload-mapping helper, and server-side StdioClientTransport wiring.

  • Schema & payload layer (browser-context.ts, customMcpServerPayload.ts): CustomMcpServerSchema is extended with a discriminated superRefine that validates http vs. process variants; a new buildChatCustomMcpServers helper centralises the mapping from stored config to request payload.
  • Server spawn layer (mcp-builder.ts): StdioClientTransport is used to launch arbitrary local processes; the user-supplied env object is passed as-is, which replaces the entire child process environment and strips PATH, causing npx (and similar launchers) to fail whenever the user provides any env vars.
  • Cache-key layer (chat-service.ts): buildMcpServerKey is updated to hash process parameters, but re-implements the type defaulting heuristic that already lives in the schema and in mcp-builder.

Confidence Score: 3/5

The process-spawn path will silently fail for any user who provides environment variables, including when using the bundled AnythingLLM preset.

The env-merging omission in connectMcpClient means the most visible new feature — the AnythingLLM preset — produces a user-visible failure the first time someone fills in the pre-populated env fields and clicks Connect. The rest of the changes (schema, UI, tests, cache key) are solid and well-structured.

packages/browseros-agent/apps/server/src/agent/mcp-builder.ts — specifically the StdioClientTransport constructor call and the resolveProcessCommand helper.

Important Files Changed

Filename Overview
packages/browseros-agent/apps/server/src/agent/mcp-builder.ts Adds process-based MCP client support via StdioClientTransport; contains a bug where user-supplied env replaces the full process environment, stripping PATH.
packages/browseros-agent/packages/shared/src/schemas/browser-context.ts Extends CustomMcpServerSchema to accept either http or process server variants; superRefine validation is correct and backward-compatible.
packages/browseros-agent/apps/agent/entrypoints/app/connect-mcp/AddCustomMCPDialog.tsx Adds connection-type selector and process fields (command, args, env, cwd) with Zod superRefine validation and AnythingLLM preset; form logic is clean.
packages/browseros-agent/apps/agent/lib/mcp/customMcpServerPayload.ts New helper that maps stored McpServer entries to ChatCustomMcpServer payloads; handles both http and process types with proper null filtering.
packages/browseros-agent/apps/server/src/api/services/chat-service.ts Updates buildMcpServerKey to hash process server parameters; duplicates the type-defaulting heuristic that also lives in the schema and mcp-builder.
packages/browseros-agent/apps/agent/lib/mcp/customMcpServerPayload.test.ts New unit tests covering backward-compatible http mapping and new process mapping; both cases are well-specified.
packages/browseros-agent/apps/server/tests/agent/mcp-builder.test.ts New integration tests for BrowserContextSchema parsing and buildMcpServerSpecs; covers the happy path, missing-command rejection, and no-HTTP-probe guarantee.

Sequence Diagram

sequenceDiagram
    participant U as User (Browser Extension)
    participant D as AddCustomMCPDialog
    participant S as McpServer Storage
    participant CR as buildChatRequestBody
    participant SV as Agent Server (chat-service)
    participant MB as mcp-builder
    participant P as Local Process (npx/stdio)
    participant H as Remote HTTP MCP

    U->>D: "Fill form (type = process | http)"
    D->>S: addServer(config with type, command/url, env, args)
    S-->>CR: customMcpServers (via buildChatCustomMcpServers)
    CR->>SV: "POST /chat { browserContext.customMcpServers }"
    SV->>MB: buildMcpServerSpecs(browserContext)
    alt "type = process"
        MB->>P: StdioClientTransport(command, args, env, cwd)
        P-->>MB: tools via stdio
    else "type = http"
        MB->>H: detectMcpTransport then SSE or HTTP
        H-->>MB: tools via HTTP
    end
    MB-->>SV: McpServerSpec[]
    SV-->>U: streamed AI response with MCP tools
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
packages/browseros-agent/apps/server/src/agent/mcp-builder.ts:141-146
**Spawned process loses `PATH` when env is provided**

`StdioClientTransport` passes the `env` option directly to `child_process.spawn`, which replaces the entire environment — it does not merge with `process.env`. When a user fills in even a single env var (e.g. `ANYTHINGLLM_BASE_URL`), `PATH`, `HOME`, and every other inherited var are stripped. The `npx` command (the canonical preset) then cannot locate binaries, causing it to fail silently with a spawn error.

```suggestion
            ? new StdioClientTransport({
                command: resolveProcessCommand(spec.command),
                args: spec.args,
                env: spec.env
                  ? { ...process.env, ...spec.env }
                  : undefined,
                cwd: spec.cwd,
              })
```

### Issue 2 of 3
packages/browseros-agent/apps/server/src/agent/mcp-builder.ts:104-108
**`resolveProcessCommand` only covers `npx` on Windows**

Other common MCP server launchers — `node`, `python`, `uvx`, `bunx` — need the same `.cmd` suffix treatment on Windows or they will fail to spawn. Consider either handling the full set of well-known launchers, or applying the `.cmd` heuristic to any command that has no path separator and is recognised as a script launcher.

### Issue 3 of 3
packages/browseros-agent/apps/server/src/api/services/chat-service.ts:490
**Duplicated type-defaulting logic across three locations**

The expression `server.type ?? (server.command ? 'process' : 'http')` appears identically in `browser-context.ts` (schema `superRefine`), `mcp-builder.ts` (`buildMcpServerSpecs`), and here in `chat-service.ts`. Consider transforming `type` to its resolved value inside the shared Zod schema, or exporting a small helper, so downstream code never needs to re-implement the heuristic.

Reviews (1): Last reviewed commit: "feat: add process MCP custom servers (bo..." | Re-trigger Greptile

Comment on lines +141 to +146
? new StdioClientTransport({
command: resolveProcessCommand(spec.command),
args: spec.args,
env: spec.env,
cwd: spec.cwd,
})
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.

P1 Spawned process loses PATH when env is provided

StdioClientTransport passes the env option directly to child_process.spawn, which replaces the entire environment — it does not merge with process.env. When a user fills in even a single env var (e.g. ANYTHINGLLM_BASE_URL), PATH, HOME, and every other inherited var are stripped. The npx command (the canonical preset) then cannot locate binaries, causing it to fail silently with a spawn error.

Suggested change
? new StdioClientTransport({
command: resolveProcessCommand(spec.command),
args: spec.args,
env: spec.env,
cwd: spec.cwd,
})
? new StdioClientTransport({
command: resolveProcessCommand(spec.command),
args: spec.args,
env: spec.env
? { ...process.env, ...spec.env }
: undefined,
cwd: spec.cwd,
})
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/agent/mcp-builder.ts
Line: 141-146

Comment:
**Spawned process loses `PATH` when env is provided**

`StdioClientTransport` passes the `env` option directly to `child_process.spawn`, which replaces the entire environment — it does not merge with `process.env`. When a user fills in even a single env var (e.g. `ANYTHINGLLM_BASE_URL`), `PATH`, `HOME`, and every other inherited var are stripped. The `npx` command (the canonical preset) then cannot locate binaries, causing it to fail silently with a spawn error.

```suggestion
            ? new StdioClientTransport({
                command: resolveProcessCommand(spec.command),
                args: spec.args,
                env: spec.env
                  ? { ...process.env, ...spec.env }
                  : undefined,
                cwd: spec.cwd,
              })
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +104 to +108
function resolveProcessCommand(command: string): string {
if (process.platform === 'win32' && command.toLowerCase() === 'npx') {
return 'npx.cmd'
}
return command
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.

P2 resolveProcessCommand only covers npx on Windows

Other common MCP server launchers — node, python, uvx, bunx — need the same .cmd suffix treatment on Windows or they will fail to spawn. Consider either handling the full set of well-known launchers, or applying the .cmd heuristic to any command that has no path separator and is recognised as a script launcher.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/agent/mcp-builder.ts
Line: 104-108

Comment:
**`resolveProcessCommand` only covers `npx` on Windows**

Other common MCP server launchers — `node`, `python`, `uvx`, `bunx` — need the same `.cmd` suffix treatment on Windows or they will fail to spawn. Consider either handling the full set of well-known launchers, or applying the `.cmd` heuristic to any command that has no path separator and is recognised as a script launcher.

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

💡 Feature Request: Native/Reliable AnythingLLM MCP Agent Integration

1 participant