#
tokens: 6371/50000 6/6 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitignore
├── Dockerfile
├── index.js
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
└── smithery.yaml
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp
.cache

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# React MCP (Model Context Protocol)

[![smithery badge](https://smithery.ai/badge/@Streen9/react-mcp)](https://smithery.ai/server/@Streen9/react-mcp)

A powerful server implementation that enables Claude AI to interact with React applications through the Model Context Protocol.

<a href="https://glama.ai/mcp/servers/xsjsdumc7x">
  <img width="380" height="200" src="https://glama.ai/mcp/servers/xsjsdumc7x/badge" alt="https://github.com/Streen9/react-mcp MCP server" />
</a>

## Sample Usage

- [Markdown Editor/Viewer By Claude](https://claude.ai/share/f68940f1-97cd-41df-9c14-f63dc6fb9faf)
  ![image](https://github.com/user-attachments/assets/2f1087f5-006f-4d3f-a718-751267adafcc)

- [API Tester By Claude](https://claude.ai/share/b0b3943c-5c90-4b8d-8613-e76eaa243407)
  ![image](https://github.com/user-attachments/assets/dc627114-736e-4ca5-824b-cd084aa1813a)

## Overview

React MCP provides a bridge between Claude AI and the React ecosystem, allowing Claude to:

- Create new React applications
- Run React development servers
- Manage files and directories
- Install npm packages
- Execute terminal commands
- Track and manage long-running processes

This server implements the Model Context Protocol, providing Claude with the ability to perform real-world actions in the development environment.

## Features

- **React Project Management**

  - Create new React applications with optional templates
  - Run development servers
  - Manage dependencies

- **File Operations**

  - Read and write files
  - Edit React components and configuration

- **Process Management**

  - Start and monitor long-running processes
  - Track process output in real-time
  - Terminate processes when needed

- **Command Execution**

  - Run arbitrary terminal commands
  - Install npm packages
  - Execute development tasks

- **Comprehensive Logging**
  - Detailed JSON and text logs
  - Process tracking with timestamps
  - Execution history

## Installation

### Installing via Smithery

To install React MCP for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@Streen9/react-mcp):

```bash
npx -y @smithery/cli install @Streen9/react-mcp --client claude
```

### Manual Installation
1. Clone this repository
2. Install dependencies:

```bash
npm install
```

## Usage

Add this in `claude_desktop_config`:

```
{
  "mcpServers": {
    "react-mcp": {
      "command": "node",
      "args": [
        "C:/Users/kalip/OneDrive/Desktop/react-mcp/index.js"
      ]
    },
  }
}
```

The server runs on the stdio transport, allowing it to be used with Desktop Claude APP as a Model Context Protocol tool.

## Available Tools

### `create-react-app`

Creates a new React application.

Parameters:

- `name` (required): Name of the React app
- `template` (optional): Template to use (e.g., typescript, cra-template-pwa)
- `directory` (optional): Base directory to create the app in (defaults to home directory)

### `run-react-app`

Runs a React application in development mode.

Parameters:

- `projectPath` (required): Path to the React project folder

### `run-command`

Runs a terminal command.

Parameters:

- `command` (required): Command to execute
- `directory` (optional): Directory to run the command in (defaults to current directory)

### `get-process-output`

Gets the output from a running or completed process.

Parameters:

- `processId` (required): ID of the process to get output from

### `stop-process`

Stops a running process.

Parameters:

- `processId` (required): ID of the process to stop

### `list-processes`

Lists all running processes.

### `edit-file`

Creates or edits a file.

Parameters:

- `filePath` (required): Path to the file to edit
- `content` (required): Content to write to the file

### `read-file`

Reads the contents of a file.

Parameters:

- `filePath` (required): Path to the file to read

### `install-package`

Installs a npm package in a project.

Parameters:

- `packageName` (required): Name of the package to install (can include version)
- `directory` (optional): Directory of the project (defaults to current directory)
- `dev` (optional): Whether to install as a dev dependency

### `check-installation-status`

Checks the status of a package installation process.

Parameters:

- `processId` (required): ID of the installation process to check

## Logging

The server maintains detailed logs in the `logs` directory:

- `react-mcp-logs.json`: Structured JSON logs
- `react-mcp-logs.txt`: Human-readable text logs

## Architecture

The server uses the following key components:

- **Model Context Protocol SDK**: For communication with Claude AI
- **StdioServerTransport**: For I/O through standard input/output
- **Zod**: For schema validation and type safety
- **Child Process**: For spawning and managing external processes

## License

MIT

## Author

[@streen9](https://github.com/Streen9)

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml

startCommand:
  type: stdio
  configSchema:
    # JSON Schema defining the configuration options for the MCP.
    {}
  exampleConfig: {}
  commandFunction:
    # A JS function that produces the CLI command based on the given config to start the MCP on stdio.
    |-
    (config) => ({ command: 'node', args: ['index.js'], env: {} })

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "react-mcp",
  "version": "0.1.0",
  "description": "A React MCP server for Claude integration",
  "type": "module",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "[email protected]",
  "license": "MIT",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0",
    "js-yaml": "^4.1.0",
    "zod": "^3.22.4"
  }
}
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
FROM node:lts-alpine

WORKDIR /app

# Copy package.json and package-lock.json (if available) for dependency installation
COPY package*.json ./

# Install dependencies without running any scripts to avoid potential issues
RUN npm install --ignore-scripts

# Copy the rest of the application files
COPY . .

# Expose port if needed (not required for stdio transport, but in case)
# EXPOSE 3000

# Command to run the MCP server using stdio transport
CMD ["npm", "start"]

```

--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------

```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 { z } from "zod";
import { spawn, exec } from "child_process";
import fs from "fs";
import path from "path";
import os from "os";

// Initialize logging
const LOG_DIR = "logs";
if (!fs.existsSync(LOG_DIR)) {
  fs.mkdirSync(LOG_DIR);
}

const getCurrentTimestamp = () => {
  return new Date().toISOString().replace(/[:.]/g, "-");
};

const logToFile = (data, type = "json") => {
  const timestamp = getCurrentTimestamp();
  const logEntry = {
    timestamp,
    ...data,
  };

  // JSON logging
  const jsonLogPath = path.join(LOG_DIR, "react-mcp-logs.json");
  let jsonLogs = [];
  if (fs.existsSync(jsonLogPath)) {
    const fileContent = fs.readFileSync(jsonLogPath, "utf8");
    jsonLogs = fileContent ? JSON.parse(fileContent) : [];
  }
  jsonLogs.push(logEntry);
  fs.writeFileSync(jsonLogPath, JSON.stringify(jsonLogs, null, 2));

  // Text logging
  const txtLogPath = path.join(LOG_DIR, "react-mcp-logs.txt");
  const txtLogEntry = `[${timestamp}] ${JSON.stringify(data)}\n`;
  fs.appendFileSync(txtLogPath, txtLogEntry);
};

// Keep track of running processes
const runningProcesses = new Map();

// Execute terminal commands
async function executeCommand(command, options = {}) {
  return new Promise((resolve, reject) => {
    exec(command, options, (error, stdout, stderr) => {
      if (error) {
        return reject({ error, stderr });
      }
      resolve({ stdout, stderr });
    });
  });
}

// Start a long-running process and return its output stream
function startProcess(command, args, cwd) {
  const childProcess = spawn(command, args, {
    cwd,
    shell: true,
    env: { ...process.env, FORCE_COLOR: "true" },
  });

  let output = "";
  let errorOutput = "";

  childProcess.stdout.on("data", (data) => {
    const chunk = data.toString();
    output += chunk;
  });

  childProcess.stderr.on("data", (data) => {
    const chunk = data.toString();
    errorOutput += chunk;
  });

  const processId = Math.random().toString(36).substring(2, 15);

  runningProcesses.set(processId, {
    process: childProcess,
    command,
    args,
    cwd,
    output,
    errorOutput,
    startTime: new Date(),
    processId,
  });

  return processId;
}

// Tool handlers
async function handleCreateReactApp(params) {
  try {
    const { name, template, directory } = params;

    if (!name) {
      throw new Error("Project name is required");
    }

    // Determine base directory
    const baseDir = directory || os.homedir();
    const projectDir = path.join(baseDir, name);

    // Check if directory already exists
    if (fs.existsSync(projectDir)) {
      throw new Error(`Directory ${projectDir} already exists`);
    }

    // Prepare create-react-app command
    const createCommand = template
      ? `npx create-react-app ${name} --template ${template}`
      : `npx create-react-app ${name}`;

    console.log(
      `Creating React app in ${baseDir} with command: ${createCommand}`
    );

    // Run the command
    const processId = startProcess(createCommand, [], baseDir);

    return {
      message: `Creating React app "${name}" in ${projectDir}`,
      processId: processId,
      projectDir: projectDir,
    };
  } catch (error) {
    return {
      error: `Error creating React app: ${error.message}`,
    };
  }
}

async function handleRunReactApp(params) {
  try {
    const { projectPath } = params;

    if (!projectPath) {
      throw new Error("Project path is required");
    }

    // Check if directory exists
    if (!fs.existsSync(projectPath)) {
      throw new Error(`Directory ${projectPath} does not exist`);
    }

    // Check if it's a React app (package.json exists with react dependency)
    const packageJsonPath = path.join(projectPath, "package.json");
    if (!fs.existsSync(packageJsonPath)) {
      throw new Error(
        `Not a valid React app: package.json not found in ${projectPath}`
      );
    }

    const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
    if (!packageJson.dependencies || !packageJson.dependencies.react) {
      throw new Error(
        `Not a valid React app: react dependency not found in package.json`
      );
    }

    // Start the development server
    const processId = startProcess("npm", ["start"], projectPath);

    return {
      message: `Starting React development server in ${projectPath}`,
      processId: processId,
      note: "The development server should be accessible at http://localhost:3000",
    };
  } catch (error) {
    return {
      error: `Error running React app: ${error.message}`,
    };
  }
}

async function handleRunCommand(params) {
  try {
    const { command, directory } = params;

    if (!command) {
      throw new Error("Command is required");
    }

    // Determine directory
    const workingDir = directory || process.cwd();

    // Check if directory exists
    if (!fs.existsSync(workingDir)) {
      throw new Error(`Directory ${workingDir} does not exist`);
    }

    // Run the command
    const result = await executeCommand(command, { cwd: workingDir });

    return {
      command: command,
      directory: workingDir,
      output: result.stdout,
      stderr: result.stderr || "",
    };
  } catch (error) {
    return {
      error: `Error executing command: ${error.message}`,
      stderr: error.stderr || "",
    };
  }
}

async function handleGetProcessOutput(params) {
  try {
    const { processId } = params;

    if (!processId) {
      throw new Error("Process ID is required");
    }

    if (!runningProcesses.has(processId)) {
      throw new Error(`Process with ID ${processId} not found`);
    }

    const processInfo = runningProcesses.get(processId);
    const isRunning = processInfo.process.exitCode === null;

    return {
      processId: processId,
      command: `${processInfo.command} ${processInfo.args.join(" ")}`,
      directory: processInfo.cwd,
      isRunning: isRunning,
      exitCode: processInfo.process.exitCode,
      output: processInfo.output,
      errorOutput: processInfo.errorOutput,
      startTime: processInfo.startTime.toISOString(),
      runTime: `${Math.floor(
        (new Date() - processInfo.startTime) / 1000
      )} seconds`,
    };
  } catch (error) {
    return {
      error: `Error getting process output: ${error.message}`,
    };
  }
}

async function handleStopProcess(params) {
  try {
    const { processId } = params;

    if (!processId) {
      throw new Error("Process ID is required");
    }

    if (!runningProcesses.has(processId)) {
      throw new Error(`Process with ID ${processId} not found`);
    }

    const processInfo = runningProcesses.get(processId);

    // Kill the process
    processInfo.process.kill();

    return {
      message: `Process ${processId} stopped`,
      command: `${processInfo.command} ${processInfo.args.join(" ")}`,
      directory: processInfo.cwd,
    };
  } catch (error) {
    return {
      error: `Error stopping process: ${error.message}`,
    };
  }
}

async function handleListProcesses() {
  try {
    const processes = [];

    for (const [processId, processInfo] of runningProcesses.entries()) {
      const isRunning = processInfo.process.exitCode === null;

      processes.push({
        processId: processId,
        command: `${processInfo.command} ${processInfo.args.join(" ")}`,
        directory: processInfo.cwd,
        isRunning: isRunning,
        exitCode: processInfo.process.exitCode,
        startTime: processInfo.startTime.toISOString(),
        runTime: `${Math.floor(
          (new Date() - processInfo.startTime) / 1000
        )} seconds`,
      });
    }

    return {
      processes: processes,
      count: processes.length,
    };
  } catch (error) {
    return {
      error: `Error listing processes: ${error.message}`,
    };
  }
}

async function handleEditFile(params) {
  try {
    const { filePath, content } = params;

    if (!filePath) {
      throw new Error("File path is required");
    }

    if (content === undefined || content === null) {
      throw new Error("File content is required");
    }

    // Make sure directory exists
    const directory = path.dirname(filePath);
    if (!fs.existsSync(directory)) {
      fs.mkdirSync(directory, { recursive: true });
    }

    // Write content to file
    fs.writeFileSync(filePath, content, "utf8");

    return {
      message: `File ${filePath} updated successfully`,
      filePath: filePath,
      size: Buffer.byteLength(content, "utf8"),
    };
  } catch (error) {
    return {
      error: `Error editing file: ${error.message}`,
    };
  }
}

async function handleReadFile(params) {
  try {
    const { filePath } = params;

    if (!filePath) {
      throw new Error("File path is required");
    }

    // Check if file exists
    if (!fs.existsSync(filePath)) {
      throw new Error(`File ${filePath} does not exist`);
    }

    // Read file content
    const content = fs.readFileSync(filePath, "utf8");

    return {
      filePath: filePath,
      content: content,
      size: Buffer.byteLength(content, "utf8"),
    };
  } catch (error) {
    return {
      error: `Error reading file: ${error.message}`,
    };
  }
}

async function handleInstallPackage(params) {
  try {
    const { packageName, directory, dev } = params;

    if (!packageName) {
      throw new Error("Package name is required");
    }

    // Determine directory
    const workingDir = directory || process.cwd();

    // Check if directory exists
    if (!fs.existsSync(workingDir)) {
      throw new Error(`Directory ${workingDir} does not exist`);
    }

    // Check if package.json exists
    const packageJsonPath = path.join(workingDir, "package.json");
    if (!fs.existsSync(packageJsonPath)) {
      throw new Error(
        `Not a valid Node.js project: package.json not found in ${workingDir}`
      );
    }

    // Install the package
    const installCommand = dev
      ? `npm install ${packageName} --save-dev`
      : `npm install ${packageName}`;

    const processId = startProcess(installCommand, [], workingDir);

    return {
      message: `Installing ${packageName} in ${workingDir}`,
      processId: processId,
      command: installCommand,
    };
  } catch (error) {
    return {
      error: `Error installing package: ${error.message}`,
    };
  }
}

// Server setup
const server = new Server(
  {
    name: "react-mcp",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {},
    },
  }
);

// Define schemas
const CreateReactAppSchema = z.object({
  name: z.string(),
  template: z.string().optional(),
  directory: z.string().optional(),
});

const RunReactAppSchema = z.object({
  projectPath: z.string(),
});

const RunCommandSchema = z.object({
  command: z.string(),
  directory: z.string().optional(),
});

const GetProcessOutputSchema = z.object({
  processId: z.string(),
});

const StopProcessSchema = z.object({
  processId: z.string(),
});

const EditFileSchema = z.object({
  filePath: z.string(),
  content: z.string(),
});

const ReadFileSchema = z.object({
  filePath: z.string(),
});

const InstallPackageSchema = z.object({
  packageName: z.string(),
  directory: z.string().optional(),
  dev: z.boolean().optional(),
});

// Tool request handler
server.setRequestHandler(ListToolsRequestSchema, async () => {
  const response = {
    tools: [
      {
        name: "create-react-app",
        description: "Create a new React application",
        inputSchema: {
          type: "object",
          properties: {
            name: {
              type: "string",
              description: "Name of the React app",
            },
            template: {
              type: "string",
              description:
                "Template to use (e.g., typescript, cra-template-pwa)",
            },
            directory: {
              type: "string",
              description:
                "Base directory to create the app in (defaults to home directory)",
            },
          },
          required: ["name"],
        },
      },
      {
        name: "run-react-app",
        description: "Run a React application in development mode",
        inputSchema: {
          type: "object",
          properties: {
            projectPath: {
              type: "string",
              description: "Path to the React project folder",
            },
          },
          required: ["projectPath"],
        },
      },
      {
        name: "run-command",
        description: "Run a terminal command",
        inputSchema: {
          type: "object",
          properties: {
            command: {
              type: "string",
              description: "Command to execute",
            },
            directory: {
              type: "string",
              description:
                "Directory to run the command in (defaults to current directory)",
            },
          },
          required: ["command"],
        },
      },
      {
        name: "get-process-output",
        description: "Get the output from a running or completed process",
        inputSchema: {
          type: "object",
          properties: {
            processId: {
              type: "string",
              description: "ID of the process to get output from",
            },
          },
          required: ["processId"],
        },
      },
      {
        name: "stop-process",
        description: "Stop a running process",
        inputSchema: {
          type: "object",
          properties: {
            processId: {
              type: "string",
              description: "ID of the process to stop",
            },
          },
          required: ["processId"],
        },
      },
      {
        name: "list-processes",
        description: "List all running processes",
        inputSchema: {
          type: "object",
          properties: {},
        },
      },
      {
        name: "edit-file",
        description: "Create or edit a file",
        inputSchema: {
          type: "object",
          properties: {
            filePath: {
              type: "string",
              description: "Path to the file to edit",
            },
            content: {
              type: "string",
              description: "Content to write to the file",
            },
          },
          required: ["filePath", "content"],
        },
      },
      {
        name: "read-file",
        description: "Read the contents of a file",
        inputSchema: {
          type: "object",
          properties: {
            filePath: {
              type: "string",
              description: "Path to the file to read",
            },
          },
          required: ["filePath"],
        },
      },
      {
        name: "install-package",
        description: "Install a npm package in a project",
        inputSchema: {
          type: "object",
          properties: {
            packageName: {
              type: "string",
              description:
                "Name of the package to install (can include version)",
            },
            directory: {
              type: "string",
              description:
                "Directory of the project (defaults to current directory)",
            },
            dev: {
              type: "boolean",
              description: "Whether to install as a dev dependency",
            },
          },
          required: ["packageName"],
        },
      },
    ],
  };
  logToFile({ event: "list_tools", response }, "json");
  return response;
});

// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request, context) => {
  const { name, arguments: args } = request.params;
  logToFile({ event: "call_tool", name, args }, "json");

  try {
    let result;

    switch (name) {
      case "create-react-app":
        const createArgs = CreateReactAppSchema.parse(args);
        result = await handleCreateReactApp(createArgs);
        break;

      case "run-react-app":
        const runArgs = RunReactAppSchema.parse(args);
        result = await handleRunReactApp(runArgs);
        break;

      case "run-command":
        const commandArgs = RunCommandSchema.parse(args);
        result = await handleRunCommand(commandArgs);
        break;

      case "get-process-output":
        const outputArgs = GetProcessOutputSchema.parse(args);
        result = await handleGetProcessOutput(outputArgs);
        break;

      case "stop-process":
        const stopArgs = StopProcessSchema.parse(args);
        result = await handleStopProcess(stopArgs);
        break;

      case "list-processes":
        result = await handleListProcesses();
        break;

      case "edit-file":
        const editArgs = EditFileSchema.parse(args);
        result = await handleEditFile(editArgs);
        break;

      case "read-file":
        const readArgs = ReadFileSchema.parse(args);
        result = await handleReadFile(readArgs);
        break;

      case "install-package":
        const installArgs = InstallPackageSchema.parse(args);
        result = await handleInstallPackage(installArgs);
        break;

      default:
        throw new Error(`Unknown tool: ${name}`);
    }

    return createTextResponse(JSON.stringify(result, null, 2));
  } catch (error) {
    if (error instanceof z.ZodError) {
      throw new Error(
        `Invalid arguments: ${error.errors
          .map((e) => `${e.path.join(".")}: ${e.message}`)
          .join(", ")}`
      );
    }
    throw error;
  }
});

// Start the server
const transport = new StdioServerTransport();
server.connect(transport).then(() => {
  console.error("React MCP Server running on stdio");
});

const createTextResponse = (text) => ({
  content: [{ type: "text", text }],
});

// Clean up processes on exit
process.on("exit", () => {
  for (const [processId, processInfo] of runningProcesses.entries()) {
    try {
      processInfo.process.kill();
    } catch (error) {
      console.error(`Failed to kill process ${processId}:`, error);
    }
  }
});

process.on("SIGINT", () => {
  process.exit(0);
});

process.on("SIGTERM", () => {
  process.exit(0);
});

```