pagespace-mcp
Version:
Model Context Protocol (MCP) server for PageSpace with complete workspace management, powerful search, batch operations, and AI agent capabilities. Provides external access to all core PageSpace functionality.
219 lines (214 loc) ⢠8.83 kB
JavaScript
/**
* @param {import('../api.js').PageSpaceApi} api
*/
export function createDocumentHandler(api) {
return {
async handleReadDocument(args) {
console.error('[MCP] handleReadDocument called')
try {
const { pageId } = args
if (!pageId) {
return {
content: [{ type: 'text', text: `ā Error: pageId is required!\n\nUse list_pages first to see available pages and their IDs, then use read_page with a specific pageId.` }],
}
}
const result = await api.makeAuthenticatedRequest('/api/mcp/documents', {
method: 'POST',
body: JSON.stringify({ operation: 'read', pageId }),
})
console.error('[MCP] Read document result:', JSON.stringify(result, null, 2))
if (result.error) {
throw new Error(result.error)
}
const response = {
content: [{
type: 'text',
text: `# š Document: "${result.pageTitle}"\n**Page ID:** ${pageId}\n**Total Lines:** ${result.totalLines}\n\n${result.numberedLines.join('\n')}\n\nš” **Next Steps:**\nNow you can edit this document using:\n- replace_lines (pageId="${pageId}", startLine=X, content="...")\n- insert_lines (pageId="${pageId}", lineNumber=X, content="...")\n- delete_lines (pageId="${pageId}", startLine=X)\n- append_to_page (pageId="${pageId}", content="...")\n- prepend_to_page (pageId="${pageId}", content="...")`,
}],
}
console.error('[MCP] Returning response:', JSON.stringify(response, null, 2))
return response
} catch (error) {
console.error('[MCP] handleReadDocument error:', error)
return {
content: [{ type: 'text', text: `Error reading document: ${error.message}` }],
}
}
},
async handleReplaceLines(args) {
const { pageId, startLine, endLine, content } = args
try {
if (!pageId) {
return {
content: [{ type: 'text', text: `ā Error: pageId is required!\n\nUse list_pages to get page IDs, then read_page to see line numbers.` }],
}
}
if (!startLine || !content) {
throw new Error('Both startLine and content are required for replace operation')
}
const result = await api.makeAuthenticatedRequest('/api/mcp/documents', {
method: 'POST',
body: JSON.stringify({
operation: 'replace',
pageId,
startLine,
endLine,
content,
}),
})
if (result.error) {
throw new Error(result.error)
}
const lineRange = endLine ? `${startLine}-${endLine}` : `${startLine}`
return {
content: [{ type: 'text', text: `ā
Successfully replaced line(s) ${lineRange} in "${result.pageTitle}" (ID: ${pageId})\n\nDocument now has ${result.totalLines} lines.\n\nContent around the change:\n${result.numberedLines.slice(Math.max(0, startLine - 3), Math.min(result.numberedLines.length, startLine + 5)).join('\n')}` }],
}
} catch (error) {
console.error('[MCP] handleReplaceLines error:', error)
return {
content: [{ type: 'text', text: `ā Error replacing lines: ${error.message}` }],
}
}
},
async handleInsertAtLine(args) {
const { pageId, lineNumber, content } = args
try {
if (!pageId) {
return {
content: [{ type: 'text', text: `ā Error: pageId is required!\n\nUse list_pages to get page IDs, then read_page to see line numbers.` }],
}
}
if (!lineNumber || !content) {
throw new Error('Both lineNumber and content are required for insert operation')
}
const result = await api.makeAuthenticatedRequest('/api/mcp/documents', {
method: 'POST',
body: JSON.stringify({
operation: 'insert',
pageId,
startLine: lineNumber,
content,
}),
})
if (result.error) {
throw new Error(result.error)
}
return {
content: [{ type: 'text', text: `ā
Successfully inserted content at line ${lineNumber} in "${result.pageTitle}" (ID: ${pageId})\n\nDocument now has ${result.totalLines} lines.\n\nContent around insertion:\n${result.numberedLines.slice(Math.max(0, lineNumber - 2), Math.min(result.numberedLines.length, lineNumber + 5)).join('\n')}` }],
}
} catch (error) {
console.error('[MCP] handleInsertAtLine error:', error)
return {
content: [{ type: 'text', text: `ā Error inserting content: ${error.message}` }],
}
}
},
async handleDeleteLines(args) {
const { pageId, startLine, endLine } = args
try {
if (!pageId) {
return {
content: [{ type: 'text', text: `ā Error: pageId is required!\n\nUse list_pages to get page IDs, then read_page to see line numbers.` }],
}
}
if (!startLine) {
throw new Error('startLine is required for delete operation')
}
const result = await api.makeAuthenticatedRequest('/api/mcp/documents', {
method: 'POST',
body: JSON.stringify({
operation: 'delete',
pageId,
startLine,
endLine,
}),
})
if (result.error) {
throw new Error(result.error)
}
const lineRange = endLine ? `${startLine}-${endLine}` : `${startLine}`
return {
content: [{ type: 'text', text: `ā
Successfully deleted line(s) ${lineRange} from "${result.pageTitle}" (ID: ${pageId})\n\nDocument now has ${result.totalLines} lines.\n\nContent around deletion:\n${result.numberedLines.slice(Math.max(0, startLine - 3), Math.min(result.numberedLines.length, startLine + 3)).join('\n')}` }],
}
} catch (error) {
console.error('[MCP] handleDeleteLines error:', error)
return {
content: [{ type: 'text', text: `ā Error deleting lines: ${error.message}` }],
}
}
},
async handleAppendToPage(args) {
const { pageId, content } = args
try {
if (!pageId) {
return {
content: [{ type: 'text', text: `ā Error: pageId is required!\n\nUse list_pages to get page IDs.` }],
}
}
if (!content) {
throw new Error('content is required for append operation')
}
const readResult = await api.makeAuthenticatedRequest('/api/mcp/documents', {
method: 'POST',
body: JSON.stringify({ operation: 'read', pageId }),
})
if (readResult.error) {
throw new Error(readResult.error)
}
const result = await api.makeAuthenticatedRequest('/api/mcp/documents', {
method: 'POST',
body: JSON.stringify({
operation: 'insert',
pageId,
startLine: readResult.totalLines + 1,
content,
}),
})
if (result.error) {
throw new Error(result.error)
}
return {
content: [{ type: 'text', text: `ā
Successfully appended content to "${result.pageTitle}" (ID: ${pageId})\n\nDocument now has ${result.totalLines} lines.\n\nNew content:\n${result.numberedLines.slice(-5).join('\n')}` }],
}
} catch (error) {
console.error('[MCP] handleAppendToPage error:', error)
return {
content: [{ type: 'text', text: `ā Error appending content: ${error.message}` }],
}
}
},
async handlePrependToPage(args) {
const { pageId, content } = args
try {
if (!pageId) {
return {
content: [{ type: 'text', text: `ā Error: pageId is required!\n\nUse list_pages to get page IDs.` }],
}
}
if (!content) {
throw new Error('content is required for prepend operation')
}
const result = await api.makeAuthenticatedRequest('/api/mcp/documents', {
method: 'POST',
body: JSON.stringify({
operation: 'insert',
pageId,
startLine: 1,
content,
}),
})
if (result.error) {
throw new Error(result.error)
}
return {
content: [{ type: 'text', text: `ā
Successfully prepended content to "${result.pageTitle}" (ID: ${pageId})\n\nDocument now has ${result.totalLines} lines.\n\nNew content:\n${result.numberedLines.slice(0, 5).join('\n')}` }],
}
} catch (error) {
console.error('[MCP] handlePrependToPage error:', error)
return {
content: [{ type: 'text', text: `ā Error prepending content: ${error.message}` }],
}
}
},
}
}