@cloudwerxlab/all-your-base64-mcp
Version:
MCP server that converts base64 images and image URLs into markdown for display in chat. Supports OpenWebUI
175 lines (156 loc) • 6.58 kB
JavaScript
import path from 'path';
import fs from 'fs';
import { fileURLToPath, URL } from 'url';
import { randomUUID } from 'crypto';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
import express from 'express';
// Get directory info for ES modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Configuration: parse BASE_URL or use default
const DEFAULT_PORT = 6644;
const BASE_URL_ENV = process.env.BASE_URL;
let BASE_URL;
let PORT;
if (BASE_URL_ENV) {
BASE_URL = BASE_URL_ENV;
try {
const urlObj = new URL(BASE_URL_ENV);
PORT = parseInt(urlObj.port) || DEFAULT_PORT;
} catch {
PORT = parseInt(process.env.PORT) || DEFAULT_PORT;
}
} else {
PORT = parseInt(process.env.PORT) || DEFAULT_PORT;
BASE_URL = `http://localhost:${PORT}`;
}
const IMAGE_DIR = path.join(__dirname, 'public', 'images');
// Ensure the images directory exists
if (!fs.existsSync(IMAGE_DIR)) {
fs.mkdirSync(IMAGE_DIR, { recursive: true });
console.error(`Created images directory: ${IMAGE_DIR}`);
}
// Start HTTP server to serve static images
const app = express();
app.use('/images', express.static(IMAGE_DIR));
app.listen(PORT, () => {
console.error(`🖼 Image server listening at ${BASE_URL}/images`);
});
// Create a shared MCP server with image display tools
function createMcpServer() {
const server = new McpServer({
name: 'AllYourBase64MCP',
version: '2.2.0',
description: 'Converts base64 images and image URLs into markdown for display in chat'
});
// Tool to convert base64 image data to a URL and return markdown
server.tool(
'base64-to-url',
{ data: z.string().describe('Base64-encoded image data, optionally prefixed with data URI scheme (e.g., data:image/png;base64,...)') },
{
title: "Convert base64 image to URL for display in chat",
description: "Use this tool when you need to display an image that's provided as base64-encoded data. Ideal for cases where an image is generated by code, extracted from PDFs, or taken from API responses that include base64 images. The tool saves the image to a server and returns a markdown link. To display the image, embed the returned markdown directly in your response: . Do NOT wrap in code blocks. Always include a description of what the image shows in your response. Supports PNG, JPEG, and other common formats, with or without the data URI prefix."
},
async ({ data }) => {
try {
// Handle data URI prefix if present
let mime = 'image/png';
let b64 = data;
const match = data.match(/^data:(image\/[^;]+);base64,(.+)$/);
if (match) {
mime = match[1];
b64 = match[2];
}
// Determine file extension based on MIME type
const ext = mime.split('/')[1];
const filename = `${randomUUID()}.${ext}`;
const filePath = path.join(IMAGE_DIR, filename);
// Ensure the images directory exists
await fs.promises.mkdir(IMAGE_DIR, { recursive: true }).catch(console.error);
// Save the image file
const buffer = Buffer.from(b64, 'base64');
await fs.promises.writeFile(filePath, buffer);
// Generate URL and markdown
const url = `${BASE_URL}/images/${filename}`;
const markdown = ``;
console.error(`Converted base64 image to URL: ${url}`);
return {
content: [{
type: 'text',
text: markdown
}]
};
} catch (error) {
console.error('Error processing base64 image:', error);
return {
content: [{
type: 'text',
text: `Error converting base64 image: ${error.message}`
}],
isError: true
};
}
}
);
// Tool to display an existing image URL as markdown
server.tool(
'display-url',
{ url: z.string().url().describe('URL of an image to display') },
{
title: "Display image URL in chat",
description: "Use this tool when you already have a valid image URL and need to display that image in the chat. Perfect for showing images from websites, documentation, or public image repositories. The tool formats the URL as markdown for proper display. To show the image, embed the returned markdown directly in your response: . Do NOT wrap in code blocks. Always reference the image content in your response text and provide context about what the image shows. This tool does not download or modify the original image."
},
async ({ url }) => {
try {
const markdown = ``;
console.error(`Formatted image URL for display: ${url}`);
return {
content: [{
type: 'text',
text: markdown
}]
};
} catch (error) {
console.error('Error formatting image URL:', error);
return {
content: [{
type: 'text',
text: `Error displaying image URL: ${error.message}`
}],
isError: true
};
}
}
);
return server;
}
// stdio-only startup
const server = createMcpServer();
const transport = new StdioServerTransport();
server.connect(transport).then(() => {
console.error('✅ AllYourBase64MCP MCP server running on stdio');
console.error('🔧 Available tools:');
console.error(' - base64-to-url: Use this tool when you need to display an image that\'s provided as base64-encoded data. Embed the returned markdown directly in your response.');
console.error(' - display-url: Use this tool when you already have a valid image URL and need to display that image in the chat. Embed the returned markdown directly in your response.');
}).catch(err => {
console.error('❌ Error connecting MCP server:', err);
process.exit(1);
});
// Handle graceful shutdown
process.on('SIGINT', async () => {
console.error('🛑 Shutting down AllYourBase64MCP...');
await server.close();
console.error('👋 Server shutdown complete. Goodbye!');
process.exit(0);
});
process.on('SIGTERM', async () => {
console.error('🛑 Shutting down AllYourBase64MCP...');
await server.close();
console.error('👋 Server shutdown complete. Goodbye!');
process.exit(0);
});
// Export for programmatic use
export default createMcpServer;