A newer version of the Gradio SDK is available:
5.34.2
Simple Game Client Guide
This guide shows you how to create a simple client to connect to the MMORPG game server using the Model Context Protocol (MCP). No LLM integration required - just basic game operations.
Table of Contents
- Overview
- Prerequisites
- MCP Server Connection
- Basic Client Example
- Available Game Operations
- Command Reference
- Error Handling
- Advanced Features
Overview
The MMORPG game server exposes its functionality through MCP (Model Context Protocol), allowing external clients to:
- 🎮 Connect to the game server
- 👤 Join/Leave the game as a player
- 🗺️ Move around the game world
- 💬 Send chat messages to other players
- 🎯 Interact with NPCs and game objects
- 📊 Get game state information
Game Server Details
- MCP Endpoint:
http://localhost:7868/gradio_api/mcp/sse
(when running locally) - Protocol: Server-Sent Events (SSE) over HTTP
- Authentication: None required for basic operations
- Data Format: JSON messages following MCP specification
Prerequisites
Before building your client, ensure you have:
Required Dependencies
pip install mcp asyncio aiohttp contextlib
Python Version
- Python 3.8 or higher
- Support for async/await syntax
Game Server Running
Make sure the MMORPG game server is running:
cd c:/Users/Chris4K/Projekte/projecthub/projects/MMOP_second_try
python app.py
The server should be accessible at http://localhost:7868
(UI) and the MCP endpoint at http://localhost:7868/gradio_api/mcp/sse
.
MCP Server Connection
Here's how to establish a connection to the game server:
Basic Connection Setup
import asyncio
from mcp import ClientSession
from mcp.client.sse import sse_client
from contextlib import AsyncExitStack
class GameClient:
def __init__(self, server_url="http://localhost:7868/gradio_api/mcp/sse"):
self.server_url = server_url
self.session = None
self.connected = False
self.tools = []
self.exit_stack = None
self.client_id = None
async def connect(self):
"""Connect to the game server"""
try:
# Clean up any existing connection
if self.exit_stack:
await self.exit_stack.aclose()
self.exit_stack = AsyncExitStack()
# Establish SSE connection
transport = await self.exit_stack.enter_async_context(
sse_client(self.server_url)
)
read_stream, write_callable = transport
# Create MCP session
self.session = await self.exit_stack.enter_async_context(
ClientSession(read_stream, write_callable)
)
# Initialize the session
await self.session.initialize()
# Get available tools/commands
response = await self.session.list_tools()
self.tools = response.tools
self.connected = True
print(f"✅ Connected to game server!")
print(f"Available commands: {[tool.name for tool in self.tools]}")
return True
except Exception as e:
print(f"❌ Connection failed: {e}")
self.connected = False
return False
async def disconnect(self):
"""Disconnect from the game server"""
if self.exit_stack:
await self.exit_stack.aclose()
self.connected = False
print("🔌 Disconnected from game server")
Connection Test
async def test_connection():
client = GameClient()
if await client.connect():
print("Connection successful!")
await client.disconnect()
else:
print("Connection failed!")
# Run the test
asyncio.run(test_connection())
Basic Client Example
Here's a complete working client that can perform basic game operations:
import asyncio
import json
import uuid
from typing import Dict, List, Optional
class SimpleGameClient:
def __init__(self, server_url="http://localhost:7868/gradio_api/mcp/sse"):
self.server_url = server_url
self.session = None
self.connected = False
self.tools = []
self.exit_stack = None
self.client_id = str(uuid.uuid4())[:8]
self.agent_id = None
self.player_name = None
async def connect(self):
"""Connect to the game server"""
try:
if self.exit_stack:
await self.exit_stack.aclose()
self.exit_stack = AsyncExitStack()
transport = await self.exit_stack.enter_async_context(
sse_client(self.server_url)
)
read_stream, write_callable = transport
self.session = await self.exit_stack.enter_async_context(
ClientSession(read_stream, write_callable)
)
await self.session.initialize()
response = await self.session.list_tools()
self.tools = response.tools
self.connected = True
return True
except Exception as e:
print(f"❌ Connection failed: {e}")
return False
async def register_player(self, player_name: str):
"""Register as a new player in the game"""
if not self.connected:
print("❌ Not connected to server")
return False
try:
# Find the register tool
register_tool = next((t for t in self.tools if 'register' in t.name), None)
if not register_tool:
print("❌ Register tool not available")
return False
# Register the player
result = await self.session.call_tool(
register_tool.name,
{"name": player_name, "client_id": self.client_id}
)
# Parse the result
content = self._extract_content(result)
if "registered" in content.lower():
self.player_name = player_name
# Extract agent_id from response if available
if "agent_id" in content or "ID:" in content:
# Parse agent ID from response
lines = content.split('\n')
for line in lines:
if "agent_id" in line.lower() or "id:" in line:
parts = line.split(':')
if len(parts) > 1:
self.agent_id = parts[1].strip()
break
print(f"✅ Registered as player: {player_name}")
return True
else:
print(f"❌ Registration failed: {content}")
return False
except Exception as e:
print(f"❌ Registration error: {e}")
return False
async def move_player(self, direction: str):
"""Move the player in the specified direction"""
if not self.connected or not self.agent_id:
print("❌ Not connected or not registered")
return False
try:
# Find the move tool
move_tool = next((t for t in self.tools if 'move' in t.name), None)
if not move_tool:
print("❌ Move tool not available")
return False
# Move the player
result = await self.session.call_tool(
move_tool.name,
{"client_id": self.client_id, "direction": direction}
)
content = self._extract_content(result)
print(f"🚶 Move result: {content}")
return True
except Exception as e:
print(f"❌ Move error: {e}")
return False
async def send_chat(self, message: str):
"""Send a chat message to other players"""
if not self.connected or not self.agent_id:
print("❌ Not connected or not registered")
return False
try:
# Find the chat tool
chat_tool = next((t for t in self.tools if 'chat' in t.name), None)
if not chat_tool:
print("❌ Chat tool not available")
return False
# Send the chat message
result = await self.session.call_tool(
chat_tool.name,
{"client_id": self.client_id, "message": message}
)
content = self._extract_content(result)
print(f"💬 Chat sent: {content}")
return True
except Exception as e:
print(f"❌ Chat error: {e}")
return False
async def get_game_state(self):
"""Get current game state information"""
if not self.connected:
print("❌ Not connected to server")
return None
try:
# Find the game state tool
state_tool = next((t for t in self.tools if 'state' in t.name or 'game' in t.name), None)
if not state_tool:
print("❌ Game state tool not available")
return None
# Get game state
result = await self.session.call_tool(state_tool.name, {})
content = self._extract_content(result)
try:
# Try to parse as JSON
game_state = json.loads(content)
return game_state
except json.JSONDecodeError:
# Return as text if not JSON
return {"info": content}
except Exception as e:
print(f"❌ Game state error: {e}")
return None
async def interact_with_npc(self, npc_id: str, message: str):
"""Interact with an NPC"""
if not self.connected or not self.agent_id:
print("❌ Not connected or not registered")
return False
try:
# Find the interact tool
interact_tool = next((t for t in self.tools if 'interact' in t.name or 'npc' in t.name), None)
if not interact_tool:
print("❌ Interact tool not available")
return False
# Interact with NPC
result = await self.session.call_tool(
interact_tool.name,
{"client_id": self.client_id, "npc_id": npc_id, "message": message}
)
content = self._extract_content(result)
print(f"🤖 NPC {npc_id}: {content}")
return True
except Exception as e:
print(f"❌ Interaction error: {e}")
return False
def _extract_content(self, result):
"""Extract content from MCP result"""
if hasattr(result, 'content') and result.content:
if isinstance(result.content, list):
return ''.join(str(item) for item in result.content)
return str(result.content)
return str(result)
async def disconnect(self):
"""Disconnect from the server"""
if self.exit_stack:
await self.exit_stack.aclose()
self.connected = False
print("🔌 Disconnected from server")
# Import required modules at the top
from mcp import ClientSession
from mcp.client.sse import sse_client
from contextlib import AsyncExitStack
Available Game Operations
The game server exposes these main operations through MCP tools:
1. Player Management
register_ai_agent
- Register a new playermove_agent
- Move player in game worldget_game_state
- Get current world state
2. Communication
send_chat
- Send chat messagesinteract_with_npc
- Talk to NPCs
3. Game Information
get_game_state
- World state and player list- Player proximity - Detect nearby players/NPCs
Tool Parameters
Each tool expects specific parameters:
# Register Player
{
"name": "PlayerName",
"client_id": "unique_client_id"
}
# Move Player
{
"client_id": "your_client_id",
"direction": "north|south|east|west"
}
# Send Chat
{
"client_id": "your_client_id",
"message": "Hello world!"
}
# Interact with NPC
{
"client_id": "your_client_id",
"npc_id": "npc_identifier",
"message": "Your message to NPC"
}
Command Reference
Basic Usage Example
async def main():
# Create and connect client
client = SimpleGameClient()
if not await client.connect():
print("Failed to connect!")
return
# Register as a player
if not await client.register_player("MyPlayer"):
print("Failed to register!")
return
# Get current game state
state = await client.get_game_state()
print(f"Game state: {state}")
# Move around
await client.move_player("north")
await client.move_player("east")
# Send a chat message
await client.send_chat("Hello everyone!")
# Interact with an NPC
await client.interact_with_npc("weather_oracle", "What's the weather in Berlin?")
# Disconnect
await client.disconnect()
# Run the client
asyncio.run(main())
Interactive Console Client
async def interactive_client():
client = SimpleGameClient()
print("🎮 MMORPG Simple Client")
print("Commands: connect, register <name>, move <direction>, chat <message>, state, quit")
while True:
command = input("> ").strip().split()
if not command:
continue
cmd = command[0].lower()
if cmd == "connect":
if await client.connect():
print("✅ Connected!")
else:
print("❌ Connection failed!")
elif cmd == "register" and len(command) > 1:
name = " ".join(command[1:])
if await client.register_player(name):
print(f"✅ Registered as {name}")
else:
print("❌ Registration failed!")
elif cmd == "move" and len(command) > 1:
direction = command[1]
await client.move_player(direction)
elif cmd == "chat" and len(command) > 1:
message = " ".join(command[1:])
await client.send_chat(message)
elif cmd == "state":
state = await client.get_game_state()
print(f"📊 Game State: {json.dumps(state, indent=2)}")
elif cmd == "npc" and len(command) > 2:
npc_id = command[1]
message = " ".join(command[2:])
await client.interact_with_npc(npc_id, message)
elif cmd == "quit":
await client.disconnect()
break
else:
print("❓ Unknown command")
# Run interactive client
asyncio.run(interactive_client())
Error Handling
Common Issues and Solutions
1. Connection Errors
async def robust_connect(client, max_retries=3):
for attempt in range(max_retries):
try:
if await client.connect():
return True
print(f"Attempt {attempt + 1} failed, retrying...")
await asyncio.sleep(2)
except Exception as e:
print(f"Connection attempt {attempt + 1} error: {e}")
print("❌ All connection attempts failed")
return False
2. Tool Not Available
def find_tool_safe(tools, keywords):
"""Safely find a tool by keywords"""
for keyword in keywords:
tool = next((t for t in tools if keyword in t.name.lower()), None)
if tool:
return tool
return None
# Usage
move_tool = find_tool_safe(client.tools, ['move', 'agent', 'player'])
if not move_tool:
print("❌ No movement tool available")
3. Response Parsing
def safe_parse_response(result):
"""Safely parse MCP response"""
try:
content = client._extract_content(result)
# Try JSON first
try:
return json.loads(content)
except json.JSONDecodeError:
# Return as structured text
return {"message": content, "type": "text"}
except Exception as e:
return {"error": str(e), "type": "error"}
Advanced Features
1. Automatic Reconnection
class RobustGameClient(SimpleGameClient):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.auto_reconnect = True
self.reconnect_delay = 5
async def _ensure_connected(self):
"""Ensure connection is active, reconnect if needed"""
if not self.connected and self.auto_reconnect:
print("🔄 Attempting to reconnect...")
await self.connect()
if self.player_name:
await self.register_player(self.player_name)
async def move_player(self, direction):
await self._ensure_connected()
return await super().move_player(direction)
2. Event Monitoring
class EventMonitorClient(SimpleGameClient):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.event_handlers = {}
def on_chat_message(self, handler):
"""Register handler for chat messages"""
self.event_handlers['chat'] = handler
def on_player_move(self, handler):
"""Register handler for player movements"""
self.event_handlers['move'] = handler
async def poll_events(self):
"""Poll for game events"""
while self.connected:
try:
state = await self.get_game_state()
# Process state changes and trigger handlers
# Implementation depends on game state format
await asyncio.sleep(1)
except Exception as e:
print(f"Event polling error: {e}")
await asyncio.sleep(5)
3. Batch Operations
async def batch_moves(client, moves):
"""Execute multiple moves in sequence"""
for direction in moves:
result = await client.move_player(direction)
if not result:
print(f"❌ Failed to move {direction}")
break
await asyncio.sleep(0.5) # Small delay between moves
# Usage
await batch_moves(client, ["north", "north", "east", "south"])
4. Configuration Management
import json
class ConfigurableClient(SimpleGameClient):
def __init__(self, config_file="client_config.json"):
# Load configuration
try:
with open(config_file, 'r') as f:
config = json.load(f)
except FileNotFoundError:
config = self.default_config()
super().__init__(config.get('server_url', 'http://localhost:7860/gradio_api/mcp/sse'))
self.config = config
def default_config(self):
return {
"server_url": "http://localhost:7860/gradio_api/mcp/sse",
"player_name": "DefaultPlayer",
"auto_reconnect": True,
"move_delay": 0.5
}
Quick Start Script
Save this as quick_client.py
for immediate testing:
#!/usr/bin/env python3
"""
Quick Game Client - Connect and play immediately
Usage: python quick_client.py [player_name]
"""
import asyncio
import sys
from simple_game_client import SimpleGameClient
async def quick_play(player_name="TestPlayer"):
client = SimpleGameClient()
print(f"🎮 Connecting as {player_name}...")
# Connect and register
if not await client.connect():
print("❌ Failed to connect!")
return
if not await client.register_player(player_name):
print("❌ Failed to register!")
return
print("✅ Connected and registered successfully!")
# Basic game session
await client.send_chat(f"Hello! {player_name} has joined the game!")
# Move around a bit
moves = ["north", "east", "south", "west"]
for move in moves:
print(f"🚶 Moving {move}...")
await client.move_player(move)
await asyncio.sleep(1)
# Get final state
state = await client.get_game_state()
print(f"📊 Final state: {state}")
await client.disconnect()
print("👋 Session ended!")
if __name__ == "__main__":
player_name = sys.argv[1] if len(sys.argv) > 1 else "TestPlayer"
asyncio.run(quick_play(player_name))
Run with:
python quick_client.py MyPlayerName
Next Steps
- Customize the client for your specific needs
- Add game-specific features like inventory management
- Implement advanced AI for automated gameplay
- Create GUI interfaces using tkinter, PyQt, or web frameworks
- Build monitoring tools for game statistics
This guide provides the foundation for any client application that needs to interact with the MMORPG game server!