Ingram Cloud

Documentation

Flue

Flue

@ingram-tech/flue-ingram-cloud drives a smith from a Flue agent — the Flue-ecosystem sibling of the Vercel AI SDK adapter. It is three thin seams, each over a surface you already know: a smith becomes a model provider, Ingram-hosted tools attach over MCP, and approvals ride the standard tool-call channel. No bespoke protocol.

Under the hood the provider stands on the OpenAI-compatible API via Flue's built-in openai-completions wire protocol, so a smith looks like any model to Flue while it runs the agent loop server-side — memory, tools, approvals, isolation.

npm install @ingram-tech/flue-ingram-cloud

@flue/runtime is a peer dependency — you already have it in a Flue app. It must stay a peer: registerProvider() writes to a module-scoped registry, so the adapter and your app share one instance.

The provider

Call registerIngramCloud() once in src/app.ts, before routing, like any other Flue provider. The token names the smith (the end-user's instance); the agent is the one that smith runs.

import { registerIngramCloud } from "@ingram-tech/flue-ingram-cloud";
import { flue } from "@flue/runtime/routing";
import { Hono } from "hono";

// A per-smith token already names one smith — browser-safe.
export const ingram = registerIngramCloud({ apiKey: process.env.IC_SMITH_TOKEN! });

const app = new Hono();
app.route("/", flue());
export default app;

Then point an agent at it:

// agents/triage.ts
import { defineAgent } from "@flue/runtime";
import { ingram } from "../src/app.js";

export default defineAgent(() => ({
  model: ingram.model("gpt-5.5"),
  instructions: "Triage the incoming request.",
}));

The model id is the LLM, not the agent

The agent a smith runs — its instructions, tools, and memory — is resolved from the smith, never from the model id. The model id is the upstream inference LLM for the turn: ingram.model("gpt-5.5") runs the smith's agent on GPT-5.5.

Unlike the raw OpenAI-compatible surface, where an empty model means "the agent's configured model", Flue requires a non-empty model id in provider/model. So name the model you want; ingram.model("") throws to make that explicit.

Server-side with a tenant-admin token instead of a smith token? Name the smith:

const ingram = registerIngramCloud({
  apiKey: process.env.IC_TENANT_TOKEN!, // server-side only
  smithId: "smt_…",                      // sent as IC-Smith-Id
});

Tools over MCP

Expose an Ingram Cloud deployment of kind: "mcp" as Flue tools and spread them into your agent. Ingram runs the tools for you, with approval gating.

import { connectIngramMcp } from "@ingram-tech/flue-ingram-cloud";

const ingramTools = await connectIngramMcp({
  apiKey: process.env.IC_SMITH_TOKEN!,
  deploymentId: "dep_…",
});

export default defineAgent(() => ({
  model: ingram.model("gpt-5.5"),
  tools: [...ingramTools.tools], // named mcp__ingram__<tool>
  instructions,
}));

The tools arrive as ordinary Flue tools, so they compose with native tools, skills, and the sandbox. Call ingramTools.close() when they're no longer needed.

Approvals

A tool the agent marks destructiveHint pauses the run for a human decision. On this surface the pause is a tool call whose id is "<run_id>::<tool_call_id>" and the turn ends with finish_reason: "tool_calls". The helpers are pure and also live at @ingram-tech/flue-ingram-cloud/approvals:

import { getApprovalRequests, approvalWireMessage } from "@ingram-tech/flue-ingram-cloud";

// `toolCalls` from the model response — composite ids are Ingram approvals.
const approvals = getApprovalRequests(toolCalls);
for (const a of approvals) {
  const decision = (await askTheHuman(a)) ? "approve" : "reject";
  // Send this tool message back as the next turn to resume the paused run:
  sendNextTurn(approvalWireMessage(a, decision));
}

On approve, Ingram Cloud runs the tool itself and continues; on reject, the run completes with stop_reason: "approval_rejected" and nothing executes. This is the same approval a /submit or a deployment confirmation drives — one mechanism, several front doors.

Identity & tokens

TokenUseHow the smith is chosen
Smith token (sub = "<tenant>:<smith>")browser-safe; the defaultthe token is the smith
Tenant-admin tokenserver-side onlypass smithId (sent as IC-Smith-Id)

The agent is the one the smith runs — chosen by the smith, never by an argument.

When you need more