@engini/sdk
v0.7.0
Published
Engini SDK — agent-first ergonomic layer over the Engini Public API
Readme
@engini/sdk
Agent-first TypeScript SDK for the Engini Public API — discover tools, execute them against your connected apps, and wrap them as LLM tool definitions.
npm install @engini/sdkQuickstart
import { Engini } from "@engini/sdk";
const client = new Engini({ apiKey: "eng_…" }); // or set ENGINI_API_KEY
// Discover canonical tool schemas
const tools = await client.tools.get({ applications: ["salesforce"], search: "accounts", limit: 5 });
// Execute a tool against a connection
const [{ connectionId }] = await client.connections.list("salesforce");
const result = await client.tools.execute(
"salesforce_getrecords",
{ sobject: "Account" },
{ connectionId },
);
console.log(result.output);JWT auth is the fallback: new Engini({ token: "<jwt>", companyToken: "<id>" }), or set
ENGINI_API_TOKEN / ENGINI_COMPANY_TOKEN. With an API key the company is bound to the key,
so no company token is needed. Point at another host with new Engini({ …, baseUrl }).
Use with an LLM
Provider adapters wrap canonical schemas into vendor tool definitions client-side, with no
vendor SDK dependency. OpenAI is the default; Anthropic is also available.
// Bind applications → connections once, then drive a tool-calling loop
const toolset = client.toolset({
tools: ["salesforce_getrecords"],
connections: { salesforce: "Prod" },
});
const openaiTools = client.provider.wrapTools(await toolset.tools()); // plain OpenAI tool-JSON
// … send openaiTools to the model, get a response …
const results = await toolset.handleToolCalls(llmResponse); // runs the calls, returns resultsclient.toolset(...) builds a local toolset (no I/O until used) or loads a server one via
client.toolset({ toolsetId }).
Files
Tools whose inputSchema marks a field "format": "engini/file" accept files. Wrap a file with
File and pass it as the field value — the SDK base64-encodes it into the
{ base64_content, mime_type, filename } wire shape. A field takes a single file or a list, per
the tool's schema.
import { Engini, File } from "@engini/sdk";
await client.tools.execute(
"gmail_send_mail",
{
to: "[email protected]",
subject: "Reports",
body: "See attached.",
attachmentsarray: [File.fromPath("q3-report.pdf"), File.fromPath("chart.png")],
},
{ connectionId },
);File.fromPath infers the filename + mime type; File.fromBytes(data, { filename, mimeType })
and File.fromBase64(b64, { filename, mimeType }) cover in-memory content.
In the LLM loop an agent can't produce base64, so file fields are presented to it as string fields. Register the files you'll allow and let the model reference one by key:
const results = await toolset.handleToolCalls(llmResponse, {
files: { "q3-report.pdf": File.fromPath("q3-report.pdf") },
});What this adds over the raw REST client
Built on the autogenerated @engini/client, the
SDK adds what the generated client deliberately lacks: typed errors (the EnginiError family),
retry/backoff, auto-pagination, pluggable auth (ApiKeyAuth / BearerAuth), Provider
adapters for OpenAI/Anthropic, and the ergonomic Toolset object.
There's also a machine-first CLI, @engini/cli,
built on this SDK.
Source & docs: https://github.com/engini/engini-sdk
