Claude API Coding Assistant: A Step-by-Step Tutorial
If you want to build a Claude API powered coding assistant — something that reads your files, suggests edits, runs commands, and streams its reasoning back to you — you need three things working together: a tool-use loop, a streaming interface, and a context strategy that doesn't blow your token budget on every request. This tutorial walks through all three with working code.
This isn't a "paste a prompt into a chat window" guide. By the end you'll have a CLI-style assistant that can call functions (read a file, write a file, run a shell command), see the results, and keep working until the task is done — the same pattern behind tools like Claude Code itself.
What a coding assistant actually needs
A coding assistant is not a single API call. It's a loop:
- Send the user's request plus available tools to Claude.
- Claude either answers directly or asks to call a tool (e.g.,
read_file). - Your code executes that tool locally and returns the result.
- Claude uses the result to decide the next step — more tool calls, or a final answer.
This loop repeats until Claude stops requesting tools. The model never touches your filesystem directly; your application code does, under instructions from the model.
Step 1: Define your tools
Start small. A minimal coding assistant needs file I/O and the ability to run tests or linters.
{
"tools": [
{
"name": "read_file",
"description": "Read the contents of a file at a given path",
"input_schema": {
"type": "object",
"properties": {
"path": { "type": "string" }
},
"required": ["path"]
}
},
{
"name": "write_file",
"description": "Write content to a file, overwriting existing contents",
"input_schema": {
"type": "object",
"properties": {
"path": { "type": "string" },
"content": { "type": "string" }
},
"required": ["path", "content"]
}
},
{
"name": "run_command",
"description": "Run a shell command and return stdout/stderr",
"input_schema": {
"type": "object",
"properties": {
"command": { "type": "string" }
},
"required": ["command"]
}
}
]
}
Keep tool descriptions precise. Claude's decision to call write_file instead of just describing the change depends entirely on how clearly the tool is defined. Vague descriptions produce vague tool use.
Step 2: Write the system prompt
The system prompt should set scope and guardrails, not personality:
You are a coding assistant with access to the user's local project files.
- Always read a file before editing it.
- Make the smallest change that solves the problem.
- After editing, run relevant tests if a test command is available.
- Explain what you changed and why, in plain language, after tool calls finish.
Coding assistants fail most often not because the model is wrong, but because the loop lacks constraints — it edits files it was never asked to touch, or skips reading before writing. Put the guardrails in the system prompt, not just the task prompt.
Step 3: Build the tool-use loop
Here's a simplified Node.js loop against Claude's Messages API:
async function runAssistant(userMessage, tools, executeTool) {
let messages = [{ role: "user", content: userMessage }];
while (true) {
const response = await callClaude(messages, tools);
if (response.stop_reason !== "tool_use") {
return response.content; // final answer
}
const toolResults = [];
for (const block of response.content) {
if (block.type === "tool_use") {
const result = await executeTool(block.name, block.input);
toolResults.push({
type: "tool_result",
tool_use_id: block.id,
content: result,
});
}
}
messages.push({ role: "assistant", content: response.content });
messages.push({ role: "user", content: toolResults });
}
}
executeTool is where you actually read files, write files, or run shell commands — with sandboxing and path restrictions, since Claude will only ever call what you let it call. Full details on request/response shapes for tool use are in /docs/tools.
Step 4: Stream the output
Tool calls happen mid-stream too, so your UI needs to show partial text as it arrives rather than waiting for the full response. Streaming matters more for coding assistants than for chatbots because responses are longer — multi-file diffs, explanations, command output.
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4",
"stream": true,
"max_tokens": 2048,
"tools": [...],
"messages": [{ "role": "user", "content": "Fix the off-by-one error in parser.js" }]
}'
If you're building the assistant as a product rather than a personal CLI, you'll want streaming, tool use, and usage metadata available on the same API key without wiring up separate billing per developer. That's exactly what SubToAPI provides — it turns your existing Claude access into an HTTPS API with sub_live_ keys, SSE streaming, and tool support out of the box. See /docs/streaming for the event format and /docs/quickstart to get a key running in a few minutes.
Step 5: Manage context without blowing the budget
Coding assistants tend to accumulate huge message histories — full file contents, tool results, diffs. Three practical rules:
- Don't re-send unchanged files. Cache file contents client-side and only re-read when you know they changed.
- Truncate tool output. A
run_commandresult with 10,000 lines of log output should be summarized or trimmed before it goes back to Claude. - Summarize old turns. After a few rounds, compress completed sub-tasks into a one-line summary instead of keeping the full transcript.
This keeps latency and cost predictable, especially if multiple developers on a team are running the assistant concurrently against the same account.
Step 6: Handle errors and retries
Tool execution will fail — a file won't exist, a command will exit non-zero. Return the error as the tool result instead of throwing:
try {
return fs.readFileSync(path, "utf-8");
} catch (err) {
return `Error reading file: ${err.message}`;
}
Claude handles error strings in tool results reasonably well — it will often retry with a corrected path or ask the user for clarification, which is exactly the behavior you want from an assistant instead of a hard crash.
Putting it together
A minimal but real coding assistant is: a system prompt with guardrails, three or four well-described tools, a loop that executes tool calls and feeds results back, streaming for responsiveness, and context trimming so long sessions don't get expensive. Everything else — syntax highlighting, diff viewers, undo — is UI polish on top of that core loop.
If you're prototyping this for a team rather than just yourself, check /pricing for how seat-based plans work once more than one developer needs API access under the same setup.
questions
Do I need the full Claude API, or can I use a simpler proxy? For tool use and streaming you need an endpoint that supports the Messages API shape with tools and stream. A managed layer like SubToAPI gives you that without setting up your own gateway — see /docs/messages for the request format.
Can the assistant edit files directly, or does it just suggest changes? That's entirely up to your executeTool implementation. Claude only requests tool calls; your code decides whether to apply them automatically or ask for confirmation first.
How do I keep costs predictable with long coding sessions? Trim tool output before sending it back, cache unchanged file contents, and summarize completed steps instead of keeping the full transcript — token usage, not request count, is what drives cost in a coding assistant loop.