mirror of
https://github.com/bknd-io/bknd/
synced 2026-08-02 16:16:02 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ee07adc70 | |||
| 53e5c6fd98 | |||
| 4591631d3c | |||
| 5a27c41516 | |||
| 24eefa5357 | |||
| d898018b49 | |||
| 09fdde1c5b | |||
| fb0dbb9a51 | |||
| 81f578895e | |||
| 2de12674d9 | |||
| 5ed1cf19b6 | |||
| 0b21157f92 | |||
| cacb13e464 |
@@ -15,6 +15,7 @@ describe("AppServer", () => {
|
|||||||
},
|
},
|
||||||
mcp: {
|
mcp: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
path: "/api/system/mcp",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -36,6 +37,7 @@ describe("AppServer", () => {
|
|||||||
},
|
},
|
||||||
mcp: {
|
mcp: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
path: "/api/system/mcp",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-7
@@ -3,7 +3,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"bin": "./dist/cli/index.js",
|
"bin": "./dist/cli/index.js",
|
||||||
"version": "0.16.1",
|
"version": "0.17.0",
|
||||||
"description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, React Router, Astro, Cloudflare, Bun, Node, AWS Lambda & more.",
|
"description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, React Router, Astro, Cloudflare, Bun, Node, AWS Lambda & more.",
|
||||||
"homepage": "https://bknd.io",
|
"homepage": "https://bknd.io",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -39,11 +39,11 @@
|
|||||||
"test:adapters": "bun test src/adapter/**/*.adapter.spec.ts --bail",
|
"test:adapters": "bun test src/adapter/**/*.adapter.spec.ts --bail",
|
||||||
"test:coverage": "ALL_TESTS=1 bun test --bail --coverage",
|
"test:coverage": "ALL_TESTS=1 bun test --bail --coverage",
|
||||||
"test:vitest:coverage": "vitest run --coverage",
|
"test:vitest:coverage": "vitest run --coverage",
|
||||||
"test:e2e": "playwright test",
|
"test:e2e": "VITE_DB_URL=:memory: playwright test",
|
||||||
"test:e2e:adapters": "bun run e2e/adapters.ts",
|
"test:e2e:adapters": "VITE_DB_URL=:memory: bun run e2e/adapters.ts",
|
||||||
"test:e2e:ui": "playwright test --ui",
|
"test:e2e:ui": "VITE_DB_URL=:memory: playwright test --ui",
|
||||||
"test:e2e:debug": "playwright test --debug",
|
"test:e2e:debug": "VITE_DB_URL=:memory: playwright test --debug",
|
||||||
"test:e2e:report": "playwright show-report",
|
"test:e2e:report": "VITE_DB_URL=:memory: playwright show-report",
|
||||||
"docs:build-assets": "bun internal/docs.build-assets.ts"
|
"docs:build-assets": "bun internal/docs.build-assets.ts"
|
||||||
},
|
},
|
||||||
"license": "FSL-1.1-MIT",
|
"license": "FSL-1.1-MIT",
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
"hono": "4.8.3",
|
"hono": "4.8.3",
|
||||||
"json-schema-library": "10.0.0-rc7",
|
"json-schema-library": "10.0.0-rc7",
|
||||||
"json-schema-to-ts": "^3.1.1",
|
"json-schema-to-ts": "^3.1.1",
|
||||||
"jsonv-ts": "^0.8.1",
|
"jsonv-ts": "0.8.2",
|
||||||
"kysely": "0.27.6",
|
"kysely": "0.27.6",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"oauth4webapi": "^2.11.1",
|
"oauth4webapi": "^2.11.1",
|
||||||
|
|||||||
+2
-1
@@ -293,9 +293,10 @@ export class App<C extends Connection = Connection, Options extends AppOptions =
|
|||||||
if (!this.mcp) {
|
if (!this.mcp) {
|
||||||
throw new Error("MCP is not enabled");
|
throw new Error("MCP is not enabled");
|
||||||
}
|
}
|
||||||
|
const mcpPath = this.modules.get("server").config.mcp.path;
|
||||||
|
|
||||||
return new McpClient({
|
return new McpClient({
|
||||||
url: "http://localhost/mcp",
|
url: "http://localhost" + mcpPath,
|
||||||
fetch: this.server.request,
|
fetch: this.server.request,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export {
|
|||||||
export { constants } from "./config";
|
export { constants } from "./config";
|
||||||
export { StorageR2Adapter, registerMedia } from "./storage/StorageR2Adapter";
|
export { StorageR2Adapter, registerMedia } from "./storage/StorageR2Adapter";
|
||||||
export { registries } from "bknd";
|
export { registries } from "bknd";
|
||||||
export { devFsPlugin, devFsWrite } from "./vite";
|
export { devFsVitePlugin, devFsWrite } from "./vite";
|
||||||
|
|
||||||
// for compatibility with old code
|
// for compatibility with old code
|
||||||
export function d1<DB extends D1Database | D1DatabaseSession = D1Database>(
|
export function d1<DB extends D1Database | D1DatabaseSession = D1Database>(
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
import {
|
import { mark, stripMark, $console, s, objectEach, transformObject, McpServer } from "bknd/utils";
|
||||||
mark,
|
import { DebugLogger } from "core/utils/DebugLogger";
|
||||||
stripMark,
|
|
||||||
$console,
|
|
||||||
s,
|
|
||||||
objectEach,
|
|
||||||
transformObject,
|
|
||||||
McpServer,
|
|
||||||
DebugLogger,
|
|
||||||
} from "bknd/utils";
|
|
||||||
import { Guard } from "auth/authorize/Guard";
|
import { Guard } from "auth/authorize/Guard";
|
||||||
import { env } from "core/env";
|
import { env } from "core/env";
|
||||||
import { BkndError } from "core/errors";
|
import { BkndError } from "core/errors";
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export const serverConfigSchema = $object(
|
|||||||
}),
|
}),
|
||||||
mcp: s.strictObject({
|
mcp: s.strictObject({
|
||||||
enabled: s.boolean({ default: false }),
|
enabled: s.boolean({ default: false }),
|
||||||
|
path: s.string({ default: "/api/system/mcp" }),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -60,8 +60,9 @@ export class SystemController extends Controller {
|
|||||||
|
|
||||||
register(app: App) {
|
register(app: App) {
|
||||||
app.server.route("/api/system", this.getController());
|
app.server.route("/api/system", this.getController());
|
||||||
|
const config = app.modules.get("server").config;
|
||||||
|
|
||||||
if (!this.app.modules.get("server").config.mcp.enabled) {
|
if (!config.mcp.enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +85,7 @@ export class SystemController extends Controller {
|
|||||||
const level = consoleMap[message.params.level];
|
const level = consoleMap[message.params.level];
|
||||||
if (!level) return;
|
if (!level) return;
|
||||||
|
|
||||||
$console[level](message.params.message);
|
$console[level]("MCP notification", message.params.message ?? message.params);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -97,7 +98,7 @@ export class SystemController extends Controller {
|
|||||||
explainEndpoint: true,
|
explainEndpoint: true,
|
||||||
},
|
},
|
||||||
endpoint: {
|
endpoint: {
|
||||||
path: "/mcp",
|
path: config.mcp.path as any,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
_init: isNode() ? { duplex: "half" } : {},
|
_init: isNode() ? { duplex: "half" } : {},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ const style = {
|
|||||||
basicChildStyle: "pl-5 ml-1 border-l border-muted hover:border-primary/20",
|
basicChildStyle: "pl-5 ml-1 border-l border-muted hover:border-primary/20",
|
||||||
container: "ml-[-10px]",
|
container: "ml-[-10px]",
|
||||||
label: "text-primary/90 font-bold font-mono mr-2",
|
label: "text-primary/90 font-bold font-mono mr-2",
|
||||||
stringValue: "text-emerald-600 dark:text-emerald-500 font-mono select-text",
|
stringValue:
|
||||||
numberValue: "text-sky-500 dark:text-sky-400 font-mono",
|
"text-emerald-600 dark:text-emerald-500 font-mono select-text text-wrap whitespace-wrap break-words",
|
||||||
|
numberValue: "text-sky-500 dark:text-sky-400 font-mono select-text",
|
||||||
nullValue: "text-zinc-400 font-mono",
|
nullValue: "text-zinc-400 font-mono",
|
||||||
undefinedValue: "text-zinc-400 font-mono",
|
undefinedValue: "text-zinc-400 font-mono",
|
||||||
otherValue: "text-zinc-400 font-mono",
|
otherValue: "text-zinc-400 font-mono",
|
||||||
|
|||||||
@@ -13,6 +13,14 @@ const Warning = ({ className, ...props }: IconProps) => (
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const Err = ({ className, ...props }: IconProps) => (
|
||||||
|
<TbAlertCircle
|
||||||
|
{...props}
|
||||||
|
className={twMerge("dark:text-red-300 text-red-700 cursor-help", className)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
export const Icon = {
|
export const Icon = {
|
||||||
Warning,
|
Warning,
|
||||||
|
Err,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useClickOutside, useHotkeys } from "@mantine/hooks";
|
import { useClickOutside, useHotkeys } from "@mantine/hooks";
|
||||||
import { IconChevronDown, IconChevronUp } from "@tabler/icons-react";
|
import { IconChevronDown, IconChevronUp } from "@tabler/icons-react";
|
||||||
import { clampNumber } from "core/utils/numbers";
|
import { transformObject, clampNumber } from "bknd/utils";
|
||||||
import { throttle } from "lodash-es";
|
import { throttle } from "lodash-es";
|
||||||
import { ScrollArea } from "radix-ui";
|
import { ScrollArea } from "radix-ui";
|
||||||
import {
|
import {
|
||||||
@@ -19,9 +19,21 @@ import { appShellStore } from "ui/store";
|
|||||||
import { useLocation } from "wouter";
|
import { useLocation } from "wouter";
|
||||||
|
|
||||||
export function Root({ children }: { children: React.ReactNode }) {
|
export function Root({ children }: { children: React.ReactNode }) {
|
||||||
|
const sidebarWidths = appShellStore((store) => store.sidebars);
|
||||||
|
const style = transformObject(sidebarWidths, (value) => value.width);
|
||||||
return (
|
return (
|
||||||
<AppShellProvider>
|
<AppShellProvider>
|
||||||
<div id="app-shell" data-shell="root" className="flex flex-1 flex-col select-none h-dvh">
|
<div
|
||||||
|
id="app-shell"
|
||||||
|
data-shell="root"
|
||||||
|
className="flex flex-1 flex-col select-none h-dvh"
|
||||||
|
style={Object.fromEntries(
|
||||||
|
Object.entries(style).map(([key, value]) => [
|
||||||
|
`--sidebar-width-${key}`,
|
||||||
|
`${value}px`,
|
||||||
|
]),
|
||||||
|
)}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</AppShellProvider>
|
</AppShellProvider>
|
||||||
|
|||||||
+1
-1
@@ -74,7 +74,7 @@
|
|||||||
@apply bg-background text-primary overflow-hidden h-dvh w-dvw;
|
@apply bg-background text-primary overflow-hidden h-dvh w-dvw;
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
@apply bg-muted;
|
@apply bg-primary/15;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as ReactDOM from "react-dom/client";
|
import * as ReactDOM from "react-dom/client";
|
||||||
import Admin from "./Admin";
|
import Admin from "./Admin";
|
||||||
import "./main.css";
|
//import "./main.css";
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { McpClient, type McpClientConfig } from "jsonv-ts/mcp";
|
||||||
|
import { useBknd } from "ui/client/bknd";
|
||||||
|
|
||||||
|
const clients = new Map<string, McpClient>();
|
||||||
|
|
||||||
|
export function getClient(opts: McpClientConfig) {
|
||||||
|
if (!clients.has(JSON.stringify(opts))) {
|
||||||
|
clients.set(JSON.stringify(opts), new McpClient(opts));
|
||||||
|
}
|
||||||
|
return clients.get(JSON.stringify(opts))!;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMcpClient() {
|
||||||
|
const { config } = useBknd();
|
||||||
|
return getClient({ url: window.location.origin + config.server.mcp.path });
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ export default function ToolsMcp() {
|
|||||||
const setFeature = useMcpStore((state) => state.setFeature);
|
const setFeature = useMcpStore((state) => state.setFeature);
|
||||||
const content = useMcpStore((state) => state.content);
|
const content = useMcpStore((state) => state.content);
|
||||||
const openSidebar = appShellStore((store) => store.toggleSidebar("default"));
|
const openSidebar = appShellStore((store) => store.toggleSidebar("default"));
|
||||||
|
const mcpPath = config.server.mcp.path;
|
||||||
|
|
||||||
if (!config.server.mcp.enabled) {
|
if (!config.server.mcp.enabled) {
|
||||||
return (
|
return (
|
||||||
@@ -39,7 +40,7 @@ export default function ToolsMcp() {
|
|||||||
<TbWorld />
|
<TbWorld />
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<span className="block truncate text-sm font-mono leading-none">
|
<span className="block truncate text-sm font-mono leading-none">
|
||||||
{window.location.origin + "/mcp"}
|
{window.location.origin + mcpPath}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useEffect, useRef, useState, useTransition } from "react";
|
import { useCallback, useEffect, useRef, useState, useTransition } from "react";
|
||||||
import { getClient, getTemplate } from "./utils";
|
import { getTemplate } from "./utils";
|
||||||
import { useMcpStore } from "./state";
|
import { useMcpStore } from "./state";
|
||||||
import { AppShell } from "ui/layouts/AppShell";
|
import { AppShell } from "ui/layouts/AppShell";
|
||||||
import { TbHistory, TbHistoryOff, TbRefresh } from "react-icons/tb";
|
import { TbHistory, TbHistoryOff, TbRefresh } from "react-icons/tb";
|
||||||
@@ -10,9 +10,11 @@ import { Field, Form } from "ui/components/form/json-schema-form";
|
|||||||
import { Button } from "ui/components/buttons/Button";
|
import { Button } from "ui/components/buttons/Button";
|
||||||
import * as Formy from "ui/components/form/Formy";
|
import * as Formy from "ui/components/form/Formy";
|
||||||
import { appShellStore } from "ui/store";
|
import { appShellStore } from "ui/store";
|
||||||
|
import { Icon } from "ui/components/display/Icon";
|
||||||
|
import { useMcpClient } from "./hooks/use-mcp-client";
|
||||||
|
|
||||||
export function Sidebar({ open, toggle }) {
|
export function Sidebar({ open, toggle }) {
|
||||||
const client = getClient();
|
const client = useMcpClient();
|
||||||
const closeSidebar = appShellStore((store) => store.closeSidebar("default"));
|
const closeSidebar = appShellStore((store) => store.closeSidebar("default"));
|
||||||
const tools = useMcpStore((state) => state.tools);
|
const tools = useMcpStore((state) => state.tools);
|
||||||
const setTools = useMcpStore((state) => state.setTools);
|
const setTools = useMcpStore((state) => state.setTools);
|
||||||
@@ -20,11 +22,18 @@ export function Sidebar({ open, toggle }) {
|
|||||||
const content = useMcpStore((state) => state.content);
|
const content = useMcpStore((state) => state.content);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [query, setQuery] = useState<string>("");
|
const [query, setQuery] = useState<string>("");
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
const handleRefresh = useCallback(async () => {
|
const handleRefresh = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const res = await client.listTools();
|
setError(null);
|
||||||
if (res) setTools(res.tools);
|
try {
|
||||||
|
const res = await client.listTools();
|
||||||
|
if (res) setTools(res.tools);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
setError(String(e));
|
||||||
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -39,6 +48,7 @@ export function Sidebar({ open, toggle }) {
|
|||||||
toggle={toggle}
|
toggle={toggle}
|
||||||
renderHeaderRight={() => (
|
renderHeaderRight={() => (
|
||||||
<div className="flex flex-row gap-2 items-center">
|
<div className="flex flex-row gap-2 items-center">
|
||||||
|
{error && <Icon.Err title={error} className="size-5 pointer-events-auto" />}
|
||||||
<span className="flex-inline bg-primary/10 px-2 py-1.5 rounded-xl text-sm font-mono leading-none">
|
<span className="flex-inline bg-primary/10 px-2 py-1.5 rounded-xl text-sm font-mono leading-none">
|
||||||
{tools.length}
|
{tools.length}
|
||||||
</span>
|
</span>
|
||||||
@@ -88,7 +98,7 @@ export function Content() {
|
|||||||
const [result, setResult] = useState<object | null>(null);
|
const [result, setResult] = useState<object | null>(null);
|
||||||
const historyVisible = useMcpStore((state) => state.historyVisible);
|
const historyVisible = useMcpStore((state) => state.historyVisible);
|
||||||
const setHistoryVisible = useMcpStore((state) => state.setHistoryVisible);
|
const setHistoryVisible = useMcpStore((state) => state.setHistoryVisible);
|
||||||
const client = getClient();
|
const client = useMcpClient();
|
||||||
const jsonViewerTabsRef = useRef<JsonViewerTabsRef>(null);
|
const jsonViewerTabsRef = useRef<JsonViewerTabsRef>(null);
|
||||||
const hasInputSchema =
|
const hasInputSchema =
|
||||||
content?.inputSchema && Object.keys(content.inputSchema.properties ?? {}).length > 0;
|
content?.inputSchema && Object.keys(content.inputSchema.properties ?? {}).length > 0;
|
||||||
@@ -129,7 +139,7 @@ export function Content() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
className="flex flex-grow flex-col max-w-screen"
|
className="flex flex-grow flex-col min-w-0 max-w-screen"
|
||||||
key={content.name}
|
key={content.name}
|
||||||
schema={{
|
schema={{
|
||||||
title: "InputSchema",
|
title: "InputSchema",
|
||||||
@@ -170,7 +180,12 @@ export function Content() {
|
|||||||
</AppShell.SectionHeaderTitle>
|
</AppShell.SectionHeaderTitle>
|
||||||
</AppShell.SectionHeader>
|
</AppShell.SectionHeader>
|
||||||
<div className="flex flex-grow flex-row w-vw">
|
<div className="flex flex-grow flex-row w-vw">
|
||||||
<div className="flex flex-grow flex-col w-full">
|
<div
|
||||||
|
className="flex flex-grow flex-col max-w-full"
|
||||||
|
style={{
|
||||||
|
width: "calc(100% - var(--sidebar-width-right) - 1px)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<AppShell.Scrollable>
|
<AppShell.Scrollable>
|
||||||
<div key={JSON.stringify(content)} className="flex flex-col py-4 px-5 gap-4">
|
<div key={JSON.stringify(content)} className="flex flex-col py-4 px-5 gap-4">
|
||||||
<p className="text-primary/80">{content?.description}</p>
|
<p className="text-primary/80">{content?.description}</p>
|
||||||
@@ -219,8 +234,9 @@ const History = () => {
|
|||||||
<JsonViewer
|
<JsonViewer
|
||||||
key={`${item.type}-${i}`}
|
key={`${item.type}-${i}`}
|
||||||
json={item.data}
|
json={item.data}
|
||||||
title={item.type}
|
title={item.type.substring(0, 3)}
|
||||||
expand={2}
|
expand={2}
|
||||||
|
showCopy
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,17 +1,5 @@
|
|||||||
import { McpClient, type McpClientConfig } from "jsonv-ts/mcp";
|
|
||||||
import { Draft2019 } from "json-schema-library";
|
import { Draft2019 } from "json-schema-library";
|
||||||
|
|
||||||
const clients = new Map<string, McpClient>();
|
|
||||||
|
|
||||||
export function getClient(
|
|
||||||
{ url, ...opts }: McpClientConfig = { url: window.location.origin + "/mcp" },
|
|
||||||
) {
|
|
||||||
if (!clients.has(String(url))) {
|
|
||||||
clients.set(String(url), new McpClient({ url, ...opts }));
|
|
||||||
}
|
|
||||||
return clients.get(String(url))!;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getTemplate(schema: object) {
|
export function getTemplate(schema: object) {
|
||||||
if (!schema || schema === undefined || schema === null) return undefined;
|
if (!schema || schema === undefined || schema === null) return undefined;
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"name": "bknd",
|
"name": "bknd",
|
||||||
"version": "0.16.1",
|
"version": "0.17.0-rc.1",
|
||||||
"bin": "./dist/cli/index.js",
|
"bin": "./dist/cli/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cfworker/json-schema": "^4.1.1",
|
"@cfworker/json-schema": "^4.1.1",
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
"hono": "4.8.3",
|
"hono": "4.8.3",
|
||||||
"json-schema-library": "10.0.0-rc7",
|
"json-schema-library": "10.0.0-rc7",
|
||||||
"json-schema-to-ts": "^3.1.1",
|
"json-schema-to-ts": "^3.1.1",
|
||||||
"jsonv-ts": "^0.8.1",
|
"jsonv-ts": "0.8.2",
|
||||||
"kysely": "0.27.6",
|
"kysely": "0.27.6",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"oauth4webapi": "^2.11.1",
|
"oauth4webapi": "^2.11.1",
|
||||||
@@ -2516,7 +2516,7 @@
|
|||||||
|
|
||||||
"jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="],
|
"jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="],
|
||||||
|
|
||||||
"jsonv-ts": ["jsonv-ts@0.8.1", "", { "optionalDependencies": { "hono": "*" }, "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-kqt1OHZ4WM92PDAxySZeGGzccZr6q5YdKpM8c7QWkwGoaa1azwTG5lV9SN3PT4kVgI0OYFDr3OGkgCszLQ+WPw=="],
|
"jsonv-ts": ["jsonv-ts@0.8.2", "", { "optionalDependencies": { "hono": "*" }, "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-1Z7+maCfoGGqBPu5vN8rU9gIsW7OatYmn+STBTPkybbtNqeMzAoJDDrXHjsZ89x5dPH9W+OgMpNLtN0ouwiMYg=="],
|
||||||
|
|
||||||
"jsonwebtoken": ["jsonwebtoken@9.0.2", "", { "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ=="],
|
"jsonwebtoken": ["jsonwebtoken@9.0.2", "", { "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ=="],
|
||||||
|
|
||||||
|
|||||||
@@ -29,15 +29,15 @@ const app = createApp();
|
|||||||
|
|
||||||
app.emgr.onEvent(AppEvents.AppRequest, async (event) => {
|
app.emgr.onEvent(AppEvents.AppRequest, async (event) => {
|
||||||
// ^? AppRequest
|
// ^? AppRequest
|
||||||
console.log("Request received", event.request.url);
|
console.log("Request received", event.params.request.url);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.emgr.on("app-request", async (event) => {
|
app.emgr.on("app-request", async (event) => {
|
||||||
console.log("Request received", event.request.url);
|
console.log("Request received", event.params.request.url);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.emgr.onAny(async (event) => {
|
app.emgr.onAny(async (event, name) => {
|
||||||
console.log("Event received", event.slug);
|
console.log("Event received", event, name);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ import { AppEvents } from "bknd";
|
|||||||
export default {
|
export default {
|
||||||
onBuilt: (app) => {
|
onBuilt: (app) => {
|
||||||
app.emgr.onEvent(AppEvents.AppRequest, async (event) => {
|
app.emgr.onEvent(AppEvents.AppRequest, async (event) => {
|
||||||
console.log("Request received", event.request.url);
|
console.log("Request received", event.params.request.url);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -63,7 +63,7 @@ By default, listeners are registered as `async` listeners, meaning that the list
|
|||||||
app.emgr.onEvent(
|
app.emgr.onEvent(
|
||||||
AppEvents.AppRequest,
|
AppEvents.AppRequest,
|
||||||
async (event) => {
|
async (event) => {
|
||||||
console.log("Request received", event.request.url);
|
console.log("Request received", event.params.request.url);
|
||||||
},
|
},
|
||||||
{ mode: "sync" },
|
{ mode: "sync" },
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ import { config } from "@/bknd";
|
|||||||
import { serve } from "bknd/adapter/nextjs";
|
import { serve } from "bknd/adapter/nextjs";
|
||||||
|
|
||||||
// optionally, you can set the runtime to edge for better performance
|
// optionally, you can set the runtime to edge for better performance
|
||||||
export const runtime = "edge";
|
// export const runtime = "edge";
|
||||||
|
|
||||||
const handler = serve({
|
const handler = serve({
|
||||||
...config,
|
...config,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ bknd includes a fully featured MCP server that can be used to interact with the
|
|||||||
<source src="/content/mcp/v0.17_mcp_o.mp4" type="video/mp4" />
|
<source src="/content/mcp/v0.17_mcp_o.mp4" type="video/mp4" />
|
||||||
</video>
|
</video>
|
||||||
|
|
||||||
Once enabled, you can access the MCP UI at `/mcp` or choose "MCP" from the top right user menu.
|
Once enabled, you can access the MCP UI at `/mcp`, or choose "MCP" from the top right user menu.
|
||||||
|
|
||||||
## Enable MCP
|
## Enable MCP
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ The implementation is closely following the [MCP spec 2025-06-18](https://modelc
|
|||||||
import { McpClient } from "bknd/utils";
|
import { McpClient } from "bknd/utils";
|
||||||
|
|
||||||
const client = new McpClient({
|
const client = new McpClient({
|
||||||
url: "http://localhost:1337/mcp",
|
url: "http://localhost:1337/api/system/mcp",
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ Pasting the following config into your Cursor `~/.cursor/mcp.json` file is the r
|
|||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"bknd": {
|
"bknd": {
|
||||||
"url": "http://localhost:1337/mcp"
|
"url": "http://localhost:1337/api/system/mcp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,7 +155,7 @@ Add this to your VS Code MCP config. See [VS Code MCP docs](https://code.visuals
|
|||||||
"servers": {
|
"servers": {
|
||||||
"bknd": {
|
"bknd": {
|
||||||
"type": "http",
|
"type": "http",
|
||||||
"url": "http://localhost:1337/mcp"
|
"url": "http://localhost:1337/api/system/mcp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,7 +188,7 @@ When using the Streamable HTTP transport, you can pass the `Authorization` heade
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const client = new McpClient({
|
const client = new McpClient({
|
||||||
url: "http://localhost:1337/mcp",
|
url: "http://localhost:1337/api/system/mcp",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* We're using separate files, so that "wrangler" doesn't get bundled with your worker.
|
* We're using separate files, so that "wrangler" doesn't get bundled with your worker.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { withPlatformProxy } from "bknd/adapter/cloudflare";
|
import { withPlatformProxy } from "bknd/adapter/cloudflare/proxy";
|
||||||
import config from "./config.ts";
|
import config from "./config.ts";
|
||||||
|
|
||||||
export default withPlatformProxy(config);
|
export default withPlatformProxy(config);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { CloudflareBkndConfig } from "bknd/adapter/cloudflare";
|
|||||||
import { syncTypes } from "bknd/plugins";
|
import { syncTypes } from "bknd/plugins";
|
||||||
import { writeFile } from "node:fs/promises";
|
import { writeFile } from "node:fs/promises";
|
||||||
|
|
||||||
const isDev = !import.meta.env.PROD;
|
const isDev = import.meta.env && !import.meta.env.PROD;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
d1: {
|
d1: {
|
||||||
|
|||||||
Reference in New Issue
Block a user