ActionForge Developer Docs
Define tools with JSON Schema. Get a URL. POST input. Receive validated output. Built for AI agents and external developers who need strict contracts.
HTTP + JSON Schema‑first Agent‑ready Hosted tools

Quick glance

POST /api/generate-tool
Authorization: Bearer <api-key>

{
  "toolName": "LoanPaymentCalculator",
  "description": "Computes monthly loan payments.",
  "inputSchema": { ... },
  "outputSchema": { ... }
}

201 Created
{
  "toolId": "UUID",
  "toolName": "LoanPaymentCalculator",
  "proxyUrl": ".../api/invoke/<toolId>",
  "createdAt": "2026-07-29T11:47:00Z",
  "workerId": "opaque-id",
  "ready": true
}

The Pitch

ActionForge lets AI agents create their own tools at run time. An agent describes the function it needs and supplies JSON Schemas for input and output; ActionForge generates the implementation, deploys it as its own Cloudflare Worker, and returns a validated HTTP endpoint — usually in seconds. Every tool is a pure function of its input, so an agent gets deterministic, schema‑checked computation instead of guessing at arithmetic in context.

Self‑tooling agents Agents write, deploy, and invoke their own micro‑tools at run time — no redeploy, no human in the loop.
Contained by construction Generated code is scanned before deploy and refused if it reaches the network or evals. A tool computes — it cannot call out.
MCP‑native Tools surface through the Model Context Protocol, so any MCP‑aware agent can discover and call them.
Schema in, schema out Inputs are validated before a tool runs and outputs before they return, so a malformed call fails loudly instead of returning something subtly wrong.

Start here

The fastest way in is the MCP server: your tools appear directly inside Claude Desktop, Claude Code, or any MCP client. If you would rather call the HTTP API yourself, skip to the Quickstart.

1. Get an API key

Access is by request while we are in early release. Email pritasharma25@gmail.com for an invite code, then:

curl -X POST https://actionforge-production.up.railway.app/api/signup \
  -H 'content-type: application/json' \
  -d '{"email":"you@example.com","inviteCode":"YOUR_CODE"}'

The response contains your key. It is shown once and stored only as a SHA‑256 digest, so keep it somewhere safe — we can issue a new one, but we cannot recover the old one.

2. Connect it to Claude

Add this to claude_desktop_config.json and restart Claude Desktop:

{
  "mcpServers": {
    "actionforge": {
      "command": "npx",
      "args": ["-y", "actionforge-mcp"],
      "env": {
        "ACTIONFORGE_URL": "https://actionforge-production.up.railway.app",
        "ACTIONFORGE_API_KEY": "af_your_key_here"
      }
    }
  }
}

On macOS that file lives at ~/Library/Application Support/Claude/claude_desktop_config.json; on Windows, %APPDATA%\Claude\claude_desktop_config.json. For Claude Code instead:

claude mcp add actionforge \
  --env ACTIONFORGE_URL=https://actionforge-production.up.railway.app \
  --env ACTIONFORGE_API_KEY=af_your_key_here \
  -- npx -y actionforge-mcp

Every tool on your account now shows up as a tool Claude can call. Nothing to install beyond Node 20.12+ — the server has no runtime dependencies.

Overview

ActionForge is a hosted service for defining and running custom tools. You describe a tool’s inputs and outputs as JSON Schema, register it, and receive a URL. POSTing a payload to that URL runs the tool and returns a result that matches the declared output schema.

When to use ActionForge

Not a workflow engine Not a job queue Tool definition + invocation only

Authentication

Every request to ActionForge must include a bearer token:

Authorization: Bearer <api-key>
Never log your API key Use env vars (e.g., process.env.ACTIONFORGE_KEY)

Endpoint Reference

POST /api/generate-tool

Registers a new tool.

FieldTypeConstraints
toolNamestring1–128 chars, ^[A-Za-z0-9_\- ]+$, unique across the deployment — a collision returns 409
descriptionstring1–4000 chars
inputSchemaobjectValid JSON Schema
outputSchemaobjectValid JSON Schema

201 Created

{
  "toolId": "UUID",
  "toolName": "LoanPaymentCalculator",
  "proxyUrl": "https://actionforge-production.up.railway.app/api/invoke/c2b1c2d0-9e4f-4a8b-9f6b-4c9a2e7f1234",
  "createdAt": "2026-07-29T11:47:00Z",
  "workerId": "opaque-string-not-to-be-parsed",
  "ready": false
}

Note: workerId is opaque. Do not parse or rely on its format.

ready is load-bearing. false means the tool deployed successfully but its edge route is still propagating — usually seconds, sometimes a few minutes. Invoking it before the route resolves returns 503 with Retry-After, which is not a fault in your tool. Either wait for ready or retry on 503; see the Agent Guide.

Errors

StatusMeaning
400Malformed JSON, missing/invalid fields, or invalid JSON Schema
401Missing or invalid API key
409toolName already in use — names are unique; choose another. Retrying will not help.
429Rate limited
503A dependency is unavailable. Transient — retry after Retry-After.
500Tool could not be created

POST /api/invoke/[toolId]

Invokes a previously registered tool.

ParameterTypeConstraints
toolId (path)stringMust be a valid UUID

The request body is validated against the tool’s registered inputSchema.

200 OK

{
  "toolId": "c2b1c2d0-9e4f-4a8b-9f6b-4c9a2e7f1234",
  "result": {
    "monthlyPayment": 1342.05
  },
  "executionMs": 87
}

executionMs is wall-clock time for the whole invocation, including the network round trip to the tool — not tool CPU time alone.

If a tool’s outputSchema declares something other than an object (an array, or a scalar), the value is wrapped: "result": { "value": … }.

Errors

StatusMeaning
400Non‑UUID id, malformed JSON, or payload failing inputSchema
401Missing or invalid API key
404No such tool
429Rate limited
502Tool failed to execute, or its output did not match outputSchema
503Retryable. The tool’s route has not propagated yet (see ready), or a dependency is unavailable. Carries Retry-After.

502 vs 503. 502 means the tool ran and misbehaved — a real fault worth surfacing. 503 means it has not run yet and you should come back. Treating them alike is the most common integration mistake.

Quickstart

We’ll build a Loan Payment Calculator and call it. You need an API key first — see Start here — then export it:

export ACTIONFORGE_KEY=af_your_key_here

1. Register the tool

The two schemas are the contract: inputs are validated against the first before the tool runs, and its result against the second before it returns.

curl -X POST https://actionforge-production.up.railway.app/api/generate-tool \
  -H "Authorization: Bearer $ACTIONFORGE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "toolName": "LoanPaymentCalculator",
    "description": "Computes monthly loan payments using standard amortization.",
    "inputSchema": {
      "type": "object",
      "required": ["principal", "annualRate", "years"],
      "properties": {
        "principal": { "type": "number", "minimum": 0 },
        "annualRate": { "type": "number", "minimum": 0 },
        "years": { "type": "number", "minimum": 1 }
      }
    },
    "outputSchema": {
      "type": "object",
      "required": ["monthlyPayment"],
      "properties": {
        "monthlyPayment": { "type": "number" }
      }
    }
  }'

2. Invoke the tool (curl)

curl -X POST https://actionforge-production.up.railway.app/api/invoke/c2b1c2d0-9e4f-4a8b-9f6b-4c9a2e7f1234 \
  -H "Authorization: Bearer $ACTIONFORGE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "principal": 250000,
    "annualRate": 5.0,
    "years": 30
  }'

3. Invoke from TypeScript

const res = await fetch(
  "https://actionforge-production.up.railway.app/api/invoke/c2b1c2d0-9e4f-4a8b-9f6b-4c9a2e7f1234",
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.ACTIONFORGE_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      principal: 250000,
      annualRate: 5.0,
      years: 30
    })
  }
);

if (!res.ok) {
  throw new Error(`Invocation failed: ${res.status}`);
}

const data = await res.json();
console.log("Monthly payment:", data.result.monthlyPayment);

Agent Integration Guide

ActionForge is designed for AI agents that need strict, typed tools. Agents can treat each tool as a deterministic capability with JSON‑Schema‑defined inputs and outputs.

Agent phases

  1. Tool registration — done by developers via /api/generate-tool.
  2. Tool discovery — agents receive toolId, toolName, proxyUrl, and schemas.
  3. Tool invocation — agents POST payloads to proxyUrl and consume validated results.

Capability registry

interface ActionForgeTool {
  toolId: string;
  toolName: string;
  proxyUrl: string;
  inputSchema: any;
  outputSchema: any;
}

Agent invocation wrapper

Two status codes are retryable and both carry Retry-After: 429 (out of quota) and 503 (route still propagating, or a dependency is down). Everything else is terminal. Cap the attempts, and never trust Retry-After to be present — Number(null) is NaN, and setTimeout(NaN) fires immediately, turning a backoff into a tight loop.

async function invokeActionForgeTool<TInput, TOutput>(
  tool: ActionForgeTool,
  payload: TInput,
  apiKey: string,
  attempt = 0
): Promise<TOutput> {
  const res = await fetch(tool.proxyUrl, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${apiKey}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(payload)
  });

  if ((res.status === 429 || res.status === 503) && attempt < 5) {
    const hinted = Number(res.headers.get("Retry-After"));
    const waitS = Number.isFinite(hinted) && hinted > 0 ? hinted : 2 ** attempt;
    await new Promise(r => setTimeout(r, Math.min(waitS, 60) * 1000));
    return invokeActionForgeTool(tool, payload, apiKey, attempt + 1);
  }

  if (!res.ok) {
    throw new Error(`Tool invocation failed: ${res.status}`);
  }

  const data = await res.json();
  return data.result as TOutput;
}

Note the 429 window is an hour, so a genuinely exhausted quota can report a Retry-After of up to 3600. Cap the wait (above: 60s) and let the outer loop decide, rather than parking an agent for an hour inside one call.

Registering from an agent

If your agent registers its own tools, honour ready rather than invoking immediately:

const created = await registerTool(definition);   // POST /api/generate-tool

if (!created.ready) {
  // The route is still propagating. Either poll the invoke endpoint until it
  // stops returning 503, or defer this tool and carry on with other work.
}

Error‑aware reasoning

StatusAgent interpretation
400Payload invalid → revise plan using inputSchema
401Authentication failure → request new key. Consumes no quota.
404Tool missing → remove from registry
409Name taken → rename and re-register. Do not retry as-is.
429Rate limit → backoff and retry after Retry-After
500Tool creation failed → notify developer
502Tool ran but its output mismatched outputSchema → adjust expectations or escalate
503Tool not ready yet, or a dependency is down → retry, do not conclude the tool is broken

Postman Collection

Import this collection into Postman to test registration and invocation quickly.

{
  "info": {
    "name": "ActionForge API",
    "_postman_id": "b7c8d1f2-3a45-4c9f-9d11-2e4f7a8b9c10",
    "description": "Postman collection for ActionForge tool registration and invocation.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "Generate Tool",
      "request": {
        "method": "POST",
        "header": [
          { "key": "Authorization", "value": "Bearer {{apiKey}}", "type": "text" },
          { "key": "Content-Type", "value": "application/json" }
        ],
        "body": {
          "mode": "raw",
          "raw": "{\n  \"toolName\": \"LoanPaymentCalculator\",\n  \"description\": \"Computes monthly loan payments using standard amortization.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"required\": [\"principal\", \"annualRate\", \"years\"],\n    \"properties\": {\n      \"principal\": { \"type\": \"number\", \"minimum\": 0 },\n      \"annualRate\": { \"type\": \"number\", \"minimum\": 0 },\n      \"years\": { \"type\": \"number\", \"minimum\": 1 }\n    }\n  },\n  \"outputSchema\": {\n    \"type\": \"object\",\n    \"required\": [\"monthlyPayment\"],\n    \"properties\": {\n      \"monthlyPayment\": { \"type\": \"number\" }\n    }\n  }\n}"
        },
        "url": {
          "raw": "{{baseUrl}}/api/generate-tool",
          "host": ["{{baseUrl}}"],
          "path": ["api", "generate-tool"]
        }
      }
    },
    {
      "name": "Invoke Tool",
      "request": {
        "method": "POST",
        "header": [
          { "key": "Authorization", "value": "Bearer {{apiKey}}", "type": "text" },
          { "key": "Content-Type", "value": "application/json" }
        ],
        "body": {
          "mode": "raw",
          "raw": "{\n  \"principal\": 250000,\n  \"annualRate\": 5.0,\n  \"years\": 30\n}"
        },
        "url": {
          "raw": "{{baseUrl}}/api/invoke/{{toolId}}",
          "host": ["{{baseUrl}}"],
          "path": ["api", "invoke", "{{toolId}}"]
        }
      }
    }
  ],
  "variable": [
    { "key": "apiKey", "value": "" },
    { "key": "baseUrl", "value": "https://actionforge-production.up.railway.app" },
    { "key": "toolId", "value": "" }
  ]
}

Pricing

No setup fee and no per-request charge. Plans differ by how many endpoints you may keep active and how often you may create and call them.

 FreeProTeam
Price$0$49 / month$299 / month
Active endpoints325200
Endpoints created per hour10100500
Requests per hour1,00050,000500,000

Payment is by card through Stripe Checkout; we never see or store card details. Charges appear on your statement as ACTIONFORGE.

Cancelling

Cancel at any time. There is no minimum term and no cancellation fee. Your account returns to the Free plan immediately and your endpoints are not deleted — you simply cannot create new ones until you are within the Free plan's limit of three.

Subscriptions are billed monthly in advance, and fees are not refunded for a partial month except where the law requires it. If something has gone wrong, email us before disputing a charge — we would rather fix it.

Getting started

Access is currently by request while the service is in early release. Email pritasharma25@gmail.com for an invite code, then create an account:

curl -X POST https://actionforge-production.up.railway.app/api/signup \
  -H 'content-type: application/json' \
  -d '{"email":"you@example.com","inviteCode":"YOUR_CODE"}'

That returns an API key. It is shown once and stored only as a cryptographic digest, so keep it safe — we can issue a new key, but we cannot recover the old one.

Support

pritasharma25@gmail.com — we aim to reply within two business days. See also our Terms of Service and Privacy Policy. Please read the Privacy Policy before sending real data: the inputs and results of every call are recorded, so the service must not be used with sensitive personal information.

FAQ

What is ActionForge?

A hosted service where you define tools with JSON Schema, receive a URL, and POST input to get validated output.

Can I update or delete tools?

There is no update endpoint — treat a tool as immutable and register a new one when its behaviour must change. There is a delete endpoint (DELETE /api/tools/{toolId}), which removes the tool and frees the plan slot it occupied.

What happens if my input doesn’t match the schema?

You receive 400 Bad Request. Fix the payload to conform to the registered inputSchema and retry.

Why does outputSchema matter?

ActionForge validates the tool’s output against outputSchema. If it doesn’t match, the call fails with 502 Bad Gateway. Tool responses are treated as untrusted: a tool that returns the wrong shape produces an error rather than a payload that quietly violates the contract you were handed.

What are the rate limits?

ActionLimit per hour (per key)
Tool registrations10
Tool invocations1000

Exceeding either returns 429 Too Many Requests with a Retry-After header and retryAfter field, plus x-ratelimit-limit and x-ratelimit-remaining headers.

Authenticated requests count against quota even when they fail — a 400 or a 404 still costs you. Unauthenticated requests (401) do not, because quota is per key and there is no key to charge.

Windows are aligned to the clock hour, not to your first request. A key that first calls at 10:59 gets a full quota again at 11:00, so retryAfter can be anywhere from 1 to 3600 seconds.

Why did my tool return 503 right after I created it?

Because it is not reachable yet. A newly registered tool is deployed to the edge, and its route takes a moment to propagate — usually seconds, occasionally a few minutes. Until it resolves, invocations return 503 with Retry-After.

The ready field on the registration response tells you which case you are in. ready: true means go; ready: false means wait or retry. This is the single most important thing to handle when an agent registering a tool immediately wants to use it.

What is executionMs?

Wall-clock time for the whole invocation, in milliseconds, including the network round trip to the tool — not tool CPU time alone. No specific accuracy guarantees are defined in the contract.

What’s intentionally unspecified?

  • Maximum request body size.
  • Maximum schema size.
  • Maximum execution time.
  • Tool update/delete semantics.
  • Versioning behavior.
  • Whether proxyUrl can change after creation.