@myvtp/mcp 0.1.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/LICENSE +21 -0
- package/README.md +65 -0
- package/dist/client.d.ts +46 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +430 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +172 -0
- package/dist/index.js.map +1 -0
- package/dist/tools.d.ts +65 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +116 -0
- package/dist/tools.js.map +1 -0
- package/dist/utils/ignoreFilter.d.ts +12 -0
- package/dist/utils/ignoreFilter.d.ts.map +1 -0
- package/dist/utils/ignoreFilter.js +74 -0
- package/dist/utils/ignoreFilter.js.map +1 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 myvtp
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# @myvtp/mcp
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server for VTP - deploy apps via Claude Code.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add to your Claude Code configuration (`~/.claude.json`):
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"mcpServers": {
|
|
12
|
+
"vtp": {
|
|
13
|
+
"command": "npx",
|
|
14
|
+
"args": ["-y", "@myvtp/mcp"]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
Once configured, simply ask Claude Code to deploy your app:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Deploy this app to VTP
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Claude will analyse your project, create the necessary configuration, and deploy it.
|
|
29
|
+
|
|
30
|
+
### Available Tools
|
|
31
|
+
|
|
32
|
+
| Tool | Description |
|
|
33
|
+
|------|-------------|
|
|
34
|
+
| `deploy` | Deploy an app from a vtp.yaml config |
|
|
35
|
+
| `list` | List all deployed apps |
|
|
36
|
+
| `list_app_types` | Show supported app types |
|
|
37
|
+
| `get_deployment_guide` | Get detailed deployment instructions |
|
|
38
|
+
|
|
39
|
+
## Configuration
|
|
40
|
+
|
|
41
|
+
The MCP server connects to the VTP API. By default it uses `https://api.myvtp.app`.
|
|
42
|
+
|
|
43
|
+
To use a different API URL (e.g., for local development):
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"mcpServers": {
|
|
48
|
+
"vtp": {
|
|
49
|
+
"command": "npx",
|
|
50
|
+
"args": ["-y", "@myvtp/mcp"],
|
|
51
|
+
"env": {
|
|
52
|
+
"VTP_API_URL": "https://api.myvtp.dev"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Authentication
|
|
60
|
+
|
|
61
|
+
On first use, the MCP server will open your browser for authentication. Your credentials are stored locally at `~/.vtp/credentials.json`.
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface App {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
type: string;
|
|
6
|
+
status: string;
|
|
7
|
+
url: string;
|
|
8
|
+
containerId: string;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
imageName: string;
|
|
11
|
+
}
|
|
12
|
+
export interface DeployResult {
|
|
13
|
+
app?: App;
|
|
14
|
+
replaced?: boolean;
|
|
15
|
+
error?: string;
|
|
16
|
+
message?: string;
|
|
17
|
+
existingApp?: App;
|
|
18
|
+
}
|
|
19
|
+
export interface AppTypeInfo {
|
|
20
|
+
type: string;
|
|
21
|
+
displayName: string;
|
|
22
|
+
description: string;
|
|
23
|
+
aliases: string[];
|
|
24
|
+
apiType: 'static' | 'node';
|
|
25
|
+
}
|
|
26
|
+
export interface DeploymentGuide extends AppTypeInfo {
|
|
27
|
+
content: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* List all deployed apps.
|
|
31
|
+
*/
|
|
32
|
+
export declare function listApps(): Promise<App[]>;
|
|
33
|
+
/**
|
|
34
|
+
* List all supported app types.
|
|
35
|
+
*/
|
|
36
|
+
export declare function listAppTypes(): Promise<AppTypeInfo[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Get deployment guide for a specific app type.
|
|
39
|
+
*/
|
|
40
|
+
export declare function getDeploymentGuide(type: string): Promise<DeploymentGuide>;
|
|
41
|
+
/**
|
|
42
|
+
* Deploy an app from a local directory.
|
|
43
|
+
* Reads vtp.yaml for all config including deploy path, creates tar.gz archive, and POSTs to the API.
|
|
44
|
+
*/
|
|
45
|
+
export declare function deploy(configPath?: string, force?: boolean): Promise<DeployResult>;
|
|
46
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAwCA,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,GAAG,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,QAAQ,GAAG,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,OAAO,EAAE,MAAM,CAAC;CACjB;AAyTD;;GAEG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAE/C;AAED;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAG3D;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAE/E;AAuDD;;;GAGG;AACH,wBAAsB,MAAM,CAC1B,UAAU,GAAE,MAAqB,EACjC,KAAK,GAAE,OAAe,GACrB,OAAO,CAAC,YAAY,CAAC,CA6CvB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import { createReadStream, existsSync, chmodSync } from 'fs';
|
|
2
|
+
import { unlink, copyFile, readFile, writeFile, mkdir } from 'fs/promises';
|
|
3
|
+
import { tmpdir, homedir } from 'os';
|
|
4
|
+
import { join, dirname, resolve } from 'path';
|
|
5
|
+
import { exec } from 'child_process';
|
|
6
|
+
import { promisify } from 'util';
|
|
7
|
+
import FormData from 'form-data';
|
|
8
|
+
import { parse as parseYaml } from 'yaml';
|
|
9
|
+
import * as tar from 'tar';
|
|
10
|
+
import { getFilesToInclude } from './utils/ignoreFilter.js';
|
|
11
|
+
const execAsync = promisify(exec);
|
|
12
|
+
const API_BASE = process.env.VTP_API_URL || 'https://api.myvtp.app';
|
|
13
|
+
const CREDENTIALS_PATH = join(homedir(), '.vtp', 'credentials.json');
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// Authentication Functions
|
|
16
|
+
// =============================================================================
|
|
17
|
+
/**
|
|
18
|
+
* Load credentials from disk.
|
|
19
|
+
*/
|
|
20
|
+
async function loadCredentials() {
|
|
21
|
+
try {
|
|
22
|
+
if (!existsSync(CREDENTIALS_PATH)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const content = await readFile(CREDENTIALS_PATH, 'utf-8');
|
|
26
|
+
return JSON.parse(content);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Save credentials to disk with restricted permissions.
|
|
34
|
+
*/
|
|
35
|
+
async function saveCredentials(credentials) {
|
|
36
|
+
const dir = dirname(CREDENTIALS_PATH);
|
|
37
|
+
await mkdir(dir, { recursive: true });
|
|
38
|
+
await writeFile(CREDENTIALS_PATH, JSON.stringify(credentials, null, 2));
|
|
39
|
+
// Set file permissions to 0600 (owner read/write only)
|
|
40
|
+
chmodSync(CREDENTIALS_PATH, 0o600);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Delete credentials from disk.
|
|
44
|
+
*/
|
|
45
|
+
async function deleteCredentials() {
|
|
46
|
+
try {
|
|
47
|
+
await unlink(CREDENTIALS_PATH);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Ignore if file doesn't exist
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if credentials are expired (with 5 minute buffer).
|
|
55
|
+
*/
|
|
56
|
+
function isTokenExpired(credentials) {
|
|
57
|
+
const bufferMs = 5 * 60 * 1000; // 5 minutes
|
|
58
|
+
return Date.now() >= (credentials.expiresAt - bufferMs);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Refresh the access token using the refresh token.
|
|
62
|
+
*/
|
|
63
|
+
async function refreshAccessToken(refreshToken) {
|
|
64
|
+
try {
|
|
65
|
+
const response = await fetch(`${API_BASE}/auth/refresh`, {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: { 'Content-Type': 'application/json' },
|
|
68
|
+
body: JSON.stringify({ refreshToken }),
|
|
69
|
+
});
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const data = await response.json();
|
|
74
|
+
const expiresAt = Date.now() + (24 * 60 * 60 * 1000); // 24 hours
|
|
75
|
+
return {
|
|
76
|
+
accessToken: data.accessToken,
|
|
77
|
+
refreshToken,
|
|
78
|
+
expiresAt,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Start the device authorization flow.
|
|
87
|
+
*/
|
|
88
|
+
async function startDeviceFlow() {
|
|
89
|
+
const response = await fetch(`${API_BASE}/auth/device/start`, {
|
|
90
|
+
method: 'POST',
|
|
91
|
+
});
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
throw new Error('Failed to start device flow');
|
|
94
|
+
}
|
|
95
|
+
return response.json();
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Poll for device authorization completion.
|
|
99
|
+
*/
|
|
100
|
+
async function pollDeviceFlow(deviceCode) {
|
|
101
|
+
const response = await fetch(`${API_BASE}/auth/device/poll`, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers: { 'Content-Type': 'application/json' },
|
|
104
|
+
body: JSON.stringify({ device_code: deviceCode }),
|
|
105
|
+
});
|
|
106
|
+
if (response.status === 428) {
|
|
107
|
+
return 'pending';
|
|
108
|
+
}
|
|
109
|
+
if (response.status === 403) {
|
|
110
|
+
return 'denied';
|
|
111
|
+
}
|
|
112
|
+
if (response.status === 410) {
|
|
113
|
+
return 'expired';
|
|
114
|
+
}
|
|
115
|
+
if (!response.ok) {
|
|
116
|
+
throw new Error('Device flow poll failed');
|
|
117
|
+
}
|
|
118
|
+
return response.json();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Open a URL in the default browser.
|
|
122
|
+
*/
|
|
123
|
+
async function openBrowser(url) {
|
|
124
|
+
const platform = process.platform;
|
|
125
|
+
let cmd;
|
|
126
|
+
if (platform === 'darwin') {
|
|
127
|
+
cmd = `open "${url}"`;
|
|
128
|
+
}
|
|
129
|
+
else if (platform === 'win32') {
|
|
130
|
+
cmd = `start "" "${url}"`;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
cmd = `xdg-open "${url}"`;
|
|
134
|
+
}
|
|
135
|
+
await execAsync(cmd);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Perform device flow authentication.
|
|
139
|
+
* Returns credentials on success, throws on failure.
|
|
140
|
+
*/
|
|
141
|
+
async function performDeviceFlow() {
|
|
142
|
+
// Start the device flow
|
|
143
|
+
const deviceFlow = await startDeviceFlow();
|
|
144
|
+
// Notify user and open browser
|
|
145
|
+
console.error(`\n┌─────────────────────────────────────────────────┐`);
|
|
146
|
+
console.error(`│ VTP Authentication Required │`);
|
|
147
|
+
console.error(`├─────────────────────────────────────────────────┤`);
|
|
148
|
+
console.error(`│ Opening browser to complete authentication... │`);
|
|
149
|
+
console.error(`│ │`);
|
|
150
|
+
console.error(`│ If the browser doesn't open, visit: │`);
|
|
151
|
+
console.error(`│ ${deviceFlow.verification_uri.padEnd(41)} │`);
|
|
152
|
+
console.error(`│ │`);
|
|
153
|
+
console.error(`│ And enter code: ${deviceFlow.user_code.padEnd(28)} │`);
|
|
154
|
+
console.error(`└─────────────────────────────────────────────────┘\n`);
|
|
155
|
+
// Open browser
|
|
156
|
+
try {
|
|
157
|
+
await openBrowser(deviceFlow.verification_uri_complete);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// Browser failed to open, user will need to manually visit the URL
|
|
161
|
+
}
|
|
162
|
+
// Poll for completion
|
|
163
|
+
const pollIntervalMs = deviceFlow.interval * 1000;
|
|
164
|
+
const expiresAt = Date.now() + (deviceFlow.expires_in * 1000);
|
|
165
|
+
while (Date.now() < expiresAt) {
|
|
166
|
+
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
167
|
+
const result = await pollDeviceFlow(deviceFlow.device_code);
|
|
168
|
+
if (result === 'pending') {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (result === 'denied') {
|
|
172
|
+
throw new Error('Authorization denied by user');
|
|
173
|
+
}
|
|
174
|
+
if (result === 'expired') {
|
|
175
|
+
throw new Error('Device code expired. Please try again.');
|
|
176
|
+
}
|
|
177
|
+
// Success - we have tokens
|
|
178
|
+
const credentials = {
|
|
179
|
+
accessToken: result.access_token,
|
|
180
|
+
refreshToken: result.refresh_token,
|
|
181
|
+
expiresAt: Date.now() + (result.expires_in * 1000),
|
|
182
|
+
};
|
|
183
|
+
// Save credentials
|
|
184
|
+
await saveCredentials(credentials);
|
|
185
|
+
console.error(`\n✓ Authentication successful!\n`);
|
|
186
|
+
return credentials;
|
|
187
|
+
}
|
|
188
|
+
throw new Error('Device code expired. Please try again.');
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Ensure we have valid credentials.
|
|
192
|
+
* Will refresh token if expired, or start device flow if no valid credentials.
|
|
193
|
+
*/
|
|
194
|
+
async function ensureAuthenticated() {
|
|
195
|
+
let credentials = await loadCredentials();
|
|
196
|
+
// No credentials - need to authenticate
|
|
197
|
+
if (!credentials) {
|
|
198
|
+
credentials = await performDeviceFlow();
|
|
199
|
+
return credentials.accessToken;
|
|
200
|
+
}
|
|
201
|
+
// Token expired - try to refresh
|
|
202
|
+
if (isTokenExpired(credentials)) {
|
|
203
|
+
const refreshed = await refreshAccessToken(credentials.refreshToken);
|
|
204
|
+
if (refreshed) {
|
|
205
|
+
await saveCredentials(refreshed);
|
|
206
|
+
return refreshed.accessToken;
|
|
207
|
+
}
|
|
208
|
+
// Refresh failed - need to re-authenticate
|
|
209
|
+
await deleteCredentials();
|
|
210
|
+
credentials = await performDeviceFlow();
|
|
211
|
+
return credentials.accessToken;
|
|
212
|
+
}
|
|
213
|
+
return credentials.accessToken;
|
|
214
|
+
}
|
|
215
|
+
// =============================================================================
|
|
216
|
+
// API Request Helper
|
|
217
|
+
// =============================================================================
|
|
218
|
+
/**
|
|
219
|
+
* Generic HTTP request helper with authentication and connection error handling.
|
|
220
|
+
*/
|
|
221
|
+
async function apiRequest(method, path, body, retried = false) {
|
|
222
|
+
const url = `${API_BASE}${path}`;
|
|
223
|
+
// Get valid access token
|
|
224
|
+
const accessToken = await ensureAuthenticated();
|
|
225
|
+
try {
|
|
226
|
+
const headers = {
|
|
227
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
228
|
+
};
|
|
229
|
+
if (body) {
|
|
230
|
+
headers['Content-Type'] = 'application/json';
|
|
231
|
+
}
|
|
232
|
+
const options = {
|
|
233
|
+
method,
|
|
234
|
+
headers,
|
|
235
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
236
|
+
};
|
|
237
|
+
const response = await fetch(url, options);
|
|
238
|
+
// Handle 401 - token might have been invalidated, try re-auth once
|
|
239
|
+
if (response.status === 401) {
|
|
240
|
+
await deleteCredentials();
|
|
241
|
+
if (!retried) {
|
|
242
|
+
return apiRequest(method, path, body, true);
|
|
243
|
+
}
|
|
244
|
+
throw new Error('Authentication failed. Please check your credentials.');
|
|
245
|
+
}
|
|
246
|
+
if (!response.ok) {
|
|
247
|
+
const error = await response.json().catch(() => ({ error: `HTTP ${response.status}` }));
|
|
248
|
+
throw new Error(error.message || error.error || `HTTP ${response.status}`);
|
|
249
|
+
}
|
|
250
|
+
return response.json();
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
// Handle connection errors
|
|
254
|
+
if (error instanceof TypeError && error.message.includes('fetch failed')) {
|
|
255
|
+
throw new Error(`Cannot connect to VTP API at ${API_BASE}. ` +
|
|
256
|
+
`Make sure the API server is running (pnpm dev:api).`);
|
|
257
|
+
}
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* List all deployed apps.
|
|
263
|
+
*/
|
|
264
|
+
export async function listApps() {
|
|
265
|
+
return apiRequest('GET', '/apps');
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* List all supported app types.
|
|
269
|
+
*/
|
|
270
|
+
export async function listAppTypes() {
|
|
271
|
+
const response = await apiRequest('GET', '/guides');
|
|
272
|
+
return response.guides;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Get deployment guide for a specific app type.
|
|
276
|
+
*/
|
|
277
|
+
export async function getDeploymentGuide(type) {
|
|
278
|
+
return apiRequest('GET', `/guides/${encodeURIComponent(type)}`);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Create a tar.gz archive of a directory.
|
|
282
|
+
* Copies the config file (vtp.yaml) into the archive.
|
|
283
|
+
* Respects .gitignore, vtp.yaml ignore patterns, and filters out sensitive files.
|
|
284
|
+
* Returns the path to the temporary tar.gz file.
|
|
285
|
+
*/
|
|
286
|
+
async function createTarGz(sourcePath, configPath, ignorePatterns) {
|
|
287
|
+
const tarPath = join(tmpdir(), `vtp-deploy-${Date.now()}.tar.gz`);
|
|
288
|
+
const destVtpYaml = join(sourcePath, 'vtp.yaml');
|
|
289
|
+
let copiedVtpYaml = false;
|
|
290
|
+
// Copy vtp.yaml into source directory if not already there
|
|
291
|
+
if (!existsSync(destVtpYaml)) {
|
|
292
|
+
await copyFile(configPath, destVtpYaml);
|
|
293
|
+
copiedVtpYaml = true;
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
// Get filtered list of files respecting vtp.yaml ignore, .gitignore, and security defaults
|
|
297
|
+
const filesToInclude = await getFilesToInclude(sourcePath, ignorePatterns);
|
|
298
|
+
// Always include vtp.yaml
|
|
299
|
+
if (!filesToInclude.includes('vtp.yaml')) {
|
|
300
|
+
filesToInclude.push('vtp.yaml');
|
|
301
|
+
}
|
|
302
|
+
// Create tar.gz archive with only the filtered files
|
|
303
|
+
await tar.create({
|
|
304
|
+
gzip: true,
|
|
305
|
+
file: tarPath,
|
|
306
|
+
cwd: sourcePath,
|
|
307
|
+
}, filesToInclude);
|
|
308
|
+
}
|
|
309
|
+
finally {
|
|
310
|
+
// Clean up the copied vtp.yaml
|
|
311
|
+
if (copiedVtpYaml) {
|
|
312
|
+
try {
|
|
313
|
+
await unlink(destVtpYaml);
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
// Ignore cleanup errors
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return tarPath;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Deploy an app from a local directory.
|
|
324
|
+
* Reads vtp.yaml for all config including deploy path, creates tar.gz archive, and POSTs to the API.
|
|
325
|
+
*/
|
|
326
|
+
export async function deploy(configPath = './vtp.yaml', force = false) {
|
|
327
|
+
// Read and parse vtp.yaml
|
|
328
|
+
const yamlContent = await readFile(configPath, 'utf-8');
|
|
329
|
+
const config = parseYaml(yamlContent);
|
|
330
|
+
// Validate required fields
|
|
331
|
+
if (!config.name) {
|
|
332
|
+
throw new Error('vtp.yaml: "name" is required');
|
|
333
|
+
}
|
|
334
|
+
if (!config.type) {
|
|
335
|
+
throw new Error('vtp.yaml: "type" is required (static or node)');
|
|
336
|
+
}
|
|
337
|
+
if (!config.path) {
|
|
338
|
+
throw new Error('vtp.yaml: "path" is required (folder to deploy)');
|
|
339
|
+
}
|
|
340
|
+
// Resolve path relative to vtp.yaml location
|
|
341
|
+
const configDir = dirname(resolve(configPath));
|
|
342
|
+
// Run predeploy commands if specified
|
|
343
|
+
if (config.predeploy) {
|
|
344
|
+
const commands = Array.isArray(config.predeploy)
|
|
345
|
+
? config.predeploy
|
|
346
|
+
: [config.predeploy];
|
|
347
|
+
for (const cmd of commands) {
|
|
348
|
+
await execAsync(cmd, { cwd: configDir });
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const sourcePath = resolve(configDir, config.path);
|
|
352
|
+
if (!existsSync(sourcePath)) {
|
|
353
|
+
throw new Error(`Deploy path does not exist: ${sourcePath}`);
|
|
354
|
+
}
|
|
355
|
+
// Create tar.gz of the directory, including vtp.yaml
|
|
356
|
+
const tarPath = await createTarGz(sourcePath, configPath, config.ignore);
|
|
357
|
+
try {
|
|
358
|
+
return await postDeploy(tarPath, config.name, config.type, force);
|
|
359
|
+
}
|
|
360
|
+
finally {
|
|
361
|
+
// Clean up temp tar file
|
|
362
|
+
await unlink(tarPath).catch(() => { });
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* POST to the /deploy endpoint with tar.gz archive.
|
|
367
|
+
* Uses form-data's submit() method for proper streaming support.
|
|
368
|
+
*/
|
|
369
|
+
async function postDeploy(tarPath, name, type, force, retried = false) {
|
|
370
|
+
// Ensure we have valid authentication
|
|
371
|
+
const accessToken = await ensureAuthenticated();
|
|
372
|
+
return new Promise((resolve, reject) => {
|
|
373
|
+
const form = new FormData();
|
|
374
|
+
form.append('archive', createReadStream(tarPath), {
|
|
375
|
+
filename: 'app.tar.gz',
|
|
376
|
+
contentType: 'application/gzip',
|
|
377
|
+
});
|
|
378
|
+
form.append('name', name);
|
|
379
|
+
form.append('type', type);
|
|
380
|
+
form.append('force', String(force));
|
|
381
|
+
// Parse API_BASE URL
|
|
382
|
+
const url = new URL(`${API_BASE}/deploy`);
|
|
383
|
+
form.submit({
|
|
384
|
+
host: url.hostname,
|
|
385
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
386
|
+
path: url.pathname,
|
|
387
|
+
protocol: url.protocol,
|
|
388
|
+
headers: {
|
|
389
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
390
|
+
},
|
|
391
|
+
}, (err, res) => {
|
|
392
|
+
if (err) {
|
|
393
|
+
// Handle connection errors
|
|
394
|
+
if (err.message.includes('ECONNREFUSED')) {
|
|
395
|
+
reject(new Error(`Cannot connect to VTP API at ${API_BASE}. ` +
|
|
396
|
+
`Make sure the API server is running (pnpm dev:api).`));
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
reject(err);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
// Handle 401 - try re-auth once
|
|
403
|
+
if (res.statusCode === 401 && !retried) {
|
|
404
|
+
deleteCredentials()
|
|
405
|
+
.then(() => postDeploy(tarPath, name, type, force, true))
|
|
406
|
+
.then(resolve)
|
|
407
|
+
.catch(reject);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (res.statusCode === 401) {
|
|
411
|
+
reject(new Error('Authentication failed. Please check your credentials.'));
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
let body = '';
|
|
415
|
+
res.on('data', (chunk) => {
|
|
416
|
+
body += chunk;
|
|
417
|
+
});
|
|
418
|
+
res.on('end', () => {
|
|
419
|
+
try {
|
|
420
|
+
resolve(JSON.parse(body));
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
reject(new Error(`Invalid JSON response: ${body}`));
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
res.on('error', reject);
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { existsSync } from 'fs';
|
|
6
|
+
import * as client from './client.js';
|
|
7
|
+
import { DeploySchema, GuideTypeSchema, toolDefinitions } from './tools.js';
|
|
8
|
+
const server = new Server({
|
|
9
|
+
name: 'vtp',
|
|
10
|
+
version: '0.7.0',
|
|
11
|
+
}, {
|
|
12
|
+
capabilities: {
|
|
13
|
+
tools: {},
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
// List available tools
|
|
17
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
18
|
+
tools: toolDefinitions,
|
|
19
|
+
}));
|
|
20
|
+
// Handle tool calls - all delegate to HTTP API via client
|
|
21
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
22
|
+
const { name, arguments: args } = request.params;
|
|
23
|
+
try {
|
|
24
|
+
switch (name) {
|
|
25
|
+
case 'list_app_types': {
|
|
26
|
+
const types = await client.listAppTypes();
|
|
27
|
+
const typeList = types
|
|
28
|
+
.map(t => `- **${t.displayName}** (${t.type})\n ${t.description}`)
|
|
29
|
+
.join('\n');
|
|
30
|
+
return {
|
|
31
|
+
content: [{
|
|
32
|
+
type: 'text',
|
|
33
|
+
text: `Supported app types:\n\n${typeList}\n\n` +
|
|
34
|
+
`Use get_deployment_guide with a type to see detailed instructions.`,
|
|
35
|
+
}],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
case 'get_deployment_guide': {
|
|
39
|
+
const { type: guideType } = GuideTypeSchema.parse(args);
|
|
40
|
+
try {
|
|
41
|
+
const guide = await client.getDeploymentGuide(guideType);
|
|
42
|
+
return {
|
|
43
|
+
content: [{
|
|
44
|
+
type: 'text',
|
|
45
|
+
text: guide.content,
|
|
46
|
+
}],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (error instanceof Error && error.message.includes('guide_not_found')) {
|
|
51
|
+
const types = await client.listAppTypes();
|
|
52
|
+
const available = types.map(t => t.type).join(', ');
|
|
53
|
+
return {
|
|
54
|
+
content: [{
|
|
55
|
+
type: 'text',
|
|
56
|
+
text: `Unknown app type: "${guideType}"\n\nAvailable types: ${available}`,
|
|
57
|
+
}],
|
|
58
|
+
isError: true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
case 'deploy': {
|
|
65
|
+
const { config, force } = DeploySchema.parse(args);
|
|
66
|
+
const configPath = config || './vtp.yaml';
|
|
67
|
+
// Validate config exists
|
|
68
|
+
if (!existsSync(configPath)) {
|
|
69
|
+
return {
|
|
70
|
+
content: [{
|
|
71
|
+
type: 'text',
|
|
72
|
+
text: `Error: Config file not found: ${configPath}\n\n` +
|
|
73
|
+
`Create a vtp.yaml file with:\n` +
|
|
74
|
+
` name: My App Name # Display name\n` +
|
|
75
|
+
` id: my-app # Optional: URL slug\n` +
|
|
76
|
+
` type: static # or "node"\n` +
|
|
77
|
+
` path: ./dist # folder to deploy\n\n` +
|
|
78
|
+
`Use list_app_types and get_deployment_guide for help.`,
|
|
79
|
+
}],
|
|
80
|
+
isError: true,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// Deploy via HTTP API
|
|
84
|
+
const result = await client.deploy(configPath, force ?? false);
|
|
85
|
+
// Handle conflict
|
|
86
|
+
if (result.error === 'conflict') {
|
|
87
|
+
const existingApp = result.existingApp;
|
|
88
|
+
return {
|
|
89
|
+
content: [{
|
|
90
|
+
type: 'text',
|
|
91
|
+
text: `App '${existingApp?.id}' already exists at ${existingApp?.url || 'unknown URL'}\n` +
|
|
92
|
+
`Name: ${existingApp?.name || 'unknown'}\n` +
|
|
93
|
+
`Status: ${existingApp?.status || 'unknown'}\n\n` +
|
|
94
|
+
`Use force: true to replace it.`,
|
|
95
|
+
}],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// Handle other errors
|
|
99
|
+
if (result.error) {
|
|
100
|
+
return {
|
|
101
|
+
content: [{
|
|
102
|
+
type: 'text',
|
|
103
|
+
text: `Error: ${result.message || result.error}`,
|
|
104
|
+
}],
|
|
105
|
+
isError: true,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
// Success
|
|
109
|
+
const app = result.app;
|
|
110
|
+
const prefix = result.replaced ? 'Replaced' : 'Deployed';
|
|
111
|
+
return {
|
|
112
|
+
content: [{
|
|
113
|
+
type: 'text',
|
|
114
|
+
text: `${prefix} ${app.name} (@${app.id})\n` +
|
|
115
|
+
` URL: ${app.url}\n` +
|
|
116
|
+
` Type: ${app.type}\n` +
|
|
117
|
+
` Status: ${app.status}`,
|
|
118
|
+
}],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
case 'list': {
|
|
122
|
+
const apps = await client.listApps();
|
|
123
|
+
if (apps.length === 0) {
|
|
124
|
+
return {
|
|
125
|
+
content: [{
|
|
126
|
+
type: 'text',
|
|
127
|
+
text: 'No apps deployed yet.',
|
|
128
|
+
}],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const appList = apps
|
|
132
|
+
.map(app => {
|
|
133
|
+
const desc = app.description ? `\n ${app.description}` : '';
|
|
134
|
+
return `- ${app.name} (@${app.id}) - ${app.type} - ${app.status}${desc}\n ${app.url}`;
|
|
135
|
+
})
|
|
136
|
+
.join('\n');
|
|
137
|
+
return {
|
|
138
|
+
content: [{
|
|
139
|
+
type: 'text',
|
|
140
|
+
text: `Deployed apps:\n${appList}`,
|
|
141
|
+
}],
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
default:
|
|
145
|
+
return {
|
|
146
|
+
content: [{
|
|
147
|
+
type: 'text',
|
|
148
|
+
text: `Unknown tool: ${name}`,
|
|
149
|
+
}],
|
|
150
|
+
isError: true,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
156
|
+
return {
|
|
157
|
+
content: [{
|
|
158
|
+
type: 'text',
|
|
159
|
+
text: `Error: ${message}`,
|
|
160
|
+
}],
|
|
161
|
+
isError: true,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
// Start the server
|
|
166
|
+
async function main() {
|
|
167
|
+
const transport = new StdioServerTransport();
|
|
168
|
+
await server.connect(transport);
|
|
169
|
+
console.error('VTP MCP server running on stdio');
|
|
170
|
+
}
|
|
171
|
+
main().catch(console.error);
|
|
172
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const DeploySchema: z.ZodObject<{
|
|
3
|
+
config: z.ZodOptional<z.ZodString>;
|
|
4
|
+
force: z.ZodOptional<z.ZodBoolean>;
|
|
5
|
+
}, "strip", z.ZodTypeAny, {
|
|
6
|
+
force?: boolean | undefined;
|
|
7
|
+
config?: string | undefined;
|
|
8
|
+
}, {
|
|
9
|
+
force?: boolean | undefined;
|
|
10
|
+
config?: string | undefined;
|
|
11
|
+
}>;
|
|
12
|
+
export declare const GuideTypeSchema: z.ZodObject<{
|
|
13
|
+
type: z.ZodString;
|
|
14
|
+
}, "strip", z.ZodTypeAny, {
|
|
15
|
+
type: string;
|
|
16
|
+
}, {
|
|
17
|
+
type: string;
|
|
18
|
+
}>;
|
|
19
|
+
export declare const toolDefinitions: ({
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: string;
|
|
24
|
+
properties: {
|
|
25
|
+
type?: undefined;
|
|
26
|
+
config?: undefined;
|
|
27
|
+
force?: undefined;
|
|
28
|
+
};
|
|
29
|
+
required?: undefined;
|
|
30
|
+
};
|
|
31
|
+
} | {
|
|
32
|
+
name: string;
|
|
33
|
+
description: string;
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: string;
|
|
36
|
+
properties: {
|
|
37
|
+
type: {
|
|
38
|
+
type: string;
|
|
39
|
+
description: string;
|
|
40
|
+
};
|
|
41
|
+
config?: undefined;
|
|
42
|
+
force?: undefined;
|
|
43
|
+
};
|
|
44
|
+
required: string[];
|
|
45
|
+
};
|
|
46
|
+
} | {
|
|
47
|
+
name: string;
|
|
48
|
+
description: string;
|
|
49
|
+
inputSchema: {
|
|
50
|
+
type: string;
|
|
51
|
+
properties: {
|
|
52
|
+
config: {
|
|
53
|
+
type: string;
|
|
54
|
+
description: string;
|
|
55
|
+
};
|
|
56
|
+
force: {
|
|
57
|
+
type: string;
|
|
58
|
+
description: string;
|
|
59
|
+
};
|
|
60
|
+
type?: undefined;
|
|
61
|
+
};
|
|
62
|
+
required: never[];
|
|
63
|
+
};
|
|
64
|
+
})[];
|
|
65
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,YAAY;;;;;;;;;EAGvB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;EAE1B,CAAC;AAGH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAwG3B,CAAC"}
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
// Tool parameter schemas
|
|
3
|
+
export const DeploySchema = z.object({
|
|
4
|
+
config: z.string().optional().describe('Path to vtp.yaml (default: ./vtp.yaml)'),
|
|
5
|
+
force: z.boolean().optional().describe('Replace existing app with same name'),
|
|
6
|
+
});
|
|
7
|
+
export const GuideTypeSchema = z.object({
|
|
8
|
+
type: z.string().describe('App type from list_app_types (e.g., "nextjs", "spa", "node")'),
|
|
9
|
+
});
|
|
10
|
+
// Tool definitions for MCP
|
|
11
|
+
export const toolDefinitions = [
|
|
12
|
+
{
|
|
13
|
+
name: 'list_app_types',
|
|
14
|
+
description: `List supported app types for deployment.
|
|
15
|
+
|
|
16
|
+
DEPLOYMENT WORKFLOW (all steps required):
|
|
17
|
+
1. list_app_types → identify your app type
|
|
18
|
+
2. get_deployment_guide → ALWAYS call this before deploy, even if vtp.yaml exists
|
|
19
|
+
3. list → check if app already exists (redeploy vs new)
|
|
20
|
+
4. Review files in deploy path, add 'ignore' to vtp.yaml for files not needed at runtime
|
|
21
|
+
5. deploy → deploy the app
|
|
22
|
+
|
|
23
|
+
After identifying the type, call get_deployment_guide to get the vtp.yaml template.`,
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'get_deployment_guide',
|
|
31
|
+
description: `Get deployment instructions and vtp.yaml template for an app type.
|
|
32
|
+
|
|
33
|
+
MANDATORY: Call this before EVERY deployment - even if vtp.yaml already exists.
|
|
34
|
+
This ensures your configuration matches the latest requirements.
|
|
35
|
+
|
|
36
|
+
Returns a vtp.yaml template with 'predeploy' commands that automatically handle:
|
|
37
|
+
- Building the app (npm run build, etc.)
|
|
38
|
+
- Copying assets (for Next.js standalone, etc.)
|
|
39
|
+
|
|
40
|
+
CRITICAL FOR NODE APPS: Before creating vtp.yaml, check if the app uses SQLite or writes files:
|
|
41
|
+
- Look for: better-sqlite3, sql.js, sqlite3, prisma with SQLite, fs.writeFile to data files
|
|
42
|
+
- If found: You MUST add 'volumes' config AND update the app to use the volume path
|
|
43
|
+
- Without volumes, all data is lost on every redeployment
|
|
44
|
+
|
|
45
|
+
Copy the vtp.yaml template, adjust the app name, then call deploy.`,
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
type: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
description: 'App type from list_app_types (e.g., "nextjs", "spa", "node")',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
required: ['type'],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'deploy',
|
|
59
|
+
description: `PREREQUISITES (complete ALL before deploying):
|
|
60
|
+
1. Call get_deployment_guide to verify vtp.yaml config (even if it already exists)
|
|
61
|
+
2. Call list to check if this app already exists (redeploy vs new deployment)
|
|
62
|
+
3. Review files in deploy path - add 'ignore' to vtp.yaml for files not needed at runtime
|
|
63
|
+
|
|
64
|
+
Deploy a web app to VTP.
|
|
65
|
+
|
|
66
|
+
The vtp.yaml 'predeploy' commands run automatically before packaging.
|
|
67
|
+
You do NOT need to manually run build commands - predeploy handles it.
|
|
68
|
+
|
|
69
|
+
REQUIRED: vtp.yaml with:
|
|
70
|
+
name: My App Name # Display name (shown in dashboard)
|
|
71
|
+
id: my-app # Optional: URL slug (auto-generated from name if omitted)
|
|
72
|
+
description: Brief description of the app
|
|
73
|
+
type: static|node
|
|
74
|
+
path: ./dist
|
|
75
|
+
predeploy: npm run build # Runs automatically!
|
|
76
|
+
ignore: # Exclude unnecessary files
|
|
77
|
+
- node_modules # ALWAYS exclude - reinstalled in container
|
|
78
|
+
- src # Source files if deploying compiled output
|
|
79
|
+
|
|
80
|
+
NAME vs ID:
|
|
81
|
+
- name: Human-friendly display name (e.g., "My Budget Tracker")
|
|
82
|
+
- id: URL-safe identifier used for: https://{id}.{user}.myvtp.dev, container name, volumes
|
|
83
|
+
If omitted, auto-generated from name: "My Budget Tracker" → "my-budget-tracker"
|
|
84
|
+
Rules: lowercase, letters/numbers/hyphens only, max 63 chars
|
|
85
|
+
|
|
86
|
+
CRITICAL - FOR APPS WITH DATABASES OR FILE STORAGE:
|
|
87
|
+
You MUST add volumes or data will be lost on redeploy:
|
|
88
|
+
volumes:
|
|
89
|
+
data: /app/data
|
|
90
|
+
AND update the app code to write to the volume path (e.g., /app/data/app.db)
|
|
91
|
+
|
|
92
|
+
FILE EXCLUSION:
|
|
93
|
+
- .env files and .git are excluded automatically (security)
|
|
94
|
+
- .gitignore patterns are respected if the file exists
|
|
95
|
+
- Add 'ignore' in vtp.yaml for anything else not needed at runtime`,
|
|
96
|
+
inputSchema: {
|
|
97
|
+
type: 'object',
|
|
98
|
+
properties: {
|
|
99
|
+
config: { type: 'string', description: 'Path to vtp.yaml (default: ./vtp.yaml)' },
|
|
100
|
+
force: { type: 'boolean', description: 'Replace existing app with same id' },
|
|
101
|
+
},
|
|
102
|
+
required: [],
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'list',
|
|
107
|
+
description: `List all deployed apps with their status and URLs.
|
|
108
|
+
|
|
109
|
+
Call this before deploying to check if the app already exists (redeploy requires force flag).`,
|
|
110
|
+
inputSchema: {
|
|
111
|
+
type: 'object',
|
|
112
|
+
properties: {},
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
];
|
|
116
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,yBAAyB;AACzB,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;IAChF,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;CAC9E,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8DAA8D,CAAC;CAC1F,CAAC,CAAC;AAEH,2BAA2B;AAC3B,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B;QACE,IAAI,EAAE,gBAAgB;QACtB,WAAW,EAAE;;;;;;;;;oFASmE;QAChF,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,EAAE;SACf;KACF;IACD;QACE,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE;;;;;;;;;;;;;;mEAckD;QAC/D,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,IAAI,EAAE;oBACJ,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,8DAA8D;iBAC5E;aACF;YACD,QAAQ,EAAE,CAAC,MAAM,CAAC;SACnB;KACF;IACD;QACE,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEAoCkD;QAC/D,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,wCAAwC,EAAE;gBACjF,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,mCAAmC,EAAE;aAC7E;YACD,QAAQ,EAAE,EAAE;SACb;KACF;IACD;QACE,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE;;8FAE6E;QAC1F,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,EAAE;SACf;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Ignore } from 'ignore';
|
|
2
|
+
/**
|
|
3
|
+
* Create an ignore filter for a directory.
|
|
4
|
+
* Priority: security defaults > vtp.yaml ignore > .gitignore
|
|
5
|
+
*/
|
|
6
|
+
export declare function createIgnoreFilter(rootPath: string, configIgnore?: string[]): Promise<Ignore>;
|
|
7
|
+
/**
|
|
8
|
+
* Get all files to include in the archive, respecting ignore patterns.
|
|
9
|
+
* Returns relative paths from the root.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getFilesToInclude(rootPath: string, configIgnore?: string[], ig?: Ignore, basePath?: string): Promise<string[]>;
|
|
12
|
+
//# sourceMappingURL=ignoreFilter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignoreFilter.d.ts","sourceRoot":"","sources":["../../src/utils/ignoreFilter.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAqBxC;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,EAAE,GACtB,OAAO,CAAC,MAAM,CAAC,CAkBjB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,EAAE,EACvB,EAAE,CAAC,EAAE,MAAM,EACX,QAAQ,GAAE,MAAW,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CAmCnB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import ignore from 'ignore';
|
|
2
|
+
import { readFile, readdir, stat } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* Patterns that are ALWAYS excluded for security.
|
|
6
|
+
* These apply even if no .gitignore exists.
|
|
7
|
+
*
|
|
8
|
+
* Keep this list minimal - users control other exclusions via .gitignore.
|
|
9
|
+
*/
|
|
10
|
+
const ALWAYS_IGNORED = [
|
|
11
|
+
// Environment files (secrets) - ALWAYS excluded
|
|
12
|
+
'.env',
|
|
13
|
+
'.env.*',
|
|
14
|
+
'.env.local',
|
|
15
|
+
'.env.*.local',
|
|
16
|
+
// Version control - never needed in deployment
|
|
17
|
+
'.git',
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Create an ignore filter for a directory.
|
|
21
|
+
* Priority: security defaults > vtp.yaml ignore > .gitignore
|
|
22
|
+
*/
|
|
23
|
+
export async function createIgnoreFilter(rootPath, configIgnore) {
|
|
24
|
+
const ig = ignore().add(ALWAYS_IGNORED);
|
|
25
|
+
// Add patterns from vtp.yaml ignore field
|
|
26
|
+
if (configIgnore && configIgnore.length > 0) {
|
|
27
|
+
ig.add(configIgnore);
|
|
28
|
+
}
|
|
29
|
+
// Load .gitignore if present
|
|
30
|
+
try {
|
|
31
|
+
const gitignorePath = join(rootPath, '.gitignore');
|
|
32
|
+
const content = await readFile(gitignorePath, 'utf-8');
|
|
33
|
+
ig.add(content);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// No .gitignore file, that's fine
|
|
37
|
+
}
|
|
38
|
+
return ig;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get all files to include in the archive, respecting ignore patterns.
|
|
42
|
+
* Returns relative paths from the root.
|
|
43
|
+
*/
|
|
44
|
+
export async function getFilesToInclude(rootPath, configIgnore, ig, basePath = '') {
|
|
45
|
+
// Initialise ignore filter at root level
|
|
46
|
+
if (!ig) {
|
|
47
|
+
ig = await createIgnoreFilter(rootPath, configIgnore);
|
|
48
|
+
}
|
|
49
|
+
const files = [];
|
|
50
|
+
const entries = await readdir(join(rootPath, basePath));
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
const relativePath = basePath ? `${basePath}/${entry}` : entry;
|
|
53
|
+
const fullPath = join(rootPath, relativePath);
|
|
54
|
+
// Check if this path should be ignored
|
|
55
|
+
if (ig.ignores(relativePath)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const entryStat = await stat(fullPath);
|
|
59
|
+
if (entryStat.isDirectory()) {
|
|
60
|
+
// Check if directory itself is ignored (with trailing slash)
|
|
61
|
+
if (ig.ignores(relativePath + '/')) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// Recurse into directory
|
|
65
|
+
const subFiles = await getFilesToInclude(rootPath, configIgnore, ig, relativePath);
|
|
66
|
+
files.push(...subFiles);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
files.push(relativePath);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return files;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=ignoreFilter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignoreFilter.js","sourceRoot":"","sources":["../../src/utils/ignoreFilter.ts"],"names":[],"mappings":"AAAA,OAAO,MAAkB,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B;;;;;GAKG;AACH,MAAM,cAAc,GAAG;IACrB,gDAAgD;IAChD,MAAM;IACN,QAAQ;IACR,YAAY;IACZ,cAAc;IAEd,+CAA+C;IAC/C,MAAM;CACP,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,QAAgB,EAChB,YAAuB;IAEvB,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAExC,0CAA0C;IAC1C,IAAI,YAAY,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5C,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACvB,CAAC;IAED,6BAA6B;IAC7B,IAAI,CAAC;QACH,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QACvD,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,kCAAkC;IACpC,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,QAAgB,EAChB,YAAuB,EACvB,EAAW,EACX,WAAmB,EAAE;IAErB,yCAAyC;IACzC,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,EAAE,GAAG,MAAM,kBAAkB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAExD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;QAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAE9C,uCAAuC;QACvC,IAAI,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC7B,SAAS;QACX,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEvC,IAAI,SAAS,CAAC,WAAW,EAAE,EAAE,CAAC;YAC5B,6DAA6D;YAC7D,IAAI,EAAE,CAAC,OAAO,CAAC,YAAY,GAAG,GAAG,CAAC,EAAE,CAAC;gBACnC,SAAS;YACX,CAAC;YAED,yBAAyB;YACzB,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,CAAC,CAAC;YACnF,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@myvtp/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for VTP - deploy apps via Claude Code",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"myvtp-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsx watch src/index.ts",
|
|
16
|
+
"start": "node dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/myvtp/mcp.git"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp",
|
|
24
|
+
"claude",
|
|
25
|
+
"deployment",
|
|
26
|
+
"vtp"
|
|
27
|
+
],
|
|
28
|
+
"author": "myvtp",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/myvtp/mcp/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/myvtp/mcp#readme",
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
36
|
+
"form-data": "^4.0.0",
|
|
37
|
+
"ignore": "^7.0.5",
|
|
38
|
+
"tar": "^7.5.7",
|
|
39
|
+
"yaml": "^2.8.2",
|
|
40
|
+
"zod": "^3.22.4"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^20.10.0",
|
|
44
|
+
"tsx": "^4.7.0",
|
|
45
|
+
"typescript": "^5.3.3"
|
|
46
|
+
}
|
|
47
|
+
}
|