Developer guide

Build a tool for Foundry

A Foundry tool is a small JavaScript function plus a manifest. It runs in a WebAssembly sandbox with only the capabilities a human grants it, on a backend that already lives on the drive. Any AI agent that can make an HTTP call can run it.

Your first tool

A tool is two files — a manifest and the source. Here's one that reads a repo's star count.

manifest.json

{
  "name": "star-counter",
  "version": "1.0.0",
  "description": "Reads a GitHub repo's star count and remembers it.",
  "author": "you",
  "params_schema": {
    "type": "object",
    "properties": { "repo": { "type": "string" } },
    "required": ["repo"]
  },
  "capabilities": {
    "vault":   { "credentials": ["github"] },
    "storage": { "namespace": "star-counter", "quota_bytes": 1048576 }
  }
}

tool.js

// tool.js — a Foundry tool is one async run(input) function.
async function run({ repo }) {
  // Call GitHub as the user, via Vault. The token is attached server-side;
  // this code never sees it.
  const res = await foundry.vault.fetch({
    credential: "github",
    url: `https://api.github.com/repos/${repo}`,
  });

  const stars = JSON.parse(res.body).stargazers_count;
  await foundry.kv.set(repo, stars); // persisted on the drive, in this tool's namespace
  return { repo, stars };
}

That's the whole thing. You export one async function run(input); whatever you return is the JSON the agent gets back. Every host call is asynchronous, so always await it.

The foundry API

The foundry global is the entire surface — storage, queues, caching, blobs, web requests, and credentials by reference. There's nothing to install; it's injected into the sandbox. Every call is brokered by the host, which enforces your grant — the JavaScript side is never trusted to limit itself.

CallReturns
await foundry.kv.set(key, value)Persist a JSON-serializable value in the tool’s namespace.
await foundry.kv.get(key)value | nullRead it back across runs.
await foundry.kv.del(key)Delete a key.
await foundry.cache.set(key, value, ttlSeconds)Like kv, but expires after the TTL.
await foundry.cache.get(key)value | nullReturns null once expired.
await foundry.queue.push(name, msg)Append to a named FIFO work queue.
await foundry.queue.pop(name)msg | nullTake the oldest message.
await foundry.blob.put(name, bytes)Object / blob storage for larger payloads.
await foundry.blob.get(name)bytes | nullRead a blob back.
await foundry.http.fetch(req)status, headers, body, truncatedreq = method, url, headers, body. Reaches only granted hosts.
await foundry.vault.fetch(req)status, headers, bodyreq = credential, method, url, headers, body. Vault attaches the secret; the tool never sees it.
foundry.log(level, msg) / console.log(…)Surfaced in the run’s logs.

http.fetch can reach only the hosts your manifest declares and the user grants. vault.fetch routes through Vault, which attaches the credential server-side — the raw secret never enters your tool or the agent's context.

The manifest

The manifest is your tool's identity and its capability request. You declare what the tool needs; the human grants a subset (possibly narrower) when they install it. Enforcement happens in the host at every call, never in your JavaScript.

FieldRequired
nameyesUnique tool id (lowercase). Used in the run URL.
versionyesSemver. A changed manifest re-prompts the user for consent.
descriptionyesOne line, shown in the catalog.
authornoYour handle. Tools are listed as author/name.
params_schemanoJSON Schema for input — how the agent knows what to pass.
capabilities.network.hostsnoHostnames foundry.http.fetch may reach.
capabilities.vault.credentialsnoVault credential names the tool may use by reference.
capabilities.storage.namespacenoStorage bucket (defaults to the tool name).
capabilities.storage.quota_bytesnoStorage cap in bytes.
capabilities.limits.mem_pages / wall_msnoSandbox memory + wall-clock ceilings.

Publishing

  1. Author and test the tool locally in the Foundry GUI (dev tools run behind a per-run human consent).
  2. Package it into a tool.zip (manifest + source) and submit it to the marketplace.
  3. The registry runs an AI exploit review, then signs the artifact.
reject

Malware or a sandbox-escape attempt. Never published.

flag

Published unverified — installable, with a warning.

pass

Published Verified.

Verified isn't a safety guarantee — it means a reviewer found nothing. The real floor is the same for every tool: the sandbox, plus the capabilities the user grants at install. That's why unverified tools are still safe to try.

How an agent runs it

Once a tool is installed and granted, any agent reads the catalog and runs it by name over the local API — the same zero-install REST + guide shape as the rest of mykeep.

# The agent lists the catalog, then runs a granted tool by name.
GET  /v1/foundry/tools            -> [{ name, version, description, params_schema }]
POST /v1/foundry/tools/star-counter
     { "repo": "tetratelabs/wazero" }
-> { "ok": true, "result": { "repo": "...", "stars": 4321 }, "logs": [] }

The agent can list and run granted tools, but never install, grant, or author them — that's the human's control plane.