trip-test 0.1.2

Contract testing & regression safety for MCP servers
Documentation
use serde_json::{json, Value};
use std::io::{self, BufRead, Write};

fn main() {
    let stdin = io::stdin();
    let mut stdout = io::stdout();
    let reader = stdin.lock();
    let mut lines = reader.lines();

    while let Some(Ok(line)) = lines.next() {
        let request: Value = match serde_json::from_str(&line) {
            Ok(r) => r,
            Err(_) => continue,
        };

        let response = match request.get("method").and_then(|m| m.as_str()) {
            Some("initialize") => {
                json!({
                    "jsonrpc": "2.0",
                    "id": request.get("id"),
                    "result": {
                        "protocolVersion": "2024-11-05",
                        "capabilities": {},
                        "serverInfo": {
                            "name": "test-server",
                            "version": "0.2.0"
                        }
                    }
                })
            }
            Some("tools/list") => {
                json!({
                    "jsonrpc": "2.0",
                    "id": request.get("id"),
                    "result": {
                        "tools": [
                            {
                                "name": "search",
                                "description": "Search records - UPDATED",
                                "inputSchema": {
                                    "type": "object",
                                    "properties": {
                                        "q": {
                                            "type": "string",
                                            "description": "Search query"
                                        },
                                        "max_results": {
                                            "type": "integer",
                                            "description": "Max results"
                                        }
                                    },
                                    "required": ["q", "max_results"]
                                }
                            },
                            {
                                "name": "fetch",
                                "description": "Fetch a record by ID",
                                "inputSchema": {
                                    "type": "object",
                                    "properties": {
                                        "id": {
                                            "type": "string",
                                            "description": "Record ID"
                                        }
                                    },
                                    "required": ["id"]
                                }
                            }
                        ]
                    }
                })
            }
            Some("tools/call") => {
                let tool_name = request
                    .get("params")
                    .and_then(|p| p.get("name"))
                    .and_then(|n| n.as_str())
                    .unwrap_or("unknown");

                let args = request
                    .get("params")
                    .and_then(|p| p.get("arguments"))
                    .cloned()
                    .unwrap_or(json!({}));

                let result = match tool_name {
                    "search" => {
                        let q = args.get("q").and_then(|v| v.as_str()).unwrap_or("unknown");
                        let limit = args.get("max_results").and_then(|v| v.as_i64()).unwrap_or(10);
                        json!({
                            "results": [
                                { "id": "1", "title": format!("Result for '{}'", q), "score": 0.95 }
                            ],
                            "total": 1,
                            "query": q,
                            "max_results": limit
                        })
                    }
                    "fetch" => {
                        let id = args.get("id").and_then(|v| v.as_str()).unwrap_or("unknown");
                        json!({
                            "id": id,
                            "title": format!("Record {}", id),
                            "content": "Sample content",
                            "created": "2026-01-01T00:00:00Z"
                        })
                    }
                    _ => {
                        json!({
                            "jsonrpc": "2.0",
                            "id": request.get("id"),
                            "error": {
                                "code": -32601,
                                "message": "Unknown tool"
                            }
                        })
                    }
                };

                json!({
                    "jsonrpc": "2.0",
                    "id": request.get("id"),
                    "result": {
                        "content": [
                            {
                                "type": "text",
                                "text": serde_json::to_string(&result).unwrap()
                            }
                        ],
                        "isError": false
                    }
                })
            }
            _ => {
                json!({
                    "jsonrpc": "2.0",
                    "id": request.get("id"),
                    "error": {
                        "code": -32601,
                        "message": "Method not found"
                    }
                })
            }
        };

        let response_str = serde_json::to_string(&response).unwrap();
        writeln!(stdout, "{}", response_str).unwrap();
    }
}