How to Implement MCP Tool Integration for AI Automation Workflows in Fincept Terminal
Fincept Terminal enables AI agents to invoke internal and external tools through Model Context Protocol (MCP) nodes, wiring execution bridges to a singleton service that handles discovery, validation, and async dispatch without modifying the workflow engine core.
The Fincept-Corporation/FinceptTerminal repository provides a visual workflow engine where AI automation workflows consume MCP tools as if they were native OpenAI function calls. By implementing three architectural layers—node registration, bridge wiring, and service execution—you can expose new capabilities to AI agents while maintaining clean separation between the UI editor and runtime logic.
Register the MCP Tool Node
The workflow engine uses a NodeRegistry to declare available nodes. In fincept-qt/src/services/workflow/nodes/IntegrationNodes.cpp (lines 15‑27), the register_integration_nodes function adds a generic MCP Tool node that appears in the visual editor under the MCP category:
registry.register_type({
.type_id = "mcp.tool_call",
.display_name = "MCP Tool",
.category = "MCP",
.description = "Call any Fincept internal MCP tool by name",
.icon_text = "M",
.accent_color = "#6366f1",
.version = 2,
.inputs = {{"input_0", "Arguments", PortDirection::Input, ConnectionType::Main}},
.outputs = {{"output_main", "Result", PortDirection::Output, ConnectionType::Main}},
.parameters = { {"tool", "Tool", "mcp_tool_select", "", {}, "", true} },
.execute = nullptr, // actual executor wired later
});
The mcp_tool_select parameter renders as a dropdown in the UI, populated dynamically from the MCP service’s cached tool list. Setting .execute = nullptr defers the runtime implementation to the bridge layer, keeping node definitions declarative.
Wire the Execution Bridge
After node registration, wire_all_bridges invokes wire_mcp_bridges in fincept-qt/src/services/workflow/adapters/ServiceBridges.cpp (lines 44‑98). This function assigns the actual execution lambda to the node’s execute callback, enabling async tool invocation:
def->execute = [](const QJsonObject& params,
const QVector<QJsonValue>& inputs,
std::function<void(bool, QJsonValue, QString)> cb) {
QString tool = params.value("tool").toString().trimmed();
if (tool.isEmpty()) {
cb(false, {}, "MCP Tool: 'tool' parameter is required");
return;
}
// Merge optional input object (e.g. from a Tool Picker node)
QJsonObject input_obj = (!inputs.isEmpty() && inputs[0].isObject())
? inputs[0].toObject() : QJsonObject();
// Resolve arguments
QJsonObject args;
if (input_obj.contains("args") && input_obj["args"].isObject())
args = input_obj["args"].toObject();
else if (!input_obj.contains("tool"))
args = input_obj; // raw data input
// Run in background thread
QtConcurrent::run([tool, args, cb]() {
auto& svc = mcp::McpService::instance();
mcp::ToolResult result =
tool.contains("__")
? svc.execute_openai_function(tool, args) // external server
: svc.execute_tool(mcp::INTERNAL_SERVER_ID, tool, args); // internal
if (!result.success) {
cb(false, {}, result.error.isEmpty() ? "MCP tool failed" : result.error);
return;
}
QJsonValue out = result.data.isNull() ? QJsonValue(result.to_json()) : result.data;
cb(true, out, {});
});
};
The bridge implements three critical behaviors:
- Parameter validation – Rejects execution if the
toolstring is empty. - Argument routing – Accepts either a structured Tool Picker output (
{tool, args}) or a plain JSON object. - Server routing – Tools containing "
__" are treated asserverId__toolNameand dispatched viaexecute_openai_functionto external MCP servers; otherwise,execute_toolroutes to internal tools usingINTERNAL_SERVER_ID.
Execution runs inside QtConcurrent::run to prevent UI blocking during network or computation-heavy operations.
Implement the MCP Service Layer
The McpService singleton declared in fincept-qt/src/mcp/McpService.h (lines 15‑36) manages the lifecycle of all MCP tools:
std::vector<UnifiedTool> get_all_tools(); // cached discovery (5 s TTL)
QJsonArray format_tools_for_openai(); // OpenAI‑compatible schema
ToolResult execute_tool(const QString&, const QString&, const QJsonObject&);
ToolResult execute_openai_function(const QString&, const QJsonObject&);
Result<void> validate_params(const QString&, const QJsonObject&);
Tool discovery maintains a 5‑second TTL cache (CACHE_TTL_MS = 5000) to minimize repeated schema generation overhead. The format_tools_for_openai() method converts internal tool definitions into the JSON schema required by OpenAI function-calling APIs. When the bridge invokes execute_tool, the service validates parameters against the tool’s schema, resolves the target server, and forwards the JSON arguments to the appropriate implementation.
Add Custom MCP Tools
Extending Fincept Terminal with new capabilities follows a four-step pattern that requires no changes to the workflow engine core:
-
Implement the tool in
fincept-qt/src/mcp/tools/(e.g.,RiskAnalysisTool.cpp). Inherit from the base tool interface and implementToolResult execute(const QJsonObject& args). -
Register the tool in
McpService::refresh_cache(). The MCP provider registry automatically discovers tools added to theMcpProvidermap, or you can manually insert them into the internal server’s tool list. -
Expose to the UI automatically. The
mcp_tool_selectdropdown queriesMcpService::format_tools_for_openai(), so new tools appear immediately in the workflow editor without modifyingIntegrationNodes.cpp. -
Configure external servers optionally. For tools hosted outside the terminal process, add server endpoints via the MCP Servers UI managed in
fincept-qt/src/screens/settings/McpServersSection.cpp. External tools follow theserverId__toolNamenaming convention in the bridge routing logic.
Practical Workflow Configuration
Below is a minimal workflow graph demonstrating how an MCP Tool node chains into subsequent automation steps:
{
"nodes": [
{
"id": "1",
"type": "mcp.tool_call",
"params": { "tool": "get_quote" },
"inputs": [{ "port": "input_0", "data": { "symbol": "AAPL" } }]
},
{
"id": "2",
"type": "utility.api_call",
"params": {
"url": "https://example.com/notify",
"method": "POST",
"body": { "message": "{{node_1.output_main}}" }
}
}
],
"connections": [
{ "from": "1", "out": "output_main", "to": "2", "in": "input_0" }
]
}
Execution proceeds as follows:
- Node 1 invokes the internal
get_quotetool viaMcpService::execute_tool, returning a JSON payload such as{"price": 174.23}. - The result flows through
output_maininto Node 2, which posts the data to an external webhook via theutility.api_callnode.
All tool discovery, argument validation, and routing logic remains encapsulated within the MCP service layer.
Key Source Files Reference
| File | Role | Location |
|---|---|---|
IntegrationNodes.cpp |
Registers the generic MCP Tool node definition | fincept-qt/src/services/workflow/nodes/ |
ServiceBridges.cpp |
Implements the async execution bridge and routing logic | fincept-qt/src/services/workflow/adapters/ |
McpService.h |
Defines the singleton service for discovery, validation, and dispatch | fincept-qt/src/mcp/ |
McpServersSection.cpp |
UI for managing external MCP server connections | fincept-qt/src/screens/settings/ |
McpServersScreen.cpp |
Full-screen server management interface | fincept-qt/src/screens/mcp_servers/ |
These files constitute the complete pipeline from visual node configuration to runtime execution of MCP-enabled AI automation workflows.
Summary
- Node registration in
IntegrationNodes.cppdeclares themcp.tool_calltype with a dynamicmcp_tool_selectparameter. - Bridge wiring in
ServiceBridges.cppimplements async execution, routing tools containing "__" to external servers and others to the internal MCP service. - Service layer caching uses a 5‑second TTL via
McpService::get_all_tools()to optimize OpenAI schema generation. - Tool addition requires only implementing the interface in
fincept-qt/src/mcp/tools/and registering in the provider cache; UI updates are automatic. - External servers integrate through the settings UI and follow the
serverId__toolNamenaming convention in workflow parameters.
Frequently Asked Questions
How does Fincept Terminal distinguish between internal and external MCP tools?
The execution bridge in ServiceBridges.cpp checks for the presence of "__" in the tool parameter string. Tools without this separator route to McpService::execute_tool using INTERNAL_SERVER_ID, while compound names like alphavantage__get_news trigger execute_openai_function, which dispatches to the external server registry. This naming convention eliminates the need for separate node types.
What caching strategy does the MCP service use for tool discovery?
McpService maintains an in-memory cache of the complete tool list with a 5‑second TTL (CACHE_TTL_MS = 5000). When the cache expires, get_all_tools() refreshes the registry by querying both internal tool providers and configured external MCP servers, then regenerates the OpenAI-compatible schema via format_tools_for_openai().
How do I expose a new internal tool to the workflow editor without modifying UI code?
Create your tool implementation in fincept-qt/src/mcp/tools/ and ensure it registers within McpService::refresh_cache(). The mcp_tool_select dropdown parameter automatically populates from McpService::instance().format_tools_for_openai(), so your tool appears immediately in the node editor without touching IntegrationNodes.cpp or QML files.
Can MCP tool execution block the UI thread?
No. The bridge explicitly wraps tool calls inside QtConcurrent::run, executing them in a background thread pool. The callback mechanism (std::function<void(bool, QJsonValue, QString)>) returns results asynchronously to the workflow engine, ensuring the visual editor remains responsive during long-running operations such as external API calls or complex calculations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →