random-tables-mcp
Version:
An MCP (Model Context Protocol) server for managing and rolling on random-table assets used in tabletop RPGs. Create, update, and roll on random tables with support for nested tables, weighted entries, and range-based results.
174 lines • 7.6 kB
JavaScript
import { z } from 'zod';
import { Range, TableEntry } from '../../../../domain/index.js';
import { v4 as uuidv4 } from 'uuid';
import { BaseTool } from './tool.js';
/**
* Tool for updating tables.
*/
export class UpdateTableTool extends BaseTool {
/**
* Creates a new UpdateTableTool instance.
* @param tableService The table service to use.
*/
constructor(tableService) {
super();
this.tableService = tableService;
}
/**
* Gets the name of the tool.
* @returns The tool name.
*/
getToolName() {
return 'update_table';
}
/**
* Gets the description of the tool.
* @returns The tool description.
*/
getToolDescription() {
return 'Update an existing random table. Table entries can include templates to reference other tables using the format {{reference-title::table-id::table-name::roll-number::separator}}.';
}
/**
* Gets the input schema for the tool.
* @returns The input schema.
*/
getInputSchema() {
return z.object({
tableId: z.string().describe('ID of the table to update'),
updates: z
.object({
name: z.string().optional().describe('New table name'),
description: z.string().optional().describe('New table description'),
entries: z
.object({
add: z
.array(z.object({
content: z
.string()
.describe('The content of this entry. Can include templates in the format {{reference-title::table-id::table-name::roll-number::separator}} to reference other tables.'),
weight: z
.number()
.optional()
.default(1)
.describe('Probability weight (default: 1)'),
range: z
.object({
min: z.number().describe('Minimum value (inclusive)'),
max: z.number().describe('Maximum value (inclusive)'),
})
.optional()
.describe('Optional range of values this entry corresponds to'),
}))
.optional()
.describe('Entries to add'),
update: z
.array(z.object({
id: z.string().describe('ID of the entry to update'),
updates: z
.object({
content: z
.string()
.optional()
.describe('New content. Can include templates in the format {{reference-title::table-id::table-name::roll-number::separator}} to reference other tables.'),
weight: z.number().optional().describe('New weight'),
range: z
.object({
min: z.number().describe('Minimum value (inclusive)'),
max: z.number().describe('Maximum value (inclusive)'),
})
.optional()
.describe('New range'),
})
.describe('Updates to apply to the entry'),
}))
.optional()
.describe('Entries to update'),
remove: z.array(z.string()).optional().describe('IDs of entries to remove'),
})
.optional()
.refine(data => data === undefined ||
data.add !== undefined ||
data.update !== undefined ||
data.remove !== undefined, {
message: "At least one of 'add', 'update', or 'remove' must be provided when updating entries",
})
.describe('Entry updates'),
})
.refine(data => data.name !== undefined || data.description !== undefined || data.entries !== undefined, {
message: 'At least one update property (name, description, or entries) must be provided',
})
.describe('Updates to apply to the table'),
});
}
/**
* Gets the output schema for the tool.
* @returns The output schema.
*/
getOutputSchema() {
return z.object({
success: z.boolean().describe('Whether the update was successful'),
});
}
/**
* Implements the tool execution logic.
* @param args The validated tool arguments.
* @returns The tool result.
*/
async executeImpl(args) {
// Prepare the updates object
const updates = {};
// Add name and description updates if provided
if (args.updates.name !== undefined) {
updates.name = args.updates.name;
}
if (args.updates.description !== undefined) {
updates.description = args.updates.description;
}
// Add entry updates if provided
if (args.updates.entries) {
updates.entries = {};
// Add new entries
if (args.updates.entries.add) {
updates.entries.add = args.updates.entries.add.map(entry => {
// Generate a unique ID for the entry
const id = uuidv4();
// Create a Range object if range is provided
const range = entry.range ? new Range(entry.range.min, entry.range.max) : undefined;
// Create the TableEntry with all required parameters
return new TableEntry(id, entry.content, entry.weight ?? 1, range);
});
}
// Update existing entries
if (args.updates.entries.update) {
updates.entries.update = args.updates.entries.update.map(({ id, updates: entryUpdatesData }) => {
// Create a plain object with the properties to update
const entryUpdates = {};
// Copy content if provided
if (entryUpdatesData.content !== undefined) {
entryUpdates.content = entryUpdatesData.content;
}
// Copy weight if provided
if (entryUpdatesData.weight !== undefined) {
entryUpdates.weight = entryUpdatesData.weight;
}
// Create Range object if range is provided
if (entryUpdatesData.range !== undefined) {
entryUpdates.range = new Range(entryUpdatesData.range.min, entryUpdatesData.range.max);
}
return {
id,
updates: entryUpdates,
};
});
}
// Remove entries
if (args.updates.entries.remove) {
updates.entries.remove = args.updates.entries.remove;
}
}
// Call the table service to update the table
await this.tableService.updateTable(args.tableId, updates);
return { success: true };
}
}
//# sourceMappingURL=update-table-tool.js.map