dbx-mcp-server
Version:
A Model Context Protocol server for Dropbox integration with AI-powered PDF analysis, multi-directory indexing, and parallel processing
230 lines • 8.2 kB
JavaScript
import { getDropboxClientForPath, formatDropboxPath } from './client-factory.js';
import { handleDropboxError } from './error-handler.js';
export async function listFiles(path) {
try {
const client = await getDropboxClientForPath(path);
const response = await client.filesListFolder({
path: formatDropboxPath(path),
recursive: false,
include_media_info: true,
include_deleted: false,
include_has_explicit_shared_members: false,
include_mounted_folders: true,
include_non_downloadable_files: true
});
// Convert entries to a simpler format for listing
const files = response.result.entries.map(entry => ({
'.tag': entry['.tag'],
name: entry.name,
path_display: entry.path_display,
size: entry['.tag'] === 'file' ? entry.size : 0,
server_modified: entry['.tag'] === 'file' ? entry.server_modified : null,
client_modified: entry['.tag'] === 'file' ? entry.client_modified : null
}));
return {
content: [{
type: 'text',
text: JSON.stringify(files, null, 2)
}],
};
}
catch (error) {
handleDropboxError(error);
}
}
export async function uploadFile(path, content) {
try {
const buffer = Buffer.from(content, 'base64');
const client = await getDropboxClientForPath(path);
await client.filesUpload({
path: formatDropboxPath(path),
contents: buffer,
mode: { '.tag': 'overwrite' },
});
return {
content: [{
type: 'text',
text: `File uploaded successfully to ${path}`,
}],
};
}
catch (error) {
handleDropboxError(error);
}
}
export async function downloadFile(path) {
try {
const client = await getDropboxClientForPath(path);
// Get metadata first to check if it's a file or folder
const metadata = await client.filesGetMetadata({
path: formatDropboxPath(path)
});
if (metadata.result['.tag'] === 'folder') {
// For folders, get contents and format as ResourceContent
const folderContents = await client.filesListFolder({
path: formatDropboxPath(path),
recursive: false,
include_media_info: true,
include_deleted: false,
include_has_explicit_shared_members: false,
include_mounted_folders: true,
include_non_downloadable_files: true
});
// For folders, return a list of ResourceReference objects
const files = folderContents.result.entries.map(entry => ({
uri: `dbx://${entry.path_display?.slice(1) || entry.name}`,
name: entry.name,
description: entry['.tag'] === 'file'
? `File (${entry.size} bytes)`
: 'Folder'
}));
return {
content: [{
type: 'text',
text: JSON.stringify({
uri: `dbx://${path.replace(/^\//, '')}`,
name: metadata.result.name,
description: `Folder containing ${files.length} items`,
mimeType: 'application/vnd.dropbox.folder',
items: files
}, null, 2)
}],
};
}
else {
// For files, download and return content
const fileResponse = await client.filesDownload({
path: formatDropboxPath(path)
});
const fileData = fileResponse.result;
// Create downloads directory if it doesn't exist
const fs = await import('fs');
const downloadDir = 'downloads';
if (!fs.existsSync(downloadDir)) {
fs.mkdirSync(downloadDir, { recursive: true });
}
// Save to downloads directory with timestamp
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const fileName = `${timestamp}_${metadata.result.name}`;
const filePath = `${downloadDir}/${fileName}`;
fs.writeFileSync(filePath, fileData.fileBinary);
return {
content: [{
type: 'text',
text: `File downloaded successfully to: ${filePath}`,
}],
};
}
}
catch (error) {
handleDropboxError(error);
}
}
export async function createFolder(path) {
try {
const client = await getDropboxClientForPath(path);
await client.filesCreateFolderV2({
path: formatDropboxPath(path),
autorename: false,
});
return {
content: [{
type: 'text',
text: `Created folder at ${path}`,
}],
};
}
catch (error) {
handleDropboxError(error);
}
}
export async function getFileMetadata(path) {
try {
const client = await getDropboxClientForPath(path);
const response = await client.filesGetMetadata({
path: formatDropboxPath(path),
include_media_info: true,
include_deleted: false,
include_has_explicit_shared_members: true,
});
return {
content: [{
type: 'text',
text: JSON.stringify(response.result, null, 2),
}],
};
}
catch (error) {
handleDropboxError(error);
}
}
export async function getFileContent(path) {
try {
const client = await getDropboxClientForPath(path);
// Get metadata first to verify it's a file
const metadata = await client.filesGetMetadata({
path: formatDropboxPath(path)
});
if (metadata.result['.tag'] !== 'file') {
return {
content: [{
type: 'text',
text: `Error: ${path} is not a file`,
}],
};
}
// Download the file content
const fileResponse = await client.filesDownload({
path: formatDropboxPath(path)
});
const fileData = fileResponse.result;
// Convert to base64 for safe transport
const content = Buffer.from(fileData.fileBinary).toString('base64');
return {
content: [{
type: 'text',
text: JSON.stringify({
name: metadata.result.name,
size: metadata.result.size,
content: content,
encoding: 'base64'
}, null, 2),
}],
};
}
catch (error) {
handleDropboxError(error);
}
}
// Get raw file metadata for processing (used by PDF analyzer and indexer)
export async function getRawFileMetadata(path) {
try {
const client = await getDropboxClientForPath(path);
const response = await client.filesGetMetadata({
path: formatDropboxPath(path),
include_media_info: true,
include_deleted: false,
include_has_explicit_shared_members: true,
});
return response.result;
}
catch (error) {
handleDropboxError(error);
}
}
// Download file as Buffer for processing (used by PDF analyzer)
export async function downloadFileAsBuffer(path) {
try {
const client = await getDropboxClientForPath(path);
// Download the file
const response = await client.filesDownload({
path: formatDropboxPath(path)
});
const fileData = response.result;
return fileData.fileBinary;
}
catch (error) {
handleDropboxError(error);
}
}
//# sourceMappingURL=file-operations.js.map