# Roo Code Native Protocol Tool Calling vs Legacy Format: Key Differences Explained

> Discover the key differences between Roo Code's native protocol tool calling and the legacy format. Understand why nativeArgs offer type safety and clarity for your code.

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

---

**Roo Code's native protocol tool calling system uses strongly-typed `nativeArgs` with required tool IDs, while the legacy format relies on loose XML or JSON `params` that lack type safety and are actively being phased out.**

Roo Code (formerly Roo Cline) is an AI-powered coding assistant in the RooCodeInc/Roo-Code repository that orchestrates LLM-driven tools through a structured execution framework. The architecture supports **two distinct calling protocols** that currently exist side-by-side for backward compatibility. Understanding the distinction between the modern **native protocol tool calling** approach and the deprecated legacy format is essential for extending the tool ecosystem or debugging integration failures.

## Core Architectural Differences

The fundamental split between these protocols centers on how tool arguments are structured, validated, and processed by the execution engine.

### Argument Structure and Type Safety

**Native protocol** calls require typed arguments defined in the `NativeToolArgs` interface located in [`src/shared/tools.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/shared/tools.ts). These arguments are passed directly to the tool's `execute` method as a strongly-typed object. For example, a `read_file` call uses `ReadFileToolParams` with explicit `path`, `mode`, `offset`, and `limit` fields.

**Legacy format** calls use free-form `params` containing either raw JSON or stringified XML snippets. The tool must manually inspect this `params` object and parse values, leading to potential runtime errors from mismatched types or missing fields.

### Invocation Requirements

Native protocol blocks **must contain an `id`** (the API-generated tool ID) and the `nativeArgs` property to be recognized. According to [`src/core/tools/BaseTool.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/core/tools/BaseTool.ts), if `block.nativeArgs` is missing, the system throws a clear error stating that XML tool calls are no longer supported.

Legacy blocks require neither an `id` nor `nativeArgs`. They historically relied on XML markup like `<read_file>...</read_file>` embedded in responses, though this path now triggers immediate errors forcing migration to the native format.

### Streaming and Partial Updates

The native protocol supports **streaming tool calls** through the `handlePartial()` method, which can safely rely on typed `nativeArgs` values (like incremental `path` or `offset` updates) to render UI progress. Legacy partial handling had to infer values from loosely-typed `params`, making real-time updates fragile and error-prone.

## Implementation in the Codebase

The protocol distinction is enforced across three critical files in the Roo Code source tree.

### Typed Definitions in tools.ts

The `NativeToolArgs` mapping in [`src/shared/tools.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/shared/tools.ts) (lines 88-99) defines the contract for all native tool calls:

```typescript
// src/shared/tools.ts
export interface NativeToolArgs {
  read_file: ReadFileToolParams;
  write_to_file: WriteToFileToolParams;
  // ... additional tools
}

```

This interface ensures that every native call adheres to a predefined schema, enabling compile-time checks and IDE autocomplete for developers extending the system.

### Dispatch Logic in BaseTool.ts

The `BaseTool.handle()` method in [`src/core/tools/BaseTool.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/core/tools/BaseTool.ts) (lines 25-48) serves as the central dispatcher that enforces the protocol distinction:

```typescript
// src/core/tools/BaseTool.ts - lines 44-48
if (!block.nativeArgs) {
  throw new Error("XML tool calls are no longer supported")
}

```

If `nativeArgs` is present, the method extracts it directly and passes the typed object to the tool's implementation. If missing, it immediately aborts with a `HandleError` result, preventing ambiguous legacy calls from executing.

### Legacy Detection in NativeToolCallParser.ts

The `NativeToolCallParser.createPartialToolUse()` method in [`src/core/assistant-message/NativeToolCallParser.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/core/assistant-message/NativeToolCallParser.ts) (lines 94-106) specifically looks for the legacy `read_file` "files" envelope to set telemetry flags:

```typescript
// src/core/assistant-message/NativeToolCallParser.ts - lines 98-105
if (params.files) {
  // Detect legacy read_file format for telemetry
  toolUse.usedLegacyFormat = true
}

```

This detection allows the system to track migration progress without breaking existing integrations that still use the old envelope format.

## Code Examples: Native vs Legacy

### Native Protocol Call (Recommended)

```json
{
  "type": "tool_use",
  "id": "tool-123",
  "name": "read_file",
  "nativeArgs": {
    "path": "src/main.ts",
    "mode": "auto",
    "offset": 0,
    "limit": 200
  },
  "partial": false
}

```

The LLM sends this block, the parser stores the typed `nativeArgs`, and `ReadFileTool.execute()` receives a **typed** object matching `ReadFileToolParams`. The `id` field is mandatory for native calls.

### Legacy Format Call (Backward Compatibility)

```json
{
  "type": "tool_use",
  "name": "read_file",
  "params": {
    "files": "[{\"path\":\"src/main.ts\",\"mode\":\"auto\"}]"
  },
  "partial": false
}

```

This triggers the `usedLegacyFormat` flag in telemetry logs. The parser attempts to extract the old `files` array and convert it internally to the native shape. If the block omitted `files` and used plain XML markup instead, `BaseTool.handle()` would throw an error terminating the execution.

### Partial Streaming (Native Only)

```json
{
  "type": "tool_use",
  "id": "tool-456",
  "name": "read_file",
  "partial": true,
  "nativeArgs": {
    "path": "src/large.log",
    "offset": 1024
  }
}

```

During streaming, the UI can safely reference `nativeArgs.path` and `nativeArgs.offset` to show progress, as the type system guarantees these fields exist and contain valid types.

## Error Handling and Enforcement

The native protocol provides strict failure modes that prevent silent errors. When `nativeArgs` are missing, `BaseTool.handle()` sends a single error tool result and aborts the entire operation. Conversely, legacy calls could continue with ambiguous data, leading to hidden failures or incorrect tool execution.

Additionally, [`src/core/assistant-message/presentAssistantMessage.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/core/assistant-message/presentAssistantMessage.ts) (lines 112-120) enforces that every native tool use must receive a corresponding `tool_result`, preventing the "missing result" bug that existed in legacy handling where partially processed XML could leave operations dangling.

## Summary

- **Native protocol** uses strongly-typed `nativeArgs` defined in `NativeToolArgs` and requires a unique `id` for every tool call.
- **Legacy format** relies on loose `params` objects and XML markup, which now trigger explicit errors rather than executing ambiguously.
- The system detects remaining legacy usage via the `usedLegacyFormat` flag when the old `read_file` "files" envelope is encountered.
- All new tools must be added to [`src/shared/tools.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/shared/tools.ts) first, with legacy support only maintained as a temporary migration shim.
- Streaming and partial updates are only reliable under the native protocol due to type-safe argument access.

## Frequently Asked Questions

### What happens if an LLM returns XML markup instead of nativeArgs?

**Roo Code will throw an explicit error and abort the tool execution.** According to [`src/core/tools/BaseTool.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/core/tools/BaseTool.ts) (lines 44-48), if `block.nativeArgs` is missing and the system detects XML-style markup or legacy `params`, it returns a `HandleError` result with the message "XML tool calls are no longer supported." This forces immediate migration to the typed native protocol.

### How does Roo Code track which conversations still use legacy formats?

**The system sets a `usedLegacyFormat` telemetry flag when it detects specific legacy patterns.** In [`src/core/assistant-message/NativeToolCallParser.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/core/assistant-message/NativeToolCallParser.ts) (lines 98-105), the parser checks for the presence of a `files` field in the parameters—specifically the old read_file envelope format—and sets `toolUse.usedLegacyFormat = true`. This allows the development team to monitor adoption rates without breaking existing functionality.

### Can I temporarily use legacy params for custom tools during development?

**No, the legacy code path is deprecated and slated for removal.** While the system currently maintains backward compatibility for the specific `read_file` "files" envelope (converting it internally to native format), all other XML and loose `params` usage triggers errors. New tools must be defined in the `NativeToolArgs` interface in [`src/shared/tools.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/shared/tools.ts) and use the `nativeArgs` property exclusively.

### Does migrating to the native protocol improve tool execution performance?

**Yes, primarily through reduced parsing overhead and safer partial execution.** Native calls pass typed objects directly to `execute()` methods without manual string parsing or type coercion. Additionally, streaming tools can access incremental `nativeArgs` updates through `handlePartial()` without defensive coding against missing or mistyped fields, resulting in more responsive UI updates and fewer runtime exceptions.