Skip to content
Decision 1 · Specification + implementation

Share the machinery, let the outside assistant plan

The rule that calls from outside assistants go only through the tool registry, the bridge code that follows it, and the usage accounting the bridge has to restore.

Quoted verbatim from HYVIRAL’s specifications, code and logs. Internal paths and identifiers are redacted.

The decision

An outside assistant, connected over MCP, gets direct access to selected tools through the existing tool registry, the one place where every tool runs. The assistant decides how to combine them, without HYVIRAL’s internal manager and specialist agents planning the workflow a second time.

The app and MCP share per-tool credit rates and one account credit pool. The bridge still has to do the session, confirmation and accounting work the app runtime normally performs; that maintenance cost is the price of sharing execution with a different orchestrator.

From the source

MCP core specification. The execution boundary is a hard rule:

The MCP module calls ToolRegistryService.execute() and only ToolRegistryService.execute(). Never a tool handler directly. Never a REST controller. Never a service that debits on its own.

Who owns what:

ResponsibilityOwner
Workflow planningConnecting agent
TransportTypeScript MCP SDK with a NestJS adapter
MCP exposureCurated tool allowlist and capability scopes
Synchronous executionExisting registry, inside an established session
Usage accountingBridge work required when bypassing the app runtime

Bridge implementation. The synchronous path builds the session and calls registry execution:

    const session = await this.sessions.build(auth, {
      conversationId,
      confirmedSpend,
      mcpCallId: callId,
    });

    const started = Date.now();
    let result: ToolResult;
    try {
      result = await AgentPlatformSession.run(session, () =>
        this.registry.execute(name, parsed.data, {
          planTier: this.tierOf(auth),
          // The closed `ConversationChannel` union has no 'mcp' member. The
          // SESSION says 'mcp' (which is what `isUnattendedChannel` reads); the
          // context says 'web' — the same split voice uses.
          channel: 'web',
        }),
      );

Bridge implementation. The accounting the bridge has to restore:

    // ── charge (D3 — identical rates) ──────────────────────────────────────
    // MCP skips the runtime, which is what accumulates `tokenUsage` and calls
    // `chargeTurn` in-app. Without this every LLM-backed read tool is free
    // forever: `draft_content`, `web_search`, `analyze_thumbnail` and
    // `get_intel_digest` all burn real provider money at `creditCost: 0`.
    const usage = result.tokenUsage;

What this shows

The synchronous path carries an authenticated session into shared execution and owns the accounting obligation that reuse would otherwise drop. Long-running work takes the existing background-job path, outside this excerpt.

Back to the case All code & records