← Blog

How to Build an AI Coding Assistant with Claude API

2026-09-27 · 5 min read · SubToAPI Team

Building an AI coding assistant with the Claude API means wiring three things together: a prompt structure that gives Claude enough context about the codebase, a tool-use loop that lets Claude read files and run commands instead of guessing, and a way to stream responses back to whatever editor or CLI your users are working in. This guide walks through each piece with working code, then covers what changes when you move from a weekend prototype to something your team actually relies on.

The short answer for developers who just want a working setup: use the Messages API with tool calling for file operations, stream the output for responsiveness, and keep the system prompt focused on the specific language/framework context rather than trying to make the model universally smart. Everything below expands on how.

Core architecture of a coding assistant

A Claude-based coding assistant is not a single API call — it's a loop. The model asks for information (read this file, list this directory, run these tests), your code executes that action locally, and you feed the result back. This continues until Claude has enough context to produce an answer or a diff.

The minimal loop looks like this:

  1. Send the user's request plus a system prompt describing the project.
  2. Claude responds with either a final answer or a tool_use block.
  3. Your code executes the requested tool (read file, grep, run linter) and returns the result.
  4. Repeat until Claude stops requesting tools.

This pattern is the same one used by CLI coding agents — the model doesn't need blind trust, it needs verifiable access to your actual files.

Defining tools for file access

Claude's tool use lets you describe functions in JSON schema and have the model call them with structured arguments. For a coding assistant, you typically need at least three tools: read a file, list a directory, and write/patch a file.

const tools = [
  {
    name: "read_file",
    description: "Read the contents of a file at the given path",
    input_schema: {
      type: "object",
      properties: {
        path: { type: "string", description: "Relative path from project root" }
      },
      required: ["path"]
    }
  },
  {
    name: "list_directory",
    description: "List files and folders in a directory",
    input_schema: {
      type: "object",
      properties: { path: { type: "string" } },
      required: ["path"]
    }
  },
  {
    name: "write_file",
    description: "Write or overwrite a file with new content",
    input_schema: {
      type: "object",
      properties: {
        path: { type: "string" },
        content: { type: "string" }
      },
      required: ["path", "content"]
    }
  }
];

When Claude returns a tool_use block, you execute the corresponding local function and send the result back as a tool_result message. Keep tool responses concise — truncate large files or return only relevant line ranges, since dumping an entire 3,000-line file back into context burns tokens fast for no benefit.

For a full walkthrough of the request/response shapes, see /docs/tools.

Handling the conversation loop

Here's a simplified version of the loop in JavaScript, assuming you're calling Claude directly:

async function runAssistant(userMessage, systemPrompt) {
  let messages = [{ role: "user", content: userMessage }];

  while (true) {
    const response = await callClaude(messages, systemPrompt, tools);

    if (response.stop_reason !== "tool_use") {
      return response.content; // final answer
    }

    const toolUse = response.content.find(b => b.type === "tool_use");
    const result = await executeTool(toolUse.name, toolUse.input);

    messages.push({ role: "assistant", content: response.content });
    messages.push({
      role: "user",
      content: [{ type: "tool_result", tool_use_id: toolUse.id, content: result }]
    });
  }
}

The executeTool function is where your actual security boundary lives — validate paths, restrict to the project root, and never let the model write outside a sandboxed directory without explicit confirmation.

Streaming for editor integration

If your assistant runs inside an editor extension or a terminal UI, streaming matters more than raw speed — users need to see tokens arrive so the tool feels responsive during longer generations (explaining a bug, writing a function, refactoring a module).

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-3-5-sonnet",
    "stream": true,
    "max_tokens": 1024,
    "system": "You are a coding assistant for a Python/Django project.",
    "messages": [{"role": "user", "content": "Explain why this migration fails."}]
  }'

Parse the SSE stream and render tokens as they arrive rather than waiting for the full response — this alone makes the assistant feel dramatically faster even when total latency is unchanged. Details on chunk format are in /docs/streaming.

Turning a prototype into an internal tool

A prototype that calls Claude directly with a hardcoded key works fine for one developer. It breaks down the moment you want to:

This is the point where routing your coding assistant through SubToAPI makes sense. It sits on top of your existing Claude access and gives you sub_live_... application keys, so each internal tool, CI job, or teammate gets an isolated key with its own usage metadata — without touching your underlying Claude credentials. Streaming, tool use, and the standard Messages format all work the same way, so the loop above needs zero rewriting.

Setup is a signup and a key swap: create an account at /signup, generate a key from the dashboard, and follow /docs/quickstart to point your existing callClaude function at the new endpoint. Plans start at Solo (€9) for individual use, with Team (€19/seat) and Scale (€49/seat) adding multi-key management for when the assistant becomes something your whole team depends on — see /pricing for details.

Practical tips for coding assistants specifically

questions

Do I need the full Anthropic SDK to build a coding assistant? No — the Messages API is plain HTTPS with JSON, so curl, fetch, or any HTTP client works. An SDK just wraps the same requests shown in /docs/messages.

How do I stop the assistant from making unlimited tool calls? Track a call counter in your loop and break after a fixed limit, returning a partial answer or asking the user for more direction instead of looping silently.

Can multiple developers share one Claude subscription for this? Yes — routing requests through SubToAPI gives each developer or service a separate sub_live_... key with its own usage tracking, so you avoid sharing one raw credential across a team.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →