Skip to content

feat: card external links (client, CLI, MCP) + card view/get_card include (#21) - #23

Merged
dsociative merged 7 commits into
masterfrom
feat/21-external-links
Aug 27, 2026
Merged

feat: card external links (client, CLI, MCP) + card view/get_card include (#21)#23
dsociative merged 7 commits into
masterfrom
feat/21-external-links

Conversation

@dsociative

@dsociative dsociative commented Aug 27, 2026

Copy link
Copy Markdown
Owner

Closes #21.

Card external links (Links (common links) in Kaiten) at every layer, plus an opt-in way to fetch related sections together with a card. Strictly additive; nothing existing changes shape.

kaiten-client

  • ExternalLink { id, uid, url, description, created, updated } and client.external_links() with list / add / update / remove over /cards/{id}/external-links. Shapes captured from the live API: updates are PATCH with only the given fields (PUT answers 404), description is optional and stored as null when absent.
  • The API validates neither the url nor duplicates; the client passes requests through as they are. cargo semver-checks against v0.4.1: no breaking changes (additive only).

CLI

  • card external-link list | add --url … [--description …] | edit <link> --url/--description | rm.
  • --url must be an absolute http(s) URL without embedded credentials; checked before any request, the value is never echoed in the error.
  • card view --include external_links,comments (repeatable or comma-separated). The value names are the same tokens the MCP get_card include takes.
  • --comments keeps working, is marked Deprecated: use --include comments in --help and prints one warning line on stderr. Plain card view output and the --comments --json shape ({card, comments}) are unchanged.

MCP

  • Four tools: list_card_external_links, add_card_external_link, update_card_external_link, remove_card_external_link (36 → 40 tools; pinned in the unit test and the stdio contract test).
  • get_card gains an optional include: ["external_links", "comments"]: each section is one extra request and one extra key. Without include the tool is a single request with the same result shape as before. An unknown section is a parameter error before any request, naming the accepted values.

Tests (written first)

  • client: external_links_test.rs (request bodies via body_json, PATCH partial body, DELETE path).
  • CLI: card_external_link_test.rs (table/json, bad url → exit 1 with zero requests, edit-with-nothing, 403), card_view_test.rs (--include second request, both sections, deprecation warning on stderr with stdout intact, json key, unknown section → exit 2 with zero requests).
  • MCP: unit tests for all four tools and get_card include (plain call makes exactly one request), schema check, stdio wire-contract test for include and the unknown-section error.

Live smoke (dstest, card 67089469)

CLI: add → list → view --include external_links,comments--comments (warning on stderr only) → --json → edit description and url → rm → bad url / credentials → exit 1 without a request → edit with nothing → rm unknown → API error 403. MCP stdio: 40 tools listed, add → get_card include (both sections; plain get_card has neither key) → update → list → bad url / nothing-to-change / unknown include → tool-level errors → remove → 403 on unknown. The card was left without links.

Compatibility

No existing CLI flag, output, config key, env var, MCP tool name/param/result shape or kaiten-client public item changed. New functionality → 0.5.0 after merge.

CI

New coverage job (cargo-llvm-cov → Codecov via the CODECOV_TOKEN Actions + Dependabot secrets; the instrumented run includes the spawned kaiten binary — 84% lines on master) and a Codecov badge in the README. The badge starts showing after the first run on master.

CodeQL

rust/cleartext-logging is excluded via .github/codeql/codeql-config.yml: it models println! as a log file and any identifier matching user(name|id)/uid/account as sensitive, so every CLI line that names a user was an alert (8 open on master, all false positives, no per-identifier allowlist and no inline suppression for Rust). The tracing calls — the only real log sinks — print host, path and status only.

`ExternalLink { id, uid, url, description, created, updated }` and the
`external_links()` facade over `/cards/{id}/external-links`. Shapes taken
from the live API: updates are PATCH with only the given fields (PUT
answers 404), `description` is optional and stored as null when absent.
The API validates neither the url nor duplicates; the client passes
requests through as they are.
#21)

`card external-link add --url … [--description …]`, `edit <link> --url/--description`
(PATCH with only the given fields), `list` (table or --json), `rm`. `--url` must
be an absolute http(s) URL without credentials — Kaiten stores anything, the CLI
refuses obvious garbage before sending; the value is never echoed in the error.

`card view --include external_links,comments` (repeatable or comma-separated)
fetches extra sections; the value names match the MCP `get_card` `include`
values. `--comments` keeps working but is marked deprecated in --help and
prints one warning line on stderr. Plain `card view` output and the
`--comments --json` shape are unchanged.
Four tools — list_card_external_links, add_card_external_link,
update_card_external_link, remove_card_external_link — over the card
external links resource; `url` is checked to be an absolute http(s) URL
before any request (Kaiten stores anything) and never echoed in the error.

`get_card` gains an optional `include: ["external_links", "comments"]`
(names shared with the CLI `card view --include`): each section is one
extra request and one extra key in the result; without `include` the tool
stays a single request with the same shape as before. 36 → 40 tools; the
stdio contract test covers `include` and rejects an unknown section
before any request.
…eview follow-ups (#21)

The WHATWG parser strips control characters and repairs `http:x`, `http:/x`
and `http:///x` into `http://x/`, while Kaiten stores the raw text — so the
url check now requires an `http(s)://` prefix followed by a host on the
trimmed text and no control characters, before the parsed host/credentials
checks.

Also from review: `card view` prints the section as `External links:`;
`--description` gets help text; `try_args!` keeps its doc comment (the
url helper moved below the macro); the server instructions mention
external links; `Card` doc says why `include` costs a request; tests pin
`edit --url` at every layer, the `{card, comments}` JSON shape of the
deprecated `--comments`, single fetch for `--comments --include comments`,
the repeated `--include` form and the exact `IncludeSection` schema.
Comment thread crates/kaiten/src/commands/card.rs Fixed
A `coverage` job on ubuntu runs the workspace tests instrumented
(cargo-llvm-cov also counts the `kaiten` binary the integration tests
spawn — 84% lines on master today) and uploads lcov to Codecov. The
upload token lives in the CODECOV_TOKEN Actions and Dependabot secrets;
PRs from forks have neither, so an upload failure fails the job only for
pushes and same-repo PRs.
@dsociative
dsociative force-pushed the feat/21-external-links branch from ea52715 to 6dc81ee Compare August 27, 2026 14:17
@codecov-commenter

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

Thanks for integrating Codecov - We've got you covered ☂️

…s on stdout by design

The query models `println!` as a log file and any identifier matching
user(name|id)/uid/account as sensitive, so every `card view`, `card member`
and `auth status` line naming a user is an alert (8 open, all false
positives). The only real log sinks are the `tracing` calls, which print
host, path and status only. The rest of the default suite stays on.
@dsociative
dsociative merged commit c5f3978 into master Aug 27, 2026
7 checks passed
@dsociative
dsociative deleted the feat/21-external-links branch August 27, 2026 14:40
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.

[Feature]: Support card external links in CLI and MCP

3 participants