import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { z } from 'zod'; const CORE_URL = process.env['TOKENVAULT_URL'] ?? 'http://localhost:3300'; async function fetchCore(path: string): Promise { const res = await fetch(`${CORE_URL}${path}`); if (!res.ok) throw new Error(`TokenVault API error: ${res.status}`); return res.json() as Promise; } const server = new McpServer({ name: 'tokenvault', version: '0.1.0', }); // ─── tv_ticket: View and search tickets ────────────────────────────────────── server.tool( 'tv_ticket', 'View, search, and manage TokenVault tickets. Every LLM request = 1 ticket.', { action: z.enum(['list', 'get', 'stats']).describe('Action to perform'), id: z.string().optional().describe('Ticket ID (for get action)'), provider: z.string().optional().describe('Filter by provider'), project: z.string().optional().describe('Filter by project'), period: z.string().optional().describe('Stats period: today, week, month, all'), limit: z.number().optional().describe('Max results (default 20)'), }, async ({ action, id, provider, project, period, limit }) => { if (action === 'get' && id) { const ticket = await fetchCore(`/v1/tickets/${id}`); return { content: [{ type: 'text' as const, text: JSON.stringify(ticket, null, 2) }] }; } if (action === 'stats') { const stats = await fetchCore(`/v1/tickets/stats?period=${period ?? 'today'}`); return { content: [{ type: 'text' as const, text: JSON.stringify(stats, null, 2) }] }; } const params = new URLSearchParams(); if (provider) params.set('provider', provider); if (project) params.set('project', project); params.set('limit', String(limit ?? 20)); const data = await fetchCore(`/v1/tickets?${params}`); return { content: [{ type: 'text' as const, text: JSON.stringify(data, null, 2) }] }; }, ); // ─── tv_cost: Cost dashboard ───────────────────────────────────────────────── server.tool( 'tv_cost', 'Show cost dashboard: spend, savings, forecasts. Tracks every token across all LLM providers.', { period: z.enum(['today', 'week', 'month', 'all']).optional().describe('Time period'), group_by: z.enum(['provider', 'model', 'project', 'team']).optional().describe('Group breakdown by'), }, async ({ period, group_by }) => { const [summary, breakdown] = await Promise.all([ fetchCore(`/v1/cost?period=${period ?? 'month'}`), group_by ? fetchCore(`/v1/cost/breakdown?group_by=${group_by}`) : Promise.resolve(null), ]); const parts = [`# Cost Summary (${period ?? 'month'})\n${JSON.stringify(summary, null, 2)}`]; if (breakdown) { parts.push(`\n# Breakdown by ${group_by}\n${JSON.stringify(breakdown, null, 2)}`); } return { content: [{ type: 'text' as const, text: parts.join('\n') }] }; }, ); // ─── tv_health: Service health ─────────────────────────────────────────────── server.tool( 'tv_health', 'Check TokenVault service health and configured providers.', {}, async () => { const health = await fetchCore('/health'); return { content: [{ type: 'text' as const, text: JSON.stringify(health, null, 2) }] }; }, ); // ─── Start ─────────────────────────────────────────────────────────────────── const transport = new StdioServerTransport(); await server.connect(transport);