Add gateway-first pattern: when AGILITON_API_KEY is set, route all external API calls through the gateway with X-API-Key auth. Falls back to direct API access when gateway is unavailable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
232 lines
8.3 KiB
JavaScript
232 lines
8.3 KiB
JavaScript
/**
|
|
* 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 <p>, <h1>-<h6>, <table>, <ac:structured-macro>, 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: "<p>Hello world</p>"',
|
|
},
|
|
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})`;
|
|
}
|