1
+ name : MCP Tests
2
+
3
+ on :
4
+ pull_request :
5
+ jobs :
6
+ # ##########################################################################
7
+ # 1 - Local integration tests (always run)
8
+ # ##########################################################################
9
+ local :
10
+ runs-on : ubuntu-latest
11
+ # Dummy key lets the clients authenticate against the local servers.
12
+ env :
13
+ API_KEY : ci-test-key
14
+
15
+ steps :
16
+ - uses : actions/checkout@v4
17
+
18
+ - name : Read Python version from .python-version
19
+ id : python-version
20
+ run : |
21
+ PY_VERSION=$(cat .python-version)
22
+ echo "version=$PY_VERSION" >> $GITHUB_OUTPUT
23
+
24
+ - uses : actions/setup-python@v5
25
+ with :
26
+ python-version : ${{ steps.python-version.outputs.version }}
27
+
28
+ - name : Read Node.js version from package.json
29
+ id : node-version
30
+ run : |
31
+ NODE_VERSION=$(jq -r '.engines.node' package.json)
32
+ echo "version=$NODE_VERSION" >> $GITHUB_OUTPUT
33
+
34
+ - uses : actions/setup-node@v4
35
+ with :
36
+ node-version : ${{ steps.node-version.outputs.version }}
37
+
38
+ - uses : actions/cache@v4
39
+ with :
40
+ path : ~/.cache/pip
41
+ key : ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }}
42
+
43
+ - name : Install dependencies
44
+ run : |
45
+ python -m pip install --upgrade pip
46
+ pip install -r requirements.txt
47
+
48
+ - name : Run pytest (local transports)
49
+ run : pytest -q
50
+
51
+ # ##########################################################################
52
+ # 2 - Deploy this PR to a temp Heroku app and run tests against deployed app (in addition to 'local')
53
+ # ##########################################################################
54
+ remote :
55
+ runs-on : ubuntu-latest
56
+ env :
57
+ HEROKU_API_KEY : ${{ secrets.HEROKU_API_KEY }}
58
+ API_KEY : ci-test-key
59
+ # also note that github CI doesn't have access to your app's config vars, so here we're setting the remote
60
+ # server type to streamable HTTP. Folks using SSE would need to change this line for their e2e remote integration
61
+ # tests to test SSE instead of streamable HTTP.
62
+ REMOTE_SERVER_TRANSPORT_MODULE : streamable_http_server
63
+ # $APP_NAME is set below because we need to shorten the repo owner's name, as a precaution
64
+
65
+ steps :
66
+ - uses : actions/checkout@v4
67
+ with :
68
+ fetch-depth : 0 # <-- disables shallow clone, which heroku is upset by when running git push heroku later on
69
+
70
+ # Setting a short $APP_NAME that will be unique even if folks choose to fork this repo --> avoids clashes.
71
+ # Needs to be shortened if the github repo owner has a long name (max 30 char app name heroku limit).
72
+ - name : Generate short APP_NAME
73
+ id : appname
74
+ run : |
75
+ OWNER_SHORT=${GITHUB_REPOSITORY_OWNER:0:5}
76
+ REPO_NAME=$(basename "$GITHUB_REPOSITORY")
77
+ PR_NUMBER=$(jq .number "$GITHUB_EVENT_PATH")
78
+ APP_NAME="${OWNER_SHORT}-${REPO_NAME}-${PR_NUMBER}"
79
+ echo "APP_NAME=$APP_NAME"
80
+ echo "APP_NAME=$APP_NAME" >> $GITHUB_ENV
81
+
82
+ - name : Read Python version from .python-version
83
+ id : python-version
84
+ run : |
85
+ PY_VERSION=$(cat .python-version)
86
+ echo "version=$PY_VERSION" >> $GITHUB_OUTPUT
87
+
88
+ - uses : actions/setup-python@v5
89
+ with :
90
+ python-version : ${{ steps.python-version.outputs.version }}
91
+
92
+ - name : Read Node.js version from package.json
93
+ id : node-version
94
+ run : |
95
+ NODE_VERSION=$(jq -r '.engines.node' package.json)
96
+ echo "version=$NODE_VERSION" >> $GITHUB_OUTPUT
97
+
98
+ - uses : actions/setup-node@v4
99
+ with :
100
+ node-version : ${{ steps.node-version.outputs.version }}
101
+
102
+ - name : Install Heroku CLI
103
+ run : |
104
+ curl https://cli-assets.heroku.com/install.sh | sh
105
+
106
+ - name : Log in to Heroku
107
+ run : |
108
+ echo "$HEROKU_API_KEY" | heroku auth:token
109
+
110
+ - name : Pre-cleanup (destroy app if it exists)
111
+ continue-on-error : true
112
+ run : |
113
+ heroku apps:destroy --app $APP_NAME --confirm $APP_NAME
114
+
115
+ # github CI can't use our app.json, so the config etc bits must be set manually.
116
+ # note WEB_CONCURRENCY is important! You get non-deterministic errors w/out it.
117
+ - name : Create temp Heroku app for this PR
118
+ run : |
119
+ heroku create $APP_NAME
120
+ heroku buildpacks:add --index 1 heroku/nodejs -a $APP_NAME
121
+ heroku buildpacks:add --index 2 heroku/python -a $APP_NAME
122
+ heroku config:set API_KEY=$API_KEY --app $APP_NAME
123
+ heroku config:set STDIO_MODE_ONLY=false
124
+ heroku config:set REMOTE_SERVER_TRANSPORT_MODULE=$REMOTE_SERVER_TRANSPORT_MODULE --app $APP_NAME
125
+ heroku config:set WEB_CONCURRENCY=1 --app $APP_NAME
126
+
127
+ - name : Deploy this branch to Heroku
128
+ run : |
129
+ git push https://heroku:[email protected] /$APP_NAME.git HEAD:refs/heads/main --force
130
+
131
+ - uses : actions/cache@v4
132
+ with :
133
+ path : ~/.cache/pip
134
+ key : ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }}
135
+
136
+ - name : Install test dependencies
137
+ run : |
138
+ python -m pip install --upgrade pip
139
+ pip install -r requirements.txt
140
+
141
+ - name : Get Heroku env vars
142
+ id : heroku_env
143
+ run : |
144
+ url=$(heroku info -s -a $APP_NAME | grep web_url | cut -d= -f2 | tr -d '\n')
145
+ echo "url=$url" >> "$GITHUB_OUTPUT"
146
+
147
+ - name : Run pytest against deployed app
148
+ env :
149
+ MCP_SERVER_URL : ${{ steps.heroku_env.outputs.url }}
150
+ run : |
151
+ echo "APP_NAME = $APP_NAME"
152
+ echo "MCP_SERVER_URL = $MCP_SERVER_URL"
153
+ echo "REMOTE_SERVER_TRANSPORT_MODULE = $REMOTE_SERVER_TRANSPORT_MODULE"
154
+ echo "API_KEY is ${API_KEY:+set}" # won't print the key, just confirms it's non-empty
155
+ pytest -q
156
+
157
+ - name : Destroy Heroku app after test
158
+ if : always()
159
+ run : |
160
+ heroku apps:destroy --app $APP_NAME --confirm $APP_NAME
0 commit comments