Ingram Cloud

Documentation

eve

eve

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

eve's model field takes a plain AI SDK LanguageModel, so the model seam stands on the OpenAI-compatible API directly — a smith looks like any model to eve while it runs the agent loop server-side: memory, tools, approvals, isolation.

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

eve and ai are peer dependencies — you already have them in an eve project.

The model

Author it in agent/agent.ts. The token names the smith (the end-user's instance); the agent is the one that smith runs.

// agent/agent.ts
import { defineAgent } from "eve";
import { ingramCloudModel } from "@ingram-tech/eve-ingram-cloud";

// A per-smith token already names one smith — browser-safe.
export default defineAgent({
  model: ingramCloudModel({ apiKey: process.env.IC_SMITH_TOKEN! }),
});

eve routes this as an external provider — it bypasses the AI Gateway and talks to Ingram Cloud directly.

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. modelId is the upstream inference LLM for the turn: omit it to use the smith's configured model, or name one to override it (ingramCloudModel({ apiKey, modelId: "gpt-5.5" })). Opt into server-side memory by passing a threadId.

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

ingramCloudModel({
  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 an eve connection. The filename is the connection name; Ingram runs the tools for you, with approval gating.

// agent/connections/ingram.ts
import { defineIngramMcpConnection } from "@ingram-tech/eve-ingram-cloud";

export default defineIngramMcpConnection({
  apiKey: process.env.IC_SMITH_TOKEN!,
  deploymentId: "dep_…",
  description: "Ingram-hosted tools for this smith.",
});

eve discovers the deployment's tools, brokers auth, and hands them to the model — the token never reaches the model. Gate them with eve's own per-connection approval (never() / once() / always() from eve/tools/approval), or narrow them with tools: { allow: [...] }.

This is distinct from the model seam: there a smith runs its own server-side tools as the model; here an eve agent calls Ingram-hosted tools directly over MCP.

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/eve-ingram-cloud/approvals:

import { getApprovalRequests, approvalToolResult } from "@ingram-tech/eve-ingram-cloud";

// `toolCalls` from the model result — composite ids are Ingram approvals.
const approvals = getApprovalRequests(toolCalls);
for (const a of approvals) {
  const decision = (await askTheHuman(a)) ? "approve" : "reject";
  // Append this tool message and run the next turn to resume the paused run:
  messages.push(approvalToolResult(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. These are the smith's server-side approvals — distinct from eve's per-connection approval gate above.

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