nanocodex-tools 0.4.0

Code Mode and heterogeneous tool runtime for Nanocodex
Documentation

Nanocodex Tools

Tool building blocks for OpenAI agents.

nanocodex-tools is useful without the Nanocodex agent loop. It provides the caller-defined [Tool] contract, tool macro, heterogeneous [Tools] registry, Code Mode runtime, standard workspace tools, and native MCP clients. The shared contract types are defined by nanocodex-oai-api and re-exported here so a tool implementation has one import surface.

Define and select tools

The definition is the single source of truth for a tool's registry name. The macro derives its input and output schemas from the function:

use nanocodex_tools::{Tools, tool};

#[tool(
    name = "deployment_region",
    description = "Return the production region for a named service.",
    parallel = true
)]
async fn deployment_region(service: String) -> Result<String, std::io::Error> {
    Ok(format!("{service}: us-west-2"))
}

# fn build() -> Result<(), nanocodex_tools::ToolsBuildError> {
let tools = Tools::builder()
    .without_defaults()
    .tool(deployment_region)
    .build()?;
# Ok(())
# }

Tools defaults to ToolExposure::CodeModeOnly, where ordinary tools are available through exec and only Code Mode entrypoints are directly visible. Select ToolExposure::DirectAndCodeMode when a consumer needs the same ordinary tools directly as well as through exec:

use nanocodex_tools::{ToolExposure, Tools};

# fn build() -> Result<(), nanocodex_tools::ToolsBuildError> {
let tools = Tools::builder()
    .exposure(ToolExposure::DirectAndCodeMode)
    .build()?;
# Ok(())
# }

Matching Codex, direct-plus-Code-Mode exposure keeps exec terse and adds each typed exec declaration to the corresponding direct tool; Code Mode-only instead carries the complete nested catalog in exec. Selection changes model-visible exposure, not registration or dispatch behavior. tool_with_exposure can override one registered tool with DirectOnly, CodeModeOnly, DirectAndCodeMode, or Hidden while preserving the global default for the rest. Host-owned exec, wait, and tool_search names cannot be replaced, and the first tool registered for a normalized JavaScript name wins the nested Code Mode surface. Namespaced Code Mode names such as image_gen__imagegen remain available to exec; normal Code Mode exposes the Codex-compatible image_gen.imagegen Responses namespace and routes its namespaced call to the same handler.

Macro tools execute serially unless parallel = true explicitly marks their local effects as safe to overlap. This does not change the provider wire protocol.

Implement [Tool] directly when execution needs [ToolContext], freeform input, multimodal [ToolOutput], or a custom definition:

use nanocodex_tools::{
    Tool, ToolContext, ToolDefinition, ToolInput, ToolOutput, ToolResult,
    contract::async_trait,
};
use serde_json::json;

struct DeploymentRegion;

#[async_trait]
impl Tool for DeploymentRegion {
    fn definition(&self) -> ToolDefinition {
        ToolDefinition::function(
            "deployment_region",
            "Return the production region for a named service.",
            json!({
                "type": "object",
                "properties": { "service": { "type": "string" } },
                "required": ["service"],
                "additionalProperties": false
            }),
        )
    }

    async fn execute(
        &self,
        input: ToolInput,
        _context: ToolContext<'_>,
    ) -> ToolResult {
        let input: serde_json::Value = input.decode_json()?;
        let service = input["service"].as_str().ok_or_else(|| {
            std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                "service must be a string",
            )
        })?;
        Ok(ToolOutput::text(format!("{service}: us-west-2")))
    }
}

Embed Code Mode in another host

[hosted] is the portable boundary for environments that own JavaScript execution outside Rust. Implement [hosted::CodeModeHost] and pass it to [hosted::HostedTools]; the adapter reuses the same execution, nested-call, notification, observer, and owned-context types as native Code Mode. The [hosted] module documentation includes a complete host implementation.

MCP is native and always available

MCP is not a feature flag. Native consumers configure stdio or Streamable HTTP servers and install the provider into the same registry:

use nanocodex_tools::{
    Tools,
    mcp::{Mcp, McpServer},
};

# fn build() -> Result<(), Box<dyn std::error::Error>> {
let mcp = Mcp::builder()
    .server(
        "company_docs",
        McpServer::stdio("company-docs-mcp").arg("--readonly"),
    )
    .build()?;

let tools = Tools::builder().provider(mcp).build()?;
# Ok(())
# }

Handshakes and discovery start with the owning runtime. Both exposure policies keep the provider-native tool_search visible while omitting deferred MCP schemas from the initial request. Code Mode lists those deferred tools as compact name/description entries in ALL_TOOLS. Search results contain loadable MCP namespaces for direct model calls and also activate matching Code Mode definitions, keeping large catalogs out of the initial tool list. McpServer::tool_exposure independently selects DeferredOnly, CodeModeOnly, DeferredAndCodeMode, or Hidden for each server. Automatic catalog and aggregate resource pagination is bounded by page, item, cursor, and wall-clock limits.

Companion workspace runtimes

The default native feature remains the complete tools crate: registry, Code Mode, MCP, web/image tools, macros, and standard workspace tools.

The narrower workspace-runtime feature exists only for process companions such as nanocodex-vm-guest. With default features disabled, it exposes the canonical [workspace_runtime::WorkspaceToolRuntime], standard tool identities, and their shared contracts without linking OpenAI transports, Code Mode/QuickJS, MCP, or HTTP clients. This is artifact separation, not a second tool implementation or an alternate mode for normal native applications.

Going lower level

The crate root intentionally contains only the normal registry path: [Tools], ToolsBuilder, ToolsBuildError, [Tool], tool, and the types required by the Tool methods. [ToolExposure] is the advanced declaration policy for consumers that compare direct and Code Mode calls. The root also exposes ambient_sensitive_environment for deliberately restoring proxy-safe credential markers to tool subprocesses.

  • [contract] contains complete model-visible inputs, outputs, errors, and retained wire forms.
  • [hosted] contains the portable application-owned Code Mode boundary.
  • [runtime] contains the stateful per-agent executor, built-in connection configuration, and dynamic-provider contract.
  • [code_mode] contains cell results, notifications, and nested-tool updates.
  • mcp contains transport configuration, authentication, discovery, login, and runtime control.
  • standard contains reusable standard-tool identities and the host-owned plan implementation.
  • [workspace_runtime] contains the retained canonical workspace-tool runtime used by process companions.
  • [image] contains prompt-image preparation and tool-output normalization.