# Tool Registry Architecture in DeepSeek-Reasonix: How Built-in Tools Self-Register

> Explore the DeepSeek-Reasonix Tool Registry architecture enabling built-in tools to self-register at compile-time via init() for deterministic discovery and runtime dynamic plugin injection.

- Repository: [YHH/DeepSeek-Reasonix](https://github.com/esengine/DeepSeek-Reasonix)
- Tags: architecture
- Published: 2026-06-27

---

**Reasonix implements a two-phase registration system where built-in tools self-register at compile-time via `init()` functions into a global map, then copy into a per-session `Registry` at runtime, enabling deterministic tool discovery while supporting dynamic plugin injection.**

The DeepSeek-Reasonix project separates tool discovery from execution through a sophisticated registry architecture. This design ensures that built-in tools located in `internal/tool/builtin/` are automatically available at startup while maintaining flexibility for MCP servers and runtime plugins. Understanding the interaction between the global `RegisterBuiltin` mechanism and the per-run `Registry` struct is essential for developers extending the system's capabilities.

## Global Compile-Time Registration of Built-in Tools

At package initialization, each built-in tool calls `tool.RegisterBuiltin` within its own `init()` function. This pattern appears consistently across files like [`internal/tool/builtin/writefile.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/builtin/writefile.go) and [`internal/tool/builtin/webfetch.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/builtin/webfetch.go).

The registration stores the tool in a package-level `builtins` map defined in [`internal/tool/tool.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/tool.go), keyed by the tool's `Name()` method. The system panics on duplicate names, guaranteeing a one-to-one mapping:

```go
// In internal/tool/tool.go
var builtins = map[string]Tool{}

func RegisterBuiltin(t Tool) {
    name := t.Name()
    if _, dup := builtins[name]; dup {
        panic("tool: duplicate built-in " + name)
    }
    builtins[name] = t
}

```

Each built-in follows this registration pattern in its `init()` function:

```go
// In internal/tool/builtin/writefile.go
func init() { tool.RegisterBuiltin(writeFile{}) }

```

Because these `init` functions compile into the binary, the global map is fully populated before any user code runs, creating a deterministic, immutable catalog of built-in capabilities.

## The Per-Run Registry Structure and Methods

When a Reasonix session starts, the system creates a fresh `Registry` instance via `tool.NewRegistry()`. This per-run container holds the active set of tools and manages their lifecycle independently of the global built-in set.

The `Registry` struct maintains four key fields:

- **`tools`**: A `map[string]Tool` storing active tool instances
- **`order`**: A `[]string` preserving insertion order for stable iteration
- **`canon`**: A `map[string]json.RawMessage` caching canonical JSON schemas
- **`suspended`**: A `map[string]bool` tracking temporarily disabled prefixes

The `NewRegistry()` constructor initializes these maps:

```go
func NewRegistry() *Registry {
    return &Registry{
        tools:     map[string]Tool{},
        canon:     map[string]json.RawMessage{},
        suspended: map[string]bool{},
    }
}

```

The registry provides thread-safe methods for dynamic management. The `Add(t Tool)` method inserts or replaces tools while preserving first-seen order and respecting suspended prefixes:

```go
func (r *Registry) Add(t Tool) {
    r.mu.Lock()
    defer r.mu.Unlock()
    name := t.Name()
    for prefix := range r.suspended {
        if strings.HasPrefix(name, prefix) {
            return
        }
    }
    if _, ok := r.tools[name]; !ok {
        r.order = append(r.order, name)
    }
    r.tools[name] = t
    r.canon[name] = provider.CanonicalizeSchema(t.Schema())
}

```

Additional methods include `RemovePrefix(prefix)` for disconnecting entire MCP servers, `SuspendPrefix`/`ResumePrefix` for temporary disabling, and `Get(name)` for tool lookup.

## Bootstrapping: Wiring Built-ins into the Session Registry

The connection between compile-time globals and runtime instances occurs during agent startup. The system retrieves all globally registered built-ins via `tool.Builtins()` and populates the fresh registry:

```go
reg := tool.NewRegistry()
for _, b := range tool.Builtins() {
    reg.Add(b)
}

```

This bootstrapping ensures every session begins with a complete, ordered list of built-in tools. After initialization, plugins and MCP connections can dynamically extend the registry by calling `reg.Add()` with new tool instances.

## MCP Tools Namespacing and Dynamic Management

External MCP servers expose tools under the `"mcp__"` prefix (e.g., `mcp__myserver__list_files`). The registry supports namespace-aware operations through the `SplitMCPName` function:

```go
func SplitMCPName(name string) (server, tool string, ok bool) {
    if !strings.HasPrefix(name, MCPNamePrefix) {
        return "", "", false
    }
    rest := name[len(MCPNamePrefix):]
    parts := strings.SplitN(rest, "__", 2)
    if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
        return "", "", false
    }
    return parts[0], parts[1], true
}

```

When an MCP server disconnects, `RemovePrefix("mcp__myserver__")` removes all tools matching that namespace. The `order` slice ensures that iteration remains stable regardless of registration timing or removal operations.

## Summary

- **Compile-time registration**: Built-in tools call `RegisterBuiltin` in `init()` functions, populating a global map in [`internal/tool/tool.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/tool.go) before runtime.
- **Runtime isolation**: Each session creates a fresh `Registry` via `NewRegistry()`, holding active tools in a thread-safe map with ordered iteration.
- **Bootstrapping flow**: The system copies all entries from `tool.Builtins()` into the registry at startup, then accepts dynamic additions via `reg.Add()`.
- **Namespace management**: MCP tools use `"mcp__"` prefixes, enabling bulk removal via `RemovePrefix()` and temporary suspension via `SuspendPrefix()`.

## Frequently Asked Questions

### How do I register a new built-in tool in Reasonix?

Create a new file in `internal/tool/builtin/` implementing the `Tool` interface with `Name()`, `Description()`, `Schema()`, `ReadOnly()`, and `Execute()` methods. Add an `init()` function that calls `tool.RegisterBuiltin()` with your tool instance. The package-level `init` ensures registration occurs at compile-time.

### What happens if two built-in tools have the same name?

The `RegisterBuiltin` function in [`internal/tool/tool.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/tool.go) checks for duplicates using the `builtins` map. If a name collision occurs, the function panics with `"tool: duplicate built-in " + name`, preventing the binary from starting and ensuring unique tool identifiers.

### How does Reasonix handle tools from external MCP servers?

MCP servers register tools with the `"mcp__"` prefix (e.g., `mcp__servername__toolname`). The registry treats these as standard tools via `Add()`, but provides namespace-aware methods like `RemovePrefix()` to disconnect entire servers and `SplitMCPName()` to parse server-tool relationships.

### Can I temporarily disable tools without removing them from the registry?

Yes. The `Registry` maintains a `suspended` map that tracks disabled prefixes. Calling `SuspendPrefix(prefix)` blocks all tools with names starting with that prefix from being added or executed, while `ResumePrefix(prefix)` restores them without requiring re-registration.