From 9924f935db96828563fd31eab35ba74c57a99a98 Mon Sep 17 00:00:00 2001 From: Andre Rabold Date: Thu, 5 Feb 2026 05:41:06 -0800 Subject: [PATCH] docs(openspec): add daemon management proposal --- .../changes/add-daemon-management/design.md | 157 ++++++++++++++ .../changes/add-daemon-management/proposal.md | 52 +++++ .../specs/daemon-management/spec.md | 205 ++++++++++++++++++ .../changes/add-daemon-management/tasks.md | 86 ++++++++ 4 files changed, 500 insertions(+) create mode 100644 openspec/changes/add-daemon-management/design.md create mode 100644 openspec/changes/add-daemon-management/proposal.md create mode 100644 openspec/changes/add-daemon-management/specs/daemon-management/spec.md create mode 100644 openspec/changes/add-daemon-management/tasks.md diff --git a/openspec/changes/add-daemon-management/design.md b/openspec/changes/add-daemon-management/design.md new file mode 100644 index 00000000..48157f86 --- /dev/null +++ b/openspec/changes/add-daemon-management/design.md @@ -0,0 +1,157 @@ +# Design: Daemon Management + +## Context + +The docs-mcp-server needs to run as a persistent background process to provide continuous MCP service to AI clients. Currently, users must manually start the server in a terminal, which is inconvenient for daily use and doesn't survive system reboots. + +**Stakeholders**: End users running docs-mcp-server locally or on servers. + +**Constraints**: +- Must support macOS, Windows, and Linux +- Must not require native module compilation (pure JavaScript) +- Must work with the existing CLI framework (Yargs) +- Service management requires elevated privileges + +## Goals / Non-Goals + +### Goals +- Provide simple CLI commands to install/uninstall the server as a system daemon +- Support all three major platforms with native service mechanisms +- Allow configuration of server options (port, host) at install time +- Provide status, start, stop, restart commands for lifecycle management +- Handle permission requirements gracefully with clear error messages + +### Non-Goals +- Container/Docker orchestration (separate concern) +- Cluster/multi-instance management +- GUI installer +- Auto-update mechanism + +## Decisions + +### Decision 1: Use node-windows/node-mac/node-linux Package Family + +**What**: Use the established `node-windows`, `node-mac`, and `node-linux` packages by Corey Butler for cross-platform daemon management. + +**Why**: +- Mature, battle-tested packages (2.9k+ stars for node-windows) +- Pure JavaScript (no native compilation required) +- Consistent API across all three packages +- MIT licensed +- Handles platform-specific details (launchd, Windows Services, systemd/init.d) +- Built-in service monitoring with configurable restart behavior + +**Alternatives considered**: +1. **Manual platform scripts**: Would require maintaining separate bash/powershell/plist scripts. High maintenance burden. +2. **PM2**: Designed for Node.js process management but not native OS services. Adds complexity and another background process. +3. **systemd-only on Linux**: Would limit Linux support to systemd-based distros. + +### Decision 2: Subcommand Pattern (`daemon `) + +**What**: Group all daemon operations under a `daemon` parent command. + +**Why**: +- Aligns with existing CLI patterns (though current commands are flat, `daemon` is a logical grouping) +- Avoids namespace conflicts (`install`, `start`, `stop`, `status` are generic terms) +- Enables discoverability (`docs-mcp-server daemon --help`) +- Follows precedent from Docker, systemctl, npm, etc. + +**Command structure**: +``` +daemon install [--port] [--host] [--resume] [--read-only] [--store-path] +daemon uninstall +daemon status +daemon start +daemon stop +daemon restart +``` + +### Decision 3: Configuration Persistence via Environment Variables + +**What**: Pass server configuration to the daemon via environment variables set in the service definition. + +**Why**: +- The node-* packages natively support environment variable configuration +- Avoids needing a separate config file for daemon-specific settings +- Consistent with 12-factor app principles +- Configuration travels with the service definition + +**Implementation**: +```javascript +const svc = new Service({ + name: 'docs-mcp-server', + script: '/path/to/dist/index.js', + env: [ + { name: 'DOCS_MCP_PORT', value: '3000' }, + { name: 'DOCS_MCP_HOST', value: '0.0.0.0' }, + { name: 'DOCS_MCP_STORE_PATH', value: '/path/to/store' }, + ] +}); +``` + +### Decision 4: Platform-Agnostic DaemonService Abstraction + +**What**: Create a `DaemonService` class that provides a unified API across all platforms. + +**Why**: +- Encapsulates platform detection and package selection +- Single interface for CLI commands to use +- Easier testing via dependency injection +- Future-proofs against package API changes + +**Structure**: +``` +src/daemon/ +├── DaemonService.ts # Main abstraction class +├── types.ts # DaemonOptions, DaemonStatus interfaces +└── DaemonService.test.ts # Unit tests +``` + +### Decision 5: Service Name + +**What**: Use `docs-mcp-server` as the fixed service name. + +**Why**: +- Matches the npm package name for consistency +- Unique enough to avoid conflicts +- Clear identification in system tools (Activity Monitor, Services, systemctl) + +## Risks / Trade-offs + +### Risk 1: Elevated Privileges Required +- **Risk**: Install/uninstall operations require sudo/admin, which may fail or confuse users. +- **Mitigation**: Clear error messages explaining the permission requirement. Documentation with example commands. + +### Risk 2: Windows WSL Confusion +- **Risk**: Users in WSL might try to install a Windows service, which won't work. +- **Mitigation**: Detect WSL environment and provide appropriate guidance to use Linux service or native Windows outside WSL. + +### Risk 3: Package Maintenance +- **Risk**: The node-* packages may become unmaintained. +- **Mitigation**: The packages are stable (minimal updates needed for mature OS APIs). Fork if necessary. The abstraction layer isolates the CLI from direct package dependencies. + +### Risk 4: Service Recovery Loops +- **Risk**: If the server has a fatal bug, the service might restart endlessly. +- **Mitigation**: Use the built-in `maxRestarts` and `maxRetries` configuration to cap restart attempts. Default to sensible limits (e.g., 5 restarts in 60 seconds). + +## Migration Plan + +This is a new feature with no migration required. Users can adopt it optionally. + +**Rollout**: +1. Implement and test on all three platforms +2. Document in README with platform-specific notes +3. Add to CLI help text + +**Rollback**: Simply don't use the daemon commands; manual startup continues to work. + +## Open Questions + +1. **Log file location**: Should we configure a specific log location for daemon output, or use the platform defaults? + - **Proposed answer**: Use platform defaults initially (`/Library/Logs/` on macOS, Event Log on Windows, `/var/log/` on Linux). Document locations. + +2. **Multiple instances**: Should we support running multiple daemon instances with different names/ports? + - **Proposed answer**: Out of scope for initial implementation. Single instance with fixed name. Can be extended later if needed. + +3. **Auto-start on install**: Should `daemon install` automatically start the service? + - **Proposed answer**: Yes, this matches user expectation and the node-* package default behavior. diff --git a/openspec/changes/add-daemon-management/proposal.md b/openspec/changes/add-daemon-management/proposal.md new file mode 100644 index 00000000..31ddc127 --- /dev/null +++ b/openspec/changes/add-daemon-management/proposal.md @@ -0,0 +1,52 @@ +# Change: Add Daemon Management CLI Commands + +## Why + +Currently, users must manually start the docs-mcp-server by running `./dist/index.js` or `docs-mcp-server` in a terminal window. This requires keeping the terminal open and remembering to restart the server after reboots. A daemon/service installation feature would allow the server to run as a persistent background process that starts automatically on system boot, providing a seamless always-on experience. + +## What Changes + +- **New CLI command group**: `daemon` with subcommands (`install`, `uninstall`, `status`, `start`, `stop`, `restart`) +- **New dependencies**: `node-mac`, `node-windows`, `node-linux` packages for cross-platform service management +- **New module**: `src/daemon/` containing platform-agnostic daemon service abstraction +- **Configuration storage**: Persist daemon configuration (port, host, etc.) for service startup + +### Command Structure + +``` +docs-mcp-server daemon install [options] # Install as system service +docs-mcp-server daemon uninstall # Remove system service +docs-mcp-server daemon status # Show daemon running status +docs-mcp-server daemon start # Start the daemon +docs-mcp-server daemon stop # Stop the daemon +docs-mcp-server daemon restart # Restart the daemon +``` + +### Platform Support + +| Platform | Mechanism | Service Location | +|----------|-----------|------------------| +| macOS | launchd | `/Library/LaunchDaemons/` (plist) | +| Windows | Windows Service Manager | Windows Services (winsw) | +| Linux | systemd/init.d | `/etc/init.d/` or systemd unit | + +### Options for `daemon install` + +The install command mirrors key server options to configure the daemon: + +- `--port` - Server port (default: from config) +- `--host` - Server host (default: from config) +- `--resume` - Resume interrupted jobs on startup +- `--read-only` - Run in read-only mode +- `--store-path` - Custom data storage directory + +## Impact + +- **Affected specs**: None (new capability) +- **Affected code**: + - `src/cli/commands/` - New daemon.ts command + - `src/cli/index.ts` - Register daemon command + - `src/daemon/` - New module for service management + - `package.json` - New dependencies +- **User experience**: Significantly improved for production/daily use +- **Permissions**: Requires elevated privileges (sudo/admin) for install/uninstall diff --git a/openspec/changes/add-daemon-management/specs/daemon-management/spec.md b/openspec/changes/add-daemon-management/specs/daemon-management/spec.md new file mode 100644 index 00000000..d4bf5f8a --- /dev/null +++ b/openspec/changes/add-daemon-management/specs/daemon-management/spec.md @@ -0,0 +1,205 @@ +# Daemon Management Specification + +## ADDED Requirements + +### Requirement: Daemon Installation + +The system SHALL provide a CLI command to install the docs-mcp-server as a native system daemon/service. + +The installation: +- MUST register the server with the operating system's service manager (launchd on macOS, Windows Service Manager on Windows, systemd/init.d on Linux) +- MUST accept optional configuration options (port, host, store-path, resume, read-only) +- MUST persist configuration as environment variables in the service definition +- MUST start the service automatically after successful installation +- MUST require elevated privileges (sudo/admin) and provide a clear error message if not available + +#### Scenario: Successful daemon installation on macOS +- **GIVEN** the user has administrative privileges +- **WHEN** the user runs `docs-mcp-server daemon install --port 3000` +- **THEN** a launchd plist is created in `/Library/LaunchDaemons/` +- **AND** the service is started automatically +- **AND** a success message is displayed with service status + +#### Scenario: Successful daemon installation on Windows +- **GIVEN** the user has administrative privileges +- **WHEN** the user runs `docs-mcp-server daemon install --port 3000` +- **THEN** a Windows Service is registered +- **AND** the service is started automatically +- **AND** a success message is displayed with service status + +#### Scenario: Successful daemon installation on Linux +- **GIVEN** the user has root privileges +- **WHEN** the user runs `docs-mcp-server daemon install --port 3000` +- **THEN** a systemd unit or init.d script is created +- **AND** the service is started automatically +- **AND** a success message is displayed with service status + +#### Scenario: Installation without elevated privileges +- **GIVEN** the user does not have elevated privileges +- **WHEN** the user runs `docs-mcp-server daemon install` +- **THEN** an error message is displayed explaining that sudo/admin privileges are required +- **AND** the command exits with a non-zero status code + +#### Scenario: Installation when already installed +- **GIVEN** the daemon is already installed +- **WHEN** the user runs `docs-mcp-server daemon install` +- **THEN** an informative message is displayed indicating the service is already installed +- **AND** the user is prompted to uninstall first or use restart + +--- + +### Requirement: Daemon Uninstallation + +The system SHALL provide a CLI command to uninstall the docs-mcp-server daemon/service. + +The uninstallation: +- MUST stop the service if running +- MUST remove the service registration from the operating system +- MUST remove service-related files (plist, wrapper executables, etc.) +- MUST NOT delete user data (database, configuration files, logs) +- MUST require elevated privileges + +#### Scenario: Successful daemon uninstallation +- **GIVEN** the daemon is installed and running +- **WHEN** the user runs `docs-mcp-server daemon uninstall` with elevated privileges +- **THEN** the service is stopped +- **AND** the service registration is removed +- **AND** a success message is displayed + +#### Scenario: Uninstallation when not installed +- **GIVEN** the daemon is not installed +- **WHEN** the user runs `docs-mcp-server daemon uninstall` +- **THEN** an informative message is displayed indicating no service is installed + +--- + +### Requirement: Daemon Status + +The system SHALL provide a CLI command to check the status of the installed daemon. + +#### Scenario: Status of running daemon +- **GIVEN** the daemon is installed and running +- **WHEN** the user runs `docs-mcp-server daemon status` +- **THEN** the output shows: + - Service name + - Status: "running" + - Process ID (PID) + - Configured port and host + +#### Scenario: Status of stopped daemon +- **GIVEN** the daemon is installed but not running +- **WHEN** the user runs `docs-mcp-server daemon status` +- **THEN** the output shows: + - Service name + - Status: "stopped" + +#### Scenario: Status when not installed +- **GIVEN** the daemon is not installed +- **WHEN** the user runs `docs-mcp-server daemon status` +- **THEN** the output indicates the service is not installed + +--- + +### Requirement: Daemon Start + +The system SHALL provide a CLI command to start an installed daemon. + +#### Scenario: Start stopped daemon +- **GIVEN** the daemon is installed but not running +- **WHEN** the user runs `docs-mcp-server daemon start` with elevated privileges +- **THEN** the service is started +- **AND** a success message is displayed + +#### Scenario: Start already running daemon +- **GIVEN** the daemon is installed and running +- **WHEN** the user runs `docs-mcp-server daemon start` +- **THEN** an informative message is displayed indicating the service is already running + +#### Scenario: Start when not installed +- **GIVEN** the daemon is not installed +- **WHEN** the user runs `docs-mcp-server daemon start` +- **THEN** an error message is displayed indicating the service must be installed first + +--- + +### Requirement: Daemon Stop + +The system SHALL provide a CLI command to stop a running daemon. + +#### Scenario: Stop running daemon +- **GIVEN** the daemon is installed and running +- **WHEN** the user runs `docs-mcp-server daemon stop` with elevated privileges +- **THEN** the service is stopped +- **AND** a success message is displayed + +#### Scenario: Stop already stopped daemon +- **GIVEN** the daemon is installed but not running +- **WHEN** the user runs `docs-mcp-server daemon stop` +- **THEN** an informative message is displayed indicating the service is already stopped + +--- + +### Requirement: Daemon Restart + +The system SHALL provide a CLI command to restart a running daemon. + +#### Scenario: Restart running daemon +- **GIVEN** the daemon is installed and running +- **WHEN** the user runs `docs-mcp-server daemon restart` with elevated privileges +- **THEN** the service is stopped and started +- **AND** a success message is displayed + +#### Scenario: Restart stopped daemon +- **GIVEN** the daemon is installed but not running +- **WHEN** the user runs `docs-mcp-server daemon restart` with elevated privileges +- **THEN** the service is started +- **AND** a success message is displayed + +--- + +### Requirement: Cross-Platform Support + +The daemon management system SHALL support macOS, Windows, and Linux operating systems. + +- On macOS, the system MUST use launchd for service management +- On Windows, the system MUST use Windows Service Manager (via winsw) +- On Linux, the system MUST use systemd or init.d scripts + +#### Scenario: Platform detection on macOS +- **GIVEN** the system is running on macOS +- **WHEN** any daemon command is executed +- **THEN** the system uses launchd-based service management + +#### Scenario: Platform detection on Windows +- **GIVEN** the system is running on Windows +- **WHEN** any daemon command is executed +- **THEN** the system uses Windows Service Manager + +#### Scenario: Platform detection on Linux +- **GIVEN** the system is running on Linux +- **WHEN** any daemon command is executed +- **THEN** the system uses systemd or init.d based service management + +--- + +### Requirement: Automatic Restart on Crash + +The daemon service SHALL automatically restart if the server process crashes unexpectedly. + +The restart behavior: +- MUST use exponential backoff (default: 1 second initial, 25% growth) +- MUST cap restart attempts to prevent infinite loops (default: 5 restarts per 60 seconds) +- MUST log restart attempts + +#### Scenario: Automatic restart after crash +- **GIVEN** the daemon is running +- **WHEN** the server process crashes unexpectedly +- **THEN** the service manager restarts the process after a brief delay +- **AND** the restart is logged + +#### Scenario: Restart cap prevents infinite loop +- **GIVEN** the daemon is running +- **AND** the server crashes repeatedly due to a fatal error +- **WHEN** the restart limit is reached (5 restarts in 60 seconds) +- **THEN** the service stops attempting restarts +- **AND** the failure is logged diff --git a/openspec/changes/add-daemon-management/tasks.md b/openspec/changes/add-daemon-management/tasks.md new file mode 100644 index 00000000..3eab1655 --- /dev/null +++ b/openspec/changes/add-daemon-management/tasks.md @@ -0,0 +1,86 @@ +# Tasks: Add Daemon Management + +## 1. Dependencies & Setup + +- [ ] 1.1 Add `node-mac`, `node-windows`, `node-linux` as dependencies in `package.json` +- [ ] 1.2 Add TypeScript type declarations (or create local types if not available) +- [ ] 1.3 Verify packages install correctly on all platforms (may need to test in CI) + +## 2. Core Daemon Service Module + +- [ ] 2.1 Create `src/daemon/types.ts` with interfaces: + - `DaemonOptions` (name, scriptPath, env vars, port, host, etc.) + - `DaemonStatus` (installed, running, pid, etc.) + - `DaemonConfig` (restart behavior, logging) +- [ ] 2.2 Create `src/daemon/DaemonService.ts`: + - Platform detection logic (`process.platform`) + - Dynamic import of platform-specific package + - `install(options)` method + - `uninstall()` method + - `start()` method + - `stop()` method + - `restart()` method + - `status()` method returning `DaemonStatus` +- [ ] 2.3 Create `src/daemon/index.ts` barrel export +- [ ] 2.4 Write unit tests `src/daemon/DaemonService.test.ts`: + - Mock platform packages + - Test install flow + - Test uninstall flow + - Test status detection + +## 3. CLI Command Implementation + +- [ ] 3.1 Create `src/cli/commands/daemon.ts`: + - Parent command `daemon` with subcommands + - `daemon install` with options (--port, --host, --resume, --read-only, --store-path) + - `daemon uninstall` with confirmation prompt + - `daemon status` showing current state + - `daemon start` to start installed service + - `daemon stop` to stop running service + - `daemon restart` to restart service +- [ ] 3.2 Add telemetry tracking for daemon commands +- [ ] 3.3 Register `createDaemonCommand` in `src/cli/index.ts` +- [ ] 3.4 Add user-friendly error messages for: + - Missing elevated privileges + - Service not installed (for start/stop/status) + - Service already installed + - WSL detection with guidance + +## 4. Configuration & Environment + +- [ ] 4.1 Implement environment variable mapping for daemon: + - `DOCS_MCP_PORT` from --port + - `DOCS_MCP_HOST` from --host + - `DOCS_MCP_STORE_PATH` from --store-path + - `DOCS_MCP_RESUME` from --resume + - `DOCS_MCP_READ_ONLY` from --read-only +- [ ] 4.2 Ensure `loadConfig` in server startup respects these env vars +- [ ] 4.3 Determine and document script path for installed daemon: + - Global npm install: use `which docs-mcp-server` or npm prefix + - Local/dev: use resolved path to `dist/index.js` + +## 5. Testing + +- [ ] 5.1 Add integration tests for daemon command parsing +- [ ] 5.2 Manual E2E testing on macOS (launchd) +- [ ] 5.3 Manual E2E testing on Windows (Windows Service) +- [ ] 5.4 Manual E2E testing on Linux (systemd/init.d) +- [ ] 5.5 Test permission error handling (run without sudo) + +## 6. Documentation + +- [ ] 6.1 Update `README.md` with daemon installation section: + - Platform-specific prerequisites + - Installation commands + - Uninstallation commands + - Log file locations + - Troubleshooting common issues +- [ ] 6.2 Add `--help` text for all daemon subcommands +- [ ] 6.3 Document permission requirements clearly + +## 7. Finalization + +- [ ] 7.1 Run linter and fix any issues +- [ ] 7.2 Run typecheck and fix any issues +- [ ] 7.3 Run full test suite +- [ ] 7.4 Update CHANGELOG if applicable