#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
Tool,
} from "@modelcontextprotocol/sdk/types.js";
import { exec } from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);
/**
* Infinigen MCP Server
*
* This server provides tools for generating 3D scenes using Infinigen,
* a procedural 3D scene generator from Princeton Vision & Learning Lab.
*/
// Server configuration
const SERVER_NAME = "infinigen-mcp";
const SERVER_VERSION = "0.1.0";
// Available tools
const TOOLS: Tool[] = [
{
name: "generate_nature_scene",
description: "Generate a photorealistic natural outdoor scene using Infinigen. Creates terrain, vegetation, and natural elements.",
inputSchema: {
type: "object",
properties: {
seed: {
type: "number",
description: "Random seed for reproducible generation (default: random)",
},
output_folder: {
type: "string",
description: "Output folder path for generated scene files",
},
configs: {
type: "array",
items: { type: "string" },
description: "Configuration files to use (e.g., ['desert.gin', 'simple.gin'])",
default: ["simple.gin"],
},
use_gpu: {
type: "boolean",
description: "Whether to use GPU acceleration (default: false)",
default: false,
},
},
required: ["output_folder"],
},
},
{
name: "generate_indoor_scene",
description: "Generate a photorealistic indoor scene with furniture and decorations using Infinigen.",
inputSchema: {
type: "object",
properties: {
seed: {
type: "number",
description: "Random seed for reproducible generation (default: random)",
},
output_folder: {
type: "string",
description: "Output folder path for generated scene files",
},
configs: {
type: "array",
items: { type: "string" },
description: "Configuration files to use",
default: ["simple.gin"],
},
use_gpu: {
type: "boolean",
description: "Whether to use GPU acceleration (default: false)",
default: false,
},
},
required: ["output_folder"],
},
},
{
name: "check_infinigen",
description: "Check if Infinigen is properly installed and accessible",
inputSchema: {
type: "object",
properties: {},
},
},
];
/**
* Check if Infinigen is installed and accessible
*/
async function checkInfinigenInstallation(): Promise<{ installed: boolean; version?: string; error?: string }> {
try {
await execAsync("python -m infinigen_examples.generate_nature --help");
return {
installed: true,
version: "Infinigen is accessible",
};
} catch (error) {
return {
installed: false,
error: error instanceof Error ? error.message : "Unknown error",
};
}
}
/**
* Generate a nature scene using Infinigen
*/
async function generateNatureScene(args: {
seed?: number;
output_folder: string;
configs?: string[];
use_gpu?: boolean;
}): Promise<string> {
const seed = args.seed ?? Math.floor(Math.random() * 1000000);
const configs = args.configs ?? ["simple.gin"];
const useGpu = args.use_gpu ?? false;
const gpuFlag = useGpu ? "" : "LocalScheduleHandler.use_gpu=False";
const command = `python -m infinigen.datagen.manage_jobs --output_folder ${args.output_folder} --num_scenes 1 --specific_seed ${seed} --configs ${configs.join(" ")} --pipeline_configs local_16GB.gin monocular.gin blender_gt.gin ${gpuFlag ? `--pipeline_overrides ${gpuFlag}` : ""}`;
try {
const { stdout, stderr } = await execAsync(command, { maxBuffer: 10 * 1024 * 1024 });
return `Scene generation started successfully.\nSeed: ${seed}\nOutput: ${args.output_folder}\n\nCommand output:\n${stdout}\n${stderr || ""}`;
} catch (error) {
throw new Error(`Failed to generate scene: ${error instanceof Error ? error.message : "Unknown error"}`);
}
}
/**
* Generate an indoor scene using Infinigen
*/
async function generateIndoorScene(args: {
seed?: number;
output_folder: string;
configs?: string[];
use_gpu?: boolean;
}): Promise<string> {
const seed = args.seed ?? Math.floor(Math.random() * 1000000);
const configs = args.configs ?? ["simple.gin"];
const useGpu = args.use_gpu ?? false;
const gpuFlag = useGpu ? "" : "LocalScheduleHandler.use_gpu=False";
const command = `python -m infinigen.datagen.manage_jobs --output_folder ${args.output_folder} --num_scenes 1 --specific_seed ${seed} --configs ${configs.join(" ")} indoor_constraintfinexterior.gin --pipeline_configs local_16GB.gin monocular.gin blender_gt.gin ${gpuFlag ? `--pipeline_overrides ${gpuFlag}` : ""}`;
try {
const { stdout, stderr } = await execAsync(command, { maxBuffer: 10 * 1024 * 1024 });
return `Indoor scene generation started successfully.\nSeed: ${seed}\nOutput: ${args.output_folder}\n\nCommand output:\n${stdout}\n${stderr || ""}`;
} catch (error) {
throw new Error(`Failed to generate indoor scene: ${error instanceof Error ? error.message : "Unknown error"}`);
}
}
/**
* Main server setup
*/
async function main() {
const server = new Server(
{
name: SERVER_NAME,
version: SERVER_VERSION,
},
{
capabilities: {
tools: {},
},
}
);
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return { tools: TOOLS };
});
// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "check_infinigen": {
const result = await checkInfinigenInstallation();
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
case "generate_nature_scene": {
const result = await generateNatureScene(args as any);
return {
content: [
{
type: "text",
text: result,
},
],
};
}
case "generate_indoor_scene": {
const result = await generateIndoorScene(args as any);
return {
content: [
{
type: "text",
text: result,
},
],
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`,
},
],
isError: true,
};
}
});
// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);
console.error(`${SERVER_NAME} v${SERVER_VERSION} started`);
console.error("Server running on stdio");
}
// Run the server
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});