Skip to main content
Glama
turn-management-tools.ts10.1 kB
import { z } from 'zod'; import { getDb } from '../storage/index.js'; import { TurnStateRepository } from '../storage/repos/turn-state.repo.js'; import { NationRepository } from '../storage/repos/nation.repo.js'; import { DiplomacyRepository } from '../storage/repos/diplomacy.repo.js'; import { RegionRepository } from '../storage/repos/region.repo.js'; import { TurnProcessor } from '../engine/strategy/turn-processor.js'; import { ConflictResolver } from '../engine/strategy/conflict-resolver.js'; import { TurnActionSchema } from '../schema/turn-state.js'; // Tool Definitions export const TurnManagementTools = { INIT_TURN_STATE: { name: 'init_turn_state', description: 'Initialize turn management for a world (call once per world)', inputSchema: z.object({ worldId: z.string() }) }, GET_TURN_STATUS: { name: 'get_turn_status', description: 'Check current turn state and which nations are ready', inputSchema: z.object({ worldId: z.string() }) }, SUBMIT_TURN_ACTIONS: { name: 'submit_turn_actions', description: 'Submit actions for this turn (batched)', inputSchema: z.object({ worldId: z.string(), nationId: z.string(), actions: z.array(TurnActionSchema) }) }, MARK_READY: { name: 'mark_ready', description: 'Signal that your nation is done planning for this turn', inputSchema: z.object({ worldId: z.string(), nationId: z.string() }) }, POLL_TURN_RESULTS: { name: 'poll_turn_results', description: 'Check if turn has resolved and get results', inputSchema: z.object({ worldId: z.string(), turnNumber: z.number() }) } } as const; // Handler export async function handleTurnManagementTool(name: string, args: any) { const db = getDb(); const turnStateRepo = new TurnStateRepository(db); const nationRepo = new NationRepository(db); const diplomacyRepo = new DiplomacyRepository(db); const regionRepo = new RegionRepository(db); switch (name) { case 'init_turn_state': { const { worldId } = args; const existing = turnStateRepo.findByWorldId(worldId); if (existing) { return { content: [{ type: 'text', text: 'Turn state already initialized' }] }; } const now = new Date().toISOString(); turnStateRepo.create({ worldId, currentTurn: 1, turnPhase: 'planning', phaseStartedAt: now, nationsReady: [], createdAt: now, updatedAt: now }); return { content: [{ type: 'text', text: 'Turn state initialized' }] }; } case 'get_turn_status': { const { worldId } = args; const turnState = turnStateRepo.findByWorldId(worldId); if (!turnState) { return { content: [{ type: 'text', text: 'Turn state not initialized' }] }; } const allNations = nationRepo.findByWorldId(worldId); const waitingFor = allNations .filter(n => !turnState.nationsReady.includes(n.id)) .map(n => n.name); return { content: [{ type: 'text', text: JSON.stringify({ currentTurn: turnState.currentTurn, phase: turnState.turnPhase, nationsReady: turnState.nationsReady.length, totalNations: allNations.length, waitingFor, canSubmitActions: turnState.turnPhase === 'planning' }, null, 2) }] }; } case 'submit_turn_actions': { const { worldId, nationId, actions } = args; const turnState = turnStateRepo.findByWorldId(worldId); if (!turnState || turnState.turnPhase !== 'planning') { return { content: [{ type: 'text', text: 'Cannot submit actions (not in planning phase)' }] }; } // Execute actions immediately (for simplicity) // In production, you might queue these for (const action of actions) { switch (action.type) { case 'claim_region': diplomacyRepo.createClaim({ id: `claim-${Date.now()}-${Math.random()}`, nationId, regionId: action.regionId!, claimStrength: 100, justification: action.justification, createdAt: new Date().toISOString() }); break; case 'propose_alliance': // Use existing diplomacy logic const relation = diplomacyRepo.getRelation(nationId, action.toNationId!); if (!relation || relation.opinion >= 50) { diplomacyRepo.upsertRelation({ fromNationId: nationId, toNationId: action.toNationId!, opinion: relation?.opinion || 50, isAllied: true, truceUntil: undefined, updatedAt: new Date().toISOString() }); } break; // Add other action types as needed } } return { content: [{ type: 'text', text: `${actions.length} actions submitted for turn ${turnState.currentTurn}` }] }; } case 'mark_ready': { const { worldId, nationId } = args; const turnState = turnStateRepo.findByWorldId(worldId); if (!turnState || turnState.turnPhase !== 'planning') { return { content: [{ type: 'text', text: 'Cannot mark ready (not in planning phase)' }] }; } turnStateRepo.addReadyNation(worldId, nationId); const updated = turnStateRepo.findByWorldId(worldId)!; const allNations = nationRepo.findByWorldId(worldId); // Check if all ready -> trigger resolution if (updated.nationsReady.length === allNations.length && allNations.length > 0) { // Start resolution turnStateRepo.updatePhase(worldId, 'resolution'); // Process turn const conflictResolver = new ConflictResolver(); const turnProcessor = new TurnProcessor(nationRepo, regionRepo, diplomacyRepo, conflictResolver); turnProcessor.processTurn(worldId, updated.currentTurn); // Move to finished turnStateRepo.updatePhase(worldId, 'finished'); turnStateRepo.incrementTurn(worldId); turnStateRepo.clearReadyNations(worldId); turnStateRepo.updatePhase(worldId, 'planning'); return { content: [{ type: 'text', text: JSON.stringify({ message: 'All nations ready! Turn resolved.', turnResolved: updated.currentTurn, nextTurn: updated.currentTurn + 1 }, null, 2) }] }; } return { content: [{ type: 'text', text: JSON.stringify({ ready: true, allReady: false, waitingFor: allNations.filter(n => !updated.nationsReady.includes(n.id)).map(n => n.name) }, null, 2) }] }; } case 'poll_turn_results': { const { worldId, turnNumber } = args; const turnState = turnStateRepo.findByWorldId(worldId); if (!turnState) { return { content: [{ type: 'text', text: 'Turn state not found' }] }; } if (turnState.currentTurn > turnNumber) { // Turn has resolved const events = diplomacyRepo.getEventsByWorld(worldId, turnNumber); return { content: [{ type: 'text', text: JSON.stringify({ resolved: true, events, nextTurn: turnState.currentTurn, phase: turnState.turnPhase }, null, 2) }] }; } else if (turnState.turnPhase === 'resolution') { return { content: [{ type: 'text', text: JSON.stringify({ resolved: false, phase: 'resolution', message: 'Turn is being resolved...' }, null, 2) }] }; } else { return { content: [{ type: 'text', text: JSON.stringify({ resolved: false, phase: turnState.turnPhase, message: 'Turn not yet resolved' }, null, 2) }] }; } } default: throw new Error(`Unknown turn management tool: ${name}`); } }

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/Mnehmos/rpg-mcp'

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