Content
# devctl-mcp
An MCP (Model Context Protocol) server that lets Claude manage long-running dev processes seamlessly — across Flutter, Next.js, Spring Boot, Vite, and any custom stack.
**The problem it solves:** When Claude spawns `flutter run`, `next dev`, or `gradle bootRun` via bash, it immediately loses control. No hot reload, no log access, no restart. This server runs as a persistent daemon that owns all dev processes and exposes simple tools Claude can call.
---
## Features
- **Flutter** — auto-detect FVM, target specific devices, hot reload & hot restart via Dart VM service
- **Next.js** — auto-detect package manager (npm / pnpm / yarn / bun) from lockfiles
- **Spring Boot** — auto-detect Maven or Gradle, use `mvnw`/`gradlew` wrappers when present
- **Vite / React** — detect `vite.config.*` or `react-scripts` in `package.json`
- **Generic** — run any custom command with full log capture
- **Log streaming** — circular buffer (1000 lines), filter by string, timestamps on demand
- **Full process lifecycle** — start, stop, restart with status tracking (starting → running → stopped/crashed)
---
## Installation
### Prerequisites
- Node.js 20+
- Claude Code CLI
### Setup
```bash
git clone git@github.com:adarsh-devappsys/devctl-mcp.git
cd devctl-mcp
npm install
npm run build
```
### Register globally with Claude Code
```bash
claude mcp add -s user devctl -- node /absolute/path/to/devctl-mcp/dist/index.js
```
Verify it's connected:
```bash
claude mcp list
# devctl: node .../dist/index.js - ✓ Connected
```
> The server will be available in all your Claude Code sessions globally.
### Project-level (alternative)
The repo ships with a `.mcp.json` — if you open this repo in Claude Code, it's auto-loaded without any extra setup.
---
## Available Tools
### Core — works for all frameworks
| Tool | Description |
|---|---|
| `list_processes` | List all managed processes with status, uptime, PID |
| `start_process` | Start a dev server — auto-detects the framework |
| `stop_process` | Gracefully stop a process (SIGTERM → SIGKILL after 5s) |
| `restart_process` | Stop + restart with original configuration |
| `get_logs` | Get recent log output, with optional line count and filter |
| `clear_logs` | Clear the log buffer for a process |
| `send_input` | Send raw text to process stdin (escape hatch) |
### Flutter-specific
| Tool | Description |
|---|---|
| `flutter_hot_reload` | Hot reload via Dart VM service, falls back to stdin `r` |
| `flutter_hot_restart` | Hot restart via Dart VM service, falls back to stdin `R` |
| `list_devices` | List connected Flutter devices and emulators |
---
## Usage Examples
### Flutter
```
List my connected Flutter devices
```
```
Start my Flutter app at /Users/me/projects/myapp, name it "myapp", on my Pixel device
```
```
Hot reload myapp
```
```
Hot restart myapp
```
```
Get the last 100 logs from myapp, filter by "error"
```
**With FVM (auto-detected):**
If your project has `.fvm/flutter_sdk` or `fvm_config.json`, FVM is used automatically. You can also force it:
```
Start myapp at /path/to/project with use_fvm true, device id "32211JEHN06807"
```
### Next.js
```
Start the Next.js project at /Users/me/projects/website, name it "web"
```
Package manager is auto-detected from lockfiles (`bun.lockb` → bun, `pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, else npm). Override with `package_manager: "pnpm"`.
### Spring Boot
```
Start /Users/me/projects/api, name it "api"
```
Maven vs Gradle auto-detected from `pom.xml` / `build.gradle`. Uses `./mvnw` or `./gradlew` wrappers when present.
### Any other stack
```
Start /Users/me/projects/backend, name it "django", command "python manage.py runserver"
```
### Checking logs
```
Show me the logs for myapp
```
```
Get last 50 lines from api, filter by "Exception"
```
```
Get logs from web with timestamps
```
---
## Framework Auto-Detection
The adapter is chosen automatically based on what's in your project directory:
| Framework | Detection |
|---|---|
| Flutter | `pubspec.yaml` exists |
| Spring Boot | `pom.xml` or `build.gradle` / `build.gradle.kts` |
| Next.js | `package.json` with `next` in dependencies |
| Vite | `vite.config.*` file, or `vite`/`react-scripts` in `package.json` |
| Generic | Fallback — requires `command` option |
Priority: Flutter → Spring Boot → Next.js → Vite → Generic
### FVM Auto-Detection (Flutter)
FVM is auto-enabled when **both** are true:
1. `fvm` binary is in `PATH`
2. Project has `.fvm/flutter_sdk`, `fvm_config.json`, or `.fvmrc`
Override: pass `use_fvm: true` or `use_fvm: false` explicitly.
### Package Manager Auto-Detection (JS projects)
Detected from lockfiles in priority order:
1. `bun.lockb` → bun
2. `pnpm-lock.yaml` → pnpm
3. `yarn.lock` → yarn
4. Fallback → npm
---
## How Flutter Hot Reload Works
The server uses a two-tier system for maximum reliability:
**Tier 1 — Dart VM service (preferred)**
When `flutter run` starts, it prints a line like:
```
A Dart VM Service on Pixel 7a is available at: http://127.0.0.1:56789/TOKEN=/
```
The server captures this URL and derives a WebSocket endpoint. Hot reload/restart is sent as JSON-RPC over WebSocket — the same protocol Flutter DevTools uses.
Hot reload sequence: `getVM` → `reloadSources` → `callServiceExtension('ext.flutter.reassemble')`
Hot restart sequence: `getVM` → `callService('hotRestart')`
**Tier 2 — stdin fallback**
If the VM service URL isn't available yet (app still starting up), the server sends `r` or `R` to the process stdin — Flutter's interactive keyboard commands.
---
## Development
```bash
# Run without building (tsx watches for changes)
npm run dev
# Build
npm run build
# Type-check only
npm run typecheck
```
---
## Project Structure
```
src/
index.ts ← MCP server entry, stdio transport
types.ts ← Shared TypeScript interfaces
process-manager.ts ← Process registry, spawn, lifecycle, VM service URL parsing
log-store.ts ← Circular buffer (1000 lines, stdout + stderr + timestamps)
adapters/
flutter.ts ← Flutter + FVM detection
nextjs.ts ← Next.js + package manager detection
spring-boot.ts ← Maven/Gradle + wrapper detection
vite.ts ← Vite/React-scripts detection
generic.ts ← Fallback for custom commands
registry.ts ← Ordered detection chain
tools/
process-tools.ts ← 7 core MCP tools
flutter-tools.ts ← Flutter hot reload/restart + list_devices
```
---
## Important Notes
- **Session persistence:** Managed processes live as long as the Claude Code session is open. If you close the session, running processes are stopped. This is a fundamental constraint of the stdio MCP transport.
- **stdout is reserved:** The MCP protocol uses `process.stdout` for JSON-RPC messages. All internal server logs go to `stderr`.
- **PATH inheritance:** The server inherits your shell `PATH`, so `fvm`, `flutter`, `mvn`, `gradle`, etc. must be on your PATH as they would be in a terminal.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
cc-switch
All-in-One Assistant for Claude Code, Codex & Gemini CLI across platforms.
servers
Model Context Protocol Servers
servers
Model Context Protocol Servers
Time
A Model Context Protocol server for time and timezone conversions.