Server4Agent

Interfaces

SDKs

Typed clients for JavaScript/TypeScript and Python, for the code around your agent: your backend, a CI job, a webhook receiver, an admin script.

Not for your agent's tool-calling loop. If your agent decides on its own when to provision a server or run a command, point it at the MCP server directly, it already speaks tool-calling and doesn't need either SDK. These clients are for the surrounding system: the parts of your stack that call the API on a schedule, in response to a webhook, or from your own product logic.

Install

shell
npm install @server4agent/sdk

Quickstart

Both clients read your key from the SERVER4AGENT_API_KEY environment variable if you don't pass one explicitly. Every method maps 1:1 to a REST endpoint, the SDK just gives it a type.

quickstart.ts
import { Server4Agent } from "@server4agent/sdk";

// Reads SERVER4AGENT_API_KEY from the environment if you don't pass one.
const client = new Server4Agent();

const server = await client.servers.create({ task: "landing page builder" });

await client.tasks.create(
  server.id,
  "Build a waitlist landing page and deploy it.",
);

Handling errors

Any non-2xx response raises Server4AgentError with the HTTP status, a machine-readable code, and the same message you'd get back over plain REST.

errors.ts
import { Server4AgentError } from "@server4agent/sdk";

try {
  await client.servers.get("srv_does_not_exist");
} catch (err) {
  if (err instanceof Server4AgentError) {
    console.error(err.status, err.code, err.message); // 404 not_found "server not found"
  }
}

Verifying webhooks

Both clients export a verify_webhook_signature /verifyWebhookSignature helper that checks the Server4Agent-Signatureheader against your webhook's secret. Always verify against the raw request body, before you parse it as JSON, parsing and re-serializing changes the bytes and breaks the signature.

Real examples

Ephemeral preview environments in CI. Provision a real, disposable server per pull request, deploy the branch to it, and clean it up when the PR closes.

preview-env.ts
// Run from CI on every PR: give it a real, disposable URL, then tear it
// down when the PR closes. lifecycle: "ephemeral" marks it for cleanup.
import { Server4Agent } from "@server4agent/sdk";

const client = new Server4Agent();
const prNumber = process.env.PR_NUMBER!;

const server = await client.servers.create({ task: `pr-${prNumber}` });
const project = await client.projects.create(server.id, {
  name: `pr-${prNumber}`,
  visibility: "public",
  lifecycle: "ephemeral",
});

await client.tasks.create(
  server.id,
  "Install dependencies, build the app, and deploy it.",
);

console.log(`Preview: ${project.url}`);
// On PR close: await client.projects.cleanup(project.id);

A webhook receiver that reacts to deployments. Subscribe once (see Webhooks), then verify and act on each delivery.

webhook-receiver.ts
import express from "express";
import { verifyWebhookSignature } from "@server4agent/sdk";

const app = express();

app.post("/hooks/server4agent", express.text({ type: "*/*" }), async (req, res) => {
  // req.body must be the raw string — verify before you JSON.parse it.
  const ok = await verifyWebhookSignature(
    process.env.SERVER4AGENT_WEBHOOK_SECRET!,
    req.body,
    req.header("Server4Agent-Signature"),
  );
  if (!ok) return res.status(401).end();

  const event = JSON.parse(req.body);
  if (event.event === "deployment.live") {
    await notifySlack(`Deployed: ${event.url}`);
  }
  res.status(200).end();
});

Per-customer workspaces from your own product. If you're building a product where a customer action should provision a real environment, that's your backend calling the SDK directly, no agent tool-calling loop involved.

provision.py
# Your product's backend, not an agent: a customer submits a request,
# your own business logic decides what to build, and you provision a
# real workspace for them on demand.
from server4agent import Server4Agent

client = Server4Agent()


def provision_for_customer(customer_id: str, goal: str) -> str:
    server = client.servers.create(task=f"customer-{customer_id}")
    project = client.projects.create(
        server.id,
        name=f"{customer_id}-workspace",
        visibility="public",
        template="dashboard",
    )
    client.tasks.create(server.id, goal)
    return project.url

Scoping a key before handing it to another tool. Mint a key restricted to one server (or project) instead of sharing your account-wide key.

scoped-key.ts
// Hand a support tool a key that can only touch one customer's server,
// not your whole account.
const supportKey = await client.keys.create("support-tool");
await client.keys.updateScope(supportKey.id, {
  allowedServerIds: [server.id],
});

// supportKey.key is the plaintext — shown once, here. Hand it to the tool
// and store it on your side if you'll need to revoke it later by id.

Source

Both SDKs are open source in the sdk/js and sdk/python directories of the Server4Agent repository, alongside their test suites. Every method wraps one of the endpoints on the REST API page, if a method's behavior is ever unclear, that page has the underlying request/response shape.

Next: Webhooks