sq-cli-gpt-mcp
README.md
# sq-cli-gpt-mcp
Private, standalone Streamable HTTP MCP server that lets one approved GitHub
identity operate a project-local Codex Security CLI against explicitly
registered repositories on this Fedora workstation.
- Public MCP endpoint: `https://sqcligpt.fredlingautomation.dev/mcp`
- Local origin: `http://127.0.0.1:8630/mcp`
- Health: `http://127.0.0.1:8630/health`
- MCP service: `sq-cli-gpt-mcp.service`
- Tunnel service: `cloudflared-sq-cli-gpt-mcp.service`
This release adds a private SQ Security Cockpit widget. It has no source mutation,
remediation, Git write, generic shell, arbitrary argv, or unrestricted file
tool.
## Design
The server exposes 14 narrow MCP tools and one versioned MCP Apps resource. A caller registers an exact Git
worktree under `/home/aidev/Projects`, receives an opaque repository ID, and
uses that ID for all later operations. Starting a scan persists the request and
captured Git revision in SQLite before returning a durable job ID. One local
worker invokes the pinned Codex Security executable with an internally built
argument array. Findings and artifacts remain in private service state and are
returned only as paged metadata or bounded, redacted excerpts.
The resource is `ui://sq-security/cockpit-v1.html`, a self-contained React
bundle using the MCP Apps bridge. A development-only `?demo=1` fixture is
tree-shaken from production and checked out by validation and installation.
Registration/removal remain chat-only; the widget can inspect summaries, start
scans, and request cancellation with inline confirmation.
Authentication is a local OAuth authorization-server proxy backed by a GitHub
OAuth App. It supports protected-resource metadata, OAuth discovery, DCR,
CIMD, authorization code with PKCE S256, refresh tokens, encrypted persistent
OAuth state, and an exact one-login allowlist. Cloudflare provides HTTPS
transport only.
See [architecture](docs/ARCHITECTURE.md) and the
[threat model](docs/THREAT-MODEL.md).
## Requirements
- Fedora user session with systemd user services and linger
- Python 3.12 or newer and `uv`
- Node.js 22 or newer and `npm`
- Git, curl, OpenSSL, SQLite
- the existing file-backed Codex sign-in at `~/.codex/auth.json`, or a
separately completed login in the service's isolated CLI home
- `cloudflared` and `~/.cloudflared/cert.pem` for public routing
- a GitHub OAuth App created with the exact values in
[GitHub OAuth setup](docs/GITHUB-OAUTH.md)
Python and Node dependencies are locked inside this repository. No other MCP
project is imported or used at runtime.
## Install
```bash
cd /home/aidev/Projects/004_MCPS/sq-cli-gpt-mcp
./scripts/validate-local.sh
./scripts/install.sh
```
The installer creates:
- `~/.config/sq-cli-gpt-mcp/sq-cli-gpt-mcp.env` with mode `0600`;
- `~/.local/state/sq-cli-gpt-mcp/` with mode `0700`;
- an isolated copy of `~/.codex/auth.json` at
`~/.local/state/sq-cli-gpt-mcp/cli-home/auth.json`;
- `~/.config/systemd/user/sq-cli-gpt-mcp.service`.
It leaves the unit disabled until the GitHub client ID, client secret, and
approved login have been placed in the protected environment file. The OAuth
JWT/encryption key is generated locally. Rerunning the installer after those
values exist enables and starts the unit.
After OAuth values are present:
```bash
systemctl --user restart sq-cli-gpt-mcp.service
curl --fail --silent http://127.0.0.1:8630/health
```
Install the dedicated named tunnel after the local origin is ready:
```bash
./scripts/install-cloudflare.sh
systemctl --user restart cloudflared-sq-cli-gpt-mcp.service
curl --fail --silent https://sqcligpt.fredlingautomation.dev/health
```
The tunnel installer creates or reuses only the named tunnel
`sq-cli-gpt-mcp`, safely checks an existing DNS record, and never edits the
workstation's shared Cloudflare configuration.
## Operate
```bash
systemctl --user start sq-cli-gpt-mcp.service
systemctl --user stop sq-cli-gpt-mcp.service
systemctl --user restart sq-cli-gpt-mcp.service
systemctl --user status sq-cli-gpt-mcp.service
journalctl --user -u sq-cli-gpt-mcp.service -f
systemctl --user start cloudflared-sq-cli-gpt-mcp.service
systemctl --user stop cloudflared-sq-cli-gpt-mcp.service
systemctl --user restart cloudflared-sq-cli-gpt-mcp.service
systemctl --user status cloudflared-sq-cli-gpt-mcp.service
journalctl --user -u cloudflared-sq-cli-gpt-mcp.service -f
```
Local health returns only `{"status":"ok"}`. MCP requests require a bearer
token; unauthenticated `/mcp` requests return `401`.
Back up state while the service is stopped:
```bash
systemctl --user stop sq-cli-gpt-mcp.service
./scripts/backup-state.sh /absolute/private/backup-directory
systemctl --user start sq-cli-gpt-mcp.service
```
For recovery, retention, database integrity, audit rotation, and credential
rotation, use [operations](docs/OPERATIONS.md).
## Tools
| Tool | Purpose |
| --- | --- |
| `sq_security_get_readiness` | Verify database, CLI, CLI authentication, and service readiness |
| `sq_security_list_repositories` | Page through safe names and opaque repository IDs |
| `sq_security_register_repository` | Register one exact allowed Git worktree |
| `sq_security_remove_repository` | Soft-remove an inactive registration |
| `sq_security_start_scan` | Queue repository, path, diff, or working-tree scan |
| `sq_security_get_scan` | Poll a durable job and inspect coverage |
| `sq_security_cancel_scan` | Cancel a queued or running process group |
| `sq_security_list_scans` | Page through durable scan history |
| `sq_security_list_findings` | Page through normalized completed-scan findings |
| `sq_security_get_finding` | Retrieve bounded evidence for one local finding ID |
| `sq_security_list_artifacts` | List private artifact metadata and hashes |
| `sq_security_get_artifact_excerpt` | Read a hash-checked bounded redacted excerpt |
| `sq_security_compare_scans` | Ask the verified CLI to compare compatible completed scans |
Only `sq_security_open_cockpit` renders UI and carries ChatGPT compatibility
aliases. Public submission remains deferred until reviewer authentication,
demo data, publisher verification, support/privacy pages, and a successful
sealed real scan are verified.
The compatibility path is explicit start/status/cancel tools. MCP protocol
Tasks are disabled because end-to-end ChatGPT support was not established for
this deployment.
## Scan boundaries
- Initial allowed root: `/home/aidev/Projects`
- Default concurrency: one real scan
- Default scan timeout: four hours
- Standard/deep modes map only to verified CLI flags
- Diff refs are validated by Git and cannot begin with `-`
- Target paths are existing, repository-relative, symlink-free paths
- Artifacts are stored under `~/.local/state/sq-cli-gpt-mcp/artifacts`
- Large outputs are bounded; artifact access accepts IDs, never paths, and
opens the stored file descriptor-relative without following symlinks
- Registered repositories are read-only scan inputs
The adapter captures branch, commit, detached/dirty state, and sanitized remote
metadata at queue time. A changed filesystem topology is revalidated before
each scan.
## Development and validation
```bash
uv sync --frozen --all-groups
npm ci --ignore-scripts
uv run ruff format --check .
uv run ruff check .
uv run mypy
uv run pytest
npm audit --audit-level=high
```
The controlled fake CLI covers successful, partial, malformed, nonzero,
cost-limit, timeout, cancellation, process-group, partial-artifact, and restart
paths. It is never used as production evidence. Executed evidence and remaining
external checks are tracked in [validation](docs/VALIDATION.md).
## Connect ChatGPT
After the production service, tunnel, and OAuth values are ready, follow
[ChatGPT setup](docs/CHATGPT.md). The URL entered in ChatGPT is exactly:
```text
https://sqcligpt.fredlingautomation.dev/mcp
```
## Troubleshooting
Readiness and CLI authentication:
```bash
CODEX_HOME="$HOME/.local/state/sq-cli-gpt-mcp/cli-home" \
HOME="$HOME/.local/state/sq-cli-gpt-mcp/cli-home" \
./node_modules/.bin/codex-security login status
```
Port ownership:
```bash
ss -ltnp 'sport = :8630'
```
Unit configuration:
```bash
systemd-analyze --user verify \
"$HOME/.config/systemd/user/sq-cli-gpt-mcp.service"
systemctl --user cat sq-cli-gpt-mcp.service
```
Tunnel configuration:
```bash
"$HOME/.local/bin/cloudflared" tunnel \
--config "$HOME/.config/sq-cli-gpt-mcp/cloudflared.yml" ingress validate
dig +short CNAME sqcligpt.fredlingautomation.dev
```
If OAuth metadata changes, restart both services and refresh the MCP
connection from the ChatGPT Plugins page before retesting.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues