Add MCP servers and custom tools for bots
Two new settings pages, MCPs and Tools. Super admins define them; super
admins and admins attach them to bots, to workspaces (every bot there),
or to every bot. Bots get them in chat replies and tasks.
- Custom tools: a JSON-schema of arguments plus an HTTP request, a script
on the bot's computer, or a prompt, templated with {{args.x}} and
{{env.KEY}}, secrets redacted from results.
- MCP servers: remote (URL and headers) or stdio commands run on the host
with a setup command and an isolated env, in a per-process pool. Tools
are listed into a cache and connect on first call.
- Add, edit, access and try open in modals.
- Markdown, JSON and command fields use twui's AceEditor (CodeEditor),
its scripts loaded on demand.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import AceEditor from "@moduletrace/twui/editors/AceEditor";
|
||||
import type AceEditorModes from "@moduletrace/twui/editors/ace-editor-modes";
|
||||
import Span from "@moduletrace/twui/layout/Span";
|
||||
import Stack from "@moduletrace/twui/layout/Stack";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import loadAceScripts from "@/src/utils/load-ace-scripts";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
/**
|
||||
* The starting text. Changes come back through `onChange`.
|
||||
*/
|
||||
content?: string;
|
||||
mode: (typeof AceEditorModes)[number];
|
||||
placeholder?: string;
|
||||
/**
|
||||
* One line under it, like a field's info.
|
||||
*/
|
||||
info?: string;
|
||||
/**
|
||||
* Its height, e.g. `h-24`.
|
||||
*/
|
||||
className?: string;
|
||||
onChange: (value: string) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* A code field: twui's `AceEditor` with its label, and a line of info
|
||||
* under it. Loads the editor's scripts when it first shows
|
||||
* (`loadAceScripts`).
|
||||
*/
|
||||
export default function CodeEditor({ title, content, mode, placeholder, info, className, onChange }: Props) {
|
||||
/**
|
||||
* The editor keeps the handler it was created with; this keeps it
|
||||
* calling the latest one.
|
||||
*/
|
||||
const change = useRef(onChange);
|
||||
change.current = onChange;
|
||||
|
||||
useEffect(() => {
|
||||
loadAceScripts().catch(() => {});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Stack className="w-full gap-1.5! items-stretch">
|
||||
<AceEditor
|
||||
title={title}
|
||||
showLabel
|
||||
content={content}
|
||||
mode={mode}
|
||||
placeholder={placeholder}
|
||||
fontSize="13px"
|
||||
delay={0}
|
||||
wrapperProps={{ className: twMerge("h-40", className) }}
|
||||
onChange={(value) => change.current(value)}
|
||||
/>
|
||||
{info ? <Span className="text-[13px] leading-[18px] text-ink-secondary">{info}</Span> : null}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,9 @@
|
||||
import Span from "@moduletrace/twui/layout/Span";
|
||||
import { Monitor } from "lucide-react";
|
||||
import IconTile from "./icon-tile";
|
||||
|
||||
/**
|
||||
* A computer's avatar in lists: the monitor icon on a small tile.
|
||||
*/
|
||||
export default function ComputerIconTile() {
|
||||
return (
|
||||
<Span className="size-8 shrink-0 flex items-center justify-center rounded-default-sm border border-solid border-hairline bg-paper">
|
||||
<Monitor size={16} aria-hidden />
|
||||
</Span>
|
||||
);
|
||||
return <IconTile Icon={Monitor} />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import Span from "@moduletrace/twui/layout/Span";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
|
||||
type Props = {
|
||||
Icon: LucideIcon;
|
||||
};
|
||||
|
||||
/**
|
||||
* An avatar in lists for what has no picture: its icon on a small tile.
|
||||
*/
|
||||
export default function IconTile({ Icon }: Props) {
|
||||
return (
|
||||
<Span className="size-8 shrink-0 flex items-center justify-center rounded-default-sm border border-solid border-hairline bg-paper">
|
||||
<Icon size={16} aria-hidden />
|
||||
</Span>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user