MCP Server
The McpServer exposes TnsAI tools, resources, and prompts to MCP clients.
Building a Server
To create an MCP server, use the builder to register your tool, resource, and prompt providers, then call start(). By default the server communicates over stdio, making it easy to launch as a subprocess from any MCP client.
McpServer server = McpServer.builder()
.serverName("My-TnsAI-Server")
.serverVersion("1.0.0")
.addToolProvider(myToolProvider)
.addResourceProvider(myResourceProvider)
.addPromptProvider(myPromptProvider)
.build();
server.start(); // Stdio transport (blocking)Expose TnsAI Tools via MCP
TnsAIToolProvider adapts TnsAI POJO toolkits and runtime tools into MCP tool definitions. Choose the factory that matches your registration pattern:
import com.tnsai.enums.BuiltInTool;
import com.tnsai.mcp.server.TnsAIToolProvider;
// From an explicit set of POJO toolkit instances
McpServer server = McpServer.builder()
.serverName("TnsAI-Tools")
.addToolProvider(TnsAIToolProvider.fromPojos(
BuiltInTool.WEB_SEARCH_TOOLS.instantiate(),
BuiltInTool.UTILITY_TOOLS.instantiate(),
new MyDomainTools()
))
.build();
server.start(); // Listens on stdioOther factory methods on TnsAIToolProvider:
| Factory | Input | Use when |
|---|---|---|
from(Object... pojos) | One or more @Tool-bearing POJOs | Quick start with a known set of toolkits |
fromPojos(Object... pojos) | Same as from, alternative name | Same |
fromDynamic(DynamicToolMethod...) | Runtime-defined tools | Bridging MCP-proxy or plugin tools to MCP |
from(Collection<Object> pojos, Collection<DynamicToolMethod> dynamics) | Both sources combined | Mixing POJOs with runtime tools in one provider |
Custom Tool Provider
To expose your own custom tools via MCP, implement the ToolProvider interface. You define which tools are available and how each one executes when called by a client.
public class MyToolProvider implements ToolProvider {
@Override
public List<McpToolDefinition> listTools() {
return List.of(
McpToolDefinition.builder()
.name("analyze")
.description("Analyze text for sentiment")
.inputSchema(schemaNode)
.build()
);
}
@Override
public ToolResult executeTool(String name, JsonNode args) {
if ("analyze".equals(name)) {
String text = args.get("text").asText();
return ToolResult.text(analyzeSentiment(text));
}
return ToolResult.error("Unknown tool: " + name);
}
}HTTP Server
For production deployments where clients connect over the network, use HttpMcpServer. It serves MCP over Streamable HTTP transport, supporting JSON-RPC requests via POST, server-sent event notifications via GET, and session termination via DELETE.
HttpMcpServer httpServer = HttpMcpServer.builder()
.port(8080)
.path("/mcp")
.addToolProvider(myProvider)
.build();
httpServer.start();
// POST /mcp — JSON-RPC requests
// GET /mcp — SSE notifications
// DELETE /mcp — Session terminationError Handling
Structured error events emitted when MCP tool execution fails. Register a ToolErrorListener to receive notifications and decide on recovery strategy.
ToolErrorCategory
Each error is classified into a category that tells clients whether they should retry the operation or fix the request. This prevents pointless retries for permanent failures like invalid parameters.
| Category | Code | Retriable | Guidance |
|---|---|---|---|
INVALID_PARAMS | invalid_params | No | Request malformed, do not retry with same params |
TOOL_NOT_FOUND | tool_not_found | No | Tool does not exist, check tool registry |
TIMEOUT | timeout | Yes | Execution timed out, may retry |
PERMISSION_DENIED | permission_denied | No | Access denied, do not retry |
INTERNAL | internal | Yes | Unexpected error, may retry |
ToolErrorEvent
A ToolErrorEvent is a structured record emitted whenever a tool execution fails. It contains the tool name, error category, a safe error message (no stack traces), a timestamp, and an optional request ID for correlation.
// Factory methods
ToolErrorEvent event = ToolErrorEvent.of("my_tool", ToolErrorCategory.TIMEOUT, "Timed out after 30s");
ToolErrorEvent event = ToolErrorEvent.of("my_tool", ToolErrorCategory.INTERNAL, "Unexpected error", requestId);
// Check retriability (delegates to category)
if (event.isRetriable()) {
// retry logic
}ToolErrorListener
Register a ToolErrorListener to get notified whenever a tool call fails. This is useful for logging, metrics, or implementing retry logic based on the error category.
// Lambda listener
server.getRequestHandler().addToolErrorListener(event -> {
logger.warn("Tool '{}' failed: {} [{}]",
event.toolName(), event.message(), event.category().code());
if (event.isRetriable()) {
retryQueue.add(event);
}
});MCP Client
The McpClient connects to any MCP-compatible server and exposes its tools, resources, and prompts.
MCP Transports
The MCP module provides 5 transport implementations plus an OAuth decorator and an auto-detection utility for connecting to MCP servers over stdio, HTTP, SSE, and bidirectional streaming.