Transport Configuration¶
The Android MCP SDK supports multiple transport layers for communication between your Android app and MCP clients. This guide covers configuration and usage of the available transport options.
Overview¶
The SDK provides two main transport mechanisms:
- WebSocket Transport: Real-time bidirectional communication (default port: 8080)
- HTTP/SSE Transport: HTTP-based communication with Server-Sent Events (default port: 8081)
Both transports are automatically started when the MCP server starts and can be accessed via adb port forwarding from your development workstation.
WebSocket Transport¶
Default Configuration¶
WebSocket transport is automatically configured and started:
val manager = McpStartup.getManager()
// Start server (WebSocket transport starts automatically)
manager.startServerAsync()
// Check transport status
val transportInfo = manager.getTransportInfo()
Log.d("MCP", "WebSocket endpoint: ws://localhost:8080/mcp")
Connecting from Development Machine¶
Set up adb port forwarding to access the WebSocket from your workstation:
# Forward WebSocket port
adb forward tcp:8080 tcp:8080
# Connect using a WebSocket client
# Endpoint: ws://localhost:8080/mcp
Sending Custom Messages¶
You can broadcast custom messages to all connected WebSocket clients:
lifecycleScope.launch {
val manager = McpStartup.getManager()
// Send a custom notification
val message = buildJsonObject {
put("jsonrpc", JsonPrimitive("2.0"))
put("method", JsonPrimitive("notification/custom"))
put("params", buildJsonObject {
put("type", JsonPrimitive("status_update"))
put("message", JsonPrimitive("Server status changed"))
put("timestamp", JsonPrimitive(System.currentTimeMillis()))
})
}
manager.broadcastMessage(message.toString())
}
WebSocket Client Example¶
Example WebSocket client code (JavaScript):
// Connect to Android MCP server via adb forwarding
const ws = new WebSocket('ws://localhost:8080/mcp');
ws.onopen = function() {
console.log('Connected to Android MCP server');
// Send tools/list request
ws.send(JSON.stringify({
jsonrpc: "2.0",
method: "tools/list",
id: 1
}));
};
ws.onmessage = function(event) {
const response = JSON.parse(event.data);
console.log('Received:', response);
};
ws.onerror = function(error) {
console.error('WebSocket error:', error);
};
HTTP/SSE Transport¶
Default Configuration¶
HTTP/SSE transport provides HTTP endpoints for request/response and Server-Sent Events for server-to-client communication:
// HTTP/SSE transport is automatically available when server starts
val manager = McpStartup.getManager()
manager.startServerAsync()
// Available endpoints:
// POST http://localhost:8081/mcp/message - Client-to-server messages
// GET http://localhost:8081/mcp/events - Server-to-client events (SSE)
// GET http://localhost:8081/mcp/status - Transport status
Connecting from Development Machine¶
Set up adb port forwarding for HTTP/SSE:
# Forward HTTP/SSE port
adb forward tcp:8081 tcp:8081
# Test the status endpoint
curl http://localhost:8081/mcp/status
# Send a message
curl -X POST http://localhost:8081/mcp/message \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
# Listen to server events (SSE)
curl -N http://localhost:8081/mcp/events
HTTP Client Example¶
Example HTTP client implementation (Python):
import requests
import json
from sseclient import SSEClient
# Base URL for Android MCP server via adb forwarding
BASE_URL = "http://localhost:8081/mcp"
# Send request to server
def send_request(method, params=None, request_id=1):
payload = {
"jsonrpc": "2.0",
"method": method,
"id": request_id
}
if params:
payload["params"] = params
response = requests.post(
f"{BASE_URL}/message",
json=payload,
headers={"Content-Type": "application/json"}
)
return response.json()
# Listen to server events
def listen_to_events():
messages = SSEClient(f"{BASE_URL}/events")
for msg in messages:
if msg.data:
event = json.loads(msg.data)
print(f"Received event: {event}")
# Example usage
if __name__ == "__main__":
# Get available tools
tools_response = send_request("tools/list")
print("Available tools:", tools_response)
# Call a tool
device_info = send_request("tools/call", {
"name": "device_info",
"arguments": {}
})
print("Device info:", device_info)
# Start listening to events (in a separate thread)
import threading
event_thread = threading.Thread(target=listen_to_events)
event_thread.daemon = True
event_thread.start()
Transport Security¶
Network Security¶
When using transport layers, consider these security aspects:
- Local Network Only: Transports are bound to localhost and only accessible via adb forwarding
- Development Use: Intended for development and debugging, not production exposure
- Firewall: Ensure your development machine firewall allows the forwarded ports
Authentication¶
Currently, the transport layers don't include built-in authentication. For production use, consider:
- VPN: Use VPN connections for remote access
- Tunnel: Use secure tunneling solutions
- Custom Auth: Implement custom authentication in your tools and resources
Advanced Configuration¶
Custom Port Configuration¶
While the default ports work for most use cases, you can check the current configuration:
val manager = McpStartup.getManager()
val transportInfo = manager.getTransportInfo()
Log.d("MCP", "Transport configuration: $transportInfo")
// Will show current port assignments and status
Connection Management¶
Monitor and manage transport connections:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val manager = McpStartup.getManager()
// Monitor transport status
lifecycleScope.launch {
while (isActive) {
val transportInfo = manager.getTransportInfo()
Log.d("MCP", "Transport status: $transportInfo")
// Check if transports are healthy
if (!transportInfo.contains("running")) {
Log.w("MCP", "Transport may be down, attempting restart")
manager.startServerAsync()
}
delay(30000) // Check every 30 seconds
}
}
}
}
Troubleshooting¶
Common Issues¶
Port Forwarding Not Working¶
# Check if adb is connected
adb devices
# Remove existing forwards and re-add
adb forward --remove tcp:8080
adb forward --remove tcp:8081
adb forward tcp:8080 tcp:8080
adb forward tcp:8081 tcp:8081
# Verify forwards are active
adb forward --list
Connection Refused¶
-
Check if server is running:
-
Check device logs:
-
Verify app is in foreground: Some lifecycle configurations may pause the server when app is backgrounded
WebSocket Connection Drops¶
WebSocket connections may drop due to:
- App lifecycle changes: Configure lifecycle management to handle background states
- Network changes: WiFi/mobile network switches
- Device sleep: Android's Doze mode or app standby
Solution:
// Configure lifecycle to keep server running
manager.initializeLifecycleManagement(
application = this,
config = McpLifecycleManager.LifecycleConfig(
autoStartOnAppStart = true,
autoStopOnAppStop = false, // Keep running in background
restartOnAppReturn = true
)
)
Debugging Transport Issues¶
Enable Verbose Logging¶
// In your Application class
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Enable debug logging for transport issues
if (BuildConfig.DEBUG) {
Log.d("MCP", "Starting MCP server with debug logging")
}
val manager = McpStartup.initializeManually(this)
manager.startServerAsync()
}
}
Test Transport Endpoints¶
#!/bin/bash
# test_transport.sh - Script to test all transport endpoints
echo "Testing Android MCP Transport Endpoints"
echo "======================================="
# Setup port forwarding
adb forward tcp:8080 tcp:8080
adb forward tcp:8081 tcp:8081
echo "1. Testing HTTP Status Endpoint"
curl -s http://localhost:8081/mcp/status | jq .
echo -e "\n2. Testing WebSocket Connection"
# Note: Requires websocat tool (cargo install websocat)
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | \
timeout 5s websocat ws://localhost:8080/mcp
echo -e "\n3. Testing HTTP Message Endpoint"
curl -s -X POST http://localhost:8081/mcp/message \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}' | jq .
echo -e "\nTransport test complete!"
Performance Considerations¶
Connection Limits¶
The transport layer can handle multiple simultaneous connections, but consider:
- Memory usage: Each connection consumes memory
- CPU overhead: Message processing for multiple clients
- Battery impact: Network activity affects battery life
Optimization Tips¶
- Batch operations: Group multiple requests when possible
- Efficient JSON: Minimize JSON payload sizes
- Connection reuse: Reuse WebSocket connections for multiple requests
- Background management: Properly handle app lifecycle transitions
Monitoring Performance¶
class TransportMonitor {
private val messageCount = AtomicInteger(0)
private val lastResetTime = AtomicLong(System.currentTimeMillis())
fun onMessageReceived() {
messageCount.incrementAndGet()
val now = System.currentTimeMillis()
val elapsed = now - lastResetTime.get()
// Log stats every minute
if (elapsed > 60000) {
val messagesPerMinute = messageCount.get()
Log.d("MCP", "Transport stats: $messagesPerMinute messages/minute")
messageCount.set(0)
lastResetTime.set(now)
}
}
}
Integration Examples¶
Claude Desktop Integration¶
Configure Claude Desktop to connect to your Android MCP server:
{
"mcpServers": {
"android-app": {
"command": "node",
"args": ["path/to/websocket-mcp-client.js"],
"env": {
"ANDROID_MCP_URL": "ws://localhost:8080/mcp"
}
}
}
}
Custom MCP Client¶
Build a custom MCP client that connects to your Android server:
// mcp-android-client.js
const WebSocket = require('ws');
class AndroidMCPClient {
constructor(url = 'ws://localhost:8080/mcp') {
this.url = url;
this.ws = null;
this.requestId = 1;
this.pendingRequests = new Map();
}
async connect() {
this.ws = new WebSocket(this.url);
this.ws.on('message', (data) => {
const message = JSON.parse(data.toString());
this.handleMessage(message);
});
return new Promise((resolve, reject) => {
this.ws.on('open', resolve);
this.ws.on('error', reject);
});
}
async callTool(name, arguments = {}) {
const id = this.requestId++;
const request = {
jsonrpc: "2.0",
method: "tools/call",
params: { name, arguments },
id
};
return this.sendRequest(request);
}
async listTools() {
const id = this.requestId++;
const request = {
jsonrpc: "2.0",
method: "tools/list",
id
};
return this.sendRequest(request);
}
sendRequest(request) {
return new Promise((resolve, reject) => {
this.pendingRequests.set(request.id, { resolve, reject });
this.ws.send(JSON.stringify(request));
});
}
handleMessage(message) {
if (message.id && this.pendingRequests.has(message.id)) {
const { resolve, reject } = this.pendingRequests.get(message.id);
this.pendingRequests.delete(message.id);
if (message.error) {
reject(new Error(message.error.message));
} else {
resolve(message.result);
}
}
}
}
module.exports = AndroidMCPClient;
This transport configuration guide provides comprehensive coverage of both WebSocket and HTTP/SSE transports, including practical examples, troubleshooting tips, and integration patterns for connecting various MCP clients to your Android MCP server.