TnsAI

MCP Advanced Features

Composio integration, database MCP clients, Goose interoperability, Stripe payments, testing utilities, and OAuth authentication.

Composio Integration

The com.tnsai.mcp.composio package bridges the Composio platform (500+ tool integrations) with the MCP ecosystem.

ComposioClient

REST client for the Composio API. Provides typed access to apps, tools, and execution.

ComposioClient client = new ComposioClient("your-api-key");

// List available apps
List<ComposioClient.ComposioApp> apps = client.listApps();
// ComposioApp(name, description, logoUrl, connected)

// List tools for an app
List<ComposioClient.ComposioTool> tools = client.listTools("github");
// ComposioTool(name, description, appName, inputSchema)

// Execute a tool
JsonNode result = client.executeTool("github_create_issue",
    Map.of("title", "Bug report", "body", "Details..."));

// Check connection status
ComposioClient.ConnectionStatus status = client.getConnectionStatus("github");
// ConnectionStatus(appName, connected, authMethod)

Key methods:

MethodReturnsDescription
listApps()List<ComposioApp>All available Composio apps
listTools(String appName)List<ComposioTool>Tools for a specific app
executeTool(String toolName, Map params)JsonNodeExecute a tool with parameters
getConnectionStatus(String appName)ConnectionStatusOAuth connection status

ComposioAuthBridge

Bridges Composio's API key authentication to MCP-compatible auth headers.

ComposioAuthBridge bridge = new ComposioAuthBridge("your-api-key");

// Verify API key validity (makes a test API call)
if (bridge.verifyApiKey()) {
    Map<String, String> headers = bridge.getAuthHeaders();
    // {"X-API-Key": "...", "Accept": "application/json"}
}

Key methods:

MethodReturnsDescription
getAuthHeaders()Map<String, String>Auth headers for API calls
verifyApiKey()booleanVerify key validity via test call
getApiKey()StringThe configured API key

ComposioDiscovery

Converts Composio tools to MCP-compatible McpToolDefinition and McpServerEntry objects for registry integration.

ComposioClient client = new ComposioClient("api-key");
ComposioDiscovery discovery = new ComposioDiscovery(client);

// Discover all tools as MCP definitions
List<McpToolDefinition> allTools = discovery.discoverTools();

// Discover tools for a specific app
List<McpToolDefinition> githubTools = discovery.discoverTools("github");

// Convert to MCP registry entries (one per Composio app)
List<McpServerEntry> entries = discovery.toServerEntries();

Key methods:

MethodReturnsDescription
discoverTools()List<McpToolDefinition>All tools from all connected apps
discoverTools(String appName)List<McpToolDefinition>Tools for a specific app
toServerEntries()List<McpServerEntry>Apps as MCP server entries

Database MCP Clients

Convenience wrappers around McpClient for specific database MCP servers.

Neo4jMcpClient

Wraps MCP tool calls to a Neo4j graph database server.

McpClient mcpClient = McpClient.create(transport);
mcpClient.connect();
mcpClient.initialize();

Neo4jMcpClient neo4j = new Neo4jMcpClient(mcpClient);

// Execute Cypher queries
JsonNode result = neo4j.executeCypher("MATCH (n:Person) RETURN n LIMIT 10");

// Explore the schema
JsonNode labels = neo4j.listNodeLabels();
JsonNode schema = neo4j.getSchema();

// Check availability
boolean available = neo4j.isAvailable();
// true when mcpClient.isConnected() && mcpClient.isInitialized()

Key methods:

MethodReturnsDescription
executeCypher(String query)JsonNodeExecute a Cypher query
listNodeLabels()JsonNodeList all node labels
getSchema()JsonNodeGet graph database schema
isAvailable()booleanCheck server availability
getMcpClient()McpClientUnderlying MCP client

PostgresMcpClient

Wraps MCP tool calls to a PostgreSQL database server.

McpClient mcpClient = McpClient.create(transport);
mcpClient.connect();
mcpClient.initialize();

PostgresMcpClient postgres = new PostgresMcpClient(mcpClient);

// Execute SQL queries
JsonNode result = postgres.executeQuery("SELECT * FROM users LIMIT 10");

// Explore schema
JsonNode tables = postgres.listTables();
JsonNode description = postgres.describeTable("users");

boolean available = postgres.isAvailable();

Key methods:

MethodReturnsDescription
executeQuery(String sql)JsonNodeExecute a SQL query
listTables()JsonNodeList all tables
describeTable(String table)JsonNodeDescribe table schema
isAvailable()booleanCheck server availability
getMcpClient()McpClientUnderlying MCP client

Goose Interoperability

The com.tnsai.mcp.goose package enables TnsAI and Goose (Block's AI agent framework) to share tools.

TnsAIGooseExtension

Generates a Goose-compatible extension manifest from an MCP server's tool definitions.

McpClient client = McpClient.create(transport);
client.connect();
client.initialize();

// Generate manifest
TnsAIGooseExtension.ExtensionManifest manifest =
    TnsAIGooseExtension.generateManifest(client);
// ExtensionManifest(name, version, description, tools)
// ExtensionTool(name, description, inputSchema)

// Export as JSON (place in Goose's extension directory)
String json = TnsAIGooseExtension.toJson(manifest);

GooseRecipeParser

Parses Goose recipe files (JSON workflow definitions) into structured objects that can be converted to MCP tool calls.

Recipe JSON format:

{
  "name": "deploy-app",
  "description": "Deploy an application",
  "steps": [
    {
      "tool": "build_project",
      "params": { "target": "production" },
      "output": "buildResult"
    },
    {
      "tool": "deploy",
      "params": { "artifact": "{{buildResult}}" }
    }
  ]
}
// Parse a recipe
GooseRecipeParser.Recipe recipe = GooseRecipeParser.parse(jsonContent);
// Recipe(name, description, steps)
// RecipeStep(tool, params, outputVariable)

// Convert to MCP tool call parameter maps
List<Map<String, Object>> mcpCalls = GooseRecipeParser.toMcpCalls(recipe);
// Each map: {"name": "tool_name", "arguments": {...}}

Stripe Integration

The com.tnsai.mcp.stripe package provides Stripe payment operations via MCP.

StripeMcpClient

Wraps MCP tool calls to a Stripe MCP server.

McpClient mcpClient = McpClient.create(transport);
mcpClient.connect();
mcpClient.initialize();

StripeMcpClient stripe = new StripeMcpClient(mcpClient);

// Payments
JsonNode payment = stripe.createPaymentIntent(2000, "usd"); // amount in cents
JsonNode payments = stripe.listPayments(10);

// Account
JsonNode balance = stripe.getBalance();

// Customers
JsonNode customers = stripe.listCustomers(10);
JsonNode newCustomer = stripe.createCustomer("Jane Doe", "jane@example.com");

StripePaymentTool

Adapter that wraps StripeMcpClient as a tool-like interface, routing named operations.

StripePaymentTool tool = new StripePaymentTool(stripeMcpClient);

// Execute by operation name
JsonNode result = tool.execute("get_balance", Map.of());
JsonNode payment = tool.execute("create_payment_intent",
    Map.of("amount", 2000, "currency", "usd"));

// List supported operations
List<String> ops = tool.listOperations();
// ["create_payment_intent", "list_payments", "get_balance",
//  "list_customers", "create_customer"]

MCP Testing Utilities

The com.tnsai.mcp.testing package provides tools for testing MCP servers and clients without network I/O.

McpServerTestKit

JUnit 5 extension that sets up a MockMcpServer, MockMcpTransport, and McpClient for each test.

@RegisterExtension
McpServerTestKit testKit = new McpServerTestKit();

@Test
void testToolExecution() throws Exception {
    // Configure mock tool responses
    testKit.getServer().whenToolReturns("echo",
        objectMapper.createObjectNode().put("text", "hello"));

    // Assert tool exists
    testKit.assertToolExists("echo");

    // Assert tool returns expected result
    testKit.assertToolReturns("echo", Map.of("msg", "hi"),
        result -> result.has("text"));
}

Key methods:

MethodReturnsDescription
getServer()MockMcpServerThe mock server instance
getTransport()MockMcpTransportThe mock transport
getClient()McpClientConnected MCP client
assertToolExists(String)voidAssert a tool is registered
assertToolReturns(String, Map, Predicate)voidAssert tool result matches

MockMcpServer

In-memory MCP server for unit testing. Handles JSON-RPC messages and routes initialize, tools/list, tools/call, resources/list, resources/read, prompts/list, and prompts/get.

MockMcpServer server = new MockMcpServer("test-server", "1.0.0");

// Register tool handlers
server.whenTool("calculate", args -> {
    int a = (int) args.get("a");
    int b = (int) args.get("b");
    return objectMapper.createObjectNode().put("result", a + b);
});

// Static tool response
server.whenToolReturns("echo", resultNode);

// Tool that simulates an error
server.whenToolThrows("fail", -32000, "Something went wrong");

// Register resources
server.whenResource("file://data.txt", "file contents");

// Register prompts
server.whenPrompt("greeting", promptResultNode);

// Process a raw JSON-RPC message
String response = server.processMessage(jsonRpcRequest);

OAuth Authentication

The com.tnsai.mcp.auth package provides OAuth 2.0 support for authenticated MCP connections.

OAuthMcpTransport

Transport decorator that adds OAuth Bearer token authentication to any McpTransport. Manages token lifecycle and ensures tokens are valid before each operation.

McpTokenManager tokenManager = new McpTokenManager();
// ... store tokens after OAuth flow ...

OAuthMcpTransport transport = OAuthMcpTransport.builder()
    .delegate(new SSETransport.Builder().sseEndpoint("...").build())
    .tokenManager(tokenManager)
    .serverUrl("https://mcp.example.com")
    .tokenEndpoint("https://auth.example.com/token")
    .clientId("my-client")
    .build();

McpClient client = new McpClient(transport);
client.connect();

// Query token state
Optional<String> bearerToken = transport.getBearerToken(); // "Bearer eyJ..."
boolean authenticated = transport.isAuthenticated();

Builder parameters:

ParameterRequiredDescription
delegateYesUnderlying transport to wrap
tokenManagerYesToken storage and refresh manager
serverUrlYesMCP server URL (token lookup key)
tokenEndpointNoOAuth token endpoint for refresh
clientIdNoOAuth client ID for refresh

PkceFlow

PKCE (Proof Key for Code Exchange) flow implementation with S256 challenge method, per RFC 7636.

Step 1: Generate challenge

PkceFlow.PkceChallenge challenge = PkceFlow.generateChallenge();
// PkceChallenge(codeVerifier, codeChallenge, codeChallengeMethod="S256")

Step 2: Build authorization URL

String authUrl = PkceFlow.buildAuthorizationUrl(
    "https://auth.example.com/authorize",
    "my-client-id",
    "http://localhost:8080/callback",
    "openid profile",          // scope
    "random-state-for-csrf",   // state
    challenge
);
// User visits authUrl, authorizes, callback receives code

Step 3: Exchange code for tokens

PkceFlow.TokenResponse token = PkceFlow.exchangeCode(
    "https://auth.example.com/token",
    "my-client-id",
    authCode,
    "http://localhost:8080/callback",
    challenge.codeVerifier()
);
// TokenResponse(accessToken, refreshToken, tokenType, expiresIn, scope, obtainedAt)

Step 4: Refresh tokens

PkceFlow.TokenResponse refreshed = PkceFlow.refreshToken(
    "https://auth.example.com/token",
    "my-client-id",
    token.refreshToken()
);

// Check token state
boolean expired = token.isExpired();
boolean expiringSoon = token.isExpiringSoon(Duration.ofMinutes(5));

Cross-References

On this page