mcpService.ts•5.29 kB
import fetch from "node-fetch";
import dotenv from "dotenv";
dotenv.config();
const GITHUB_MCP_URL = process.env.GITHUB_MCP_URL || "";
const MONGODB_URI = process.env.MONGODB_URI || '';
const DB_NAME = process.env.DB_NAME || "";
const COLLECTION_NAME = process.env.COLLECTION_NAME || "";
export interface Tool {
name: string;
description: string;
category?: string;
inputSchema: {
type: "object";
properties: Record<string, any>;
required?: string[];
};
}
class MCPService {
private githubSessionId: string | null = null;
private currentToken: string | null = null;
async initializeGitHubSession(token: string) {
try {
const response = await fetch(GITHUB_MCP_URL, {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "initialize",
params: {
protocolVersion: "2025-03-26",
capabilities: {
tools: { listChanged: true },
},
clientInfo: {
name: "MCP Server Proxy",
version: "1.0.0",
},
},
}),
});
const sessionId = response.headers.get('mcp-session-id');
if (sessionId) {
this.githubSessionId = sessionId;
this.currentToken = token;
}
const text = await response.text();
if (!response.ok) {
throw new Error(`GitHub MCP Initialize Error (${response.status}): ${text}`);
}
return JSON.parse(text);
} catch (error) {
throw error;
}
}
async getToolSpecs(): Promise<Tool[]> {
try {
const { MongoClient } = await import("mongodb");
const client = new MongoClient(MONGODB_URI);
await client.connect();
const db = client.db(DB_NAME);
const collection = db.collection(COLLECTION_NAME);
const tools = await collection.find({}).toArray();
await client.close();
return tools.map(tool => ({
name: tool.toolId,
description: tool.toolDescription,
category: tool.category,
inputSchema: {
type: "object",
properties: tool.parameters.reduce((acc: Record<string, any>, param: any) => {
acc[param.key] = {
type: "string",
description: param.description || `${param.key} parameter`,
};
return acc;
}, {}),
required: tool.parameters
.filter((param: any) => param.required === true)
.map((param: any) => param.key),
},
}));
} catch (error) {
console.error("MongoDB Error:", error);
throw error;
}
}
async proxyToGitHub(method: string, params: object, token: string) {
if (!this.githubSessionId || this.currentToken !== token) {
await this.initializeGitHubSession(token);
}
const requestBody = {
jsonrpc: "2.0",
id: 1,
method,
params,
};
const headers: Record<string, string> = {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
};
if (this.githubSessionId) {
headers["Mcp-Session-Id"] = this.githubSessionId;
}
const response = await fetch(GITHUB_MCP_URL, {
method: "POST",
headers,
body: JSON.stringify(requestBody),
});
const text = await response.text();
if (!response.ok) {
// If session expired, try to reinitialize
if (response.status === 400 && text.includes("Invalid session ID")) {
this.githubSessionId = null;
await this.initializeGitHubSession(token);
// Retry the request with new session
const retryHeaders = {
...headers,
"Mcp-Session-Id": this.githubSessionId!,
};
const retryResponse = await fetch(GITHUB_MCP_URL, {
method: "POST",
headers: retryHeaders,
body: JSON.stringify(requestBody),
});
const retryText = await retryResponse.text();
if (!retryResponse.ok) {
throw new Error(`GitHub MCP Error (${retryResponse.status}): ${retryText}`);
}
return JSON.parse(retryText).result;
}
throw new Error(`GitHub MCP Error (${response.status}): ${text}`);
}
try {
const parsed = JSON.parse(text);
return parsed.result;
} catch (e) {
throw new Error(`Invalid JSON from GitHub MCP: ${text}`);
}
}
async executeTool(toolName: string, parameters: Record<string, any>, token: string) {
const tools = await this.getToolSpecs();
const tool = tools.find(t => t.name === toolName);
if (!tool) {
throw new Error("Tool not found");
}
if (toolName === "github_token") {
return {
content: [
{
text: `Your GitHub token has been received: ${parameters.token}`,
},
],
};
} else {
const result = await this.proxyToGitHub("tools/call", {
name: toolName,
arguments: parameters,
}, token);
return result;
}
}
}
export const mcpService = new MCPService();