{"slug": "show-hn-anyclaude-sdk-claude-code-style-sdk-for-openai-anthropic-endpoints", "title": "Show HN: Anyclaude-SDK – Claude Code-Style SDK for OpenAI/Anthropic Endpoints", "summary": "Anyclaude-SDK, a new open-source SDK that brings Claude Code-style agent capabilities to any OpenAI- or Anthropic-compatible LLM endpoint, has been released on npm. The SDK supports multi-agent teams, MCP servers, and runs in the browser via WebContainer, Node, and Bun without requiring a backend or OAuth. It exposes the same interface as the official Anthropic Claude agent SDK, allowing code written for that SDK to work unchanged.", "body_md": "Claude Code agent capabilities — tools, the tool loop, multi-turn conversations,\nMCP, sub-agents, **multi-agent teams**, sessions — against **any OpenAI- or\nAnthropic-compatible LLM endpoint**, running in the **browser**\n([WebContainer](https://webcontainers.io)), **Node**, and **Bun**. No backend\nrequired, no OAuth, no native binaries.\n\nLive demo:[a full IDE running in your browser]·Docs:[anyclaude-docs.puter.site]·React UI kit:`anyclaude-react`\n\nIt exposes the same `query()`\n\nasync-generator interface and the same `SDKMessage`\n\nenvelope as `@anthropic-ai/claude-agent-sdk`\n\n, so code written against the official\nSDK can iterate our output unchanged.\n\n**Multi-agent teams** go beyond one agent: a coordinator delegates board tasks to\nworker sub-agents in parallel, you can **dispatch a message to a running worker**\nand have it land on its next tool round (push delivery, like the message queue),\nsupervise them live with background dispatch, block for the next finished worker\nwith\n\n**(event-driven — integrate each result as it lands, no busy-polling), and even run agents in**\n\n`wait_for_worker`\n\n**separate Web Workers or browser tabs** that share one mailbox via\n\n`BroadcastChannelMailbox`\n\n.\nSee [Teams & sub-agents](https://anyclaude-docs.puter.site/teams.html).\n\n```\nnpm install anyclaude-sdk @webcontainer/api\n```\n\n`@webcontainer/api`\n\nis an optional peer dependency — only needed if you use\n`WebContainerWorkspace`\n\n. You can supply your own `FileSystem`\n\n/`CommandExecutor`\n\n.\n\n``` js\nimport { WebContainer } from '@webcontainer/api'\nimport {\n  query,\n  WebContainerWorkspace,\n  createOpenAIClient,\n  ALL_CLAUDE_CODE_TOOLS,\n} from 'anyclaude-sdk'\n\n// 1. Boot a WebContainer and wrap it as a workspace.\nconst wc = await WebContainer.boot()\nconst workspace = new WebContainerWorkspace(wc)\n\n// 2. Point at any OpenAI-compatible endpoint.\nconst llm = createOpenAIClient({\n  apiKey: import.meta.env.VITE_OPENAI_API_KEY,\n  baseUrl: 'https://api.openai.com/v1', // or Groq, Together, OpenRouter, local…\n  model: 'gpt-4o',\n})\n\n// 3. Run the agent — same shape as the official SDK.\nfor await (const msg of query({ prompt: 'List the files and summarize the project', workspace, llm })) {\n  if (msg.type === 'assistant') {\n    for (const block of msg.message.content) {\n      if (block.type === 'text') console.log(block.text)\n    }\n  } else if (msg.type === 'result' && msg.subtype === 'success') {\n    console.log('Done:', msg.result)\n  }\n}\n```\n\nConnect external MCP servers or define in-process tools. Because browsers block\ndirect cross-origin MCP fetches (CORS), pass a `mcpProxy`\n\nfor remote servers:\n\n``` js\nimport { createSdkMcpServer, tool } from 'anyclaude-sdk'\n\nconst calc = createSdkMcpServer({\n  name: 'calc',\n  tools: [tool('add', 'Add two numbers',\n    { type: 'object', properties: { a: { type: 'number' }, b: { type: 'number' } }, required: ['a', 'b'] },\n    (args) => ({ content: [{ type: 'text', text: String(args.a + args.b) }] }))],\n})\n\nquery({\n  prompt, workspace, llm,\n  mcpServers: {\n    calc,                                                   // in-process, no network\n    docs: { type: 'http', url: 'https://mcp.example.com' }, // remote\n  },\n  // Route remote MCP through a CORS proxy (function, `{url}`/`{rawUrl}` template, or bare prefix):\n  mcpProxy: 'https://my-proxy.example/?url={url}',\n})\n```\n\nRemote tools are exposed as `mcp__<server>__<tool>`\n\n.\n\nThree transport clients, all implementing the same `LLMClient`\n\ninterface:\n\n``` js\nimport { createOpenAIClient, createAnthropicClient, createResponsesClient } from 'anyclaude-sdk'\n\n// OpenAI-compatible Chat Completions (OpenAI, Groq, Together, OpenRouter, xAI, Kilo, local…)\nconst a = createOpenAIClient({ apiKey, baseUrl: 'https://api.x.ai/v1', model: 'grok-build-0.1' })\n\n// Anthropic Messages API\nconst b = createAnthropicClient({ apiKey, model: 'claude-sonnet-4-6' })\n\n// OpenAI Responses API (POST /v1/responses)\nconst c = createResponsesClient({ apiKey, model: 'gpt-4o' })\n\n// AgentRouter gateway (sponsor) — one-line preset over createOpenAIClient; key\n// falls back to AGENTROUTER_API_KEY. Reaches Claude/GPT/Gemini/DeepSeek/GLM/… via one base URL.\nimport { createAgentRouterClient } from 'anyclaude-sdk'\nconst d = createAgentRouterClient({ model: 'claude-sonnet-4-5-20250929' })\n```\n\nAll three normalize tool calls, streaming, and usage to the same `StreamResult`\n\n,\nand include a fallback parser for models that emit tool calls as inline text.\nEvery client also accepts `extraHeaders`\n\n/ `extraBody`\n\n(merge provider-specific\nheaders/params without a custom client) and surfaces `retries`\n\non the result when\ntransient failures were retried.\n\nUse a `PromptStream`\n\nto push user turns over time:\n\n``` js\nimport { query, PromptStream } from 'anyclaude-sdk'\n\nconst prompts = new PromptStream()\nconst session = query({ prompt: prompts, workspace, llm, model: 'gpt-4o' })\n\nprompts.push('Create a hello.txt with a greeting')\n// …later, based on UI input:\nprompts.push('Now translate it to French')\nprompts.end() // close the conversation\n\nfor await (const msg of session) {\n  // render msg…\n}\n```\n\n`ALL_CLAUDE_CODE_TOOLS`\n\nincludes:\n\n| Tool | Purpose |\n|---|---|\n`bash` |\nRun shell commands via jsh (`2>&1` /`/dev/null` redirects are stripped) |\n`read_file` |\nRead text (numbered lines, offset/limit), images (auto-downsampled base64), PDFs (document block), and notebooks (`.ipynb` cells + outputs); binary files are rejected with guidance |\n`write_file` |\nWrite a file, creating parent dirs |\n`edit_file` |\nExact-match string replace (requires a prior read) |\n`multi_edit` |\nApply a sequence of edits to one file atomically |\n`notebook_edit` |\nReplace/insert/delete cells in a `.ipynb` |\n`delete_file` |\nRemove a file/dir |\n`glob` |\nFind files by glob pattern (`**` , `*` , `?` ) |\n`grep` |\nRegex search across files |\n`list_files` |\nList a directory |\n`todo_write` |\nTrack a multi-step task list across turns |\n`web_fetch` |\nFetch a URL → clean Markdown via the Jina Reader (CORS-free, JS-rendered) |\n`web_search` |\nWeb search via Jina + DuckDuckGo HTML; returns top-N title/URL/snippet |\n`render_page` |\nRender a public page in a headless browser: Playwright-style `actions` (click/fill/scroll/press), accessibility `snapshot` , `evaluate` JS, console+network `logs` , and a screenshot (as a viewable image) or extracted markdown/html. Refuses localhost/private/WebContainer hosts. |\n\n`read_file`\n\ndispatches by file type. Image and PDF bytes are forwarded to the\nmodel automatically as a follow-up user turn (Anthropic gets native\n`image`\n\n/`document`\n\nblocks; OpenAI-compatible endpoints get `image_url`\n\n/`file`\n\nparts), so the model can actually *see* the file, not just a text summary.\nTune the caps via `limits`\n\n:\n\n```\nquery({ prompt, workspace, llm, limits: { maxTokens: 25000, maxImageBytes: 3_750_000, maxPdfPages: 20 } })\n```\n\nPass a subset, or your own `Tool[]`\n\n, via `tools:`\n\n:\n\n``` js\nimport { readFile, writeFile, editFile } from 'anyclaude-sdk'\n\nquery({ prompt, workspace, llm, tools: [readFile, writeFile, editFile] })\n```\n\nA user turn beginning with `/`\n\nis intercepted. Built-ins: `/help`\n\n, `/clear`\n\n,\n`/compact [focus]`\n\n(summarizes history to free context), `/tools`\n\n, `/cost`\n\n,\n`/model`\n\n. Define your own prompt-template commands:\n\n``` js\nimport { query, promptCommand } from 'anyclaude-sdk'\n\nquery({\n  prompt: promptStream, workspace, llm,\n  commands: [promptCommand('review', 'Review the diff', 'Review this code and list issues: $ARGUMENTS')],\n})\n// user types: /review src/app.ts\n```\n\nEnable with `background: true`\n\nto run sub-agents or long work off the critical\npath. The `task`\n\ntool gains `run_in_background`\n\n(returns a task id immediately),\nand `task_list`\n\n/ `task_output`\n\n/ `task_stop`\n\ntools let the agent poll them.\nOptional off-main-thread execution via a Comlink worker harness\n(`exposeBackgroundWorker`\n\n/ `wrapWorker`\n\n); the in-thread manager works without it.\n\n```\nquery({ prompt, workspace, llm, agents: {}, background: true })\n```\n\nTwo halves: **Comlink** for main→worker control (`wrapWorker`\n\n/ `exposeBackgroundWorker`\n\n,\nabove), and ** BroadcastChannelMailbox** so agents in\n\n*different*workers gossip mailbox-style. It's a drop-in\n\n`Mailbox`\n\n, so the existing `team`\n\ntools\n(`send_message`\n\n/ `dispatch_tasks`\n\n) work unchanged across workers:\n\n``` js\nimport { BroadcastChannelMailbox } from 'anyclaude-sdk'\n\n// inside each Web Worker / tab / worker_thread, same channel name:\nconst mailbox = new BroadcastChannelMailbox({ channelName: 'team', origin: 'planner' })\nquery({ prompt, workspace, llm, team: true, mailbox })\n// messages sent by one worker land in the addressed agent's inbox in another.\n```\n\nUses the global `BroadcastChannel`\n\nby default. For durable cross-tab delivery\n(IndexedDB/localStorage fallbacks, older browsers, Node) use the one-call helper\n— it's backed by the bundled [ broadcast-channel](https://www.npmjs.com/package/broadcast-channel)\npackage, lazy-imported so it stays out of bundles that don't use it:\n\n``` js\nconst mailbox = await BroadcastChannelMailbox.crossTab({ channelName: 'team', origin: 'planner' })\nquery({ prompt, workspace, llm, team: true, mailbox })\n```\n\n**Push delivery to a running agent.** Messages addressed to an agent are\nauto-injected into its transcript at the next turn boundary — same model as the\nmessage queue, but from the shared mailbox. So a coordinator (or peer, or another\nworker) can redirect a **running** sub-agent mid-task and it lands on the\nsub-agent's next tool round, no polling tool needed. `dispatch_tasks`\n\nnames each\nworker `worker:<taskId>`\n\nso you can target a specific one:\n\n```\nmailbox.send('coordinator', 'worker:task_1', 'while you work: also add logging')\n// worker:task_1 sees \"[Team messages] - from coordinator: ...\" on its next step.\n```\n\nOn by default with `team: true`\n\n; opt out via `query({ deliverTeamMessages: false })`\n\n.\n\nYou aren't tied to WebContainer. A `Sandbox`\n\nis just a `FileSystem`\n\nplus a\n`CommandExecutor`\n\n, and you can mix and match.\n\nAdapters wrap each provider's client structurally (no hard dependency on their SDKs — install only the one you use):\n\n``` js\nimport { E2BSandbox, VercelSandbox, DaytonaSandbox, CloudflareSandbox } from 'anyclaude-sdk'\n\n// e.g. E2B\nimport { Sandbox } from 'e2b'\nconst sbx = await Sandbox.create()\nconst workspace = new E2BSandbox(sbx)\n\nquery({ prompt, workspace, llm })\n```\n\nSupported: **WebContainer**, **E2B**, **Vercel Sandbox**, **Daytona**,\n**Cloudflare Sandbox**, and **LocalSandbox** (real OS). All implement the same\n`Sandbox`\n\ninterface.\n\nRun the agent directly against the host machine's filesystem and shell — like Claude Code — with automatic platform detection (Windows / macOS / Linux):\n\n``` js\nimport { LocalSandbox, createAnthropicClient, query } from 'anyclaude-sdk'\n\nconst workspace = new LocalSandbox({ cwd: '/path/to/project' }) // defaults to process.cwd()\nconst llm = createAnthropicClient({ baseUrl, model: 'claude-sonnet-4-6', apiKey })\n\nfor await (const msg of query({ prompt: 'add a CLI flag and run the tests', workspace, llm })) { /* … */ }\n```\n\nThe agent's working directory is taken from the sandbox automatically. See\n`examples/local-agent.mjs`\n\nfor a runnable headless demo. On Windows it uses\n`cmd.exe`\n\n; elsewhere `$SHELL`\n\n/`/bin/sh`\n\n(override via `shell`\n\n/`shellArgs`\n\n).\n\nFor a durable local filesystem in the browser, use a DB-backed FS and seed a\nstandard Linux tree. `DexieFileSystem`\n\n(IndexedDB) is the recommended default\n— persistent across reloads, indexed for fast `readdir`\n\n/`glob`\n\n, with metadata\n(mode, mtime, symlinks):\n\n``` js\nimport {\n  DexieFileSystem, OpfsFileSystem, seedLinuxTree, composeWorkspace, NoopCommandExecutor,\n} from 'anyclaude-sdk'\n\nconst fs = new DexieFileSystem('my-project-fs')   // or: new OpfsFileSystem()\nawait seedLinuxTree(fs)                            // /bin /etc /home/user /tmp /usr …\n\n// File-only agent (no shell):\nconst workspace = composeWorkspace(fs, new NoopCommandExecutor(), '/home/user')\n\n// …or pair a persistent FS with a remote shell:\n// const workspace = composeWorkspace(fs, new E2BSandbox(sbx), '/home/user')\n```\n\n`OpfsFileSystem`\n\n(Origin Private File System) is offered alongside Dexie for\nlarge-binary / native-handle scenarios; use `OpfsFileSystem.isSupported()`\n\nto\nfeature-detect.\n\nA `MemoryFileSystem`\n\nalso ships for tests:\n\n``` js\nimport { MemoryFileSystem, NoopCommandExecutor, composeWorkspace } from 'anyclaude-sdk'\n\nconst fs = new MemoryFileSystem()\nawait fs.writeFile('/app/index.ts', 'export const x = 1')\nconst workspace = composeWorkspace(fs, new NoopCommandExecutor())\n```\n\nDeclare reusable prompt-skills inline — each becomes a `/name`\n\nslash command and is invokable by the agent through the `skill`\n\ntool. `$ARGUMENTS`\n\nis substituted at call time:\n\n``` js\nimport { query, defineSkill } from 'anyclaude-sdk'\n\nquery({\n  prompt, workspace, llm,\n  skills: [\n    defineSkill({\n      name: 'changelog',\n      description: 'Summarize git changes into a changelog entry',\n      instructions: 'Write a concise changelog entry for: $ARGUMENTS',\n      argumentHint: '<since>',\n    }),\n  ],\n})\n```\n\nYou can also pass plain `Skill`\n\nobjects, or `skills: true`\n\nto load `.claude/skills/*.md`\n\nfrom the workspace.\n\nRun `query()`\n\nin a serverless function and stream `SDKMessage`\n\ns to the browser. For runs longer than the platform's time cap, checkpoint at a turn boundary and continue transparently in a fresh invocation:\n\n```\n// pause near the deadline, persist to the store, emit a `paused` message\nquery({ prompt, workspace, llm, sessionStore, maxDurationMs: 20_000 })\n// later — resume + continue the tool loop with NO new user message\nquery({ workspace, llm, sessionStore, resume: true, continueRun: true })\n```\n\nPluggable `SessionStore`\n\nadapters (all implement `SessionStoreLike`\n\n): `SessionStore`\n\n(IndexedDB), `MemorySessionStore`\n\n, `KVSessionStore`\n\n(Vercel KV / Upstash), `RedisSessionStore`\n\n, `PostgresSessionStore`\n\n(Neon / pg / postgres.js), `SupabaseSessionStore`\n\n.\n\nDeclare tools the **host** executes — e.g. run `bash`\n\nin the user's browser WebContainer while the agent loop runs on your server. The run pauses with a `client_tool_request`\n\n; the client executes it and you resume with the result:\n\n``` js\nimport { WORKSPACE_TOOL_NAMES } from 'anyclaude-sdk'\nquery({ prompt, llm, workspace, sessionId, clientTools: WORKSPACE_TOOL_NAMES })  // → emits client_tool_request + pauses\nquery({ llm, workspace, sessionId, resume: true, continueRun: true, clientToolResults })  // → continues\n```\n\nOn the browser side, `anyclaude-react`\n\nturns those into a ready executor map backed by **any** workspace — a WebContainer (real shell + files), the user's **IndexedDB** (`DexieFileSystem`\n\n), OPFS, or memory:\n\n``` js\nimport { createWebContainerClientTools, createWorkspaceClientTools } from 'anyclaude-react'\nuseAgent({ endpoint: '/api/agent', clientTools: createWebContainerClientTools(wc) })           // files + bash\nuseAgent({ endpoint: '/api/agent', clientTools: createWorkspaceClientTools(new DexieFileSystem('my-db')) }) // IndexedDB\n```\n\nProvide `onAskUser`\n\nand the agent gains an `ask_user_question`\n\ntool to put a decision to the user — multiple-choice **or free-text** (`inputType: 'choice' | 'text' | 'textarea' | 'number'`\n\n, plus `allowOther`\n\n). Hosts that only render choices can ignore the new fields:\n\n```\nquery({ prompt, workspace, llm, onAskUser: async ({ question, options, inputType }) =>\n  inputType && inputType !== 'choice' ? promptText(question) : pickOne(question, options) })\n```\n\nThe agent loop runs server-side, so your system prompt, tool instructions, and retrieved context live in the server→LLM request and **never reach the browser**. To also strip sensitive artifacts (reasoning, raw tool output / RAG, model identity) from the streamed messages, wrap the stream — a pure, opt-in output transform:\n\n``` js\nimport { projectMessages } from 'anyclaude-sdk'\nfor await (const m of projectMessages(query({ /* ... */ }), { preset: 'public' }))\n  res.write(JSON.stringify(m) + '\\n')\n```\n\n`paused`\n\nand `client_tool_request`\n\ncontrol messages are always preserved. (Note: anything that *runs in the browser* — `createAgentClient`\n\nmode — necessarily exposes its request; use the server/endpoint path when the prompt is proprietary.)\n\n```\nnpm install anyclaude-react\n```\n\n`useAgent()`\n\nplus restylable components — chat (`AgentChat`\n\n, `ChatPanel`\n\n, `Transcript`\n\n, `MarkdownMessage`\n\n, `Composer`\n\n, `Working`\n\n, `ToolCall`\n\n) and an IDE set (`Terminal`\n\n, `FileExplorer`\n\n, `CodeEditor`\n\n, `AskUser`\n\n). `createAgentClient`\n\n/ `createEndpointClient`\n\nauto-stitch `paused`\n\ncontinuations and run `clientTools`\n\nin the browser.\n\nStand up an Anthropic Messages API-compatible endpoint backed by any OpenAI-compatible model, so **Claude Code itself** (or any Anthropic-Messages client) runs against DeepSeek / Qwen / GLM / Kimi / local Ollama. Unlike a naive proxy, inline tool-call **dialects are recovered into proper tool_use blocks**, so tool use actually works on cheap models.\n\n``` js\nimport { createOpenAIClient } from 'anyclaude-sdk/llm'\nimport { anthropicToChat, anthropicSSE } from 'anyclaude-sdk/anthropic-endpoint'\n\nconst llm = createOpenAIClient({ baseUrl: 'https://api.deepseek.com/v1', model: 'deepseek-chat', apiKey })\n// POST /v1/messages:\nfor await (const evt of anthropicSSE(llm, anthropicToChat(body), { model: 'deepseek-chat' })) res.write(evt)\n// then: ANTHROPIC_BASE_URL=http://localhost:8787 claude\n```\n\nRunnable: [ examples/claude-code-router](/pipilot-dev/anyclaude-sdk/blob/main/examples/claude-code-router).\n\nFrontier models emit clean native function-calls; cheaper ones often don't. Three layers (in `anyclaude-sdk/llm`\n\n) close the gap: **tool-call dialects** (`parseToolCalls`\n\n— xml-function / hermes / json-fence), **auto-detected model profiles** (`profileForModel`\n\n— qwen/deepseek/moonshot/zhipu/mistral/llama), and **self-healing argument repair** (`query({ repairToolCalls })`\n\n, on by default — validates args and feeds the model a corrective tool_result instead of running with garbage). Prove it on your endpoints with [ scripts/compat-matrix.mjs](/pipilot-dev/anyclaude-sdk/blob/main/scripts/compat-matrix.mjs) →\n\n[COMPATIBILITY.md](/pipilot-dev/anyclaude-sdk/blob/main/COMPATIBILITY.md).\n\n```\nnpm create anyclaude-app@latest my-app   # template: bolt — WebContainer + chat + live preview, no backend\n```\n\nThe `bolt`\n\ntemplate wires `useWebContainerPreview({ wc })`\n\n(boot a dev server → live preview URL) + a browser-side `query()`\n\n+ the IDE components. See [ anyclaude-react](#react-ui-kit--anyclaude-react).\n\nKeep a large pool of rarely-used tools **out of the per-turn payload** (big savings on weak/uncached models) while staying discoverable + callable. Mark them deferred; `tool_search`\n\nindexes them and the loop **arms** a tool (sends its schema on subsequent turns) once search surfaces it — then it executes normally.\n\n```\nquery({ prompt, workspace, llm,\n  extraTools: [deploy, ...integrationTools],   // e.g. 35 integration tools\n  deferredTools: ['stripe_charge', 'supabase_query', /* … the niche ones */],\n})\n// or per-tool: defineTool({ name, description, parameters, run, defer: true })\n```\n\nOnly the lean core + `tool_search`\n\nare sent each turn; the model searches when it needs a niche tool, the SDK arms it, and the call goes through. Register 35, send ~10.\n\nOpt-in knobs for token cost and latency — especially on weak / uncached models:\n\n```\nquery({\n  prompt, workspace, llm,\n  systemPromptPreset: 'lean',      // ~70% shorter built-in prompt — saved every turn on uncached models\n  keepToolResults: 6,              // context editing: stub tool_results older than the last 6 (caps transcript growth)\n  parallelToolExecution: true,     // run a turn's read-only tool calls concurrently (~2× faster on multi-read turns)\n  deferredTools: [/* niche tools */], // keep rarely-used tools out of the payload until tool_search arms them\n})\n// custom read tool opting into parallelism:\ndefineTool({ name: 'get_logs', description: '…', parameters, run, parallelSafe: true })\n```\n\nMutating tools / `bash`\n\n/ delegated client tools always execute serially; `keepToolResults`\n\nand `parallelToolExecution`\n\npreserve correctness, just trim cost/latency.\n\n**Live compaction marker**—`autoCompact`\n\nemits a`compact_boundary`\n\nwith`status: 'start'`\n\n*before*summarizing (for a live \"compacting…\" shimmer) and`status: 'end'`\n\nafter with`post_tokens`\n\n.**Cancel a queued message**—`MessageQueue.push()`\n\nreturns a stable id;`remove(id)`\n\ncancels a single pending message (per-pill ✕ in a UI).**BYO LLM client**— reuse the SDK's wire codec:`toOpenAIMessages`\n\n,`consumeSSE`\n\n, and the LLM types from`anyclaude-sdk/llm`\n\n(no bare-root import in browser bundles).\n\nRunnable Vite projects in [ examples/](/pipilot-dev/anyclaude-sdk/blob/main/examples):\n\n**(WebContainer IDE — real shell + Node in the tab),**\n\n`browser-ide`\n\n`browser-chat`\n\n, `claude-code-router`\n\n, `vercel-kv-survivor`\n\n, `vercel-supabase-survivor`\n\n, `vercel-indexeddb-survivor`\n\n, **(server brain / browser hands). Try the**\n\n`vercel-clienttools`\n\n**.**\n\n[live demo](https://anyclaude-docs.puter.site/demo/)`query(options): AsyncGenerator<SDKMessage>`\n\n— main entry.`prompt: string | AsyncIterable<SDKUserMessage>`\n\n`workspace: FileSystem & CommandExecutor`\n\n`llm: LLMClient`\n\n`tools?`\n\n,`extraTools?`\n\n,`allowedTools?`\n\n/`disallowedTools?`\n\n,`deferredTools?`\n\n(lazy-load),`model?`\n\n,`systemPrompt?`\n\n/`appendSystemPrompt?`\n\n,`maxTurns?`\n\n(default 50),`cwd?`\n\n,`abortController?`\n\n- serverless:\n`sessionStore?`\n\n,`resume?`\n\n,`maxDurationMs?`\n\n,`continueRun?`\n\n- client tools:\n`clientTools?`\n\n,`clientToolResults?`\n\n; interactive:`onAskUser?`\n\n- also:\n`mcpServers?`\n\n,`agents?`\n\n,`commands?`\n\n,`hooks?`\n\n,`background?`\n\n,`team?`\n\n,`memory?`\n\n,`permissionMode?`\n\n/`canUseTool?`\n\n,`messageQueue?`\n\n`createOpenAIClient`\n\n/`createAnthropicClient`\n\n/`createResponsesClient`\n\n`WebContainerWorkspace`\n\n,`MemoryFileSystem`\n\n,`NoopCommandExecutor`\n\n,`LocalSandbox`\n\n,`composeWorkspace`\n\n`defineTool`\n\n(custom tools),`projectMessages`\n\n(server-side stream redaction)`ALL_CLAUDE_CODE_TOOLS`\n\n, individual tools,`toolDefs`\n\n,`toolByName`\n\n- browser-clean subpaths:\n`anyclaude-sdk/{query,loop,llm,fs,workspace,tools,session,memory,compact,permissions,skills,queue,prompt,anthropic-endpoint,telemetry}`\n\n`anyclaude-sdk/llm`\n\n:`parseToolCalls`\n\n+ dialects,`profileForModel`\n\n(model profiles),`validateToolArguments`\n\n(repair),`toOpenAIMessages`\n\n/`consumeSSE`\n\n(BYO-client codec)`anyclaude-sdk/anthropic-endpoint`\n\n:`anthropicToChat`\n\n,`anthropicSSE`\n\n,`streamResultToAnthropicMessage`\n\n(Claude-Code router)`runToolLoop`\n\n(`/loop`\n\n),`compactWithWindow`\n\n(`/compact`\n\n),`track`\n\n/`telemetryEnabled`\n\n(`/telemetry`\n\n)- All\n`SDK*`\n\nmessage types,`ContentBlockParam`\n\n,`LLMClient`\n\n,`ToolDef`\n\n,`SessionStoreLike`\n\n, etc.\n\n| Feature | Official SDK | anyclaude-sdk |\n|---|---|---|\n| Auth | OAuth token | None required |\n| Backend | claude.ai API | Any OpenAI/Anthropic endpoint |\n| Runtime | Node only | Browser, Node, Bun |\n| File ops | Native filesystem | Pluggable (WebContainer / Memory / IndexedDB / local) |\n| Commands | Native shell | jsh (WebContainer) / local / client-side tools |\n| MCP / slash commands / background tasks / sub-agents | Built-in | Built-in |\n| Serverless survivor + prompt projection | — | Built-in |\n\nThe SDK emits **anonymous, opt-out** usage telemetry (SDK version, runtime, a coarse model-family bucket, and which features are used) — never code, prompts, repo identity, paths, or keys. It sends to an aggregate-only collector (a Puter Worker; source in [ examples/telemetry-collector](/pipilot-dev/anyclaude-sdk/blob/main/examples/telemetry-collector)). Disable with\n\n`ANYCLAUDE_TELEMETRY=0`\n\n, `DO_NOT_TRACK=1`\n\n, or `query({ disableTelemetry: true })`\n\n; repoint with `ANYCLAUDE_TELEMETRY_URL`\n\n(or set it to `''`\n\nto send nowhere). Full disclosure: [TELEMETRY.md](/pipilot-dev/anyclaude-sdk/blob/main/TELEMETRY.md).\n\nMIT", "url": "https://wpnews.pro/news/show-hn-anyclaude-sdk-claude-code-style-sdk-for-openai-anthropic-endpoints", "canonical_source": "https://github.com/pipilot-dev/anyclaude-sdk", "published_at": "2026-07-26 22:27:22+00:00", "updated_at": "2026-07-26 22:52:40.230126+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "large-language-models", "generative-ai"], "entities": ["Anyclaude-SDK", "OpenAI", "Anthropic", "WebContainer", "npm", "Bun"], "alternates": {"html": "https://wpnews.pro/news/show-hn-anyclaude-sdk-claude-code-style-sdk-for-openai-anthropic-endpoints", "markdown": "https://wpnews.pro/news/show-hn-anyclaude-sdk-claude-code-style-sdk-for-openai-anthropic-endpoints.md", "text": "https://wpnews.pro/news/show-hn-anyclaude-sdk-claude-code-style-sdk-for-openai-anthropic-endpoints.txt", "jsonld": "https://wpnews.pro/news/show-hn-anyclaude-sdk-claude-code-style-sdk-for-openai-anthropic-endpoints.jsonld"}}