UNPKG

url-builder-mcp

Version:

URL Builder MCP Server - Professional URL construction with intelligent parameter handling for Claude Desktop

359 lines (337 loc) 11.8 kB
#!/usr/bin/env node /** * URL Builder MCP Server * * MCP server that provides URL building tools following project-specific rules */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; import { buildBookingUrl, buildInternalUrl, buildCompleteUrl, validateUrlParams } from './urlBuilder.js'; import { UrlParams } from './types.js'; class UrlBuilderServer { private server: Server; constructor() { this.server = new Server({ name: 'url-builder-mcp', version: '1.0.0', }); this.setupToolHandlers(); this.setupErrorHandling(); } private setupErrorHandling(): void { this.server.onerror = error => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } private setupToolHandlers(): void { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'build_booking_url', description: 'Build external booking URL with query parameters', inputSchema: { type: 'object', properties: { checkIn: { type: 'string', description: 'Check-in date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, checkOut: { type: 'string', description: 'Check-out date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, guestName: { type: 'string', description: 'Guest name (required)', }, guestPhone: { type: 'string', description: 'Guest phone (required)', }, adults: { type: 'number', description: 'Number of adults (optional, default: 2)', minimum: 1, maximum: 20, }, children: { type: 'number', description: 'Number of children (optional, default: 0)', minimum: 0, maximum: 10, }, roomType: { type: 'string', description: 'Room type name (optional, e.g., "garden-deluxe")', }, guestEmail: { type: 'string', description: 'Guest email (optional)', }, addUtmTracking: { type: 'boolean', description: 'Add UTM tracking parameters for traffic analysis (default: true)', }, utmCampaign: { type: 'string', description: 'Custom UTM campaign name (default: "url-builder-mcp")', }, utmSource: { type: 'string', description: 'Custom UTM source (default: "claude-desktop")', }, utmMedium: { type: 'string', description: 'Custom UTM medium (default: "ai-assistant")', }, }, required: ['checkIn', 'checkOut', 'guestName', 'guestPhone'], }, }, { name: 'build_internal_url', description: 'Build internal project URL with query parameters and UTM tracking', inputSchema: { type: 'object', properties: { checkIn: { type: 'string', description: 'Check-in date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, checkOut: { type: 'string', description: 'Check-out date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, adults: { type: 'number', description: 'Number of adults (optional, default: 2)', minimum: 1, maximum: 20, }, children: { type: 'number', description: 'Number of children (optional, default: 0)', minimum: 0, maximum: 10, }, roomType: { type: 'string', description: 'Room type name (optional)', }, guestName: { type: 'string', description: 'Guest name (optional)', }, guestEmail: { type: 'string', description: 'Guest email (optional)', }, guestPhone: { type: 'string', description: 'Guest phone (optional)', }, useBaseDomain: { type: 'boolean', description: 'Use base domain for complete URL (default: false)', }, addUtmTracking: { type: 'boolean', description: 'Add UTM tracking parameters (default: true)', }, }, required: ['checkIn', 'checkOut'], }, }, { name: 'build_complete_url', description: 'Build complete URL using configurable base domain with UTM tracking', inputSchema: { type: 'object', properties: { relativePath: { type: 'string', description: 'Relative path to append to base domain (e.g., "/api/bookings")', }, checkIn: { type: 'string', description: 'Check-in date in YYYY-MM-DD format (optional)', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, checkOut: { type: 'string', description: 'Check-out date in YYYY-MM-DD format (optional)', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, adults: { type: 'number', description: 'Number of adults (optional)', minimum: 1, maximum: 20, }, children: { type: 'number', description: 'Number of children (optional)', minimum: 0, maximum: 10, }, addUtmTracking: { type: 'boolean', description: 'Add UTM tracking parameters (default: true)', }, }, required: ['relativePath'], }, }, { name: 'validate_url_params', description: 'Validate URL parameters without building URLs', inputSchema: { type: 'object', properties: { checkIn: { type: 'string', description: 'Check-in date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, checkOut: { type: 'string', description: 'Check-out date in YYYY-MM-DD format', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, adults: { type: 'number', description: 'Number of adults (optional)', minimum: 1, maximum: 20, }, children: { type: 'number', description: 'Number of children (optional)', minimum: 0, maximum: 10, }, roomType: { type: 'string', description: 'Room type name (optional, e.g., "garden-deluxe")', }, guestName: { type: 'string', description: 'Guest name (optional)', }, guestEmail: { type: 'string', description: 'Guest email (optional)', }, guestPhone: { type: 'string', description: 'Guest phone (optional)', }, }, required: ['checkIn', 'checkOut'], }, }, ], })); this.server.setRequestHandler(CallToolRequestSchema, async request => { const { name, arguments: args } = request.params; try { switch (name) { case 'build_booking_url': { const { addUtmTracking, utmCampaign, utmSource, utmMedium, ...params } = args as any; // Build options with UTM parameters const options = { addUtmTracking: addUtmTracking !== false, // Default to true customUtm: { ...(utmCampaign && { utm_campaign: utmCampaign }), ...(utmSource && { utm_source: utmSource }), ...(utmMedium && { utm_medium: utmMedium }), }, }; const url = buildBookingUrl(params as UrlParams, options); return { content: [ { type: 'text', text: `Booking URL with UTM tracking: ${url}`, }, ], }; } case 'build_internal_url': { const { useBaseDomain, addUtmTracking, ...params } = args as any; const options = { useBaseDomain: useBaseDomain || false, addUtmTracking: addUtmTracking !== false, }; const url = buildInternalUrl(params as UrlParams, options); return { content: [ { type: 'text', text: `Internal URL with UTM tracking: ${url}`, }, ], }; } case 'build_complete_url': { const { relativePath, addUtmTracking, ...params } = args as any; const options = { addUtmTracking: addUtmTracking !== false, }; // Only pass params if they exist const urlParams = Object.keys(params).length > 0 ? (params as UrlParams) : undefined; const url = buildCompleteUrl(relativePath, urlParams, options); return { content: [ { type: 'text', text: `Complete URL with UTM tracking: ${url}`, }, ], }; } case 'validate_url_params': { const params = args as unknown as UrlParams; const validation = validateUrlParams(params); return { content: [ { type: 'text', text: validation.valid ? 'Parameters are valid' : `Validation errors: ${validation.errors.join(', ')}`, }, ], }; } default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }); } async run(): Promise<void> { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('URL Builder MCP server running on stdio'); } } const server = new UrlBuilderServer(); server.run().catch(console.error);