Skip to content

mcp: add log and per-artifact issue query tools - #306

Open
bhcopeland wants to merge 1 commit into
kernelci:mainfrom
bhcopeland:mcp-triage-tools
Open

mcp: add log and per-artifact issue query tools#306
bhcopeland wants to merge 1 commit into
kernelci:mainfrom
bhcopeland:mcp-triage-tools

Conversation

@bhcopeland

Copy link
Copy Markdown
Member

Add three read-only dashboard tools, exposed both as MCP tools and on KernelCIClient:

  • get_log fetches a test's log_url and gzip-decompresses it, returning the content size-bounded to the tail by default, with total_bytes and a truncated flag so large logs stay within client limits
  • get_test_issues / get_build_issues list the issues tracked for a specific test or build, the inverse of the existing get_issue_tests and get_issue_builds

get_log is new on KernelCIClient; the issue tools expose existing client methods that were not yet surfaced in the MCP.

Comment thread kcidev/api.py Outdated
Comment thread kcidev/api.py Outdated
content = response.content
if content[:2] == b"\x1f\x8b":
try:
content = gzip.decompress(content)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The download and decompression are not memory-bounded
A large log could exhaust the MCP process’s memory.
This should use streaming decompression with a bounded head/tail buffer and a hard size limit.

Comment thread kcidev/api.py Outdated
@aliceinwire

Copy link
Copy Markdown
Member

The trust boundary around log_url should be clarified. The code performs a direct GET, including redirects, against a URL obtained from dashboard data. If external submitters can control that field, this creates risk. The implementation should validate schemes, private/link-local addresses, DNS results, and redirect targets or document why all returned URLs are trusted.

@aliceinwire

Copy link
Copy Markdown
Member

The happy paths have pytest coverage, but the security- and boundary-sensitive behavior is not covered. Please add tests for zero/negative/oversized max_bytes, truncated or malformed gzip input, download failures, bounded streaming/decompression, and URL/redirect validation. These tests should accompany the corresponding implementation fixes before approval.

@bhcopeland

Copy link
Copy Markdown
Member Author

The happy paths have pytest coverage, but the security- and boundary-sensitive behavior is not covered. Please add tests for zero/negative/oversized max_bytes, truncated or malformed gzip input, download failures, bounded streaming/decompression, and URL/redirect validation. These tests should accompany the corresponding implementation fixes before approval.

Good point, now becomes a KciDevError I also fixed multi-member gzip, which previously dropped everything after the first member. To your first comment r.e. log_url, I added a fetch-time guard that allows only http/https. Which also resolves the host and rejects private/loopback/link-local/reserved addresses (and follows redirects).

Also have added tests. Thanks for the feedback @aliceinwire. Sorry would done it quicker (was on holiday but back now)

@bhcopeland
bhcopeland force-pushed the mcp-triage-tools branch 3 times, most recently from dd446ee to 9ff6eca Compare August 28, 2026 10:05
@yogeshlal

Copy link
Copy Markdown

Hi @bhcopeland ,

I tested 3 scenario, but infra failure log need some work. other interface works fine, next I will test including kci-dev as lib to my https://github.com/yogeshlal/triage-agent.

  1. baseline boot failure or non fatal issue, its working fine.

Pulled the full log for maestro:6a90aec3c5867fba94687850 (106,313 bytes, in three get_log slices — head, tail, and the middle — so the whole file is covered).

Job: LAVA job 217635, device qcs615-ride-ch-kci-02 (QCS615 Ride / IQ-615 Beta EVK), baseline-arm64-preempt_rt-qualcomm
Kernel: 7.2.0 #1 SMP PREEMPT_RT, aarch64-linux-gnu-gcc 14.2.0 (tuxmake)

The one real kernel defect: a boot-time WARNING in the tracepoint self-check

Fires at [0.000000], during trace_init() — before anything else runs:

TRACE EVENT ERROR: Event mtu3_gadget_ep_set_halt has double dereference in TP_printk: &REC->gpd_ring->dma
------------[ cut here ]------------
WARNING: kernel/trace/trace_events.c:420 at test_double_dereference+0x128/0x130, CPU#0: swapper/0/0
CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 7.2.0 #1 PREEMPT_RT
Call trace:
test_double_dereference+0x128/0x130 (P)
trace_event_raw_init+0x3ec/0x6f8
event_init+0x38/0xa0
trace_event_init+0xd4/0x3b0
trace_init+0x40/0x624
start_kernel+0x5c0/0x7ec
__primary_switched+0x88/0x90

Two more of the same class follow (no second splat, warn-once):

  • mtu3_gadget_ep_disable — double dereference in TP_printk: &REC->gpd_ring->dma
  • mtu3_gadget_ep_enable — same

Root cause is in the tracepoint definitions, not on this platform: the MediaTek mtu3 USB gadget tracepoints (drivers/usb/mtu3/mtu3_trace.h) format &REC->gpd_ring->dma,
which dereferences a pointer stored in the ring buffer at print time — exactly what test_double_dereference() in kernel/trace/trace_events.c was added to catch. It's a
WARN triggered by a new(-ish) validator, hardware-independent, and would fire on any build with CONFIG_USB_MTU3 tracepoints compiled in. This is the only
WARNING/BUG/Call trace in the entire log.

Everything else is missing-firmware noise from the rootfs, not kernel failure

  1. Kseftest log fetch working fine.

Pulled the log for maestro:6a90b649c5867fba9468bab3 (tail + head; 115,639 bytes total, kselftest-ring-buffer-ramdisk, device kaanapali-mdt-kci-0801).

Verdict: harness/rootfs failure, not a kernel regression

The single reported result is a harness-level marker, not a ring-buffer test case:

  • cd ./automated/linux/kselftest/
  • ./kselftest.sh -c ring-buffer -T -t kselftest_armhf.tar.gz -s True -u -L -S /dev/null
    -b -g -e -p /opt/kselftests/mainline/ -n 1 -i 1 -E
    INFO: install_deps skipped
    kselftests found on rootfs
    skiplist:
    ========================================
    ========================================
    shardfile-ring-buffer fail
    <LAVA_SIGNAL_TESTCASE TEST_CASE_ID=shardfile-ring-buffer RESULT=fail>
    <LAVA_SIGNAL_ENDRUN 1_kselftest-ring-buffer-ramdisk 217907_1.1.3.5>
    <LAVA_TEST_RUNNER EXIT>

shardfile-ring-buffer is the ID kselftest.sh emits when it cannot produce/read a shard list for the requested collection. Zero ring-buffer subtests ran — no ok/not ok,
no per-test IDs, and the run returned essentially instantly between STARTRUN and ENDRUN. The empty skiplist block confirms nothing was filtered out; there was simply
nothing to shard.

  1. Lab infra related log not able to fetch, might be due to mcp tool using dashboard to fetch log, but infra related log not able to register to dashboard as it not valid run as per dashboard specification ?, but raw lava_callback do have the link at https://kernelci-api.westus3.cloudapp.azure.com.
    get_log can't resolve that node — tried both forms:

┌──────────────────────────────────┬───────────────────────────────────────────────┐
│ test_id │ result │
├──────────────────────────────────┼───────────────────────────────────────────────┤
│ maestro:6a90b63cc5867fba9468b9b1 │ Dashboard test request failed: Test not found │
├──────────────────────────────────┼───────────────────────────────────────────────┤
│ 6a90b63cc5867fba9468b9b1 │ Dashboard test request failed: Test not found │
└──────────────────────────────────┴───────────────────────────────────────────────┘

get_log only resolves test/boot nodes via the dashboard's test endpoint. A "Test not found" for both forms means that id isn't a test node the dashboard has ingested —
most likely it's a build node (or a checkout), which has its logs under get_build instead, not get_log.

You restricted me to get_log, so I stopped here rather than probing get_build/list_tests. Say the word and I'll try get_build maestro:6a90b63cc5867fba9468b9b1 — that's
the natural next call if this is a kbuild node.

Add three read-only dashboard tools, exposed both as MCP tools and on
KernelCIClient:

- get_log resolves a test's log from log_url, or a log entry in
  output_files when log_url is empty (as it is for many failures), and
  returns the decompressed text bounded to the tail by default with
  total_bytes and a truncated flag. The download streams with a bounded
  head/tail buffer, decompresses gzip incrementally, caps max_bytes at a
  hard ceiling, stops after a scan limit to bound memory, and validates
  the URL scheme and resolved address (rejecting private/link-local
  hosts and unvalidated redirects) since log URLs come from submitter
  data
- get_test_issues / get_build_issues list the issues tracked for a
  specific test or build, the inverse of the existing get_issue_tests
  and get_issue_builds

get_log is new on KernelCIClient; the issue tools expose existing client
methods that were not yet surfaced in the MCP.

The request timeout is per read rather than total, and the scan limit is
64 MiB read in 64 KiB chunks, so a slow server could hold the caller for
far longer than any single timeout suggests. Reading also stops at a
total deadline, reported as deadline_exceeded, which matters now that
tool calls run in worker threads and a stalled download occupies one.

The dashboard reports an artifact with no known issues as an error
rather than an empty list, so the two issue tools return that as an
empty list. "Nothing is tracked against this build" is the answer a
caller checking a failure wants, not a failed call. The CLI already
treats the same response that way at its own call sites.

Signed-off-by: Ben Copeland <ben.copeland@linaro.org>
@bhcopeland

Copy link
Copy Markdown
Member Author

Thanks @yogeshlal for the report, very useful :)

Looking at your data, that's an incomplete job: kind=job, result=incomplete. It never reaches the dashboard, because the dashboard ingests test and build results, not failed jobs. It's not a build node either, so get_build will give you the same "not found".

The good news is you don't need get_log for this one. get_node on that id returns it already:

  data.error_code         = Infrastructure
  data.error_msg          = Unable to flash the device
  artifacts.callback_data = https://files.kernelci.org/qualcomm/kselftest-perf-events-ramdisk-6a90b63cc5867fba9468b9b1/lava_callback.json.gz

So the diagnosis is right there, plus a link to the raw LAVA callback if you want it. That should unblock the triage agent work you are doing.

For wiring it in properly, I'd either have get_log fall back to Maestro when the dashboard has no test, or add a separate tool for job-level failures. The first feels friendlier for agents (one tool, works for any id). Either way it's a gap rather than a bug in this PR, so I'd suggest doing it separately.

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.

3 participants