build_encounter
Calculate balanced D&D 5e encounters by determining XP budgets for different difficulty levels and suggesting monster combinations based on party size and level.
Instructions
Build balanced D&D 5e encounters. Given party size and level, calculates XP budgets for each difficulty and suggests monster combinations.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| party_size | Yes | Number of party members (1-10) | |
| party_level | Yes | Average party level (1-20) | |
| difficulty | No | Target difficulty. If omitted, shows budgets and suggestions for all difficulties. | |
| monster_cr_min | No | Minimum monster CR to consider (numeric, e.g. 0.25 for 1/4) | |
| monster_cr_max | No | Maximum monster CR to consider (numeric) |
Implementation Reference
- src/tools/build-encounter.ts:190-249 (handler)The handler function for the build_encounter MCP tool, which takes party details and monster CR constraints to generate and format balanced encounter suggestions.
async ({ party_size, party_level, difficulty, monster_cr_min, monster_cr_max }) => { const budgets = calculatePartyBudget(party_size, party_level); const crMin = monster_cr_min ?? 0; const crMax = monster_cr_max ?? party_level + 3; const monsters = getMonstersByCrRange(db, crMin, crMax); const lines: string[] = []; lines.push(`# Encounter Builder`); lines.push(''); lines.push(`**Party:** ${party_size} characters at level ${party_level}`); lines.push(`**Monster CR range:** ${crMin}–${crMax}`); lines.push(''); // XP budget table lines.push('## XP Budgets'); lines.push(''); lines.push('| Difficulty | XP Budget |'); lines.push('|------------|-----------|'); for (const d of DIFFICULTIES) { const marker = difficulty === d ? ' ←' : ''; lines.push(`| ${d.charAt(0).toUpperCase() + d.slice(1)} | ${budgets[d].toLocaleString()} XP${marker} |`); } lines.push(''); if (monsters.length === 0) { lines.push('*No SRD monsters found in the specified CR range.*'); return { content: [{ type: 'text' as const, text: lines.join('\n') }], }; } // Suggest groups for target difficulties const targetDifficulties: Difficulty[] = difficulty ? [difficulty] : DIFFICULTIES; for (const d of targetDifficulties) { const budget = budgets[d]; lines.push(`## ${d.charAt(0).toUpperCase() + d.slice(1)} Encounters (${budget.toLocaleString()} XP)`); lines.push(''); const groups = findMonsterGroups(monsters, budget, 5); if (groups.length === 0) { lines.push('*No suitable monster combinations found for this budget and CR range. Try widening the CR range.*'); } else { groups.forEach((group, i) => { lines.push(`**Option ${i + 1}:**`); lines.push(formatGroup(group)); lines.push(''); }); } lines.push(''); } lines.push('---'); lines.push('*Adjusted XP uses DMG encounter multipliers based on monster count. The actual difficulty may vary based on terrain, tactics, and party composition.*'); return { content: [{ type: 'text' as const, text: lines.join('\n') }], }; }, - src/tools/build-encounter.ts:170-189 (schema)Zod-based schema for the build_encounter tool input parameters.
{ description: 'Build balanced D&D 5e encounters. Given party size and level, calculates XP budgets for each difficulty and suggests monster combinations.', inputSchema: { party_size: z.number().min(1).max(10).describe('Number of party members (1-10)'), party_level: z.number().min(1).max(20).describe('Average party level (1-20)'), difficulty: z .enum(['easy', 'medium', 'hard', 'deadly']) .optional() .describe('Target difficulty. If omitted, shows budgets and suggestions for all difficulties.'), monster_cr_min: z .number() .optional() .describe('Minimum monster CR to consider (numeric, e.g. 0.25 for 1/4)'), monster_cr_max: z .number() .optional() .describe('Maximum monster CR to consider (numeric)'), }, }, - src/tools/build-encounter.ts:164-169 (registration)The registration function for the build_encounter MCP tool.
export function registerBuildEncounter( server: McpServer, db: Database.Database, ): void { server.registerTool( 'build_encounter',