MCP and Callable Goals
ReActMCP combines structured model tool calls with remote MCP tools and ordinary
ASTRA callable goals. It extends ReActTools, which shares ReActCore turn control with the textual
ReAct agent, and is provided by astra-langchain4j-tools.
Local goal registration
Define a callable achievement goal and explicitly register its contract with the tool catalogue:
import astra.langchain4j.reasoning.*;
agent MainMCP extends ReActMCP {
@Callable(description="Double an integer", inputs=["value"], outputs=["result"])
rule +!double(int value, int result) { result = value * 2; }
rule +!configure() {
tools.registerCallables();
mcp.connectHttp("math", "http://127.0.0.1:8090/mcp", tools.catalog());
}
}
Configure model using the usual provider configuration, adopt !solve(question, string answer), and call mcp.stop() when retiring the integration. The complete
runnable MCP examples
include a deterministic model and a small local server, so they need no API key.
They also include a standalone MainGoals agent with no MCP interaction.
The model sees local double and remote math__add. If it selects double with
{"value":7}, the local branch in ReActMCP performs:
GoalInvocation call = tools.invocation(session);
invoke(call);
tools.result(session, tools.resultJson(call), false);
The returned output is {"result":14}. Java prepares and reads the invocation;
ASTRA performs the native subgoal execution and applies ordinary guards and
failure handling. There is no wrapper rule to write in the application.
Callable JSON contracts support scalar inputs and outputs (string, char,
int, long, float, double, boolean) and lists containing JSON scalar values
or nested lists. Every input is required. Nulls, extra inputs, duplicate JSON
fields, incompatible types and out-of-range numbers are rejected. Other ASTRA
parameter types can exist in general callable metadata, but cannot be imported
into this JSON tool catalogue. Overloads that produce the same model tool name
are rejected rather than selected ambiguously. Catalogue registration is atomic;
register once after the agent is configured.
Remote tools and conversation control
mcp.connectHttp(alias, url, tools.catalog()) uses MCP Streamable HTTP.
mcp.connectStdio(alias, commandList, tools.catalog()) starts a stdio server.
Remote tool names are advertised as alias__tool; execution sends the original
name to the server. Each solve takes a catalogue snapshot and its own message
history. ReActMCP executes every requested call sequentially, preserving model
call IDs, and records all results before the next model turn.
Network I/O uses a bounded executor and delivers native $mcp.completed events.
The intention waits for its correlated result without blocking the agent’s other
intentions. Tool failures become error observations; model failures propagate
through normal goal failure. The inherited maxSteps belief bounds model turns.
Execution has a 30-second deadline. mcp.cancel(call),
mcp.disconnect(alias) and mcp.stop() complete pending calls once and discard
late replies. Local cancellation cannot guarantee reversal or interruption of an
already executing remote tool. Disconnecting and reconnecting an alias does not
retarget an existing conversation to its replacement connection.
This integration supports MCP tools, not MCP resources or prompts. It currently
passes structured content as JSON text and otherwise uses the SDK’s extracted
text content; full multimodal content is not exposed. Existing manually
registered tools can still use handwritten !invoke handlers for compatibility.