Quickstart: Remote Server Mode
ccsinfo remote server mode is for the case where your Claude Code data lives on one machine, but you want to inspect it from somewhere else. You start the FastAPI server on the data host with ccsinfo serve, then point any ccsinfo CLI client at that base URL with --server-url or CCSINFO_SERVER_URL.
Note: This page assumes the
ccsinfocommand is already installed on the machine that will host the server and on any machine that will act as a client.
Where the server reads data from
The server does not read data from your current working directory. It reads Claude Code data from the server host's ~/.claude tree, specifically ~/.claude/projects and ~/.claude/tasks:
def get_claude_base_dir() -> Path:
"""Get the base Claude Code directory (~/.claude)."""
return Path.home() / ".claude"
def get_projects_dir() -> Path:
"""Get the projects directory (~/.claude/projects)."""
return get_claude_base_dir() / "projects"
def get_tasks_dir() -> Path:
"""Get the tasks directory (~/.claude/tasks)."""
return get_claude_base_dir() / "tasks"
Note: Start the server on the machine that actually has the Claude Code data you want to browse. Remote mode changes where the client reads from, not where the data is stored.
Start the server
The serve command is intentionally small: it starts Uvicorn with a host and port. The defaults are 127.0.0.1 and 8080.
@app.command()
def serve(
host: str = typer.Option("127.0.0.1", "--host", "-h", help="Host to bind to (use 0.0.0.0 for network access)"),
port: int = typer.Option(8080, "--port", "-p", help="Port to bind"),
) -> None:
"""Start the API server."""
uvicorn.run(fastapi_app, host=host, port=port)
If you only need access from the same machine:
ccsinfo serve
If you want other machines to connect to it:
ccsinfo serve --host 0.0.0.0 --port 8080
Warning: The default bind address is
127.0.0.1, so other machines cannot reach it. For remote access, use--host 0.0.0.0or another routable interface.Warning: The server code in this repo does not configure authentication, authorization, or TLS. Only expose it on a network you trust, or place it behind your own VPN, reverse proxy, or firewall rules.
What the server exposes
The FastAPI app mounts routers for sessions, projects, tasks, stats, search, and health:
app = FastAPI(
title="ccsinfo",
description="Claude Code Session Info API",
version=__version__,
)
# Include routers
app.include_router(sessions.router, prefix="/sessions", tags=["sessions"])
app.include_router(projects.router, prefix="/projects", tags=["projects"])
app.include_router(tasks.router, prefix="/tasks", tags=["tasks"])
app.include_router(stats.router, prefix="/stats", tags=["stats"])
app.include_router(search.router, prefix="/search", tags=["search"])
app.include_router(health.router, tags=["health"])
In practice, that gives you a small read-only API with endpoints like:
GET /healthandGET /infoGET /sessionsandGET /sessions/activeGET /projectsGET /tasksandGET /tasks/pendingGET /stats,GET /stats/daily, andGET /stats/trendsGET /search,GET /search/messages, andGET /search/history
Note: In the current codebase, the server routers only expose
GEThandlers. Remote server mode is for reading, listing, and searching Claude Code data, not modifying it.
Verify the server with health endpoints
The health router defines two endpoints:
@router.get("/health")
async def health() -> dict[str, str]:
"""Health check endpoint."""
return {"status": "healthy"}
@router.get("/info")
async def info() -> dict[str, Any]:
"""Server info endpoint."""
stats = stats_service.get_global_stats()
return {
"version": __version__,
"total_sessions": stats.total_sessions,
"total_projects": stats.total_projects,
}
Use them as your first smoke test after startup:
curl http://localhost:8080/health
curl http://localhost:8080/info
If you are calling the server from another machine, replace localhost with the server hostname or IP address.
What each endpoint tells you:
GET /healthconfirms that the FastAPI process is up and answering requests.GET /infoconfirms that the server can also calculate basic stats, and returns the serverversion,total_sessions, andtotal_projects.
Tip:
GET /infois usually the better quick check. If/healthworks but/infoshows0sessions and0projects, the server is up, but it is probably looking at an empty or unexpected~/.claudedirectory on that host.
Point CLI clients at the server
The CLI has a top-level --server-url option, and the same value can be supplied through CCSINFO_SERVER_URL:
@app.callback()
def main_callback(
_version: bool | None = typer.Option(
None,
"--version",
"-v",
help="Show version information.",
callback=version_callback,
is_eager=True,
),
server_url: str | None = typer.Option(
None,
"--server-url",
"-s",
envvar="CCSINFO_SERVER_URL",
help="Remote server URL (e.g., http://localhost:8080). If not set, reads local files.",
),
) -> None:
"""Claude Code Session Info CLI."""
state.server_url = server_url
Once that URL is set, commands switch from local file access to HTTP requests. For example, stats global checks state.server_url and uses the HTTP client in remote mode:
@app.command("global")
def global_stats(
json_output: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
) -> None:
"""Show global statistics."""
client = get_client(state.server_url)
if client:
# Remote mode - use HTTP client
stats_data = client.get_global_stats()
One-off client calls
Use --server-url before the subcommand:
ccsinfo --server-url http://localhost:8080 stats global
ccsinfo --server-url http://localhost:8080 sessions active
ccsinfo --server-url http://localhost:8080 tasks pending
ccsinfo --server-url http://localhost:8080 search messages "timeout" --json
Persistent shell configuration
If you will run several commands, exporting the URL is more convenient:
export CCSINFO_SERVER_URL=http://localhost:8080
ccsinfo stats global
ccsinfo sessions active
ccsinfo tasks pending
ccsinfo search messages "timeout" --json
Tip: When you want machine-readable output for scripts or pipelines, add
--json. The CLI commands support both Rich-formatted terminal output and JSON output.
Request flow
flowchart LR
A[Client shell] --> B["`ccsinfo` CLI with `--server-url` or `CCSINFO_SERVER_URL`"]
B --> C["HTTP requests to `ccsinfo serve`"]
C --> D["FastAPI routers: `/health`, `/info`, `/sessions`, `/projects`, `/tasks`, `/stats`, `/search`"]
D --> E["ccsinfo services"]
E --> F["`~/.claude/projects` on the server host"]
E --> G["`~/.claude/tasks` on the server host"]
Useful first commands
| Goal | CLI or HTTP call | Backing endpoint |
|---|---|---|
| Confirm the API process is alive | curl http://localhost:8080/health |
GET /health |
| Check server version and visible data counts | curl http://localhost:8080/info |
GET /info |
| Get high-level usage totals | ccsinfo --server-url http://localhost:8080 stats global |
GET /stats |
| Show currently active sessions | ccsinfo --server-url http://localhost:8080 sessions active |
GET /sessions/active |
| Show pending tasks | ccsinfo --server-url http://localhost:8080 tasks pending |
GET /tasks/pending |
| Search message content | ccsinfo --server-url http://localhost:8080 search messages "error" --json |
GET /search/messages?q=error |
If you keep those three ideas in mind, remote mode stays simple:
- Start
ccsinfo serveon the machine with the Claude Code data. - Verify it with
/healthand/info. - Point your CLI at that base URL with
--server-urlorCCSINFO_SERVER_URL.