Skip to main content
Glama
mcp_inspector.py6.93 kB
#!/usr/bin/env python3 """Simple MCP inspector for the pagila MCP server. Features: - Start the `mcp_pagila_server.py` as a subprocess (same Python executable). - Send single or multiple JSON requests and print pretty JSON responses. - Interactive REPL for quick testing (list_films, run_pagila_query, raw JSON). Usage examples: # one-shot: list first 5 films python mcp_inspector.py -c "list_films 5" # interactive python mcp_inspector.py > list_films 5 > run SELECT title FROM film LIMIT 2 > raw {"id":1,"method":"list_films","params":{"limit":5}} > quit """ from __future__ import annotations import argparse import json import os import subprocess import sys import threading from typing import Optional def start_server(python_executable: str = None) -> subprocess.Popen: # Prefer workspace virtualenv python if present if python_executable is None: venv_python = os.path.join(os.getcwd(), ".venv", "bin", "python") if os.path.exists(venv_python): python_executable = venv_python else: python_executable = sys.executable cmd = [python_executable, "mcp_pagila_server.py"] proc = subprocess.Popen( cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1, ) # thread to forward stderr from server to our stderr with a prefix def _stderr_reader(): assert proc.stderr is not None for line in proc.stderr: sys.stderr.write("[server-stderr] " + line) t = threading.Thread(target=_stderr_reader, daemon=True) t.start() return proc def send_request(proc: subprocess.Popen, request: dict) -> Optional[dict]: assert proc.stdin is not None and proc.stdout is not None line = json.dumps(request, default=str) + "\n" proc.stdin.write(line) proc.stdin.flush() # read one line response resp_line = proc.stdout.readline() if not resp_line: return None try: return json.loads(resp_line) except Exception: print("Failed to parse server response:\n", resp_line) return None def pretty_print(obj: object) -> None: print(json.dumps(obj, indent=2, default=str)) def repl(proc: subprocess.Popen) -> None: print("MCP inspector REPL. Type 'help' for commands.") while True: try: s = input("> ").strip() except (EOFError, KeyboardInterrupt): print() break if not s: continue if s in ("q", "quit", "exit"): break if s == "help": help_text = ( "Commands:\n" " list_films <limit>\n" " run <SQL SELECT>\n" " text2sql <natural language>\n" " raw <JSON request>\n" " quit" ) print(help_text) continue if s.startswith("list_films"): parts = s.split(None, 1) limit = int(parts[1]) if len(parts) > 1 else 10 req = {"id": 1, "method": "list_films", "params": {"limit": limit}} resp = send_request(proc, req) pretty_print(resp) continue if s.startswith("text2sql "): text = s[len("text2sql ") :] req = { "id": 1, "method": "text_to_sql", "params": {"text": text, "execute": False}, } resp = send_request(proc, req) pretty_print(resp) continue if s.startswith("raw "): js = s[len("raw ") :] # Be tolerant of shell-escaped JSON like: {\"id\":1,...} req = None parse_attempts = [js, js.replace('\\"', '"')] if js.startswith("'") and js.endswith("'"): parse_attempts.append(js[1:-1]) for candidate in parse_attempts: try: req = json.loads(candidate) break except Exception: continue if req is None: print( 'Invalid JSON for raw command. Try: raw {"id":1,...} ' "or use proper shell quoting." ) continue resp = send_request(proc, req) pretty_print(resp) continue print("Unknown command. Type 'help'.") def main() -> int: parser = argparse.ArgumentParser() parser.add_argument( "-c", "--command", help=( "one-shot command: e.g. 'list_films 5' or 'run SELECT ...' or " "raw JSON starting with 'raw {...}'" ), ) args = parser.parse_args() proc = start_server() try: if args.command: s = args.command.strip() if s.startswith("raw "): js = s[len("raw ") :] # tolerant parsing for shell-escaped JSON req = None parse_attempts = [js, js.replace('\\"', '"')] if js.startswith("'") and js.endswith("'"): parse_attempts.append(js[1:-1]) for candidate in parse_attempts: try: req = json.loads(candidate) break except Exception: continue if req is None: print( "Invalid JSON for raw command. Try using proper quoting, " "e.g. -c 'raw {\"id\":1,...}'" ) return 2 elif s.startswith("list_films"): parts = s.split(None, 1) limit = int(parts[1]) if len(parts) > 1 else 10 req = {"id": 1, "method": "list_films", "params": {"limit": limit}} elif s.startswith("text2sql "): text = s[len("text2sql ") :] req = { "id": 1, "method": "text_to_sql", "params": {"text": text, "execute": False}, } elif s.startswith("run "): query = s[len("run ") :] req = { "id": 1, "method": "run_pagila_query", "params": {"query": query}, } else: print("Unknown one-shot command") return 2 resp = send_request(proc, req) pretty_print(resp) return 0 # interactive repl(proc) return 0 finally: try: proc.stdin.close() # type: ignore[union-attr] except Exception: pass proc.terminate() proc.wait(timeout=2) if __name__ == "__main__": raise SystemExit(main())

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/karthikingithub/pagila-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server