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 Why ActionForge exists
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.
Start here Two steps
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 What ActionForge does
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
- Expose custom logic as a simple HTTP endpoint with strict contracts.
- Give AI agents typed tools with predictable input/output shapes.
- Enforce validation on both requests and responses.
- Chain tools together in workflows using schemas as the source of truth.
Authentication Key handling
Every request to ActionForge must include a bearer token:
Authorization: Bearer <api-key>
- Keys are issued by the service operator.
- Keys can be revoked; revoked keys immediately stop working.
- Treat keys as secrets:
- Do not commit them to source control.
- Do not log or echo them.
- Do not place them in URLs.
Endpoint Reference Contract only
POST /api/generate-tool
Registers a new tool.
| Field | Type | Constraints |
|---|---|---|
| toolName | string | 1–128 chars, ^[A-Za-z0-9_\- ]+$, unique across the deployment — a collision returns 409 |
| description | string | 1–4000 chars |
| inputSchema | object | Valid JSON Schema |
| outputSchema | object | Valid 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
| Status | Meaning |
|---|---|
| 400 | Malformed JSON, missing/invalid fields, or invalid JSON Schema |
| 401 | Missing or invalid API key |
| 409 | toolName already in use — names are unique; choose another. Retrying will not help. |
| 429 | Rate limited |
| 503 | A dependency is unavailable. Transient — retry after Retry-After. |
| 500 | Tool could not be created |
POST /api/invoke/[toolId]
Invokes a previously registered tool.
| Parameter | Type | Constraints |
|---|---|---|
| toolId (path) | string | Must 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
| Status | Meaning |
|---|---|
| 400 | Non‑UUID id, malformed JSON, or payload failing inputSchema |
| 401 | Missing or invalid API key |
| 404 | No such tool |
| 429 | Rate limited |
| 502 | Tool failed to execute, or its output did not match outputSchema |
| 503 | Retryable. 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 First tool in minutes
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 Agentic systems
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
- Tool registration — done by developers via
/api/generate-tool. - Tool discovery — agents receive
toolId,toolName,proxyUrl, and schemas. - Tool invocation — agents POST payloads to
proxyUrland 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
| Status | Agent interpretation |
|---|---|
| 400 | Payload invalid → revise plan using inputSchema |
| 401 | Authentication failure → request new key. Consumes no quota. |
| 404 | Tool missing → remove from registry |
| 409 | Name taken → rename and re-register. Do not retry as-is. |
| 429 | Rate limit → backoff and retry after Retry-After |
| 500 | Tool creation failed → notify developer |
| 502 | Tool ran but its output mismatched outputSchema → adjust expectations or escalate |
| 503 | Tool not ready yet, or a dependency is down → retry, do not conclude the tool is broken |
Postman Collection Importable JSON
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 Monthly subscriptions, USD
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.
| Free | Pro | Team | |
|---|---|---|---|
| Price | $0 | $49 / month | $299 / month |
| Active endpoints | 3 | 25 | 200 |
| Endpoints created per hour | 10 | 100 | 500 |
| Requests per hour | 1,000 | 50,000 | 500,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 Common questions
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?
| Action | Limit per hour (per key) |
|---|---|
| Tool registrations | 10 |
| Tool invocations | 1000 |
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
proxyUrlcan change after creation.