portview

portview

See what's on your ports, then act on it. Diagnostic-first port viewer for Linux, MacOS and Windows.

Category
Visit Server

README

portview

CI Crates.io License: MIT

See what's on your ports, then act on it. So can your AI agent.

lsof -i is slow and cryptic. ss -tlnp is unreadable. netstat is deprecated. You just want to know what's on port 3000 and kill it.

portview

One command. Every listening port, the process behind it, memory usage, uptime, and the full command — in a colored table. Then inspect, kill, or watch it live.

<p align="center"> <img src="demo/demo.gif" alt="portview demo" width="100%" loop=infinite> </p>

It's also an MCP server — one binary, no Node, no npx — so Claude Code and Cursor can answer "what's on 3000?" without shelling out to lsof and misparsing the result.

~1 MB single binary. Zero runtime dependencies. Linux, macOS, and Windows.

Install

curl -fsSL https://raw.githubusercontent.com/mapika/portview/main/install.sh | sh   # Linux/macOS
irm https://raw.githubusercontent.com/mapika/portview/main/install.ps1 | iex         # Windows
brew install mapika/tap/portview                                                      # Homebrew
cargo install portview                                                                # Cargo

Or grab a binary from Releases.

What it does

portview                          # list all listening ports
portview 3000                     # inspect port 3000 in detail
portview node                     # find ports by process name
portview watch                    # interactive TUI with live refresh
portview watch --docker           # TUI with Docker containers as rows
portview kill 3000 --force        # kill what's on port 3000
portview doctor                   # diagnose port conflicts and issues
portview ssh user@server          # inspect ports on a remote host
portview ssh user@server watch    # remote TUI over SSH
portview ssh user@server --agentless   # no portview needed on the remote
portview mcp                      # run as an MCP server for AI agents

Features

Scan

$ portview
╭──────┬───────┬─────┬──────────────┬──────┬────────────┬────────┬────────┬────────────────────────────╮
│ PORT │ PROTO │ PID │ ADDR         │ USER │ PROCESS    │ UPTIME │ MEM    │ COMMAND                    │
├──────┼───────┼─────┼──────────────┼──────┼────────────┼────────┼────────┼────────────────────────────┤
│ 3000 │ TCP   │ 8   │ 127.0.0.1    │ root │ node       │     7s │  44 MB │ node /opt/app/web.js       │
│ 5000 │ TCP   │ 11  │ 127.0.0.1    │ root │ python3.12 │     7s │  18 MB │ python3 /opt/app/worker.py │
│ 6380 │ TCP   │ 12  │ 127.0.0.1    │ root │ node       │     7s │ 1.2 GB │ node /opt/app/cache.js     │
│ 7000 │ TCP   │ 10  │ 127.0.0.1    │ root │ node       │     7s │  45 MB │ node /opt/app/ingest.js    │
│ 8080 │ TCP   │ 9   │ 127.0.0.1    │ root │ node       │     7s │  44 MB │ node /opt/app/api.js       │
╰──────┴───────┴─────┴──────────────┴──────┴────────────┴────────┴────────┴────────────────────────────╯

--all includes non-listening connections — one row per connection, so a pile-up of TIME_WAIT or CLOSE_WAIT sockets is visible rather than collapsed. --wide shows full commands. --json for scripting.

Ports whose owner can't be resolved are still listed, with - in the columns that can't be filled. That happens for another user's process without sudo, and for sockets like TIME_WAIT that outlive the process that opened them.

The scan, doctor, and MCP examples below are real output, captured by demo/record.sh inside an isolated namespace — which is why the user is root and the paths are /opt/app. The Docker example is illustrative, since it needs a running daemon.

MCP server (for AI agents)

Give your coding agent eyes on your ports. portview mcp speaks the Model Context Protocol over stdio, so Claude Code, Cursor, and any other MCP client can query and act on ports directly instead of shelling out to lsof and guessing at the output.

claude mcp add portview -- portview mcp

Or configure it manually:

{
  "mcpServers": {
    "portview": {
      "command": "portview",
      "args": ["mcp"]
    }
  }
}
Tool What it does
list_ports Every listening port with process, user, uptime, memory, full command
inspect_port One port in detail, including each process's working directory
find_process Which ports a service is on, by name or command substring
doctor Conflicts, wildcard exposure, stale connections, resource hogs
kill_port Terminate what's on a port (marked destructive to the client)

<p align="center"> <img src="demo/mcp.gif" alt="portview MCP server demo" width="100%" loop=infinite> </p>

No Node, no npx, no runtime. It's the same ~1 MB binary — nothing extra to install. The MCP server added 29 KB, because it pulls in no new dependencies.

Pass --read-only to withhold kill_port entirely, so the agent can look but not touch:

portview mcp --read-only

Listed in the MCP Registry as mcp-name: io.github.Mapika/portview.

Watch mode (interactive TUI)

portview watch                    # live-refresh every 1s
portview watch --docker           # Docker containers as first-class rows
portview watch --sort mem         # sort by memory on launch
Key Action
j/k, ↑/↓ Navigate rows
Enter Inspect port (full command, cwd, children, connections)
d/D Kill process or manage Docker container
/ Filter across all columns
←/→, r Cycle sort column, reverse direction
t Toggle process tree view
a Toggle all/listening-only
q Quit

Tree view (t): Groups child processes under their parents with visual connectors. See which workers belong to which master process at a glance.

Detail view (Enter): Shows the full unwrapped command, working directory, child process list with ports, and open connections (in --all mode).

Doctor

Diagnose common port problems in one command:

$ portview doctor
  ✓ No port conflicts
  ✓ No wildcard exposure issues
  ! Port 7000 has 16 CLOSE_WAIT connections — possible connection leak
  ! node (PID 12) is listening on port 6380 and using 1.2 GB of memory

  2 warnings found
Check Flags
Port conflicts Multiple PIDs bound to the same port
Wildcard exposure Databases (postgres, redis, mysql, mongod, …) listening on 0.0.0.0
Docker-host conflicts A container publishing a port the host already uses
Stale connections TIME_WAIT or CLOSE_WAIT pileups on one port — a connection leak
Resource hogs Listeners holding more than 1 GB resident

Docker is auto-detected. portview doctor --json for scripting (exit code 1 on errors).

In CI

There's a GitHub Action, so a workflow can fail when a service ends up exposed or a test run leaks connections:

- uses: mapika/portview@v2
  with:
    fail-on: error        # error | warning | never

It annotates each finding inline on the run, writes a summary table, and exposes findings (JSON), count, errors, and warnings as step outputs:

- uses: mapika/portview@v2
  id: doctor
  with:
    fail-on: never
- run: echo '${{ steps.doctor.outputs.findings }}' | jq .

Set install: false if portview is already on PATH. Linux and macOS runners.

SSH remote mode

Inspect ports on any machine you can SSH to:

portview ssh user@server              # one-shot scan
portview ssh user@server watch        # full interactive TUI
portview ssh user@server doctor       # remote diagnostics
portview ssh user@server 3000         # inspect a remote port
portview ssh user@server --ssh-opt "-p 2222"  # custom SSH port

Kill actions in the remote TUI are forwarded over SSH.

Nothing to install on the remote host. If portview isn't there, it falls back automatically to collecting over the same SSH connection with ss and ps — present on essentially every Linux box:

$ portview ssh user@server
portview not found on user@server — falling back to agentless mode (ss + ps over SSH).
╭──────┬───────┬─────┬──────────────┬──────┬─────────┬────────┬───────┬──────────────────────╮
│ PORT │ PROTO │ PID │ ADDR         │ USER │ PROCESS │ UPTIME │ MEM   │ COMMAND              │
├──────┼───────┼─────┼──────────────┼──────┼─────────┼────────┼───────┼──────────────────────┤
│ 3000 │ TCP   │ 6   │ 127.0.0.1    │ root │ node    │     3s │ 45 MB │ node /opt/app/web.js │
│ 8080 │ TCP   │ 7   │ 127.0.0.1    │ root │ node    │     3s │ 45 MB │ node /opt/app/api.js │
╰──────┴───────┴─────┴──────────────┴──────┴─────────┴────────┴───────┴──────────────────────╯

Force it with --agentless to skip the remote portview entirely. You still get the process, user, memory, uptime, and full command — it resolves /proc/<pid>/exe on the remote host, so a Node server reads as node rather than the MainThread that ss reports.

doctor works agentless too — the checks are pure functions over collected data, so they run locally against whatever the probe brought back:

portview ssh user@server doctor --agentless

That produces the same findings as running portview doctor on the host itself. The Docker check is reported as skipped rather than passed, since the probe doesn't query Docker on the far end.

Agentless mode covers scans, port inspection, process search, and diagnostics. watch still needs portview installed remotely — the TUI consumes a streaming JSON pipe.

Docker integration

Add --docker to any command. Docker-published ports appear as first-class rows:

$ portview --docker
╭──────┬───────┬───────┬──────────────┬────────┬──────────┬────────┬────────┬────────────────────────────╮
│ PORT │ PROTO │ PID   │ ADDR         │ USER   │ PROCESS  │ UPTIME │ MEM    │ COMMAND                    │
├──────┼───────┼───────┼──────────────┼────────┼──────────┼────────┼────────┼────────────────────────────┤
│ 3000 │ TCP   │ 48291 │ 127.0.0.1    │ mark   │ node     │ 3h 12m │ 248 MB │ next dev [docker:web]      │
│ 8080 │ TCP   │ -     │ 0.0.0.0      │ docker │ pv-nginx │      - │      - │ nginx:alpine :8080->80/tcp │
╰──────┴───────┴───────┴──────────────┴────────┴──────────┴────────┴────────┴────────────────────────────╯

Container-only rows have no host process, so PID, UPTIME, and MEM render as -.

Press d on a Docker row to Stop, Restart, or tail Logs.

JSON output

portview --json                   # pipe to jq, scripts, dashboards
portview --docker --json          # includes Docker ownership data
portview watch --json             # streaming JSON, one array per tick
portview doctor --json            # machine-readable diagnostics

Custom colors

PORTVIEW_COLORS="port=red,pid=magenta,command=bright_cyan" portview

Columns: port, proto, pid, user, process, uptime, mem, command. Use --no-color to disable.

How it works

All data is read directly from the OS — no shelling out to lsof, ss, or netstat.

Field Linux macOS Windows
Ports /proc/net/tcp{,6}, udp{,6} proc_pidfdinfo GetExtendedTcp/UdpTable
PID inode→pid via /proc/*/fd/ proc_listpids Included in socket table
Process /proc/<pid>/exe proc_pidpath QueryFullProcessImageNameW
Memory /proc/<pid>/status VmRSS proc_pidinfo K32GetProcessMemoryInfo
Uptime /proc/<pid>/stat proc_pidinfo GetProcessTimes

The process name comes from the executable, not /proc/<pid>/comm. comm is the thread name, and runtimes overwrite it — Node.js renames its main thread to MainThread, which is why ps, ss, and lsof all report a Node dev server as MainThread. It is also truncated to 15 bytes.

Docker integration queries docker ps when --docker is passed. SSH mode runs portview --json on the remote host via the system ssh binary. MCP mode speaks newline-delimited JSON-RPC 2.0 on stdin/stdout, with a hand-rolled JSON reader — no serde, no extra dependency.

Why not...

Tool What's missing
lsof -i :3000 Different flags per OS, cryptic output, slow
ss -tlnp Unreadable, no uptime/memory/docker, no TUI
netstat Deprecated on modern Linux, limited info
fkill-cli Requires Node.js, kill-first not diagnostic-first
procs General process viewer, not port-centric

None of them speak MCP, so none of them can be handed to an agent.

There's also a smaller thing they all get wrong. Start a Node dev server and ask what's on the port:

$ ss -tlnp | grep 3000
LISTEN 0      511        127.0.0.1:3000      0.0.0.0:*    users:(("MainThread",pid=6,fd=21))

$ portview 3000
Port 3000 (TCP) — node (PID 6)

ps, ss, and lsof all read /proc/<pid>/comm, the thread name — and Node renames its main thread to MainThread. portview reads the executable instead.

portview is diagnostic-first: understand what's on your ports, then act.

Building from source

git clone https://github.com/mapika/portview
cd portview
cargo build --release

Requires Rust 1.85+ (edition 2024). Shell completions and man page are generated at build time.

There's also a Dockerfile. Note that a container has its own network and PID namespaces, so portview inside one sees the container's ports — share the host's namespaces to inspect the host:

docker run --rm -i --network host --pid host portview

Contributing

See CONTRIBUTING.md for development setup and guidelines, and demo/README.md for regenerating the recordings.

Changes should type-check on all three platforms, not just yours:

cargo check --target aarch64-apple-darwin
cargo check --target x86_64-pc-windows-msvc

Limitations

  • Linux: Other users' ports are listed, but naming the process needs sudo (it reads /proc/<pid>/fd/). Rows you can't attribute show - for PID, user, process, and command rather than being hidden.
  • macOS: Other users' ports are not listed without sudo — sockets are enumerated per process via proc_pidfdinfo, so a process that can't be opened contributes nothing to enumerate. For the same reason doctor cannot detect TIME_WAIT pileups there; CLOSE_WAIT is detected normally.
  • Windows: Ports owned by inaccessible system processes are listed with the PID but - for name and user. Kill always force-terminates. Run as Administrator for full detail.
  • Docker: Requires docker CLI and daemon access
  • SSH: watch requires portview on the remote host. Scans, inspection, search and doctor fall back to agentless collection (ss + ps), which needs a Linux remote — macOS and BSD hosts still need portview installed.

License

MIT

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured