Skip to content

Report a stdio server that cannot be started as a launch failure - #177

Merged
M3gA-Mind merged 3 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/168-launch-failed
Nov 11, 2026
Merged

M3gA-Mind merged 3 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/168-launch-failed

Conversation

@M3gA-Mind

Copy link
Copy Markdown
Contributor

Summary

When a stdio server's launcher is found but its process can't be started, the host got malformed mcp response: spawning npx failed: …, though the server never ran and nothing it said was malformed. This happens with Windows os error 193 for npm's extensionless npx script (the case in #168), with a permission error, or when the jail refuses the command. Hosts and Sentry grouped these with real protocol failures.

These now come back as a new Error::LaunchFailed { command, detail } under a new error name, ai.tinyhumans.tinymcp.Error.LaunchFailed. The command-not-found case stays MissingRuntime, as before.

Related issue

Closes #168. The PATHEXT resolution half is in #175.

API or behavior changes

Validation

On our Linux CI runner, at b029ac1db5. The head differs only by applying cargo fmt's patch:

  • cargo clippy --all-targets --all-features -- -D warnings: 0 errors, same as main. cargo build and RUSTDOCFLAGS="-D warnings" cargo doc: ok.
  • cargo test -p tinymcp transport::stdio: 103 passed. -p tinymcp error: 88 passed. -p tinymcp-bus version: 7 passed.
  • cargo test --workspace --no-fail-fast: no failures unique to this branch. The 3 failures are main's known full-suite timeouts (registry::boot, tinybus_module::directories), which also fail on main.
  • Red control: mapping the spawn failure back to Error::malformed fails a_process_that_cannot_be_started_is_a_launch_failure_not_a_bad_response, which then gets MalformedResponse { detail: "jail refused" }.

Tests

  • a_process_that_cannot_be_started_is_a_launch_failure_not_a_bad_response: a spawner that refuses the launch, as a jail or Windows would, gives LaunchFailed with the reason, under the LAUNCH_FAILED name.
  • The error-table tests: every variant travels under a listed name, every listed name is produced, and no two variants share one.
  • The version test pins 1.32.
  • Red control: mapping the failure back to malformed fails the new test.

Documentation

Rustdoc on the variant, the name and initialize's # Errors, and the contract version history.

Checklist

  • The change is focused on one logical change
  • No new #[allow(...)], #[ignore], or relaxed lints
  • No secrets, tokens, or .env contents in the diff or the description

A found launcher whose process the OS or the jail refused to start (Windows
os error 193 for npm's extensionless npx script, a permission error, a jail
without a policy for the command) came back as "malformed mcp response:
spawning ... failed", though the server never ran. It is now
Error::LaunchFailed under the new LaunchFailed error name, contract 1.32.

Closes tinyhumansai#168
@coderabbitai

coderabbitai Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7c667c4a-9675-4e82-81f9-d1b20d62f360
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

@tinysweeper

tinysweeper Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Reviewed head: d4b80cf35a2c39fe2be791b76b83683ee33164b8

Verdict: Ready for maintainer review · Priority: none
Coverage: Reviewed 7 of 7 files.
Pull request purpose (title): Report a stdio serve…

Diagnostics and supporting details

Full summary

Pull request purpose (title): Report a stdio server that cannot be started as a launch failure. This pull request separates "the launcher could not be started" from "the server said something malformed". A stdio server whose command is found but whose process cannot be started (Windows os error 193 for npm's extensionless `npx`, a permission error, a jail refusal) previously surfaced as a `MalformedResponse`; it now surfaces as a new `Error::LaunchFailed { command, detail }` carried under a new wire name, `ai.tinyhumans.tinymcp.Error.LaunchFailed`, registered in the bus crate's `ALL` list. The command-not-found path is unchanged and remains `MissingRuntime`. Because a contract name was added, the shipped contract version moves to 1.32 and the version-pinning test is updated to match. The latest-push delta is unavailable — the review state holds the prior reviewed head and rendered evidence but no trusted base-to-head comparison — so this summary describes the whole pull request only, not a push delta.

Review snapshot

Change surface Files Review signal Count
Production 4 Active findings 0
Tests 3 Noted findings 0
Documentation 0 Resolved findings 0
Configuration 0 Pending checks/questions 0

Completeness: Complete
Test assessment: Test coverage is assessed from changed tests and lane evidence; execution is not claimed without trusted check data.

What changed

A stdio spawn failure is now reported as a launch failure rather than a protocol failure. The client's `initialize` maps a failed `spawn_process` call to `Error::LaunchFailed { command, detail }`, matching that variant to the new `errors::LAUNCH_FAILED` name in `wire_name`, and the new name is added to the bus crate's `ALL` list so it participates in the error-table invariants. The `initialize` `# Errors` documentation is corrected to distinguish `MissingRuntime` (command not found on the resolved path), `LaunchFailed` (found but not startable, or refused by the jail), `MalformedResponse` (reply shape wrong) and `UnsupportedProtocolVersion` (server settles on an unspoken version). The shipped contract version is bumped to 1.32 with a history entry for the `LaunchFailed` addition, and the pinning test asserts the new value. The cross-cutting error fixtures gain the new variant, and a new stdio test drives a refusing spawner and asserts both the variant with its detail and the wire-name mapping.

Features

  • Added — New `Error::LaunchFailed { command, detail }` variant with its wire-name mapping: A refused launch now carries the configured command and the underlying reason instead of being folded into a generic malformed-response message, and `wire_name` reports it under `LAUNCH_FAILED` rather than `MALFORMED_RESPONSE`, so per the described error-table rule a host that does not know the name treats it as a generic failure. (crates/tinymcp/src/error/mod.rs pub enum Error {, crates/tinymcp/src/error/mod.rs impl Error {)
  • Modified — Stdio spawn errors reclassified from malformed response to launch failure: When the launcher resolves but the child cannot be started — an unrunnable script on Windows, a permission error, a jail that refuses it — `initialize` now returns `LaunchFailed` because nothing was said to or by the server; the command-not-found case stays `MissingRuntime`, and the `# Errors` documentation now names each of the four outcomes separately. (crates/tinymcp/src/transport/stdio/mod.rs impl McpStdioClient {)
  • Modified — Shipped contract version bumped to 1.32: The added error name is a minor contract change, so the version consumers compare against moves to 1.32, with the history entry recording the `LaunchFailed` addition; a host that does not know the name still binds and treats it as a generic failure rather than a version incompatibility. (crates/tinymcp-bus/src/version/mod.rs, crates/tinymcp-bus/src/version/mod_tests.rs use super::{CONTRACT_VERSION, binds, is_compatible};)

Tests

  • unit — `a_process_that_cannot_be_started_is_a_launch_failure_not_a_bad_response` runs `initialize` against a client whose spawner refuses the launch, as a jail or Windows would, and asserts the result is `Error::LaunchFailed` whose `detail` contains the refusal, and that `wire_name()` equals `tinymcp_bus::errors::LAUNCH_FAILED`.: The tests lane (answered) reports this exercises the new branch and asserts both the variant and the wire name, pinning the behaviour. (crates/tinymcp/src/transport/stdio/mod_tests.rs mod against_a_fake_server {)
  • unit — The cross-cutting error fixtures gain `Error::LaunchFailed { command: "npx", detail: os error 193 }`, so the error-table tests — every variant travels under a listed name, every listed name is produced, and no two variants share one — cover the new variant.: The tests lane (answered) reports the new variant is included in the error-message test list, so the behavioural change is covered by the table invariants. (crates/tinymcp/src/error/mod_tests.rs fn assorted_other_errors() -> Vec<Error> {)
  • unit — `the_shipped_contract_version_is_pinned` asserts `CONTRACT_VERSION == (1, 32)`, keeping the shipped contract version pinned to the value this change introduces.: The tests lane (answered) reports the contract-version bump is pinned by the updated test. (crates/tinymcp-bus/src/version/mod_tests.rs use super::{CONTRACT_VERSION, binds, is_compatible};)

Findings

No active actionable findings.

Before merge

None.

How this fits together

flowchart LR
  n0["Error<br/>changed"]:::changed
  n1["assorted_other_errors<br/>changed"]:::changed
  n2["McpStdioClient<br/>changed"]:::changed
  n3["against_a_fake_server<br/>changed"]:::changed
  n4["one_of_every_variant"]:::impacted
  n5["StdioSpawner"]:::impacted
  n6["every_message_is_lowercase_and_unpunctuated"]:::impacted
  n7["bare_unauthorized_error"]:::impacted
  n8["sync"]:::impacted
  n9["StdioLaunch"]:::impacted
  n1 -->|uses| n0
  n2 -->|uses| n5
  n2 -->|uses| n8
  n3 -->|uses| n5
  n4 -->|uses| n0
  n4 -->|calls| n1
  n5 -->|uses| n9
  n6 -->|calls| n1
  n6 -->|tests| n1
  n6 -->|calls| n7
  n6 -->|tests| n7
  n7 -->|uses| n0
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading
Agent review details

critique

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change bumps CONTRACT_VERSION to (1, 32) and documents the new LaunchFailed error name, which exists in crates/tinymcp-bus/src/errors/mod.rs and is emitted by crates/tinymcp/src/error/mod.rs; the pinned test is updated to match. The version doc's own rule (minor bump for an added member or error name) is satisfied, and is_compatible's semantics are unchanged. I found no defect in these two files. _The code index is behind this pull request (indexed at `38794ebf1717`), so retrieved context may be out of date._

security

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change bumps CONTRACT_VERSION from (1, 31) to (1, 32) and updates the pinned test to match, with a doc comment describing the new LaunchFailed error name. Both edits are consistent with each other and with the version rule; no security-relevant behaviour is introduced. _The code index is behind this pull request (indexed at `38794ebf1717`), so retrieved context may be out of date._

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The tests lane answered and reports the new test exercises the new branch and asserts both the variant and the wire name, the error-message test list gains the new variant, and the version bump is pinned, with no reportable defect.
  • Lane summary: The change adds a `LaunchFailed` error variant, maps spawn failures to it instead of `MalformedResponse`, and bumps the contract to 1.32 with the pinned-version test updated. The new test `a_process_that_cannot_be_started_is_a_launch_failure_not_a_bad_response` exercises the new branch and asserts both the variant and the wire name, and the error-message test list gains the new variant, so the behaviour is pinned. I found no reportable defect. _The code index is behind this pull request (indexed at `38794ebf1717`), so retrieved context may be out of date._

commits

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The commits lane answered and reports no sensitive content in the committed files.
  • Lane summary: No sensitive content in committed files. _The code index is behind this pull request (indexed at `38794ebf1717`), so retrieved context may be out of date._

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The description lane answered and reports the follow-up commits bump `CONTRACT_VERSION` to 1.32 with a matching history line and update the pinning test, so title and body still match the diff, with no defect found.
  • Lane summary: The follow-up commits bump `CONTRACT_VERSION` to 1.32 with a matching history line and update the pinning test; the description already states 1.32 as the contract bump, so title and body still match the diff. I found no defect in these changes. _The code index is behind this pull request (indexed at `38794ebf1717`), so retrieved context may be out of date._
Evidence and run details
  • Models: ladder/vectors-oai3, deepseek-v4.1-flash
  • Spend: $0.001264
  • Tokens: 100636 input · 2768 output · 43520 cached · 331 embedding
  • Updated: 2026-11-11T18:31:42Z
Head State Pass summary
4773e117c86e ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-11-11T13:01:37Z)
8dcc4c9192be ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-11-11T15:30:04Z)
d4b80cf35a2c ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-11-11T18:31:42Z)

tinysweeper 0.1.2

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

tinysweeper found nothing blocking. Approving.

Diagnostics

Total spend: $0.0193

# Conflicts:
#	crates/tinymcp-bus/src/version/mod.rs
#	crates/tinymcp-bus/src/version/mod_tests.rs
# Conflicts:
#	crates/tinymcp-bus/src/version/mod.rs
#	crates/tinymcp-bus/src/version/mod_tests.rs
@M3gA-Mind
M3gA-Mind merged commit 0693195 into tinyhumansai:main Nov 11, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

On Windows, a stdio MCP server with command npx spawns the extensionless npx shell script and fails with os error 193

1 participant