@velixir/mcp 0.1.2 → 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.
- package/README.md +13 -2
- package/package.json +2 -2
- package/src/index.js +62 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @velixir/mcp
|
|
2
2
|
|
|
3
|
-
Deploy web apps in **any language** to [velixir](https://velixir.net), the European cloud, straight from your AI agent.
|
|
3
|
+
Deploy web apps in **any language** to [velixir](https://velixir.net), the European cloud, straight from your AI agent. `deploy` points at a source directory, packs the code, builds it with no Dockerfile, and returns a live URL plus a claim link the human opens to keep the app on a free, card-free account. `logs` fetches the build and runtime logs so the agent can debug what it shipped. No velixir account is needed.
|
|
4
4
|
|
|
5
5
|
## Why
|
|
6
6
|
|
|
@@ -42,10 +42,21 @@ Point the editor's MCP settings at the same command (`npx -y @velixir/mcp`).
|
|
|
42
42
|
| `language` | no | node, python, go, ruby, php, java, elixir, rust, dotnet, deno, fsharp. Auto-detected if omitted. |
|
|
43
43
|
| `port` | no | Only if the app hard-codes a port instead of reading the `PORT` env var. |
|
|
44
44
|
|
|
45
|
-
It returns the live URL, a claim URL (give this to the user), when the claim window closes, and a status URL to poll.
|
|
45
|
+
It returns the live URL, a claim URL (give this to the user), when the claim window closes, a **deployment ID** (pass it to `logs`), and a status URL to poll.
|
|
46
46
|
|
|
47
47
|
`node_modules`, `.git`, build output, and caches are skipped automatically. Nixpacks reinstalls dependencies and builds from source.
|
|
48
48
|
|
|
49
|
+
## The `logs` tool
|
|
50
|
+
|
|
51
|
+
Fetch recent build and runtime logs for an app you deployed, so you can debug without an account: a failed build returns the build error, a running app returns its recent container output.
|
|
52
|
+
|
|
53
|
+
| Input | Required | Description |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| `deployment_id` | yes | The deployment ID from the `deploy` response. |
|
|
56
|
+
| `tail` | no | How many recent lines to fetch (default 200, max 1000). |
|
|
57
|
+
|
|
58
|
+
Works for unclaimed (agent-deployed) apps addressed by deployment ID. Once an app is claimed it moves to the owner's account; use the dashboard or the authenticated API for a claimed app's logs.
|
|
59
|
+
|
|
49
60
|
## Config
|
|
50
61
|
|
|
51
62
|
| Env var | Default | Description |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velixir/mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Deploy web apps in any language to Velixir (the European cloud) straight from your AI agent
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Deploy web apps in any language to Velixir (the European cloud) straight from your AI agent: point deploy at a directory for a live URL and claim link, then read logs to debug what you shipped.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"velixir-mcp": "src/index.js"
|
package/src/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Velixir MCP server. Exposes
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// on a free, card-free account.
|
|
2
|
+
// Velixir MCP server. Exposes two tools:
|
|
3
|
+
// - `deploy`: pack a source directory and POST it to Velixir's account-less deploy
|
|
4
|
+
// endpoint (POST /api/v1/deploy), returning a live URL + a single-use claim link
|
|
5
|
+
// the human opens to keep the app on a free, card-free account.
|
|
6
|
+
// - `logs`: fetch recent build + runtime logs for a deployment by its ID
|
|
7
|
+
// (GET /api/v1/deploy/{id}/logs), so the agent can debug what it just shipped.
|
|
8
|
+
// No Velixir account is needed for either.
|
|
6
9
|
//
|
|
7
10
|
// Zero setup: run with `npx -y @velixir/mcp`. Point VELIXIR_API_URL at a different
|
|
8
11
|
// host for staging/self-host (default https://velixir.net).
|
|
@@ -43,7 +46,7 @@ async function tarballOf(dir) {
|
|
|
43
46
|
return Buffer.concat(chunks);
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
const server = new McpServer({ name: "velixir", version: "0.
|
|
49
|
+
const server = new McpServer({ name: "velixir", version: "0.2.0" });
|
|
47
50
|
|
|
48
51
|
server.tool(
|
|
49
52
|
"deploy",
|
|
@@ -108,6 +111,9 @@ server.tool(
|
|
|
108
111
|
? `Claim URL: ${data.claim_url}\n(Give this to the user. Opening it keeps the app on a free, card-free Velixir account; unclaimed apps are removed after a short window.)`
|
|
109
112
|
: null,
|
|
110
113
|
data.claim_expires_at ? `Claim before: ${data.claim_expires_at}` : null,
|
|
114
|
+
data.deployment_id
|
|
115
|
+
? `Deployment ID: ${data.deployment_id}\n(Use the "logs" tool with this ID to see build/runtime logs and debug if the app doesn't come up.)`
|
|
116
|
+
: null,
|
|
111
117
|
data.status_url ? `Deployment status: ${data.status_url}` : null,
|
|
112
118
|
].filter(Boolean);
|
|
113
119
|
|
|
@@ -115,5 +121,56 @@ server.tool(
|
|
|
115
121
|
},
|
|
116
122
|
);
|
|
117
123
|
|
|
124
|
+
server.tool(
|
|
125
|
+
"logs",
|
|
126
|
+
"Fetch recent build and runtime logs for an app you deployed to Velixir, by its deployment ID (from the `deploy` tool's response). Use this to debug: if the build failed you get the build error; if it built but isn't responding you get the container's recent output. No Velixir account is needed.",
|
|
127
|
+
{
|
|
128
|
+
deployment_id: z
|
|
129
|
+
.string()
|
|
130
|
+
.describe("The deployment ID returned by the `deploy` tool."),
|
|
131
|
+
tail: z
|
|
132
|
+
.number()
|
|
133
|
+
.int()
|
|
134
|
+
.optional()
|
|
135
|
+
.describe("How many recent log lines to fetch (default 200, max 1000)."),
|
|
136
|
+
},
|
|
137
|
+
async ({ deployment_id, tail }) => {
|
|
138
|
+
const qs = tail != null ? `?tail=${encodeURIComponent(tail)}` : "";
|
|
139
|
+
const url = `${API_URL}/api/v1/deploy/${encodeURIComponent(deployment_id)}/logs${qs}`;
|
|
140
|
+
|
|
141
|
+
let res, body;
|
|
142
|
+
try {
|
|
143
|
+
res = await fetch(url);
|
|
144
|
+
body = await res.text();
|
|
145
|
+
} catch (err) {
|
|
146
|
+
return { isError: true, content: [{ type: "text", text: `Could not reach Velixir at ${API_URL}: ${err?.message ?? err}` }] };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (res.status === 404) {
|
|
150
|
+
return { isError: true, content: [{ type: "text", text: `No logs for deployment ${deployment_id}. It may not exist, may already be claimed (use the dashboard for claimed apps), or its unclaimed window may have passed.` }] };
|
|
151
|
+
}
|
|
152
|
+
if (!res.ok) {
|
|
153
|
+
return { isError: true, content: [{ type: "text", text: `Could not fetch logs (HTTP ${res.status}): ${body}` }] };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let data;
|
|
157
|
+
try { data = JSON.parse(body); } catch { data = {}; }
|
|
158
|
+
|
|
159
|
+
const out = [];
|
|
160
|
+
if (data.status) out.push(`Status: ${data.status}`);
|
|
161
|
+
if (data.build_error) out.push(`Build error:\n${data.build_error}`);
|
|
162
|
+
|
|
163
|
+
const logLines = Array.isArray(data.lines) ? data.lines : [];
|
|
164
|
+
if (logLines.length) {
|
|
165
|
+
out.push("Recent logs:");
|
|
166
|
+
for (const l of logLines) out.push(l.line ?? "");
|
|
167
|
+
} else if (!data.build_error) {
|
|
168
|
+
out.push("No log lines yet (the app may still be starting, or has produced no output).");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return { content: [{ type: "text", text: out.join("\n") }] };
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
|
|
118
175
|
const transport = new StdioServerTransport();
|
|
119
176
|
await server.connect(transport);
|