feat(mcp): enhance MCP installation with status tracking and custom configs

- Add /mcp-servers/ to .gitignore for MCP server installations
- Extend mcp_installations table with status, logs, and validation fields
- Refactor MCP install route to support custom configs, enable logs, and async installation
- Improve MCPSyncService with promise-based syncing to prevent concurrent operations

This update improves MCP management by adding robust installation tracking, custom configuration support, and better error handling for a more reliable user experience.
This commit is contained in:
Evandro Fonseca Junior committed 2025-10-28 21:59:00 -03:00
1 parent 1042f2136b
commit f19bf10ef3
35 files changed
+4743 -429

No files matched your search

+214 -20
View File
@@ -24,9 +24,18 @@ export class MCPRepository {
*/
static saveInstallation(mcpId: string, config: any, tools: any[]): void {
try {
const [owner, repo] = mcpId.split('/');
if (!owner || !repo) {
throw new Error(`Invalid MCP ID format: ${mcpId}`);
// Tentar extrair owner/repo do ID
let owner: string;
let repo: string;
if (mcpId.includes("/")) {
const parts = mcpId.split("/");
owner = parts[0];
repo = parts.slice(1).join("/"); // Suportar múltiplas barras
} else {
// Se não tem /, usar mcpId como repo e extrair owner do config se disponível
owner = config.owner || config.author || "unknown";
repo = mcpId;
}
const stmt = db.prepare(`
@@ -58,7 +67,7 @@ export class MCPRepository {
try {
const stmt = db.prepare("DELETE FROM mcp_installations WHERE id = ?");
const result = stmt.run(mcpId);
if (result.changes > 0) {
console.log(`MCP ${mcpId} removed from local database`);
} else {
@@ -75,9 +84,11 @@ export class MCPRepository {
*/
static listInstalled(): string[] {
try {
const stmt = db.prepare("SELECT id FROM mcp_installations ORDER BY installed_at DESC");
const stmt = db.prepare(
"SELECT id FROM mcp_installations ORDER BY installed_at DESC"
);
const results = stmt.all() as { id: string }[];
return results.map(row => row.id);
return results.map((row) => row.id);
} catch (error) {
console.error("Error listing installed MCPs:", error);
return [];
@@ -89,9 +100,11 @@ export class MCPRepository {
*/
static getLocalConfig(mcpId: string): any {
try {
const stmt = db.prepare("SELECT config FROM mcp_installations WHERE id = ?");
const stmt = db.prepare(
"SELECT config FROM mcp_installations WHERE id = ?"
);
const result = stmt.get(mcpId) as { config: string } | undefined;
if (result) {
return JSON.parse(result.config);
}
@@ -107,9 +120,11 @@ export class MCPRepository {
*/
static getLocalTools(mcpId: string): any[] {
try {
const stmt = db.prepare("SELECT tools FROM mcp_installations WHERE id = ?");
const stmt = db.prepare(
"SELECT tools FROM mcp_installations WHERE id = ?"
);
const result = stmt.get(mcpId) as { tools: string } | undefined;
if (result) {
return JSON.parse(result.tools);
}
@@ -130,12 +145,12 @@ export class MCPRepository {
FROM mcp_installations WHERE id = ?
`);
const result = stmt.get(mcpId) as any;
if (result) {
return {
...result,
config: JSON.parse(result.config),
tools: JSON.parse(result.tools)
tools: JSON.parse(result.tools),
};
}
return null;
@@ -155,11 +170,11 @@ export class MCPRepository {
FROM mcp_installations ORDER BY installed_at DESC
`);
const results = stmt.all() as any[];
return results.map(row => ({
return results.map((row) => ({
...row,
config: JSON.parse(row.config),
tools: JSON.parse(row.tools)
tools: JSON.parse(row.tools),
}));
} catch (error) {
console.error("Error listing installed MCPs with details:", error);
@@ -172,19 +187,48 @@ export class MCPRepository {
*/
static updateTimestamp(mcpId: string): void {
try {
const stmt = db.prepare("UPDATE mcp_installations SET updated_at = CURRENT_TIMESTAMP WHERE id = ?");
const stmt = db.prepare(
"UPDATE mcp_installations SET updated_at = CURRENT_TIMESTAMP WHERE id = ?"
);
stmt.run(mcpId);
} catch (error) {
console.error(`Error updating timestamp for MCP ${mcpId}:`, error);
}
}
/**
* Atualizar configuração de um MCP instalado
*/
static updateConfig(mcpId: string, newConfig: any): void {
try {
const stmt = db.prepare(`
UPDATE mcp_installations
SET config = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`);
const result = stmt.run(JSON.stringify(newConfig), mcpId);
if (result.changes > 0) {
console.log(`MCP ${mcpId} configuration updated successfully`);
} else {
console.log(`MCP ${mcpId} was not found for configuration update`);
throw new Error(`MCP ${mcpId} not found`);
}
} catch (error) {
console.error(`Error updating configuration for MCP ${mcpId}:`, error);
throw error;
}
}
/**
* Verificar integridade do banco de dados
*/
static checkDatabaseIntegrity(): boolean {
try {
const result = db.prepare("PRAGMA integrity_check").get() as { integrity_check: string };
const result = db.prepare("PRAGMA integrity_check").get() as {
integrity_check: string;
};
return result.integrity_check === "ok";
} catch (error) {
console.error("Error checking database integrity:", error);
@@ -197,7 +241,9 @@ export class MCPRepository {
*/
static getInstallationStats(): any {
try {
const totalStmt = db.prepare("SELECT COUNT(*) as total FROM mcp_installations");
const totalStmt = db.prepare(
"SELECT COUNT(*) as total FROM mcp_installations"
);
const totalResult = totalStmt.get() as { total: number };
const recentStmt = db.prepare(`
@@ -211,16 +257,164 @@ export class MCPRepository {
SELECT COUNT(DISTINCT owner) as unique_owners
FROM mcp_installations
`);
const categoriesResult = categoriesStmt.get() as { unique_owners: number };
const categoriesResult = categoriesStmt.get() as {
unique_owners: number;
};
return {
total: totalResult.total,
recent: recentResult.recent,
uniqueOwners: categoriesResult.unique_owners
uniqueOwners: categoriesResult.unique_owners,
};
} catch (error) {
console.error("Error getting installation stats:", error);
return { total: 0, recent: 0, uniqueOwners: 0 };
}
}
/**
* Atualizar status de instalação de um MCP
*/
static updateInstallationStatus(
mcpId: string,
status: string,
message?: string
): void {
try {
const stmt = db.prepare(`
UPDATE mcp_installations
SET status = ?, status_message = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`);
stmt.run(status, message || null, mcpId);
console.log(`MCP ${mcpId} status updated to: ${status}`);
} catch (error) {
console.error(`Error updating status for MCP ${mcpId}:`, error);
throw error;
}
}
/**
* Salvar informações de ambiente de um MCP instalado
*/
static saveEnvironment(mcpId: string, environment: any): void {
try {
const stmt = db.prepare(`
UPDATE mcp_installations
SET environment_path = ?,
executable_command = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`);
const executableCommand = `${environment.executable} ${(
environment.args || []
).join(" ")}`.trim();
stmt.run(environment.path, executableCommand, mcpId);
console.log(`MCP ${mcpId} environment saved`);
} catch (error) {
console.error(`Error saving environment for MCP ${mcpId}:`, error);
throw error;
}
}
/**
* Obter ambiente de um MCP instalado
*/
static getEnvironment(mcpId: string): any | null {
try {
const stmt = db.prepare(`
SELECT environment_path, executable_command
FROM mcp_installations
WHERE id = ?
`);
const result = stmt.get(mcpId) as any;
if (!result || !result.environment_path) {
return null;
}
// Reconstruir objeto de ambiente
const parts = result.executable_command.split(" ");
return {
path: result.environment_path,
executable: parts[0],
args: parts.slice(1),
};
} catch (error) {
console.error(`Error getting environment for MCP ${mcpId}:`, error);
return null;
}
}
/**
* Adicionar logs de instalação
*/
static appendLogs(mcpId: string, logs: string[]): void {
try {
const stmt = db.prepare(`
SELECT install_logs FROM mcp_installations WHERE id = ?
`);
const result = stmt.get(mcpId) as
| { install_logs: string | null }
| undefined;
const existingLogs = result?.install_logs
? JSON.parse(result.install_logs)
: [];
const updatedLogs = [...existingLogs, ...logs];
// Limitar a 1000 linhas de log
const trimmedLogs = updatedLogs.slice(-1000);
const updateStmt = db.prepare(`
UPDATE mcp_installations
SET install_logs = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`);
updateStmt.run(JSON.stringify(trimmedLogs), mcpId);
} catch (error) {
console.error(`Error appending logs for MCP ${mcpId}:`, error);
}
}
/**
* Salvar resultado de validação
*/
static saveValidationResult(mcpId: string, validationResult: any): void {
try {
const stmt = db.prepare(`
UPDATE mcp_installations
SET validation_result = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`);
stmt.run(JSON.stringify(validationResult), mcpId);
console.log(`MCP ${mcpId} validation result saved`);
} catch (error) {
console.error(`Error saving validation result for MCP ${mcpId}:`, error);
}
}
/**
* Salvar ferramentas validadas
*/
static saveTools(mcpId: string, tools: any[]): void {
try {
const stmt = db.prepare(`
UPDATE mcp_installations
SET tools = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`);
stmt.run(JSON.stringify(tools), mcpId);
console.log(`MCP ${mcpId} tools saved: ${tools.length} tools`);
} catch (error) {
console.error(`Error saving tools for MCP ${mcpId}:`, error);
}
}
}