deploy-mcp 0.1.0 → 0.2.0

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.
Files changed (3) hide show
  1. package/README.md +90 -5
  2. package/dist/worker.js +388 -19
  3. package/package.json +5 -4
package/README.md CHANGED
@@ -195,6 +195,75 @@ All AI tools (except Continue.dev) use the same configuration:
195
195
 
196
196
  💡 Replace `your-vercel-token` with your actual Vercel API token from [vercel.com/account/tokens](https://vercel.com/account/tokens)
197
197
 
198
+ ## Deployment Status Badges
199
+
200
+ Get live deployment status badges for your repositories that update in real-time via webhooks.
201
+
202
+ ### Badge URLs
203
+
204
+ Add these badges to your README to show live deployment status:
205
+
206
+ ```markdown
207
+ ![Vercel Deployment](https://deploy-mcp.io/badge/username/repository/vercel)
208
+ ```
209
+
210
+ **Examples:**
211
+ - `https://deploy-mcp.io/badge/john/my-app/vercel`
212
+ - `https://deploy-mcp.io/badge/youruser/yourrepo/vercel`
213
+
214
+ **⚠️ Requirements:**
215
+ - **Public repositories only** - Private repos not supported for security reasons
216
+ - **Vercel Pro/Enterprise plan** - Webhooks required for real-time badge updates
217
+
218
+ **For free tier users:** The MCP server works perfectly for checking deployment status in your AI conversations. Badges are a premium feature requiring paid Vercel plans.
219
+
220
+ ### Setting Up Webhooks
221
+
222
+ For badges to show real-time status, configure webhooks in your deployment platform:
223
+
224
+ #### Vercel Setup
225
+
226
+ **Note:** Vercel webhooks require a Pro or Enterprise plan.
227
+
228
+ 1. **Go to your Vercel team settings**
229
+ 2. **Navigate to "Webhooks" section**
230
+ 3. **Click "Create Webhook"**
231
+ 4. **Configure the webhook:**
232
+ - **URL**: `https://deploy-mcp.io/webhook/yourusername/yourrepo/vercel`
233
+ - **Events**: Select "Deployment Created", "Deployment Ready", and "Deployment Error"
234
+ - **Projects**: Choose your specific project or leave empty for all projects
235
+ - **Secret**: Leave empty (not required for public repositories)
236
+ 5. **Save the webhook**
237
+
238
+ **Important:** Replace `yourusername` and `yourrepo` with your actual GitHub username and repository name.
239
+
240
+ **Note:** Vercel's free tier doesn't support webhooks, so badges won't work. Use the MCP server instead for deployment status in your AI conversations.
241
+
242
+ #### Supported Events
243
+
244
+ The webhook will trigger on:
245
+ - ✅ Deployment started (badge shows "building")
246
+ - ✅ Deployment succeeded (badge shows "success")
247
+ - ❌ Deployment failed (badge shows "failed")
248
+ - ⚠️ Deployment errored (badge shows "error")
249
+
250
+ #### Badge Status Colors
251
+
252
+ - 🟢 **Success** - Green badge when deployment is live
253
+ - 🔴 **Failed** - Red badge when deployment failed
254
+ - 🟡 **Building** - Yellow badge during deployment
255
+ - ⚫ **Unknown** - Grey badge when no status available
256
+ - 🔴 **Error** - Red badge when deployment errored
257
+
258
+ ### Testing Your Setup
259
+
260
+ 1. **Add the badge** to your repository README
261
+ 2. **Configure the webhook** as described above
262
+ 3. **Make a commit** and push to trigger a deployment
263
+ 4. **Watch the badge** update in real-time during deployment
264
+
265
+ **Initial badge status will show "unknown" until the first webhook is received.**
266
+
198
267
 
199
268
  ### Getting API Tokens
200
269
 
@@ -232,14 +301,13 @@ Render integration is on our roadmap. [Star the repo](https://github.com/alexpot
232
301
 
233
302
  ## Usage Examples
234
303
 
235
- ### In Your AI Conversation
304
+ ### MCP Server Usage
236
305
 
237
- Once configured, simply ask your AI assistant:
306
+ Once configured with your AI assistant, simply ask:
238
307
 
239
308
  ```
240
309
  You: Check my latest Vercel deployment
241
310
 
242
-
243
311
  Assistant: I'll check your latest Vercel deployment for you.
244
312
 
245
313
  ✅ **Status**: Success
@@ -256,13 +324,30 @@ Assistant: I'll check your latest Vercel deployment for you.
256
324
  Everything looks good - your deployment is live and running successfully!
257
325
  ```
258
326
 
259
- ### More Examples
260
-
327
+ **More examples:**
261
328
  - `"Is my website deployment finished?"`
262
329
  - `"Show me the deployment status for project-xyz"`
263
330
  - `"Check if the production deployment succeeded"`
264
331
  - `"What's the status of my latest deploy?"`
265
332
 
333
+ ### Badge Usage
334
+
335
+ Add live deployment status badges to your README:
336
+
337
+ ```markdown
338
+ # My Project
339
+
340
+ ![Vercel](https://deploy-mcp.io/badge/yourusername/yourrepo/vercel)
341
+
342
+ <!-- Other content -->
343
+ ```
344
+
345
+ **Multiple platforms:**
346
+ ```markdown
347
+ [![Vercel](https://deploy-mcp.io/badge/user/repo/vercel)](https://deploy-mcp.io)
348
+ [![Netlify](https://deploy-mcp.io/badge/user/repo/netlify)](https://deploy-mcp.io)
349
+ ```
350
+
266
351
 
267
352
  ## MCP Tools Reference
268
353
  Retrieve the latest deployment status for a project.
package/dist/worker.js CHANGED
@@ -3,6 +3,129 @@ import {
3
3
  VercelAdapter
4
4
  } from "./chunk-QRZL43CY.js";
5
5
 
6
+ // src/utils/github.ts
7
+ async function validateRepository(user, repo) {
8
+ try {
9
+ const response = await fetch(
10
+ `https://api.github.com/repos/${user}/${repo}`,
11
+ {
12
+ headers: {
13
+ "User-Agent": "deploy-mcp/1.0",
14
+ Accept: "application/vnd.github.v3+json"
15
+ }
16
+ }
17
+ );
18
+ if (!response.ok) {
19
+ return { exists: false, isPublic: false };
20
+ }
21
+ const repoData = await response.json();
22
+ const isPublic = !repoData.private;
23
+ return {
24
+ exists: true,
25
+ isPublic,
26
+ name: repoData.name,
27
+ fullName: repoData.full_name
28
+ };
29
+ } catch {
30
+ return { exists: false, isPublic: false };
31
+ }
32
+ }
33
+ function validateParams(user, repo, platform) {
34
+ const validName = /^[a-zA-Z0-9._-]+$/;
35
+ const validPlatforms = ["vercel", "netlify", "railway"];
36
+ return validName.test(user) && validName.test(repo) && validPlatforms.includes(platform);
37
+ }
38
+
39
+ // src/server/badge.ts
40
+ var STATUS_COLORS = {
41
+ success: "brightgreen",
42
+ failed: "red",
43
+ building: "yellow",
44
+ error: "lightgrey",
45
+ unknown: "lightgrey"
46
+ };
47
+ var PLATFORM_CONFIG = {
48
+ vercel: {
49
+ label: "Vercel",
50
+ logo: "vercel"
51
+ },
52
+ netlify: {
53
+ label: "Netlify",
54
+ logo: "netlify"
55
+ },
56
+ railway: {
57
+ label: "Railway",
58
+ logo: "railway"
59
+ }
60
+ };
61
+ function validateParams2(user, repo, platform) {
62
+ const validPlatforms = Object.keys(PLATFORM_CONFIG);
63
+ return validateParams(user, repo, platform) && user.length <= 50 && repo.length <= 100 && validPlatforms.includes(platform);
64
+ }
65
+ async function getDeploymentStatus(user, repo, platform, env) {
66
+ if (!env.BADGE_KV) {
67
+ return "unknown";
68
+ }
69
+ try {
70
+ const key = `${user}/${repo}/${platform}`;
71
+ const status = await env.BADGE_KV.get(key, "text");
72
+ if (!status) return "unknown";
73
+ const validStatuses = [
74
+ "success",
75
+ "failed",
76
+ "building",
77
+ "error",
78
+ "unknown"
79
+ ];
80
+ return validStatuses.includes(status) ? status : "unknown";
81
+ } catch {
82
+ return "error";
83
+ }
84
+ }
85
+ async function generateBadgeData(user, repo, platform, env) {
86
+ if (!validateParams2(user, repo, platform)) {
87
+ throw new Error("Invalid parameters");
88
+ }
89
+ const repoInfo = await validateRepository(user, repo);
90
+ if (!repoInfo.exists || !repoInfo.isPublic) {
91
+ return generateErrorBadge(platform, "Repository not found or private");
92
+ }
93
+ const status = await getDeploymentStatus(user, repo, platform, env);
94
+ const config = PLATFORM_CONFIG[platform];
95
+ const messages = {
96
+ success: "deployed",
97
+ failed: "failed",
98
+ building: "deploying",
99
+ error: "error",
100
+ unknown: "unknown"
101
+ };
102
+ return {
103
+ schemaVersion: 1,
104
+ label: config.label,
105
+ message: messages[status],
106
+ color: STATUS_COLORS[status]
107
+ };
108
+ }
109
+ function generateErrorBadge(platform, message) {
110
+ const config = PLATFORM_CONFIG[platform];
111
+ return {
112
+ schemaVersion: 1,
113
+ label: config ? config.label : platform,
114
+ message,
115
+ color: "lightgrey"
116
+ };
117
+ }
118
+ async function updateDeploymentStatus(user, repo, platform, status, env) {
119
+ if (!env.BADGE_KV) {
120
+ throw new Error("KV storage not configured");
121
+ }
122
+ const key = `${user}/${repo}/${platform}`;
123
+ await env.BADGE_KV.put(key, status, {
124
+ expirationTtl: 86400 * 30
125
+ // Expire after 30 days
126
+ });
127
+ }
128
+
6
129
  // src/landing-page.ts
7
130
  var landingPageHTML = `<!DOCTYPE html>
8
131
  <html lang="en">
@@ -595,7 +718,8 @@ var landingPageHTML = `<!DOCTYPE html>
595
718
  }
596
719
 
597
720
  .platform.supported .platform-icon {
598
- background: linear-gradient(135deg, var(--accent-green), #34d399);
721
+ background: rgba(255, 255, 255, 0.1);
722
+ border: 2px solid var(--accent-green);
599
723
  }
600
724
 
601
725
  .platform.coming-soon {
@@ -896,28 +1020,46 @@ var landingPageHTML = `<!DOCTYPE html>
896
1020
 
897
1021
  <div class="badge-showcase">
898
1022
  <div class="badge-preview">
899
- <div class="badge-example">
900
- <div class="badge-status success"></div>
901
- <span>deploy: success</span>
902
- </div>
903
- <div class="badge-example">
904
- <div class="badge-status building"></div>
905
- <span>deploy: building</span>
906
- </div>
907
- <div class="badge-example">
908
- <div class="badge-status error"></div>
909
- <span>deploy: failed</span>
1023
+ <h3 style="margin-bottom: 1rem; color: var(--text-primary); font-size: 1.1rem;">Live Examples:</h3>
1024
+ <div style="display: flex; flex-direction: column; gap: 0.75rem; margin-bottom: 1.5rem;">
1025
+ <div style="display: flex; align-items: center;">
1026
+ <img src="https://img.shields.io/endpoint?url=https://deploy-mcp.io/badge/microsoft/vscode/vercel" alt="Vercel Deploy Status" style="margin-right: 0.75rem;" />
1027
+ <span style="color: var(--text-secondary); font-size: 0.9rem;">microsoft/vscode on Vercel</span>
1028
+ </div>
1029
+ <div style="display: flex; align-items: center;">
1030
+ <img src="https://img.shields.io/endpoint?url=https://deploy-mcp.io/badge/facebook/react/vercel" alt="Netlify Deploy Status" style="margin-right: 0.75rem;" />
1031
+ <span style="color: var(--text-secondary); font-size: 0.9rem;">facebook/react on Netlify</span>
1032
+ </div>
1033
+ <div style="display: flex; align-items: center;">
1034
+ <img src="https://img.shields.io/endpoint?url=https://deploy-mcp.io/badge/angular/angular/vercel" alt="Railway Deploy Status" style="margin-right: 0.75rem;" />
1035
+ <span style="color: var(--text-secondary); font-size: 0.9rem;">angular/angular on Railway</span>
1036
+ </div>
910
1037
  </div>
911
1038
  </div>
912
1039
  <div class="code-wrapper multiline">
913
1040
  <div class="code-header">
914
- <button class="copy-btn" onclick="copyToClipboard('[![Deploy Status](https://deploy-mcp.io/badge/vercel/your-project)](https://deploy-mcp.io)', event)">Copy</button>
1041
+ <button class="copy-btn" onclick="copyToClipboard('[![Deploy Status](https://img.shields.io/endpoint?url=https://deploy-mcp.io/badge/user/repo/vercel)](https://deploy-mcp.io)', event)">Copy</button>
915
1042
  </div>
916
- <div class="code-content">[![Deploy Status](https://deploy-mcp.io/badge/vercel/your-project)](https://deploy-mcp.io)</div>
1043
+ <div class="code-content">[![Deploy Status](https://img.shields.io/endpoint?url=https://deploy-mcp.io/badge/user/repo/vercel)](https://deploy-mcp.io)</div>
917
1044
  </div>
918
1045
  </div>
919
1046
  <div style="text-align: center; margin-top: 1.5rem;">
920
- <p style="color: var(--accent-orange); font-size: 1rem; font-weight: 600;">\u{1F680} Coming Soon</p>
1047
+ <p style="color: var(--accent-green); font-size: 1rem; font-weight: 600;">\u2705 Now Available</p>
1048
+ <p style="color: var(--text-secondary); font-size: 0.9rem; margin-top: 0.5rem;">
1049
+ Supports Vercel \u2022 Netlify and Railway coming soon
1050
+ </p>
1051
+ </div>
1052
+
1053
+ <div style="margin-top: 2rem; padding: 1.5rem; background: var(--bg-secondary); border-radius: 8px; border-left: 3px solid var(--accent-blue);">
1054
+ <h4 style="margin: 0 0 1rem 0; color: var(--text-primary); font-size: 1rem;">Quick Setup:</h4>
1055
+ <ol style="margin: 0; padding-left: 1.5rem; color: var(--text-secondary); line-height: 1.6;">
1056
+ <li>Add badge to your README (replace user/repo in URL above)</li>
1057
+ <li>Configure webhook in your deployment platform</li>
1058
+ <li>Badge updates automatically on each deployment</li>
1059
+ </ol>
1060
+ <p style="margin: 1rem 0 0 0; color: var(--text-secondary); font-size: 0.9rem;">
1061
+ \u{1F4D6} <strong>Detailed setup instructions:</strong> <a href="https://github.com/alexpota/deploy-mcp#deployment-status-badges" style="color: var(--accent-blue); text-decoration: none;">View README</a>
1062
+ </p>
921
1063
  </div>
922
1064
  </div>
923
1065
 
@@ -929,25 +1071,33 @@ var landingPageHTML = `<!DOCTYPE html>
929
1071
  <div class="platforms">
930
1072
  <div class="platform supported">
931
1073
  <div class="platform-status"></div>
932
- <div class="platform-icon">V</div>
1074
+ <div class="platform-icon">
1075
+ <img src="https://vercel.com/favicon.ico" alt="Vercel" style="width: 24px; height: 24px;">
1076
+ </div>
933
1077
  <h3>Vercel</h3>
934
1078
  <p>Full deployment tracking with real-time status updates</p>
935
1079
  </div>
936
1080
  <div class="platform coming-soon">
937
1081
  <div class="platform-status"></div>
938
- <div class="platform-icon">N</div>
1082
+ <div class="platform-icon">
1083
+ <img src="https://www.netlify.com/favicon.ico" alt="Netlify" style="width: 24px; height: 24px;">
1084
+ </div>
939
1085
  <h3>Netlify</h3>
940
1086
  <p>Coming in next release</p>
941
1087
  </div>
942
1088
  <div class="platform coming-soon">
943
1089
  <div class="platform-status"></div>
944
- <div class="platform-icon">R</div>
1090
+ <div class="platform-icon">
1091
+ <img src="https://railway.app/favicon.ico" alt="Railway" style="width: 24px; height: 24px;">
1092
+ </div>
945
1093
  <h3>Railway</h3>
946
1094
  <p>On the roadmap</p>
947
1095
  </div>
948
1096
  <div class="platform coming-soon">
949
1097
  <div class="platform-status"></div>
950
- <div class="platform-icon">R</div>
1098
+ <div class="platform-icon">
1099
+ <img src="https://us1.discourse-cdn.com/flex016/uploads/render/original/2X/a/ad2cd49c57c27455f695b61f3f8a01571697b336.svg" alt="Render" style="width: 24px; height: 24px;">
1100
+ </div>
951
1101
  <h3>Render</h3>
952
1102
  <p>On the roadmap</p>
953
1103
  </div>
@@ -1153,6 +1303,154 @@ var landingPageHTML = `<!DOCTYPE html>
1153
1303
  </body>
1154
1304
  </html>`;
1155
1305
 
1306
+ // src/server/webhook.ts
1307
+ async function validateWebhookSignature(request, platform, env) {
1308
+ if (platform === "vercel") {
1309
+ const signature = request.headers.get("x-vercel-signature");
1310
+ if (!signature) {
1311
+ return false;
1312
+ }
1313
+ const secret = env.VERCEL_WEBHOOK_SECRET;
1314
+ if (!secret) {
1315
+ console.log(
1316
+ "VERCEL_WEBHOOK_SECRET not configured, skipping signature validation"
1317
+ );
1318
+ return true;
1319
+ }
1320
+ try {
1321
+ const body = await request.clone().text();
1322
+ const encoder = new TextEncoder();
1323
+ const keyData = encoder.encode(secret);
1324
+ const bodyData = encoder.encode(body);
1325
+ const cryptoKey = await crypto.subtle.importKey(
1326
+ "raw",
1327
+ keyData,
1328
+ { name: "HMAC", hash: "SHA-256" },
1329
+ false,
1330
+ ["sign"]
1331
+ );
1332
+ const signatureBuffer = await crypto.subtle.sign(
1333
+ "HMAC",
1334
+ cryptoKey,
1335
+ bodyData
1336
+ );
1337
+ const computedSignature = Array.from(new Uint8Array(signatureBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
1338
+ const expectedSignature = `sha256=${computedSignature}`;
1339
+ return signature === expectedSignature;
1340
+ } catch (error) {
1341
+ console.error("Webhook signature validation error:", error);
1342
+ return false;
1343
+ }
1344
+ }
1345
+ return true;
1346
+ }
1347
+ function mapDeploymentStatus(platform, payload) {
1348
+ switch (platform) {
1349
+ case "vercel": {
1350
+ const vercelPayload = payload;
1351
+ switch (vercelPayload.deployment?.state) {
1352
+ case "READY":
1353
+ return "success";
1354
+ case "BUILDING":
1355
+ return "building";
1356
+ case "ERROR":
1357
+ return "failed";
1358
+ case "CANCELED":
1359
+ return "failed";
1360
+ default:
1361
+ return "unknown";
1362
+ }
1363
+ }
1364
+ case "netlify": {
1365
+ const netlifyPayload = payload;
1366
+ switch (netlifyPayload.state) {
1367
+ case "ready":
1368
+ return "success";
1369
+ case "building":
1370
+ return "building";
1371
+ case "error":
1372
+ return "failed";
1373
+ default:
1374
+ return "unknown";
1375
+ }
1376
+ }
1377
+ case "railway":
1378
+ return "unknown";
1379
+ default:
1380
+ return "unknown";
1381
+ }
1382
+ }
1383
+ async function handleWebhook(request, user, repo, platform, env) {
1384
+ console.log(`Processing webhook: ${user}/${repo}/${platform}`);
1385
+ if (!validateParams(user, repo, platform)) {
1386
+ return {
1387
+ success: false,
1388
+ message: "Invalid parameters",
1389
+ error: "User, repo, or platform format is invalid"
1390
+ };
1391
+ }
1392
+ const repoValidation = await validateRepository(user, repo);
1393
+ if (!repoValidation.exists) {
1394
+ return {
1395
+ success: false,
1396
+ message: "Repository not found",
1397
+ error: `Repository ${user}/${repo} does not exist or is not accessible`
1398
+ };
1399
+ }
1400
+ if (!repoValidation.isPublic) {
1401
+ return {
1402
+ success: false,
1403
+ message: "Private repository",
1404
+ error: "Badge updates are only supported for public repositories"
1405
+ };
1406
+ }
1407
+ const isValidSignature = await validateWebhookSignature(
1408
+ request,
1409
+ platform,
1410
+ env
1411
+ );
1412
+ if (!isValidSignature) {
1413
+ return {
1414
+ success: false,
1415
+ message: "Invalid webhook",
1416
+ error: "Webhook validation failed"
1417
+ };
1418
+ }
1419
+ let payload;
1420
+ try {
1421
+ payload = await request.json();
1422
+ console.log(
1423
+ `${platform} webhook payload:`,
1424
+ JSON.stringify(payload, null, 2)
1425
+ );
1426
+ } catch {
1427
+ return {
1428
+ success: false,
1429
+ message: "Invalid JSON payload",
1430
+ error: "Could not parse webhook payload"
1431
+ };
1432
+ }
1433
+ const deploymentStatus = mapDeploymentStatus(platform, payload);
1434
+ console.log(`Mapped deployment status: ${deploymentStatus}`);
1435
+ try {
1436
+ await updateDeploymentStatus(user, repo, platform, deploymentStatus, env);
1437
+ console.log(
1438
+ `Updated KV storage: ${user}/${repo}/${platform} = ${deploymentStatus}`
1439
+ );
1440
+ return {
1441
+ success: true,
1442
+ message: `Deployment status updated to: ${deploymentStatus}`
1443
+ };
1444
+ } catch (error) {
1445
+ console.error("Failed to update KV storage:", error);
1446
+ return {
1447
+ success: false,
1448
+ message: "Failed to update deployment status",
1449
+ error: error instanceof Error ? error.message : "Unknown KV error"
1450
+ };
1451
+ }
1452
+ }
1453
+
1156
1454
  // src/worker.ts
1157
1455
  function getAdapters(_env) {
1158
1456
  const adapters = /* @__PURE__ */ new Map();
@@ -1189,6 +1487,77 @@ var worker_default = {
1189
1487
  );
1190
1488
  }
1191
1489
  }
1490
+ if (request.method === "GET" && url.pathname.startsWith("/badge/")) {
1491
+ try {
1492
+ const pathParts = url.pathname.split("/");
1493
+ if (pathParts.length !== 5) {
1494
+ const errorBadge = generateErrorBadge("deploy", "Invalid URL format");
1495
+ return new Response(JSON.stringify(errorBadge), {
1496
+ status: 400,
1497
+ headers: {
1498
+ "Content-Type": "application/json",
1499
+ "Cache-Control": "public, max-age=60",
1500
+ "Access-Control-Allow-Origin": "*"
1501
+ }
1502
+ });
1503
+ }
1504
+ const [, , user, repo, platform] = pathParts;
1505
+ const badgeData = await generateBadgeData(user, repo, platform, env);
1506
+ return new Response(JSON.stringify(badgeData), {
1507
+ headers: {
1508
+ "Content-Type": "application/json",
1509
+ "Cache-Control": "public, max-age=300",
1510
+ // Cache for 5 minutes
1511
+ "Access-Control-Allow-Origin": "*"
1512
+ }
1513
+ });
1514
+ } catch (error) {
1515
+ const errorBadge = generateErrorBadge(
1516
+ "deploy",
1517
+ error instanceof Error ? error.message : "Unknown error"
1518
+ );
1519
+ return new Response(JSON.stringify(errorBadge), {
1520
+ status: 500,
1521
+ headers: {
1522
+ "Content-Type": "application/json",
1523
+ "Cache-Control": "public, max-age=60",
1524
+ "Access-Control-Allow-Origin": "*"
1525
+ }
1526
+ });
1527
+ }
1528
+ }
1529
+ if (request.method === "POST" && url.pathname.startsWith("/webhook/")) {
1530
+ try {
1531
+ const pathParts = url.pathname.split("/");
1532
+ if (pathParts.length !== 5) {
1533
+ return new Response("Invalid webhook URL format", { status: 400 });
1534
+ }
1535
+ const [, , user, repo, platform] = pathParts;
1536
+ const result = await handleWebhook(request, user, repo, platform, env);
1537
+ return new Response(JSON.stringify(result), {
1538
+ status: result.success ? 200 : 400,
1539
+ headers: {
1540
+ "Content-Type": "application/json",
1541
+ "Access-Control-Allow-Origin": "*"
1542
+ }
1543
+ });
1544
+ } catch (error) {
1545
+ console.error("Webhook error:", error);
1546
+ return new Response(
1547
+ JSON.stringify({
1548
+ success: false,
1549
+ error: error instanceof Error ? error.message : "Unknown error"
1550
+ }),
1551
+ {
1552
+ status: 500,
1553
+ headers: {
1554
+ "Content-Type": "application/json",
1555
+ "Access-Control-Allow-Origin": "*"
1556
+ }
1557
+ }
1558
+ );
1559
+ }
1560
+ }
1192
1561
  if (request.method === "OPTIONS") {
1193
1562
  return new Response(null, {
1194
1563
  headers: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deploy-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Universal deployment tracker for AI assistants",
5
5
  "keywords": [
6
6
  "mcp",
@@ -37,6 +37,7 @@
37
37
  "dev:worker": "wrangler dev",
38
38
  "build": "tsup src/index.ts src/worker.ts --format esm --platform node --target node22",
39
39
  "test": "vitest",
40
+ "test:ci": "vitest --run",
40
41
  "lint": "eslint src --ext .ts",
41
42
  "lint:fix": "eslint src --ext .ts --fix",
42
43
  "type-check": "tsc --noEmit",
@@ -52,7 +53,7 @@
52
53
  "zod": "^3.24.1"
53
54
  },
54
55
  "devDependencies": {
55
- "@cloudflare/workers-types": "^4.20250109.0",
56
+ "@cloudflare/workers-types": "^4.20250803.0",
56
57
  "@types/node": "^22.10.5",
57
58
  "@typescript-eslint/eslint-plugin": "^8.38.0",
58
59
  "@typescript-eslint/parser": "^8.38.0",
@@ -64,8 +65,8 @@
64
65
  "prettier": "^3.6.2",
65
66
  "tsup": "^8.3.5",
66
67
  "tsx": "^4.19.2",
67
- "typescript": "^5.7.3",
68
- "vitest": "^2.1.8",
68
+ "typescript": "^5.8.3",
69
+ "vitest": "^3.2.4",
69
70
  "wrangler": "^4.27.0"
70
71
  },
71
72
  "engines": {