Skip to content
tnsaiDocs
GitHub

Actions and tools

Choose between an action, a tool object, a built-in toolkit and an MCP server for something the model can call.

All four reach the model the same way: each turns into a function in the tool list sent with a chat turn. They differ in where the code lives and what the framework wraps around it.

Which one to use

You haveUseRegister it with
Work that belongs to a role, and needs contracts, resilience, approvals or typed routingAn @ActionSpec method on a RoleAgentBuilder.role(...)
A helper function on a plain objectAn @Tool methodAgentBuilder.toolPojos(...)
A toolkit that already ships in tnsai-toolsA BuiltInTool constantAgentBuilder.builtInTools(...)
Tools served by an MCP serverAn MCP_TOOL action that names the serverAgentBuilder.role(...)

Actions are tools

An @ActionSpec action is sent to the model as a function by default. Its name, description and parameters become the function schema, and the model can call it during a chat turn. Two cases are left out:

  • excludeFromLLM = true. The action stays callable from code, for example with agent.executeAction(...), but the model never sees it.
  • MCP_TOOL actions. The action itself is configuration: it names the server. The tools that server lists are sent instead.

An action or MCP tool that the access gate would refuse is also left out of the list. See Principal and authority.

An LLM action runs a model call. It does not add tools of its own.

All four on one agent

package com.example.tnsai.docs;

import com.tnsai.accountability.AuthorityScope;
import com.tnsai.agents.Agent;
import com.tnsai.agents.AgentBuilder;
import com.tnsai.annotations.ActionSpec;
import com.tnsai.annotations.Tool;
import com.tnsai.annotations.ToolParam;
import com.tnsai.enums.ActionType;
import com.tnsai.enums.BuiltInTool;
import com.tnsai.identity.AgentPrincipal;
import com.tnsai.llm.LLMClient;
import com.tnsai.models.role.RoleIdentity;
import com.tnsai.roles.Role;

import java.util.Map;

public final class ActionsAndToolsExample {
    private ActionsAndToolsExample() {}

    public static final class OrdersRole extends Role {
        @Override
        public RoleIdentity getIdentity() {
            return new RoleIdentity("orders", "Answer questions about orders", "support");
        }

        @ActionSpec(type = ActionType.LOCAL, description = "Look up an order by id")
        public String findOrder(String orderId) {
            return "Order " + orderId + ": shipped";
        }

        @ActionSpec(type = ActionType.LOCAL, description = "Rebuild the order index",
                    excludeFromLLM = true)
        public String reindex() {
            return "reindexed";
        }
    }

    public static final class CurrencyTools {
        @Tool(name = "convert_currency", description = "Convert an amount between currencies")
        public double convert(
                @ToolParam(description = "Amount to convert") double amount,
                @ToolParam(description = "ISO code to convert from, e.g. EUR") String from,
                @ToolParam(description = "ISO code to convert to, e.g. USD") String to) {
            return amount;
        }
    }

    public static Agent build(LLMClient llm, AgentPrincipal principal, AuthorityScope scope) {
        return AgentBuilder.create()
            .llm(llm)
            .role(Role.create(OrdersRole.class))
            .toolPojos(new CurrencyTools())
            .builtInTools(BuiltInTool.WEB_SEARCH_TOOLS)
            .principal(principal)
            .authorityScope(scope)
            .build();
    }

    public static Object reindexFromCode(Agent agent) {
        return agent.executeAction("reindex", Map.of());
    }
}
  • The model sees findOrder, convert_currency and the web search tools.
  • It does not see reindex, which your code runs through executeAction.
  • WEB_SEARCH_TOOLS creates com.tnsai.tools.search.WebSearchTools from tnsai-tools. Without that module on the classpath, BuiltInToolInstantiationException is thrown.

Keep secrets out of schemas

Names, descriptions and parameter descriptions of actions and tools are sent to the model. Keep API keys and other credentials in the environment, not in any of them. To approve or reject a call at run time, use a tool call filter rather than wording in the schema. See Tool approval.