feat(mcp): support sse and streamable-http transports#382
Merged
Conversation
Add --transport, --host, and --port flags to `reqstool mcp`. Defaults remain stdio / 127.0.0.1 / 8000 so existing usage is unchanged. For streamable-http, json_response and stateless_http are set on FastMCP settings for plain fetch compatibility. Closes #377 Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
jimisola
added a commit
that referenced
this pull request
May 25, 2026
…ool-regression monorepo (#391) * fix(enrich): format multiline Description/Rationale fields with label-only line and indented values Closes #383 For multiline values `_block_field` now emits the label alone on its own line followed by each value line indented 4 spaces, instead of placing the first value line next to the label and aligning continuation lines with the first character. Single-line values are unchanged: label and value remain on one line. Add unit tests for `_block_field` (single-line, multiline, blank-line preservation) and an integration test fixture `spec_multiline_description` backed by a new SVC_203 with a GIVEN/WHEN/THEN description in ms-101 test data. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * feat(mcp): support sse and streamable-http transports (#382) Add --transport, --host, and --port flags to `reqstool mcp`. Defaults remain stdio / 127.0.0.1 / 8000 so existing usage is unchanged. For streamable-http, json_response and stateless_http are set on FastMCP settings for plain fetch compatibility. Closes #377 Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * fix(tmpdir): replace hardcoded 'can_we_use_urn_here' suffix with location-based identifier (#381) * fix(tmpdir): replace hardcoded 'can_we_use_urn_here' suffix with location-based identifier Closes #370 Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * test(tmpdir): verify tmpdir suffix uses location type prefix for all location types Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * fix(tmpdir): address full-pr-review findings for suffix robustness and security - Add LocalMavenLocation/LocalPypiLocation to __extract_location_provenance - Change unknown location type fallthrough to log warning and return "unknown" - Apply _SUFFIX_MAX_LEN cap to full suffix string (prefix + uri), not just uri - Replace consecutive dots (..) with _ to prevent path traversal via mkdir - Add containment guard in get_suffix_path (resolve-based, is_relative_to) - Redact Git URL userinfo (credentials) before using as location_uri - Extract _SUFFIX_MAX_LEN and _UNSAFE_PATH_CHARS as module-level constants - Deduplicate test capturing closure into shared _capture_suffix_calls() - Strengthen test assertions: URI fragment presence, safe-char regex, length guard Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * fix(lint): move module-level constants after imports to fix E402 Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * refactor(tmpdir): move suffix naming to LocationInterface.tmpdir_key() Add abstract tmpdir_key() to LocationInterface with make_safe_tmpdir_suffix() helper in location.py. Each concrete location class implements tmpdir_key() directly. __parse_source() now calls current_location_handler.current.tmpdir_key() instead of duplicating sanitization logic in the generator. Closes finding #7 from PR review. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> --------- Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * fix(enrich): use blockquote prefix for multiline field continuation lines Spaces at the start of a line are collapsed by Markdown renderers so 4-space indentation had no visual effect. Switch to blockquote (`> `) prefix which all Markdown renderers preserve and render with a visible left bar. Blank lines within a multiline value emit a bare `>` to keep the blockquote context continuous. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * fix(enrich): terminate blockquote with paragraph break before next field Without a blank line after the last > line, Markdown renderers continue the blockquote and swallow subsequent field labels. Add a paragraph-break sentinel ("") to the end of multiline _block_field results and emit it as a bare newline in enrich_text so each blockquote is closed before the next **Label**: line. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * fix(enrich): drop blockquote prefix — use plain label-on-own-line for multiline fields Blockquote rendering is renderer-dependent and OpenSpecUI does not break the blockquote context on blank lines, causing subsequent field labels to be swallowed inside the block. Use a plain label-on-own-line format instead: the label is emitted alone on its line and each value line follows without any Markdown prefix, which works correctly in all renderers. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * fix(enrich): indent multiline field values with U+00A0 for CommonMark-safe indentation U+00A0 (non-breaking space) is not treated as whitespace by CommonMark parsers, so it survives as visible indentation in react-markdown where regular ASCII spaces are stripped. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * test(regression): add cross-ecosystem integration tests against reqstool-regression monorepo Closes reqstool-client#386 (local + git axes). Adds tests/integration/reqstool/model_generators/test_regression_monorepo.py: - test_ecosystem_git_location[python/java/typescript]: parameterised over all three ecosystem wrappers via GitLocation. Verifies the wrapper URN, parent URN (reqstool-regression), grandparent URNs (regression-base-a/b), and that REQ_B02 is excluded by the parent import filter. - test_parent_entry_aggregates_all_ecosystems: entry point is the parent layer (fixtures/parent). Verifies all three ecosystem URNs are walked via implementations.local — the "count grows with each new ecosystem" regression signal. All tests gated on GITHUB_TOKEN; marked @pytest.mark.integration. Also updates tests/fixtures/reqstool-regression-python/README.md to remove the stale "becomes a git submodule" guidance. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * style: apply black formatting to test_regression_monorepo Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * fix(test): address full-pr-review findings in test_regression_monorepo Fixes applied: - #1/#2: REQ_B02 assertion was vacuously true (UrnId vs string); replace with string comparison; add REQ_B01 positive assertion; document that REQ_B02 filter exclusion lives at the DB layer (DatabaseFilterProcessor), not at the raw-dataset level - #2: Ecosystem URN check changed from substring match to exact key lookup - #3: Replace per-function @pytest.mark.integration and @pytest.mark.skipif with module-level pytestmark - #4: Ecosystem names in parent test now derived from ECOSYSTEMS constant - #6: Move UrnId import to module top (then removed as unused after #1 fix) - #7: Extract _GITHUB_TOKEN_ENV constant; use everywhere - #8: Extract _COMMON_URNS frozenset; use issubset() in both tests - #9: Document intentional unpinned 'main' branch with inline comment Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * fix(test): address round-2 review findings in test_regression_monorepo - #1: Consolidate GitLocation construction into _make_generator(path) — eliminates the two-site duplication and adds a _all_req_ids helper - #2: _make_generator now returns (gen, holder); both tests assert holder.get_no_of_errors() == 0 so semantic errors are not silently swallowed - #3: test_parent_entry_aggregates_all_ecosystems gains REQ_B01 assertion to verify base-b data was actually aggregated, not just keyed - #4: skipif guard tightened to os.getenv(..., "").strip() so whitespace- only token values are treated as absent - #5: all_req_ids extracted to _all_req_ids() helper with clarifying docstring; comment in test makes scope explicit - #6: README bare #386 reference replaced with full GitHub URL Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * fix(test): use holder.get_errors() in validation assertion message Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * test(regression): add explicit structural assertions per req/svc/mvr Add test_regression_structure.py with field-level assertions for all entities in the regression fixture: significance, lifecycle state, categories, implementation type, verification type, requirement_ids, and svc_ids. Covers all six URNs (parent, base-a, base-b, python, java, typescript) with ~40 test functions, sharing a single module-scoped git clone. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * style: apply black formatting to test_regression_structure Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * test(regression): rewrite structural tests to use SQLite pipeline end-to-end Replace raw-dataset assertions with build_database() + RequirementsRepository queries so the full pipeline is exercised: parse → populate → filter → lifecycle validation. Key behavioral differences now captured: - REQ_B02 excluded by parent import filter (base-b count 1, not 2) - Ecosystem requirement rows absent from DB (implementation-chain filtering) - SVC requirement_id links to filtered ecosystem reqs cascade-deleted Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * refactor(test): address full-pr-review findings in regression tests - Extract shared constants (URL, branch, token env, ecosystem lists) to _regression_shared.py — single source of truth for both test files - Add conftest.py with pytestmark (integration + skipif) and a module-scoped shared_tmpdir fixture; removes duplication from both test files and makes the monorepo tests share one git checkout - Fix _all_req_ids: rename loop var req → urn_id, remove redundant or [] - Move validation-error assert into _make_generator; callers no longer need to check holder separately - Add repo.get_initial_urn() guard in fixture to catch wrong entry point - Add test_base_b_in_db guard so test_base_b_no_mvrs cannot pass as a false positive when the URN is absent from the DB - Add SVC existence check before len(requirement_ids)==0 in svc_l02 test - Add inline comments on count assertions citing source YAML files - Add cross-reference comment from monorepo test to structure test for REQ_B02 filter coverage Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> * docs(test): document SHA-pinning strategy for regression repo branch Signed-off-by: Jimisola Laursen <jimisola@jimisola.com> --------- Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
--transport {stdio,sse,streamable-http},--host, and--portflags toreqstool mcpstdio/127.0.0.1/8000— no existing usage breaksstreamable-http, setsjson_response=Trueandstateless_http=Trueon FastMCP settings for plain-fetch HTTP clientsuvicornandstarletteare already transitive deps ofmcp>=1.0Test plan
reqstool mcp --helpshows--transport,--host,--portreqstool mcp --transport sse local -p <path>starts an SSE server on port 8000reqstool mcp --transport streamable-http --port 9001 local -p <path>responds to POST/mcpCloses #377