agent-webkit

Getting started

Five-minute walkthrough — Python server + React client, end to end.

By the end of this page you'll have a Python FastAPI server holding a Claude Agent SDK session and a React app talking to it over SSE.

Prerequisites

  • Python ≥ 3.10
  • Node ≥ 18
  • A CLAUDE_CODE_OAUTH_TOKEN from claude setup-token (or any auth the Claude Agent SDK accepts).

1. Stand up the server

pip install "agent-webkit-server[fastapi]"

Save this as main.py:

import os
import uvicorn
from agent_webkit_server.adapters.fastapi import create_app
from agent_webkit_server.auth import AuthConfig

app = create_app(auth=AuthConfig(disabled=True))

if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=8000)

Then:

python main.py

You now have a server at http://127.0.0.1:8000 that exposes the agent-webkit wire protocol. For real deployments you'll want auth on — see the FastAPI guide.

2. Drive it from React

npm install @agent-webkit/core @agent-webkit/react
App.tsx
import { useAgentSession } from "@agent-webkit/react";

export default function App() {
  const session = useAgentSession({ baseUrl: "http://127.0.0.1:8000" });

  return (
    <div>
      <ul>
        {session.messages.map((m) => (
          <li key={m.id}>
            <b>{m.role}</b>: {textOf(m)}
          </li>
        ))}
      </ul>
      <button
        disabled={session.status !== "idle"}
        onClick={() => session.send("Hello, who are you?")}
      >
        Send
      </button>
    </div>
  );
}

function textOf(m: { content: { type: string; text?: string }[] }) {
  return m.content.filter((b) => b.type === "text").map((b) => b.text).join("");
}

That's it. useAgentSession opens a session on mount, opens the SSE stream, reconciles deltas into complete messages, and exposes a typed send() for user input.

3. Handle a permission request

When Claude wants to use a tool, the server emits a permission_request event. The hook surfaces it as session.permissionRequest:

{session.permissionRequest && (
  <div className="modal">
    <p>Allow tool: <code>{session.permissionRequest.tool_name}</code>?</p>
    <button onClick={() => session.respondToPermission("allow")}>Allow</button>
    <button onClick={() => session.respondToPermission("deny")}>Deny</button>
  </div>
)}

More on permissions →

4. Handle AskUserQuestion

Same pattern, separate state slot:

{session.askUserQuestion && (
  <Dialog
    questions={session.askUserQuestion.questions}
    onSubmit={(answers) => session.answerUserQuestion(answers)}
  />
)}

What's next

On this page