mcp-inflight 0.2.4 → 0.3.1

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
@@ -83,9 +83,9 @@ The server automatically patches configuration files for CodeSandbox compatibili
83
83
 
84
84
  Projects over 3MB are automatically chunked into smaller uploads to handle Vercel's serverless function body size limits.
85
85
 
86
- ## Slash Commands (Optional)
86
+ ## Slash Commands (Recommended)
87
87
 
88
- For a better experience, install the included slash commands. These provide `/share` and `/inflight` commands in Claude Code.
88
+ For the best experience, install the included slash commands. The `/share` command enables **automated AI feedback guide generation** - Claude analyzes your git changes and generates product-focused review questions that appear on your InFlight version.
89
89
 
90
90
  ### Installation
91
91
 
@@ -109,7 +109,7 @@ cp packages/mcp-inflight/commands/*.md ~/.claude/commands/
109
109
 
110
110
  | Command | Description |
111
111
  |---------|-------------|
112
- | `/share` | Share a project to InFlight (shortcut for the share tool) |
112
+ | `/share` | Share a project to InFlight with AI-generated feedback guide |
113
113
  | `/inflight` | Menu for managing prototypes: list, sync, delete, login, logout |
114
114
 
115
115
  ## Troubleshooting
@@ -0,0 +1,70 @@
1
+ # InFlight
2
+
3
+ Manage InFlight prototypes and authentication.
4
+
5
+ ## Arguments
6
+
7
+ $ARGUMENTS - Optional action: `login`, `logout`, `list`, `sync`, `delete`. If not provided, shows menu.
8
+
9
+ ## Instructions
10
+
11
+ ### If no argument provided, show menu
12
+
13
+ Ask the user what they'd like to do using the AskUserQuestion tool with these options:
14
+ - **Check login status** - See who's logged in
15
+ - **List prototypes** - Show all shared prototypes
16
+ - **Sync prototype** - Update files in an existing prototype
17
+ - **Delete prototype** - Remove a prototype
18
+ - **Logout** - Log out of InFlight
19
+
20
+ Then execute the selected action below.
21
+
22
+ ### Action: login (or "Check login status")
23
+
24
+ Call the `login` MCP tool. It will either:
25
+ - Show current user info if already logged in
26
+ - Open browser for authentication if not logged in
27
+
28
+ Display the result to the user.
29
+
30
+ ### Action: logout
31
+
32
+ Call the `logout` MCP tool to clear stored credentials.
33
+ Confirm to the user they've been logged out.
34
+
35
+ ### Action: list (or "List prototypes")
36
+
37
+ Call the `prototype_list` MCP tool.
38
+
39
+ Display results in a table:
40
+ - Prototype ID
41
+ - Project Name
42
+ - Type
43
+ - Status
44
+ - InFlight URL
45
+ - Created
46
+
47
+ If no prototypes found, inform the user.
48
+
49
+ ### Action: sync (or "Sync prototype")
50
+
51
+ 1. If a prototype ID is provided in arguments, use that
52
+ 2. Otherwise, call `prototype_list` and ask user which prototype to sync
53
+ 3. Ask for the project path (or use current directory)
54
+ 4. Check for .env files - only ask about including them if they exist
55
+ 5. Call `prototype_sync` with:
56
+ - `prototypeId`: The selected ID
57
+ - `path`: The project path
58
+ - `includeEnvFiles`: Based on user response (default: false)
59
+
60
+ ### Action: delete (or "Delete prototype")
61
+
62
+ 1. If a prototype ID is provided in arguments, use that
63
+ 2. Otherwise, call `prototype_list` and ask user which prototype to delete
64
+ 3. Confirm: "Delete prototype [ID]? This will stop it and make it unavailable."
65
+ 4. Call `prototype_delete` with the prototype ID
66
+ 5. Confirm deletion to user
67
+
68
+ ## Related Commands
69
+
70
+ - `/share` - Share a project to InFlight
@@ -0,0 +1,75 @@
1
+ # Share Project
2
+
3
+ Share a local project to InFlight for feedback and collaboration.
4
+
5
+ ## Arguments
6
+
7
+ $ARGUMENTS - Optional path to the project directory. If not provided, uses current working directory.
8
+
9
+ ## Instructions
10
+
11
+ ### 1. Determine project path
12
+
13
+ Use the provided path argument or current working directory.
14
+
15
+ ### 2. Check for .env files (STOP only if found)
16
+
17
+ Look for `.env`, `.env.local`, `.env.development`, `.env.production` in the project directory.
18
+
19
+ **Only if .env files exist, ASK:** "Found .env files. Include them in deployment? (May contain sensitive API keys)"
20
+ - Yes: set `includeEnvFiles: true`
21
+ - No: set `includeEnvFiles: false` (default)
22
+
23
+ If no .env files found, skip this step entirely.
24
+
25
+ ### 3. Analyze git changes (if on feature branch)
26
+
27
+ Check if there are git changes to generate review questions:
28
+
29
+ 1. **Check for commits on branch:** Run `git log main..HEAD --oneline 2>/dev/null || git log master..HEAD --oneline 2>/dev/null`
30
+ 2. **If commits exist**, analyze the changes:
31
+ - Run `git diff main...HEAD 2>/dev/null || git diff master...HEAD 2>/dev/null` to see what changed
32
+ - **Generate a product-focused summary:** Write 2-3 sentences about user-visible changes (not internal code details)
33
+ - **List key changes:** Identify main user-facing changes (e.g., "Added dark mode toggle", "Redesigned checkout flow")
34
+ - **List affected areas:** Identify UI/feature areas (e.g., "Settings page", "Navigation", "Forms")
35
+
36
+ **Skip this step if:**
37
+ - On main/master branch
38
+ - No commits found on the branch
39
+ - Git commands fail (not a git repo)
40
+
41
+ ### 4. Call the `share` MCP tool
42
+
43
+ Call the `share` tool with:
44
+ - `path`: the project path
45
+ - `includeEnvFiles`: based on user response (default: false)
46
+ - `diffSummary` (only if git changes were analyzed in step 3):
47
+ - `summary`: your product-focused summary
48
+ - `keyChanges`: array of key user-visible changes
49
+ - `affectedAreas`: array of affected UI areas
50
+
51
+ The MCP tool handles everything:
52
+ - Project analysis and type detection
53
+ - Package manager detection
54
+ - VM tier selection
55
+ - Creating prototype and InFlight version
56
+ - Generating review questions from your summary (automatic)
57
+ - Opening InFlight in browser
58
+
59
+ ### 5. Display the result
60
+
61
+ Show the InFlight URL to the user:
62
+
63
+ ```
64
+ Shared!
65
+
66
+ InFlight: https://www.inflight.co/v/[version-id]
67
+ ```
68
+
69
+ ## Error Handling
70
+
71
+ If the tool returns an error, display it to the user.
72
+
73
+ ## Related Commands
74
+
75
+ - `/inflight` - Manage prototypes, login/logout
package/dist/index.js CHANGED
@@ -986,7 +986,8 @@ async function deployProject(files, options, log) {
986
986
  vmTier: options.vmTier || "Micro",
987
987
  projectName: options.projectName,
988
988
  workspaceId: options.workspaceId || auth.defaultWorkspaceId,
989
- gitInfo: options.gitInfo
989
+ gitInfo: options.gitInfo,
990
+ diffSummary: options.diffSummary
990
991
  })
991
992
  });
992
993
  if (!response.ok && !response.headers.get("content-type")?.includes("text/event-stream")) {
@@ -1227,7 +1228,8 @@ async function finalizeSandbox(sandboxId, options, log) {
1227
1228
  port: options.port,
1228
1229
  projectName: options.projectName,
1229
1230
  workspaceId: options.workspaceId,
1230
- gitInfo: options.gitInfo
1231
+ gitInfo: options.gitInfo,
1232
+ diffSummary: options.diffSummary
1231
1233
  })
1232
1234
  });
1233
1235
  if (!response.ok && !response.headers.get("content-type")?.includes("text/event-stream")) {
@@ -1284,7 +1286,8 @@ async function deployProjectChunked(fileChunks, options, log) {
1284
1286
  port: options.port,
1285
1287
  projectName: options.projectName,
1286
1288
  workspaceId,
1287
- gitInfo: options.gitInfo
1289
+ gitInfo: options.gitInfo,
1290
+ diffSummary: options.diffSummary
1288
1291
  },
1289
1292
  log
1290
1293
  );
@@ -1926,6 +1929,9 @@ async function deployPrototype(args, log) {
1926
1929
  const dirtyMarker = gitInfo.isDirty ? " (modified)" : "";
1927
1930
  await log(`Git: ${gitInfo.branch}@${gitInfo.commitShort}${dirtyMarker}`);
1928
1931
  }
1932
+ if (args.diffSummary) {
1933
+ await log(`Diff summary provided: ${args.diffSummary.keyChanges.length} key changes`);
1934
+ }
1929
1935
  if (!projectInfo.compatibility.canDeploy) {
1930
1936
  throw new Error(
1931
1937
  `Project cannot be deployed: ${projectInfo.compatibility.issues.join(", ")}`
@@ -1997,124 +2003,74 @@ async function deployPrototype(args, log) {
1997
2003
  }
1998
2004
  const totalSize = calculateTotalSize(files);
1999
2005
  await log(`Found ${fileCount} files (${(totalSize / 1024 / 1024).toFixed(1)} MB)`);
2000
- let existingSandbox;
2001
- try {
2002
- const sandboxes = await listSandboxes();
2003
- const projectName = path5.basename(projectPath);
2004
- existingSandbox = sandboxes.find(
2005
- (s) => s.projectName === projectName && s.status === "running"
2006
- );
2007
- } catch {
2008
- }
2009
2006
  let result;
2010
- if (existingSandbox) {
2011
- await log(`Found existing sandbox: ${existingSandbox.sandboxId}`);
2012
- await log("Syncing files...");
2013
- try {
2014
- const syncResult = await syncProject(
2015
- existingSandbox.sandboxId,
2016
- files,
2017
- {
2018
- restartServer: true,
2019
- startCommand: projectInfo.startCommand,
2020
- port: projectInfo.port,
2021
- gitInfo: gitInfo || void 0
2022
- },
2023
- log
2024
- );
2025
- const inflightUrl = existingSandbox.inflightUrl || "";
2026
- const inflightVersionId = existingSandbox.inflightVersionId || "";
2027
- result = {
2028
- url: syncResult.sandboxUrl,
2029
- sandboxId: existingSandbox.sandboxId,
2030
- projectType: projectInfo.type,
2031
- synced: true,
2032
- inflightUrl,
2033
- inflightVersionId,
2034
- startCommand: projectInfo.startCommand,
2035
- port: projectInfo.port,
2036
- packageManager: projectInfo.detectedPackageManager,
2037
- isMonorepo: projectInfo.isMonorepo,
2038
- vmTier: projectInfo.sizing.recommendedTier,
2039
- embedCheck
2040
- };
2041
- const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
2042
- await log(`Sync complete in ${elapsed}s`);
2043
- } catch (syncError) {
2044
- const errorMsg = syncError instanceof Error ? syncError.message : String(syncError);
2045
- await log(`Sync failed, creating new sandbox: ${errorMsg}`, "warning");
2046
- existingSandbox = void 0;
2047
- }
2048
- }
2049
- if (!existingSandbox) {
2050
- const useChunkedUpload = needsChunkedUpload(files);
2051
- if (useChunkedUpload) {
2052
- const totalSize2 = calculateTotalSize(files);
2053
- await log(`Large project detected (${(totalSize2 / 1024 / 1024).toFixed(1)} MB), using chunked upload...`);
2054
- const fileChunks = chunkFiles(files);
2055
- await log(`Split into ${fileChunks.length} chunks`);
2056
- const deployResult = await deployProjectChunked(
2057
- fileChunks,
2058
- {
2059
- projectType: projectInfo.type,
2060
- installCommand: projectInfo.installCommand || void 0,
2061
- startCommand: projectInfo.startCommand,
2062
- port: projectInfo.port,
2063
- vmTier: projectInfo.sizing.recommendedTier,
2064
- projectName: path5.basename(projectPath),
2065
- workspaceId: args.workspaceId,
2066
- gitInfo: gitInfo || void 0
2067
- },
2068
- log
2069
- );
2070
- result = {
2071
- url: deployResult.sandboxUrl,
2072
- sandboxId: deployResult.sandboxId,
2007
+ const useChunkedUpload = needsChunkedUpload(files);
2008
+ if (useChunkedUpload) {
2009
+ const totalSize2 = calculateTotalSize(files);
2010
+ await log(`Large project detected (${(totalSize2 / 1024 / 1024).toFixed(1)} MB), using chunked upload...`);
2011
+ const fileChunks = chunkFiles(files);
2012
+ await log(`Split into ${fileChunks.length} chunks`);
2013
+ const deployResult = await deployProjectChunked(
2014
+ fileChunks,
2015
+ {
2073
2016
  projectType: projectInfo.type,
2074
- synced: false,
2075
- inflightUrl: deployResult.inflightUrl,
2076
- inflightVersionId: deployResult.versionId,
2017
+ installCommand: projectInfo.installCommand || void 0,
2077
2018
  startCommand: projectInfo.startCommand,
2078
2019
  port: projectInfo.port,
2079
- packageManager: projectInfo.detectedPackageManager,
2080
- isMonorepo: projectInfo.isMonorepo,
2081
2020
  vmTier: projectInfo.sizing.recommendedTier,
2082
- embedCheck
2083
- };
2084
- } else {
2085
- await log("Deploying via InFlight API...");
2086
- const deployResult = await deployProject(
2087
- files,
2088
- {
2089
- projectType: projectInfo.type,
2090
- installCommand: projectInfo.installCommand || void 0,
2091
- startCommand: projectInfo.startCommand,
2092
- port: projectInfo.port,
2093
- vmTier: projectInfo.sizing.recommendedTier,
2094
- projectName: path5.basename(projectPath),
2095
- workspaceId: args.workspaceId,
2096
- gitInfo: gitInfo || void 0
2097
- },
2098
- log
2099
- );
2100
- result = {
2101
- url: deployResult.sandboxUrl,
2102
- sandboxId: deployResult.sandboxId,
2021
+ projectName: path5.basename(projectPath),
2022
+ workspaceId: args.workspaceId,
2023
+ gitInfo: gitInfo || void 0,
2024
+ diffSummary: args.diffSummary || void 0
2025
+ },
2026
+ log
2027
+ );
2028
+ result = {
2029
+ url: deployResult.sandboxUrl,
2030
+ sandboxId: deployResult.sandboxId,
2031
+ projectType: projectInfo.type,
2032
+ inflightUrl: deployResult.inflightUrl,
2033
+ inflightVersionId: deployResult.versionId,
2034
+ startCommand: projectInfo.startCommand,
2035
+ port: projectInfo.port,
2036
+ packageManager: projectInfo.detectedPackageManager,
2037
+ isMonorepo: projectInfo.isMonorepo,
2038
+ vmTier: projectInfo.sizing.recommendedTier,
2039
+ embedCheck
2040
+ };
2041
+ } else {
2042
+ await log("Deploying via InFlight API...");
2043
+ const deployResult = await deployProject(
2044
+ files,
2045
+ {
2103
2046
  projectType: projectInfo.type,
2104
- synced: false,
2105
- inflightUrl: deployResult.inflightUrl,
2106
- inflightVersionId: deployResult.versionId,
2047
+ installCommand: projectInfo.installCommand || void 0,
2107
2048
  startCommand: projectInfo.startCommand,
2108
2049
  port: projectInfo.port,
2109
- packageManager: projectInfo.detectedPackageManager,
2110
- isMonorepo: projectInfo.isMonorepo,
2111
2050
  vmTier: projectInfo.sizing.recommendedTier,
2112
- embedCheck
2113
- };
2114
- }
2115
- const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
2116
- await log(`Deploy complete in ${elapsed}s`);
2051
+ projectName: path5.basename(projectPath),
2052
+ workspaceId: args.workspaceId,
2053
+ gitInfo: gitInfo || void 0,
2054
+ diffSummary: args.diffSummary || void 0
2055
+ },
2056
+ log
2057
+ );
2058
+ result = {
2059
+ url: deployResult.sandboxUrl,
2060
+ sandboxId: deployResult.sandboxId,
2061
+ projectType: projectInfo.type,
2062
+ inflightUrl: deployResult.inflightUrl,
2063
+ inflightVersionId: deployResult.versionId,
2064
+ startCommand: projectInfo.startCommand,
2065
+ port: projectInfo.port,
2066
+ packageManager: projectInfo.detectedPackageManager,
2067
+ isMonorepo: projectInfo.isMonorepo,
2068
+ vmTier: projectInfo.sizing.recommendedTier,
2069
+ embedCheck
2070
+ };
2117
2071
  }
2072
+ const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
2073
+ await log(`Deploy complete in ${elapsed}s`);
2118
2074
  if (hadUncommittedChanges && result.inflightVersionId) {
2119
2075
  const newCommit = autoCommitForShare(projectPath, result.inflightVersionId);
2120
2076
  if (newCommit) {
@@ -2188,6 +2144,27 @@ function createServer2() {
2188
2144
  workspaceId: {
2189
2145
  type: "string",
2190
2146
  description: "InFlight workspace ID to create the version in. If not provided, uses the last-used workspace or the user's first workspace."
2147
+ },
2148
+ diffSummary: {
2149
+ type: "object",
2150
+ description: "Claude-generated summary of git changes for review question generation. Provide this when sharing a feature branch with commits.",
2151
+ properties: {
2152
+ summary: {
2153
+ type: "string",
2154
+ description: "A 2-3 sentence product-focused summary of the changes. Focus on user-visible changes, not internal code details."
2155
+ },
2156
+ keyChanges: {
2157
+ type: "array",
2158
+ items: { type: "string" },
2159
+ description: "List of key user-visible changes (e.g., 'Added dark mode toggle', 'Redesigned checkout flow')"
2160
+ },
2161
+ affectedAreas: {
2162
+ type: "array",
2163
+ items: { type: "string" },
2164
+ description: "UI/feature areas affected by the changes (e.g., 'Settings page', 'Navigation', 'Forms')"
2165
+ }
2166
+ },
2167
+ required: ["summary", "keyChanges"]
2191
2168
  }
2192
2169
  },
2193
2170
  required: ["path"]
@@ -2262,7 +2239,7 @@ function createServer2() {
2262
2239
  if (name === "share") {
2263
2240
  const deployArgs = args;
2264
2241
  const result = await deployPrototype(deployArgs, log);
2265
- const message = result.synced ? `Files synced. InFlight: ${result.inflightUrl}` : `Project shared successfully. InFlight: ${result.inflightUrl}`;
2242
+ const message = `Project shared successfully. InFlight: ${result.inflightUrl}`;
2266
2243
  return {
2267
2244
  content: [
2268
2245
  {
@@ -2281,7 +2258,6 @@ function createServer2() {
2281
2258
  },
2282
2259
  // Embed check results - shows if there are iframe embedding issues
2283
2260
  embedCheck: result.embedCheck,
2284
- synced: result.synced ?? false,
2285
2261
  inflightUrl: result.inflightUrl,
2286
2262
  inflightVersionId: result.inflightVersionId,
2287
2263
  message
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-inflight",
3
- "version": "0.2.4",
3
+ "version": "0.3.1",
4
4
  "description": "MCP server for sharing prototypes via InFlight",
5
5
  "author": "InFlight <hello@inflight.co>",
6
6
  "homepage": "https://github.com/inflight/mcp-inflight",
@@ -13,11 +13,6 @@
13
13
  "bin": {
14
14
  "mcp-inflight": "dist/index.js"
15
15
  },
16
- "scripts": {
17
- "build": "tsup",
18
- "dev": "tsup --watch",
19
- "typecheck": "tsc --noEmit"
20
- },
21
16
  "dependencies": {
22
17
  "@modelcontextprotocol/sdk": "^1.0.0"
23
18
  },
@@ -27,7 +22,8 @@
27
22
  "typescript": "^5.0.0"
28
23
  },
29
24
  "files": [
30
- "dist"
25
+ "dist",
26
+ "commands"
31
27
  ],
32
28
  "keywords": [
33
29
  "mcp",
@@ -36,5 +32,10 @@
36
32
  "share",
37
33
  "deploy"
38
34
  ],
39
- "license": "MIT"
40
- }
35
+ "license": "MIT",
36
+ "scripts": {
37
+ "build": "tsup",
38
+ "dev": "tsup --watch",
39
+ "typecheck": "tsc --noEmit"
40
+ }
41
+ }