From agent-sdk-pro
Use this skill when developing TypeScript applications with the Claude Agent SDK (@anthropic-ai/claude-agent-sdk), implementing query() calls, configuring SDK options, handling streaming message iteration, working with SDKMessage and SDKResultMessage types, managing AbortSignal, passing custom env variables, or setting up the single-point-of-contact types file for SDK imports.
How this skill is triggered — by the user, by Claude, or both
Slash command
/agent-sdk-pro:sdk-typescript-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Always centralize all SDK imports in one `types.ts` file. This prevents scattered `@anthropic-ai/claude-agent-sdk` imports and provides a single place to add type guards and helpers.
Always centralize all SDK imports in one types.ts file. This prevents scattered @anthropic-ai/claude-agent-sdk imports and provides a single place to add type guards and helpers.
// types.ts — single point of contact with the SDK package
export type {
HookCallback,
HookInput,
HookJSONOutput,
Options,
PostToolUseHookInput,
PreToolUseHookInput,
SDKMessage,
SDKResultMessage,
} from "@anthropic-ai/claude-agent-sdk";
export { query } from "@anthropic-ai/claude-agent-sdk";
// Type guards
export function isResultMessage(message: SDKMessage): message is SDKResultMessage {
return message.type === "result";
}
Use query() directly — no wrappers. The SDK returns an AsyncIterable<SDKMessage>.
import { query, type SDKMessage } from "./types";
const messageStream: AsyncIterable<SDKMessage> = query({
prompt: userPrompt,
options: {
model: "claude-sonnet-4-6",
systemPrompt: params.systemPrompt,
allowedTools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"],
permissionMode: "bypassPermissions",
maxTurns: 50,
maxBudgetUsd: 2,
cwd: params.workingDirectory,
abortController,
sessionId: requestId,
env: { ...process.env, ANTHROPIC_BASE_URL: proxyUrl, ANTHROPIC_AUTH_TOKEN: jwtToken },
hooks: {
PreToolUse: [{ matcher: "Write|Edit", hooks: [fileRestrictionHook] }],
PostToolUse: [{ matcher: "Bash", hooks: [testPruneHook] }],
},
},
});
Iterate the stream and act only on the result message:
for await (const message of messageStream) {
// log every message for visibility
messageLogger.logMessage(message);
if (isResultMessage(message)) {
// message.duration_ms, message.total_cost_usd, message.modelUsage
const code = readFileSync(params.testFilePath, "utf8");
return { code, costUsd: message.total_cost_usd };
}
}
// Fallback: stream ended without result message
return { code: "" };
Always create an AbortController internally. Forward external AbortSignal to it:
const abortController = new AbortController();
if (params.abortSignal) {
params.abortSignal.addEventListener("abort", () => abortController.abort());
}
// Pass to query() options
{ abortController }
Check signal.aborted at the start of every hook callback.
Always check is_error and handle all subtypes:
if (isResultMessage(message)) {
if (message.is_error) {
switch (message.subtype) {
case "error_max_turns": throw new Error("Agent hit turn limit");
case "error_max_budget_usd": throw new Error("Agent exceeded budget");
case "error_during_execution": throw new Error("Agent execution failed");
case "error_max_structured_output_retries": throw new Error("Output validation failed");
}
}
// Check what was blocked
if (message.permission_denials?.length) {
console.warn("Blocked calls:", message.permission_denials.map(d => d.tool_name));
}
}
See references/query-options.md for the full options object documentation including canUseTool, disallowedTools, permissionMode values, and settingSources.
See references/message-types.md for SDKMessage variant details including permission_denials and all error subtypes.
Define all SDK tuning constants in a dedicated *.const.ts file:
export const SDK_MODEL = "claude-sonnet-4-6" as const;
export const SDK_MAX_BUDGET_USD = 2;
export const SDK_MAX_TURNS = 50;
export const SDK_PERMISSION_MODE = "bypassPermissions" as const;
export const SDK_ALLOWED_TOOLS = ["Read", "Write", "Edit", "Bash", "Glob", "Grep"] as const;
Prepend run metadata to output files after SDK completes:
function buildMetadataComment(result: SDKResultMessage): string {
return [
"/**",
" * @generated Agent SDK",
` * @duration ${(result.duration_ms / 1000).toFixed(1)}s`,
` * @cost $${result.total_cost_usd.toFixed(4)}`,
" */",
].join("\n") + "\n";
}
Pass custom env to the SDK for proxy URLs and auth tokens:
env: {
...process.env,
ANTHROPIC_BASE_URL: this.globalConfigService.getAnthropicProxyUrl(),
ANTHROPIC_AUTH_TOKEN: await this.authStorage.getJWTToken(),
ANTHROPIC_CUSTOM_HEADERS: `x-request-id: ${requestId}`,
}
npx claudepluginhub itamarzand88/claude-code-agentic-engineering --plugin agent-sdk-proGuides programmatic control of Claude Code sessions via Claude Agent SDK in TypeScript/JavaScript or Python. Supports custom agents, tools, streaming, and event handling for building AI agents.
Implements Anthropic Claude Agent SDK for autonomous agents, subagents, tool orchestration, MCP servers, and multi-step workflows. Useful for session management, permissions, and errors like CLI not found or context exceeded.
Builds autonomous AI agents using the Claude Agent SDK (TypeScript and Python). Covers hooks, MCP servers, sandbox, session management, and structured outputs.