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.
161 lines • 6.46 kB
JavaScript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
import { CreateTableTool, ListTablesTool, RollOnTableTool, UpdateTableTool, GetTableTool, CreateTemplateTool, GetTemplateTool, ListTemplateTool, UpdateTemplateTool, DeleteTemplateTool, EvaluateTemplateTool, } from './tools/index.js';
import { TableResource, TablesResource, TemplateResource, TemplatesResource, } from './resources/index.js';
/**
* MCP Server implementation for Random Tables.
*/
export class McpServer {
/**
* Creates a new McpServer instance.
* @param tableService The table service to use.
* @param rollService The roll service to use.
* @param templateService The roll template service to use.
*/
constructor(tableService, rollService, templateService) {
this.tableService = tableService;
this.rollService = rollService;
this.templateService = templateService;
this.server = new Server({
name: 'random-tables-server',
version: '1.0.0',
}, {
capabilities: {
tools: {},
resources: {},
},
});
// Check if resources can be used (defaults to false if not specified)
const canUseResource = process.env.CAN_USE_RESOURCE === 'true';
// Initialize tools
this.tools = [
new CreateTableTool(tableService),
new RollOnTableTool(rollService),
new UpdateTableTool(tableService),
new ListTablesTool(tableService),
new CreateTemplateTool(templateService),
new GetTemplateTool(templateService),
new ListTemplateTool(templateService),
new UpdateTemplateTool(templateService),
new DeleteTemplateTool(templateService),
new EvaluateTemplateTool(templateService),
];
// Add GetTableTool if resources cannot be used
if (!canUseResource) {
this.tools.push(new GetTableTool(tableService));
}
// Initialize resources
this.resources = [new TablesResource(tableService), new TemplatesResource(templateService)];
// Add TableResource only if resources can be used
if (canUseResource) {
this.resources.push(new TableResource(tableService));
this.resources.push(new TemplateResource(templateService));
}
}
/**
* Initializes the server by registering tools and resources.
*/
initialize() {
this.registerTools();
this.registerResources();
}
/**
* Registers tools with the server.
*/
registerTools() {
this.server.setRequestHandler(ListToolsRequestSchema, () => {
return {
tools: this.tools.map(tool => tool.getToolDefinition()),
};
});
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
const { name, arguments: args } = request.params;
const tool = this.tools.find(t => t.getName() === name);
if (!tool) {
throw new Error(`Unknown tool: ${name}`);
}
const result = await tool.execute(args);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [{ type: 'text', text: `Error: ${errorMessage}` }],
isError: true,
};
}
});
}
/**
* Registers resources with the server.
*/
registerResources() {
// Set up a handler for resource requests
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
try {
const { uri } = request.params;
// Find the resource that matches the URI pattern
for (const resource of this.resources) {
const uriPattern = resource.getUriPattern();
const match = this.matchUriPattern(uri, uriPattern);
if (match) {
const content = await resource.getContent(match);
return {
content: [{ type: 'text', text: JSON.stringify(content, null, 2) }],
};
}
}
throw new Error(`No resource found for URI: ${uri}`);
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [{ type: 'text', text: `Error: ${errorMessage}` }],
isError: true,
};
}
});
}
/**
* Matches a URI against a pattern and extracts parameters.
* @param uri The URI to match.
* @param pattern The pattern to match against.
* @returns An object with extracted parameters, or null if no match.
*/
matchUriPattern(uri, pattern) {
// Convert pattern to regex
const regexPattern = pattern.replace(/{([^}]+)}/g, '([^/]+)');
const regex = new RegExp(`^${regexPattern}$`);
// Extract parameter names from pattern
const paramNames = [];
let match;
const paramRegex = /{([^}]+)}/g;
while ((match = paramRegex.exec(pattern)) !== null) {
paramNames.push(match[1]);
}
// Match URI against regex
const matches = uri.match(regex);
if (!matches) {
return null;
}
// Extract parameters
const params = {};
for (let i = 0; i < paramNames.length; i++) {
params[paramNames[i]] = matches[i + 1];
}
return params;
}
/**
* Starts the server.
*/
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.warn('MCP Random Tables Server running on stdio');
}
}
//# sourceMappingURL=mcp-server.js.map