# How to Implement MCP Tool Integration for AI Automation Workflows in Fincept Terminal

> Learn how to implement MCP tool integration for AI automation workflows in Fincept Terminal. Seamlessly connect AI agents to internal and external tools for enhanced automation.

- Repository: [Fincept Corporation/FinceptTerminal](https://github.com/Fincept-Corporation/FinceptTerminal)
- Tags: how-to-guide
- Published: 2026-04-20

---

**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`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/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:

```cpp
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`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/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:

```cpp
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 `tool` string is empty.
- **Argument routing** – Accepts either a structured **Tool Picker** output (`{tool, args}`) or a plain JSON object.
- **Server routing** – Tools containing "`__`" are treated as `serverId__toolName` and dispatched via `execute_openai_function` to external MCP servers; otherwise, `execute_tool` routes to internal tools using `INTERNAL_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`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/mcp/McpService.h) (lines 15‑36) manages the lifecycle of all MCP tools:

```cpp
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:

1. **Implement the tool** in `fincept-qt/src/mcp/tools/` (e.g., [`RiskAnalysisTool.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/RiskAnalysisTool.cpp)). Inherit from the base tool interface and implement `ToolResult execute(const QJsonObject& args)`.

2. **Register the tool** in `McpService::refresh_cache()`. The MCP provider registry automatically discovers tools added to the `McpProvider` map, or you can manually insert them into the internal server’s tool list.

3. **Expose to the UI** automatically. The `mcp_tool_select` dropdown queries `McpService::format_tools_for_openai()`, so new tools appear immediately in the workflow editor without modifying [`IntegrationNodes.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/IntegrationNodes.cpp).

4. **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`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/fincept-qt/src/screens/settings/McpServersSection.cpp). External tools follow the `serverId__toolName` naming 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:

```json
{
  "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:

1. Node **1** invokes the internal `get_quote` tool via `McpService::execute_tool`, returning a JSON payload such as `{"price": 174.23}`.
2. The result flows through `output_main` into Node **2**, which posts the data to an external webhook via the `utility.api_call` node.

All tool discovery, argument validation, and routing logic remains encapsulated within the MCP service layer.

## Key Source Files Reference

| File | Role | Location |
|------|------|----------|
| [`IntegrationNodes.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/IntegrationNodes.cpp) | Registers the generic **MCP Tool** node definition | `fincept-qt/src/services/workflow/nodes/` |
| [`ServiceBridges.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/ServiceBridges.cpp) | Implements the async execution bridge and routing logic | `fincept-qt/src/services/workflow/adapters/` |
| [`McpService.h`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/McpService.h) | Defines the singleton service for discovery, validation, and dispatch | `fincept-qt/src/mcp/` |
| [`McpServersSection.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/McpServersSection.cpp) | UI for managing external MCP server connections | `fincept-qt/src/screens/settings/` |
| [`McpServersScreen.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/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.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/IntegrationNodes.cpp) declares the `mcp.tool_call` type with a dynamic `mcp_tool_select` parameter.
- **Bridge wiring** in [`ServiceBridges.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/ServiceBridges.cpp) implements 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__toolName` naming convention in workflow parameters.

## Frequently Asked Questions

### How does Fincept Terminal distinguish between internal and external MCP tools?

The execution bridge in [`ServiceBridges.cpp`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/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`](https://github.com/Fincept-Corporation/FinceptTerminal/blob/main/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.