The Kobayashi Maru: Testing and Deployment


MCP Deep-Dive Series

๐ŸŽญ Testing with MCP Inspector โ€” Holodeck Simulations

Before sending the Enterprise into battle, every system is tested on the holodeck. In MCP, that holodeck is the MCP Inspector โ€” an interactive development tool that lets you poke and prod your server without needing a full LLM client:

# Launch the Inspector โ€” your personal holodeck
mcp dev your_server.py

The Inspector opens a web UI where you can:

Think of it as running a Level 1 diagnostic from the bridge. You can validate every system before going to warp.

๐Ÿ–ฅ๏ธ The MCP CLI for Development

The mcp CLI provides additional commands for development workflow:

# Install your server in Claude Desktop for testing
mcp install your_server.py

# Install with a custom name
mcp install your_server.py --name "USS Enterprise"

# Install with environment variables (like ship access codes)
mcp install your_server.py -v STARFLEET_API_KEY=alpha-omega-7

# Run the interactive inspector
mcp dev your_server.py

The install command automatically configures Claude Desktop to connect to your server โ€” no manual JSON editing required. It's like docking at a starbase and having the maintenance crew handle the hookups.

๐Ÿงช Writing a Test Client โ€” The Kobayashi Maru

The Kobayashi Maru is Starfleet's no-win scenario โ€” a stress test that pushes cadets to their limits. Your MCP server deserves the same treatment. Here's how to write programmatic tests:

import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def kobayashi_maru_test():
    """The no-win scenario: stress test all ship systems."""
    params = StdioServerParameters(command='python', args=['warp_core.py'])

    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()

            # Test 1: Tool discovery โ€” can we see all weapons systems?
            tools = await session.list_tools()
            assert len(tools.tools) > 0, 'No tools found โ€” weapons offline!'
            print(f"โœ“ {len(tools.tools)} tools discovered")

            # Test 2: Resource access โ€” are sensors operational?
            resources = await session.list_resources()
            print(f"โœ“ {len(resources.resources)} resources available")

            # Test 3: Tool execution โ€” fire at will!
            result = await session.call_tool('engage_warp', {'factor': 9})
            assert 'engaged' in result.content[0].text.lower(), \
                'Warp drive failed to engage!'
            print(f"โœ“ Warp drive: {result.content[0].text}")

            # Test 4: Error handling โ€” push past the limits
            try:
                result = await session.call_tool('engage_warp', {'factor': 15})
                # Should have raised an error
                if result.isError:
                    print("โœ“ Warp factor limits enforced correctly")
            except Exception as e:
                print(f"โœ“ Error properly raised: {e}")

            print("\n๐Ÿ–– All systems nominal. Ship is ready for deployment.")

asyncio.run(kobayashi_maru_test())

This test spawns your server, connects to it, and exercises every capability โ€” just like Starfleet Academy's infamous test scenario. Run it in CI/CD to catch regressions before they reach production.

๐Ÿšข Multi-Server Client โ€” Connecting the Fleet

A single ship is powerful. A fleet is unstoppable. MCP clients can connect to multiple servers simultaneously, combining their capabilities into a single unified interface:

from langchain_mcp_adapters.client import MultiServerMCPClient

# Connect to an entire fleet of MCP servers
client = MultiServerMCPClient({
    'enterprise': {
        'command': 'python',
        'args': ['enterprise_server.py'],
    },
    'defiant': {
        'command': 'python',
        'args': ['defiant_server.py'],
    },
})

# The LLM now has access to ALL tools from ALL servers
# Enterprise tools: fire_phasers, raise_shields, sensor_scan
# Defiant tools: quantum_torpedo, ablative_armor, cloaking_device

Each server runs independently โ€” different processes, possibly different languages. The client aggregates all their tools into a single namespace. The model sees one unified set of capabilities, like an admiral commanding multiple ships from a flagship.

๐Ÿณ Deployment with Docker โ€” Loading into Spacedock

For production deployment, containerize your MCP server. It's like loading the ship into spacedock โ€” everything sealed, self-contained, ready for any environment:

# Dockerfile for your MCP server
FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY warp_core.py .

# MCP servers communicate via stdio, so no ports to expose
CMD ["python", "warp_core.py"]

Since MCP uses stdio transport by default, there are no ports to expose โ€” the client spawns the container and pipes stdin/stdout. For remote deployments, MCP also supports SSE (Server-Sent Events) transport over HTTP.

๐ŸŽฏ Claude Desktop / Cursor Integration โ€” Connecting to Starfleet Command

To connect your server to Claude Desktop (Starfleet Command), add it to the configuration file. On macOS, edit ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "enterprise": {
      "command": "python",
      "args": ["/path/to/warp_core.py"],
      "env": {
        "STARFLEET_API_KEY": "your-api-key-here"
      }
    },
    "defiant": {
      "command": "python",
      "args": ["/path/to/defiant_server.py"]
    }
  }
}

For Cursor, the configuration lives in .cursor/mcp.json at the project root:

{
  "mcpServers": {
    "enterprise": {
      "command": "python",
      "args": ["./warp_core.py"]
    }
  }
}

After saving, restart the client. Your server's tools will appear in the interface โ€” ready for the model to invoke at will.

๐Ÿ” Debugging Tips โ€” Engineering Deck

When things go wrong (hull breach on Deck 7!), here's how to diagnose issues:

1. Check the MCP logs

Claude Desktop writes MCP logs to:

# macOS
~/Library/Logs/Claude/mcp*.log

# Tail logs in real-time during development
tail -f ~/Library/Logs/Claude/mcp-server-enterprise.log

2. Use stderr for debugging output

Since MCP uses stdout for protocol messages, print debug info to stderr:

import sys

@enterprise.tool()
def problematic_tool(x: int) -> str:
    """A tool that needs debugging."""
    print(f"DEBUG: received x={x}", file=sys.stderr)  # Goes to logs, not protocol
    return f"Result: {x * 2}"

3. Common transport issues

๐ŸŒŸ Final Complete Server โ€” All Hands, Battle Stations

Here's a complete, production-ready MCP server bringing all the concepts together โ€” resources, tools, prompts, error handling, and external API calls:

"""
USS Enterprise NCC-1701-D โ€” MCP Server
A complete Model Context Protocol server demonstrating all three primitives.
"""
from mcp.server.fastmcp import FastMCP
import httpx

enterprise = FastMCP("USS Enterprise NCC-1701-D")

# ============================================================
# RESOURCES โ€” Ship's Sensors (read-only, application-controlled)
# ============================================================

@enterprise.resource("enterprise://ship/status")
def ship_status() -> str:
    """Current ship status report."""
    return (
        "USS Enterprise NCC-1701-D Status Report\n"
        "========================================\n"
        "Hull Integrity: 98%\n"
        "Shields: Online (standby)\n"
        "Warp Core: Nominal\n"
        "Life Support: Optimal\n"
        "Crew Complement: 1,014\n"
        "Current Position: Sector 001, Sol System"
    )

@enterprise.resource("enterprise://crew/{department}")
def crew_manifest(department: str) -> str:
    """Crew manifest for a specific department."""
    departments = {
        "command": "Picard, J-L (Captain)\nRiker, W.T. (Commander)\nData (Lt. Cmdr)",
        "engineering": "La Forge, G. (Lt. Cmdr)\nBarclay, R. (Lieutenant)",
        "medical": "Crusher, B. (Cmdr)\nOgawa, A. (Nurse)",
        "security": "Worf (Lt. Cmdr)\nDaniels (Lieutenant)",
    }
    return departments.get(department, f"Unknown department: {department}")

# ============================================================
# TOOLS โ€” Weapons & Systems (model-controlled, may have side effects)
# ============================================================

@enterprise.tool()
def fire_phasers(target: str, power_level: int = 50) -> str:
    """Fire phaser array at specified target. Power level 1-100."""
    if power_level < 1 or power_level > 100:
        raise ValueError("Power level must be between 1 and 100!")
    if power_level > 80:
        return f"Maximum phaser discharge at {target}. Direct hit!"
    return f"Phaser burst at {target}, power level {power_level}%. Standing by."

@enterprise.tool()
def raise_shields(configuration: str = "standard") -> str:
    """Raise deflector shields. Configurations: standard, modulated, metaphasic."""
    configs = {
        "standard": "Standard shield configuration active. All frequencies rotating.",
        "modulated": "Shields modulated to counter Borg cutting beam frequency.",
        "metaphasic": "Metaphasic shields engaged. Safe for stellar corona entry.",
    }
    result = configs.get(configuration)
    if not result:
        raise ValueError(f"Unknown shield configuration: {configuration}")
    return result

@enterprise.tool()
def engage_warp(factor: float) -> str:
    """Engage warp drive. Factor must be between 1 and 9.99."""
    if factor < 1 or factor > 9.99:
        raise ValueError(f"Warp factor {factor} outside safe parameters (1-9.99)!")
    if factor > 9.9:
        return f"WARNING: Warp {factor} engaged. Hull stress approaching critical!"
    return f"Warp {factor} engaged. Smooth sailing through subspace."

@enterprise.tool()
async def hail_starbase(starbase_id: int) -> str:
    """Open hailing frequencies to a Federation starbase."""
    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(
                f"https://api.example.com/starbase/{starbase_id}",
                timeout=10.0
            )
            return response.json().get("message", "Channel open. No response.")
        except httpx.RequestError:
            return f"Unable to establish subspace link to Starbase {starbase_id}."

# ============================================================
# PROMPTS โ€” Tactical Patterns (user-controlled, selectable)
# ============================================================

@enterprise.prompt()
def tactical_analysis(threat: str, quadrant: str = "Alpha") -> str:
    """Request a tactical analysis of a threat."""
    return (
        f"You are Lt. Commander Worf, tactical officer aboard the USS Enterprise.\n\n"
        f"Analyze the following threat: {threat} in the {quadrant} Quadrant.\n\n"
        f"Provide:\n"
        f"1) Threat assessment (scale 1-10)\n"
        f"2) Recommended shield configuration\n"
        f"3) Suggested evasive maneuvers\n"
        f"4) Diplomatic options (if any โ€” you may note your skepticism)"
    )

@enterprise.prompt()
def captains_log(stardate: str, summary: str) -> str:
    """Format a Captain's Log entry in the style of Jean-Luc Picard."""
    return (
        f"Captain's Log, Stardate {stardate}.\n\n"
        f"Context: {summary}\n\n"
        f"Write a formal Captain's Log entry. Reflect on the philosophical "
        f"and ethical implications of the situation. Reference relevant literature "
        f"or historical parallels where appropriate. Maintain the measured, "
        f"contemplative tone characteristic of Captain Picard."
    )

# ============================================================
# MAIN โ€” Launch the ship
# ============================================================

if __name__ == "__main__":
    enterprise.run()

Save this as warp_core.py, run mcp dev warp_core.py to test, then mcp install warp_core.py to deploy to Claude Desktop. Your ship is now fully operational.

There are four lights. ๐Ÿ––