Skip to main content
Glama
index.ts8.53 kB
#!/usr/bin/env node import "dotenv/config"; import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, type Tool, } from "@modelcontextprotocol/sdk/types.js"; import fetch from "node-fetch"; // ... reste du code inchangé /** * Represents a message in the Cairo Coder conversation */ interface CairoCoderMessage { role: "user" | "assistant" | "system"; content: string; } /** * Request payload for the Cairo Coder API */ interface CairoCoderRequest { messages: CairoCoderMessage[]; } /** * Response from the Cairo Coder API */ interface CairoCoderResponse { choices: Array<{ message: { content: string; role: string; }; }>; } /** * MCP Server implementation for Cairo Coder API integration * Provides AI-powered assistance for Cairo and Starknet development */ class CairoCoderMCPServer { private server: Server; private apiKey: string; private apiUrl: string; private isLocalMode: boolean; /** * Initializes the Cairo Coder MCP Server * @throws {Error} If CAIRO_CODER_API_KEY environment variable is not set when using public API */ constructor() { this.server = new Server({ name: "cairo-coder-mcp", version: "1.0.0", capabilities: { tools: {}, }, }); // Check if local endpoint is specified const localEndpoint = process.env.CAIRO_CODER_API_ENDPOINT; if (localEndpoint) { // Local mode: use custom endpoint, no API key required this.isLocalMode = true; this.apiUrl = `${localEndpoint}/v1/chat/completions`; this.apiKey = ""; console.error( `Cairo Coder MCP server configured for local mode: ${this.apiUrl}`, ); } else { // Public API mode: use official endpoint, API key required this.isLocalMode = false; this.apiUrl = "https://api.cairo-coder.com/v1/chat/completions"; this.apiKey = process.env.CAIRO_CODER_API_KEY || ""; if (!this.apiKey) { console.error( "Error: CAIRO_CODER_API_KEY environment variable is required when using public API", ); process.exit(1); } console.error("Cairo Coder MCP server configured for public API mode"); } this.setupToolHandlers(); this.setupErrorHandling(); } /** * Sets up the tool handlers for the MCP server * Configures the assist_with_cairo tool for Cairo/Starknet development assistance */ private setupToolHandlers(): void { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "assist_with_cairo", description: `Provides assistance with Cairo and Starknet development tasks through AI-powered analysis. Call this tool when the user's request involves **writing, refactoring, implementing from scratch, or completing specific parts (like TODOs)** of Cairo code or smart contracts. The tool analyzes the query and context against Cairo/Starknet best practices and documentation, returning helpful information to generate accurate code or explanations. This tool should also be called to get a better understanding of Starknet's ecosystem, features, and capacities.`, inputSchema: { type: "object", properties: { query: { type: "string", description: "The user's question regarding Cairo and Starknet development. Try to be as specific as possible for better results (e.g., 'Using OpenZeppelin to build an ERC20' rather than just 'ERC20').", }, codeSnippets: { type: "array", items: { type: "string", }, description: "Optional: Code snippets for context. This will help the tool understand the user's intent and provide more accurate answers. Provide as much relevant code as possible to fit the user's request.", }, history: { type: "array", items: { type: "string", }, description: "Optional: The preceding conversation history. This can help the tool understand the context of the discussion and provide more accurate answers.", }, }, required: ["query"], }, } as Tool, ], }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; if (name === "assist_with_cairo") { return await this.handleCairoAssistance( args as { query: string; codeSnippets?: string[]; history?: string[]; }, ); } throw new Error(`Unknown tool: ${name}`); }); } /** * Handles Cairo assistance requests by calling the Cairo Coder API * @param args - The arguments containing query, optional code snippets, and conversation history * @returns The response from the Cairo Coder API or an error message */ private async handleCairoAssistance(args: { query: string; codeSnippets?: string[]; history?: string[]; }) { try { const { query, codeSnippets, history } = args; if (!query) { throw new Error("Query parameter is required"); } let contextualMessage = query; if (codeSnippets && codeSnippets.length > 0) { contextualMessage += `\n\nCode snippets for context:\n${codeSnippets.join("\n\n")}`; } if (history && history.length > 0) { contextualMessage = `Previous conversation context:\n${history.join("\n")}\n\nCurrent query: ${contextualMessage}`; } const requestBody: CairoCoderRequest = { messages: [ { role: "user", content: contextualMessage, }, ], }; // Prepare headers based on mode const headers: Record<string, string> = { "Content-Type": "application/json", mcp: "true", }; // Only add API key header in public API mode if (!this.isLocalMode && this.apiKey) { headers["x-api-key"] = this.apiKey; } const response = await fetch(this.apiUrl, { method: "POST", headers, body: JSON.stringify(requestBody), }); if (!response.ok) { const errorText = await response.text(); throw new Error( `API request failed: ${response.status} ${response.statusText} - ${errorText}`, ); } const data = (await response.json()) as CairoCoderResponse; if (!data.choices || data.choices.length === 0) { throw new Error("No response received from Cairo Coder API"); } const assistantResponse = data.choices[0].message.content; return { content: [ { type: "text", text: assistantResponse, }, ], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; return { content: [ { type: "text", text: `Error: ${errorMessage}`, }, ], isError: true, }; } } /** * Sets up error handling for the server * Configures error logging and graceful shutdown on SIGINT */ private setupErrorHandling(): void { this.server.onerror = (error) => { console.error("[MCP Error]", error); }; process.on("SIGINT", async () => { await this.server.close(); process.exit(0); }); } /** * Starts the MCP server with stdio transport * @throws {Error} If the server fails to start */ async run(): Promise<void> { const transport = new StdioServerTransport(); console.error("Cairo Coder MCP server running on stdio"); await this.server.connect(transport); } } /** * Main entry point for the application * Creates and starts the Cairo Coder MCP server */ async function main() { const server = new CairoCoderMCPServer(); await server.run(); } main().catch((error) => { console.error("Fatal error in main():", error); process.exit(1); }); export default CairoCoderMCPServer;

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/KasarLabs/cairo-coder-mcp'

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