Created
October 24, 2025 17:33
-
-
Save bioshazard/8929a65f2f9670a3e3129b60eaa2ae9c to your computer and use it in GitHub Desktop.
MCPO multi-service CLI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # MCPO CLI - Efficient MCP OpenAPI Proxy Client | |
| # Usage: ./mcpo.sh <command> [args...] | |
| set -e | |
| # Configuration | |
| MCPO_HOST="${MCPO_HOST:-http://localhost:8000}" | |
| MCPO_TOKEN="${MCPO_TOKEN:-supersecret}" | |
| # Colors for output | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| BOLD='\033[1m' | |
| NC='\033[0m' # No Color | |
| # Helper functions | |
| log_info() { echo -e "${BLUE}ℹ${NC} $1" >&2; } | |
| log_success() { echo -e "${GREEN}✓${NC} $1" >&2; } | |
| log_warn() { echo -e "${YELLOW}⚠${NC} $1" >&2; } | |
| log_error() { echo -e "${RED}✗${NC} $1" >&2; } | |
| usage() { | |
| cat << EOF | |
| ${BOLD}MCPO CLI - MCP OpenAPI Proxy Client${NC} | |
| ${BOLD}USAGE:${NC} | |
| mcpo.sh discover # List available services | |
| mcpo.sh describe <service> # Get service details | |
| mcpo.sh operations <service> # List service operations | |
| mcpo.sh schema <service> <operation> # Get operation schema | |
| mcpo.sh call <service> <operation> [json] # Call service operation | |
| ${BOLD}EXAMPLES:${NC} | |
| mcpo.sh discover | |
| mcpo.sh describe context7 | |
| mcpo.sh operations memory | |
| mcpo.sh call context7 resolve-library-id '{"libraryName": "react"}' | |
| mcpo.sh call memory search_nodes '{"query": "authentication"}' | |
| ${BOLD}ENVIRONMENT:${NC} | |
| MCPO_HOST Target MCPO server (default: http://localhost:8000) | |
| MCPO_TOKEN Bearer token for authentication (default: supersecret) | |
| EOF | |
| } | |
| # Get authentication headers | |
| get_auth_headers() { | |
| if [[ -n "$MCPO_TOKEN" ]]; then | |
| echo "Authorization:Bearer $MCPO_TOKEN" | |
| fi | |
| } | |
| # Discover available services | |
| discover_services() { | |
| log_info "Discovering services at $MCPO_HOST..." | |
| local response | |
| response=$(http GET "$MCPO_HOST/openapi.json" 2>/dev/null || { | |
| log_error "Failed to connect to MCPO server at $MCPO_HOST" | |
| exit 1 | |
| }) | |
| echo "$response" | jq -r ' | |
| if .info.description then | |
| .info.description | | |
| split("\n") | | |
| map(select(test("^\\s*-\\s*\\*\\*"))) | | |
| map(gsub("^\\s*-\\s*\\*\\*([^*]+)\\*\\*.*\\[([^]]+)\\].*"; "\\1: \\2"))[] | |
| else | |
| empty | |
| end | |
| ' 2>/dev/null || { | |
| log_warn "Could not parse service list from response" | |
| echo "$response" | |
| } | |
| } | |
| # Get service description | |
| describe_service() { | |
| local service="$1" | |
| [[ -z "$service" ]] && { log_error "Service name required"; usage; exit 1; } | |
| log_info "Getting description for service: $service" | |
| local auth_header | |
| auth_header=$(get_auth_headers) | |
| local response | |
| if [[ -n "$auth_header" ]]; then | |
| response=$(http GET "$MCPO_HOST/$service/openapi.json" "$auth_header" 2>/dev/null) | |
| else | |
| response=$(http GET "$MCPO_HOST/$service/openapi.json" 2>/dev/null) | |
| fi | |
| if [[ $? -ne 0 ]]; then | |
| log_error "Failed to get schema for service: $service" | |
| exit 1 | |
| fi | |
| echo "$response" | jq -r ' | |
| "Service: " + .info.title, | |
| "Version: " + .info.version, | |
| "Description: " + (.info.description // "No description available"), | |
| "", | |
| "Base URL: " + (.servers[0].url // "/") | |
| ' | |
| } | |
| # List service operations | |
| list_operations() { | |
| local service="$1" | |
| [[ -z "$service" ]] && { log_error "Service name required"; usage; exit 1; } | |
| log_info "Listing operations for service: $service" | |
| local auth_header | |
| auth_header=$(get_auth_headers) | |
| local response | |
| if [[ -n "$auth_header" ]]; then | |
| response=$(http GET "$MCPO_HOST/$service/openapi.json" "$auth_header" 2>/dev/null) | |
| else | |
| response=$(http GET "$MCPO_HOST/$service/openapi.json" 2>/dev/null) | |
| fi | |
| if [[ $? -ne 0 ]]; then | |
| log_error "Failed to get schema for service: $service" | |
| exit 1 | |
| fi | |
| echo "$response" | jq -r ' | |
| .paths | to_entries[] | | |
| .key as $path | | |
| .value | to_entries[] | | |
| .key as $method | | |
| .value | | |
| "Operation: " + ($path | ltrimstr("/")) + " (" + ($method | ascii_upcase) + ")", | |
| "Summary: " + (.summary // "No summary"), | |
| "Description: " + ((.description // "No description") | split("\n")[0]), | |
| "" | |
| ' | |
| } | |
| # Get operation schema | |
| get_operation_schema() { | |
| local service="$1" | |
| local operation="$2" | |
| [[ -z "$service" || -z "$operation" ]] && { | |
| log_error "Service name and operation required"; usage; exit 1; | |
| } | |
| log_info "Getting schema for $service/$operation" | |
| local auth_header | |
| auth_header=$(get_auth_headers) | |
| local response | |
| if [[ -n "$auth_header" ]]; then | |
| response=$(http GET "$MCPO_HOST/$service/openapi.json" "$auth_header" 2>/dev/null) | |
| else | |
| response=$(http GET "$MCPO_HOST/$service/openapi.json" 2>/dev/null) | |
| fi | |
| if [[ $? -ne 0 ]]; then | |
| log_error "Failed to get schema for service: $service" | |
| exit 1 | |
| fi | |
| echo "$response" | jq --arg op "/$operation" ' | |
| .paths[$op] | to_entries[] | | |
| .key as $method | | |
| .value | | |
| { | |
| operation: $op, | |
| method: ($method | ascii_upcase), | |
| summary: .summary, | |
| description: .description, | |
| requestBody: (.requestBody.content."application/json".schema // null), | |
| responses: .responses | |
| } | |
| ' | |
| } | |
| # Call service operation | |
| call_operation() { | |
| local service="$1" | |
| local operation="$2" | |
| local json_data="$3" | |
| [[ -z "$service" || -z "$operation" ]] && { | |
| log_error "Service name and operation required"; usage; exit 1; | |
| } | |
| log_info "Calling $service/$operation" | |
| local auth_header | |
| auth_header=$(get_auth_headers) | |
| local url="$MCPO_HOST/$service/$operation" | |
| # Build http command | |
| local cmd=(http POST "$url") | |
| if [[ -n "$auth_header" ]]; then | |
| cmd+=("$auth_header") | |
| fi | |
| if [[ -n "$json_data" ]]; then | |
| # Validate JSON | |
| echo "$json_data" | jq . >/dev/null 2>&1 || { | |
| log_error "Invalid JSON data provided" | |
| exit 1 | |
| } | |
| cmd+=(--json) | |
| # Use process substitution to pass JSON data | |
| "${cmd[@]}" < <(echo "$json_data") | |
| else | |
| "${cmd[@]}" --json | |
| fi | |
| } | |
| # Main command dispatcher | |
| main() { | |
| case "${1:-}" in | |
| "discover") | |
| discover_services | |
| ;; | |
| "describe") | |
| describe_service "$2" | |
| ;; | |
| "operations") | |
| list_operations "$2" | |
| ;; | |
| "schema") | |
| get_operation_schema "$2" "$3" | |
| ;; | |
| "call") | |
| call_operation "$2" "$3" "$4" | |
| ;; | |
| "-h"|"--help"|"help"|"") | |
| usage | |
| ;; | |
| *) | |
| log_error "Unknown command: $1" | |
| usage | |
| exit 1 | |
| ;; | |
| esac | |
| } | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment