Contextwaise

Contextwaise is in early access: API keys and the dashboard open at launch. Join the waitlist to get yours first.

Docs · Anthropic SDK›
Agent SDK

Anthropic SDK

You run the agent loop with the Anthropic SDK; one call turns Claude's tool calls into the tool results to send back, each one shrunk the moment the tool returns.

Setup#

Add the expand tool to your tools, and replace the code that runs tools and builds tool_result blocks with one call:

import Anthropic from "@anthropic-ai/sdk";
import { createContextwaise } from "contextwaise";
import { expandTool, toolResults } from "contextwaise/anthropic";

const client = new Anthropic();
// Reads CONTEXTWAISE_API_KEY and CONTEXTWAISE_URL from the environment.
const cw = createContextwaise({ agent: "support-triage", budgets: { read_file: 1500 } });

const messages: Anthropic.MessageParam[] = [{ role: "user", content: question }];
while (true) {
  const response = await client.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 4096,
    tools: [...myTools, expandTool],
    messages,
  });
  messages.push({ role: "assistant", content: response.content });
  if (response.stop_reason !== "tool_use") break;

  // Runs your tools, shrinks their outputs, answers expand.
  const results = await toolResults(cw, response, runTool, { request: question });
  messages.push({ role: "user", content: results });
}

Your tools stay as they are. The call runs them through a function you give it:

// Your tools: return the raw output, text or any JSON value. Throw to report a failure.
async function runTool(name: string, args: Record<string, unknown>) {
  if (name === "read_file") return readFile(String(args.path), "utf8");
  if (name === "search_logs") return searchLogs(String(args.query));
  throw new Error(`unknown tool ${name}`);
}

What the call does#

For every tool_use block in the response, in order:

  • Runs your tool with the arguments Claude chose, and keeps the raw output on your side.
  • Shrinks the output for the model: noise removed, the relevant parts kept within the tool's budget. Small results, and results where a cut would save too little (under 1,000 tokens or 30%), pass whole.
  • Answers expand itself, from the original output, so trimmed parts are one call away. It never reaches your tools.
  • Reports failures: a tool that throws becomes a tool_result with is_error and the message, so Claude can react instead of your loop crashing.

It returns the tool_result blocks, in the order of the calls, ready for the next user message. The history keeps the shrunk view, so later turns resend the same bytes and the prompt cache keeps hitting.

The request#

Relevance is judged against what the agent is after. The call combines the text Claude wrote before the tool calls with request: pass the user's request for the turn. Without it, only the tool's arguments and Claude's text are used.

Parallel tools#

When Claude asks for several tools in one response, they run in parallel. runTool can be sync or async.

Serverless and scripts#

Dashboard reports are sent in the background. In a short-lived process, await cw.flush() before it ends.

Note
The adapter reads the SDK's response objects (or plain objects); the Anthropic SDK isn't a dependency of contextwaise. All options are on Options; for other loops, see the Core API.