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

```java
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:

| Method | Returns | Description |
|--------|---------|-------------|
| `listApps()` | `List<ComposioApp>` | All available Composio apps |
| `listTools(String appName)` | `List<ComposioTool>` | Tools for a specific app |
| `executeTool(String toolName, Map params)` | `JsonNode` | Execute a tool with parameters |
| `getConnectionStatus(String appName)` | `ConnectionStatus` | OAuth connection status |

### ComposioAuthBridge

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

```java
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:

| Method | Returns | Description |
|--------|---------|-------------|
| `getAuthHeaders()` | `Map<String, String>` | Auth headers for API calls |
| `verifyApiKey()` | `boolean` | Verify key validity via test call |
| `getApiKey()` | `String` | The configured API key |

### ComposioDiscovery

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

```java
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:

| Method | Returns | Description |
|--------|---------|-------------|
| `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.

```java
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:

| Method | Returns | Description |
|--------|---------|-------------|
| `executeCypher(String query)` | `JsonNode` | Execute a Cypher query |
| `listNodeLabels()` | `JsonNode` | List all node labels |
| `getSchema()` | `JsonNode` | Get graph database schema |
| `isAvailable()` | `boolean` | Check server availability |
| `getMcpClient()` | `McpClient` | Underlying MCP client |

### PostgresMcpClient

Wraps MCP tool calls to a PostgreSQL database server.

```java
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:

| Method | Returns | Description |
|--------|---------|-------------|
| `executeQuery(String sql)` | `JsonNode` | Execute a SQL query |
| `listTables()` | `JsonNode` | List all tables |
| `describeTable(String table)` | `JsonNode` | Describe table schema |
| `isAvailable()` | `boolean` | Check server availability |
| `getMcpClient()` | `McpClient` | Underlying 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.

```java
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:**

```json
{
  "name": "deploy-app",
  "description": "Deploy an application",
  "steps": [
    {
      "tool": "build_project",
      "params": { "target": "production" },
      "output": "buildResult"
    },
    {
      "tool": "deploy",
      "params": { "artifact": "{{buildResult}}" }
    }
  ]
}
```

```java
// 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.

```java
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.

```java
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.

```java
@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:

| Method | Returns | Description |
|--------|---------|-------------|
| `getServer()` | `MockMcpServer` | The mock server instance |
| `getTransport()` | `MockMcpTransport` | The mock transport |
| `getClient()` | `McpClient` | Connected MCP client |
| `assertToolExists(String)` | void | Assert a tool is registered |
| `assertToolReturns(String, Map, Predicate)` | void | Assert 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`.

```java
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.

```java
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:

| Parameter | Required | Description |
|-----------|----------|-------------|
| `delegate` | Yes | Underlying transport to wrap |
| `tokenManager` | Yes | Token storage and refresh manager |
| `serverUrl` | Yes | MCP server URL (token lookup key) |
| `tokenEndpoint` | No | OAuth token endpoint for refresh |
| `clientId` | No | OAuth client ID for refresh |

### PkceFlow

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

**Step 1: Generate challenge**

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

**Step 2: Build authorization URL**

```java
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**

```java
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**

```java
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

- [MCP Client](client.md) -- core MCP client and transport
- [MCP Server](server.md) -- building MCP servers
- [MCP Registry](registry.md) -- unified registry for MCP servers
- [MCP Transports](transports.md) -- stdio, SSE, HTTP, and streamable HTTP
- [Tools Advanced](../capabilities/tools/advanced.md) -- Composio tool integration in TnsAI.Tools
