jetty-mcp-server 1.0.3 → 1.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/README.md +59 -0
- package/dist/api-client.d.ts +71 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +201 -0
- package/dist/api-client.js.map +1 -0
- package/dist/client.d.ts +12 -39
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +21 -187
- package/dist/client.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib.d.ts +14 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +11 -0
- package/dist/lib.js.map +1 -0
- package/dist/tool-definitions.d.ts +49 -0
- package/dist/tool-definitions.d.ts.map +1 -0
- package/dist/tool-definitions.js +428 -0
- package/dist/tool-definitions.js.map +1 -0
- package/dist/tools.d.ts +8 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +10 -339
- package/dist/tools.js.map +1 -1
- package/dist/webmcp.d.ts +61 -0
- package/dist/webmcp.d.ts.map +1 -0
- package/dist/webmcp.js +98 -0
- package/dist/webmcp.js.map +1 -0
- package/package.json +49 -6
- package/src/client.ts +0 -332
- package/src/index.ts +0 -17
- package/src/tools.ts +0 -585
- package/tests/auth.test.mjs +0 -129
- package/tests/routines.test.mjs +0 -236
- package/tsconfig.json +0 -18
package/src/client.ts
DELETED
|
@@ -1,332 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
|
|
5
|
-
const DEFAULT_API_URL = "https://flows-api.jetty.io";
|
|
6
|
-
|
|
7
|
-
// The `jetty` CLI / Claude Code skill writes the collection-scoped `mlc_` key
|
|
8
|
-
// here on `jetty login`. We fall back to it so an already-logged-in user does
|
|
9
|
-
// not also have to plumb JETTY_API_TOKEN into the MCP server's env. Resolved
|
|
10
|
-
// lazily (not memoized at module load) so HOME changes are honoured.
|
|
11
|
-
function tokenFilePath(): string {
|
|
12
|
-
return join(homedir(), ".config", "jetty", "token");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class JettyClient {
|
|
16
|
-
private token: string | undefined;
|
|
17
|
-
private apiUrl: string;
|
|
18
|
-
|
|
19
|
-
constructor() {
|
|
20
|
-
this.token = resolveToken();
|
|
21
|
-
this.apiUrl = process.env.JETTY_API_URL || DEFAULT_API_URL;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
private requireToken(): string {
|
|
25
|
-
if (!this.token) {
|
|
26
|
-
throw new Error(
|
|
27
|
-
"No Jetty token found. Set JETTY_API_TOKEN, or run `jetty login` " +
|
|
28
|
-
`(writes ${tokenFilePath()}). ` +
|
|
29
|
-
"Get a token at https://jetty.io → Settings → API Tokens"
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
return this.token;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
private async request(
|
|
36
|
-
path: string,
|
|
37
|
-
options: RequestInit = {}
|
|
38
|
-
): Promise<unknown> {
|
|
39
|
-
const url = `${this.apiUrl}${path}`;
|
|
40
|
-
const headers: Record<string, string> = {
|
|
41
|
-
Authorization: `Bearer ${this.requireToken()}`,
|
|
42
|
-
...((options.headers as Record<string, string>) || {}),
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const res = await fetch(url, { ...options, headers });
|
|
46
|
-
|
|
47
|
-
if (!res.ok) {
|
|
48
|
-
const body = await res.text();
|
|
49
|
-
throw new Error(`Jetty API error ${res.status}: ${body}`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const contentType = res.headers.get("content-type") || "";
|
|
53
|
-
if (contentType.includes("application/json")) {
|
|
54
|
-
return res.json();
|
|
55
|
-
}
|
|
56
|
-
return res.text();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
private api(path: string, options?: RequestInit) {
|
|
60
|
-
return this.request(path, options);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Collections
|
|
64
|
-
async listCollections() {
|
|
65
|
-
return this.api("/api/v1/collections/");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async getCollection(collection: string) {
|
|
69
|
-
return this.api(`/api/v1/collections/${collection}`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Tasks
|
|
73
|
-
async listTasks(collection: string) {
|
|
74
|
-
return this.api(`/api/v1/tasks/${collection}/`);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async getTask(collection: string, task: string) {
|
|
78
|
-
return this.api(`/api/v1/tasks/${collection}/${task}`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async createTask(
|
|
82
|
-
collection: string,
|
|
83
|
-
name: string,
|
|
84
|
-
workflow: unknown,
|
|
85
|
-
description?: string
|
|
86
|
-
) {
|
|
87
|
-
return this.api(`/api/v1/tasks/${collection}`, {
|
|
88
|
-
method: "POST",
|
|
89
|
-
headers: { "Content-Type": "application/json" },
|
|
90
|
-
body: JSON.stringify({ name, description: description || "", workflow }),
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async updateTask(
|
|
95
|
-
collection: string,
|
|
96
|
-
task: string,
|
|
97
|
-
updates: { workflow?: unknown; description?: string }
|
|
98
|
-
) {
|
|
99
|
-
return this.api(`/api/v1/tasks/${collection}/${task}`, {
|
|
100
|
-
method: "PUT",
|
|
101
|
-
headers: { "Content-Type": "application/json" },
|
|
102
|
-
body: JSON.stringify(updates),
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async deleteTask(collection: string, task: string) {
|
|
107
|
-
return this.api(`/api/v1/tasks/${collection}/${task}`, {
|
|
108
|
-
method: "DELETE",
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Trial keys
|
|
113
|
-
async getTrialStatus(collection: string) {
|
|
114
|
-
return this.api(`/api/v1/trial/${collection}`);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async activateTrial(collection: string) {
|
|
118
|
-
return this.api(`/api/v1/trial/${collection}/activate`, {
|
|
119
|
-
method: "POST",
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Collection environment
|
|
124
|
-
async getCollectionEnvironment(collection: string) {
|
|
125
|
-
return this.api(`/api/v1/collections/${collection}/environment`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Run workflows
|
|
129
|
-
async runWorkflow(
|
|
130
|
-
collection: string,
|
|
131
|
-
task: string,
|
|
132
|
-
initParams?: Record<string, unknown>,
|
|
133
|
-
useTrialKeys?: boolean
|
|
134
|
-
) {
|
|
135
|
-
const formData = new FormData();
|
|
136
|
-
formData.append("init_params", JSON.stringify(initParams || {}));
|
|
137
|
-
if (useTrialKeys) {
|
|
138
|
-
formData.append("use_trial_keys", "true");
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return this.api(`/api/v1/run/${collection}/${task}`, {
|
|
142
|
-
method: "POST",
|
|
143
|
-
body: formData,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
async runWorkflowSync(
|
|
148
|
-
collection: string,
|
|
149
|
-
task: string,
|
|
150
|
-
initParams?: Record<string, unknown>,
|
|
151
|
-
useTrialKeys?: boolean
|
|
152
|
-
) {
|
|
153
|
-
const formData = new FormData();
|
|
154
|
-
formData.append("init_params", JSON.stringify(initParams || {}));
|
|
155
|
-
if (useTrialKeys) {
|
|
156
|
-
formData.append("use_trial_keys", "true");
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return this.api(`/api/v1/run-sync/${collection}/${task}`, {
|
|
160
|
-
method: "POST",
|
|
161
|
-
body: formData,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Trajectories
|
|
166
|
-
async listTrajectories(
|
|
167
|
-
collection: string,
|
|
168
|
-
task: string,
|
|
169
|
-
limit = 10,
|
|
170
|
-
page = 1
|
|
171
|
-
) {
|
|
172
|
-
return this.api(
|
|
173
|
-
`/api/v1/db/trajectories/${collection}/${task}?limit=${limit}&page=${page}`
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
async getTrajectory(
|
|
178
|
-
collection: string,
|
|
179
|
-
task: string,
|
|
180
|
-
trajectoryId: string
|
|
181
|
-
) {
|
|
182
|
-
return this.api(
|
|
183
|
-
`/api/v1/db/trajectory/${collection}/${task}/${trajectoryId}`
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Stats
|
|
188
|
-
async getStats(collection: string, task: string) {
|
|
189
|
-
return this.api(`/api/v1/db/stats/${collection}/${task}`);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Labels
|
|
193
|
-
async addLabel(
|
|
194
|
-
collection: string,
|
|
195
|
-
task: string,
|
|
196
|
-
trajectoryId: string,
|
|
197
|
-
key: string,
|
|
198
|
-
value: string,
|
|
199
|
-
author: string
|
|
200
|
-
) {
|
|
201
|
-
return this.api(
|
|
202
|
-
`/api/v1/trajectory/${collection}/${task}/${trajectoryId}/labels`,
|
|
203
|
-
{
|
|
204
|
-
method: "POST",
|
|
205
|
-
headers: { "Content-Type": "application/json" },
|
|
206
|
-
body: JSON.stringify({ key, value, author }),
|
|
207
|
-
}
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Workflow logs
|
|
212
|
-
async getWorkflowLogs(workflowId: string) {
|
|
213
|
-
return this.api(`/api/v1/workflows-logs/${workflowId}`);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Step templates
|
|
217
|
-
async listStepTemplates() {
|
|
218
|
-
return this.request("/api/v1/step-templates");
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
async getStepTemplate(name: string) {
|
|
222
|
-
return this.request(`/api/v1/step-templates/${name}`);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Environment variables
|
|
226
|
-
async setEnvironmentVars(
|
|
227
|
-
collection: string,
|
|
228
|
-
vars: Record<string, string>
|
|
229
|
-
) {
|
|
230
|
-
return this.api(`/api/v1/collections/${collection}/environment`, {
|
|
231
|
-
method: "PATCH",
|
|
232
|
-
headers: { "Content-Type": "application/json" },
|
|
233
|
-
body: JSON.stringify({ environment_variables: vars }),
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Routines (scheduled runs)
|
|
238
|
-
async listRoutines(collection: string, task?: string) {
|
|
239
|
-
const path = task
|
|
240
|
-
? `/api/v1/routines/${collection}/${task}`
|
|
241
|
-
: `/api/v1/routines/${collection}`;
|
|
242
|
-
return this.api(path);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
async getRoutine(collection: string, task: string, name: string) {
|
|
246
|
-
return this.api(`/api/v1/routines/${collection}/${task}/${name}`);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
async createRoutine(
|
|
250
|
-
collection: string,
|
|
251
|
-
task: string,
|
|
252
|
-
body: Record<string, unknown>
|
|
253
|
-
) {
|
|
254
|
-
return this.api(`/api/v1/routines/${collection}/${task}`, {
|
|
255
|
-
method: "POST",
|
|
256
|
-
headers: { "Content-Type": "application/json" },
|
|
257
|
-
body: JSON.stringify(body),
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
async updateRoutine(
|
|
262
|
-
collection: string,
|
|
263
|
-
task: string,
|
|
264
|
-
name: string,
|
|
265
|
-
patch: Record<string, unknown>
|
|
266
|
-
) {
|
|
267
|
-
return this.api(`/api/v1/routines/${collection}/${task}/${name}`, {
|
|
268
|
-
method: "PATCH",
|
|
269
|
-
headers: { "Content-Type": "application/json" },
|
|
270
|
-
body: JSON.stringify(patch),
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
async deleteRoutine(collection: string, task: string, name: string) {
|
|
275
|
-
return this.api(`/api/v1/routines/${collection}/${task}/${name}`, {
|
|
276
|
-
method: "DELETE",
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
async pauseRoutine(collection: string, task: string, name: string) {
|
|
281
|
-
return this.api(
|
|
282
|
-
`/api/v1/routines/${collection}/${task}/${name}/pause`,
|
|
283
|
-
{ method: "POST" }
|
|
284
|
-
);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
async resumeRoutine(collection: string, task: string, name: string) {
|
|
288
|
-
return this.api(
|
|
289
|
-
`/api/v1/routines/${collection}/${task}/${name}/resume`,
|
|
290
|
-
{ method: "POST" }
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
async runRoutineNow(collection: string, task: string, name: string) {
|
|
295
|
-
return this.api(
|
|
296
|
-
`/api/v1/routines/${collection}/${task}/${name}/run-now`,
|
|
297
|
-
{ method: "POST" }
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
async listRoutineRuns(
|
|
302
|
-
collection: string,
|
|
303
|
-
task: string,
|
|
304
|
-
name: string,
|
|
305
|
-
limit?: number
|
|
306
|
-
) {
|
|
307
|
-
const qs = typeof limit === "number" ? `?limit=${limit}` : "";
|
|
308
|
-
return this.api(
|
|
309
|
-
`/api/v1/routines/${collection}/${task}/${name}/runs${qs}`
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Resolve the API token: prefer JETTY_API_TOKEN (trimmed, non-empty), then the
|
|
316
|
-
* token file written by `jetty login`. The env var is checked for emptiness
|
|
317
|
-
* because the plugin's .mcp.json may pass it through as "" — which must not
|
|
318
|
-
* count as "set" and must not block the file fallback.
|
|
319
|
-
*/
|
|
320
|
-
function resolveToken(): string | undefined {
|
|
321
|
-
const fromEnv = process.env.JETTY_API_TOKEN?.trim();
|
|
322
|
-
if (fromEnv) return fromEnv;
|
|
323
|
-
|
|
324
|
-
try {
|
|
325
|
-
const fromFile = readFileSync(tokenFilePath(), "utf8").trim();
|
|
326
|
-
if (fromFile) return fromFile;
|
|
327
|
-
} catch {
|
|
328
|
-
// file absent / unreadable — fall through to undefined
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
return undefined;
|
|
332
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
-
import { JettyClient } from "./client.js";
|
|
6
|
-
import { registerTools } from "./tools.js";
|
|
7
|
-
|
|
8
|
-
const server = new McpServer({
|
|
9
|
-
name: "jetty",
|
|
10
|
-
version: "1.0.0",
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
const client = new JettyClient();
|
|
14
|
-
registerTools(server, client);
|
|
15
|
-
|
|
16
|
-
const transport = new StdioServerTransport();
|
|
17
|
-
await server.connect(transport);
|