Redouble AI Nucleo documentation
Part 1 · Examples

An MCP tool wrapped as your own

ai.redouble.examples.mcpwrap · nucleo-examples

The generic MCP call of the previous example sends untyped JSON and gets text back. For a remote tool your workflows depend on, wrap it once as a tool of your own, with the input and output classes fixed in your code:

@ToolName("order_status")
@ToolDescription(value = "Look up one order by its number: its status, its customer, the date it was promised for", readOnly = true)
public class RemoteOrderStatusTool extends AbstractTool<OrderNumber, OrderStatus> {
    public RemoteOrderStatusTool(Identifiable parent) {
        super(parent);
    }

    @Override
    public JobRequirements getRequirements() {
        JobRequirements requirements = new JobRequirements();
        requirements.setRequiresTransaction(false);
        requirements.setReadOnly(true);
        // The call travels over HTTP, so it takes a place on the shared HTTP pool
        requirements.setRequiresHttpConnection(true);
        return requirements;
    }

    @Override
    public OrderStatus execute(JobResources resources, JobContext<OrderStatus> context) throws LLMReadableCheckedException {
        try {
            ObjectNode arguments = NucleoJsonSerializer.createObjectNode();
            arguments.put("order_number", getInput().getOrderNumber());
            MCPToolResult result = OrdersServer.client().callTool("order_status", arguments);
            return NucleoJsonSerializer.parse(result.getTextContent(), OrderStatus.class);
        }
        catch (Exception e) {
            throw LLMReadableCheckedException.unwrap(e);
        }
    }
}

The class is a tool like Your first tool, with the same OrderNumber in and OrderStatus out; only its body reaches over the wire. OrdersServer holds the application's one connection, opened at startup and closed at shutdown, so the tool keeps the (Identifiable parent) constructor every tool has and an agent creates it like any other. Start the server example's OrdersOverMcp first, as for the previous page:

JobDispatcher dispatcher = JobDispatcher.getInstance();
dispatcher.start();
OrdersServer.connect("http://localhost:8901/mcp");
try {
    RemoteOrderStatusTool tool = new RemoteOrderStatusTool(Job.workflow("you", "mcp-wrap"));
    tool.setInput(new OrderNumber("A-1002"));
    OrderStatus order = dispatcher.submit(tool).get();
    System.out.println(order);
}
finally {
    OrdersServer.close();
    dispatcher.shutdown(JobDispatcher.DEFAULT_SHUTDOWN_TIMEOUT_MS);
}

Why this is the right way for serious work

For a remote tool your workflows depend on, the typed wrapper earns its thirty lines several times over:

The generic path stays the right one for what it is for: browsing, one-off calls, an agent that works across whatever servers it is pointed at.

Next

The examples end here, and Tools, thinkers and doers begins the part on writing an agent. Every example ran on the job runtime.

^