The harness that ships your customer's idea.
Your customer describes what they want. The build-time agent interprets the intent, writes the extension against your tools, validates it, and ships it — sandboxed, scoped to one customer, only able to touch what you've declared.
Two phases. One load-bearing distinction.
Your customer asks for a feature. Your trigger forwards their intent to Forge with subject context. The build-time agent writes the extension against the tools you've exposed, validates it against your registry, and ships it.
The shipped extension lives in a sandboxed namespace, scoped to one customer. Triggers fire — your events, your crons — and the extension calls your APIs over plain HTTP using namespace-scoped secrets.
The agent never runs at runtime. The extension never invokes capabilities at runtime. That separation is how the blast radius of any one extension stays bounded by what you declared. See how isolation works →
Codegen, validate, retry, ship.
The build-time agent runs the loop most platforms can't build for themselves: a system prompt aware of the runtime contract, type-checking against your tools, trigger / event-type validation against your catalog, synthetic dry-run with retry-on-error, and an eval harness that catches regressions as models drift.
Your trigger surface posts the customer's request, plus subject context.
The agent writes extension code against the tools your sources expose.
Type-check, tool-existence check, trigger / event-type check, dry-run.
Working extension lands in your product, ready for your customer to review and run.
Four integration points. That's the SDK.
The harness is what Forge runs. Below is what your platform calls to wire it in.
Scope every extension to one of your customers
Upsert a subject when one of your customers logs in. A subject is whatever isolation unit your platform wants — a tenant, an end-user, a project. Forge treats it as an opaque scoping ID. Mint a session token scoped to that subject; the build-time agent uses it to call Forge's MCP and can only build or manage extensions for that subject.
const forge = new Forge({
workspaceId: "ws_acme",
apiKey: process.env.FORGE_API_KEY,
});
await forge.subjects.upsert({
externalId: "tenant_12345",
displayName: "Globex Co.",
metadata: { plan: "pro" },
});
const sessionToken = await forge.tokens.createSubjectScoped({
subjectExternalId: "tenant_12345",
permissions: ["extensions:create", "extensions:list", "extensions:pause"],
ttlSeconds: 3600,
}); Your tools, auto-discovered. Bounds enforced at build time.
Point Forge at your existing MCP server, OpenAPI, or GraphQL endpoint. Forge introspects it and exposes only those operations to the build-time agent — and the agent can never reference a tool that isn't in your registry. When the agent invokes one, Forge signs the dispatch with HMAC and includes the subject in a signed header, so your server knows whose context the call is in. Forge holds no per-subject host credentials. Runtime extension code talks to your APIs over plain HTTP using namespace-scoped secrets, so the blast radius of any one extension is bounded by what those secrets allow.
// Primary path: point Forge at your MCP server.
// Forge introspects it and surfaces every tool to the
// build-time agent.
await forge.sources.register({
kind: "MCP",
name: "Acme platform tools",
url: "https://api.acme.com/mcp",
});
// Fallback: declare a single capability manually for
// hosts that can't run an MCP server.
await forge.capabilities.register({
name: "send_slack_message",
description: "Post a message to an approved Slack channel",
inputSchema: { /* ... */ },
handlerUrl: "https://api.acme.com/forge/cap/send_slack",
}); Your customer sees the extension before it runs
Generated extensions don't go live on their own. Your platform fetches the pending list and renders a review screen — branded, placed where it makes sense in your product (modal, inbox item, settings page). Your customer approves, edits, or rejects. Forge ships no opinionated review UI; the screen lives in your product, not ours.
const pending = await forge.extensions.listPendingApproval({
subjectExternalId: "tenant_12345",
});
// pending[0]:
// {
// id, name, description,
// triggers: [{ kind: "event", eventType: "support_ticket.created" }],
// capabilities: ["get_customer", "send_slack_message"],
// secretsRequired: ["SLACK_WEBHOOK_URL"],
// }
await forge.extensions.approve(pending[0].id);
// or .edit(...) / .reject(...) Fire events; Forge dispatches to subscribed extensions
Some triggers are crons — Forge runs them. Others are host events. You declare an event-type catalog up front; extensions can only subscribe to types in the catalog. When something happens in your product, you fire an event at Forge and we fan out to extensions subscribed to that event type for that subject.
await forge.events.deliver({
subjectExternalId: "tenant_12345",
eventType: "support_ticket.created",
payload: { ticketId: "abc123", customerId: "cust_42" },
}); - — A chat product. You bring the trigger surface; Forge accepts intent over an API and returns drafts.
- — A workflow builder. Extensions are written by the build-time agent, not by users dragging boxes.
- — A model vendor. BYO key or pay through us; we don't run models from scratch.
- — An embedded UI kit. You render the dashboard, approval flow, and run logs against the headless API.
- — A general agent platform. Forge is opinionated for one job: scoped, durable, sandbox-safe extensions for your customers.