feat(mcp): add read-only API server foundation #76
Closed
timothy
wants to merge 5 commits from
issue58-mcp into main
pull from: issue58-mcp
merge into: :main
:main
:renovate/meziantou.analyzer-3.x
:renovate/nuget-sixlabors.imagesharp-vulnerability
:913-scripted-playout-401
:929-vuln-pkcs-imagesharp-v4
:renovate/dotnet-monorepo
:release/v26.15.0-notes
:renovate/lucene.net
:renovate/cliwrap-3.x
:issue-806-guard-populations
:scratch/767b-poisoned
:scratch/767b-control
:release/v26.14.0-notes
:release/v26.14.0
:renovate/sqlitepclraw.bundle_e_sqlite3-3.x
:docs/510-skill-logo-bug-policy
:fix/510-watermark-resolution-policy
:fix/629-verdict-classifier-falseopens
:fix/609-decisions-edit-token-scope
:issue-135-clear-to-none
:release/v26.12.0-notes
:fix/409b-lastscan-api-parity
:fix/401-updatechannel-mirror-422
:fix/327-playlist-rename-validation
:fix/410-scancancel-log-level
:fix/409-447-librariesscreen-neverscanned
:fix/338-zap-exit-code
:fix/367-plex-budget-message
:fix/310-debom-legacy-cs
:ci/604-lane-rebalance
:feat/388-design-mirror
:feat/247-test-ownership
:feat/247-primary-action
:feat/357-player-owned-playback
:feat/357-jellyfin-plugin-poc
:fix/289-mcp-hardening
:issue58-mcp
:feat/244-channels-extract
:ci/auto-bump-prod-compose
:feat/multi-rerun-collections-api
:feat/collections-api
:feat/quick-wins
:feat/185-docs-part2
:feat/140-collections-screen
:feat/146-channel-edit
:feat/147-classic-ui-link
:issue22-renovate-dashboard
:feat/91-cutover
:feat/63-composite-create
:feat/65-library-browse
:feat/85-epg
:feat/86-schedule-editor
:feat/109-dashboard-data
:feat/99-session-tracking
:fix/dockerfile-node-tag
:feat/59-spa-foundation
:docs/59-ui-redesign-brief
:feat/102-json-guide
:feat/111-schedule-durations
:feat/104-artwork-upload
:feat/103-media-sources-api
:feat/playouts-read-api
:feat/108-health-api
:feat/105-picker-list-endpoints
:issue-97-channel-state-api
:issue42-jellyfin-musicvideos
:issue46-rest-api-error-contract
:dependabot/nuget/ErsatzTV.FFmpeg.Tests/multi-d307a2e06f
:qsv-improvements
:hdr-vulkan-cuda-test
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
65d88b5167 |
test(#289): pin BoundedLineReader cap/CRLF boundaries + document per-segment path assumption
Non-blocking polish from the fix-commit re-review (verdict MERGEABLE):
- BuildPath: comment the assumption that each {param} is its own path segment
(revalidate the whole path if a template ever concatenates adjacent params).
- Two additive BoundedLineReader tests: exactly-at-cap line returned intact
(inclusive max), and \r\n carriage-return stripping. No logic change.
36 tests (was 34).
Refs #289
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
4f68805d9a |
security(#289): review fixes — body-read timeout, transport-error response, bounded stdin, env clamps
Folds in findings from the independent reviews of the initial hardening diff (a cold adversarial pass + a Codex pass — complementary catches). Fork (HIGH/MED): - Body-read timeout regression: ResponseHeadersRead moved the body read outside HttpClient.Timeout and it used CancellationToken.None, so a slow-drip upstream hung the single-threaded session. Now a per-request linked CTS (options .EffectiveRequestTimeout) covers headers + body; HttpClient.Timeout set to Infinite so one timer owns it. Verified live against a black-hole upstream: 1s timeout → -32603 in ~1s, no hang. - Transport/timeout exceptions escaped HandleAsync's catch filter → no response for the id → client hangs. Added a broad final catch → -32603 when hasId. Codex (env/input robustness): - ERSATZTV_REQUEST_TIMEOUT_SECONDS / ERSATZTV_MAX_RESPONSE_BYTES at int.MaxValue crashed at startup / overflowed `cap+1` to a negative alloc. ParseInt now clamps to [min,max]; the client also hard-ceils the cap (MaxAllowedResponseBytes). - A ".."/"." path param collapsed the URL onto another route under Uri canonicalization — rejected in BuildPath. Both reviewers (stdin OOM): ReadLineAsync buffered an unbounded line before any guard. New BoundedLineReader drains+drops oversized lines (memory-bounded), keeping subsequent lines aligned. Nits: case-insensitive ParseBool fallback; UTF-8 boundary backoff so a mid- codepoint truncation doesn't emit U+FFFD; renamed a misleading test. 34 tests (was 26); +8 covering the transport-error path, dot-segment rejection, cap-overflow fallback, UTF-8 seam, and the bounded reader. Refs #289 #197 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
55fc210385 |
security(#289): harden MCP server (read-only posture, JSON-DoS, response cap, arg validation)
From the #197 cold review's MCP-consumer pass. Hardens `ErsatzTV.Mcp` (PR #76) before it can go live against a soon-remote-exposed API. - HIGH verb-guard: runtime read-only *posture* (ERSATZTV_ALLOW_WRITES, default false) enforced in the executor — a wrong catalog entry can't mutate/delete, and future write tools (#58) slot in behind the opt-in. Not a hardcoded GET-only clamp, so the non-read-only final design stays reachable. - HIGH JSON-crash DoS: guard JsonDocument.Parse in HandleAsync (-32700, id null), handle missing/non-string method (-32600) instead of throwing, and wrap the Program.Main read loop so one bad stdin line can never kill the session. - MED unbounded response / LOW no-timeout: cap the buffered body (ERSATZTV_MAX_RESPONSE_BYTES, truncation marker) + explicit HttpClient.Timeout. - MED args unvalidated: ToolArgumentValidator checks caller args against each tool's InputSchema (required present, no unknown args, basic types) before a request is built. - MED prompt injection: documented untrusted-data posture in docs/mcp.md. - LOW base-URL prefix: preserve a reverse-proxy path prefix in the URL join. Also documents that ERSATZTV_API_KEY is now effectively required (Bundle A gates all /api reads behind X-Api-Key). 14 new tests (26 total); live stdin smoke confirms malformed/no-method/non-object lines no longer crash the loop. Refs #289 #197 #58 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d1c04030af | chore: retrigger CI (MySQL service port collision with concurrent run) | ||
|
|
945d108334 | feat(mcp): add read-only API server foundation refs #58 |