Register a tool manifest
const url = 'https://example.com/v1/tools';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"id":"example","projectId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","description":"example","version":"example","input":{},"output":{},"needs":[{"name":"example","optional":true}],"effects":[{"kind":"reads","resource":"example","notes":"example"}],"transport":"auto","mcpEndpoint":"example","metadata":{},"mutating":true,"needsApprovalWhenUnattended":true,"pauses":true,"storesFiles":true,"readsFiles":true,"resultIsOutsideData":true,"sandbox":"none","limits":{"memMB":1,"cpuMs":1},"network":{"kind":"none"},"needsSpec":{"env":{"additionalProperty":{}},"secrets":{"additionalProperty":{}},"config":{"additionalProperty":{}},"capabilities":["example"],"bindings":["example"],"connections":{"additionalProperty":{"app":"example","permissions":["example"],"as":"person"}}},"codeArtifactRef":{"kind":"oci","imageRef":"example","modulePath":"example","artifactVersion":"example"},"spec":{"kind":"http","method":"GET","urlTemplate":"example","headers":[{"name":"example","value":"example"}],"authorization":{"kind":"bearer","secretRef":{"envName":"example","name":"example"}},"requestBody":{"kind":"json-input"},"timeoutMs":1,"parseJson":true,"successStatus":{"min":1,"max":1}}}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/v1/tools \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "id": "example", "projectId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "description": "example", "version": "example", "input": {}, "output": {}, "needs": [ { "name": "example", "optional": true } ], "effects": [ { "kind": "reads", "resource": "example", "notes": "example" } ], "transport": "auto", "mcpEndpoint": "example", "metadata": {}, "mutating": true, "needsApprovalWhenUnattended": true, "pauses": true, "storesFiles": true, "readsFiles": true, "resultIsOutsideData": true, "sandbox": "none", "limits": { "memMB": 1, "cpuMs": 1 }, "network": { "kind": "none" }, "needsSpec": { "env": { "additionalProperty": {} }, "secrets": { "additionalProperty": {} }, "config": { "additionalProperty": {} }, "capabilities": [ "example" ], "bindings": [ "example" ], "connections": { "additionalProperty": { "app": "example", "permissions": [ "example" ], "as": "person" } } }, "codeArtifactRef": { "kind": "oci", "imageRef": "example", "modulePath": "example", "artifactVersion": "example" }, "spec": { "kind": "http", "method": "GET", "urlTemplate": "example", "headers": [ { "name": "example", "value": "example" } ], "authorization": { "kind": "bearer", "secretRef": { "envName": "example", "name": "example" } }, "requestBody": { "kind": "json-input" }, "timeoutMs": 1, "parseJson": true, "successStatus": { "min": 1, "max": 1 } } }'Body is a ToolManifest (Tool minus its runtime handler). Server validates via @kindgi/tools.validateToolManifest. Metadata only: the handler is not uploaded through this route and must already be available to the runtime. A tool never gets a model provider’s key: one that declares (needsSpec.secrets) or sends (spec.authorization.secretRef) a secret a provider registration of the tenant names, in any env, is refused. Where each secret it names is read is recorded on the version (secretScopes): the first of its own scope, its project’s org and the tenant that has the secret and that the caller administers, else the broadest they administer; the runtime reads it there only.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Header Parameters
Section titled “Header Parameters”Caller-supplied idempotency key, scoped to the caller: the same key from someone else in the tenant is their own request (from 0.1.5). Retries with the same key return the original response byte-identical (per docs/API-ROUTE-CONVENTIONS.md §3.1). A retry sent while the first request still runs is answered 409 idempotency-key-in-flight with Retry-After, on a runtime whose store holds keys (from 0.1.5); retry after it, and you get the first request’s answer. An answer that carries a secret (a new API key or public run token, a session token, a generated signing secret) isn’t kept: a retry gets 409 idempotency-key-replay-withheld, with the first request’s status and when it succeeded (from 0.1.5). A request whose key store can’t be read before it runs is answered 503 idempotency-store-unavailable with Retry-After: nothing ran, so retry with the same key (from 0.3.0).
Request Bodyrequired
Section titled “Request Bodyrequired”ToolManifest — Tool minus its runtime handler. Validated server-side via @kindgi/tools.validateToolManifest. Metadata-only registration: the handler is not uploaded and must already be available to the runtime.
object
ToolId — dotted namespace (e.g. acme.verify-citation).
Project this belongs to (its content scope). Required: missing, or not a project in the caller’s tenant → 400 bad-input.
JSON Schema (Draft 2020-12) for the tool input.
object
JSON Schema (Draft 2020-12) for the tool output.
object
object
object
object
Semantic marker: true when this tool causes observable side effects (writes state, calls external APIs with mutations, sends messages). Read-only tools set false. Absent defaults to true. Consumed by the HITL default classifier — a read-only tool passes straight through, a mutating tool asks on first use. It also decides what runs together: calls to read-only tools that a model step asks for one after another run at the same time, up to 8 at once, unless the agent sets parallelToolCalls: false; a call to a mutating tool runs on its own, and so does a call to a read-only tool that pauses or declares connections, since it may wait.
true: in a run no person started (one whose startedBy isn’t person), every call of this tool waits for a person’s yes, whatever the agent’s approval rules say, and a flow’s tool step that calls it is refused. A webhook trigger, a schedule or a mail trigger whose toolsAllowedUnattended names the tool (a project admin’s to set) lifts that, in its own runs only. A tool that uses a person’s connection, reads included, counts as declaring it. In a run a mail started, so does a tool that isn’t mutating: false, and one that uses the run’s own mailbox. Absent means false.
true when the tool may make its run wait for another system’s answer: each call gets a wait token, hands its link to that system, and answers pause; the system’s answer, valid against output, becomes the call’s result. The run waits at most 24 days (24 hours by default) and fails the call when the time passes. Only a pack tool with a handler can pause: not one with a declarative spec, nor an MCP tool. Absent means false.
true when the tool may store files for its run, such as a mail’s attachments: each call can store up to 10, the runtime checks each one’s bytes, and the tool returns the stored files in its result’s top-level files, which an agent is shown. Only a pack tool with a handler can store files: not one with a declarative spec, nor an MCP tool. Absent means false.
true when the tool may read stored files’ bytes, such as a PDF an agent was given, to parse it: each call can read a file its run may be shown, by its file value’s artifactId, as it was stored. Only a pack tool with a handler can read files: not one with a declarative spec, nor an MCP tool. Absent means false.
true when the tool’s results are text from outside that nobody checked, such as a mail someone sent: an agent reads each result as data, not instructions, in that turn and in every later turn’s history. Absent means false.
Isolation posture the runtime enforces around the handler. Optional additive field.
Sandbox-enforced resource caps at dispatch. Optional additive field.
object
Typed discriminated needs (env / secrets / config / capabilities / bindings / connections). Optional additive field.
object
object
object
object
object
object
object
Accounts at other apps the tool acts with, by the tool’s own name for each (mail). The runtime resolves them per call into the context’s connections.
object
object
The app’s entry id: google, github.
The entry’s permission keys the call needs: gmail.send.
Whose connection: person, the run’s person (the one it acts for, who started it or owns its trigger); company, the tenant’s.
Declarative HTTP-invocation spec. Attached to ToolManifest.spec under the discriminant kind: 'http'. The runtime Tool.handler is synthesized by the ‘http’ spec synthesizer to perform URL-template substitution, secret-ref resolution via ToolContext.resolveSecret, and the outbound fetch. All fields serialize cleanly to JSON.
object
URL template with {param} placeholders substituted from the tool’s input at invoke time.
object
object
object
object
object
A connection the tool declares in needsSpec.connections, by its name there: sent as Authorization: Bearer <access token>.
object
Wall-clock timeout in ms. Default 30000.
When true (default), the response body is parsed as JSON before returning to the invoker.
object
Responses
Section titled “Responses”Tool registered.
object
Examplegenerated
{ "toolId": "example"}Validation failed (see details.issues); or projectId isn’t a project id (a UUID). Or provider-key-refused: the tool names a model provider’s key (details.secret, details.providerId); store the key it needs under its own name (the same value is fine) and name that. Or reserved-name-prefix: the tool names a secret under the reserved kindgi. prefix (details.secret); those are Kindgi’s own. Or validation-failed for a metadata key under kindgi.: those are the runtime’s own (details.issues, at /metadata/<key>).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Missing / malformed / expired / revoked bearer token.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}The body’s projectId names no project of this tenant (project-not-found).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}tool-already-registered: that (id, version) is taken. Or tool-project-mismatch: the tool’s versions live in another project (a tool belongs to the project its first version was published into and never moves, though in a tenant with environment orders a version may be made in another project of that project’s order; the message doesn’t name the project). Or tool-kind-mismatch: the tool’s active versions are of another kind, an MCP endpoint’s tool (details.endpointId) where this version is published here, or the other way round; a tool’s active versions are all one kind, and nothing was written (details.toolId, details.version). Or environment-takes-promotions: the project is a later environment of an environment order, which takes builds only by promotion from the one before it; publish in the order’s first environment instead (details.firstProjectId, when you can read that project; details.position, the project’s place from 0). Nothing is written. Or registry-read-only: this registry takes no writes (under kindgi dev, the pack’s files are the source); the message says what to do instead.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}The request body is over the API’s limit (request-too-large; details.maxBytes says how much).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}rate-limit-exceeded with details.limit: "schema-compile-time", on a server with a compile budget (Kindgi Cloud): this tenant’s schemas spent details.budgetMs compiling in the last 60 seconds. Nothing was stored; Retry-After says when to send it again.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Server error (unmapped domain code or framework crash).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}The Idempotency-Key store couldn’t be read before the request ran (idempotency-store-unavailable), so nothing was changed; Retry-After (whole seconds) says when to try again with the same key.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}See also
GET /v1/tools: List tools- Give a tool a secret
- Kindgi 0.1.8
- Error code
environment-takes-promotions - Error code
idempotency-store-unavailable - Error code
project-not-found - Error code
provider-key-refused - Error code
rate-limit-exceeded - Error code
registry-read-only - Error code
request-too-large - Error code
reserved-name-prefix - Error code
tool-already-registered - Error code
tool-kind-mismatch - Error code
tool-project-mismatch - Error code
validation-failed - Every error code