#!/usr/bin/env node /** * Vibn MCP Adapter for Zed * * This script runs locally as a standard stdio MCP server for Zed. * It intercepts tool calls and forwards them over HTTP to the Vibn API * using the proprietary format that Vibn expects. */ const { Server } = require("@modelcontextprotocol/sdk/server/index.js"); const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js"); const { CallToolRequestSchema, ListToolsRequestSchema } = require("@modelcontextprotocol/sdk/types.js"); // Configuration from environment variables const VIBN_API_URL = process.env.VIBN_API_URL || "https://vibnai.com/api/mcp"; const VIBN_API_KEY = process.env.VIBN_API_KEY; if (!VIBN_API_KEY) { console.error("Missing VIBN_API_KEY environment variable."); process.exit(1); } // Create the MCP server const server = new Server( { name: "vibn-zed-adapter", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); let cachedTools = null; // Fetch the tool list from the Vibn API async function fetchTools() { if (cachedTools) return cachedTools; try { const response = await fetch(VIBN_API_URL, { method: "GET", headers: { Authorization: \`Bearer \${VIBN_API_KEY}\`, }, }); if (!response.ok) { throw new Error(\`Failed to fetch tools: \${response.statusText}\`); } const data = await response.json(); // We need to fetch the actual tool schemas since the GET endpoint // only returns a string array of tool names. // For this adapter, we define the schema passthrough. // Note: Since Vibn's GET /api/mcp only returns an array of names: // "available": ["workspace.describe", "gitea.credentials", ...] // We will construct basic MCP tool definitions for them. // In a production scenario, you would expose the JSON schemas on the GET endpoint. const available = data.capabilities?.tools?.available || []; cachedTools = available.map(name => ({ name: name, description: \`Vibn workspace tool: \${name}\`, inputSchema: { type: "object", properties: { // Accept any parameters generically for the proxy projectId: { type: "string", description: "Vibn project ID" }, }, additionalProperties: true } })); return cachedTools; } catch (error) { console.error("Error fetching tools:", error); return []; } } // Handle List Tools Request server.setRequestHandler(ListToolsRequestSchema, async () => { const tools = await fetchTools(); return { tools: tools, }; }); // Handle Call Tool Request server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { // Forward the request to Vibn API const response = await fetch(VIBN_API_URL, { method: "POST", headers: { "Content-Type": "application/json", Authorization: \`Bearer \${VIBN_API_KEY}\`, }, body: JSON.stringify({ tool: name, // Vibn's expected format params: args || {}, }), }); const data = await response.json(); if (!response.ok) { return { content: [ { type: "text", text: \`Error (\${response.status}): \${JSON.stringify(data.error || data)}\`, }, ], isError: true, }; } return { content: [ { type: "text", text: JSON.stringify(data.result || data, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text", text: \`Internal Adapter Error: \${error.message}\`, }, ], isError: true, }; } }); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Vibn Zed MCP Adapter running on stdio"); } main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });