Kimi API Tool Calling: Python, Node.js & 8/8 Test

Kimi API tool calling lets a model select a function and return its arguments as JSON. Your application—not the model—must validate those arguments, run the function, attach the result to the conversation and ask Kimi for the final answer.

We tested that selection step with kimi-k3 on eight frozen synthetic cases. K3 returned the exact expected tool and arguments in 8/8 single-pass calls. The test used no real inventory, payment, calendar, file or currency system, so it does not prove that a production integration is safe or reliable.

Tested versus documented: The 8/8 result below is independently observed. The multi-turn implementation flow, schema rules and message ordering are checked against Kimi’s current official documentation. We did not run a new API call for this guide, and the downloadable examples were tested offline only.

The short implementation pattern

  1. Describe each permitted function in the request’s tools array using the documented JSON Schema structure.
  2. Send the user message and tools to POST /v1/chat/completions.
  3. If the response has finish_reason: "tool_calls", keep the complete assistant message.
  4. Parse every function.arguments string as JSON, then validate the tool name, fields, types and values before execution.
  5. Execute only an allowlisted server-side handler with the minimum required permission.
  6. Add one role: "tool" message per call, preserving the matching tool_call_id.
  7. Send the expanded message history back to Kimi and stop only when the model returns a normal answer or your application reaches a strict round limit.

Kimi’s official tool-calling guide documents this six-stage model-to-application loop. Its Tool Use reference describes the accepted function structure and schema constraints.

What tool calling does—and does not do

StageKimi’s roleYour application’s role
Tool definitionReads names, descriptions and parameter schemasExposes only approved tools with narrow schemas
Tool selectionChooses zero, one or multiple toolsDecides whether the choice is allowed
ArgumentsReturns serialized JSON in function.argumentsParses and validates the JSON as untrusted input
ExecutionDoes not run your local or third-party functionRuns the handler with authorization, timeouts and audit controls
Tool resultReads the returned resultMatches it to the original tool_call_id
Final responseProduces the user-facing answerEnforces loop, cost and side-effect limits

This page covers developer-defined function tools. Kimi’s hosted Formula tools, such as official web search, have a separate discovery, execution and billing flow described in the official tools guide.

Define a narrow Kimi tool

This is one of the exact synthetic schemas used in our pilot:

{
  "type": "function",
  "function": {
    "name": "calculate_shipping",
    "description": "Calculate synthetic shipping for a postal code, package weight and priority flag.",
    "parameters": {
      "type": "object",
      "properties": {
        "postal_code": { "type": "string" },
        "weight_kg": { "type": "number" },
        "priority": { "type": "boolean" }
      },
      "required": ["postal_code", "weight_kg", "priority"],
      "additionalProperties": false
    }
  }
}

The current Tool Use reference says function.strict defaults to true when omitted and requires a supported MFJS/JSON Schema subset. Our frozen definitions omitted the explicit strict field, so they relied on that documented default. We still checked the returned arguments independently rather than treating schema enforcement as an application security boundary.

Good tool definitions are deliberately small:

  • use a unique, descriptive function name;
  • state when the tool should be selected;
  • require every field needed for safe execution;
  • use exact data types and enums or patterns where they materially reduce ambiguity;
  • reject unexpected fields with additionalProperties: false where supported; and
  • split read and write operations so a low-risk lookup cannot silently become a mutation.

Python: validate before dispatch

Kimi documents OpenAI-compatible Chat Completions and the SDK base URL https://api.moonshot.ai/v1. The full downloadable Python example requires an explicit --live flag, caps tool rounds and dispatches only synthetic local handlers.

The critical section is the trust boundary:

choice = response.choices[0]

if choice.finish_reason == "tool_calls":
    messages.append(choice.message.model_dump(exclude_none=True))

    for tool_call in choice.message.tool_calls:
        name = tool_call.function.name
        arguments = json.loads(tool_call.function.arguments)
        validate_arguments(name, arguments, tool_schemas)
        result = SYNTHETIC_HANDLERS[name](arguments)

        messages.append({
            "role": "tool",
            "tool_call_id": tool_call.id,
            "name": name,
            "content": json.dumps(result),
        })

Never index a handler map with an unchecked model-supplied name. validate_arguments in the kit confirms the allowlist, required fields, extra fields, types, patterns and minimum values before the handler is called.

Node.js: preserve the assistant message

The Node.js example uses the same control flow with the raw HTTP endpoint. It deliberately pushes the complete assistant message before any tool result:

const choice = completion.choices[0];

if (choice.finish_reason === "tool_calls") {
  messages.push(choice.message);

  for (const toolCall of choice.message.tool_calls) {
    const name = toolCall.function.name;
    const args = JSON.parse(toolCall.function.arguments);
    validateArguments(name, args, toolDefinitions);
    const result = executeSyntheticTool(name, args);

    messages.push({
      role: "tool",
      tool_call_id: toolCall.id,
      name,
      content: JSON.stringify(result),
    });
  }
}

Kimi’s guide specifically warns that omitting the returned assistant message can produce tool_call_id not found. It also requires every returned call to have a matching role: "tool" message with the correct ID.

Our K3 tool-selection result

We froze six non-operative function definitions and eight prompts before the first paid response. Each case was a separate streamed request to the official Global Kimi Open Platform using kimi-k3, reasoning_effort: high, a 2,048-token completion cap, concurrency one and zero corrective retries.

CasePrompt boundaryExpected and observed toolExact argumentsResult
TC01Inventory lookupinventory_lookup{"sku":"ORB-17"}Pass
TC02Standard shippingcalculate_shipping{"postal_code":"02139","weight_kg":2.5,"priority":false}Pass
TC03Dated reviewschedule_review{"project_id":"ALTA-42","date":"2032-09-17"}Pass
TC04Exact filenamefind_document{"filename":"field-notes-v3.md"}Pass
TC05Integer quantityadd_line_item{"sku":"NOVA-8","quantity":4}Pass
TC06Currency fieldsconvert_currency{"amount":125.5,"currency_from":"USD","currency_to":"EUR"}Pass
TC07Case-sensitive SKUinventory_lookup{"sku":"teal-9x"}Pass
TC08Priority and UK postcodecalculate_shipping{"postal_code":"SW1A 1AA","weight_kg":0.75,"priority":true}Pass

Every case met all six frozen checks: exactly one tool call, exact tool name, valid JSON, exact values, exact types and no extra arguments. Across the eight calls, the API reported 5,001 prompt tokens, 2,560 cached tokens and 915 completion tokens. The usage-derived cost assigned to this track was $0.021816 at the dated rates used by the parent K3 pilot.

This track was part of our broader Kimi K3 API pilot, not a new run for this article.

What 8/8 does not establish

The result is useful but narrow:

  • each case ran once, so it does not measure repeatability or failure rate;
  • all functions and data were synthetic and non-operative;
  • prompts requested exactly one tool, so parallel or dependent multi-tool execution was not scored;
  • the pilot scored selection and arguments, not a second-round final answer;
  • no malformed tool result, timeout, rate-limit or adversarial prompt was injected;
  • no production authorization, idempotency or rollback system was tested; and
  • results from one model, account, region and date should not be generalized to every Kimi model or later release.

An 8/8 single pass supports the statement “K3 matched these eight frozen cases once.” It does not support “Kimi tool calling is 100% reliable.”

Streaming: assemble arguments by call index

Our pilot used streaming. The TC01 response delivered the tool name first and split {"sku":"ORB-17"} across several later deltas. Parsing each fragment as complete JSON would have failed.

Kimi’s streaming documentation says the initial tool-call chunk carries the ID and function name, later chunks carry argument fragments, and multiple calls are separated by index. A streaming implementation should therefore:

  1. maintain one accumulator per choice and tool-call index;
  2. concatenate argument fragments in arrival order;
  3. wait for the completed call before parsing its JSON;
  4. retain each tool-call ID and name; and
  5. never execute a partially assembled argument object.

The downloadable examples use non-streaming responses for a smaller, auditable implementation. The evidence sample shows the assembled output from the real streamed call.

Safe execution controls

Before a model-selected function can affect an external system, add controls outside the model:

  • Allowlist: reject any unknown function name.
  • Schema validation: reject missing, extra or incorrectly typed fields.
  • Authorization: check the user’s permission for the exact resource and action.
  • Confirmation: require explicit confirmation for payments, deletion, publishing or account changes.
  • Idempotency: attach a stable operation key so retries cannot duplicate a write.
  • Limits: cap rounds, calls, arguments, execution time and total API spend.
  • Isolation: run parsers and code tools in a constrained environment.
  • Audit: record request IDs and sanitized metadata, not secrets or unnecessary personal data.
  • Failure handling: return a bounded error object to the model; do not retry a side effect blindly.

Treat model-generated arguments exactly like untrusted user input. A valid JSON object can still request an unauthorized or harmful action.

Download the reproducibility kit

Download the Kimi API Tool Calling Kit. It contains:

  • the six frozen function definitions and eight test cases;
  • complete Python and Node.js examples with explicit live-run gates;
  • offline unit tests and an evidence validator;
  • a sanitized TC01 request/response example;
  • the derived 8/8 case ledger with usage, cost and timing fields; and
  • a file manifest plus SHA-256 checksums.

Package SHA-256: 8F06B74DE14B29E618A500EC5FA86EA9E92C6A3245779CA2FCA6BEC76586A882 Package size: 19,453 bytes

The ZIP contains no API key, authorization header, raw account balance or private response headers. Running either live example can incur API cost; offline tests do not contact Kimi.

Run the offline checks

After extracting the package from its own root directory:

python -m unittest python/test_kimi_tool_calling.py
node --test node/test-kimi-tool-calling.mjs
node validator/validate-bundle.mjs

The Python and Node tests validate the frozen schemas, cases, allowlist and type rejection without network access. The bundle validator also rechecks the sanitized evidence, manifest and checksums.

Common implementation failures

SymptomLikely causeCorrective check
tool_call_id not foundThe assistant tool-call message was omitted or an ID does not matchAppend the complete assistant message, then one correctly matched tool result per call
JSON parse error while streamingAn argument fragment was parsed before all chunks arrivedAccumulate by tool-call index, then parse once complete
Unknown function executedThe model-supplied name was dispatched directlyReject names outside a fixed server-side allowlist
Extra or wrong-type fields reach a handlerJSON syntax was checked but the schema was notValidate required, additional and typed properties locally
Duplicate write after retryA transient failure retriggered a side effectUse idempotency keys and separate selection retries from execution retries
Endless tool loopNo application-level stop conditionSet a strict maximum number of model and tool rounds

Pre-production checklist

  • [ ] API key stored only in a server-side secret manager
  • [ ] Tool names and JSON Schemas versioned and reviewed
  • [ ] Model-selected names checked against an allowlist
  • [ ] Arguments validated locally before dispatch
  • [ ] User and resource authorization checked after validation
  • [ ] Destructive or billable actions require confirmation
  • [ ] Every tool call has a matching result and tool_call_id
  • [ ] Round, time, concurrency and spend limits configured
  • [ ] Side-effecting operations use idempotency and audit records
  • [ ] Logs exclude API keys and unnecessary sensitive content
  • [ ] Failure, timeout and malformed-result cases tested
  • [ ] Repeatability measured before publishing a reliability claim

Verification record

  • Official documentation rechecked: August 5, 2026
  • Independent source run: kimi-k3-pilot-20260805-v1.1
  • Tool-selection calls: 8 completed, 0 failed, 0 retried
  • Independently scored result: 8/8 exact single-pass cases
  • New API calls for this guide: 0
  • Downloadable examples: offline-tested; live end-to-end execution not run
  • WordPress status: Published; no tags and no featured image

Corrections are welcome through Sources & Corrections. For endpoint setup and key handling, start with our Kimi API guide.

Official sources