feat: initial v0.1.0 of mcp-sonarqube-proxy
Stdio MCP server that proxies tools from an upstream SonarQube MCP server over streamable HTTP. Tools are forwarded 1:1 with full schema preservation (inputSchema, outputSchema, annotations, title, _meta); CallToolResult is forwarded including isError and structuredContent. - proxy.py: persistent upstream ClientSession, low-level Server with @list_tools and @call_tool(validate_input=False) handlers — the upstream is the sole schema authority. - cli.py: Click-based 'serve' (stdio) and 'check' (probe) commands; logging strictly on stderr (stdout reserved for JSON-RPC). - Targets mcp 1.27.x decorator API (pinned <2 to guard against the unreleased constructor-API rewrite on main). - pytest suite (14 tests) covering env-var resolution, schema passthrough, CallToolResult forwarding, registration, dispatch end-to-end, and CLI success/error paths. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
"""Tests for the Click CLI: serve startup errors, check success/failure."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
from mcp import types
|
||||
|
||||
from mcp_sonarqube_proxy import cli
|
||||
|
||||
|
||||
def test_check_success_lists_tools(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
fake_tools = [
|
||||
types.Tool(
|
||||
name="search",
|
||||
description="Search issues.",
|
||||
inputSchema={"type": "object"},
|
||||
),
|
||||
types.Tool(
|
||||
name="get_metric",
|
||||
title="Project Metric",
|
||||
description=None,
|
||||
inputSchema={"type": "object"},
|
||||
),
|
||||
]
|
||||
|
||||
async def fake_run_check() -> list[types.Tool]:
|
||||
return fake_tools
|
||||
|
||||
monkeypatch.setattr(cli, "_run_check", fake_run_check)
|
||||
|
||||
result = CliRunner().invoke(cli.main, ["check"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "2 Tools" in result.output
|
||||
assert "search" in result.output
|
||||
assert "Project Metric" in result.output
|
||||
|
||||
|
||||
def test_check_failure_exits_one_with_stderr(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
async def fake_run_check() -> list[types.Tool]:
|
||||
raise ConnectionError("connection refused")
|
||||
|
||||
monkeypatch.setattr(cli, "_run_check", fake_run_check)
|
||||
|
||||
result = CliRunner().invoke(cli.main, ["check"])
|
||||
|
||||
assert result.exit_code == 1
|
||||
assert "Upstream nicht erreichbar" in result.stderr
|
||||
assert "connection refused" in result.stderr
|
||||
|
||||
|
||||
def test_serve_startup_error_exits_one(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
async def fake_run_serve() -> None:
|
||||
raise RuntimeError("upstream initialize failed")
|
||||
|
||||
monkeypatch.setattr(cli, "_run_serve", fake_run_serve)
|
||||
|
||||
result = CliRunner().invoke(cli.main, ["serve"])
|
||||
|
||||
assert result.exit_code == 1
|
||||
assert "fatal" in result.stderr
|
||||
assert "upstream initialize failed" in result.stderr
|
||||
|
||||
|
||||
def test_version_flag() -> None:
|
||||
from mcp_sonarqube_proxy import __version__
|
||||
|
||||
result = CliRunner().invoke(cli.main, ["--version"])
|
||||
assert result.exit_code == 0
|
||||
assert __version__ in result.output
|
||||
Reference in New Issue
Block a user