mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
mcp: sorting tools, fixed cloudflare example, fixed some styling
This commit is contained in:
@@ -25,7 +25,7 @@ const result = await Bun.build({
|
||||
target: "node",
|
||||
outdir: "./dist/cli",
|
||||
env: "PUBLIC_*",
|
||||
minify: false,
|
||||
minify: true,
|
||||
external: ["jsonv-ts", "jsonv-ts/*"],
|
||||
define: {
|
||||
__isDev: "0",
|
||||
|
||||
@@ -16,9 +16,13 @@ export function getSystemMcp(app: App) {
|
||||
...middlewareServer.tools,
|
||||
// tools added from ctx
|
||||
...app.modules.ctx().mcp.tools,
|
||||
// tools from app schema
|
||||
...nodes.flatMap((n) => n.schema.getTools(n)),
|
||||
];
|
||||
].sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
// tools from app schema
|
||||
tools.push(
|
||||
...nodes.flatMap((n) => n.schema.getTools(n)).sort((a, b) => a.name.localeCompare(b.name)),
|
||||
);
|
||||
|
||||
const resources = [...middlewareServer.resources, ...app.modules.ctx().mcp.resources];
|
||||
|
||||
return new McpServer(
|
||||
|
||||
@@ -8,6 +8,7 @@ export type EmptyProps = {
|
||||
primary?: ButtonProps;
|
||||
secondary?: ButtonProps;
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
export const Empty: React.FC<EmptyProps> = ({
|
||||
Icon = undefined,
|
||||
@@ -16,6 +17,7 @@ export const Empty: React.FC<EmptyProps> = ({
|
||||
primary,
|
||||
secondary,
|
||||
className,
|
||||
children,
|
||||
}) => (
|
||||
<div className={twMerge("flex flex-col h-full w-full justify-center items-center", className)}>
|
||||
<div className="flex flex-col gap-3 items-center max-w-80">
|
||||
@@ -27,6 +29,7 @@ export const Empty: React.FC<EmptyProps> = ({
|
||||
<div className="mt-1.5 flex flex-row gap-2">
|
||||
{secondary && <Button variant="default" {...secondary} />}
|
||||
{primary && <Button variant="primary" {...primary} />}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -124,7 +124,7 @@ function DataEntityListImpl({ params }) {
|
||||
</>
|
||||
}
|
||||
>
|
||||
{entity.label}
|
||||
<AppShell.SectionHeaderTitle>{entity.label}</AppShell.SectionHeaderTitle>
|
||||
</AppShell.SectionHeader>
|
||||
<AppShell.Scrollable key={entity.name}>
|
||||
<div className="flex flex-col flex-grow p-3 gap-3">
|
||||
|
||||
@@ -5,11 +5,15 @@ import { TbWorld } from "react-icons/tb";
|
||||
import { McpIcon } from "./components/mcp-icon";
|
||||
import { useBknd } from "ui/client/bknd";
|
||||
import { Empty } from "ui/components/display/Empty";
|
||||
import { Button } from "ui/components/buttons/Button";
|
||||
import { appShellStore } from "ui/store";
|
||||
|
||||
export default function ToolsMcp() {
|
||||
const { config, options } = useBknd();
|
||||
const feature = useMcpStore((state) => state.feature);
|
||||
const setFeature = useMcpStore((state) => state.setFeature);
|
||||
const content = useMcpStore((state) => state.content);
|
||||
const openSidebar = appShellStore((store) => store.toggleSidebar("default"));
|
||||
|
||||
if (!config.server.mcp.enabled) {
|
||||
return (
|
||||
@@ -25,15 +29,20 @@ export default function ToolsMcp() {
|
||||
<AppShell.SectionHeader>
|
||||
<div className="flex flex-row gap-4 items-center">
|
||||
<McpIcon />
|
||||
<AppShell.SectionHeaderTitle>MCP UI</AppShell.SectionHeaderTitle>
|
||||
<div className="flex flex-row gap-2 items-center bg-primary/5 rounded-full px-3 pr-3.5 py-2">
|
||||
<AppShell.SectionHeaderTitle className="whitespace-nowrap truncate">
|
||||
MCP UI
|
||||
</AppShell.SectionHeaderTitle>
|
||||
<div className="hidden md:flex flex-row gap-2 items-center bg-primary/5 rounded-full px-3 pr-3.5 py-2">
|
||||
<TbWorld />
|
||||
<span className="text-sm font-mono leading-none">
|
||||
{window.location.origin + "/mcp"}
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<span className="block truncate text-sm font-mono leading-none">
|
||||
{window.location.origin + "/mcp"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AppShell.SectionHeader>
|
||||
|
||||
<div className="flex h-full">
|
||||
<AppShell.Sidebar>
|
||||
<AppShell.MaxHeightContainer className="overflow-y-scroll md:overflow-auto">
|
||||
@@ -50,6 +59,18 @@ export default function ToolsMcp() {
|
||||
</AppShell.MaxHeightContainer>
|
||||
</AppShell.Sidebar>
|
||||
{feature === "tools" && <Tools.Content />}
|
||||
|
||||
{!content && (
|
||||
<Empty title="No tool selected" description="Please select a tool to continue.">
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => openSidebar()}
|
||||
className="block md:hidden"
|
||||
>
|
||||
Open Tools
|
||||
</Button>
|
||||
</Empty>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -6,15 +6,14 @@ import { TbHistory, TbHistoryOff, TbRefresh } from "react-icons/tb";
|
||||
import { IconButton } from "ui/components/buttons/IconButton";
|
||||
import { JsonViewer, JsonViewerTabs, type JsonViewerTabsRef } from "ui/components/code/JsonViewer";
|
||||
import { twMerge } from "ui/elements/mocks/tailwind-merge";
|
||||
import {
|
||||
Form,
|
||||
} from "ui/components/form/json-schema-form";
|
||||
import { Form } from "ui/components/form/json-schema-form";
|
||||
import { Button } from "ui/components/buttons/Button";
|
||||
import * as Formy from "ui/components/form/Formy";
|
||||
import { JsonEditor } from "ui/components/code/JsonEditor";
|
||||
import { appShellStore } from "ui/store";
|
||||
|
||||
export function Sidebar({ open, toggle }) {
|
||||
const client = getClient();
|
||||
const closeSidebar = appShellStore((store) => store.closeSidebar("default"));
|
||||
const tools = useMcpStore((state) => state.tools);
|
||||
const setTools = useMcpStore((state) => state.setTools);
|
||||
const setContent = useMcpStore((state) => state.setContent);
|
||||
@@ -56,7 +55,7 @@ export function Sidebar({ open, toggle }) {
|
||||
/>
|
||||
<nav className="flex flex-col flex-1 gap-1">
|
||||
{tools
|
||||
.filter((tool) => tool.name.includes(query))
|
||||
.filter((tool) => tool.name.toLowerCase().includes(query.toLowerCase()))
|
||||
.map((tool) => {
|
||||
return (
|
||||
<AppShell.SidebarLink
|
||||
@@ -65,7 +64,10 @@ export function Sidebar({ open, toggle }) {
|
||||
"flex flex-col items-start h-auto py-3 gap-px",
|
||||
content?.name === tool.name ? "active" : "",
|
||||
)}
|
||||
onClick={() => setContent(tool)}
|
||||
onClick={() => {
|
||||
setContent(tool);
|
||||
closeSidebar();
|
||||
}}
|
||||
>
|
||||
<span className="font-mono">{tool.name}</span>
|
||||
<span className="text-sm text-primary/50">{tool.description}</span>
|
||||
@@ -124,6 +126,7 @@ export function Content() {
|
||||
return (
|
||||
<div className="flex flex-grow flex-col">
|
||||
<AppShell.SectionHeader
|
||||
className="max-w-full min-w-0 debug"
|
||||
right={
|
||||
<div className="flex flex-row gap-2">
|
||||
<IconButton
|
||||
@@ -135,17 +138,18 @@ export function Content() {
|
||||
disabled={!content?.name}
|
||||
variant="primary"
|
||||
onClick={handleSubmit}
|
||||
className="whitespace-nowrap"
|
||||
>
|
||||
Call Tool
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<AppShell.SectionHeaderTitle className="">
|
||||
<AppShell.SectionHeaderTitle className="leading-tight">
|
||||
<span className="opacity-50">
|
||||
Tools <span className="opacity-70">/</span>
|
||||
</span>{" "}
|
||||
{content?.name}
|
||||
<span className="truncate">{content?.name}</span>
|
||||
</AppShell.SectionHeaderTitle>
|
||||
</AppShell.SectionHeader>
|
||||
<div className="flex flex-grow flex-row w-full">
|
||||
|
||||
Reference in New Issue
Block a user