Files
openproject-mcp/src/tools/meetings.ts
T

186 lines
4.5 KiB
TypeScript

/**
* Meeting Tools for OpenProject MCP.
* Implements openproject_list_meetings, openproject_get_meeting, and openproject_search_meetings tools.
*/
import { z } from "zod";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import {
formatToolSuccess,
formatToolError,
registerTool,
type McpToolResponse,
type ToolDefinition,
} from "./common";
import {
listMeetings,
getMeeting,
searchMeetings,
} from "../services/meetings";
export const listMeetingsShape = {
projectId: z
.union([z.number().int().positive(), z.string().min(1)])
.optional()
.describe("Filter meetings by project ID or identifier"),
time: z
.string()
.optional()
.describe("Filter by meeting time context ('upcoming' or 'past')"),
offset: z
.number()
.int()
.positive()
.optional()
.default(1)
.describe("Page number to retrieve (1-based, default 1)"),
pageSize: z
.number()
.int()
.positive()
.max(100)
.optional()
.default(20)
.describe("Number of items per page (max 100, default 20)"),
};
export type ListMeetingsArgs = z.input<z.ZodObject<typeof listMeetingsShape>>;
/**
* Tool execution handler for openproject_list_meetings.
*/
export async function handleListMeetings(
args: ListMeetingsArgs
): Promise<McpToolResponse> {
try {
const result = await listMeetings(args);
return formatToolSuccess(result);
} catch (error) {
return formatToolError(error);
}
}
export const getMeetingShape = {
id: z
.number()
.int()
.positive()
.describe("Numeric ID of the meeting"),
includeAgendaItems: z
.boolean()
.optional()
.default(true)
.describe("Whether to include agenda items and outcomes (default true)"),
};
export type GetMeetingArgs = z.input<z.ZodObject<typeof getMeetingShape>>;
/**
* Tool execution handler for openproject_get_meeting.
*/
export async function handleGetMeeting(
args: GetMeetingArgs
): Promise<McpToolResponse> {
try {
const result = await getMeeting(args.id, {
includeAgendaItems: args.includeAgendaItems ?? true,
});
return formatToolSuccess(result);
} catch (error) {
return formatToolError(error);
}
}
export const searchMeetingsShape = {
query: z
.string()
.min(1)
.describe(
"Search keywords to match across meeting titles, locations, and agenda item notes"
),
projectId: z
.union([z.number().int().positive(), z.string().min(1)])
.optional()
.describe("Scope search to a specific project by numeric ID or slug identifier"),
offset: z
.number()
.int()
.positive()
.optional()
.default(1)
.describe("Page number to retrieve (1-based, default 1)"),
pageSize: z
.number()
.int()
.positive()
.max(100)
.optional()
.default(20)
.describe("Number of items per page (max 100, default 20)"),
};
export type SearchMeetingsArgs = z.input<z.ZodObject<typeof searchMeetingsShape>>;
/**
* Tool execution handler for openproject_search_meetings.
*/
export async function handleSearchMeetings(
args: SearchMeetingsArgs
): Promise<McpToolResponse> {
try {
const result = await searchMeetings(args);
return formatToolSuccess(result);
} catch (error) {
return formatToolError(error);
}
}
export const listMeetingsTool: ToolDefinition<
typeof listMeetingsShape,
ListMeetingsArgs
> = {
name: "openproject_list_meetings",
description: "List and filter meetings visible to the user.",
parameters: listMeetingsShape,
readOnly: true,
execute: handleListMeetings,
};
export const getMeetingTool: ToolDefinition<
typeof getMeetingShape,
GetMeetingArgs
> = {
name: "openproject_get_meeting",
description:
"Retrieve detailed meeting information including agenda items, sections, notes, and participants.",
parameters: getMeetingShape,
readOnly: true,
execute: handleGetMeeting,
};
export const searchMeetingsTool: ToolDefinition<
typeof searchMeetingsShape,
SearchMeetingsArgs
> = {
name: "openproject_search_meetings",
description: "Search across meetings and agenda items by keywords.",
parameters: searchMeetingsShape,
readOnly: true,
execute: handleSearchMeetings,
};
export const meetingsTools = [
listMeetingsTool,
getMeetingTool,
searchMeetingsTool,
];
/**
* Registers all meetings tools with an McpServer instance.
*/
export function registerMeetingsTools(server: McpServer): void {
registerTool(server, listMeetingsTool);
registerTool(server, getMeetingTool);
registerTool(server, searchMeetingsTool);
}