www.hubleto.eu Reddit User Guide Stack Overflow LinkedIn Report an issue

AI assistant and MCP tools

Hubleto has two ways to connect with an AI: the built-in AI assistant app and an MCP server that gives external assistants access to the ERP.

The AI assistant app

The AiAssistent app adds a chat to Hubleto. It calls the Gemini API with the key and the model that the administrator sets in the app's settings (apiKey, model; the default model is gemini-2.5-flash):

apps/AiAssistent/GeminiProvider.php (excerpt)
  public function __construct(AppInterface $app)
  {
    $this->app = $app;
    $this->apiKey = $app->configAsString('apiKey') ?: '';
    $this->model = $app->configAsString('model') ?: 'gemini-2.5-flash';
  }

The app adds itself to the app menu, registers its pages and a settings page:

apps/AiAssistent/Loader.php (excerpt)
  public function init(): void
  {
    parent::init();

    // Add app routes.
    $this->router()->get([
      '/^ai-assistant\/?$/' => Controllers\Home::class,
      '/^ai-assistant\/settings\/?$/' => Controllers\Settings::class,
    ]);

    $this->router()->get([
      '/^api\/ai-assistant\/chat\/?$/' => Controllers\Chat::class,
    ]);

    // DO NOT DELETE FOLLOWING LINE, OR `php hubleto` WILL NOT GENERATE CODE HERE
    //@hubleto-cli:routes

    // Add placeholder for custom settings.
    // This will be displayed in the Settings app, under the "All settings" card.
    $settingsApp = $this->appManager()->getApp(\Hubleto\App\Community\Settings\Loader::class);
    $settingsApp->addSetting($this, [
      'title' => $this->translate('AI Assistant'),
      'icon' => 'fas fa-robot',
      'url' => 'settings/ai-assistant',
    ]);

    // Add app menu item
    $appMenu = $this->getService(\Hubleto\App\Community\Desktop\AppMenuManager::class);
    $appMenu->addItem($this, 'ai-assistant', $this->translate('AIAssistant'), 'fas fa-robot');
  }

Telling the assistant about your data

A model decides what the assistant may read about a record by overriding getAiAssistantContext():

apps/Customers/Models/Customer.php (excerpt)
  public function getAiAssistantContext(int $sensitivityLevel, int $recordId): array
  {
    $customer = $this->record->prepareReadQuery()->where('customers.id', $recordId)->first();

    if (!$customer) return [];

    switch ($sensitivityLevel) {
      case 0:
      default:
        return ['name' => $customer->name];
      break;
      case 1:
        return [
          'name' => $customer->name,
          'vat_id' => $customer->vat_id,
          'company_id' => $customer->company_id,
          'tax_id' => $customer->tax_id,
        ];
      break;
      case 2:
        return [
          'name' => $customer->name,
          'vat_id' => $customer->vat_id,
          'company_id' => $customer->company_id,
          'tax_id' => $customer->tax_id,
          'street_line_1' => $customer->street_line_1,
          'street_line_2' => $customer->street_line_2,
          'region' => $customer->region,
          'city' => $customer->city,
          'postal_code' => $customer->postal_code,
          'note' => $customer->note,
        ];
      break;
    }
  }

Return only the data that is useful and that the user may see. The method receives the level of detail and the ID of the record.

MCP tools

The Model Context Protocol (MCP) lets an external assistant (Claude, ChatGPT, an IDE agent) call functions of your ERP. The project ships mcp-server.php, which starts the server from the loader:

erp-project: mcp-server.php
<?php

// bootstrap
require_once(__DIR__ . "/boot.php");

$mcpServer = new \Hubleto\McpServer\Builder($hubleto)->build();

$mcpServer->run();

An app provides its tools from getMcpTools() of its Loader. A tool is a class in McpTools/ that extends Hubleto\Erp\McpTool. Each method marked with the #[McpTool] attribute becomes a tool. The docblock is the description that the assistant reads, so write it carefully:

apps/Contacts/McpTools/ContactsTool.php
<?php

namespace Hubleto\App\Community\Contacts\McpTools;

use Hubleto\Erp\McpTool;

class ContactsTool extends McpTool {
  /**
   * Get information about contact by providing its email
   * address.
   * @param string $email The email of the contact to look for.
   */
  #[McpTool]
  public function getContactByEmail(string $email): string {
    return ".." . $email . "..";
  }
}
apps/Contacts/Loader.php (excerpt)
  public function getMcpTools(): array
  {
    return [
      [McpTools\ContactsTool::class, 'getContactByEmail']
    ];
  }

NOTE The method of ContactsTool is a placeholder that returns a stub string. Query the database with the models of the app. A tool called by an external program runs without a browser session, so authProvider()->getUserId() is 0. Check how prepareReadQuery() behaves in that case before you rely on the ownership filters, and do not expose personal data without a check of your own.

Guidelines