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
| Token | Use | How the smith is chosen |
|---|---|---|
Smith token (sub = "<tenant>:<smith>") | browser-safe; the default | the token is the smith |
| Tenant-admin token | server-side only | pass 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
- Raw wire format or the
openaiSDK → OpenAI-compatible API. - The same product over the Vercel AI SDK → Vercel AI SDK.
- Where every surface fits → Ecosystem & compatibility.