"use client"; import { useActionState } from "react"; import { Button } from "@/app/_components/ui/button"; import { Input, Label } from "@/app/_components/ui/input"; /** * State returned by the `createTokenAction` server action. * * `projectKey` is the project the user chose to pin the token to. It's NOT * baked into the JWT itself — the token remains identity-only — it just * lets us bake `--header "X-Project-Key: "` into the generated * `claude mcp add` snippet so calls from this client default to that * project without the model having to pass it explicitly. */ export interface CreateTokenState { token: string | null; error: string | null; projectKey: string | null; } export interface ProjectOption { key: string; displayName: string | null; } interface Props { action: (prev: CreateTokenState, formData: FormData) => Promise; ttlDays: number; projects: ProjectOption[]; } const initial: CreateTokenState = { token: null, error: null, projectKey: null }; export default function TokensManager({ action, ttlDays, projects }: Props) { const [state, formAction, pending] = useActionState(action, initial); if (state.token) { return (
Token generated — copy now, you won't see it again
          {state.token}
        
claude mcp add command
{buildMcpAddSnippet(state.token, state.projectKey)}

Valid for {ttlDays} days. Revoke individually below if it leaks. {state.projectKey ? ( <> {" "}This token is pinned to project{" "} {state.projectKey} via the{" "} X-Project-Key header in the snippet above — the JWT itself is identity-only. ) : null}

); } return (
{state.error ? (

error: {state.error}

) : null}
); } function ProjectSelect({ projects }: { projects: ProjectOption[] }) { // Match Input styling — Tailwind v4 classes from `lib/ui/input.tsx`. const cls = "mt-1 block w-full h-9 px-3 text-sm rounded-md bg-surface-1 " + "border border-border text-fg focus:border-accent-400 focus:outline-none " + "disabled:opacity-50 transition-colors"; if (projects.length === 0) { return ( ); } return ( ); } function buildMcpAddSnippet(token: string, projectKey: string | null): string { const headerLines = [` --header "Authorization: Bearer ${token}"`]; if (projectKey) { headerLines.push(` --header "X-Project-Key: ${projectKey}"`); } return [ "claude mcp add --transport http --scope user \\", ...headerLines.map((l) => `${l} \\`), " shared-memory https://memory.dnspegasus.net/api/mcp", ].join("\n"); }