This section as Markdown: View Download

Node.js Library

Use Wiro AI models directly in your Node.js or TypeScript projects with a simple API client.

Overview

The @wiro-ai/wiro-mcp package exports a WiroClient class that you can use as a standalone API client, with no MCP setup required. It handles authentication (both signature-based and API key only), model discovery, execution, task polling, and file uploads.

Links

Installation

npm install @wiro-ai/wiro-mcp

Requires Node.js 20 or later.

Quick Start

import { WiroClient } from '@wiro-ai/wiro-mcp/client';

const client = new WiroClient('YOUR_API_KEY', 'YOUR_API_SECRET');

// Run an image generation model
const run = await client.runModel('google/nano-banana-pro', {
  prompt: 'A futuristic city at sunset',
  aspectRatio: '16:9',
  resolution: '2K'
});

if (!run.result) {
  console.log('Run failed:', run.errors);
  process.exit(1);
}

// Wait for the result (polls Task/Detail until complete)
const result = await client.waitForTask(run.socketaccesstoken);
const task = result.tasklist[0];

if (task.pexit === '0') {
  console.log('Output:', task.outputs[0].url);
} else {
  console.log('Failed:', task.pexit);
}

Authentication

The client supports both Wiro authentication methods:

Signature-Based (Recommended)

Provide both API key and secret. HMAC-SHA256 signatures are generated automatically per request.

const client = new WiroClient('your-api-key', 'your-api-secret');

API Key Only

Omit the secret for simpler server-side usage.

const client = new WiroClient('your-api-key');

Custom Base URL

Override the API endpoint if needed (third parameter):

const client = new WiroClient('key', 'secret', 'https://custom-api.example.com/v1');

Your Own Request Headers

To send headers of your own with every API request, pass requestHeaders in the options (fourth parameter):

const client = new WiroClient('your-api-key', 'your-api-secret', undefined, {
  requestHeaders: () => ({ 'x-app-name': 'my-app' })
});

The function runs for every request the client sends, JSON and multipart, and gets one object, { pathname, apiKey }, where pathname includes the base path, such as /v1/Tool/List. It returns a plain object of string values, and those headers are sent after the client's own. It cannot set x-api-key, x-signature, x-nonce or Content-Type, in any letter case: such a request fails before it is sent. Type it with RequestHeadersHook. This option replaces trustedProxy, which was removed in 1.5.0: a client created with trustedProxy throws.

Available Methods

Method Description
searchModels(params?) Search and browse models by keyword, category, or owner.
getModelSchema(model) Get full parameter schema and pricing for a model.
explore() Browse curated models organized by category.
runModel(model, params, signal?) Run a model. Returns task ID and socket access token.
waitForTask(tasktokenOrReference, timeoutMs?, options?) Poll until completion. Default timeout: 120 seconds. Supports AbortSignal, stepped polling, progress callbacks, and transient retries.
getTask({ tasktoken?, taskid? }, signal?) Get current task status and outputs.
cancelTask(tasktokenOrReference, signal?) Cancel a queued task. Resolves a token to the task ID required by the API.
killTask(tasktokenOrReference, signal?) Kill a running task using its socket access token or task ID.
listTasks(params?, signal?) List the tasks of the key's workspace, newest first.
uploadFile(url, fileName?) Upload a file from a URL for use as model input.
post(path, body?, signal?) Call any other Wiro API endpoint with the same credentials, for example post('/Workflow/List', { scope: 'my' }). Wiro reports each outcome in the response body, so check result on the returned body.
postForm(path, form, signal?) Same as post, for endpoints that take multipart/form-data.
isOwnApiKey(value) Says whether a value is this client's API key, without handing the key out.

If waitForTask exceeds its timeout, it throws TaskWaitTimeoutError and preserves lastDetail. Call it again with the same token or task ID; do not call runModel again for the same generation.

Examples

Search Models

const models = await client.searchModels({
  search: 'image generation',
  categories: ['text-to-image'],
  limit: 5
});

for (const model of models.tool) {
  console.log(`${model.cleanslugowner}/${model.cleanslugproject} — ${model.title}`);
}

Get Model Parameters

const detail = await client.getModelSchema('google/nano-banana-pro');
const model = detail.tool[0];

if (model.parameters) {
  console.log('Parameters:');
  for (const group of model.parameters) {
    for (const param of group.items) {
      console.log(`  ${param.id} (${param.type}) ${param.required ? '— required' : ''}`);
    }
  }
}

Run an LLM

const run = await client.runModel('openai/gpt-5-2', {
  prompt: 'Explain quantum computing in 3 sentences'
});

const result = await client.waitForTask(run.socketaccesstoken);
const task = result.tasklist[0];

if (task.pexit === '0') {
  // Merged text
  console.log(task.debugoutput);

  // Structured thinking/answer
  const output = task.outputs[0];
  if (output.contenttype === 'raw') {
    console.log('Thinking:', output.content.thinking);
    console.log('Answer:', output.content.answer);
  }
}

Upload a File and Use It

Tip: Most models take a public URL instead of an upload. For most image and video inputs you pass it in the field itself, such as inputImage: 'https://example.com/photo.jpg'; a fileinput takes it in its {id}Url field (such as inputImageUrl), so check the model's parameter types. Use uploadFile() when you need to reuse files across multiple runs.

// Upload an image
const upload = await client.uploadFile('https://example.com/photo.jpg');
const fileUrl = upload.list[0].url;

// Use it in a model run
const run = await client.runModel('wiro/virtual-try-on', {
  inputImageHuman: fileUrl,
  inputImageClothes: 'https://example.com/shirt.jpg'
});

const result = await client.waitForTask(run.socketaccesstoken);
console.log('Output:', result.tasklist[0].outputs[0].url);

Poll Manually

const run = await client.runModel('klingai/kling-v3', {
  prompt: 'A drone shot over mountains',
  duration: '5',
  aspectRatio: '16:9'
});

// Poll manually instead of using waitForTask
const interval = setInterval(async () => {
  const detail = await client.getTask({ tasktoken: run.socketaccesstoken });
  const task = detail.tasklist[0];
  console.log('Status:', task.status);

  if (task.status === 'task_postprocess_end') {
    clearInterval(interval);
    if (task.pexit === '0') {
      console.log('Video:', task.outputs[0].url);
    }
  }
}, 5000);

TypeScript

All types are exported from the package:

import { WiroClient } from '@wiro-ai/wiro-mcp/client';
import type {
  RunModelResult,
  TaskDetailResponse,
  Task,
  TaskOutput,
  ToolListResponse,
  ToolDetailResponse,
  ToolListItem,
  SearchModelsParams,
  TaskReference,
  WaitForTaskOptions,
  RequestHeadersHook,
} from '@wiro-ai/wiro-mcp/client';
import {
  TaskPollingError,
  TaskWaitTimeoutError,
} from '@wiro-ai/wiro-mcp/client';
Type Description
RunModelResult Response from runModel(): taskid, socketaccesstoken.
TaskDetailResponse Response from getTask() / waitForTask(): contains tasklist array.
Task Individual task object: status, pexit, outputs, debugoutput, etc.
TaskOutput Output entry: file (name, url) or LLM (contenttype: "raw", content).
ToolListResponse Response from searchModels(): contains tool array.
ToolDetailResponse Response from getModelSchema(): contains model with parameters.
SearchModelsParams Search parameters: search, categories, slugowner, limit, etc.
TaskReference Existing task identifier: tasktoken or taskid.
WaitForTaskOptions Polling options: signal, onPoll, pollIntervalMs, and retry limit.
RequestHeadersHook The requestHeaders option: gets { pathname, apiKey } and returns the headers to add.
TaskWaitTimeoutError Bounded wait expired; carries the last Task/Detail response.
TaskPollingError Repeated or permanent status lookup failure; also carries the last response.