Skip to main content
Glama

Kalendis MCP

Official
by kalendis-dev
client.ts54.8 kB
/** * Simple client generator for Kalendis API * Generates TypeScript clients with correct x-api-key authentication */ import { ENDPOINTS, EndpointDefinition } from '../endpoints'; import { convertToLocalTime } from '@kalendis/utils'; import * as fs from 'fs'; import * as path from 'path'; import * as readline from 'readline'; export const BASE_URLS = { production: 'https://api.kalendis.dev', staging: 'https://dev-303703761.us-central1.run.app', development: process.env.KALENDIS_API_URL || 'https://sandbox.api.kalendis.dev', } as const;; export type Environment = keyof typeof BASE_URLS; export type Framework = 'react' | 'nextjs' | 'express' | 'vanilla' | 'fastify' | 'nestjs'; export interface GenerateOptions { framework: Framework; environment: Environment; typesImportPath?: string; outputDir?: string; } function needsTimezoneConversion(methodName: string): boolean { return ( methodName.includes('Availability') || methodName.includes('Booking') || methodName.includes('availability') || methodName.includes('booking') ); } function mapResponseType(type: string, useTypes: boolean = true): string { if (!useTypes) return type; if (type.includes('[]')) { const baseType = type.replace('[]', ''); if (isKnownType(baseType)) { return `Types.${baseType}[]`; } return `${baseType}[]`; } if (type.startsWith('Array<')) { const innerType = type.slice(6, -1); if (isKnownType(innerType)) { return `Types.${innerType}[]`; } return type; } if (isKnownType(type)) { return `Types.${type}`; } if (type.startsWith('{') && type.endsWith('}')) { return type; } return type; } function isKnownType(type: string): boolean { const knownTypes = ['User', 'Availability', 'RecurringAvailability', 'AvailabilityException', 'Booking', 'Account']; return knownTypes.includes(type); } function generateParamType(param: { type: string; required: boolean }): string { const tsType = param.type === 'number' ? 'number' : param.type === 'boolean' ? 'boolean' : 'string'; return param.required ? tsType : `${tsType} | undefined`; } function generateBodyType(field: { type: string; required: boolean }): string { if (field.type.includes('[]')) { const baseType = field.type.replace('[]', ''); const mappedType = field.type === 'DaysOfWeek[]' ? 'Types.DaysOfWeek[]' : `${baseType}[]`; return field.required ? mappedType : `${mappedType} | undefined`; } const tsType = field.type === 'number' ? 'number' : field.type === 'boolean' ? 'boolean' : 'string'; return field.required ? tsType : `${tsType} | undefined`; } function generateBaseClient(environment: Environment, typesPath: string): string { return `import * as Types from '${typesPath}'; class KalendisClient { private readonly apiKey: string; private readonly baseUrl: string; constructor(options: { apiKey: string }) { if (!options.apiKey) { throw new Error('API key is required. Pass it in the constructor: new KalendisClient({ apiKey: "your-key" })'); } this.apiKey = options.apiKey; this.baseUrl = process.env.KALENDIS_API_URL || 'https://sandbox.api.kalendis.dev'; } private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> { const url = \`\${this.baseUrl}\${endpoint}\`; try { const response = await fetch(url, { ...options, headers: { 'x-api-key': this.apiKey, 'Content-Type': 'application/json', ...options.headers } }); if (!response.ok) { let errorMessage = \`Kalendis API Error (\${response.status}): \${response.statusText}\`; try { const error = await response.json(); errorMessage = \`Kalendis API Error (\${response.status}): \${error.message || error.error || response.statusText}\`; } catch { // Use default error message if response isn't JSON } if (response.status === 401) { throw new Error('Authentication failed: Invalid or missing API key'); } else if (response.status === 403) { throw new Error('Permission denied: Your API key does not have access to this resource'); } throw new Error(errorMessage); } return response.json(); } catch (error) { if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error(\`Cannot connect to Kalendis API at \${this.baseUrl}. Please ensure the service is running.\`); } throw error; } }`; } function generateMethod(name: string, endpoint: EndpointDefinition): string { const returnType = mapResponseType(endpoint.response.type); if (endpoint.method === 'GET' && endpoint.params) { const paramTypes = Object.entries(endpoint.params) .map(([key, param]) => ` ${key}?: ${generateParamType(param)};`) .join('\n'); return ` async ${name}(params: { ${paramTypes} } = {}): Promise<${returnType}> { const query = new URLSearchParams(); Object.entries(params).forEach(([key, value]) => { if (value !== undefined) { query.append(key, String(value)); } }); const queryString = query.toString(); return this.request<${returnType}>('${endpoint.path}' + (queryString ? '?' + queryString : ''), { method: 'GET' }); }`; } if (endpoint.method === 'DELETE' && endpoint.params) { const paramTypes = Object.entries(endpoint.params) .map(([key, param]) => ` ${key}: ${generateParamType(param)};`) .join('\n'); return ` async ${name}(params: { ${paramTypes} }): Promise<${returnType}> { const query = new URLSearchParams(); Object.entries(params).forEach(([key, value]) => { if (value !== undefined) { query.append(key, String(value)); } }); return this.request<${returnType}>('${endpoint.path}?' + query.toString(), { method: 'DELETE' }); }`; } if ((endpoint.method === 'POST' || endpoint.method === 'PUT') && endpoint.body) { const bodyTypes = Object.entries(endpoint.body) .map(([key, field]) => ` ${key}${field.required ? '' : '?'}: ${generateBodyType(field)};`) .join('\n'); return ` async ${name}(data: { ${bodyTypes} }): Promise<${returnType}> { return this.request<${returnType}>('${endpoint.path}', { method: '${endpoint.method}', body: JSON.stringify(data) }); }`; } return ` async ${name}(): Promise<${returnType}> { return this.request<${returnType}>('${endpoint.path}', { method: '${endpoint.method}' }); }`; } export function generateBackendClient(options: GenerateOptions): string { const typesPath = options.typesImportPath || '../types'; const baseClient = generateBaseClient(options.environment, typesPath); const methods = Object.entries(ENDPOINTS) .map(([name, endpoint]) => generateMethod(name, endpoint)) .join('\n'); return `${baseClient} ${methods} } export default KalendisClient; `; } export function generateFrontendClient(options?: { typesImportPath?: string }): string { const typesPath = options?.typesImportPath || '../types'; return `import * as Types from '${typesPath}'; import { convertToLocalTime } from '@kalendis/utils'; export const api = { getUsers: async (): Promise<Types.User[]> => { const response = await fetch('/api/users'); if (!response.ok) throw new Error('Failed to fetch users'); return response.json(); }, createUser: async (data: { id: string; name: string; email?: string; timezone?: string }): Promise<Types.User> => { const response = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to create user'); return response.json(); }, updateUser: async (data: { id: string; name?: string; timezone?: string }): Promise<Types.User> => { const response = await fetch(\`/api/users/\${data.id}\`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to update user'); return response.json(); }, deleteUser: async (id: string): Promise<Types.User> => { const response = await fetch(\`/api/users/\${id}\`, { method: 'DELETE' }); if (!response.ok) throw new Error('Failed to delete user'); return response.json(); }, getAvailability: async (params?: { userId?: string; start?: string; end?: string }): Promise<Types.Availability[]> => { const query = new URLSearchParams(params as any).toString(); const response = await fetch('/api/availability' + (query ? '?' + query : '')); if (!response.ok) throw new Error('Failed to fetch availability'); const data = await response.json(); return convertToLocalTime(data); }, getAllAvailability: async (params: { start: string; end?: string; timezone?: string }): Promise<Array<{userId: string; start: string; end: string}>> => { const query = new URLSearchParams(params as any).toString(); const response = await fetch('/api/availability/all?' + query); if (!response.ok) throw new Error('Failed to fetch all availability'); const data = await response.json(); return convertToLocalTime(data); }, getMultiUserCalculatedAvailability: async (data: { userIds: string[]; start: string; end?: string; timezone?: string; }): Promise<Array<{userId: string; availability: Array<{start: string; end: string; offset: number}>}>> => { const response = await fetch('/api/availability/calculated', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to fetch calculated availability'); const responseData = await response.json(); return convertToLocalTime(responseData); }, getRecurringAvailabilityByDate: async (data: { userId: string; start: string; cadence: string; frequency: number; numberOfOccurrences: number; timezone?: string; }): Promise<Array<Array<{start: string; end: string; offset: number}>>> => { const response = await fetch('/api/availability/recurring', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to fetch recurring availability'); const responseData = await response.json(); return convertToLocalTime(responseData); }, getMatchingAvailabilityByDate: async (data: { userIds: string[]; start: string; end?: string; timezone?: string; }): Promise<Array<{start: string; end: string}>> => { const response = await fetch('/api/availability/matching', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to fetch matching availability'); const responseData = await response.json(); return convertToLocalTime(responseData); }, addAvailability: async (data: { userId: string; start: string; end: string; timezone?: string }): Promise<Types.Availability> => { const response = await fetch('/api/availability', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to add availability'); const responseData = await response.json(); return convertToLocalTime(responseData); }, updateAvailability: async (data: { id: string; start?: string; end?: string; timezone?: string }): Promise<Types.Availability> => { const response = await fetch(\`/api/availability/\${data.id}\`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to update availability'); const responseData = await response.json(); return convertToLocalTime(responseData); }, deleteAvailability: async (id: string, userId: string): Promise<{message: string}> => { const response = await fetch(\`/api/availability/\${id}?userId=\${userId}\`, { method: 'DELETE' }); if (!response.ok) throw new Error('Failed to delete availability'); return response.json(); }, getRecurringAvailability: async (userId: string): Promise<Types.RecurringAvailability[]> => { const response = await fetch(\`/api/recurring-availability?userId=\${userId}\`); if (!response.ok) throw new Error('Failed to fetch recurring availability'); const responseData = await response.json(); return convertToLocalTime(responseData); }, addRecurringAvailability: async (data: { userId: string; daysOfWeek: Types.DaysOfWeek[]; start: string; end: string; expiration?: string; timezone?: string; }): Promise<Types.RecurringAvailability> => { const response = await fetch('/api/recurring-availability', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to add recurring availability'); const responseData = await response.json(); return convertToLocalTime(responseData); }, updateRecurringAvailability: async (data: { id: string; userId: string; daysOfWeek?: Types.DaysOfWeek[]; start?: string; end?: string; expiration?: string; makeInfinite?: boolean; timezone?: string; }): Promise<Types.RecurringAvailability> => { const response = await fetch(\`/api/recurring-availability/\${data.id}\`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to update recurring availability'); const responseData = await response.json(); return convertToLocalTime(responseData); }, deleteRecurringAvailability: async (id: string, userId: string): Promise<{message: string}> => { const response = await fetch(\`/api/recurring-availability/\${id}?userId=\${userId}\`, { method: 'DELETE' }); if (!response.ok) throw new Error('Failed to delete recurring availability'); return response.json(); }, getAvailabilityException: async (userId: string): Promise<Types.AvailabilityException[]> => { const response = await fetch(\`/api/availability-exceptions?userId=\${userId}\`); if (!response.ok) throw new Error('Failed to fetch availability exceptions'); const responseData = await response.json(); return convertToLocalTime(responseData); }, addAvailabilityException: async (data: { userId: string; start: string; end: string; timezone?: string; }): Promise<Types.AvailabilityException> => { const response = await fetch('/api/availability-exceptions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to add availability exception'); const responseData = await response.json(); return convertToLocalTime(responseData); }, addRecurringAvailabilityException: async (data: { userId: string; daysOfWeek: Types.DaysOfWeek[]; start: string; end: string; expiration?: string; timezone?: string; }): Promise<Types.AvailabilityException[]> => { const response = await fetch('/api/availability-exceptions/recurring', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to add recurring availability exception'); const responseData = await response.json(); return convertToLocalTime(responseData); }, updateAvailabilityException: async (data: { id: string; start?: string; end?: string; timezone?: string; }): Promise<Types.AvailabilityException> => { const response = await fetch(\`/api/availability-exceptions/\${data.id}\`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to update availability exception'); const responseData = await response.json(); return convertToLocalTime(responseData); }, deleteAvailabilityException: async (id: string, userId: string): Promise<{message: string}> => { const response = await fetch(\`/api/availability-exceptions/\${id}?userId=\${userId}\`, { method: 'DELETE' }); if (!response.ok) throw new Error('Failed to delete availability exception'); return response.json(); }, getBookings: async (params: { userId: string; start: string; end?: string }): Promise<Types.Booking[]> => { const query = new URLSearchParams(params as any).toString(); const response = await fetch('/api/bookings?' + query); if (!response.ok) throw new Error('Failed to fetch bookings'); const responseData = await response.json(); return convertToLocalTime(responseData); }, getBookingsByIds: async (bookingIds: string[]): Promise<Types.Booking[]> => { const response = await fetch('/api/bookings/bulk', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ bookingIds }) }); if (!response.ok) throw new Error('Failed to fetch bookings'); const responseData = await response.json(); return convertToLocalTime(responseData); }, createBooking: async (data: { userIds: string[]; start: string; end: string; timezone?: string }): Promise<Types.Booking> => { const response = await fetch('/api/bookings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to create booking'); const responseData = await response.json(); return convertToLocalTime(responseData); }, updateBooking: async (data: { id: string; userIds?: string[]; start?: string; end?: string; timezone?: string }): Promise<Types.Booking> => { const response = await fetch(\`/api/bookings/\${data.id}\`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to update booking'); const responseData = await response.json(); return convertToLocalTime(responseData); }, deleteBooking: async (id: string): Promise<{message: string}> => { const response = await fetch(\`/api/bookings/\${id}\`, { method: 'DELETE' }); if (!response.ok) throw new Error('Failed to delete booking'); return response.json(); }, getAccount: async (): Promise<Types.Account> => { const response = await fetch('/api/account'); if (!response.ok) throw new Error('Failed to fetch account'); return response.json(); }, updateAccount: async (data: { name?: string; active?: boolean }): Promise<Types.Account> => { const response = await fetch('/api/account', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Failed to update account'); return response.json(); } }; export default api; `; } export function generateNextjsRoutes(typesPath: string = '@/lib/types'): Record<string, string> { const files: Record<string, string> = {}; files['app/api/users/route.ts'] = `import { NextRequest, NextResponse } from 'next/server'; import KalendisClient from '@/lib/kalendisClient'; const kalendisClient = new KalendisClient({ apiKey: process.env.KALENDIS_API_KEY! }); export async function GET() { try { const users = await kalendisClient.getUsersByAccountId(); return NextResponse.json(users); } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } } export async function POST(request: NextRequest) { try { const body = await request.json(); const user = await kalendisClient.addUser(body); return NextResponse.json(user, { status: 201 }); } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } } export async function PUT(request: NextRequest) { try { const body = await request.json(); const user = await kalendisClient.updateUser(body); return NextResponse.json(user); } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } } export async function DELETE(request: NextRequest) { try { const { searchParams } = new URL(request.url); const id = searchParams.get('id'); if (!id) throw new Error('User ID required'); const result = await kalendisClient.deleteUser({ id }); return NextResponse.json(result); } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } }`; files['app/api/bookings/route.ts'] = `import { NextRequest, NextResponse } from 'next/server'; import KalendisClient from '@/lib/kalendisClient'; const kalendisClient = new KalendisClient({ apiKey: process.env.KALENDIS_API_KEY! }); export async function GET(request: NextRequest) { try { const { searchParams } = new URL(request.url); const userId = searchParams.get('userId'); const start = searchParams.get('start'); const end = searchParams.get('end') || undefined; if (!userId || !start) { return NextResponse.json({ error: 'userId and start are required' }, { status: 400 }); } const bookings = await kalendisClient.getBooking({ userId, start, end }); return NextResponse.json(bookings); } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } } export async function POST(request: NextRequest) { try { const body = await request.json(); // Check if bulk fetch if (body.bookingIds) { const bookings = await kalendisClient.getBookingsByIds(body); return NextResponse.json(bookings); } // Otherwise create new booking const booking = await kalendisClient.addBooking(body); return NextResponse.json(booking, { status: 201 }); } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } } export async function PUT(request: NextRequest) { try { const body = await request.json(); const booking = await kalendisClient.updateBooking(body); return NextResponse.json(booking); } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } } export async function DELETE(request: NextRequest) { try { const { searchParams } = new URL(request.url); const id = searchParams.get('id'); if (!id) throw new Error('Booking ID required'); const result = await kalendisClient.deleteBooking({ id }); return NextResponse.json(result); } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } }`; files['app/api/availability/route.ts'] = `import { NextRequest, NextResponse } from 'next/server'; import KalendisClient from '@/lib/kalendisClient'; const kalendisClient = new KalendisClient({ apiKey: process.env.KALENDIS_API_KEY! }); export async function GET(request: NextRequest) { try { const { searchParams } = new URL(request.url); const params = Object.fromEntries(searchParams.entries()); // Check which endpoint to call if (request.url.includes('/all')) { const availability = await kalendisClient.getAllAvailability(params as any); return NextResponse.json(availability); } else if (request.url.includes('/recurring-availability')) { const availability = await kalendisClient.getRecurringAvailability(params as any); return NextResponse.json(availability); } else if (request.url.includes('/exceptions')) { const exceptions = await kalendisClient.getAvailabilityException(params as any); return NextResponse.json(exceptions); } else { const availability = await kalendisClient.getAvailability(params); return NextResponse.json(availability); } } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } } export async function POST(request: NextRequest) { try { const body = await request.json(); // Route to correct endpoint based on URL if (request.url.includes('/calculated')) { const result = await kalendisClient.getMultiUserCalculatedAvailability(body); return NextResponse.json(result); } else if (request.url.includes('/recurring-by-date')) { const result = await kalendisClient.getRecurringAvailabilityByDate(body); return NextResponse.json(result); } else if (request.url.includes('/matching')) { const result = await kalendisClient.getMatchingAvailabilityByDate(body); return NextResponse.json(result); } else if (request.url.includes('/recurring-availability')) { const result = await kalendisClient.addRecurringAvailability(body); return NextResponse.json(result, { status: 201 }); } else if (request.url.includes('/exceptions/recurring')) { const result = await kalendisClient.addRecurringAvailabilityException(body); return NextResponse.json(result, { status: 201 }); } else if (request.url.includes('/exceptions')) { const result = await kalendisClient.addAvailabilityException(body); return NextResponse.json(result, { status: 201 }); } else { const availability = await kalendisClient.addAvailability(body); return NextResponse.json(availability, { status: 201 }); } } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } } export async function PUT(request: NextRequest) { try { const body = await request.json(); if (request.url.includes('/recurring-availability')) { const result = await kalendisClient.updateRecurringAvailability(body); return NextResponse.json(result); } else if (request.url.includes('/exceptions')) { const result = await kalendisClient.updateAvailabilityException(body); return NextResponse.json(result); } else { const availability = await kalendisClient.updateAvailability(body); return NextResponse.json(availability); } } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } } export async function DELETE(request: NextRequest) { try { const { searchParams } = new URL(request.url); const id = searchParams.get('id'); if (!id) throw new Error('ID required'); if (request.url.includes('/recurring-availability')) { const userId = searchParams.get('userId'); if (!userId) throw new Error('User ID required'); const result = await kalendisClient.deleteRecurringAvailability({ id, userId }); return NextResponse.json(result); } else if (request.url.includes('/exceptions')) { const userId = searchParams.get('userId'); if (!userId) throw new Error('User ID required'); const result = await kalendisClient.deleteAvailabilityException({ id, userId }); return NextResponse.json(result); } else { const userId = searchParams.get('userId'); if (!userId) throw new Error('User ID required'); const result = await kalendisClient.deleteAvailability({ id, userId }); return NextResponse.json(result); } } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } }`; files['app/api/account/route.ts'] = `import { NextRequest, NextResponse } from 'next/server'; import KalendisClient from '@/lib/kalendisClient'; const kalendisClient = new KalendisClient({ apiKey: process.env.KALENDIS_API_KEY! }); export async function GET() { try { const account = await kalendisClient.getAccount(); return NextResponse.json(account); } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } } export async function PUT(request: NextRequest) { try { const body = await request.json(); const account = await kalendisClient.updateAccount(body); return NextResponse.json(account); } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } }`; return files; } async function askOverwrite(filePath: string): Promise<boolean> { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); return new Promise(resolve => { rl.question(`File exists: ${filePath}. Overwrite? (y/n) `, answer => { rl.close(); resolve(answer.toLowerCase() === 'y'); }); }); } export async function writeFile(filePath: string, content: string): Promise<void> { const fullPath = path.resolve(filePath); if (fs.existsSync(fullPath)) { const shouldOverwrite = await askOverwrite(fullPath); if (!shouldOverwrite) { console.log('\n=== File content (add to a file of your choosing) ===\n'); console.log(content); console.log('\n=== End of file content ===\n'); return; } } const dir = path.dirname(fullPath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } fs.writeFileSync(fullPath, content, 'utf-8'); console.log(`✅ Written: ${fullPath}`); } export function generateExpressRoutes(): string { return `import { Request, Response, Router } from 'express'; import KalendisClient from './kalendisClient'; const router = Router(); const kalendisClient = new KalendisClient({ apiKey: process.env.KALENDIS_API_KEY! }); const handleError = (error: any, res: Response) => { res.status(500).json({ error: error.message }); }; router.get('/api/users', async (req, res) => { try { const users = await kalendisClient.getUsersByAccountId(); res.json(users); } catch (error: any) { handleError(error, res); } }); router.post('/api/users', async (req, res) => { try { const user = await kalendisClient.addUser(req.body); res.status(201).json(user); } catch (error: any) { handleError(error, res); } }); router.put('/api/users/:id', async (req, res) => { try { const { id } = req.params as { id: string }; const user = await kalendisClient.updateUser({ id, ...req.body }); res.json(user); } catch (error: any) { handleError(error, res); } }); router.delete('/api/users/:id', async (req, res) => { try { const { id } = req.params as { id: string }; const result = await kalendisClient.deleteUser({ id }); res.json(result); } catch (error: any) { handleError(error, res); } }); router.get('/api/availability', async (req, res) => { try { const availability = await kalendisClient.getAvailability(req.query as any); res.json(availability); } catch (error: any) { handleError(error, res); } }); router.get('/api/availability/all', async (req, res) => { try { const availability = await kalendisClient.getAllAvailability(req.query as any); res.json(availability); } catch (error: any) { handleError(error, res); } }); router.post('/api/availability/calculated', async (req, res) => { try { const result = await kalendisClient.getMultiUserCalculatedAvailability(req.body); res.json(result); } catch (error: any) { handleError(error, res); } }); router.post('/api/availability/recurring', async (req, res) => { try { const result = await kalendisClient.getRecurringAvailabilityByDate(req.body); res.json(result); } catch (error: any) { handleError(error, res); } }); router.post('/api/availability/matching', async (req, res) => { try { const result = await kalendisClient.getMatchingAvailabilityByDate(req.body); res.json(result); } catch (error: any) { handleError(error, res); } }); router.post('/api/availability', async (req, res) => { try { const availability = await kalendisClient.addAvailability(req.body); res.status(201).json(availability); } catch (error: any) { handleError(error, res); } }); router.put('/api/availability/:id', async (req, res) => { try { const { id } = req.params as { id: string }; const availability = await kalendisClient.updateAvailability({ id, ...req.body }); res.json(availability); } catch (error: any) { handleError(error, res); } }); router.delete('/api/availability/:id', async (req, res) => { try { const { id } = req.params as { id: string }; const { userId } = req.query as { userId: string }; const result = await kalendisClient.deleteAvailability({ id, userId }); res.json(result); } catch (error: any) { handleError(error, res); } }); router.get('/api/recurring-availability', async (req, res) => { try { const availability = await kalendisClient.getRecurringAvailability(req.query as any); res.json(availability); } catch (error: any) { handleError(error, res); } }); router.post('/api/recurring-availability', async (req, res) => { try { const result = await kalendisClient.addRecurringAvailability(req.body); res.status(201).json(result); } catch (error: any) { handleError(error, res); } }); router.put('/api/recurring-availability/:id', async (req, res) => { try { const { id } = req.params as { id: string }; const result = await kalendisClient.updateRecurringAvailability({ id, ...req.body }); res.json(result); } catch (error: any) { handleError(error, res); } }); router.delete('/api/recurring-availability/:id', async (req, res) => { try { const { id } = req.params as { id: string }; const { userId } = req.query as { userId: string }; const result = await kalendisClient.deleteRecurringAvailability({ id, userId }); res.json(result); } catch (error: any) { handleError(error, res); } }); router.get('/api/availability-exceptions', async (req, res) => { try { const exceptions = await kalendisClient.getAvailabilityException(req.query as any); res.json(exceptions); } catch (error: any) { handleError(error, res); } }); router.post('/api/availability-exceptions', async (req, res) => { try { const exception = await kalendisClient.addAvailabilityException(req.body); res.status(201).json(exception); } catch (error: any) { handleError(error, res); } }); router.post('/api/availability-exceptions/recurring', async (req, res) => { try { const exceptions = await kalendisClient.addRecurringAvailabilityException(req.body); res.status(201).json(exceptions); } catch (error: any) { handleError(error, res); } }); router.put('/api/availability-exceptions/:id', async (req, res) => { try { const { id } = req.params as { id: string }; const exception = await kalendisClient.updateAvailabilityException({ id, ...req.body }); res.json(exception); } catch (error: any) { handleError(error, res); } }); router.delete('/api/availability-exceptions/:id', async (req, res) => { try { const { id } = req.params as { id: string }; const { userId } = req.query as { userId: string }; const result = await kalendisClient.deleteAvailabilityException({ id, userId }); res.json(result); } catch (error: any) { handleError(error, res); } }); router.get('/api/bookings', async (req, res) => { try { const bookings = await kalendisClient.getBooking(req.query as any); res.json(bookings); } catch (error: any) { handleError(error, res); } }); router.post('/api/bookings/bulk', async (req, res) => { try { const bookings = await kalendisClient.getBookingsByIds(req.body); res.json(bookings); } catch (error: any) { handleError(error, res); } }); router.post('/api/bookings', async (req, res) => { try { const booking = await kalendisClient.addBooking(req.body); res.status(201).json(booking); } catch (error: any) { handleError(error, res); } }); router.put('/api/bookings/:id', async (req, res) => { try { const { id } = req.params as { id: string }; const booking = await kalendisClient.updateBooking({ id, ...req.body }); res.json(booking); } catch (error: any) { handleError(error, res); } }); router.delete('/api/bookings/:id', async (req, res) => { try { const { id } = req.params as { id: string }; const result = await kalendisClient.deleteBooking({ id }); res.json(result); } catch (error: any) { handleError(error, res); } }); router.get('/api/account', async (req, res) => { try { const account = await kalendisClient.getAccount(); res.json(account); } catch (error: any) { handleError(error, res); } }); router.put('/api/account', async (req, res) => { try { const account = await kalendisClient.updateAccount(req.body); res.json(account); } catch (error: any) { handleError(error, res); } }); export default router; `; } export function generateFastifyRoutes(): string { return `import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'; import KalendisClient from './kalendisClient'; const kalendisClient = new KalendisClient({ apiKey: process.env.KALENDIS_API_KEY! }); export default async function routes(fastify: FastifyInstance) { fastify.get('/api/users', async (request, reply) => { const users = await kalendisClient.getUsersByAccountId(); return users; }); fastify.post('/api/users', async (request, reply) => { const user = await kalendisClient.addUser(request.body as any); reply.code(201).send(user); }); fastify.put('/api/users/:id', async (request, reply) => { const { id } = request.params as { id: string }; const user = await kalendisClient.updateUser({ id, ...request.body as any }); return user; }); fastify.delete('/api/users/:id', async (request, reply) => { const { id } = request.params as { id: string }; const result = await kalendisClient.deleteUser({ id }); return result; }); fastify.get('/api/availability', async (request, reply) => { const availability = await kalendisClient.getAvailability(request.query as any); return availability; }); fastify.get('/api/availability/all', async (request, reply) => { const availability = await kalendisClient.getAllAvailability(request.query as any); return availability; }); fastify.post('/api/availability/calculated', async (request, reply) => { const result = await kalendisClient.getMultiUserCalculatedAvailability(request.body as any); return result; }); fastify.post('/api/availability/recurring', async (request, reply) => { const result = await kalendisClient.getRecurringAvailabilityByDate(request.body as any); return result; }); fastify.post('/api/availability/matching', async (request, reply) => { const result = await kalendisClient.getMatchingAvailabilityByDate(request.body as any); return result; }); fastify.post('/api/availability', async (request, reply) => { const availability = await kalendisClient.addAvailability(request.body as any); reply.code(201).send(availability); }); fastify.put('/api/availability/:id', async (request, reply) => { const { id } = request.params as { id: string }; const availability = await kalendisClient.updateAvailability({ id, ...request.body as any }); return availability; }); fastify.delete('/api/availability/:id', async (request, reply) => { const { id } = request.params as { id: string }; const { userId } = request.query as { userId: string }; const result = await kalendisClient.deleteAvailability({ id, userId }); return result; }); fastify.get('/api/recurring-availability', async (request, reply) => { const availability = await kalendisClient.getRecurringAvailability(request.query as any); return availability; }); fastify.post('/api/recurring-availability', async (request, reply) => { const result = await kalendisClient.addRecurringAvailability(request.body as any); reply.code(201).send(result); }); fastify.put('/api/recurring-availability/:id', async (request, reply) => { const { id } = request.params as { id: string }; const result = await kalendisClient.updateRecurringAvailability({ id, ...request.body as any }); return result; }); fastify.delete('/api/recurring-availability/:id', async (request, reply) => { const { id } = request.params as { id: string }; const { userId } = request.query as { userId: string }; const result = await kalendisClient.deleteRecurringAvailability({ id, userId }); return result; }); fastify.get('/api/availability-exceptions', async (request, reply) => { const exceptions = await kalendisClient.getAvailabilityException(request.query as any); return exceptions; }); fastify.post('/api/availability-exceptions', async (request, reply) => { const exception = await kalendisClient.addAvailabilityException(request.body as any); reply.code(201).send(exception); }); fastify.post('/api/availability-exceptions/recurring', async (request, reply) => { const exceptions = await kalendisClient.addRecurringAvailabilityException(request.body as any); reply.code(201).send(exceptions); }); fastify.put('/api/availability-exceptions/:id', async (request, reply) => { const { id } = request.params as { id: string }; const exception = await kalendisClient.updateAvailabilityException({ id, ...request.body as any }); return exception; }); fastify.delete('/api/availability-exceptions/:id', async (request, reply) => { const { id } = request.params as { id: string }; const { userId } = request.query as { userId: string }; const result = await kalendisClient.deleteAvailabilityException({ id, userId }); return result; }); fastify.get('/api/bookings', async (request, reply) => { const bookings = await kalendisClient.getBooking(request.query as any); return bookings; }); fastify.post('/api/bookings/bulk', async (request, reply) => { const bookings = await kalendisClient.getBookingsByIds(request.body as any); return bookings; }); fastify.post('/api/bookings', async (request, reply) => { const booking = await kalendisClient.addBooking(request.body as any); reply.code(201).send(booking); }); fastify.put('/api/bookings/:id', async (request, reply) => { const { id } = request.params as { id: string }; const booking = await kalendisClient.updateBooking({ id, ...request.body as any }); return booking; }); fastify.delete('/api/bookings/:id', async (request, reply) => { const { id } = request.params as { id: string }; const result = await kalendisClient.deleteBooking({ id }); return result; }); fastify.get('/api/account', async (request, reply) => { const account = await kalendisClient.getAccount(); return account; }); fastify.put('/api/account', async (request, reply) => { const account = await kalendisClient.updateAccount(request.body as any); return account; }); } `; } export function generateNestJSModule(typesPath: string = '@/types'): Record<string, string> { const files: Record<string, string> = {}; files['kalendis.controller.ts'] = `import { Controller, Get, Post, Put, Delete, Param, Query, Body, HttpCode, HttpStatus } from '@nestjs/common'; import { KalendisService } from './kalendis.service'; import * as Types from '${typesPath}'; @Controller('api') export class KalendisController { constructor(private readonly kalendisService: KalendisService) {} @Get('users') async getUsers() { return this.kalendisService.getUsersByAccountId(); } @Post('users') @HttpCode(HttpStatus.CREATED) async createUser(@Body() body: { id: string; name: string; email?: string; timezone?: string }) { return this.kalendisService.addUser(body); } @Put('users/:id') async updateUser(@Param('id') id: string, @Body() body: { name?: string; timezone?: string }) { return this.kalendisService.updateUser({ id, ...body }); } @Delete('users/:id') async deleteUser(@Param('id') id: string) { return this.kalendisService.deleteUser({ id }); } @Get('availability') async getAvailability(@Query() query: { userId?: string; start?: string; end?: string }) { return this.kalendisService.getAvailability(query); } @Get('availability/all') async getAllAvailability(@Query() query: { start: string; end?: string; timezone?: string }) { return this.kalendisService.getAllAvailability(query); } @Post('availability/calculated') async getCalculatedAvailability(@Body() body: { userIds: string[]; start: string; end?: string; timezone?: string }) { return this.kalendisService.getMultiUserCalculatedAvailability(body); } @Post('availability/recurring') async getRecurringAvailabilityByDate(@Body() body: { userId: string; start: string; cadence: string; frequency: number; numberOfOccurrences: number; timezone?: string }) { return this.kalendisService.getRecurringAvailabilityByDate(body); } @Post('availability/matching') async getMatchingAvailability(@Body() body: { userIds: string[]; start: string; end?: string; timezone?: string }) { return this.kalendisService.getMatchingAvailabilityByDate(body); } @Post('availability') @HttpCode(HttpStatus.CREATED) async addAvailability(@Body() body: { userId: string; start: string; end: string; timezone?: string }) { return this.kalendisService.addAvailability(body); } @Put('availability/:id') async updateAvailability(@Param('id') id: string, @Body() body: { start?: string; end?: string; timezone?: string }) { return this.kalendisService.updateAvailability({ id, ...body }); } @Delete('availability/:id') async deleteAvailability(@Param('id') id: string, @Query('userId') userId: string) { return this.kalendisService.deleteAvailability({ id, userId }); } @Get('recurring-availability') async getRecurringAvailability(@Query('userId') userId: string) { return this.kalendisService.getRecurringAvailability({ userId }); } @Post('recurring-availability') @HttpCode(HttpStatus.CREATED) async addRecurringAvailability(@Body() body: { userId: string; daysOfWeek: Types.DaysOfWeek[]; start: string; end: string; expiration?: string; timezone?: string }) { return this.kalendisService.addRecurringAvailability(body); } @Put('recurring-availability/:id') async updateRecurringAvailability(@Param('id') id: string, @Body() body: { userId: string; daysOfWeek?: Types.DaysOfWeek[]; start?: string; end?: string; expiration?: string; makeInfinite?: boolean; timezone?: string }) { return this.kalendisService.updateRecurringAvailability({ id, ...body }); } @Delete('recurring-availability/:id') async deleteRecurringAvailability(@Param('id') id: string, @Query('userId') userId: string) { return this.kalendisService.deleteRecurringAvailability({ id, userId }); } @Get('availability-exceptions') async getAvailabilityExceptions(@Query('userId') userId: string) { return this.kalendisService.getAvailabilityException({ userId }); } @Post('availability-exceptions') @HttpCode(HttpStatus.CREATED) async addAvailabilityException(@Body() body: { userId: string; start: string; end: string; timezone?: string }) { return this.kalendisService.addAvailabilityException(body); } @Post('availability-exceptions/recurring') @HttpCode(HttpStatus.CREATED) async addRecurringAvailabilityException(@Body() body: { userId: string; daysOfWeek: Types.DaysOfWeek[]; start: string; end: string; expiration?: string; timezone?: string }) { return this.kalendisService.addRecurringAvailabilityException(body); } @Put('availability-exceptions/:id') async updateAvailabilityException(@Param('id') id: string, @Body() body: { start?: string; end?: string; timezone?: string }) { return this.kalendisService.updateAvailabilityException({ id, ...body }); } @Delete('availability-exceptions/:id') async deleteAvailabilityException(@Param('id') id: string, @Query('userId') userId: string) { return this.kalendisService.deleteAvailabilityException({ id, userId }); } @Get('bookings') async getBookings(@Query() query: { userId: string; start: string; end?: string }) { return this.kalendisService.getBooking(query); } @Post('bookings/bulk') async getBookingsByIds(@Body() body: { bookingIds: string[] }) { return this.kalendisService.getBookingsByIds(body); } @Post('bookings') @HttpCode(HttpStatus.CREATED) async createBooking(@Body() body: { userIds: string[]; start: string; end: string; timezone?: string }) { return this.kalendisService.addBooking(body); } @Put('bookings/:id') async updateBooking(@Param('id') id: string, @Body() body: { userIds?: string[]; start?: string; end?: string; timezone?: string }) { return this.kalendisService.updateBooking({ id, ...body }); } @Delete('bookings/:id') async deleteBooking(@Param('id') id: string) { return this.kalendisService.deleteBooking({ id }); } @Get('account') async getAccount() { return this.kalendisService.getAccount(); } @Put('account') async updateAccount(@Body() body: { name?: string; active?: boolean }) { return this.kalendisService.updateAccount(body); } } `; files['kalendis.service.ts'] = `import { Injectable } from '@nestjs/common'; import KalendisClient from './kalendisClient'; import * as Types from '${typesPath}'; @Injectable() export class KalendisService { private kalendisClient: KalendisClient; constructor() { this.kalendisClient = new KalendisClient({ apiKey: process.env.KALENDIS_API_KEY! }); } async getUsersByAccountId() { return this.kalendisClient.getUsersByAccountId(); } async addUser(data: { id: string; name: string; email?: string; timezone?: string }) { return this.kalendisClient.addUser(data); } async updateUser(data: { id: string; name?: string; timezone?: string }) { return this.kalendisClient.updateUser(data); } async deleteUser(data: { id: string }) { return this.kalendisClient.deleteUser(data); } async getAvailability(params?: { userId?: string; start?: string; end?: string }) { return this.kalendisClient.getAvailability(params); } async getAllAvailability(params: { start: string; end?: string; timezone?: string }) { return this.kalendisClient.getAllAvailability(params); } async getMultiUserCalculatedAvailability(data: { userIds: string[]; start: string; end?: string; timezone?: string }) { return this.kalendisClient.getMultiUserCalculatedAvailability(data); } async getRecurringAvailabilityByDate(data: { userId: string; start: string; cadence: string; frequency: number; numberOfOccurrences: number; timezone?: string }) { return this.kalendisClient.getRecurringAvailabilityByDate(data); } async getMatchingAvailabilityByDate(data: { userIds: string[]; start: string; end?: string; timezone?: string }) { return this.kalendisClient.getMatchingAvailabilityByDate(data); } async addAvailability(data: { userId: string; start: string; end: string; timezone?: string }) { return this.kalendisClient.addAvailability(data); } async updateAvailability(data: { id: string; start?: string; end?: string; timezone?: string }) { return this.kalendisClient.updateAvailability(data); } async deleteAvailability(data: { id: string; userId: string }) { return this.kalendisClient.deleteAvailability(data); } async getRecurringAvailability(params: { userId: string }) { return this.kalendisClient.getRecurringAvailability(params); } async addRecurringAvailability(data: { userId: string; daysOfWeek: Types.DaysOfWeek[]; start: string; end: string; expiration?: string; timezone?: string }) { return this.kalendisClient.addRecurringAvailability(data); } async updateRecurringAvailability(data: { id: string; userId: string; daysOfWeek?: Types.DaysOfWeek[]; start?: string; end?: string; expiration?: string; makeInfinite?: boolean; timezone?: string }) { return this.kalendisClient.updateRecurringAvailability(data); } async deleteRecurringAvailability(data: { id: string; userId: string }) { return this.kalendisClient.deleteRecurringAvailability(data); } async getAvailabilityException(params: { userId: string }) { return this.kalendisClient.getAvailabilityException(params); } async addAvailabilityException(data: { userId: string; start: string; end: string; timezone?: string }) { return this.kalendisClient.addAvailabilityException(data); } async addRecurringAvailabilityException(data: { userId: string; daysOfWeek: Types.DaysOfWeek[]; start: string; end: string; expiration?: string; timezone?: string }) { return this.kalendisClient.addRecurringAvailabilityException(data); } async updateAvailabilityException(data: { id: string; start?: string; end?: string; timezone?: string }) { return this.kalendisClient.updateAvailabilityException(data); } async deleteAvailabilityException(data: { id: string; userId: string }) { return this.kalendisClient.deleteAvailabilityException(data); } async getBooking(params: { userId: string; start: string; end?: string }) { return this.kalendisClient.getBooking(params); } async getBookingsByIds(data: { bookingIds: string[] }) { return this.kalendisClient.getBookingsByIds(data); } async addBooking(data: { userIds: string[]; start: string; end: string; timezone?: string }) { return this.kalendisClient.addBooking(data); } async updateBooking(data: { id: string; userIds?: string[]; start?: string; end?: string; timezone?: string }) { return this.kalendisClient.updateBooking(data); } async deleteBooking(data: { id: string }) { return this.kalendisClient.deleteBooking(data); } async getAccount() { return this.kalendisClient.getAccount(); } async updateAccount(data: { name?: string; active?: boolean }) { return this.kalendisClient.updateAccount(data); } } `; files['kalendis.module.ts'] = `import { Module } from '@nestjs/common'; import { KalendisController } from './kalendis.controller'; import { KalendisService } from './kalendis.service'; @Module({ controllers: [KalendisController], providers: [KalendisService], exports: [KalendisService], }) export class KalendisModule {} `; return files; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kalendis-dev/kalendis-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server