Atlassian Confluence MCP Server
by aashari
Verified
import { Command } from 'commander';
import { Logger } from '../utils/logger.util.js';
import { handleCliError } from '../utils/error.util.js';
import atlassianPagesController from '../controllers/atlassian.pages.controller.js';
import { ListPagesOptions } from '../controllers/atlassian.pages.types.js';
import { formatHeading, formatPagination } from '../utils/formatter.util.js';
/**
* CLI module for managing Confluence pages.
* Provides commands for listing pages and retrieving page details.
* All commands require valid Atlassian credentials.
*/
/**
* Register Confluence Pages CLI commands with the Commander program
* @param program - The Commander program instance to register commands with
* @throws Error if command registration fails
*/
function register(program: Command): void {
const cliLogger = Logger.forContext(
'cli/atlassian.pages.cli.ts',
'register',
);
cliLogger.debug('Registering Confluence Pages CLI commands...');
registerListPagesCommand(program);
registerGetPageCommand(program);
cliLogger.debug('CLI commands registered successfully');
}
/**
* Register the command for listing Confluence pages
* @param program - The Commander program instance
*/
function registerListPagesCommand(program: Command): void {
program
.command('list-pages')
.description(
`List Confluence pages, optionally filtering by space ID(s), status, or title/content/label query.
PURPOSE: Discover pages within specific spaces or across your instance based on status or simple text matching. Useful for finding page IDs for 'get-page'.
Use Case: Use this for browsing pages in a known space, finding archived pages, or doing simple text searches within titles/labels. For complex content searches, use the 'search' command with CQL.
Output: Formatted list of pages including ID, title, space ID, status, author, creation date, and URL. Supports filtering and pagination.
Sorting: By default, pages are sorted by modified date in descending order (most recently modified first).
Note: --space-id requires numeric IDs. Use 'list-spaces' or 'get-space' first if you only have the space key.
Examples:
$ mcp-atlassian-confluence list-pages --space-id 123456 --status current
$ mcp-atlassian-confluence list-pages --limit 50 --query "Project"
$ mcp-atlassian-confluence list-pages --space-id 123456,789012 --query "documentation"
$ mcp-atlassian-confluence list-pages --cursor "next-cursor-value"`,
)
.option(
'-l, --limit <number>',
'Maximum number of items to return (1-100)',
'25',
)
.option(
'-c, --cursor <string>',
'Pagination cursor for retrieving the next set of results',
)
.option(
'-q, --query <text>',
'Filter pages by title, content, or labels (simple text search, not query language)',
)
.option(
'-s, --space-id <ids...>',
'Filter by one or more space IDs (numeric), separated by spaces. This is also referred to as "containerId" in the API for cross-service consistency.',
)
.option(
'-S, --status <status>',
'Filter by page status: current, archived',
'current',
)
.option(
'--sort <sort>',
'Sort order for pages (e.g., "title", "-modified-date"). Default is "-modified-date" (most recently modified first).',
)
.action(async (options) => {
const actionLogger = Logger.forContext(
'cli/atlassian.pages.cli.ts',
'list-pages',
);
try {
actionLogger.debug('Processing command options:', options);
// Validate status if provided
if (
options.status &&
!['current', 'archived'].includes(options.status)
) {
throw new Error(
'Status must be either "current" or "archived"',
);
}
// Validate limit if provided
if (options.limit) {
const limit = parseInt(options.limit, 10);
if (isNaN(limit) || limit <= 0) {
throw new Error(
'Invalid --limit value: Must be a positive integer.',
);
}
}
// Create filter options for controller
const filterOptions: ListPagesOptions = {
// Map from CLI --space-id flag to the controller's standardized containerId parameter
...(options.spaceId && { containerId: options.spaceId }),
...(options.status && { status: [options.status] }),
...(options.limit && {
limit: parseInt(options.limit, 10),
}),
...(options.cursor && { cursor: options.cursor }),
...(options.query && { query: options.query }),
...(options.sort && { sort: options.sort }),
};
actionLogger.debug(
'Fetching pages with filters:',
filterOptions,
);
const result =
await atlassianPagesController.list(filterOptions);
actionLogger.debug('Successfully retrieved pages');
// Print the main content
console.log(formatHeading('Pages', 2));
console.log(result.content);
// Print pagination information if available
if (result.pagination) {
console.log(
'\n' +
formatPagination(
result.pagination.count ?? 0,
result.pagination.hasMore,
result.pagination.nextCursor,
),
);
}
} catch (error) {
actionLogger.error('Operation failed:', error);
handleCliError(error);
}
});
}
/**
* Register the command for retrieving a specific Confluence page
* @param program - The Commander program instance
*/
function registerGetPageCommand(program: Command): void {
program
.command('get-page')
.description(
`Get detailed information about a specific Confluence page using its ID.
PURPOSE: Retrieve the full content (converted to Markdown) and comprehensive metadata for a specific Confluence page, identified by its numeric ID.`,
)
.requiredOption(
'-p, --page-id <id>',
'ID of the page to retrieve (numeric)',
)
.action(async (options) => {
const actionLogger = Logger.forContext(
'cli/atlassian.pages.cli.ts',
'get-page',
);
try {
actionLogger.debug('Processing command options:', options);
// Validate page ID format (numeric)
if (!options.pageId.match(/^\d+$/)) {
throw new Error('Page ID must be numeric.');
}
actionLogger.debug(`Fetching page: ${options.pageId}`);
const result = await atlassianPagesController.get({
pageId: options.pageId,
});
console.log(result.content);
} catch (error) {
handleCliError(error);
}
});
}
export default { register };