mirror of
https://github.com/warmbly/warmbly.git
synced 2026-11-03 08:15:36 +00:00
125 lines
5.8 KiB
Plaintext
125 lines
5.8 KiB
Plaintext
---
|
|
title: Agent tools (REST)
|
|
description: Use Warmbly's AI tool registry from any function-calling agent, no MCP client required. List the tools your credentials allow, export OpenAI and Hermes format manifests, and execute tools over plain HTTP.
|
|
---
|
|
|
|
Not every agent speaks MCP. Hermes-style models, OpenAI-compatible frameworks, LangChain agents, and plain scripts all do function calling over ordinary HTTP, so Warmbly exposes the same tool registry as the [MCP server](/api/mcp/) through two REST endpoints: one that lists the tools your credentials allow (in the manifest format your agent framework expects), and one that executes a tool.
|
|
|
|
The rules are identical to MCP: every tool is gated by its own permission bits, the list only ever shows what the caller may use, and send-class tools (anything that puts real mail on the wire) are never exposed. An agent wired through this surface can read, search, label, and draft, but a human always presses send.
|
|
|
|
- **List**: `GET https://api.warmbly.com/v1/ai/tools`
|
|
- **Execute**: `POST https://api.warmbly.com/v1/ai/tools/{name}/call`
|
|
- **Auth**: an API key or OAuth access token as a bearer header, or a dashboard JWT. JWT members need the **Use AI** permission, or both endpoints answer `403`. Each tool checks its own scope from the [permissions](/api/permissions/) table; for JWT members, the matching organization permission.
|
|
|
|
## List the tools
|
|
|
|
`GET /ai/tools` returns the catalog filtered to what the caller's credentials allow. Three formats:
|
|
|
|
| `format` | Shape |
|
|
| --- | --- |
|
|
| `warmbly` (default) | `{ name, description, input_schema }` per tool |
|
|
| `openai` | OpenAI function-calling objects: `{ type: "function", function: { name, description, parameters } }` |
|
|
| `hermes`, `functions` | Aliases of `openai`; the same JSON schemas drop into a Hermes `<tools>` block verbatim |
|
|
|
|
```bash
|
|
curl -s "https://api.warmbly.com/v1/ai/tools?format=openai" \
|
|
-H "Authorization: Bearer wmbly_..."
|
|
```
|
|
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"type": "function",
|
|
"function": {
|
|
"name": "list_threads",
|
|
"description": "List unified-inbox conversation threads...",
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {
|
|
"subject": { "type": "string" },
|
|
"folder": { "type": "string" },
|
|
"unseen_only": { "type": "boolean" },
|
|
"limit": { "type": "integer" }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Because the list is permission-filtered, you can hand the response straight to your agent: everything in it is callable with the same credential, and nothing in it can send mail.
|
|
|
|
## Execute a tool
|
|
|
|
`POST /ai/tools/{name}/call` runs one tool. The request body is the tool's JSON argument object, exactly as your model produced it in its tool call; an empty body means no arguments.
|
|
|
|
```bash
|
|
curl -s -X POST "https://api.warmbly.com/v1/ai/tools/list_threads/call" \
|
|
-H "Authorization: Bearer wmbly_..." \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"folder": "inbox", "unseen_only": true, "limit": 10}'
|
|
```
|
|
|
|
```json
|
|
{
|
|
"data": {
|
|
"name": "list_threads",
|
|
"result": { "threads": [], "count": 0 }
|
|
}
|
|
}
|
|
```
|
|
|
|
`result` is the tool's output, embedded as JSON. Feed it back to the model as the tool-call result and continue the loop.
|
|
|
|
Errors use the standard envelope with a stable `code`: an unknown tool is `404 not_found`, a tool your credential lacks the scope for is `403 forbidden`, a malformed argument body is `400 bad_request`, and a tool-level failure (a validation message, a missing record) is `422 unprocessable` with the tool's own message, which is meant for the model to read and react to. A failure on the server's side is `500 internal_error` with a request id and no detail.
|
|
|
|
This is a side-effectful POST, so `Idempotency-Key` is honored like everywhere else in the API; per-key rate limits and usage logging apply.
|
|
|
|
## Wiring a Hermes agent
|
|
|
|
Hermes-format models (and anything served with an OpenAI-compatible tool-calling API) take the function schemas in the system prompt or the `tools` array, then emit tool calls you execute and answer. The loop against Warmbly:
|
|
|
|
1. `GET /ai/tools?format=hermes` once at session start and give the `data` array to the model as its available tools.
|
|
2. When the model emits a tool call `{ "name": ..., "arguments": ... }`, `POST /ai/tools/{name}/call` with the arguments object as the body.
|
|
3. Return `data.result` to the model as the tool response, and repeat.
|
|
|
|
```python
|
|
import json, requests
|
|
|
|
BASE = "https://api.warmbly.com/v1"
|
|
HEADERS = {"Authorization": "Bearer wmbly_..."}
|
|
|
|
tools = requests.get(f"{BASE}/ai/tools?format=hermes", headers=HEADERS).json()["data"]
|
|
# system prompt: "You may call these tools:\n<tools>" + json.dumps(tools) + "</tools>"
|
|
|
|
def run_tool(call):
|
|
r = requests.post(
|
|
f"{BASE}/ai/tools/{call['name']}/call",
|
|
headers=HEADERS,
|
|
json=call.get("arguments") or {},
|
|
)
|
|
body = r.json()
|
|
return body["data"]["result"] if r.ok else body # errors are model-readable too
|
|
```
|
|
|
|
The same two calls back any framework's "custom tool" escape hatch: point the executor at `/ai/tools/{name}/call` and the discovery step at `/ai/tools`.
|
|
|
|
## Choosing a surface
|
|
|
|
| You have | Use |
|
|
| --- | --- |
|
|
| Claude Code, Claude Desktop, Cursor, any MCP client | The [MCP server](/api/mcp/) at `/v1/mcp` |
|
|
| A Hermes / OpenAI-style function-calling agent | This REST surface |
|
|
| The dashboard | The built-in assistant, which runs the same registry as the signed-in member |
|
|
| A terminal | `warmblyctl tool list` and `warmblyctl tool call` |
|
|
|
|
All four run the identical registry with identical gates, so a tool behaves the same no matter which door it came through.
|
|
|
|
## See also
|
|
|
|
- [MCP server](/api/mcp/) for the protocol-native version of this surface
|
|
- [Permissions](/api/permissions/) for the scope each tool checks
|
|
- [Realtime API](/api/realtime/) to stream events into a long-running agent instead of polling
|