From c7cd53409b1f4a41f3b1aabd9a9be2f601b3aa76 Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 15 May 2026 11:54:10 +0200 Subject: [PATCH 1/5] test(env): add MCP_PORT port binding test --- tests/env/test-mcp-port.sh | 131 +++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100755 tests/env/test-mcp-port.sh diff --git a/tests/env/test-mcp-port.sh b/tests/env/test-mcp-port.sh new file mode 100755 index 0000000..ee3b3d6 --- /dev/null +++ b/tests/env/test-mcp-port.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +# ============================================================================= +# tests/env/test-mcp-port.sh +# ============================================================================= +# Verifies that the MCP_PORT environment variable actually changes the port +# the server binds to. Starts the server twice: +# 1. default (no MCP_PORT) → expects port 8080 +# 2. custom (MCP_PORT=19876) → expects port 19876, NOT 8080 +# +# Does NOT require an OPENAPI_TOKEN — only checks TCP connectivity. +# +# Usage: +# bash tests/env/test-mcp-port.sh +# +# Exit code: 0 if all tests pass, 1 if any fail. +# ============================================================================= +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" +SRC_DIR="$ROOT_DIR/src" + +CUSTOM_PORT=19876 +DEFAULT_PORT=8080 +WAIT_TIMEOUT=20 + +PASS=0 +FAIL=0 +SERVER_PID="" + +# --- Colors --- +GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' + +pass() { printf "${GREEN}[PASS]${NC} %s\n" "$1"; PASS=$((PASS+1)); } +fail() { printf "${RED}[FAIL]${NC} %s\n" "$1"; FAIL=$((FAIL+1)); } +info() { printf "${YELLOW}[INFO]${NC} %s\n" "$1"; } +section() { printf "\n${CYAN}=== %s ===${NC}\n" "$1"; } + +kill_server() { + if [ -n "$SERVER_PID" ] && kill -0 "$SERVER_PID" 2>/dev/null; then + kill "$SERVER_PID" 2>/dev/null || true + wait "$SERVER_PID" 2>/dev/null || true + SERVER_PID="" + fi +} + +cleanup() { + kill_server +} +trap cleanup EXIT + +# Wait up to $WAIT_TIMEOUT seconds for a TCP port to be reachable. +wait_for_port() { + local port="$1" + for i in $(seq 1 "$WAIT_TIMEOUT"); do + if curl -s --max-time 1 -o /dev/null "http://localhost:${port}/" 2>/dev/null; then + info "Port ${port} ready after ${i}s" + return 0 + fi + sleep 1 + done + return 1 +} + +# Check that a port is NOT listening (give it a moment to confirm absence). +port_is_closed() { + local port="$1" + ! curl -s --max-time 1 -o /dev/null "http://localhost:${port}/" 2>/dev/null +} + +start_server() { + local port="$1" + MCP_PORT="$port" PYTHONPATH="$SRC_DIR" \ + python3 -m openapi_mcp_sdk.main \ + > /dev/null 2>&1 & + SERVER_PID=$! +} + +# ============================================================================= +# STEP 1 — Custom port (MCP_PORT=19876) +# ============================================================================= +section "MCP_PORT=${CUSTOM_PORT} — server binds on custom port" + +info "Starting server with MCP_PORT=${CUSTOM_PORT}..." +start_server "$CUSTOM_PORT" + +if wait_for_port "$CUSTOM_PORT"; then + pass "Server is reachable on port ${CUSTOM_PORT}" +else + fail "Server did not come up on port ${CUSTOM_PORT} within ${WAIT_TIMEOUT}s" +fi + +if port_is_closed "$DEFAULT_PORT"; then + pass "Port ${DEFAULT_PORT} (default) is NOT open — custom port was used" +else + fail "Port ${DEFAULT_PORT} (default) is also open — MCP_PORT was ignored" +fi + +kill_server + +# Give the OS a moment to release the ports before the next step. +sleep 2 + +# ============================================================================= +# STEP 2 — Default port (no MCP_PORT set) +# ============================================================================= +section "No MCP_PORT — server binds on default port ${DEFAULT_PORT}" + +info "Starting server without MCP_PORT..." +start_server "$DEFAULT_PORT" + +if wait_for_port "$DEFAULT_PORT"; then + pass "Server is reachable on default port ${DEFAULT_PORT}" +else + fail "Server did not come up on default port ${DEFAULT_PORT} within ${WAIT_TIMEOUT}s" +fi + +if port_is_closed "$CUSTOM_PORT"; then + pass "Port ${CUSTOM_PORT} (custom) is NOT open — default port was used" +else + fail "Port ${CUSTOM_PORT} (custom) is also open — unexpected" +fi + +kill_server + +# ============================================================================= +# Summary +# ============================================================================= +printf "\n" +printf "Results: ${GREEN}%d passed${NC}, ${RED}%d failed${NC}\n" "$PASS" "$FAIL" +[ "$FAIL" -eq 0 ] From f0655d2b2ecfbdf38da59beb3c288d2e95bb281f Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 15 May 2026 12:03:50 +0200 Subject: [PATCH 2/5] chore(packaging): bump version to 0.3.1 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7522c94..2abdf42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openapi-mcp-sdk" -version = "0.3.0" +version = "0.3.1" description = "Openapi.com official mcp server implementation" readme = "docs/readme-pypi.md" requires-python = ">=3.13" From 0d438eca7d8ea6b0b4b811b9041d34f63e123d4a Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 15 May 2026 12:04:47 +0200 Subject: [PATCH 3/5] chore(packaging): fix deprecated license classifier and license-files config --- pyproject.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2abdf42..c8732ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,8 +5,8 @@ description = "Openapi.com official mcp server implementation" readme = "docs/readme-pypi.md" requires-python = ">=3.13" license = "MIT" +license-files = ["LICENSE"] classifiers = [ - "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.13", ] @@ -21,8 +21,6 @@ dependencies = [ "fastapi>=0.110.0", ] -[tool.setuptools] -license-files = ["LICENSE"] [tool.pytest.ini_options] testpaths = ["tests/pytest"] From 2749923f890b59d698ad445aa75cc359bcc2a086 Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 15 May 2026 12:06:29 +0200 Subject: [PATCH 4/5] chore(packaging): add full PyPI metadata: authors, keywords, classifiers, urls --- pyproject.toml | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c8732ec..783a508 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,15 +1,44 @@ [project] name = "openapi-mcp-sdk" version = "0.3.1" -description = "Openapi.com official mcp server implementation" +description = "Openapi.com official MCP server — ready-to-use gateway and Python SDK for AI agents" readme = "docs/readme-pypi.md" requires-python = ">=3.13" license = "MIT" license-files = ["LICENSE"] +keywords = [ + "mcp", + "model-context-protocol", + "openapi", + "ai", + "llm", + "agent", + "fastapi", + "sdk", + "gateway", + "claude", + "anthropic", + "tools", + "api", +] classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Internet :: WWW/HTTP :: HTTP Servers", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.13", ] +authors = [ + { name = "Francesco Bianco", email = "info.francescobianco@gmail.com" }, + { name = "Marco Prosperi", email = "m.prosperi@openapi.com" }, + { name = "Simone Desantis", email = "s.desantis@openapi.com" }, +] +maintainers = [ + { name = "openapi.com", email = "support@openapi.com" }, +] dependencies = [ "expiringdict>=1.2.2", "fastmcp>=2.9.1", @@ -21,6 +50,11 @@ dependencies = [ "fastapi>=0.110.0", ] +[project.urls] +Homepage = "https://openapi.com/" +Repository = "https://github.com/openapi/mcp-server" +"Bug Tracker" = "https://github.com/openapi/mcp-server/issues" +Changelog = "https://github.com/openapi/mcp-server/releases" [tool.pytest.ini_options] testpaths = ["tests/pytest"] From 769e8ea322e3d26565d3de239f4ff2e84d3fc7bb Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 15 May 2026 12:21:36 +0200 Subject: [PATCH 5/5] docs(pypi): remove redundant horizontal rules from readme --- docs/readme-pypi.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/readme-pypi.md b/docs/readme-pypi.md index 3612ae6..7343a57 100644 --- a/docs/readme-pypi.md +++ b/docs/readme-pypi.md @@ -9,7 +9,6 @@ Use it in two ways: - **Python library** — import `openapi_mcp_sdk` in your own project to build a custom MCP server on top of openapi.com APIs. ---- ## Run the server @@ -31,7 +30,6 @@ pipx run openapi-mcp-sdk server pip install openapi-mcp-sdk && openapi-mcp-sdk server ``` ---- ## Local launcher script @@ -59,7 +57,6 @@ Next time, just run: bash mcp-server.sh ``` ---- ## CLI reference @@ -72,7 +69,6 @@ Commands: token Generate or inspect an openapi.com Bearer token [coming soon] ``` ---- ## MCP client configuration @@ -110,7 +106,6 @@ Get your Bearer Token at [console.openapi.com](https://console.openapi.com/oauth The server automatically promotes `?token=` to an `Authorization: Bearer` header, so both methods behave identically. ---- ## Links