Skip to content

feat: add update check to all surfaces (CLI, MCP, HTTP) - #990

Merged
ajianaz merged 3 commits into
developfrom
feat/update-check-all-surfaces
Aug 10, 2026
Merged

feat: add update check to all surfaces (CLI, MCP, HTTP)#990
ajianaz merged 3 commits into
developfrom
feat/update-check-all-surfaces

Conversation

@ajianaz

@ajianaz ajianaz commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

What

Move update-check logic from uteke-cli to uteke-core so all three runtime surfaces (CLI, MCP server, HTTP server) share one code path. Each surface now notifies users when a newer version is available on GitHub.

Why

Users running uteke via MCP (Claude Code, Cursor, Copilot) or HTTP server (Docker containers, long-running processes) had no way to know when a new version was released. Only CLI users got the startup notification. Now all surfaces check and notify.

Changes

Core (uteke-core/src/update_check.rs) - new shared module with cached check + network check + banner formatting. 5 unit tests included.

MCP server - detached background thread at startup, writes to stderr.

HTTP server - background thread with cache-first check on startup + every 24h. Health endpoint gains update_available field.

CLI - thin wrapper over core. Config opt-out preserved.

Testing

cargo test --workspace: 479 pass, 0 fail. Clippy: 0 warnings. Fmt: clean.

Move update-check logic from uteke-cli to uteke-core so all three
runtime surfaces share one code path. Each surface now notifies users
when a newer version is available.

Core (uteke-core/src/update_check.rs):
- UpdateInfo struct with is_update_available() + banner()
- check_cached(): instant cache lookup (24h TTL)
- check_network(): blocking GitHub fetch, updates cache
- check(): cached first, falls back to network
- check_and_notify(): prints banner to stderr if update available
- Cache file: ~/.config/uteke/update-cache.json
- Primary: GitHub 302 redirect (no API rate limit)
- Fallback: GitHub REST API
- 5 unit tests for version comparison + banner formatting

MCP server:
- Spawn detached background thread at startup
- Writes to stderr (MCP uses stdout for JSON-RPC)
- Uses check() which tries cache first

HTTP server:
- Background thread: check on startup + every 24h
- Logs via warn! for structured log visibility
- /health endpoint gains update_available field (from cache)
- Periodic loop respects SHUTDOWN flag (hourly wake for fast exit)

CLI (thin wrapper):
- Calls uteke_core::update_check::check_cached/check_network
- Respects update_check = false from uteke.toml (CLI-only config)

Tests: 479 pass, 0 fail, 0 warnings, 0 clippy errors
Comment thread crates/uteke-core/src/update_check.rs Fixed
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

🔍 Cora AI Code Review

Blocked — critical issues found.

🔴 Error (1)

  • crates/uteke-core/src/update_check.rs:191 — The primary method parses the location header from the 302 redirect. The code checks for strip_prefix(&prefix) where prefix = "/codecoradev/uteke/releases/tag/". However, GitHub redirects typically return absolute URLs like https://github.com/codecoradev/uteke/releases/tag/v0.14.0, not relative paths. The strip_prefix on a relative prefix will fail on absolute URLs, falling through to the rsplit('/') fallback which works but only if the tag starts with 'v'. If GitHub returns a tag without 'v' prefix in an absolute URL, both branches fail and it falls through to the API call unnecessarily.

Review powered by cora-code · BYOK · MIT

- HTTP server update thread now tries cache before hitting network
- Regenerate docs/api-reference.md for new update_available field
Avoid hardcoded repo path in strip_prefix — use format! with REPO
constant so it stays in sync if the repo path ever changes.

if let Some(location) = resp.headers().get("location") {
let loc = location.to_str().unwrap_or_default();
let prefix = format!("/{REPO}/releases/tag/");
@ajianaz
ajianaz merged commit 99d3b26 into develop Aug 10, 2026
14 of 15 checks passed
@ajianaz
ajianaz deleted the feat/update-check-all-surfaces branch August 10, 2026 09:07
ajianaz added a commit that referenced this pull request Aug 10, 2026
* fix(core): handle absolute URLs in update check redirect parser

GitHub 302 redirects return absolute URLs
(https://github.com/codecoradev/uteke/releases/tag/v0.13.1),
not relative paths. The previous strip_prefix('/codecoradev/...')
always failed on absolute URLs, falling through to the rsplit
fallback which required a 'v' prefix.

Fix: use find() to locate the tag marker anywhere in the URL,
and relax the rsplit fallback to accept any non-empty tag.

Found by Cora Code review on PR #990.

* fix(core): use split_once for UTF-8 safe URL parsing in update check

Cora finding: byte-indexed slicing (&loc[idx+len..]) could panic on
non-char-boundary if Location header contains multi-byte UTF-8.

Replace with split_once() — idiomatic, zero-cost, UTF-8 safe.

---------

Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
@ajianaz ajianaz mentioned this pull request Aug 11, 2026
ajianaz added a commit that referenced this pull request Aug 11, 2026
Version bump 0.13.1 → 0.13.2 + CHANGELOG.

Changes since v0.13.1:
- feat: update check (CLI, MCP, HTTP) (#990)
- fix: NULL embeddings crash vector index (#992, #993)
- fix: absolute URL in update check redirect (#994)
- fix: LongMemEval benchmark threshold false negatives (#995, #996)
- chore: branding refresh (#991)

Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants