Adds a comprehensive conformance test suite for the Kotlin MCP SDK, covering core protocol operations, tool calls, elicitation, resources, prompts, and 20 OAuth/auth scenarios - Conformance server and client implementations - OAuth/auth test scenarios: JWT, authorization code flow, client credentials, PKCE, scope handling, cross-app access, client registration - CI workflow - Baseline file for tracking expected failures - Shell script fixes: - #592 - #593 - #596 ## Remaining known failures (tracked issues, will be fixed directly in `main`) - [x] `tools-call-with-logging`, `tools-call-with-progress`, `tools-call-sampling`, `tools-call-elicitation`, `elicitation-sep1034-defaults`- see #599, - [x] `elicitation-sep1330-enums` - #587 #600 - [x] `initialize` - #588 - [x] `tools_call`, `auth/scope-step-up`, `auth/scope-retry-limit` - #589 - [ ] `elicitation-sep1034-client-defaults` - #414 - [x] `sse-retry` - #590 - [ ] `resources-templates-read` - #591 ## Breaking Changes from #596 - `StreamableHttpClientTransport` and `mcpStreamableHttp`/`mcpStreamableHttpTransport`: old constructors accepting `Duration` timeout are now `@Deprecated` — use the new overloads with `ReconnectionOptions` instead - `StreamableHttpClientTransport.close()` no longer calls `terminateSession()` automatically ## Types of changes - [x] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [x] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update ## Checklist - [x] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [x] My code follows the repository's style guidelines - [x] New and existing tests pass locally - [x] I have added appropriate error handling - [x] I have added or updated documentation as needed --------- Co-authored-by: Konstantin Pavlov <1517853+kpavlov@users.noreply.github.com>
This commit is contained in:
parent
338f94804f
commit
e88b34170c
1 changed files with 3186 additions and 1122 deletions
194
conformance-test/run-conformance.sh
Executable file
194
conformance-test/run-conformance.sh
Executable file
|
|
@ -0,0 +1,194 @@
|
|||
#!/bin/bash
|
||||
# Script to run MCP conformance tests for the Kotlin SDK.
|
||||
#
|
||||
# Usage: ./conformance-test/run-conformance.sh <command> [extra-args...]
|
||||
# Commands: server | client | client-auth | all
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1; pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." || exit 1; pwd)"
|
||||
|
||||
CONFORMANCE_VERSION="0.1.15"
|
||||
PORT="${MCP_PORT:-3001}"
|
||||
SERVER_URL="http://localhost:${PORT}/mcp"
|
||||
RESULTS_DIR="$SCRIPT_DIR/results"
|
||||
SERVER_DIST="$SCRIPT_DIR/build/install/conformance-test/bin/conformance-test"
|
||||
CLIENT_DIST="$SCRIPT_DIR/build/install/conformance-test/bin/conformance-client"
|
||||
|
||||
SERVER_PID=""
|
||||
|
||||
# shellcheck disable=SC2317
|
||||
cleanup() {
|
||||
if [ -n "$SERVER_PID" ] && kill -0 "$SERVER_PID" 2>/dev/null; then
|
||||
echo "Stopping server (PID: $SERVER_PID)..."
|
||||
kill "$SERVER_PID" 2>/dev/null || true
|
||||
wait "$SERVER_PID" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
build() {
|
||||
echo "Building conformance-test distributions..."
|
||||
cd "$PROJECT_ROOT" || return 1
|
||||
./gradlew :conformance-test:installDist --quiet
|
||||
cd "$SCRIPT_DIR" || return 1
|
||||
echo "Build complete."
|
||||
}
|
||||
|
||||
start_server() {
|
||||
echo "Starting conformance server on port $PORT..."
|
||||
MCP_PORT="$PORT" "$SERVER_DIST" &
|
||||
SERVER_PID=$!
|
||||
|
||||
echo "Waiting for server to be ready..."
|
||||
local retries=0
|
||||
local max_retries=30
|
||||
while ! curl -sf "$SERVER_URL" > /dev/null 2>&1; do
|
||||
retries=$((retries + 1))
|
||||
if [ "$retries" -ge "$max_retries" ]; then
|
||||
echo "ERROR: Server failed to start after $max_retries attempts"
|
||||
return 1
|
||||
fi
|
||||
sleep 0.5
|
||||
done
|
||||
echo "Server is ready (PID: $SERVER_PID)."
|
||||
}
|
||||
|
||||
stop_server() {
|
||||
if [ -n "$SERVER_PID" ] && kill -0 "$SERVER_PID" 2>/dev/null; then
|
||||
echo "Stopping server (PID: $SERVER_PID)..."
|
||||
kill "$SERVER_PID" 2>/dev/null || true
|
||||
wait "$SERVER_PID" 2>/dev/null || true
|
||||
SERVER_PID=""
|
||||
fi
|
||||
}
|
||||
|
||||
run_list_scenarios() {
|
||||
local output_dir="$RESULTS_DIR/list"
|
||||
mkdir -p "$output_dir"
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " List Available Scenarios"
|
||||
echo "=========================================="
|
||||
local rc=0
|
||||
npx "@modelcontextprotocol/conformance@$CONFORMANCE_VERSION" list \
|
||||
"$@" > "$output_dir/scenarios.txt" || rc=$?
|
||||
|
||||
cat "$output_dir/scenarios.txt"
|
||||
return $rc
|
||||
}
|
||||
|
||||
run_server_suite() {
|
||||
local output_dir="$RESULTS_DIR/server"
|
||||
mkdir -p "$output_dir"
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Running SERVER conformance tests"
|
||||
echo "=========================================="
|
||||
start_server || return 1
|
||||
local rc=0
|
||||
npx "@modelcontextprotocol/conformance@$CONFORMANCE_VERSION" server \
|
||||
--url "$SERVER_URL" \
|
||||
--output-dir "$output_dir" \
|
||||
--expected-failures "$SCRIPT_DIR/conformance-baseline.yml" \
|
||||
"$@" || rc=$?
|
||||
stop_server
|
||||
return $rc
|
||||
}
|
||||
|
||||
run_client_suite() {
|
||||
local output_dir="$RESULTS_DIR/client"
|
||||
mkdir -p "$output_dir"
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Running CLIENT conformance tests"
|
||||
echo "=========================================="
|
||||
local scenarios=("initialize" "tools_call" "elicitation-sep1034-client-defaults" "sse-retry")
|
||||
local rc=0
|
||||
for scenario in "${scenarios[@]}"; do
|
||||
npx "@modelcontextprotocol/conformance@$CONFORMANCE_VERSION" client \
|
||||
--command "$CLIENT_DIST" \
|
||||
--scenario "$scenario" \
|
||||
--output-dir "$output_dir" \
|
||||
--expected-failures "$SCRIPT_DIR/conformance-baseline.yml" \
|
||||
"$@" || rc=$?
|
||||
done
|
||||
return $rc
|
||||
}
|
||||
|
||||
run_client_auth_suite() {
|
||||
local output_dir="$RESULTS_DIR/client-auth"
|
||||
mkdir -p "$output_dir"
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Running CLIENT (auth) conformance tests"
|
||||
echo "=========================================="
|
||||
local rc=0
|
||||
npx "@modelcontextprotocol/conformance@$CONFORMANCE_VERSION" client \
|
||||
--command "$CLIENT_DIST" \
|
||||
--suite auth \
|
||||
--output-dir "$output_dir" \
|
||||
--expected-failures "$SCRIPT_DIR/conformance-baseline.yml" \
|
||||
"$@" || rc=$?
|
||||
|
||||
local extra_scenarios=("auth/client-credentials-jwt" "auth/client-credentials-basic" "auth/cross-app-access-complete-flow")
|
||||
for scenario in "${extra_scenarios[@]}"; do
|
||||
npx "@modelcontextprotocol/conformance@$CONFORMANCE_VERSION" client \
|
||||
--command "$CLIENT_DIST" \
|
||||
--scenario "$scenario" \
|
||||
--output-dir "$output_dir" \
|
||||
--expected-failures "$SCRIPT_DIR/conformance-baseline.yml" \
|
||||
"$@" || rc=$?
|
||||
done
|
||||
return $rc
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Main
|
||||
# ============================================================================
|
||||
|
||||
COMMAND="${1:-}"
|
||||
shift 2>/dev/null || true
|
||||
|
||||
if [ -z "$COMMAND" ]; then
|
||||
echo "Usage: $0 <command> [extra-args...]"
|
||||
echo "Commands: list | server | client | client-auth | all"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
build
|
||||
|
||||
EXIT_CODE=0
|
||||
|
||||
case "$COMMAND" in
|
||||
list)
|
||||
run_list_scenarios "$@" || EXIT_CODE=1
|
||||
;;
|
||||
server)
|
||||
run_server_suite "$@" || EXIT_CODE=1
|
||||
;;
|
||||
client)
|
||||
run_client_suite "$@" || EXIT_CODE=1
|
||||
;;
|
||||
client-auth)
|
||||
run_client_auth_suite "$@" || EXIT_CODE=1
|
||||
;;
|
||||
all)
|
||||
run_server_suite "$@" || EXIT_CODE=1
|
||||
run_client_suite "$@" || EXIT_CODE=1
|
||||
run_client_auth_suite "$@" || EXIT_CODE=1
|
||||
;;
|
||||
*)
|
||||
echo "Unknown command: $COMMAND"
|
||||
echo "Commands: list | server | client | client-auth | all"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Results saved to: $RESULTS_DIR"
|
||||
echo "=========================================="
|
||||
|
||||
exit $EXIT_CODE
|
||||
Loading…
Reference in a new issue