# How NativeToolCallParser Converts API Tool Calls to Typed Arguments in Roo Code

> Discover how Roo Code's NativeToolCallParser converts API tool calls into typed arguments by parsing JSON, coercing types, and validating against NativeToolArgs for robust integration.

- Repository: [Roo Code/Roo-Code](https://github.com/RooCodeInc/Roo-Code)
- Tags: internals
- Published: 2026-04-26

---

**The `NativeToolCallParser` class transforms raw OpenAI-style function call payloads into strongly-typed `ToolUse` objects by parsing JSON arguments, coercing values to specific TypeScript types, and validating them against the `NativeToolArgs` definitions.**

Roo Code, developed by RooCodeInc, processes LLM-generated tool calls through a strict parsing pipeline that ensures type safety across its entire tool ecosystem. The `NativeToolCallParser` located in [`src/core/assistant-message/NativeToolCallParser.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/core/assistant-message/NativeToolCallParser.ts) serves as the bridge between unstructured API responses and the typed execution layer. This conversion guarantees that built-in tools, MCP integrations, and custom user tools receive `nativeArgs` that exactly match their TypeScript interfaces, preventing runtime errors while maintaining backward compatibility with legacy parameter formats.

## Entry Point: The `parseToolCall` Method

The conversion process begins with the static `parseToolCall` method, which acts as the primary entry point for all incoming tool calls.

```typescript
public static parseToolCall<TName extends ToolName>(toolCall: {
  id: string
  name: TName
  arguments: string
}): ToolUse<TName> | McpToolUse | null

```

This method receives the raw payload from the LLM, which includes a tool name and a JSON string of arguments. First, it normalizes dynamic MCP tool names using `resolveToolAlias` to handle the `mcp--server--tool` naming convention, falling back to standard tool names if the alias resolution does not match an MCP pattern. Invalid tool names are rejected early with a logged warning and a `null` return value, preventing downstream execution of undefined tools.

## Parsing and Type Coercion Pipeline

Once the tool name is resolved, the parser executes a multi-stage pipeline to convert the raw JSON string into typed arguments.

### JSON Extraction and Error Handling

The parser attempts to parse the `arguments` string into a JavaScript object, treating empty strings as empty objects to handle edge cases in API responses.

```typescript
const args = toolCall.arguments === "" ? {} : JSON.parse(toolCall.arguments);

```

If `JSON.parse` throws due to malformed JSON, the catch block logs the specific error and returns `null`, ensuring that corrupt payloads never reach the tool executors.

### Legacy Parameter Construction

For backward compatibility with the UI layer, the parser constructs a `params` object that stringifies all values. This legacy format maps each argument key to its string representation, validating keys against the known `toolParamNames` registry and warning about unknown parameters unless the tool is a custom user-registered tool.

```typescript
const params: Partial<Record<ToolParamName, string>> = {};
for (const [key, value] of Object.entries(args)) {
  if (!toolParamNames.includes(key as ToolParamName) && !customToolRegistry.has(resolvedName)) {
    console.warn(`Unknown parameter '${key}' for tool '${resolvedName}'`);
    continue;
  }
  params[key as ToolParamName] = typeof value === "string" ? value : JSON.stringify(value);
}

```

### Typed `nativeArgs` Assembly

The core type conversion happens in a type-guarded switch statement that matches the resolved tool name against entries in `NativeToolArgs`. For each tool, the parser extracts specific fields from `args` and applies coercion helpers to ensure type safety.

For example, the `read_file` tool handles both legacy array formats and modern single-file arguments:

```typescript
case "read_file":
  // Legacy format { files: [...] } → convert to typed FileEntry[]
  if (args.files !== undefined) {
    nativeArgs = {
      files: this.convertFileEntries(filesArray),
      _legacyFormat: true as const,
    } as NativeArgsFor<TName>;
  }
  // New format { path, mode, offset, … }
  if (!nativeArgs && args.path !== undefined) {
    nativeArgs = {
      path: args.path,
      mode: args.mode,
      offset: this.coerceOptionalNumber(args.offset),
      limit: this.coerceOptionalNumber(args.limit),
    } as NativeArgsFor<TName>;
  }
  break;

```

The parser uses helper methods `coerceOptionalNumber` and `coerceOptionalBoolean` to safely convert strings or numbers into the expected types defined in [`src/shared/tools.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/shared/tools.ts). Custom tools skip this switch entirely and pass the raw `args` object directly as `nativeArgs`, allowing dynamic schemas without strict type checking.

## Validation and Final Object Construction

After constructing `nativeArgs`, the parser validates that the object exists for all non-custom tools. If validation fails, it throws a descriptive error identifying the problematic tool and the invalid arguments.

The final `ToolUse` object combines both the legacy `params` for UI rendering and the strongly-typed `nativeArgs` for execution:

```typescript
const result: ToolUse<TName> = {
  type: "tool_use",
  name: resolvedName,
  params,
  partial: false,
  nativeArgs,
};
if (toolCall.name !== resolvedName) {
  result.originalName = toolCall.name;   // preserve alias for API history
}
return result;

```

## Streaming Tool Call Support

For streaming LLM responses, `processStreamingChunk` accumulates partial JSON and uses the `partial-json` library to parse incomplete argument strings. The parser then calls `createPartialToolUse`, which mirrors the main conversion logic but sets `partial: true` and only builds arguments from the data received so far. This allows the UI to display live tool call formation while the final execution waits for `finalizeStreamingToolCall` to complete the typed conversion.

## Practical Implementation Example

When integrating the parser into custom handlers or tests, you can invoke the conversion directly:

```typescript
import { NativeToolCallParser } from "@roo-code/core/assistant-message/NativeToolCallParser";

const rawCall = {
  id: "call_1",
  name: "read_file",
  arguments: JSON.stringify({
    path: "src/index.ts",
    mode: "full",
    offset: "10",  // String input from API
    limit: 20,
  }),
};

const toolUse = NativeToolCallParser.parseToolCall(rawCall);

if (toolUse) {
  // toolUse.nativeArgs is typed as NativeToolArgs["read_file"]
  // offset is now number | undefined, not string
  console.log(toolUse.nativeArgs.path);  // string
  console.log(toolUse.nativeArgs.offset); // number (10)
}

```

## Summary

- **Entry Point**: The `parseToolCall` static method in [`src/core/assistant-message/NativeToolCallParser.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/core/assistant-message/NativeToolCallParser.ts) receives raw tool calls and resolves aliases via `resolveToolAlias`.
- **Type Safety**: The parser coerces JSON values using `coerceOptionalNumber` and `coerceOptionalBoolean` to match the `NativeToolArgs` TypeScript definitions from [`src/shared/tools.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/shared/tools.ts).
- **Dual Format**: Each `ToolUse` object contains both legacy `params` (stringified for UI) and strongly-typed `nativeArgs` (for execution).
- **Validation**: Unknown parameters trigger warnings for built-in tools, while custom tools pass through unvalidated to support dynamic schemas.
- **Streaming**: `processStreamingChunk` handles partial JSON using the `partial-json` library, enabling real-time tool call display.

## Frequently Asked Questions

### What is the difference between `params` and `nativeArgs` in Roo Code's tool system?

The `params` field contains stringified key-value pairs intended for display in the UI layer, maintaining backward compatibility with legacy string-based formats. The `nativeArgs` field contains strongly-typed objects that match the TypeScript definitions in [`src/shared/tools.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/shared/tools.ts), providing type-safe arguments for the actual tool execution logic in [`src/core/tools/BaseTool.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/core/tools/BaseTool.ts).

### How does `NativeToolCallParser` handle invalid or malformed tool arguments?

If `JSON.parse` fails on the arguments string, the parser logs the error and returns `null`. After parsing, if the tool is not a custom registered tool and `nativeArgs` remains undefined after the type-specific switch block, the parser throws an explicit error stating that the arguments are invalid for the specific tool name. This prevents malformed payloads from reaching downstream executors.

### Can `NativeToolCallParser` process streaming tool calls from the LLM?

Yes, the parser supports streaming through `processStreamingChunk`, which accumulates partial JSON strings and uses the `partial-json` library to parse incomplete data. It creates partial `ToolUse` objects with `partial: true` that update the UI in real-time, then finalizes the conversion with `finalizeStreamingToolCall` once the complete arguments are received.

### Where are the TypeScript type definitions for tool arguments maintained?

The source of truth for typed tool arguments is [`src/shared/tools.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/shared/tools.ts), which exports the `NativeToolArgs` interface. This interface maps each tool name to its specific argument structure, and `NativeToolCallParser` uses these definitions through the `NativeArgsFor<TName>` generic to ensure that the `nativeArgs` field matches the expected TypeScript shape for every built-in tool.