@suiflex/suitest-mcp 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suiflex/suitest-mcp",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -12,7 +12,10 @@ Every tool takes a single ``config_path`` argument and returns the structured
12
12
  from __future__ import annotations
13
13
 
14
14
  import json
15
+ import os
15
16
  import sys
17
+ import urllib.error
18
+ import urllib.request
16
19
  from typing import TYPE_CHECKING, TextIO
17
20
 
18
21
  from suitest_lifecycle.tools import KWARG_TOOLS, TOOLS
@@ -147,7 +150,7 @@ def handle(message: dict[str, object]) -> dict[str, object] | None:
147
150
  {
148
151
  "protocolVersion": PROTOCOL_VERSION,
149
152
  "capabilities": {"tools": {}},
150
- "serverInfo": {"name": "suitest-lifecycle", "version": "0.1.1"},
153
+ "serverInfo": {"name": "suitest-lifecycle", "version": "0.1.2"},
151
154
  },
152
155
  )
153
156
  if method in ("notifications/initialized", "initialized"):
@@ -194,7 +197,39 @@ def handle(message: dict[str, object]) -> dict[str, object] | None:
194
197
  return None
195
198
 
196
199
 
200
+ def verify_credentials() -> str | None:
201
+ """Check SUITEST_API_URL + SUITEST_API_KEY; return an error string if unusable.
202
+
203
+ Both must be set, and the key must authenticate against the URL
204
+ (``GET /api/v1/api-keys/whoami`` — the key pins the workspace/project every
205
+ tool publishes into). Any failure must abort the connection: a server that
206
+ accepts empty or mismatched credentials silently drops all publishes.
207
+ """
208
+ api_url = os.environ.get("SUITEST_API_URL", "").strip().rstrip("/")
209
+ api_key = os.environ.get("SUITEST_API_KEY", "").strip()
210
+ if not api_url or not api_key:
211
+ return (
212
+ "SUITEST_API_URL and SUITEST_API_KEY are both required "
213
+ "(set them in the mcpServers env block); refusing to start"
214
+ )
215
+ req = urllib.request.Request(
216
+ f"{api_url}/api/v1/api-keys/whoami",
217
+ headers={"Authorization": f"Bearer {api_key}"},
218
+ )
219
+ try:
220
+ with urllib.request.urlopen(req, timeout=10):
221
+ return None
222
+ except urllib.error.HTTPError as exc:
223
+ return f"SUITEST_API_KEY rejected by {api_url} (HTTP {exc.code}); refusing to start"
224
+ except (urllib.error.URLError, OSError) as exc:
225
+ return f"SUITEST_API_URL {api_url} unreachable ({exc}); refusing to start"
226
+
227
+
197
228
  def serve(stdin: TextIO = sys.stdin, stdout: TextIO = sys.stdout) -> None:
229
+ error = verify_credentials()
230
+ if error is not None:
231
+ sys.stderr.write(f"suitest-mcp: {error}\n")
232
+ raise SystemExit(1)
198
233
  for line in stdin:
199
234
  line = line.strip()
200
235
  if not line: