TnsAI

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 stdio

Other factory methods on TnsAIToolProvider:

FactoryInputUse when
from(Object... pojos)One or more @Tool-bearing POJOsQuick start with a known set of toolkits
fromPojos(Object... pojos)Same as from, alternative nameSame
fromDynamic(DynamicToolMethod...)Runtime-defined toolsBridging MCP-proxy or plugin tools to MCP
from(Collection<Object> pojos, Collection<DynamicToolMethod> dynamics)Both sources combinedMixing 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 termination

Error 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.

CategoryCodeRetriableGuidance
INVALID_PARAMSinvalid_paramsNoRequest malformed, do not retry with same params
TOOL_NOT_FOUNDtool_not_foundNoTool does not exist, check tool registry
TIMEOUTtimeoutYesExecution timed out, may retry
PERMISSION_DENIEDpermission_deniedNoAccess denied, do not retry
INTERNALinternalYesUnexpected 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);
    }
});

On this page