Skip to main content
Glama
stdio.ts3.89 kB
import { t } from './i18n/index.js'; // __I18N_READY__ // src/stdio.ts import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { z, ZodTypeAny } from 'zod'; // ⬇️ IMPORTANT : garde bien les suffixes ".js" car le build ESM référencera build/*.js import { setSessionAuth, getSessionAuth } from './context.js'; import { registerPaymentsTools } from './tools/payments.js'; import { register402client } from './tools/402client.js'; import { registerAuthTool } from './tools/auth.js'; import { createPLinkMCPserver } from './support/mcp.js'; // ==== Session globale (STDIO: une seule connexion) ==== type AuthState = { ok: boolean; APIKEY?: string; scopes?: string[] }; /** Vérifie qu'un objet est un ZodRawShape (Record<string, ZodTypeAny>) */ function isZodRawShape(x: unknown): x is Record<string, ZodTypeAny> { if (!x || typeof x !== 'object' || Array.isArray(x)) return false; for (const v of Object.values(x as Record<string, unknown>)) { if (!v || typeof v !== 'object' || !(v as any)._def) return false; } return true; } /** Normalise en ZodRawShape ; log si correction appliquée */ function ensureZodRawShape( maybeShape: unknown, kind: 'inputSchema' | 'outputSchema', toolName: string ): Record<string, ZodTypeAny> { if (isZodRawShape(maybeShape)) return maybeShape; process.stderr.write(`[P-Link][patch] ${kind} absent/non-ZodRawShape → {} pour ${toolName}\n`); return {}; } async function main() { const envKey = process.env.APIKEY ?? process.env.MCP_APIKEY; if (!getSessionAuth() && envKey) { setSessionAuth({ ok: true, APIKEY: envKey, scopes: ['*'] }); process.stderr.write('[P-Link][auth] Session initialisée depuis variables d’environnement.\n'); } // --- Logs de contexte --- try { // @ts-ignore const __dir = typeof __dirname !== 'undefined' ? __dirname : '(no __dirname)'; process.stderr.write(`[P-Link][path] __dirname=${__dir}\n`); process.stderr.write(`[P-Link][env] API_BASE=${process.env.API_BASE ?? ''} \n`); } catch { } // --- Création du serveur MCP --- const server = createPLinkMCPserver(); // --- Guard inline: protège tous les tools sauf whitelist --- //type Ctx = { auth?: { ok: boolean; user?: string; scopes?: string[] } }; // Wrap de registerTool try { registerAuthTool(server); registerPaymentsTools(server); register402client(server); } catch (e: any) { process.stderr.write(`[P-Link][error] Echec registerXTools: ${e?.stack || e}\n`); } // --- Tool "ping" minimal (inputSchema sous forme de shape, pas z.object) --- (server as any).registerTool( 'ping', { title: t('tools.ping.title'), description: t('tools.ping.description'), inputSchema: { msg: z.string().optional() }, // ✅ ZodRawShape }, async ({ msg }: { msg?: string }) => ({ content: [{ type: 'text', text: `pong${msg ? ': ' + msg : ''}` }], structuredContent: { ok: true, echo: msg ?? null }, }) ); // --- Démarrage en STDIO --- const transport = new StdioServerTransport(); await server.connect(transport); process.stderr.write(`[P-Link][info] Server started and connected successfully (stdio)\n`); } // --- Garde-fous globaux --- process.on('unhandledRejection', (err: any) => { process.stderr.write(`[P-Link][fatal] UnhandledRejection: ${err?.stack || err}\n`); }); process.on('uncaughtException', (err: any) => { process.stderr.write(`[P-Link][fatal] UncaughtException: ${err?.stack || err}\n`); }); main().catch((e) => { process.stderr.write(`[P-Link][fatal] main() failed: ${e?.stack || e}\n`); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/paracetamol951/P-Link-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server