feat: wire meetings, wikis, and activities tools into server registry (18 tools)

This commit is contained in:
Lucas Winther
2026-09-07 02:56:00 +02:00
parent 2fb25a9604
commit d6e78d8cc3
7 changed files with 47 additions and 14 deletions
+3
View File
@@ -8,3 +8,6 @@ export * from "./work-packages.ts";
export * from "./queries.ts"; export * from "./queries.ts";
export * from "./metadata.ts"; export * from "./metadata.ts";
export * from "./openapi.ts"; export * from "./openapi.ts";
export * from "./meetings.ts";
export * from "./wikis.ts";
export type { PaginatedResult } from "./meetings.ts";
+11 -1
View File
@@ -14,6 +14,8 @@ import { workPackageTools } from "./work-packages";
import { queryTools } from "./queries"; import { queryTools } from "./queries";
import { metadataTools } from "./metadata"; import { metadataTools } from "./metadata";
import { openApiTools } from "./openapi"; import { openApiTools } from "./openapi";
import { meetingsTools, registerMeetingsTools } from "./meetings";
import { wikisTools, registerWikisTools } from "./wikis";
export * from "./common"; export * from "./common";
export * from "./projects"; export * from "./projects";
@@ -21,13 +23,19 @@ export * from "./work-packages";
export * from "./queries"; export * from "./queries";
export * from "./metadata"; export * from "./metadata";
export * from "./openapi"; export * from "./openapi";
export * from "./meetings";
export * from "./wikis";
export { registerMeetingsTools, registerWikisTools };
export type AnyToolDefinition = export type AnyToolDefinition =
| (typeof projectTools)[number] | (typeof projectTools)[number]
| (typeof workPackageTools)[number] | (typeof workPackageTools)[number]
| (typeof queryTools)[number] | (typeof queryTools)[number]
| (typeof metadataTools)[number] | (typeof metadataTools)[number]
| (typeof openApiTools)[number]; | (typeof openApiTools)[number]
| (typeof meetingsTools)[number]
| (typeof wikisTools)[number];
export const allTools: AnyToolDefinition[] = [ export const allTools: AnyToolDefinition[] = [
...projectTools, ...projectTools,
@@ -35,6 +43,8 @@ export const allTools: AnyToolDefinition[] = [
...queryTools, ...queryTools,
...metadataTools, ...metadataTools,
...openApiTools, ...openApiTools,
...meetingsTools,
...wikisTools,
]; ];
export interface RegisterToolsOptions { export interface RegisterToolsOptions {
+10 -3
View File
@@ -5,7 +5,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
import { createServer, SERVER_NAME, SERVER_VERSION } from "../src/server"; import { createServer, SERVER_NAME, SERVER_VERSION } from "../src/server";
describe("MCP Server Factory", () => { describe("MCP Server Factory", () => {
test("creates server and lists all 11 tools via InMemoryTransport", async () => { test("creates server and lists all 18 tools via InMemoryTransport", async () => {
const config = { const config = {
baseUrl: "http://localhost:8080", baseUrl: "http://localhost:8080",
apiKey: "test-key", apiKey: "test-key",
@@ -21,12 +21,13 @@ describe("MCP Server Factory", () => {
await client.connect(clientTransport); await client.connect(clientTransport);
const toolsResult = await client.listTools(); const toolsResult = await client.listTools();
expect(toolsResult.tools).toHaveLength(11); expect(toolsResult.tools).toHaveLength(18);
const names = toolsResult.tools.map((t) => t.name); const names = toolsResult.tools.map((t) => t.name);
expect(names).toContain("openproject_list_projects"); expect(names).toContain("openproject_list_projects");
expect(names).toContain("openproject_get_project"); expect(names).toContain("openproject_get_project");
expect(names).toContain("openproject_list_work_packages"); expect(names).toContain("openproject_list_work_packages");
expect(names).toContain("openproject_get_work_package"); expect(names).toContain("openproject_get_work_package");
expect(names).toContain("openproject_list_work_package_activities");
expect(names).toContain("openproject_list_queries"); expect(names).toContain("openproject_list_queries");
expect(names).toContain("openproject_get_query"); expect(names).toContain("openproject_get_query");
expect(names).toContain("openproject_list_types"); expect(names).toContain("openproject_list_types");
@@ -34,6 +35,12 @@ describe("MCP Server Factory", () => {
expect(names).toContain("openproject_list_priorities"); expect(names).toContain("openproject_list_priorities");
expect(names).toContain("openproject_list_users"); expect(names).toContain("openproject_list_users");
expect(names).toContain("openproject_get_openapi_spec"); expect(names).toContain("openproject_get_openapi_spec");
expect(names).toContain("openproject_list_meetings");
expect(names).toContain("openproject_get_meeting");
expect(names).toContain("openproject_search_meetings");
expect(names).toContain("openproject_get_wiki_page");
expect(names).toContain("openproject_search_wiki_pages");
expect(names).toContain("openproject_list_wiki_page_links");
await client.close(); await client.close();
await mcpServer.stop(); await mcpServer.stop();
@@ -136,7 +143,7 @@ describe("MCP Server Factory", () => {
// In read-only mode, only readOnly: true tools should be registered // In read-only mode, only readOnly: true tools should be registered
const toolsResult = await client.listTools(); const toolsResult = await client.listTools();
expect(toolsResult.tools).toHaveLength(11); expect(toolsResult.tools).toHaveLength(18);
await client.close(); await client.close();
await mcpServer.stop(); await mcpServer.stop();
+2 -2
View File
@@ -231,10 +231,10 @@ describe("OpenApi MCP Tool Registration & Execution", () => {
await server.close(); await server.close();
}); });
test("allTools includes openproject_get_openapi_spec (total: 11 tools)", () => { test("allTools includes openproject_get_openapi_spec (total: 18 tools)", () => {
const toolNames = allTools.map((t) => t.name); const toolNames = allTools.map((t) => t.name);
expect(toolNames).toContain("openproject_get_openapi_spec"); expect(toolNames).toContain("openproject_get_openapi_spec");
expect(allTools).toHaveLength(11); expect(allTools).toHaveLength(18);
}); });
}); });
+3 -3
View File
@@ -74,7 +74,7 @@ describe("Read-Only Mode Enforcement", () => {
expect(configFalse.readOnly).toBe(false); expect(configFalse.readOnly).toBe(false);
}); });
test("read-only server lists all 11 tools (all are readOnly: true)", async () => { test("read-only server lists all 18 tools (all are readOnly: true)", async () => {
const mcpServer = createServer({ const mcpServer = createServer({
baseUrl: "http://localhost:8080", baseUrl: "http://localhost:8080",
apiKey: "test-key", apiKey: "test-key",
@@ -87,7 +87,7 @@ describe("Read-Only Mode Enforcement", () => {
await client.connect(clientTransport); await client.connect(clientTransport);
const toolsResult = await client.listTools(); const toolsResult = await client.listTools();
expect(toolsResult.tools).toHaveLength(11); expect(toolsResult.tools).toHaveLength(18);
await client.close(); await client.close();
await mcpServer.stop(); await mcpServer.stop();
@@ -118,7 +118,7 @@ describe("Read-Only Mode Enforcement", () => {
const standaloneTools = await client2.listTools(); const standaloneTools = await client2.listTools();
const standaloneNames = standaloneTools.tools.map((t) => t.name); const standaloneNames = standaloneTools.tools.map((t) => t.name);
expect(standaloneNames).not.toContain("openproject_create_work_package_synthetic"); expect(standaloneNames).not.toContain("openproject_create_work_package_synthetic");
expect(standaloneTools.tools).toHaveLength(11); expect(standaloneTools.tools).toHaveLength(18);
await client2.close(); await client2.close();
await standaloneServer.close(); await standaloneServer.close();
+7
View File
@@ -641,12 +641,19 @@ describe("Metadata Service", () => {
expect(typeof domainServices.getProjectSchema).toBe("function"); expect(typeof domainServices.getProjectSchema).toBe("function");
expect(typeof domainServices.listWorkPackages).toBe("function"); expect(typeof domainServices.listWorkPackages).toBe("function");
expect(typeof domainServices.getWorkPackage).toBe("function"); expect(typeof domainServices.getWorkPackage).toBe("function");
expect(typeof domainServices.listWorkPackageActivities).toBe("function");
expect(typeof domainServices.listQueries).toBe("function"); expect(typeof domainServices.listQueries).toBe("function");
expect(typeof domainServices.getQuery).toBe("function"); expect(typeof domainServices.getQuery).toBe("function");
expect(typeof domainServices.listStatuses).toBe("function"); expect(typeof domainServices.listStatuses).toBe("function");
expect(typeof domainServices.listTypes).toBe("function"); expect(typeof domainServices.listTypes).toBe("function");
expect(typeof domainServices.listPriorities).toBe("function"); expect(typeof domainServices.listPriorities).toBe("function");
expect(typeof domainServices.listUsers).toBe("function"); expect(typeof domainServices.listUsers).toBe("function");
expect(typeof domainServices.listMeetings).toBe("function");
expect(typeof domainServices.getMeeting).toBe("function");
expect(typeof domainServices.searchMeetings).toBe("function");
expect(typeof domainServices.getWikiPage).toBe("function");
expect(typeof domainServices.searchWikiPages).toBe("function");
expect(typeof domainServices.listWikiPageLinks).toBe("function");
}); });
}); });
+11 -5
View File
@@ -1640,8 +1640,8 @@ describe("Wikis Tools", () => {
}); });
describe("Tool Registry", () => { describe("Tool Registry", () => {
test("allTools contains exactly 12 tools including OpenAPI spec", () => { test("allTools contains exactly 18 tools including OpenAPI spec, meetings, and wikis", () => {
expect(allTools).toHaveLength(12); expect(allTools).toHaveLength(18);
const names = allTools.map((t) => t.name); const names = allTools.map((t) => t.name);
expect(names).toEqual([ expect(names).toEqual([
"openproject_list_projects", "openproject_list_projects",
@@ -1656,6 +1656,12 @@ describe("Tool Registry", () => {
"openproject_list_priorities", "openproject_list_priorities",
"openproject_list_users", "openproject_list_users",
"openproject_get_openapi_spec", "openproject_get_openapi_spec",
"openproject_list_meetings",
"openproject_get_meeting",
"openproject_search_meetings",
"openproject_get_wiki_page",
"openproject_search_wiki_pages",
"openproject_list_wiki_page_links",
]); ]);
}); });
@@ -1665,7 +1671,7 @@ describe("Tool Registry", () => {
} }
}); });
test("registerAllTools registers all 12 tools on McpServer", () => { test("registerAllTools registers all 18 tools on McpServer", () => {
const server = new McpServer({ name: "test-mcp", version: "1.0.0" }); const server = new McpServer({ name: "test-mcp", version: "1.0.0" });
registerAllTools(server); registerAllTools(server);
@@ -1675,7 +1681,7 @@ describe("Tool Registry", () => {
} }
)._registeredTools; )._registeredTools;
expect(Object.keys(registeredTools)).toHaveLength(12); expect(Object.keys(registeredTools)).toHaveLength(18);
for (const tool of allTools) { for (const tool of allTools) {
expect(registeredTools[tool.name]).toBeDefined(); expect(registeredTools[tool.name]).toBeDefined();
} }
@@ -1707,7 +1713,7 @@ describe("Tool Registry", () => {
expect( expect(
registeredTools["openproject_synthetic_mutating_tool"] registeredTools["openproject_synthetic_mutating_tool"]
).toBeUndefined(); ).toBeUndefined();
expect(Object.keys(registeredTools)).toHaveLength(12); expect(Object.keys(registeredTools)).toHaveLength(18);
for (const tool of allTools) { for (const tool of allTools) {
expect(registeredTools[tool.name]).toBeDefined(); expect(registeredTools[tool.name]).toBeDefined();
} }