Skip to content
tnsaiDocs
GitHub

Coding agents and IDEs

Coding agents and IDEs such as Claude Code, Cursor and Goose call tools over the Model Context Protocol (MCP). tnsai-mcp serves TnsAI tools to them, generates a Goose extension manifest, and reads Goose extensions and recipes. For the other direction, where a TnsAI agent calls an MCP server, see MCP client.

Example

package com.example.tnsai.docs;

import com.tnsai.enums.BuiltInTool;
import com.tnsai.mcp.client.McpClient;
import com.tnsai.mcp.goose.GooseExtensionLoader;
import com.tnsai.mcp.goose.GooseRecipeParser;
import com.tnsai.mcp.goose.TnsAIGooseExtension;
import com.tnsai.mcp.registry.McpServerEntry;
import com.tnsai.mcp.server.HttpMcpServer;
import com.tnsai.mcp.server.TnsAIToolProvider;
import com.tnsai.tools.method.ToolMethod;
import com.tnsai.tools.method.ToolMethodRegistry;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/** Compilation contract for connect/coding-agents.md. */
public final class ConnectCodingAgentsExample {

    private ConnectCodingAgentsExample() {
    }

    static HttpMcpServer serveToolkits(Object... ownTools) throws IOException {
        HttpMcpServer server = HttpMcpServer.builder()
            .port(3000)                                   // the default, 0, picks a free port
            .serverName("tnsai")
            .addToolProvider(TnsAIToolProvider.fromPojos(
                BuiltInTool.WEB_SEARCH_TOOLS.instantiate(),   // needs tnsai-tools on the classpath
                BuiltInTool.PDF_TOOLS.instantiate()))
            .addToolProvider(TnsAIToolProvider.fromPojos(ownTools))
            .build();
        server.start();                                   // serves http://localhost:3000/mcp
        return server;
    }

    static void printSchemas(Object... toolPojos) {
        for (ToolMethod tool : new ToolMethodRegistry(List.of(toolPojos)).all()) {
            System.out.println(tool.name() + ": " + tool.parametersSchema());
        }
    }

    static String gooseManifest(McpClient connectedClient) throws IOException {
        // The client must already be connected and initialized.
        return TnsAIGooseExtension.toJson(TnsAIGooseExtension.generateManifest(connectedClient));
    }

    static List<McpServerEntry> gooseExtensions(Path extensionDir) throws IOException {
        GooseExtensionLoader loader = new GooseExtensionLoader();
        return loader.loadFromDirectory(extensionDir).stream()   // unreadable files are skipped
            .map(loader::toServerEntry)                           // empty unless type is "mcp"
            .flatMap(Optional::stream)
            .toList();
    }

    static List<Map<String, Object>> recipeCalls(String recipeJson) throws IOException {
        GooseRecipeParser.Recipe recipe = GooseRecipeParser.parse(recipeJson);
        return GooseRecipeParser.toMcpCalls(recipe);      // {"name": ..., "arguments": ...} per step
    }
}

Serve TnsAI tools

TnsAIToolProvider.fromPojos(...) turns every @Tool method on the objects you pass into an MCP tool. Built-in toolkits come from BuiltInTool.X.instantiate(). It loads the class by name, so tnsai-tools must be on the classpath. Without it, instantiate() throws BuiltInToolInstantiationException. Expose the toolkits the client needs, not the whole catalog. When two providers list the same tool name, tools/list shows it twice and calls go to the provider added last, so keep names unique.

HttpMcpServer listens on a free port unless you set port, so set it when a client config names a fixed URL. The endpoint is http://localhost:<port>/mcp. HTTP server lists the builder options.

HttpMcpServer checks no credentials. Keep it on the local machine, or put it behind a proxy that authenticates callers and terminates TLS.

Connect a client

Claude Code adds an HTTP MCP server with its CLI:

claude mcp add --transport http tnsai http://localhost:3000/mcp

Cursor, Continue and other MCP clients take the same URL in their MCP server settings. A client that starts servers as subprocesses needs a stdio server instead. Stdio server shows one.

Inspect tool schemas

new ToolMethodRegistry(List.of(pojos...)).all() returns each tool's name, description and JSON Schema for its parameters (parametersSchema()). An agent builds the same registry type from its tool objects, so the schemas match what the agent sends to its model. The constructor throws IllegalArgumentException when two tools share a name. No agent is needed.

Goose

ClassDoes
TnsAIGooseExtensiongenerateManifest(client) lists the tools of a connected, initialized MCP client and returns a manifest. Its name and version come from the server's initialize reply, or tnsai and 1.0.0 when the server sent none. toJson(manifest) writes indented JSON.
GooseExtensionLoaderloadFromDirectory(dir) reads every *.json file in the directory. It logs and skips files it cannot parse, and throws IOException when dir is not a directory. toServerEntry(extension) returns an McpServerEntry for extensions of type mcp (the default) and empty for any other type. The transport defaults to stdio, and the endpoint comes from endpoint, then command.
GooseRecipeParserparse(json) reads name, description and steps, where each step has tool, params and an optional output. toMcpCalls(recipe) returns one {"name", "arguments"} map per step.

toMcpCalls copies params as they are. It does not run the steps or fill in {{variable}} placeholders from an earlier step's output, so your code does that between calls.

MCP registry covers McpServerEntry.

llms.txt and AGENTS.md

Two plain Markdown files help coding agents work with a project built on TnsAI:

  • /llms.txt at a site root lists what the project is and where its docs live. tnsai.dev/llms.txt is one example, and llmstxt.org describes the format.
  • AGENTS.md at a repository root gives coding agents their working instructions. agents.md describes the convention.