# Share the machinery, let the outside assistant plan

*Letting any AI assistant use HYVIRAL · Specification + implementation. From the portfolio of Guilherme Rohden. Excerpts are quoted verbatim; internal paths and identifiers are redacted.*

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.

## 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:**

| Responsibility | Owner |
|---|---|
| Workflow planning | Connecting agent |
| Transport | TypeScript MCP SDK with a NestJS adapter |
| MCP exposure | Curated tool allowlist and capability scopes |
| Synchronous execution | Existing registry, inside an established session |
| Usage accounting | Bridge work required when bypassing the app runtime |

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

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

```typescript
    // ── 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.
