taiga-mcp-server
Version:
Full-featured MCP server for Taiga project management with advanced sprint and issue tracking. Based on mcpTAIGA by adriapedralbes, enhanced with substantial modifications. Developed collaboratively with Claude Code (AI-assisted development).
342 lines (301 loc) • 12.8 kB
JavaScript
/**
* Wiki management tools for Taiga MCP Server
* Handles Wiki page creation, management, and collaboration features
*/
import { z } from 'zod';
import { TaigaService } from '../taigaService.js';
import { createSuccessResponse, createErrorResponse, resolveProjectId } from '../utils.js';
import { ERROR_MESSAGES, SUCCESS_MESSAGES, RESPONSE_TEMPLATES } from '../constants.js';
const taigaService = new TaigaService();
/**
* Create Wiki Page tool
* Creates a new Wiki page in a project
*/
export const createWikiPageTool = {
name: 'createWikiPage',
description: 'Create a new Wiki page in a project for documentation and knowledge sharing',
inputSchema: z.object({
project: z.union([z.number(), z.string()]).describe('Project ID, slug, or name'),
slug: z.string().min(1, 'Wiki page slug is required').describe('URL-friendly identifier for the Wiki page'),
content: z.string().min(1, 'Wiki page content is required').describe('Content of the Wiki page (supports Markdown)'),
watchers: z.array(z.number()).optional().describe('Optional list of user IDs to watch this Wiki page'),
}),
handler: async ({ project, slug, content, watchers }) => {
try {
if (!taigaService.isAuthenticated()) {
return createErrorResponse(ERROR_MESSAGES.AUTHENTICATION_FAILED);
}
// Resolve project to get project ID
const resolvedProject = await resolveProjectId(project);
if (!resolvedProject) {
return createErrorResponse(ERROR_MESSAGES.PROJECT_NOT_FOUND);
}
const wikiData = {
project: resolvedProject.id,
slug,
content,
watchers: watchers || []
};
const result = await taigaService.createWikiPage(wikiData);
return createSuccessResponse(
`${SUCCESS_MESSAGES.WIKI_PAGE_CREATED}\n\n` +
`📖 **Wiki頁面創建成功**\n` +
`- Wiki ID: ${result.id}\n` +
`- Slug: ${result.slug}\n` +
`- 專案: ${resolvedProject.name}\n` +
`- 創建時間: ${new Date(result.created_date).toLocaleString()}\n` +
`- 內容長度: ${result.content?.length || 0} 字符\n` +
`- 關注者: ${result.watchers?.length || 0} 人`
);
} catch (error) {
console.error('Failed to create wiki page:', error);
return createErrorResponse(error.message || ERROR_MESSAGES.FAILED_TO_CREATE_WIKI);
}
}
};
/**
* List Wiki Pages tool
* Lists all Wiki pages in a project
*/
export const listWikiPagesTool = {
name: 'listWikiPages',
description: 'List all Wiki pages in a project',
inputSchema: z.object({
project: z.union([z.number(), z.string()]).describe('Project ID, slug, or name'),
}),
handler: async ({ project }) => {
try {
if (!taigaService.isAuthenticated()) {
return createErrorResponse(ERROR_MESSAGES.AUTHENTICATION_FAILED);
}
// Resolve project to get project ID
const resolvedProject = await resolveProjectId(project);
if (!resolvedProject) {
return createErrorResponse(ERROR_MESSAGES.PROJECT_NOT_FOUND);
}
const wikiPages = await taigaService.listWikiPages(resolvedProject.id);
if (!wikiPages || wikiPages.length === 0) {
return createSuccessResponse(
`📖 **${resolvedProject.name} - Wiki頁面列表**\n\n` +
`${RESPONSE_TEMPLATES.NO_WIKI_PAGES}`
);
}
const wikiList = wikiPages.map(wiki =>
`📖 **${wiki.slug}**\n` +
` - ID: ${wiki.id}\n` +
` - 修改時間: ${new Date(wiki.modified_date).toLocaleString()}\n` +
` - 關注者: ${wiki.watchers?.length || 0} 人\n` +
` - 內容: ${wiki.content ? `${wiki.content.substring(0, 100)}${wiki.content.length > 100 ? '...' : ''}` : '無內容'}`
).join('\n\n');
return createSuccessResponse(
`📖 **${resolvedProject.name} - Wiki頁面列表** (${wikiPages.length}個)\n\n` +
wikiList
);
} catch (error) {
console.error('Failed to list wiki pages:', error);
return createErrorResponse(error.message || ERROR_MESSAGES.FAILED_TO_LIST_WIKI);
}
}
};
/**
* Get Wiki Page tool
* Gets detailed information about a specific Wiki page
*/
export const getWikiPageTool = {
name: 'getWikiPage',
description: 'Get detailed information about a specific Wiki page by ID or slug',
inputSchema: z.object({
project: z.union([z.number(), z.string()]).describe('Project ID, slug, or name'),
identifier: z.union([z.number(), z.string()]).describe('Wiki page ID (number) or slug (string)'),
}),
handler: async ({ project, identifier }) => {
try {
if (!taigaService.isAuthenticated()) {
return createErrorResponse(ERROR_MESSAGES.AUTHENTICATION_FAILED);
}
// Resolve project to get project ID
const resolvedProject = await resolveProjectId(project);
if (!resolvedProject) {
return createErrorResponse(ERROR_MESSAGES.PROJECT_NOT_FOUND);
}
let wikiPage;
// If identifier is a number, get by ID; if string, get by slug
if (typeof identifier === 'number') {
wikiPage = await taigaService.getWikiPage(identifier);
} else {
wikiPage = await taigaService.getWikiPageBySlug(identifier, resolvedProject.id);
}
return createSuccessResponse(
`📖 **Wiki頁面詳情**\n\n` +
`**基本信息**\n` +
`- ID: ${wikiPage.id}\n` +
`- Slug: ${wikiPage.slug}\n` +
`- 專案: ${resolvedProject.name}\n` +
`- 創建時間: ${new Date(wikiPage.created_date).toLocaleString()}\n` +
`- 修改時間: ${new Date(wikiPage.modified_date).toLocaleString()}\n` +
`- 版本: ${wikiPage.version}\n\n` +
`**協作信息**\n` +
`- 關注者: ${wikiPage.watchers?.length || 0} 人\n` +
`- 擁有者: ${wikiPage.owner_full_name || '未設定'}\n\n` +
`**內容**\n` +
`${wikiPage.content || '此Wiki頁面暫無內容'}`
);
} catch (error) {
console.error('Failed to get wiki page:', error);
return createErrorResponse(error.message || ERROR_MESSAGES.FAILED_TO_GET_WIKI);
}
}
};
/**
* Update Wiki Page tool
* Updates content and settings of an existing Wiki page
*/
export const updateWikiPageTool = {
name: 'updateWikiPage',
description: 'Update content and settings of an existing Wiki page',
inputSchema: z.object({
project: z.union([z.number(), z.string()]).describe('Project ID, slug, or name'),
identifier: z.union([z.number(), z.string()]).describe('Wiki page ID (number) or slug (string)'),
content: z.string().optional().describe('New content for the Wiki page (supports Markdown)'),
watchers: z.array(z.number()).optional().describe('Updated list of user IDs to watch this Wiki page'),
}),
handler: async ({ project, identifier, content, watchers }) => {
try {
if (!taigaService.isAuthenticated()) {
return createErrorResponse(ERROR_MESSAGES.AUTHENTICATION_FAILED);
}
// Resolve project to get project ID
const resolvedProject = await resolveProjectId(project);
if (!resolvedProject) {
return createErrorResponse(ERROR_MESSAGES.PROJECT_NOT_FOUND);
}
// First get the wiki page to get its ID if identifier is a slug
let wikiPageId;
if (typeof identifier === 'number') {
wikiPageId = identifier;
} else {
const wikiPage = await taigaService.getWikiPageBySlug(identifier, resolvedProject.id);
wikiPageId = wikiPage.id;
}
// Prepare update data
const updateData = {};
if (content !== undefined) updateData.content = content;
if (watchers !== undefined) updateData.watchers = watchers;
const result = await taigaService.updateWikiPage(wikiPageId, updateData);
return createSuccessResponse(
`${SUCCESS_MESSAGES.WIKI_PAGE_UPDATED}\n\n` +
`📖 **Wiki頁面更新成功**\n` +
`- Wiki ID: ${result.id}\n` +
`- Slug: ${result.slug}\n` +
`- 專案: ${resolvedProject.name}\n` +
`- 更新時間: ${new Date(result.modified_date).toLocaleString()}\n` +
`- 版本: ${result.version}\n` +
`- 內容長度: ${result.content?.length || 0} 字符\n` +
`- 關注者: ${result.watchers?.length || 0} 人`
);
} catch (error) {
console.error('Failed to update wiki page:', error);
return createErrorResponse(error.message || ERROR_MESSAGES.FAILED_TO_UPDATE_WIKI);
}
}
};
/**
* Delete Wiki Page tool
* Deletes a Wiki page from the project
*/
export const deleteWikiPageTool = {
name: 'deleteWikiPage',
description: 'Delete a Wiki page from the project (irreversible action)',
inputSchema: z.object({
project: z.union([z.number(), z.string()]).describe('Project ID, slug, or name'),
identifier: z.union([z.number(), z.string()]).describe('Wiki page ID (number) or slug (string)'),
}),
handler: async ({ project, identifier }) => {
try {
if (!taigaService.isAuthenticated()) {
return createErrorResponse(ERROR_MESSAGES.AUTHENTICATION_FAILED);
}
// Resolve project to get project ID
const resolvedProject = await resolveProjectId(project);
if (!resolvedProject) {
return createErrorResponse(ERROR_MESSAGES.PROJECT_NOT_FOUND);
}
// First get the wiki page to get its ID and details if identifier is a slug
let wikiPageId, wikiSlug;
if (typeof identifier === 'number') {
wikiPageId = identifier;
const wikiPage = await taigaService.getWikiPage(identifier);
wikiSlug = wikiPage.slug;
} else {
const wikiPage = await taigaService.getWikiPageBySlug(identifier, resolvedProject.id);
wikiPageId = wikiPage.id;
wikiSlug = wikiPage.slug;
}
await taigaService.deleteWikiPage(wikiPageId);
return createSuccessResponse(
`${SUCCESS_MESSAGES.WIKI_PAGE_DELETED}\n\n` +
`🗑️ **Wiki頁面刪除成功**\n` +
`- 已刪除Wiki: ${wikiSlug}\n` +
`- Wiki ID: ${wikiPageId}\n` +
`- 專案: ${resolvedProject.name}\n` +
`- 刪除時間: ${new Date().toLocaleString()}\n\n` +
`⚠️ 注意:此操作不可逆轉`
);
} catch (error) {
console.error('Failed to delete wiki page:', error);
return createErrorResponse(error.message || ERROR_MESSAGES.FAILED_TO_DELETE_WIKI);
}
}
};
/**
* Watch Wiki Page tool
* Watch or unwatch a Wiki page for notifications
*/
export const watchWikiPageTool = {
name: 'watchWikiPage',
description: 'Watch or unwatch a Wiki page to receive notifications about changes',
inputSchema: z.object({
project: z.union([z.number(), z.string()]).describe('Project ID, slug, or name'),
identifier: z.union([z.number(), z.string()]).describe('Wiki page ID (number) or slug (string)'),
watch: z.boolean().default(true).describe('True to watch, false to unwatch the Wiki page'),
}),
handler: async ({ project, identifier, watch }) => {
try {
if (!taigaService.isAuthenticated()) {
return createErrorResponse(ERROR_MESSAGES.AUTHENTICATION_FAILED);
}
// Resolve project to get project ID
const resolvedProject = await resolveProjectId(project);
if (!resolvedProject) {
return createErrorResponse(ERROR_MESSAGES.PROJECT_NOT_FOUND);
}
// First get the wiki page to get its ID if identifier is a slug
let wikiPageId, wikiSlug;
if (typeof identifier === 'number') {
wikiPageId = identifier;
const wikiPage = await taigaService.getWikiPage(identifier);
wikiSlug = wikiPage.slug;
} else {
const wikiPage = await taigaService.getWikiPageBySlug(identifier, resolvedProject.id);
wikiPageId = wikiPage.id;
wikiSlug = wikiPage.slug;
}
await taigaService.watchWikiPage(wikiPageId, watch);
const action = watch ? '關注' : '取消關注';
const actionIcon = watch ? '👁️' : '🚫';
return createSuccessResponse(
`${SUCCESS_MESSAGES.WIKI_PAGE_WATCHED}\n\n` +
`${actionIcon} **Wiki頁面${action}成功**\n` +
`- Wiki: ${wikiSlug}\n` +
`- Wiki ID: ${wikiPageId}\n` +
`- 專案: ${resolvedProject.name}\n` +
`- 操作: ${action}\n` +
`- 時間: ${new Date().toLocaleString()}\n\n` +
`${watch ? '🔔 您將收到此Wiki頁面的變更通知' : '🔕 您將不再收到此Wiki頁面的通知'}`
);
} catch (error) {
console.error('Failed to watch/unwatch wiki page:', error);
return createErrorResponse(error.message || ERROR_MESSAGES.FAILED_TO_WATCH_WIKI);
}
}
};