justfile•7.16 kB
# MCP OpenAPI Monorepo - Multi-API Deployment Manager
# This justfile manages deployment and testing of multiple independent API packages
root_dir := justfile_directory()
apis_dir := root_dir + "/packages/apis"
mcp_server_dir := root_dir + "/packages/mcp-server"
infra_dir := root_dir + "/packages/infrastructure"
list-apis:
@echo "Available API packages:"
@ls -1 {{apis_dir}} | while read api; do \
if [ -f "{{apis_dir}}/$api/config.py" ]; then \
echo " - $api"; \
fi; \
done
install:
uv sync --all-extras
_cdk api +args:
@cd {{infra_dir}} && API_NAME={{api}} API_DIR=packages/apis/{{api}} cdk {{args}}
bootstrap:
@cd {{infra_dir}} && cdk bootstrap
deploy api:
@echo "Deploying {{api}}..."
@just _cdk {{api}} deploy --require-approval never
destroy api:
@echo "Destroying {{api}}..."
@just _cdk {{api}} destroy
diff api:
@just _cdk {{api}} diff
synth api:
@just _cdk {{api}} synth
status:
@aws cloudformation describe-stacks --query 'Stacks[?starts_with(StackName, `openapi-mcp-`)].{Name:StackName,Status:StackStatus,ApiUrl:Outputs[?contains(OutputKey, `ApiUrl`)].OutputValue | [0],Updated:LastUpdatedTime}' --output table
logs api:
#!/usr/bin/env bash
STACK_NAME="openapi-mcp-{{api}}"
FUNCTION_NAME=$(aws cloudformation describe-stack-resources --stack-name "$STACK_NAME" --query 'StackResources[?ResourceType==`AWS::Lambda::Function`].PhysicalResourceId' --output text)
if [ -z "$FUNCTION_NAME" ]; then
echo "Error: Could not find Lambda function in stack $STACK_NAME"
exit 1
fi
aws logs tail "/aws/lambda/$FUNCTION_NAME" --follow --format short
traces api minutes="5":
#!/usr/bin/env bash
echo "Fetching X-Ray traces for {{api}} from the last {{minutes}} minutes..."
STACK_NAME="openapi-mcp-{{api}}"
FUNCTION_NAME=$(aws cloudformation describe-stack-resources --stack-name "$STACK_NAME" --query 'StackResources[?ResourceType==`AWS::Lambda::Function`].PhysicalResourceId' --output text)
if [ -z "$FUNCTION_NAME" ]; then
echo "Error: Could not find Lambda function in stack $STACK_NAME"
exit 1
fi
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
START_TIME=$(date -u -v-{{minutes}}M +%s)
END_TIME=$(date -u +%s)
TRACE_IDS=$(aws xray get-trace-summaries \
--start-time $START_TIME \
--end-time $END_TIME \
--filter-expression "service(id(name: \"$FUNCTION_NAME\", type: \"AWS::Lambda::Function\", account.id: \"$ACCOUNT_ID\"))" \
--query 'TraceSummaries[*].Id' \
--output text)
if [ -z "$TRACE_IDS" ]; then
echo "No traces found for function: $FUNCTION_NAME"
echo "in the last {{minutes}} minutes"
exit 0
fi
echo "Found $(echo $TRACE_IDS | wc -w) trace(s) for function: $FUNCTION_NAME"
echo ""
for TRACE_ID in $TRACE_IDS; do
echo "═══════════════════════════════════════════════════════════════"
echo "Trace ID: $TRACE_ID"
echo "═══════════════════════════════════════════════════════════════"
aws xray batch-get-traces --trace-ids $TRACE_ID
echo ""
done
trace-summary api minutes="5":
#!/usr/bin/env bash
echo "X-Ray trace summary for {{api}} (last {{minutes}} minutes):"
STACK_NAME="openapi-mcp-{{api}}"
FUNCTION_NAME=$(aws cloudformation describe-stack-resources --stack-name "$STACK_NAME" --query 'StackResources[?ResourceType==`AWS::Lambda::Function`].PhysicalResourceId' --output text)
if [ -z "$FUNCTION_NAME" ]; then
echo "Error: Could not find Lambda function in stack $STACK_NAME"
exit 1
fi
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
START_TIME=$(date -u -v-{{minutes}}M +%s)
END_TIME=$(date -u +%s)
aws xray get-trace-summaries \
--start-time $START_TIME \
--end-time $END_TIME \
--filter-expression "service(id(name: \"$FUNCTION_NAME\", type: \"AWS::Lambda::Function\", account.id: \"$ACCOUNT_ID\"))" \
--query 'TraceSummaries[*].{Duration:Duration,ResponseTime:ResponseTime,Http:Http.HttpStatus}' \
--output table
docker-build api:
@echo "Building Docker image for {{api}}..."
@docker build -t mcp-{{api}} \
-f {{infra_dir}}/mcp_openapi_infrastructure/Dockerfile \
--build-arg API_DIR=packages/apis/{{api}} \
{{root_dir}}
docker-run api port="8080":
#!/usr/bin/env bash
CONTAINER_NAME="local-mcp-{{api}}"
docker stop "$CONTAINER_NAME" 2>/dev/null || true
docker run --rm -d -p {{port}}:8080 \
--name "$CONTAINER_NAME" \
mcp-{{api}}
echo "Started container: $CONTAINER_NAME on port {{port}}"
echo "Test with: curl http://localhost:{{port}}/"
docker-stop api:
#!/usr/bin/env bash
CONTAINER_NAME="local-mcp-{{api}}"
echo "Stopping container: $CONTAINER_NAME"
docker stop "$CONTAINER_NAME" 2>/dev/null || echo "Container not running"
docker-list:
#!/usr/bin/env bash
echo "Running MCP containers:"
docker ps --filter "name=local-mcp-" --format "table {{"{{.Names}}"}}\t{{"{{.Ports}}"}}\t{{"{{.Status}}"}}" || echo "No containers running"
docker-stop-all:
#!/usr/bin/env bash
echo "Stopping all MCP containers..."
CONTAINERS=$(docker ps -q --filter "name=local-mcp-")
if [ -z "$CONTAINERS" ]; then
echo "No containers to stop"
else
docker stop $CONTAINERS
echo "Stopped all containers"
fi
test-local api port="8080":
#!/usr/bin/env bash
echo "Testing {{api}} at http://localhost:{{port}}/"
uv run python -m pytest {{mcp_server_dir}}/tests/ --mcp-url=http://localhost:{{port}}/ -v
test-deployed api:
#!/usr/bin/env bash
STACK_NAME="openapi-mcp-{{api}}"
API_URL=$(aws cloudformation describe-stacks --stack-name "$STACK_NAME" --query 'Stacks[0].Outputs[?contains(OutputKey, `ApiUrl`)].OutputValue | [0]' --output text)
if [ -z "$API_URL" ]; then
echo "Error: Could not find API URL in stack $STACK_NAME"
exit 1
fi
echo "Testing {{api}} at $API_URL"
uv run python -m pytest {{mcp_server_dir}}/tests/ --mcp-url="$API_URL" -v
test-full api:
#!/usr/bin/env bash
set -e
echo "Running full test cycle for {{api}}..."
just docker-build {{api}}
just docker-run {{api}}
sleep 3
just test-local {{api}}
EXIT_CODE=$?
just docker-stop {{api}}
exit $EXIT_CODE
deploy-and-test api:
#!/usr/bin/env bash
set -e
echo "Deploying and testing {{api}}..."
just deploy {{api}}
sleep 5
just test-deployed {{api}}
curl-test api port="8080":
#!/usr/bin/env bash
echo "Testing tools/list endpoint..."
curl -X POST http://localhost:{{port}}/ \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "method": "tools/list", "id": 1}' | jq .