A search engine for AI-agent-callable capabilities — import it into your framework, copy a tool definition, or call POST /search / POST /resolve directly.
Five ways to plug this in, in order of how little code you have to write.
Point your framework's tool-calling / plugin importer at the spec below. Most modern agent frameworks (and a growing number of LLM providers' own tool-use setup flows) can ingest an OpenAPI document directly and generate a working tool with no hand-written integration code.
GET /openapi.json
Same source of truth as the OpenAPI spec — generated from the actual request/response schemas, not hand-maintained, so it can't drift from what the API really does.
GET /tools/openai.json // OpenAI Chat Completions / Responses API `tools` array
GET /tools/anthropic.json // Claude Messages API `tools` array
// Example: OpenAI
const tools = await fetch("https://agent-capability-search-api.shamarwebsterrb.workers.dev/tools/openai.json")
.then((r) => r.json());
// pass `tools` directly as the `tools` param on your Chat Completions call
If your agent already speaks MCP, add this as a server directly — no HTTP
code to write, just config. It's a thin local passthrough to the same
/search and /resolve endpoints below, using your
own API key, so tiering/rate limits/billing all work exactly like calling
the REST API yourself. (Runs locally via npx, not a remote
hosted server — see packages/mcp-server/README.md in the repo
for why, if you're curious.)
{
"mcpServers": {
"agent-capability-search": {
"command": "npx",
"args": ["-y", "@acs/mcp-server"],
"env": { "ACS_API_KEY": "<your key>" }
}
}
}
Exposes two tools: search_capabilities and resolve_capability.
Typed request/response, an error type that carries the API's own status
and body instead of a bare HTTP error, and a couple of function calls
instead of raw fetch/requests boilerplate.
TypeScript and Python are both built and live-verified against this API
today, but not yet published to a registry — install straight from the
repo for now (packages/sdk-ts,
sdk/python), npm/PyPI publishing
is a pending decision.
// TypeScript — packages/sdk-ts
import { AcsClient } from "@acs/sdk-ts";
const client = new AcsClient({ apiKey: "<your key>" });
const { results } = await client.search({ query: "weather", limit: 5 });
# Python — sdk/python
from acs_client import AcsClient
client = AcsClient(api_key="<your key>")
results = client.search("weather", limit=5)["results"]
POST /search and POST /resolve, documented below
— works from any language, any framework, zero dependencies.
Compatible out of the box with local-first agent platforms — the emerging class of orchestrator that runs its planning model on-device (often a swappable open-weight model) and only escalates to cloud tools with the user's explicit, per-step consent. Perplexity's "Portable Computer" is one concrete example; the same applies to any local orchestrator that escalates to MCP tools or OpenAI-format function-calling.
The MCP server above (option 3) is the recommended integration path for this audience: it's the same thin passthrough everyone else uses, but its two tool descriptions explicitly state that calling them sends your query or capability ID over the network to this hosted API — nothing runs or is processed on-device. That disclosure isn't decorative. A local-first orchestrator's consent UI has no way to inspect what a tool actually does before calling it; the description text is the only signal it has to tell the user "this step is about to leave your device." Tools that stay silent about being a network call make that UI lie to the user by omission. Ours doesn't.
Same billing, tiering, and rate limits as calling the REST API directly — see Authentication below.
Pass your key as x-api-key. /search allows a small
unauthenticated demo quota; /resolve always requires a key.
| Tier | /search | /resolve |
|---|---|---|
| Free (no key) | Limited demo quota | Not allowed |
| Free (with key) | Allowed | 20 / day |
| Starter+ | Allowed | Higher limits, plus payment-hint detail and verified-only filtering |
Find capabilities matching a natural-language query.
{
"query": "get the current weather for a city",
"protocol": ["mcp_tool", "rest_api"], // optional
"constraints": { // optional
"verifiedOnly": true,
"authType": "api_key",
"paymentMethod": "free"
},
"limit": 10, // 1-50, default 10
"cursor": null // optional pagination cursor
}
{
"results": [
{
"capabilityId": "b6f1a2b0-1c2d-4e3f-8a9b-000000000002",
"name": "get_weather",
"description": "Returns current weather for a city.",
"protocol": "mcp_tool",
"provider": { "id": "...", "name": "Acme Weather" },
"score": 0.94,
"verificationStatus": "scanned_clean",
"paymentHintSummary": ["free"]
}
],
"nextCursor": null
}
curl -X POST https://api.example.com/search \
-H "content-type: application/json" \
-d '{"query":"weather"}'
Get everything needed to invoke a capability returned by /search.
{
"capabilityId": "b6f1a2b0-1c2d-4e3f-8a9b-000000000002",
"intentContext": "user is asking about weather in Berlin" // optional
}
{
"capabilityId": "b6f1a2b0-1c2d-4e3f-8a9b-000000000002",
"protocol": "mcp_tool",
"endpoint": { "url": "https://...", "method": "streamable-http" },
"auth": { "type": "none" },
"schema": { "input": { ... }, "output": { ... } },
"payment": { "accepted": ["free"], "providerDeclared": true, "verified": false },
"safety": { "verificationStatus": "scanned_clean", "lastScannedAt": "2026-08-01T00:00:00Z" },
"expiresAt": "2026-08-29T00:10:00Z"
}
curl -X POST https://api.example.com/resolve \
-H "content-type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{"capabilityId":"b6f1a2b0-1c2d-4e3f-8a9b-000000000002"}'
Every capability is scanned before listing. verificationStatus
tells you what stage it's at — only scanned_clean and
manual_reviewed capabilities are ever returned by
/search or /resolve. Flagged or delisted
capabilities are excluded automatically.