Agent

Connect client tools

Let the agent call browser functions and continue with their results.

Let the agent read page state on demand or perform a page action. Declare a tool on the agent and implement a matching callback in your Kit page. Kit returns the result to the session so the agent can continue.

Prepare the integration

You need a working Kit page, a service agent's public ID (agent_…), and an API Key on your backend. This example reads a lesson selection without a video, database, or extra dependencies. Your website must provide an authenticated POST /api/praxis-token route returning { token }, as described in Embed tokens and users. This is your route, not a Praxis endpoint.

Use a client tool for on-demand reads. Use page context for small facts needed with every message.

Declare the tool

Save this as register-tool.ts, replace agent_example, and run bun register-tool.ts on a server with PRAXIS_API_KEY set. It reads the agent, then updates tools through the public API. Other tools are preserved, and the current version guards against concurrent changes.

// register-tool.ts — run on the server with: bun register-tool.ts
const key = process.env.PRAXIS_API_KEY;
if (!key) throw new Error("Set PRAXIS_API_KEY on the server");
const url = "https://api-develop.deepraxis.com/agent/v1/agents/agent_example";
const headers = { Authorization: `Bearer ${key}`, "Content-Type": "application/json" };
const currentResponse = await fetch(url, { headers });
if (!currentResponse.ok) throw new Error(`Read failed: ${currentResponse.status}`);
const current = await currentResponse.json();
const tool = {
  type: "custom",
  name: "read_selected_lesson",
  description: "Read the selected lesson when the user asks which lesson is open.",
  input_schema: { type: "object", properties: {}, additionalProperties: false },
};
const tools = [...(current.tools ?? []).filter(
  (item: { type: string; name?: string }) =>
    !(item.type === "custom" && item.name === tool.name),
), tool];
const response = await fetch(url, {
  method: "POST", headers,
  body: JSON.stringify({ version: current.version, tools }),
});
if (!response.ok) throw new Error(`Update failed: ${response.status}`);
const saved = await response.json();
console.log({ id: saved.id, version: saved.version });

Expected result: the terminal prints the agent ID and new version. Issue a token for that version and start a new session. Tokens pinned to an older version and existing sessions do not automatically adopt the new definition. If the embed policy pins a version, update it in the Dashboard or specify the new version during issuance.

Tool names must match ^[a-zA-Z0-9_-]{1,64}$ and must not duplicate custom or built-in tool names. input_schema.type must be object. Describe when the agent should call the tool. A browser function alone does not register a tool with the agent.

Handle the call in your page

Serve this HTML from your website. It uses the Develop Kit and API; the embed policy must allow the page origin.

<!doctype html>
<html lang="en">
<meta charset="utf-8" />
<title>Lesson assistant</title>
<label>Current lesson
  <select id="lesson">
    <option value="Lesson 3: Delivery rules">Lesson 3: Delivery rules</option>
    <option value="Lesson 4: Returns">Lesson 4: Returns</option>
  </select>
</label>
<output id="tool-status" aria-live="polite"></output>
<praxis-agent style="display:block;height:600px"></praxis-agent>
<script type="module">
  import "https://kit-develop.praxisusercontent.com/v1/praxis-kit.js";
  await customElements.whenDefined("praxis-agent");
  const chat = document.querySelector("praxis-agent");
  const lesson = document.querySelector("#lesson");
  chat.onClientTool = async ({ name }) => {
    if (name !== "read_selected_lesson") throw new Error(`Unknown tool: ${name}`);
    return { title: lesson.value };
  };
  chat.addEventListener("praxis.tool.change", event => {
    const { name, status, isError } = event.detail;
    document.querySelector("#tool-status").textContent =
      `${name}: ${status}${isError ? " (error)" : ""}`;
  });
  chat.addEventListener("praxis.log", event => console.warn(event.detail));
  chat.setOptions({
    api: {
      url: "https://api-develop.deepraxis.com",
      getToken: async () => {
        const response = await fetch("/api/praxis-token", {
          method: "POST", credentials: "same-origin",
        });
        if (!response.ok) throw new Error(`Token request failed: ${response.status}`);
        const { token } = await response.json();
        if (typeof token !== "string" || !token) throw new Error("Missing token");
        return token;
      },
    },
    agent: { name: "Lesson assistant" },
  });
</script>
</html>

Assign onClientTool as a JavaScript property on the element. It is neither an HTML attribute nor a setOptions option. The callback receives { id, name, input }: call ID, tool name, and input. Validate the input in your handler and throw for unknown tool names.

Strings are returned unchanged. Other serializable values become JSON text; undefined becomes an empty string. Exceptions, rejected promises, and serialization failures become error results. Do not include credentials or another user's data.

Verify calls and recovery

Ask the agent to use the tool to identify the open lesson. Expected result: the page reports start, then end, and the agent answers using the returned lesson. Change the selection and ask again. An end event means the server accepted the result, not that the entire response finished.

SymptomAction
No callCheck the new session's agent version and the tool description
not available on this pageAssign onClientTool before a call arrives
Handler failureFix input validation or page logic; the agent receives an error result
Result submission failure in praxis.logThe handler is not automatically rerun. Check connectivity and session state before repeating side effects

The server waits at most 120 seconds, then records an error result and continues. Kit claims call IDs and checks historical results to avoid duplicate execution. This is not a transaction guarantee across browser tabs. Backend authorization and idempotency are required for tools that change business data.

Authorization and next steps

Client custom tools bypass the platform's tool confirmation cards. Implement any page-action confirmation in your own UI. Enforce data permissions on the backend, without trusting browser values or model-supplied identity.

Tool authorization · Component reference · Update agent API.

Export

On this page