Flue
@ingram-cloud/flue 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-cloud/flue
@flue/runtime (v2) is a peer dependency — you already have it in a Flue app. It
must stay a peer: setProvider() 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. Flue 2 resolves model ids against the
provider's declared list, so models is required — name every model you plan to
use ({} per model is enough).
import { registerIngramCloud } from "@ingram-cloud/flue";
import { createAgentRouter } from "@flue/runtime/routing";
import { Hono } from "hono";
import { Triage } from "./agents/triage.js";
// A per-smith token already names one smith — browser-safe.
export const ingram = registerIngramCloud({
apiKey: process.env.IC_SMITH_TOKEN!,
models: { "gpt-5.6-sol": {} },
});
const app = new Hono();
app.route("/agents/triage", createAgentRouter(Triage));
export default app;
Then point an agent at it:
// agents/triage.ts
"use agent";
import { useModel } from "@flue/runtime";
import { ingram } from "../app.js";
export function Triage() {
useModel(ingram.model("gpt-5.6-sol"));
return "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.6-sol") runs the smith's agent on
GPT-5.6 Sol.
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
models: { "gpt-5.6-sol": {} },
});
Tools over MCP
Expose an Ingram Cloud deployment of kind: "mcp" as Flue
tools. defineIngramMcp() builds the connection definition; mount it in an agent
render with useMcpConnection(). Ingram runs the tools for you, with
approval gating.
import { defineIngramMcp } from "@ingram-cloud/flue";
import { useMcpConnection, useModel } from "@flue/runtime";
import { ingram } from "../app.js";
const ingramMcp = defineIngramMcp({
apiKey: process.env.IC_SMITH_TOKEN!,
deploymentId: "dep_…",
});
export function Assistant() {
useModel(ingram.model("gpt-5.6-sol"));
useMcpConnection(ingramMcp); // tools named mcp__ingram__<tool>
return instructions;
}
The tools arrive as ordinary Flue tools, so they compose with native tools,
skills, and the sandbox. Outside an agent render (trusted application code, Node
target only), connectIngramMcp(settings) is the imperative form — it connects
now and returns { tools, close }.
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-cloud/flue/approvals:
import { getApprovalRequests, approvalWireMessage } from "@ingram-cloud/flue";
// `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.