
AIエージェントのMCPツール呼び出しとLLMトラフィックを保護・統制・監視するオープンソースゲートウェイ。APIキー認証と監視用の管理コンソールを備えています。
Tuskira AI Agent Gateway の管理コンソール。React 19 + Vite + TypeScript (strict) + Tailwind CSS 4 + shadcn/ui。
Node 26+ と npm 11+ が必要です (pnpm は不可)。
npm install
npm run dev
開発サーバーは /api を http://localhost:8081 にプロキシします(vite.config.ts を参照)。そのため、UI と並行してゲートウェイを :8081 で実行してください。/login で、ゲートウェイが発行した API キー(gk_…)を使ってサインインします。
リポジトリのルートからは、代わりに次を実行できます:
make ui-install
make ui-dev
npm run build
dist/ に本番用バンドルを出力します。本番環境ではゲートウェイ自体が :8081 の / でこのバンドルを配信するため、アプリは同一オリジンで API と通信します — プロキシは不要です。
make ui-build # from the repo root
コンソールは @tuskira/gateway-web としてもパッケージ化されているため、そのページを独自の React アプリ内にマウントできます。このパッケージは npm レジストリには公開されていません。各 GitHub リリースでは tarball として提供され、そのバージョンは常にゲートウェイのリリースタグと一致します。React、React DOM、React Router(react-router-dom 7)、および TanStack Query 5 はピア依存関係です。それ以外はすべてパッケージとともにインストールされます。
npm install https://github.com/Tuskira/ai-agent-gateway/releases/download/v<version>/tuskira-gateway-web-<version>.tgz \
react react-dom react-router-dom @tanstack/react-query
npm は tarball の整合性ハッシュを package-lock.json に記録します。
import {
AppShell, AuthProvider, RequireAuth, LoginPage, Toaster, TooltipProvider,
configureGateway, gatewayPageRoutes,
} from '@tuskira/gateway-web'
import '@tuskira/gateway-web/style.css' // Tailwind output + design tokens
import '@tuskira/gateway-web/fonts.css' // optional: self-hosted Inter / JetBrains Mono
configureGateway({
apiBase: '/gw/api/v1', // default '/api/v1'
mcpBase: 'https://gw.example.com/mcp', // default: guessed from the page origin
onUnauthenticated: () => router.navigate('/login'), // default: window.location.assign('/login')
})
const router = createBrowserRouter(
[
{ path: '/login', element: <LoginPage /> },
{ element: <RequireAuth />, children: [{ element: <AppShell />, children: gatewayPageRoutes() }] },
],
{ basename: '/gateway' },
)
QueryClientProvider、AuthProvider、TooltipProvider でルーターをラップし、src/App.tsx が行っているように <Toaster /> を一度だけレンダリングします。コンソールにルーター全体を所有させたい場合は、gatewayRoutes() が完全なツリー(ログイン、ドキュメント、パスワード変更、シェル、およびキャッチオールリダイレクト)を返します。サポートされているのは @tuskira/gateway-web がエクスポートするものだけです。src/ からインポートしないでください。
extension を渡すことで、独自のサイドバーグループとページをコンソールのシェルおよびサインインガード内に配置できます:
gatewayRoutes({ extension: { navGroups: [...], routes: [...] } })
import { Plug } from 'lucide-react'
import { AppShell, gatewayPageRoutes, type ConsoleExtension } from '@tuskira/gateway-web'
const extension: ConsoleExtension = {
navGroups: [
// New group, placed before the core "Observe" group (appended without `before`).
{ label: 'Billing', before: 'Observe', items: [
{ label: 'Invoices', path: '/invoices', icon: Plug, description: 'Invoices and usage.' },
] },
// Same label as a core group: the items are added to that group.
{ label: 'Access', items: [
{ label: 'Audit', path: '/audit', icon: Plug, description: 'Audit trail.' },
] },
],
routes: [
{ path: '/invoices', Component: InvoicesPage },
{ path: '/audit', Component: AuditPage },
],
}
// Own router: pass the extension to both the routes and the shell.
{ element: <AppShell extension={extension} />, children: gatewayPageRoutes(extension) }
gatewayRoutes({ extension }) がその両方を処理します。パスに対応するルートがないナビ項目には「coming soon」プレースホルダーが表示されます。パスが他の項目のパスのプレフィックスになっている項目には end: true を設定すると、その項目自身のパスでのみアクティブになります。
ブラウザは、apiBase として設定したパスでゲートウェイ API に到達できる必要があります。ホストアプリがコンソールを配信する場合は、/api/v1/*(または apiBase のパスを /api/v1 に書き換えたもの)をポート 8081 のゲートウェイへ転送するリバースプロキシルールを追加してください。パス、すべてのリクエストヘッダーとレスポンスヘッダー(Authorization、X-CSRF-Token、X-Gateway-Acting-Tenant)、および Cookie は変更しないでください。変更するとサインインや CSRF チェックが失敗します。
npm run build:lib でパッケージをビルドします。これにより dist-lib/(index.js、types/、style.css、fonts.css)が出力されます。その後 npm pack で tarball が生成され、リリースワークフローが v* タグごとに GitHub リリースへ添付します。
npm run lint # eslint
npm run typecheck # tsc --noEmit (project references)
npm run test # vitest
npm run format:check # prettier --check
npx shadcn@latest add <component>
このプロジェクトの components.json は Vite テンプレート、radix ベース、neutral ベースカラー、およびテーマ設定用の CSS 変数を対象としており、@/* インポートエイリアスは src/* に解決されます。新しいコンポーネントは src/components/ui/ に書き込まれます。
tokens.json(リポジトリルート)はデザインシステムの唯一の信頼できる情報源です — 色、スペーシング、角丸、シャドウ、フォント。編集後は npm run gen:tokens を実行して以下を再生成してください:
src/styles/tokens.css — CSS カスタムプロパティとしての生のトークン(ライト値は :root、ダークオーバーライドは .dark)、デザイナー自身のトークン名(--indigo-9、--sev-high、--r-4、--shadow-1、...)を保持。src/styles/tokens.ts — 同じカテゴリカルカラースケール(colors.indigo、colors.slate、colors.sev、colors.status、colors.brand)に加えて fonts、radii、shadows を、非 CSS コンシューマ(例:recharts のシリーズカラー)向けに提供。src/index.css は shadcn のセマンティック変数(--background、--primary、--sidebar、...)をこれらのトークンにマッピングし、Tailwind v4 の @theme エントリを登録することで、bg-indigo-9、text-text-muted、border-border-strong、shadow-1、rounded-r-4、font-mono などのユーティリティがアプリ内のどこでも利用可能になります。src/styles/tokens.css / src/styles/tokens.ts を手動で編集しないでください — ジェネレーターによって上書きされます。src/test/tokens.test.ts は、すべての tokens.ts エントリに対応する宣言が tokens.css に存在することを検証します。
web/
├── index.html
├── vite.config.ts # React + Tailwind plugins, /api dev proxy, vitest config
├── components.json # shadcn/ui config
├── tokens.json # design-token source of truth (see Design tokens)
├── scripts/
│ └── gen-tokens.mjs # tokens.json -> src/styles/tokens.{css,ts}
├── src/
│ ├── main.tsx # ReactDOM root
│ ├── App.tsx # QueryClientProvider, AuthProvider, RouterProvider
│ ├── index.css # Tailwind entrypoint + semantic token mapping
│ ├── styles/
│ │ ├── tokens.css # generated — raw design tokens as CSS vars
│ │ └── tokens.ts # generated — same tokens for non-CSS use
│ ├── auth/
│ │ ├── AuthContext.tsx # AuthProvider / useAuth — principal, signIn, signOut
│ │ └── RequireAuth.tsx # route guard for the authenticated shell
│ ├── components/
│ │ ├── ui/ # shadcn/ui primitives (generated, don't hand-edit)
│ │ ├── layout/ # AppShell, Sidebar, TopBar
│ │ └── app/ # ComingSoon, StatCard
│ ├── hooks/
│ │ └── use-theme.ts # light/dark toggle, class strategy, localStorage
│ ├── lib/
│ │ ├── api.ts # fetch wrapper: bearer auth, 401 handling, ApiError
│ │ ├── nav.ts # single source of truth for sidebar nav + routes
│ │ ├── queries.ts # react-query hooks (useHealth)
│ │ ├── query-client.ts # QueryClient instance
│ │ ├── types.ts # Principal, HealthResponse, ApiErrorBody
│ │ └── utils.ts # cn() (shadcn)
│ ├── routes/
│ │ ├── router.tsx # createBrowserRouter (data router)
│ │ ├── LoginPage.tsx
│ │ ├── OverviewPage.tsx
│ │ └── DocsPage.tsx
│ └── test/
│ ├── setup.ts
│ ├── LoginPage.test.tsx
│ ├── Sidebar.test.tsx
│ ├── SignOut.test.tsx
│ └── tokens.test.ts