Give bots tools and MCP servers per bot and workspace, with status and tests

- 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]>
This commit is contained in:
2026-10-19 15:35:13 +01:00
co-authored by Claude Opus 5.5
parent c6ec7d77a2
commit 6294b911e6
69 changed files with 2026 additions and 271 deletions
@@ -0,0 +1,31 @@
import type { BotExtensionsPayload } from "@/src/types";
import { extensionOptionValue, parseExtensionOptionValue } from "@/src/utils/extension-option-value";
import InlineAddSelect from "./inline-add-select";
type Props = {
id: string;
addable: BotExtensionsPayload["addable"];
loading?: boolean;
onAdd: (params: { kind: "mcp" | "tool"; id: number }) => void;
};
/**
* Picks an MCP server or tool to give a bot.
*/
export default function ExtensionAddSelect({ id, addable, loading, onAdd }: Props) {
return (
<InlineAddSelect
id={id}
noun="tool or MCP server"
options={addable.map((item) => ({
value: extensionOptionValue(item),
title: `${item.kind == "mcp" ? "MCP" : "Tool"} · ${item.name}`,
}))}
loading={loading}
onAdd={({ value }) => {
const picked = parseExtensionOptionValue({ value });
if (picked) onAdd(picked);
}}
/>
);
}
@@ -0,0 +1,9 @@
import { Plug, Wrench } from "lucide-react";
import type { BotExtension } from "@/src/types";
/**
* The icon of an MCP server (a plug) or a tool (a wrench).
*/
export default function extensionIcon({ kind }: { kind: BotExtension["kind"] }) {
return kind == "mcp" ? Plug : Wrench;
}