search_monsters
Search D&D 5e SRD monsters by name, challenge rating, type, size, or alignment to retrieve complete stat blocks for encounter planning and game preparation.
Instructions
Search D&D 5e SRD monsters by name, CR, type, size, or alignment. Returns full stat blocks.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | Search term for monster name or description | |
| cr | No | Exact challenge rating (e.g. "1/4", "5") | |
| cr_min | No | Minimum challenge rating (numeric, e.g. 0.25 for 1/4) | |
| cr_max | No | Maximum challenge rating (numeric) | |
| type | No | Monster type (e.g. "beast", "undead", "dragon") | |
| size | No | Size category (Tiny, Small, Medium, Large, Huge, Gargantuan) | |
| alignment | No | Alignment (e.g. "chaotic evil", "neutral") | |
| limit | No | Results per page (max 50) | |
| offset | No | Offset for pagination |
Implementation Reference
- src/tools/search-monsters.ts:135-169 (handler)The tool handler function for `search_monsters` which processes the search request and formats the database results.
async ({ query, cr, cr_min, cr_max, type, size, alignment, limit, offset }) => { const result = searchMonsters(db, { query, cr, cr_min, cr_max, type, size, alignment, limit, offset, }); if (result.rows.length === 0) { return { content: [ { type: 'text' as const, text: 'No monsters found matching your criteria. Try a broader search — for example, remove some filters or use a partial name.', }, ], isError: true, }; } const start = (offset ?? 0) + 1; const end = (offset ?? 0) + result.rows.length; const header = `Found ${result.total} monster${result.total === 1 ? '' : 's'} (showing ${start}-${end})\n`; const monsters = result.rows.map(formatMonster).join('\n\n---\n\n'); return { content: [{ type: 'text' as const, text: header + '\n' + monsters }], }; }, - src/tools/search-monsters.ts:114-171 (registration)The registration function for the `search_monsters` tool, including its input schema and description.
export function registerSearchMonsters( server: McpServer, db: Database.Database, ): void { server.registerTool( 'search_monsters', { description: 'Search D&D 5e SRD monsters by name, CR, type, size, or alignment. Returns full stat blocks.', inputSchema: { query: z.string().optional().describe('Search term for monster name or description'), cr: z.string().optional().describe('Exact challenge rating (e.g. "1/4", "5")'), cr_min: z.number().optional().describe('Minimum challenge rating (numeric, e.g. 0.25 for 1/4)'), cr_max: z.number().optional().describe('Maximum challenge rating (numeric)'), type: z.string().optional().describe('Monster type (e.g. "beast", "undead", "dragon")'), size: z.string().optional().describe('Size category (Tiny, Small, Medium, Large, Huge, Gargantuan)'), alignment: z.string().optional().describe('Alignment (e.g. "chaotic evil", "neutral")'), limit: z.number().min(1).max(50).default(10).describe('Results per page (max 50)'), offset: z.number().min(0).default(0).describe('Offset for pagination'), }, }, async ({ query, cr, cr_min, cr_max, type, size, alignment, limit, offset }) => { const result = searchMonsters(db, { query, cr, cr_min, cr_max, type, size, alignment, limit, offset, }); if (result.rows.length === 0) { return { content: [ { type: 'text' as const, text: 'No monsters found matching your criteria. Try a broader search — for example, remove some filters or use a partial name.', }, ], isError: true, }; } const start = (offset ?? 0) + 1; const end = (offset ?? 0) + result.rows.length; const header = `Found ${result.total} monster${result.total === 1 ? '' : 's'} (showing ${start}-${end})\n`; const monsters = result.rows.map(formatMonster).join('\n\n---\n\n'); return { content: [{ type: 'text' as const, text: header + '\n' + monsters }], }; }, ); }