iachat/enterprise/app/services/captain/mcp/tools/base_tool.rb
Rodribm10 23911ea878 feat(captain): MCP server (HTTP) expondo tools pro Hermes Agent
Implementa servidor MCP (Model Context Protocol) HTTP no Captain pra
o Hermes Agent invocar tools do Captain via `hermes mcp add`. Substrato
pra integração de Nível 2 onde o agente consulta tools quando precisa
executar ações reais (buscar FAQ, adicionar label, futuramente Pix etc).

Arquivos:

- app/controllers/webhooks/captain/mcp_controller.rb
  Endpoint POST /webhooks/captain/mcp. Valida HMAC (CAPTAIN_MCP_SECRET),
  parseia JSON-RPC, despacha pro Server. Extrai params._captain_context
  com multi-tenant ids (conversation_id, inbox_id, account_id, etc).

- enterprise/app/services/captain/mcp/server.rb
  Subset MCP suficiente: initialize, tools/list, tools/call, ping,
  notifications/initialized. JSON-RPC síncrono (sem SSE).

- enterprise/app/services/captain/mcp/tool_registry.rb
  Lista centralizada de tools.

- enterprise/app/services/captain/mcp/tools/base_tool.rb
  Interface base pras tools (.name, .description, .input_schema, #call).

- enterprise/app/services/captain/mcp/tools/add_label_tool.rb
  Tool 1: aplica label na conversation atual.

- enterprise/app/services/captain/mcp/tools/faq_lookup_tool.rb
  Tool 2: busca semântica em FAQs (Captain::AssistantResponse via pgvector
  cosine). Reaproveita SearchReplyDocumentationService pra paridade com
  o caminho legado do Captain.

- config/routes.rb
  Rota POST /webhooks/captain/mcp.

Conexão pelo Hermes:
  hermes mcp add captain-tools --url http://CAPTAIN_HOST/webhooks/captain/mcp

Auth: HMAC X-Hub-Signature-256 quando CAPTAIN_MCP_SECRET setado.

TODO próxima sprint: generate_pix_tool, send_suite_images_tool. Handoff
fica via automation hoje (UI Chatwoot).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:32:38 -03:00

57 lines
1.4 KiB
Ruby

# Interface base pras tools MCP do Captain.
#
# Cada tool concreta herda desta classe e implementa:
# - .name — identificador (snake_case)
# - .description — texto pro LLM decidir quando chamar
# - .input_schema — JSON Schema (Draft 2020-12) dos argumentos
# - #call(args, context:) — execução real
#
# context é um hash com metadata da invocação (ex: conversation_id,
# inbox_id, account_id) extraído do request MCP. Tools usam isso pra
# resolver entidades do Captain (Conversation, Inbox, etc).
class Captain::Mcp::Tools::BaseTool
class ExecutionError < StandardError; end
class << self
def name
raise NotImplementedError, "#{self} must implement .name"
end
def description
raise NotImplementedError, "#{self} must implement .description"
end
def input_schema
raise NotImplementedError, "#{self} must implement .input_schema"
end
def to_mcp_descriptor
{
name: name,
description: description,
inputSchema: input_schema
}
end
end
def call(_args, context:)
raise NotImplementedError, "#{self.class} must implement #call"
end
protected
def text_response(text)
{
content: [{ type: 'text', text: text.to_s }],
isError: false
}
end
def error_response(message)
{
content: [{ type: 'text', text: message.to_s }],
isError: true
}
end
end