The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "MUD Player Agent",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "player-action",
"responseMode": "onReceived",
"options": {}
},
"id": "webhook-trigger",
"name": "Player Action Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
240,
300
]
},
{
"parameters": {
"functionCode": "// Extract player action from webhook\nconst action = $input.first().json;\nconst playerId = $env.PLAYER_ID || 'player1';\nconst characterName = $env.CHARACTER_NAME || 'Unknown';\nconst characterClass = $env.CHARACTER_CLASS || 'warrior';\n\n// Determine action type\nlet mcpAction = {};\n\nif (action.type === 'create_character') {\n mcpAction = {\n tool: 'create_character',\n parameters: {\n player_id: playerId,\n name: characterName,\n character_class: characterClass\n }\n };\n} else if (action.type === 'move') {\n mcpAction = {\n tool: 'move_character',\n parameters: {\n player_id: playerId,\n direction: action.direction || 'north'\n }\n };\n} else if (action.type === 'examine') {\n mcpAction = {\n tool: 'examine_object',\n parameters: {\n player_id: playerId,\n target: action.target || 'room'\n }\n };\n} else if (action.type === 'skill_check') {\n mcpAction = {\n tool: 'attempt_skill_check',\n parameters: {\n player_id: playerId,\n skill_type: action.skill_type || 'strength',\n target: action.target\n }\n };\n} else if (action.type === 'rest') {\n mcpAction = {\n tool: 'rest_character',\n parameters: {\n player_id: playerId\n }\n };\n} else if (action.type === 'status') {\n mcpAction = {\n tool: 'get_character_status',\n parameters: {\n player_id: playerId\n }\n };\n} else {\n // Default to getting status\n mcpAction = {\n tool: 'get_character_status',\n parameters: {\n player_id: playerId\n }\n };\n}\n\nreturn {\n json: {\n action: mcpAction,\n playerId: playerId,\n originalAction: action\n }\n};"
},
"id": "action-parser",
"name": "Parse Player Action",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
460,
300
]
},
{
"parameters": {
"url": "http://mcp-mud-server:8080/mcp",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "method",
"value": "tools/call"
},
{
"name": "params",
"value": "={{ $json.action }}"
}
]
},
"options": {}
},
"id": "mcp-call",
"name": "Call MCP Tool",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
680,
300
]
},
{
"parameters": {
"functionCode": "// Process MCP response and format for output\nconst mcpResponse = $input.first().json;\nconst originalAction = $node['Parse Player Action'].json.originalAction;\nconst playerId = $node['Parse Player Action'].json.playerId;\n\n// Extract result from MCP response\nlet result = {};\nif (mcpResponse.result && mcpResponse.result.content) {\n try {\n result = JSON.parse(mcpResponse.result.content[0].text);\n } catch (e) {\n result = { success: false, error: 'Failed to parse MCP response' };\n }\n} else {\n result = { success: false, error: 'No response from MCP server' };\n}\n\n// Format response for the player\nlet formattedResponse = {\n playerId: playerId,\n action: originalAction.type,\n success: result.success,\n timestamp: new Date().toISOString()\n};\n\nif (result.success) {\n formattedResponse.message = result.message || 'Action completed successfully';\n formattedResponse.data = result;\n} else {\n formattedResponse.error = result.error || 'Unknown error occurred';\n}\n\n// Add specific formatting for different action types\nif (originalAction.type === 'status' && result.character_data) {\n formattedResponse.characterStatus = {\n name: result.character_data.name,\n class: result.character_data.class,\n level: result.character_data.level,\n health: result.character_data.health,\n mana: result.character_data.mana,\n location: result.character_data.location\n };\n} else if (originalAction.type === 'move' && result.room_description) {\n formattedResponse.roomDescription = result.room_description;\n} else if (originalAction.type === 'examine' && result.description) {\n formattedResponse.description = result.description;\n} else if (originalAction.type === 'skill_check' && result.skill_check_result) {\n formattedResponse.skillResult = {\n success: result.skill_check_result.success,\n total: result.skill_check_result.total,\n roll: result.skill_check_result.roll,\n successLevel: result.skill_check_result.success_level\n };\n}\n\nreturn { json: formattedResponse };"
},
"id": "response-formatter",
"name": "Format Response",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
900,
300
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json }}"
},
"id": "webhook-response",
"name": "Send Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
1120,
300
]
},
{
"parameters": {
"url": "http://ollama:11434/api/generate",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "llama3.2"
},
{
"name": "prompt",
"value": "You are playing a character named {{ $env.CHARACTER_NAME }} ({{ $env.CHARACTER_CLASS }}) in a text-based dungeon adventure game. Based on the current game state, decide what action to take next. Current status: {{ $json.data }}. Respond with a JSON object containing your next action, like: {\"type\": \"move\", \"direction\": \"north\"} or {\"type\": \"examine\", \"target\": \"door\"} or {\"type\": \"skill_check\", \"skill_type\": \"strength\", \"target\": \"door\"}."
},
{
"name": "stream",
"value": false
}
]
}
},
"id": "ai-decision",
"name": "AI Decision Making",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
900,
500
]
}
],
"connections": {
"Player Action Webhook": {
"main": [
[
{
"node": "Parse Player Action",
"type": "main",
"index": 0
}
]
]
},
"Parse Player Action": {
"main": [
[
{
"node": "Call MCP Tool",
"type": "main",
"index": 0
}
]
]
},
"Call MCP Tool": {
"main": [
[
{
"node": "Format Response",
"type": "main",
"index": 0
}
]
]
},
"Format Response": {
"main": [
[
{
"node": "Send Response",
"type": "main",
"index": 0
},
{
"node": "AI Decision Making",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {},
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z",
"id": "player-agent-workflow"
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
MUD Player Agent. Uses httpRequest. Webhook trigger; 6 nodes.
Source: https://github.com/sbaumgartner/MCP-Game/blob/cf4e4706437b3216416e269990fad73bf2eaf45c/workflows/player-agent.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Jigsaw API key for image processing, I use this as a gatekeeper/second pair of eyes. LINK to their website https://jigsawstack.com/ SECOND A postgress DATABASE (I use Supabase) LlamaCloud for the pars
Whatsapp Multi Agent System optimized copy 2.0. Uses airtable, httpRequest, errorTrigger. Webhook trigger; 44 nodes.
Invoice Agent. Uses httpRequest, emailSend. Webhook trigger; 29 nodes.
Reputation Engine — SEO QA Agent. Uses httpRequest. Webhook trigger; 28 nodes.
This workflow handles incoming voice calls or audio messages, transcribes them using Whisper (OpenAI) or ElevenLabs, extracts booking intent and preferred time slots using AI, checks availability on C