@noranekob/fal-mcp-server
Version:
MCP Server for fal.ai - Upload any files (images, videos, audio, text, etc.) to fal.ai and get remote URLs
149 lines (129 loc) • 4.38 kB
JavaScript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { spawn } from 'child_process';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
class FALMCPServer {
constructor() {
this.server = new Server(
{
name: 'fal-mcp-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
this.setupToolHandlers();
}
setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'upload_file_to_fal',
description: '任意のファイル(画像、動画、音声、テキストなど)をfal.aiにアップロードしてリモートURLを取得する',
inputSchema: {
type: 'object',
properties: {
file_path: {
type: 'string',
description: 'アップロードするファイルのパス'
}
},
required: ['file_path']
}
}
]
};
});
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
if (name === 'upload_file_to_fal') {
return await this.uploadFileToFal(args.file_path);
}
throw new Error(`Unknown tool: ${name}`);
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error: ${error.message}`
}
],
isError: true
};
}
});
}
async uploadFileToFal(filePath) {
return new Promise((resolve, reject) => {
// Pythonスクリプトのパス
const pythonScript = path.join(__dirname, 'upload_to_fal.py');
// ファイルの存在確認
if (!fs.existsSync(filePath)) {
reject(new Error(`ファイルが見つかりません: ${filePath}`));
return;
}
if (!fs.existsSync(pythonScript)) {
reject(new Error(`Pythonスクリプトが見つかりません: ${pythonScript}`));
return;
}
// Pythonスクリプトを実行(Windows/Mac両対応)
const pythonCommand = process.platform === 'win32' ? 'python' : 'python3';
const pythonProcess = spawn(pythonCommand, [pythonScript, filePath], {
stdio: ['pipe', 'pipe', 'pipe']
});
let stdout = '';
let stderr = '';
pythonProcess.stdout.on('data', (data) => {
stdout += data.toString();
});
pythonProcess.stderr.on('data', (data) => {
stderr += data.toString();
});
pythonProcess.on('close', (code) => {
if (code === 0) {
// 成功時はURLを抽出
const urlMatch = stdout.match(/Remote URL: (https?:\/\/[^\s]+)/);
const url = urlMatch ? urlMatch[1] : null;
if (url) {
resolve({
content: [
{
type: 'text',
text: `✅ ファイルのアップロードに成功しました!\n\n**ファイル**: ${path.basename(filePath)}\n**リモートURL**: ${url}\n\n詳細ログ:\n${stdout}`
}
]
});
} else {
reject(new Error(`URLの抽出に失敗しました。出力: ${stdout}`));
}
} else {
reject(new Error(`Pythonスクリプトの実行に失敗しました (終了コード: ${code})。エラー: ${stderr}\n出力: ${stdout}`));
}
});
pythonProcess.on('error', (error) => {
reject(new Error(`Pythonプロセスの起動に失敗しました: ${error.message}`));
});
});
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('FAL MCP server running on stdio');
}
}
const server = new FALMCPServer();
server.run().catch(console.error);