- Bots and workspaces get tools and MCP servers from their settings pages; bots also from chips under the chat composer, removable there - "Every bot" now gives one to every bot now and every new bot; each can still drop it (attach-every-bot-extensions.ts moves old installs over) - Status lines: MCP servers record whether they connect, tools whether their last run worked - Test connection in the MCP form and a Test panel in the tool form check the form as it is, without saving - Hide Google sign-in on the login form Co-Authored-By: Claude Opus 5.5 <[email protected]>
55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
import { AppData } from "@/src/data/app-data";
|
|
import { SiteData } from "@/src/data/site-data";
|
|
import type { McpServerRecord } from "@/src/types";
|
|
import grabMcpDir from "./grab-mcp-dir";
|
|
import hostShell from "./host-shell";
|
|
import runMcpSetup from "./run-mcp-setup";
|
|
|
|
type Params = {
|
|
server: McpServerRecord;
|
|
/**
|
|
* Its environment (`grabMcpStdioEnv`).
|
|
*/
|
|
env: Record<string, string>;
|
|
};
|
|
|
|
/**
|
|
* Starts a stdio MCP server after its setup (`runMcpSetup`), with the
|
|
* host's shell in its directory, and connects to it. A failed start
|
|
* throws with the last lines it wrote to stderr. Closing the client stops
|
|
* it.
|
|
*/
|
|
export default async function startStdioMcpClient({ server, env }: Params) {
|
|
await runMcpSetup({ server, env });
|
|
|
|
const transport = new StdioClientTransport({
|
|
command: hostShell(),
|
|
args: ["-c", server.command],
|
|
cwd: await grabMcpDir({ server_id: server.id }),
|
|
env,
|
|
stderr: "pipe",
|
|
});
|
|
let stderr = "";
|
|
|
|
transport.stderr?.on("data", (chunk: Buffer) => {
|
|
stderr = `${stderr}${chunk.toString()}`.slice(-4000);
|
|
});
|
|
|
|
const client = new Client({ name: SiteData["SiteSlug"], version: "1.0.0" });
|
|
|
|
try {
|
|
await client.connect(transport, { timeout: AppData.McpConnectTimeoutMs * 4 });
|
|
} catch (error) {
|
|
await client.close().catch(() => {});
|
|
throw new Error(
|
|
`Couldn't start ${server.name}: ${error instanceof Error ? error.message : String(error)}${
|
|
stderr.trim() ? `\n${stderr.trim().slice(-2000)}` : ""
|
|
}`,
|
|
);
|
|
}
|
|
|
|
return { client, transport };
|
|
}
|