deploy-mcp 0.4.0 → 0.5.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.
package/README.md CHANGED
@@ -116,6 +116,8 @@ You can use **multiple platforms simultaneously** by providing tokens for each p
116
116
  - `"What's the status of my latest Vercel deployment?"`
117
117
  - `"Show me Vercel deployment logs"`
118
118
  - `"Watch my Vercel deployment progress"`
119
+ - `"List all my Vercel projects"`
120
+ - `"Show last 5 deployments for project-name"`
119
121
 
120
122
  4. **Required permissions:** Read access to deployments and projects
121
123
 
@@ -146,6 +148,8 @@ You can use **multiple platforms simultaneously** by providing tokens for each p
146
148
  - `"What's the status of my latest Netlify deployment?"`
147
149
  - `"Show me Netlify deployment logs"`
148
150
  - `"Watch my Netlify deployment progress"`
151
+ - `"List all my Netlify sites"`
152
+ - `"Show deployment history for site-name"`
149
153
 
150
154
  4. **Required permissions:** Read access to sites and deploys
151
155
 
@@ -338,10 +342,11 @@ deploy-mcp provides these tools to your AI assistant:
338
342
 
339
343
  | Tool | Description | Example Command |
340
344
  |------|-------------|-----------------|
341
- | `check_deployment_status` | Get latest deployment status | *"Check my deployment status"* |
345
+ | `check_deployment_status` | Get latest deployment status or history | *"Check my deployment status"* / *"Show last 5 deployments"* |
342
346
  | `watch_deployment` | Monitor deployment in real-time | *"Watch my deployment progress"* |
343
347
  | `compare_deployments` | Compare recent deployments | *"Compare my last 2 deployments"* |
344
348
  | `get_deployment_logs` | Fetch deployment logs | *"Show me deployment logs"* |
349
+ | `list_projects` | List all available projects | *"List my Vercel projects"* / *"Show all Netlify sites"* |
345
350
 
346
351
  ### Platform-Specific Usage
347
352
 
@@ -349,9 +354,30 @@ Commands work across all configured platforms:
349
354
 
350
355
  ```
351
356
  "Check my Vercel deployment for my-app"
357
+ "Show last 10 deployments for my-app on Vercel"
358
+ "List all my Vercel projects"
352
359
  "Check my Netlify deployment for my-site"
353
360
  "Show me logs for deployment abc123 on Vercel"
354
361
  "Watch my Netlify deployment progress"
362
+ "Show all my Netlify sites"
363
+ ```
364
+
365
+ ### New Features
366
+
367
+ #### List Projects
368
+ Discover all your projects/sites across platforms:
369
+ ```
370
+ "List my Vercel projects"
371
+ "Show all Netlify sites"
372
+ "What projects do I have on Vercel?"
373
+ ```
374
+
375
+ #### Deployment History
376
+ View multiple recent deployments at once:
377
+ ```
378
+ "Show last 5 deployments for my-app"
379
+ "Get deployment history for my-site"
380
+ "Check last 10 deployments on Vercel"
355
381
  ```
356
382
 
357
383
  The AI will automatically use the correct platform based on:
@@ -4,7 +4,8 @@ var SUPPORTED_PLATFORMS = ["vercel", "netlify"];
4
4
  var checkDeploymentStatusSchema = z.object({
5
5
  platform: z.enum(SUPPORTED_PLATFORMS).describe("The deployment platform"),
6
6
  project: z.string().describe("The project name or ID"),
7
- token: z.string().optional().describe("API token for authentication (optional if set in environment)")
7
+ token: z.string().optional().describe("API token for authentication (optional if set in environment)"),
8
+ limit: z.number().min(1).max(20).default(1).describe("Number of recent deployments to return (default: 1, max: 20)")
8
9
  });
9
10
  var watchDeploymentSchema = z.object({
10
11
  platform: z.enum(SUPPORTED_PLATFORMS).describe("The deployment platform"),
@@ -42,6 +43,11 @@ var getDeploymentLogsSchema = z.object({
42
43
  filter: z.enum(["error", "warning", "all"]).default("error").describe("Filter logs by type (default: error)"),
43
44
  token: z.string().optional().describe("API token for authentication (optional if set in environment)")
44
45
  });
46
+ var listProjectsSchema = z.object({
47
+ platform: z.enum(SUPPORTED_PLATFORMS).describe("The deployment platform"),
48
+ limit: z.number().min(1).max(100).default(20).describe("Maximum number of projects to return (default: 20, max: 100)"),
49
+ token: z.string().optional().describe("API token for authentication (optional if set in environment)")
50
+ });
45
51
  var tools = [
46
52
  {
47
53
  name: "check_deployment_status",
@@ -61,6 +67,13 @@ var tools = [
61
67
  token: {
62
68
  type: "string",
63
69
  description: "API token for authentication (optional if set in environment)"
70
+ },
71
+ limit: {
72
+ type: "number",
73
+ minimum: 1,
74
+ maximum: 20,
75
+ default: 1,
76
+ description: "Number of recent deployments to return (default: 1, max: 20)"
64
77
  }
65
78
  },
66
79
  required: ["platform", "project"]
@@ -176,6 +189,32 @@ var tools = [
176
189
  },
177
190
  required: ["platform", "deploymentId"]
178
191
  }
192
+ },
193
+ {
194
+ name: "list_projects",
195
+ description: "List all available projects/sites on a platform that you have access to",
196
+ inputSchema: {
197
+ type: "object",
198
+ properties: {
199
+ platform: {
200
+ type: "string",
201
+ enum: ["vercel", "netlify"],
202
+ description: "The deployment platform"
203
+ },
204
+ limit: {
205
+ type: "number",
206
+ minimum: 1,
207
+ maximum: 100,
208
+ default: 20,
209
+ description: "Maximum number of projects to return (default: 20, max: 100)"
210
+ },
211
+ token: {
212
+ type: "string",
213
+ description: "API token for authentication (optional if set in environment)"
214
+ }
215
+ },
216
+ required: ["platform"]
217
+ }
179
218
  }
180
219
  ];
181
220
 
@@ -317,7 +356,7 @@ var API_CONFIG = {
317
356
  DEFAULT_DEPLOYMENT_LIMIT: 10,
318
357
  SINGLE_DEPLOYMENT_LIMIT: 1
319
358
  };
320
- var PLATFORM_NAMES = {
359
+ var PLATFORM = {
321
360
  VERCEL: "vercel",
322
361
  NETLIFY: "netlify",
323
362
  RAILWAY: "railway",
@@ -336,6 +375,23 @@ var VERCEL_STATES = {
336
375
  INITIALIZING: "INITIALIZING",
337
376
  QUEUED: "QUEUED"
338
377
  };
378
+ var NETLIFY_STATES = {
379
+ NEW: "new",
380
+ PENDING_REVIEW: "pending_review",
381
+ ACCEPTED: "accepted",
382
+ REJECTED: "rejected",
383
+ ENQUEUED: "enqueued",
384
+ BUILDING: "building",
385
+ UPLOADING: "uploading",
386
+ UPLOADED: "uploaded",
387
+ PREPARING: "preparing",
388
+ PREPARED: "prepared",
389
+ PROCESSING: "processing",
390
+ PROCESSED: "processed",
391
+ READY: "ready",
392
+ ERROR: "error",
393
+ RETRYING: "retrying"
394
+ };
339
395
  var ADAPTER_ERRORS = {
340
396
  TOKEN_REQUIRED: "Vercel token required. Set VERCEL_TOKEN environment variable or pass token parameter.",
341
397
  FETCH_DEPLOYMENT_FAILED: "Failed to fetch deployment status from Vercel",
@@ -573,6 +629,12 @@ var VercelEndpoints = {
573
629
  method: "GET",
574
630
  docsUrl: "https://vercel.com/docs/rest-api/endpoints/user#get-the-authenticated-user",
575
631
  description: "Get authenticated user information"
632
+ },
633
+ listProjects: {
634
+ path: "/v10/projects",
635
+ method: "GET",
636
+ docsUrl: "https://vercel.com/docs/rest-api/reference/endpoints/projects/retrieve-a-list-of-projects",
637
+ description: "List all projects for authenticated user or team"
576
638
  }
577
639
  };
578
640
 
@@ -673,6 +735,23 @@ var VercelAPI = class extends BaseAPIClient {
673
735
  );
674
736
  }
675
737
  }
738
+ async listProjects(token, limit = 20) {
739
+ try {
740
+ return await this.request(
741
+ this.endpoints.listProjects,
742
+ {
743
+ searchParams: { limit: limit.toString() },
744
+ headers: {
745
+ Authorization: `Bearer ${token}`
746
+ },
747
+ token
748
+ // Pass token for rate limiting
749
+ }
750
+ );
751
+ } catch (error) {
752
+ throw this.handleApiError(error, "Failed to fetch projects list");
753
+ }
754
+ }
676
755
  handleApiError(error, context) {
677
756
  if (error instanceof Error) {
678
757
  const message = error.message.toLowerCase();
@@ -718,7 +797,7 @@ var VercelAPI = class extends BaseAPIClient {
718
797
 
719
798
  // src/adapters/vercel/index.ts
720
799
  var VercelAdapter = class extends BaseAdapter {
721
- name = PLATFORM_NAMES.VERCEL;
800
+ name = PLATFORM.VERCEL;
722
801
  api;
723
802
  constructor(config) {
724
803
  super();
@@ -744,7 +823,7 @@ var VercelAdapter = class extends BaseAdapter {
744
823
  return {
745
824
  status: ADAPTER_ERRORS.UNKNOWN_STATUS,
746
825
  projectName: project,
747
- platform: PLATFORM_NAMES.VERCEL
826
+ platform: PLATFORM.VERCEL
748
827
  };
749
828
  }
750
829
  return this.transformDeployment(data.deployments[0]);
@@ -770,7 +849,7 @@ var VercelAdapter = class extends BaseAdapter {
770
849
  status,
771
850
  url: deployment.url ? `https://${deployment.url}` : void 0,
772
851
  projectName: deployment.name,
773
- platform: PLATFORM_NAMES.VERCEL,
852
+ platform: PLATFORM.VERCEL,
774
853
  timestamp: this.formatTimestamp(deployment.createdAt),
775
854
  duration: deployment.ready ? this.calculateDuration(deployment.createdAt, deployment.ready) : void 0,
776
855
  environment: deployment.target || ENVIRONMENT_TYPES.PRODUCTION,
@@ -809,6 +888,14 @@ var VercelAdapter = class extends BaseAdapter {
809
888
  async getDeploymentStatus(project, token) {
810
889
  return this.getLatestDeployment(project, token);
811
890
  }
891
+ async listProjects(token, limit = 20) {
892
+ const response = await this.api.listProjects(token, limit);
893
+ return response.projects.map((project) => ({
894
+ id: project.id,
895
+ name: project.name,
896
+ url: project.latestDeployments?.[0]?.url ? `https://${project.latestDeployments[0].url}` : void 0
897
+ }));
898
+ }
812
899
  };
813
900
 
814
901
  // src/adapters/netlify/endpoints.ts
@@ -883,15 +970,6 @@ var NetlifyAPI = class extends BaseAPIClient {
883
970
  this.siteCache.set(siteNameOrId, site.id);
884
971
  return site.id;
885
972
  }
886
- async listSites(token) {
887
- const options = {
888
- headers: {
889
- Authorization: `Bearer ${token}`
890
- },
891
- token
892
- };
893
- return this.request(this.endpoints.listSites, options);
894
- }
895
973
  async listDeploys(siteNameOrId, token, limit = 10) {
896
974
  const siteId = await this.getSiteId(siteNameOrId, token);
897
975
  const endpoint = {
@@ -942,11 +1020,23 @@ var NetlifyAPI = class extends BaseAPIClient {
942
1020
  };
943
1021
  return this.request(this.endpoints.getUser, options);
944
1022
  }
1023
+ async listSites(token, limit = 20) {
1024
+ const options = {
1025
+ headers: {
1026
+ Authorization: `Bearer ${token}`
1027
+ },
1028
+ searchParams: {
1029
+ per_page: limit.toString()
1030
+ },
1031
+ token
1032
+ };
1033
+ return this.request(this.endpoints.listSites, options);
1034
+ }
945
1035
  };
946
1036
 
947
1037
  // src/adapters/netlify/index.ts
948
1038
  var NetlifyAdapter = class extends BaseAdapter {
949
- name = "netlify";
1039
+ name = PLATFORM.NETLIFY;
950
1040
  api;
951
1041
  constructor() {
952
1042
  super();
@@ -1090,6 +1180,14 @@ var NetlifyAdapter = class extends BaseAdapter {
1090
1180
  );
1091
1181
  }
1092
1182
  }
1183
+ async listProjects(token, limit = 20) {
1184
+ const sites = await this.api.listSites(token, limit);
1185
+ return sites.map((site) => ({
1186
+ id: site.id,
1187
+ name: site.name,
1188
+ url: site.ssl_url || site.url || void 0
1189
+ }));
1190
+ }
1093
1191
  };
1094
1192
 
1095
1193
  // src/core/deployment-intelligence.ts
@@ -1798,6 +1896,37 @@ ${status.commit ? `### Commit Info
1798
1896
  }
1799
1897
  return formatted;
1800
1898
  }
1899
+ static formatMultipleStatuses(statuses) {
1900
+ const display = `## Deployment History
1901
+
1902
+ ### Recent Deployments (${statuses.length})
1903
+
1904
+ ${statuses.map((status, index) => {
1905
+ const statusIcon = status.status === "success" ? "\u2705" : status.status === "building" ? "\u{1F504}" : status.status === "failed" ? "\u274C" : "\u2753";
1906
+ return `**${index + 1}. ${statusIcon} ${status.projectName}**
1907
+ Status: ${status.status}
1908
+ Environment: ${status.environment || "production"}
1909
+ ${status.url ? `URL: ${status.url} ` : ""}
1910
+ Time: ${status.timestamp}
1911
+ ${status.duration ? `Duration: ${status.duration}s ` : ""}
1912
+ ${status.commit ? `Commit: \`${status.commit.sha?.slice(0, 7) || "N/A"}\` ${status.commit.message || ""}` : ""}`;
1913
+ }).join("\n\n")}
1914
+ `;
1915
+ return {
1916
+ version: "1.0",
1917
+ tool: "check_deployment_status",
1918
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1919
+ display,
1920
+ data: {
1921
+ count: statuses.length,
1922
+ deployments: statuses
1923
+ },
1924
+ highlights: {
1925
+ status: `${statuses.length} deployments`,
1926
+ url: statuses[0]?.url
1927
+ }
1928
+ };
1929
+ }
1801
1930
  };
1802
1931
 
1803
1932
  // src/core/mcp-handler.ts
@@ -1876,6 +2005,8 @@ var MCPHandler = class {
1876
2005
  return this.compareDeployments(args);
1877
2006
  case "get_deployment_logs":
1878
2007
  return this.getDeploymentLogs(args);
2008
+ case "list_projects":
2009
+ return this.listProjects(args);
1879
2010
  default:
1880
2011
  throw new Error(`Unknown tool: ${tool}`);
1881
2012
  }
@@ -1911,12 +2042,32 @@ var MCPHandler = class {
1911
2042
  throw new Error(`Unsupported platform: ${validated.platform}`);
1912
2043
  }
1913
2044
  try {
1914
- const status = await adapter.getLatestDeployment(
1915
- validated.project,
1916
- validated.token
1917
- );
1918
- const formattedStatus = this.formatResponse(status, validated.platform);
1919
- return ResponseFormatter.formatStatus(formattedStatus);
2045
+ if (validated.limit === 1) {
2046
+ const status = await adapter.getLatestDeployment(
2047
+ validated.project,
2048
+ validated.token
2049
+ );
2050
+ const formattedStatus = this.formatResponse(status, validated.platform);
2051
+ return ResponseFormatter.formatStatus(formattedStatus);
2052
+ } else {
2053
+ const deployments = await adapter.getRecentDeployments(
2054
+ validated.project,
2055
+ validated.token || process.env[`${validated.platform.toUpperCase()}_TOKEN`] || "",
2056
+ validated.limit
2057
+ );
2058
+ if (deployments.length === 0) {
2059
+ return ResponseFormatter.formatStatus({
2060
+ status: "unknown",
2061
+ projectName: validated.project,
2062
+ platform: validated.platform,
2063
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2064
+ });
2065
+ }
2066
+ const statuses = deployments.map(
2067
+ (d) => this.formatDeploymentStatus(d, validated.platform)
2068
+ );
2069
+ return ResponseFormatter.formatMultipleStatuses(statuses);
2070
+ }
1920
2071
  } catch (error) {
1921
2072
  console.error(`Error checking deployment status:`, error);
1922
2073
  throw new Error(
@@ -1924,6 +2075,69 @@ var MCPHandler = class {
1924
2075
  );
1925
2076
  }
1926
2077
  }
2078
+ formatDeploymentStatus(deployment, platform) {
2079
+ if (platform === PLATFORM.VERCEL) {
2080
+ return {
2081
+ id: deployment.uid,
2082
+ status: this.mapVercelState(deployment.state),
2083
+ url: deployment.url ? `https://${deployment.url}` : void 0,
2084
+ projectName: deployment.name,
2085
+ platform,
2086
+ timestamp: new Date(deployment.createdAt).toISOString(),
2087
+ duration: deployment.ready ? Math.floor((deployment.ready - deployment.createdAt) / 1e3) : void 0,
2088
+ environment: deployment.target || "production",
2089
+ commit: deployment.meta ? {
2090
+ sha: deployment.meta.githubCommitSha,
2091
+ message: deployment.meta.githubCommitMessage,
2092
+ author: deployment.meta.githubCommitAuthorName
2093
+ } : void 0
2094
+ };
2095
+ } else if (platform === PLATFORM.NETLIFY) {
2096
+ return {
2097
+ id: deployment.id,
2098
+ status: this.mapNetlifyState(deployment.state),
2099
+ url: deployment.ssl_url || deployment.url,
2100
+ projectName: deployment.name,
2101
+ platform,
2102
+ timestamp: deployment.created_at,
2103
+ duration: deployment.deploy_time,
2104
+ environment: deployment.context || "production",
2105
+ commit: deployment.commit_ref ? {
2106
+ sha: deployment.commit_ref,
2107
+ message: deployment.title,
2108
+ author: deployment.committer
2109
+ } : void 0
2110
+ };
2111
+ }
2112
+ return deployment;
2113
+ }
2114
+ mapVercelState(state) {
2115
+ switch (state) {
2116
+ case VERCEL_STATES.READY:
2117
+ return "success";
2118
+ case VERCEL_STATES.ERROR:
2119
+ case VERCEL_STATES.CANCELED:
2120
+ return "failed";
2121
+ case VERCEL_STATES.BUILDING:
2122
+ case VERCEL_STATES.INITIALIZING:
2123
+ case VERCEL_STATES.QUEUED:
2124
+ return "building";
2125
+ default:
2126
+ return "unknown";
2127
+ }
2128
+ }
2129
+ mapNetlifyState(state) {
2130
+ switch (state) {
2131
+ case NETLIFY_STATES.READY:
2132
+ case NETLIFY_STATES.PROCESSED:
2133
+ return "success";
2134
+ case NETLIFY_STATES.ERROR:
2135
+ case NETLIFY_STATES.REJECTED:
2136
+ return "failed";
2137
+ default:
2138
+ return "building";
2139
+ }
2140
+ }
1927
2141
  formatResponse(status, platform) {
1928
2142
  return {
1929
2143
  ...status,
@@ -1997,6 +2211,69 @@ ${messages.join("\n\n")}
1997
2211
  schemaToJsonSchema(zodSchema) {
1998
2212
  return JSON.parse(JSON.stringify(zodSchema));
1999
2213
  }
2214
+ async listProjects(args) {
2215
+ const validated = listProjectsSchema.parse(args);
2216
+ const adapter = this.adapters.get(validated.platform);
2217
+ if (!adapter) {
2218
+ throw new Error(`Unsupported platform: ${validated.platform}`);
2219
+ }
2220
+ const tokenEnvKey = `${validated.platform.toUpperCase()}_TOKEN`;
2221
+ const token = validated.token || process.env[tokenEnvKey];
2222
+ if (!token) {
2223
+ throw new Error(
2224
+ `Authentication required. Please provide a token or set ${tokenEnvKey} environment variable.`
2225
+ );
2226
+ }
2227
+ try {
2228
+ const projects = await adapter.listProjects(token, validated.limit);
2229
+ const display = `## Projects on ${validated.platform}
2230
+
2231
+ ### Found ${projects.length} project${projects.length !== 1 ? "s" : ""}
2232
+
2233
+ ${projects.map((p, i) => {
2234
+ const safeName = p.name.replace(/[<>"'&]/g, "");
2235
+ return `**${i + 1}. ${safeName}**
2236
+ ID: \`${p.id}\`
2237
+ ${p.url ? `URL: ${p.url}` : "URL: Not deployed yet"}`;
2238
+ }).join("\n\n")}
2239
+
2240
+ ${projects.length === validated.limit ? `
2241
+ *Showing first ${validated.limit} projects. Use a higher limit to see more.*` : ""}`;
2242
+ return {
2243
+ version: "1.0",
2244
+ tool: "list_projects",
2245
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2246
+ display,
2247
+ data: {
2248
+ platform: validated.platform,
2249
+ count: projects.length,
2250
+ projects: projects.map((p) => ({
2251
+ id: p.id,
2252
+ name: p.name,
2253
+ url: p.url
2254
+ }))
2255
+ },
2256
+ highlights: {
2257
+ status: `${projects.length} projects found`
2258
+ }
2259
+ };
2260
+ } catch (error) {
2261
+ const message = error instanceof Error ? error.message : "Unknown error";
2262
+ if (message.includes("401") || message.includes("unauthorized")) {
2263
+ throw new Error(
2264
+ `Authentication failed. Please check your ${validated.platform} token.`
2265
+ );
2266
+ }
2267
+ if (message.includes("429") || message.includes("rate limit")) {
2268
+ throw new Error(
2269
+ `Rate limit exceeded for ${validated.platform}. Please try again later.`
2270
+ );
2271
+ }
2272
+ throw new Error(
2273
+ `Failed to list projects on ${validated.platform}: ${message}`
2274
+ );
2275
+ }
2276
+ }
2000
2277
  };
2001
2278
 
2002
2279
  export {
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  NetlifyAdapter,
5
5
  VercelAdapter,
6
6
  tools
7
- } from "./chunk-V4XY4X6P.js";
7
+ } from "./chunk-YU5SU5ZI.js";
8
8
 
9
9
  // src/index.ts
10
10
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
package/dist/worker.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  MCPHandler,
3
3
  VercelAdapter
4
- } from "./chunk-V4XY4X6P.js";
4
+ } from "./chunk-YU5SU5ZI.js";
5
5
 
6
6
  // src/utils/github.ts
7
7
  async function validateRepository(user, repo) {
@@ -786,7 +786,7 @@ var landingPageHTML = `<!DOCTYPE html>
786
786
  <div class="tools-grid">
787
787
  <div class="tool">
788
788
  <div class="tool-name">check_deployment_status</div>
789
- <div class="tool-desc">Get the latest deployment status for any project</div>
789
+ <div class="tool-desc">Get latest deployment status or view deployment history</div>
790
790
  </div>
791
791
 
792
792
  <div class="tool">
@@ -803,6 +803,11 @@ var landingPageHTML = `<!DOCTYPE html>
803
803
  <div class="tool-name">get_deployment_logs</div>
804
804
  <div class="tool-desc">Fetch and analyze deployment logs with error detection</div>
805
805
  </div>
806
+
807
+ <div class="tool">
808
+ <div class="tool-name">list_projects</div>
809
+ <div class="tool-desc">Discover all your projects and sites across platforms</div>
810
+ </div>
806
811
  </div>
807
812
  </section>
808
813
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deploy-mcp",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Universal deployment tracker for AI assistants",
5
5
  "keywords": [
6
6
  "mcp",