/** * MCP tool definitions and handlers for Confluence Cloud. */ import { listSpaces, searchPages, getPage, createPage, updatePage, getPageComments, addPageComment, createSpace, getWebUrl, } from './client.js'; // --- Tool Definitions --- export const toolDefinitions = [ { name: 'confluence_list_spaces', description: 'List available Confluence spaces.', inputSchema: { type: 'object', properties: { limit: { type: 'number', description: 'Max spaces to return (default: 25)', }, }, }, }, { name: 'confluence_create_space', description: 'Create a new Confluence space.', inputSchema: { type: 'object', properties: { key: { type: 'string', description: 'Space key (uppercase, e.g. "AI", "CLAUDE")', }, name: { type: 'string', description: 'Space display name', }, description: { type: 'string', description: 'Space description (optional)', }, }, required: ['key', 'name'], }, }, { name: 'confluence_search', description: 'Search Confluence pages by text content. Returns matching pages with titles and links.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search text to find in pages', }, space_id: { type: 'string', description: 'Filter by space ID (optional)', }, limit: { type: 'number', description: 'Max results (default: 10)', }, }, required: ['query'], }, }, { name: 'confluence_get_page', description: 'Get a Confluence page by ID, including its full body content and version number. Use the version number for updates.', inputSchema: { type: 'object', properties: { page_id: { type: 'string', description: 'Confluence page ID', }, }, required: ['page_id'], }, }, { name: 'confluence_create_page', description: 'Create a new Confluence page in a space. Body is XHTML storage format (supports

,

-

, , , etc).', inputSchema: { type: 'object', properties: { space_id: { type: 'string', description: 'Space ID to create the page in', }, title: { type: 'string', description: 'Page title', }, body: { type: 'string', description: 'Page body in Confluence storage format (XHTML). Example: "

Hello world

"', }, parent_id: { type: 'string', description: 'Parent page ID (optional, creates at root if omitted)', }, }, required: ['space_id', 'title', 'body'], }, }, { name: 'confluence_update_page', description: 'Update an existing Confluence page. MUST provide the current version number (from confluence_get_page) incremented by 1. Replaces the full page body.', inputSchema: { type: 'object', properties: { page_id: { type: 'string', description: 'Page ID to update', }, title: { type: 'string', description: 'Page title (can be changed)', }, body: { type: 'string', description: 'New page body in Confluence storage format (replaces entire body)', }, version_number: { type: 'number', description: 'New version number (must be current version + 1)', }, version_message: { type: 'string', description: 'Version change message (optional)', }, }, required: ['page_id', 'title', 'body', 'version_number'], }, }, { name: 'confluence_get_comments', description: 'Get footer comments on a Confluence page.', inputSchema: { type: 'object', properties: { page_id: { type: 'string', description: 'Page ID', }, limit: { type: 'number', description: 'Max comments (default: 25)', }, }, required: ['page_id'], }, }, { name: 'confluence_add_comment', description: 'Add a footer comment to a Confluence page.', inputSchema: { type: 'object', properties: { page_id: { type: 'string', description: 'Page ID to comment on', }, body: { type: 'string', description: 'Comment body in storage format (XHTML)', }, }, required: ['page_id', 'body'], }, }, ]; // --- Handlers --- export async function handleListSpaces(args) { const spaces = await listSpaces(args.limit); if (spaces.length === 0) return 'No spaces found.'; const lines = spaces.map((s) => `${s.key} | ${s.name} | ID: ${s.id} | ${s.type}`); return `Found ${spaces.length} spaces:\n\n${lines.join('\n')}`; } export async function handleCreateSpace(args) { const space = await createSpace(args.key, args.name, args.description); return `Space created: ${space.key} (${space.name}) — ID: ${space.id}`; } export async function handleSearch(args) { const pages = await searchPages(args.query, args.space_id, args.limit); if (pages.length === 0) return `No pages found for: ${args.query}`; const lines = pages.map((p) => { const url = getWebUrl(p); return `${p.title} | ID: ${p.id} | v${p.version.number} | ${url}`; }); return `Found ${pages.length} pages:\n\n${lines.join('\n')}`; } export async function handleGetPage(args) { const page = await getPage(args.page_id, true); const url = getWebUrl(page); const body = page.body?.storage?.value || '(empty)'; return [ `Title: ${page.title}`, `ID: ${page.id} | Space: ${page.spaceId} | Version: ${page.version.number}`, `URL: ${url}`, `Last updated: ${page.version.createdAt}`, '', '--- Content (storage format) ---', body, ].join('\n'); } export async function handleCreatePage(args) { const page = await createPage(args.space_id, args.title, args.body, args.parent_id); const url = getWebUrl(page); return `Page created: "${page.title}" | ID: ${page.id} | Version: ${page.version.number}\nURL: ${url}`; } export async function handleUpdatePage(args) { const page = await updatePage(args.page_id, args.title, args.body, args.version_number, args.version_message); const url = getWebUrl(page); return `Page updated: "${page.title}" | Version: ${page.version.number}\nURL: ${url}`; } export async function handleGetComments(args) { const comments = await getPageComments(args.page_id, args.limit); if (comments.length === 0) return 'No comments on this page.'; const lines = comments.map((c) => { const body = c.body?.storage?.value || '(empty)'; const date = c.version?.createdAt || 'unknown'; return `[${date}] ${body}`; }); return `${comments.length} comments:\n\n${lines.join('\n\n')}`; } export async function handleAddComment(args) { const comment = await addPageComment(args.page_id, args.body); return `Comment added (ID: ${comment.id})`; }