Increase the time before auto shutdown of dev server (vibe-kanban 8b5175a7)

Increase from 60s to 120s, allow to be overriden with CLI
This commit is contained in:
Louis Knight-Webb
2025-11-05 11:48:41 +00:00
parent bb7562b47c
commit 54909d4215
4 changed files with 14 additions and 12 deletions

View File

@@ -1,6 +1,6 @@
# MCP Dev Server Manager # MCP Dev Server Manager
A daemon that accepts requests from MCP clients to start dev servers, allocating unique ports to avoid collisions and shutting down idle connections after 60s of inactivity. A daemon that accepts requests from MCP clients to start dev servers, allocating unique ports to avoid collisions and shutting down idle connections after 120s of inactivity.
## Example ## Example
@@ -16,7 +16,7 @@ A daemon that accepts requests from MCP clients to start dev servers, allocating
- **Avoid port collisions**: when working with websites, it's often necessary to specify different ports if you want to run multiple dev servers - **Avoid port collisions**: when working with websites, it's often necessary to specify different ports if you want to run multiple dev servers
- **Automatic port allocation** starting at 3010 with reuse - **Automatic port allocation** starting at 3010 with reuse
- **Log capture** with 512KB ring buffers per server - **Log capture** with 512KB ring buffers per server
- **Auto-cleanup** of idle sessions after 60 seconds - **Auto-cleanup** of idle sessions after 120 seconds (configurable via `--idle-timeout`)
## Installation & Usage ## Installation & Usage
@@ -129,7 +129,7 @@ Get stdout/stderr logs for a development server session.
- Single `Arc<Manager>` shared across all client connections - Single `Arc<Manager>` shared across all client connections
- Each connection gets a fresh `DevManagerService` instance - Each connection gets a fresh `DevManagerService` instance
- Mutex-protected HashMap for session storage - Mutex-protected HashMap for session storage
- Background sweeper runs every 5 seconds to clean up idle sessions (>60s) - Background sweeper runs every 5 seconds to clean up idle sessions (>120s by default)
### Session Keys ### Session Keys

View File

@@ -16,6 +16,7 @@ use serde_json::Value;
use service::DevManagerService; use service::DevManagerService;
use std::net::{Ipv4Addr, SocketAddr}; use std::net::{Ipv4Addr, SocketAddr};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration;
use tokio::sync::mpsc; use tokio::sync::mpsc;
fn inject_cwd_if_start_tool( fn inject_cwd_if_start_tool(
@@ -48,8 +49,8 @@ fn inject_cwd_if_start_tool(
msg msg
} }
pub async fn run_daemon(port: u16) -> Result<()> { pub async fn run_daemon(port: u16, idle_timeout_secs: u64) -> Result<()> {
let manager = Arc::new(Manager::new()); let manager = Arc::new(Manager::new(Duration::from_secs(idle_timeout_secs)));
let bind = SocketAddr::from((Ipv4Addr::LOCALHOST, port)); let bind = SocketAddr::from((Ipv4Addr::LOCALHOST, port));
println!("MCP daemon listening on {}", bind); println!("MCP daemon listening on {}", bind);

View File

@@ -14,6 +14,8 @@ enum Command {
Daemon { Daemon {
#[arg(long, env = "PORT", default_value_t = 3009)] #[arg(long, env = "PORT", default_value_t = 3009)]
port: u16, port: u16,
#[arg(long, env = "MCP_IDLE_TIMEOUT", default_value_t = 120)]
idle_timeout: u64,
}, },
#[command(about = "Run as STDIO proxy that connects to daemon")] #[command(about = "Run as STDIO proxy that connects to daemon")]
Stdio { Stdio {
@@ -30,8 +32,8 @@ enum Command {
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
let cli = Cli::parse(); let cli = Cli::parse();
match cli.command.unwrap_or(Command::Daemon { port: 3009 }) { match cli.command.unwrap_or(Command::Daemon { port: 3009, idle_timeout: 120 }) {
Command::Daemon { port } => dev_manager_mcp::run_daemon(port).await, Command::Daemon { port, idle_timeout } => dev_manager_mcp::run_daemon(port, idle_timeout).await,
Command::Stdio { daemon_url } => dev_manager_mcp::run_stdio_proxy(&daemon_url).await, Command::Stdio { daemon_url } => dev_manager_mcp::run_stdio_proxy(&daemon_url).await,
} }
} }

View File

@@ -10,7 +10,6 @@ use tokio::process::Command;
type SessionKey = String; type SessionKey = String;
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const RUNNING_IDLE_SECS: u64 = 60;
const EXITED_RETENTION_SECS: u64 = 600; const EXITED_RETENTION_SECS: u64 = 600;
fn generate_session_key() -> String { fn generate_session_key() -> String {
@@ -31,7 +30,7 @@ pub struct Manager {
} }
impl Manager { impl Manager {
pub fn new() -> Self { pub fn new(idle_timeout: Duration) -> Self {
let manager = Self { let manager = Self {
inner: Arc::new(Mutex::new(ManagerInner { inner: Arc::new(Mutex::new(ManagerInner {
servers: HashMap::new(), servers: HashMap::new(),
@@ -39,11 +38,11 @@ impl Manager {
})), })),
}; };
manager.start_sweeper(); manager.start_sweeper(idle_timeout);
manager manager
} }
fn start_sweeper(&self) { fn start_sweeper(&self, idle_timeout: Duration) {
let inner = self.inner.clone(); let inner = self.inner.clone();
tokio::spawn(async move { tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_secs(5)); let mut interval = tokio::time::interval(Duration::from_secs(5));
@@ -57,7 +56,7 @@ impl Manager {
}; };
let now = Instant::now(); let now = Instant::now();
let idle_threshold = Duration::from_secs(RUNNING_IDLE_SECS); let idle_threshold = idle_timeout;
let retention_threshold = Duration::from_secs(EXITED_RETENTION_SECS); let retention_threshold = Duration::from_secs(EXITED_RETENTION_SECS);
let mut to_stop = Vec::new(); let mut to_stop = Vec::new();