feat(api): integrate MCP tools support in Ollama chat route
Add support for Model Context Protocol (MCP) tools in the Ollama chat API. Detect model capabilities to use native tools or prompt engineering. Handle tool calls, execute them via MCPExecutor, and inject results back into the conversation for seamless tool-assisted responses. This enhances chat functionality by enabling external tool integration.
This commit is contained in:
1 parent
745746afa9
commit
4739a13458
18 files changed
+1782
-10
No files matched your search
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Repositório para gerenciar MCPs ativos por conversa/usuário
|
||||
*/
|
||||
|
||||
import Database from "better-sqlite3";
|
||||
import path from "path";
|
||||
|
||||
const dbPath = path.join(process.cwd(), "database", "ollahub.db");
|
||||
const db = new Database(dbPath);
|
||||
|
||||
export interface ActiveMCP {
|
||||
id: string;
|
||||
conversation_id: string;
|
||||
mcp_id: string;
|
||||
enabled: boolean;
|
||||
config?: any;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export class MCPActiveRepository {
|
||||
/**
|
||||
* Salvar MCPs ativos para uma conversa
|
||||
* @param conversationId ID da conversa
|
||||
* @param mcpIds Array de IDs de MCPs ativos
|
||||
*/
|
||||
static saveActiveMCPs(conversationId: string, mcpIds: string[]): void {
|
||||
try {
|
||||
// Primeiro, remover todas as entradas antigas da conversa
|
||||
const deleteStmt = db.prepare(
|
||||
"DELETE FROM active_mcps WHERE conversation_id = ?"
|
||||
);
|
||||
deleteStmt.run(conversationId);
|
||||
|
||||
// Inserir novos MCPs ativos
|
||||
const insertStmt = db.prepare(`
|
||||
INSERT INTO active_mcps (conversation_id, mcp_id, enabled, created_at)
|
||||
VALUES (?, ?, 1, CURRENT_TIMESTAMP)
|
||||
`);
|
||||
|
||||
for (const mcpId of mcpIds) {
|
||||
insertStmt.run(conversationId, mcpId);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Saved ${mcpIds.length} active MCPs for conversation ${conversationId}`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error saving active MCPs:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter MCPs ativos de uma conversa
|
||||
* @param conversationId ID da conversa
|
||||
* @returns Array de IDs de MCPs ativos
|
||||
*/
|
||||
static getActiveMCPs(conversationId: string): string[] {
|
||||
try {
|
||||
const stmt = db.prepare(`
|
||||
SELECT mcp_id FROM active_mcps
|
||||
WHERE conversation_id = ? AND enabled = 1
|
||||
ORDER BY created_at DESC
|
||||
`);
|
||||
|
||||
const results = stmt.all(conversationId) as { mcp_id: string }[];
|
||||
return results.map((row) => row.mcp_id);
|
||||
} catch (error) {
|
||||
console.error("Error getting active MCPs:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ativar/desativar um MCP específico para uma conversa
|
||||
* @param conversationId ID da conversa
|
||||
* @param mcpId ID do MCP
|
||||
* @param enabled true para ativar, false para desativar
|
||||
*/
|
||||
static toggleMCP(
|
||||
conversationId: string,
|
||||
mcpId: string,
|
||||
enabled: boolean
|
||||
): void {
|
||||
try {
|
||||
// Verificar se já existe
|
||||
const checkStmt = db.prepare(`
|
||||
SELECT id FROM active_mcps
|
||||
WHERE conversation_id = ? AND mcp_id = ?
|
||||
`);
|
||||
const existing = checkStmt.get(conversationId, mcpId);
|
||||
|
||||
if (existing) {
|
||||
// Atualizar
|
||||
const updateStmt = db.prepare(`
|
||||
UPDATE active_mcps SET enabled = ?
|
||||
WHERE conversation_id = ? AND mcp_id = ?
|
||||
`);
|
||||
updateStmt.run(enabled ? 1 : 0, conversationId, mcpId);
|
||||
} else {
|
||||
// Inserir
|
||||
const insertStmt = db.prepare(`
|
||||
INSERT INTO active_mcps (conversation_id, mcp_id, enabled, created_at)
|
||||
VALUES (?, ?, ?, CURRENT_TIMESTAMP)
|
||||
`);
|
||||
insertStmt.run(conversationId, mcpId, enabled ? 1 : 0);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`MCP ${mcpId} ${
|
||||
enabled ? "enabled" : "disabled"
|
||||
} for conversation ${conversationId}`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error toggling MCP:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Limpar MCPs ativos de uma conversa
|
||||
* @param conversationId ID da conversa
|
||||
*/
|
||||
static clearActiveMCPs(conversationId: string): void {
|
||||
try {
|
||||
const stmt = db.prepare(
|
||||
"DELETE FROM active_mcps WHERE conversation_id = ?"
|
||||
);
|
||||
stmt.run(conversationId);
|
||||
console.log(`Cleared active MCPs for conversation ${conversationId}`);
|
||||
} catch (error) {
|
||||
console.error("Error clearing active MCPs:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter todas as conversas que usam um MCP específico
|
||||
* @param mcpId ID do MCP
|
||||
* @returns Array de IDs de conversas
|
||||
*/
|
||||
static getConversationsUsingMCP(mcpId: string): string[] {
|
||||
try {
|
||||
const stmt = db.prepare(`
|
||||
SELECT DISTINCT conversation_id FROM active_mcps
|
||||
WHERE mcp_id = ? AND enabled = 1
|
||||
`);
|
||||
|
||||
const results = stmt.all(mcpId) as { conversation_id: string }[];
|
||||
return results.map((row) => row.conversation_id);
|
||||
} catch (error) {
|
||||
console.error("Error getting conversations using MCP:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,6 +182,38 @@ export class MCPRepository {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter todos os MCPs instalados com informações completas (incluindo status, environment, etc)
|
||||
*/
|
||||
static getAllInstalled(): any[] {
|
||||
try {
|
||||
const stmt = db.prepare(`
|
||||
SELECT
|
||||
id, owner, repo, name, config, tools,
|
||||
status, status_message,
|
||||
environment_path, executable_command,
|
||||
validation_result, install_logs,
|
||||
installed_at, updated_at
|
||||
FROM mcp_installations
|
||||
ORDER BY installed_at DESC
|
||||
`);
|
||||
const results = stmt.all() as any[];
|
||||
|
||||
return results.map((row) => ({
|
||||
...row,
|
||||
config: row.config ? JSON.parse(row.config) : null,
|
||||
tools: row.tools ? JSON.parse(row.tools) : [],
|
||||
validation_result: row.validation_result
|
||||
? JSON.parse(row.validation_result)
|
||||
: null,
|
||||
install_logs: row.install_logs ? JSON.parse(row.install_logs) : [],
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("Error getting all installed MCPs:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atualizar timestamp de um MCP instalado
|
||||
*/
|
||||
|
||||
@@ -73,6 +73,20 @@ CREATE TABLE IF NOT EXISTS mcp_cache_metadata (
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Tabela para rastrear MCPs ativos por conversa
|
||||
CREATE TABLE IF NOT EXISTS active_mcps (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
conversation_id TEXT NOT NULL,
|
||||
mcp_id TEXT NOT NULL,
|
||||
enabled INTEGER DEFAULT 1,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE,
|
||||
UNIQUE(conversation_id, mcp_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_active_mcps_conversation ON active_mcps(conversation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_active_mcps_mcp ON active_mcps(mcp_id);
|
||||
|
||||
-- Índices para performance
|
||||
CREATE INDEX idx_messages_conversation ON messages(conversation_id, timestamp);
|
||||
CREATE INDEX idx_conversations_updated ON conversations(updated_at DESC);
|
||||
|
||||
Reference in new issue
Block a user