160 lines
4.0 KiB
JavaScript
Executable File
160 lines
4.0 KiB
JavaScript
Executable File
#!/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);
|
|
});
|