---
title: Local Dev Server
description: Run FrontMCP locally with hot reload, MCP Inspector, and debug logging
slug: deployment/local-dev-server
icon: computer
keywords: ['frontmcp', 'mcp', 'model context protocol', 'deployment', 'production', 'hosting', 'local dev', 'development server', 'typescript']
---

Run your FrontMCP server locally with hot-reload and verify it with the **FrontMCP Inspector** (zero setup).

## Prerequisites

- **Node.js**:
  - **Minimum**: Version 22 (LTS Maintenance)
  - **Recommended**: Version 24 (Active LTS)
  - _FrontMCP is developed and tested on Node.js 24_
- **npm ≥ 10** (pnpm/yarn also supported)

---

## Quick start

### Option A — New project

Creates a folder and scaffolds everything for you.

<CodeGroup>
    ```bash universal
    npx frontmcp create my-app
    ```

</CodeGroup>

<Info>
The `create` command is interactive by default. Use `--yes` for non-interactive mode with defaults (Docker target, Redis via Docker Compose, GitHub Actions enabled).
</Info>

### Option B — Existing project

Install and initialize in your current repo:

<CodeGroup>```bash universal npm i -D frontmcp @types/node@^24 npx frontmcp init ```</CodeGroup>

`init` adds the required scripts and updates **tsconfig.json** automatically.

---

## Package scripts

After `create` or `init`, your `package.json` will include:

```json
{
  "scripts": {
    "dev": "frontmcp dev",
    "build": "frontmcp build",
    "inspect": "frontmcp inspector",
    "doctor": "frontmcp doctor"
  }
}
```

- `frontmcp dev` — run in watch mode with type-checks
- `frontmcp build` — compile to `./dist` (override with `--out-dir`)
- `frontmcp inspector` — launches **@modelcontextprotocol/inspector** with zero setup
- `frontmcp doctor` — verifies Node/npm versions and project configuration

### Docker scripts (Docker target only)

If you created your project with the Docker target (`--target node`), you'll also have:

```json
{
  "scripts": {
    "docker:up": "docker compose -f ci/docker-compose.yml up",
    "docker:down": "docker compose -f ci/docker-compose.yml down",
    "docker:build": "docker compose -f ci/docker-compose.yml build"
  }
}
```

```bash
# Start the full stack (app + Redis if configured)
npm run docker:up

# Stop containers
npm run docker:down

# Rebuild the Docker image
npm run docker:build

# Rebuild only the app service (selective rebuild — skips Redis)
docker compose -f ci/docker-compose.yml up --build app
```

---

## Recommended tsconfig

`init` writes this for you, but if you prefer to manage it manually:

```json title="tsconfig.json"
{
  "compilerOptions": {
    "target": "es2021",
    "module": "esnext",
    "lib": ["es2021"],
    "moduleResolution": "bundler",
    "rootDir": "src",
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "sourceMap": true
  },
  "include": ["src/**/*.ts"],
  "exclude": ["**/*.spec.ts", "**/__tests__/**"]
}
```

---

## Minimal app

> You can import from `@frontmcp/sdk` directly; no extra install needed.

**src/main.ts**

```ts
import 'reflect-metadata';
import { FrontMcp, LogLevel } from '@frontmcp/sdk';
import HelloApp from './hello.app';

@FrontMcp({
  info: { name: 'Hello MCP', version: '0.1.0' },
  apps: [HelloApp],
  http: { port: Number(process.env.PORT) || 3000 },
  logging: { level: LogLevel.Info },
})
export default class Server {}
```

**src/hello.app.ts**

```ts
import { App } from '@frontmcp/sdk';
import Greet from './tools/greet.tool';

@App({ id: 'hello', name: 'Hello', tools: [Greet] })
export default class HelloApp {}
```

**src/tools/greet.tool.ts**

```ts
import { Tool, ToolContext, z } from '@frontmcp/sdk';

@Tool({
  name: 'greet',
  description: 'Greets a user by name',
  inputSchema: { name: z.string() },
})
export default class Greet extends ToolContext {
  async execute({ name }: { name: string }) {
    return `Hello, ${name}!`;
  }
}
```

---

## Run the dev server

<CodeGroup>```bash universal npm run dev ```</CodeGroup>

Your server will start (default `http://localhost:3000`). The console will print the MCP endpoint.

### Port conflict handling

`frontmcp dev` performs a pre-flight TCP probe before spawning `tsx --watch`. If
the port is already in use the command exits with a one-line message instead of
a raw `EADDRINUSE` stack trace:

```text
[dev] Port 3000 is already in use — refusing to start.
      Retry with one of:
        • frontmcp dev --port <other-port>
        • frontmcp dev --auto-port     (pick the next free port automatically)
        • PORT=<other-port> frontmcp dev
```

| Flag                | Behaviour                                                                                        |
| ------------------- | ------------------------------------------------------------------------------------------------ |
| `--port <n>`        | Bind the dev server to TCP port `<n>` (sets `PORT=<n>` in the child's env).                      |
| `--auto-port`       | If the requested port is busy, walk forward to the next free port.                               |
| `--show-conflict`   | On EADDRINUSE, run `lsof` (POSIX) to print which process is holding the port.                    |

<Warning>
The `PORT` env var only takes effect when your `@FrontMcp` metadata reads it —
the canonical `http: { port: Number(process.env.PORT) || 3000 }` pattern shown
above. If you **hard-code** `http.port: 4000` in metadata, your server will
bind to 4000 regardless of `--port` or `PORT`, and the pre-flight probe is
advisory only.
</Warning>

### MCP endpoint path

By default the MCP endpoint is served at the root path (`/`). Set
`transport.http.path` in `frontmcp.config.ts` to mount it elsewhere — `frontmcp dev`
honors it, so the endpoint matches the URL emitted in your generated client
config (`clients.*.url`) and an HTTP client pointed at that path no longer 404s:

```ts
// frontmcp.config.ts
export default {
  // ...
  transport: { default: 'http', http: { port: 3000, path: '/mcp' } },
};
```

```text
[dev] listening on port: 3000
[dev] MCP endpoint path: /mcp        # POST http://localhost:3000/mcp
```

Under the hood `dev` passes the configured path to the spawned server via the
`FRONTMCP_HTTP_ENTRY_PATH` env var, which the SDK's `http.entryPath` default
reads — the same pattern as `PORT`.

<Warning>
As with `PORT`, an explicit `@FrontMcp({ http: { entryPath } })` in your metadata
wins over `transport.http.path` / the env var. Keep the two in sync, or omit
`entryPath` from metadata and let `transport.http.path` drive the mount.
</Warning>

---

## Inspect locally (zero setup)

Launch the **FrontMCP Inspector** to exercise tools and messages in a friendly UI:

<CodeGroup>```bash universal npm run inspect ```</CodeGroup>

- This runs `npx @modelcontextprotocol/inspector` behind the scenes.
- Point it at your local server URL printed by `dev` (e.g., `http://localhost:3000`).
- Try calling `greet` and watch responses stream back in real time.

---

## Troubleshooting

- **Check configuration**

```bash
npm run doctor
```

Ensures Node/npm versions, entry detection, and `tsconfig.json` are correct.

- **Entry detection**
  The CLI looks for `package.json.main`; if missing, it falls back to `src/main.ts`. If no entry is found, it will tell you how to create one.

- **Type errors in dev**
  The `dev` command performs async type-checks while watching your files, so you’ll see issues immediately without stopping the server.
