The short answer
List what's listening with lsof -iTCP -sTCP:LISTEN -n -P, find the server on the port you need (3000, 5173, 8000…), check its command with ps -o pid,ppid,etime,command -p and the PID, then stop it with kill and the PID. A parent PID of 1 means whatever started it has gone. Use kill -9 only if it's still running a few seconds later. Don't kill by name: pkill node stops every Node process, including your editor's.
On this page
Why agents leave processes running
Claude Code, Codex CLI, Gemini CLI, Cursor's agent, Aider, OpenCode and other agents run commands to check their work: npm run dev, vite, next dev, uvicorn --reload, a test watcher. Long-running ones go in the background so the agent can keep going. Most of the time they stop when the session does. They survive when:
- the session ended abruptly: the terminal was closed, the agent crashed or the Mac slept mid-session
- the command detached itself from the terminal, as
nohup,&withdisown, ordocker compose up -ddo - a server restarted itself in a child process that its parent didn't take with it
What you notice is “port 3000 is already in use” the next day, or a fan running for no reason. Each forgotten Node server holds a few hundred MB.
Step 1: find what's listening
lsof -iTCP -sTCP:LISTEN -n -PCOMMAND PID USER FD TYPE NODE NAME
node 48211 you 23u IPv6 TCP *:3000 (LISTEN)
node 48377 you 19u IPv4 TCP 127.0.0.1:5173 (LISTEN)
Python 48502 you 6u IPv4 TCP 127.0.0.1:8000 (LISTEN)lsof -nP -iTCP:3000 -sTCP:LISTEN-n and -P show addresses and port numbers instead of names, which is faster and easier to read. The PID column is what you need next.
Step 2: check it's a leftover
ps -o pid,ppid,etime,command -p 48211 PID PPID ELAPSED COMMAND
48211 1 21:14:07 node /Users/you/code/storefront/node_modules/.bin/next dev --turbopackThe command shows the project folder, so you know which repo it belongs to. A PPID of 1 means the process's parent has exited and launchd adopted it: whatever started it, an agent or a terminal, is gone. An ELAPSED time of hours on a server you didn't start yourself is the other giveaway.
ps -axo pid,ppid,etime,command | awk '$2 == 1' | grep -E 'node|vite|next|uvicorn|python|ruby|rails' | grep -v grepContainers are separate: docker ps lists the ones still running, and docker compose down in the project folder stops a project's set. Activity Monitor shows the same processes, but not their ports.
Step 3: stop it politely, then force if you must
kill 48211kill $(lsof -t -iTCP:3000 -sTCP:LISTEN)kill sends SIGTERM, which lets the server close its connections and remove its own temporary files. Give it a few seconds, check with lsof again, and only if it's still there use kill -9, which can't be ignored but skips that tidy-up.
Making it happen less
- Ask the agent to stop the servers it started before it finishes, or to use a fixed port so a leftover is easy to spot.
- End sessions from the agent rather than by closing the terminal window.
- Prefer commands that run in the foreground of the agent's own shell over
nohupor detached containers.
The faster way: Agent sessions in iKnowMyMac
iKnowMyMac's Agent sessions section, under Projects, tracks each agent session from the moment the agent starts: the project folder, how long it ran, every process it started and the ports they hold, and what's still running after it ended. Prompts and transcripts are never read.