The Model Context Protocol (MCP) is an open standard for connecting AI models like Claude to external tools, data sources, and services. Think of it as a type-safe, structured alternative to telling Claude "please run this bash command" — instead, the server declares what it can do (its tools), and the AI model calls those tools with structured JSON parameters.
Gentepede MCP uses StdioServerTransport. The MCP client (Claude Desktop) starts the server as a subprocess and communicates with it by:
- Writing JSON-RPC messages to the server's stdin
- Reading JSON-RPC responses from the server's stdout
This is deliberately simple: no HTTP server, no port binding, no TLS configuration. The OS manages the pipe between the two processes.
Each message is a single JSON object followed by a newline character — one line per message (newline-delimited JSON, NDJSON):
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_available_blueprints","arguments":{}}}
No Content-Length header, no blank-line separator — just one JSON object per line. The MCP SDK handles this framing automatically. You never write newline logic in Gentepede's code.
When Claude Desktop starts your MCP server for the first time in a session:
- Client → Server
initializerequest: "I am Claude, using MCP protocol version 2024-11-05" - Server → Client
initializeresponse: "I am Gentepede MCP v1.0.0; I have tools capability with 8 tools" - Client → Server
initializednotification: "Acknowledged, we are ready" - Client → Server
tools/listrequest: "What tools do you offer?" - Server → Client
tools/listresponse: list of 8 tools with their input schemas
After this handshake, the client knows the server's capabilities and Claude can decide when to call each tool.
Gentepede uses io.modelcontextprotocol:kotlin-sdk:0.13.0 rather than hand-rolling JSON-RPC for several reasons:
- Correctness: The SDK implements the exact MCP spec, including the initialize handshake, capability negotiation, and error response format. Hand-rolled implementations miss edge cases.
- Maintenance: When the protocol evolves, updating the SDK version is one line in
build.gradle.kts. - Tool registration:
server.addTool(name, description, inputSchema) { handler }is declarative and type-safe.
Add this to your Claude Desktop configuration file:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
Linux: ~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"gentepede": {
"command": "java",
"args": ["-jar", "/absolute/path/to/gentepede-mcp-all.jar"],
"env": {
"AWS_PROFILE": "my-aws-profile",
"AWS_DEFAULT_REGION": "us-east-1"
}
}
}
}Field explanations:
command: the executable to start (javamust be in PATH, or use the full path)args: passed directly tojava— the JAR path must be absoluteenv.AWS_PROFILE: AWS CLI named profile for credential lookup (optional if credentials are already in the environment)env.AWS_DEFAULT_REGION: AWS region for deployments
You type: "Generate AWS infrastructure for my Spring Boot app with a PostgreSQL database"
- Claude understands you want ECS Fargate with RDS. It decides to call
generate_infrastructure_package. - MCP SDK sends a
tools/callJSON-RPC request to the Gentepede server's stdin. - Main.kt receives the request via the registered tool handler lambda.
- Engine.kt extracts
blueprint_name,project_name, andvariablesfrom the JSON arguments. - InfrastructureService loads the blueprint, creates workspace files, and returns a result.
- Engine.kt formats the result as a human-readable string.
- MCP SDK wraps it in a
CallToolResultwithisError = false. - Claude Desktop shows the formatted result to you in the conversation.
The entire round trip (excluding Terraform subprocess time) takes milliseconds.
If validate_infrastructure_package finds a checkov HIGH or CRITICAL violation, Engine.kt returns:
CallToolResult(content = listOf(TextContent(text = "Error: ...")), isError = true)Claude Desktop surfaces this as an error in the conversation, and Claude can explain the finding and suggest a fix — without you needing to read raw JSON.