import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import axios, { AxiosRequestConfig } from "axios";
import { z } from "zod";
import { SessionManager } from "./auth-tools.js";
// Helper function for calendar API requests
async function makeCalendarRequest<T>(
method: string,
path: string,
body: any = null,
params: any = null,
apiKey?: string,
sessionId?: string
): Promise<T> {
let hostUrl = process.env.CALENDAR_API_HOST_URL || "http://localhost:8000";
if (!hostUrl.endsWith("/")) hostUrl += "/";
// Add platform-calendar prefix if not present
if (!hostUrl.includes("/platform-calendar")) {
hostUrl = hostUrl.replace(/\/$/, "") + "/platform-calendar/";
}
const url = `${hostUrl}${path}`;
// Получаем API ключ из параметров, сессии или переменной окружения
const sessionManager = SessionManager.getInstance();
const finalApiKey =
apiKey ||
sessionManager.getApiKey(sessionId) ||
sessionManager.getCurrentApiKey() ||
process.env.CALENDAR_API_KEY ||
"";
const headers: Record<string, string> = {
authorization: finalApiKey,
Accept: "application/json",
};
if (method.toUpperCase() !== "GET") {
headers["Content-Type"] = "application/json";
}
console.log("[DEBUG] Calendar API Request", {
method,
url,
headers: { ...headers, authorization: "***" },
body,
params,
hasStoredApiKey: !!sessionManager.getCurrentApiKey(),
sessionId: sessionId || "current",
});
try {
const config: AxiosRequestConfig = {
url,
method,
headers,
params,
};
if (method.toUpperCase() !== "GET" && body !== null) {
config.data = body;
}
const response = await axios(config);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
if (error.response) {
console.error("[DEBUG] Calendar API Error Response", {
status: error.response.status,
data: error.response.data,
});
}
throw new Error(`Calendar API request failed: ${error.message}`);
}
throw error;
}
}
// Schema definitions
const CalendarGetMySchema = z.object({
start: z.string().optional().describe("Start date in YYYY-MM-DD format"),
end: z.string().optional().describe("End date in YYYY-MM-DD format"),
_: z.string().optional().describe("Additional parameter (min 3 chars)"),
api_key: z.string().optional().describe("API key for authentication (overrides stored session)"),
session_id: z.string().optional().describe("Session ID to use specific session (optional)"),
});
const CalendarGetByUserSchema = z.object({
user_id: z.number().describe("User ID to get calendar for"),
start: z.string().optional().describe("Start date/datetime"),
end: z.string().optional().describe("End date/datetime"),
api_key: z.string().optional().describe("API key for authentication (overrides stored session)"),
session_id: z.string().optional().describe("Session ID to use specific session (optional)"),
});
const MeetingCreateSchema = z.object({
title: z.string().describe("Meeting title"),
description: z.string().optional().describe("Meeting description"),
place: z.string().optional().describe("Meeting place"),
start: z.string().describe("Start date-time in ISO format"),
end: z.string().describe("End date-time in ISO format"),
invited: z.array(z.number()).optional().describe("List of invited user IDs"),
notify: z.number().optional().describe("Notification time in minutes"),
api_key: z.string().optional().describe("API key for authentication (overrides stored session)"),
session_id: z.string().optional().describe("Session ID to use specific session (optional)"),
});
const MeetingGetSchema = z.object({
meeting_id: z.string().describe("Meeting ID to retrieve"),
api_key: z.string().optional().describe("API key for authentication (overrides stored session)"),
session_id: z.string().optional().describe("Session ID to use specific session (optional)"),
});
const MeetingAvailabilitySchema = z.object({
start: z.string().describe("Start date-time in ISO format"),
end: z.string().describe("End date-time in ISO format"),
user_ids: z.array(z.number()).describe("List of user IDs to check availability"),
meeting_id: z.string().optional().describe("Exclude specific meeting ID"),
api_key: z.string().optional().describe("API key for authentication (overrides stored session)"),
session_id: z.string().optional().describe("Session ID to use specific session (optional)"),
});
const MeetingRequiringAttentionSchema = z.object({
api_key: z.string().optional().describe("API key for authentication (overrides stored session)"),
session_id: z.string().optional().describe("Session ID to use specific session (optional)"),
});
export const registerCalendarTools = (server: McpServer) => {
// Get current user's calendar
server.tool(
"calendar_get_my",
CalendarGetMySchema.shape,
{
title: "Get current user's calendar with events for specified date range",
},
async (args) => {
const validatedArgs = CalendarGetMySchema.parse(args);
const { api_key, session_id, ...requestParams } = validatedArgs;
const params = Object.fromEntries(Object.entries(requestParams).filter(([, value]) => value !== undefined));
const result = await makeCalendarRequest("GET", "api/v1/calendar/my", null, params, api_key, session_id);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
);
// Get calendar by user ID
server.tool(
"calendar_get_by_user",
CalendarGetByUserSchema.shape,
{
title: "Get calendar for a specific user by user ID",
},
async (args) => {
const validatedArgs = CalendarGetByUserSchema.parse(args);
const { user_id, api_key, session_id, ...params } = validatedArgs;
const filteredParams = Object.fromEntries(Object.entries(params).filter(([, value]) => value !== undefined));
const result = await makeCalendarRequest(
"GET",
`api/v1/calendar/${user_id}`,
null,
filteredParams,
api_key,
session_id
);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
);
// Create meeting
server.tool(
"meeting_create",
MeetingCreateSchema.shape,
{
title: "Create a new meeting",
},
async (args) => {
const validatedArgs = MeetingCreateSchema.parse(args);
const { api_key, session_id, ...requestBody } = validatedArgs;
const result = await makeCalendarRequest("POST", "api/v1/meeting", requestBody, null, api_key, session_id);
return {
content: [
{
type: "text",
text: `Meeting created successfully: ${JSON.stringify(result, null, 2)}`,
},
],
};
}
);
// Get meeting by ID
server.tool(
"meeting_get",
MeetingGetSchema.shape,
{
title: "Get meeting details by ID",
},
async (args) => {
const validatedArgs = MeetingGetSchema.parse(args);
const { api_key, session_id, meeting_id } = validatedArgs;
const result = await makeCalendarRequest("GET", `api/v1/meeting/${meeting_id}`, null, null, api_key, session_id);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
);
// Check user availability
server.tool(
"meeting_get_user_availability",
MeetingAvailabilitySchema.shape,
{
title: "Check user availability (busy times) for a time period",
},
async (args) => {
const validatedArgs = MeetingAvailabilitySchema.parse(args);
const { user_ids, meeting_id, api_key, session_id, ...dateParams } = validatedArgs;
const params = {
...dateParams,
user: user_ids,
...(meeting_id && { meetId: meeting_id }),
};
const result = await makeCalendarRequest("GET", "api/v1/meeting", null, params, api_key, session_id);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
);
// Get meetings requiring attention
server.tool(
"meeting_get_requiring_attention",
MeetingRequiringAttentionSchema.shape,
{
title: "Get meetings that require attention from the current user",
},
async (args) => {
const validatedArgs = MeetingRequiringAttentionSchema.parse(args);
const { api_key, session_id } = validatedArgs;
const result = await makeCalendarRequest(
"GET",
"api/v1/meeting/requiring_attention",
null,
null,
api_key,
session_id
);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
);
};