ultralytics-mcp 0.1.0 → 0.1.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 +22 -3
- package/dist/client.js +4 -0
- package/dist/tools/datasets.js +55 -0
- package/dist/tools/index.js +42 -4
- package/dist/tools/projects.js +32 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
# Ultralytics Platform MCP
|
|
2
2
|
|
|
3
|
-
MCP server for the Ultralytics Platform
|
|
3
|
+
MCP server for the [Ultralytics Platform](https://platform.ultralytics.com).
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
> Independent community project. Not affiliated with or endorsed by Ultralytics.
|
|
6
|
+
|
|
7
|
+
Current milestone: read, monitor, predict, export, and initial project and
|
|
8
|
+
dataset lifecycle tools are available. Additional resource-management tools
|
|
9
|
+
land incrementally from here.
|
|
10
|
+
|
|
11
|
+
## Tools (18)
|
|
12
|
+
|
|
13
|
+
| Tool | Description |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| `projects_list` / `projects_get` | Browse projects |
|
|
16
|
+
| `projects_create` / `projects_delete` | Create / soft-delete projects |
|
|
17
|
+
| `datasets_list` / `datasets_get` / `datasets_create` / `datasets_delete` | Browse / create / soft-delete datasets |
|
|
18
|
+
| `models_list` / `models_get` | Browse trained models and metrics |
|
|
19
|
+
| `training_monitor` | Status, progress, and latest metrics |
|
|
20
|
+
| `model_predict` | Run inference on an image URL or base64 source |
|
|
21
|
+
| `model_download` | Download a model weight file to a local path |
|
|
22
|
+
| `gpu_availability` | Cloud GPU stock status |
|
|
23
|
+
| `exports_list` / `export_status` | List / check export jobs |
|
|
24
|
+
| `export_create` | Create an export job — **requires `confirm_cost: true`** |
|
|
25
|
+
| `training_start` | Start cloud training — **requires `confirm_cost: true`** |
|
|
7
26
|
|
|
8
27
|
## Development
|
|
9
28
|
|
package/dist/client.js
CHANGED
|
@@ -63,6 +63,10 @@ export class UltralyticsClient {
|
|
|
63
63
|
retryOn429: options.retryOn429 ?? false,
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
|
+
/** DELETE requests are state-changing and do not retry 429 responses. */
|
|
67
|
+
async delete(path) {
|
|
68
|
+
return this.request("DELETE", path, { retryOn429: false });
|
|
69
|
+
}
|
|
66
70
|
/** Download bytes from a signed URL WITHOUT forwarding API credentials. */
|
|
67
71
|
async downloadBytes(url) {
|
|
68
72
|
let attempt = 0;
|
package/dist/tools/datasets.js
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
/** Read-only dataset tools. */
|
|
2
2
|
import { resolveDataset } from "../resolve.js";
|
|
3
3
|
import { asRecord, listField, pyCount, pyField } from "./shared.js";
|
|
4
|
+
const DATASET_TASKS = new Set([
|
|
5
|
+
"detect",
|
|
6
|
+
"segment",
|
|
7
|
+
"semantic",
|
|
8
|
+
"classify",
|
|
9
|
+
"pose",
|
|
10
|
+
"obb",
|
|
11
|
+
]);
|
|
12
|
+
function resourceId(item, fallback) {
|
|
13
|
+
const value = item._id ?? item.id ?? item.projectId ?? item.datasetId;
|
|
14
|
+
return String(value ?? fallback ?? "None");
|
|
15
|
+
}
|
|
4
16
|
/** List datasets in the workspace, optionally filtered by username. */
|
|
5
17
|
export async function datasetsList(client, username) {
|
|
6
18
|
const data = await client.get("/datasets", username ? { username } : undefined);
|
|
@@ -28,3 +40,46 @@ export async function datasetsGet(client, dataset) {
|
|
|
28
40
|
data: item,
|
|
29
41
|
};
|
|
30
42
|
}
|
|
43
|
+
/** Create a dataset. */
|
|
44
|
+
export async function datasetsCreate(client, options) {
|
|
45
|
+
if (!DATASET_TASKS.has(options.task)) {
|
|
46
|
+
const allowed = Array.from(DATASET_TASKS).sort().join(", ");
|
|
47
|
+
throw new Error(`Unsupported dataset task '${options.task}'. Expected one of: ${allowed}.`);
|
|
48
|
+
}
|
|
49
|
+
if (!options.slug.trim()) {
|
|
50
|
+
throw new Error("`slug` is required.");
|
|
51
|
+
}
|
|
52
|
+
const payload = {
|
|
53
|
+
name: options.name,
|
|
54
|
+
task: options.task,
|
|
55
|
+
slug: options.slug,
|
|
56
|
+
};
|
|
57
|
+
if (options.description !== undefined) {
|
|
58
|
+
payload.description = options.description;
|
|
59
|
+
}
|
|
60
|
+
if (options.visibility !== undefined) {
|
|
61
|
+
payload.visibility = options.visibility;
|
|
62
|
+
}
|
|
63
|
+
if (options.classNames !== undefined) {
|
|
64
|
+
payload.classNames = options.classNames;
|
|
65
|
+
}
|
|
66
|
+
const data = await client.postJson("/datasets", payload);
|
|
67
|
+
const record = asRecord(data);
|
|
68
|
+
const item = asRecord("dataset" in record ? record.dataset : data);
|
|
69
|
+
const id = resourceId(item);
|
|
70
|
+
const slug = item.slug ?? options.slug;
|
|
71
|
+
const task = item.task ?? options.task;
|
|
72
|
+
return {
|
|
73
|
+
summary: `Created dataset ${id} slug=${String(slug)} task=${String(task)}.`,
|
|
74
|
+
data: item,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Soft-delete a dataset by id, slug, username/slug, or dataset ul:// URI. */
|
|
78
|
+
export async function datasetsDelete(client, dataset) {
|
|
79
|
+
const datasetId = await resolveDataset(client, dataset);
|
|
80
|
+
const data = await client.delete(`/datasets/${datasetId}`);
|
|
81
|
+
return {
|
|
82
|
+
summary: `Deleted dataset ${datasetId} (soft delete).`,
|
|
83
|
+
data: { id: datasetId, response: data },
|
|
84
|
+
};
|
|
85
|
+
}
|
package/dist/tools/index.js
CHANGED
|
@@ -7,28 +7,32 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { toMcpTextResult } from "../tool-result.js";
|
|
10
|
-
import { datasetsGet, datasetsList } from "./datasets.js";
|
|
10
|
+
import { datasetsCreate, datasetsDelete, datasetsGet, datasetsList, } from "./datasets.js";
|
|
11
11
|
import { modelDownload } from "./downloads.js";
|
|
12
12
|
import { exportCreate, exportStatus, exportsList } from "./exports.js";
|
|
13
13
|
import { gpuAvailability } from "./gpu.js";
|
|
14
14
|
import { modelsGet, modelsList } from "./models.js";
|
|
15
15
|
import { modelPredict } from "./predict.js";
|
|
16
|
-
import { projectsGet, projectsList } from "./projects.js";
|
|
16
|
+
import { projectsCreate, projectsDelete, projectsGet, projectsList, } from "./projects.js";
|
|
17
17
|
import { trainingMonitor, trainingStart } from "./training.js";
|
|
18
|
-
export { datasetsGet, datasetsList } from "./datasets.js";
|
|
18
|
+
export { datasetsCreate, datasetsDelete, datasetsGet, datasetsList, } from "./datasets.js";
|
|
19
19
|
export { modelDownload } from "./downloads.js";
|
|
20
20
|
export { exportCreate, exportStatus, exportsList } from "./exports.js";
|
|
21
21
|
export { gpuAvailability } from "./gpu.js";
|
|
22
22
|
export { modelsGet, modelsList } from "./models.js";
|
|
23
23
|
export { modelPredict } from "./predict.js";
|
|
24
|
-
export { projectsGet, projectsList } from "./projects.js";
|
|
24
|
+
export { projectsCreate, projectsDelete, projectsGet, projectsList, } from "./projects.js";
|
|
25
25
|
export { trainingMonitor, trainingStart } from "./training.js";
|
|
26
26
|
/** Names of the read-only tools registered by `registerReadTools`. */
|
|
27
27
|
export const READ_TOOL_NAMES = [
|
|
28
28
|
"projects_list",
|
|
29
29
|
"projects_get",
|
|
30
|
+
"projects_create",
|
|
31
|
+
"projects_delete",
|
|
30
32
|
"datasets_list",
|
|
31
33
|
"datasets_get",
|
|
34
|
+
"datasets_create",
|
|
35
|
+
"datasets_delete",
|
|
32
36
|
"models_list",
|
|
33
37
|
"models_get",
|
|
34
38
|
"gpu_availability",
|
|
@@ -43,6 +47,18 @@ export function registerReadTools(server, getClient) {
|
|
|
43
47
|
description: "Get details for one project by id, slug, username/slug, or project ul:// URI.",
|
|
44
48
|
inputSchema: { project: z.string() },
|
|
45
49
|
}, async ({ project }) => toMcpTextResult(await projectsGet(getClient(), project)));
|
|
50
|
+
server.registerTool("projects_create", {
|
|
51
|
+
description: "Create a project in your Ultralytics workspace.",
|
|
52
|
+
inputSchema: {
|
|
53
|
+
name: z.string(),
|
|
54
|
+
slug: z.string().optional(),
|
|
55
|
+
description: z.string().optional(),
|
|
56
|
+
},
|
|
57
|
+
}, async ({ name, slug, description }) => toMcpTextResult(await projectsCreate(getClient(), { name, slug, description })));
|
|
58
|
+
server.registerTool("projects_delete", {
|
|
59
|
+
description: "Soft-delete a project by id, slug, username/slug, or project ul:// URI.",
|
|
60
|
+
inputSchema: { project: z.string() },
|
|
61
|
+
}, async ({ project }) => toMcpTextResult(await projectsDelete(getClient(), project)));
|
|
46
62
|
server.registerTool("datasets_list", {
|
|
47
63
|
description: "List datasets in your Ultralytics workspace.",
|
|
48
64
|
inputSchema: { username: z.string().optional() },
|
|
@@ -51,6 +67,28 @@ export function registerReadTools(server, getClient) {
|
|
|
51
67
|
description: "Get details for one dataset by id, slug, username/slug, or dataset ul:// URI.",
|
|
52
68
|
inputSchema: { dataset: z.string() },
|
|
53
69
|
}, async ({ dataset }) => toMcpTextResult(await datasetsGet(getClient(), dataset)));
|
|
70
|
+
server.registerTool("datasets_create", {
|
|
71
|
+
description: "Create a dataset in your Ultralytics workspace.",
|
|
72
|
+
inputSchema: {
|
|
73
|
+
name: z.string(),
|
|
74
|
+
task: z.string(),
|
|
75
|
+
slug: z.string(),
|
|
76
|
+
description: z.string().optional(),
|
|
77
|
+
visibility: z.string().optional(),
|
|
78
|
+
classNames: z.array(z.string()).optional(),
|
|
79
|
+
},
|
|
80
|
+
}, async ({ name, task, slug, description, visibility, classNames }) => toMcpTextResult(await datasetsCreate(getClient(), {
|
|
81
|
+
name,
|
|
82
|
+
task,
|
|
83
|
+
slug,
|
|
84
|
+
description,
|
|
85
|
+
visibility,
|
|
86
|
+
classNames,
|
|
87
|
+
})));
|
|
88
|
+
server.registerTool("datasets_delete", {
|
|
89
|
+
description: "Soft-delete a dataset by id, slug, username/slug, or dataset ul:// URI.",
|
|
90
|
+
inputSchema: { dataset: z.string() },
|
|
91
|
+
}, async ({ dataset }) => toMcpTextResult(await datasetsDelete(getClient(), dataset)));
|
|
54
92
|
server.registerTool("models_list", {
|
|
55
93
|
description: "List models in a project by project id, slug, username/slug, or project ul:// URI.",
|
|
56
94
|
inputSchema: { project: z.string() },
|
package/dist/tools/projects.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/** Read-only project tools. */
|
|
2
2
|
import { resolveProject } from "../resolve.js";
|
|
3
3
|
import { asRecord, listField, pyCount, pyField } from "./shared.js";
|
|
4
|
+
function resourceId(item, fallback) {
|
|
5
|
+
const value = item._id ?? item.id ?? item.projectId ?? item.datasetId;
|
|
6
|
+
return String(value ?? fallback ?? "None");
|
|
7
|
+
}
|
|
4
8
|
/** List projects in the workspace, optionally filtered by username. */
|
|
5
9
|
export async function projectsList(client, username) {
|
|
6
10
|
const data = await client.get("/projects", username ? { username } : undefined);
|
|
@@ -27,3 +31,31 @@ export async function projectsGet(client, project) {
|
|
|
27
31
|
data: item,
|
|
28
32
|
};
|
|
29
33
|
}
|
|
34
|
+
/** Create a project. */
|
|
35
|
+
export async function projectsCreate(client, options) {
|
|
36
|
+
const payload = { name: options.name };
|
|
37
|
+
if (options.slug !== undefined) {
|
|
38
|
+
payload.slug = options.slug;
|
|
39
|
+
}
|
|
40
|
+
if (options.description !== undefined) {
|
|
41
|
+
payload.description = options.description;
|
|
42
|
+
}
|
|
43
|
+
const data = await client.postJson("/projects", payload);
|
|
44
|
+
const record = asRecord(data);
|
|
45
|
+
const item = asRecord("project" in record ? record.project : data);
|
|
46
|
+
const id = resourceId(item);
|
|
47
|
+
const slug = item.slug ?? options.slug ?? "None";
|
|
48
|
+
return {
|
|
49
|
+
summary: `Created project ${id} slug=${String(slug)}.`,
|
|
50
|
+
data: item,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Soft-delete a project by id, slug, username/slug, or project ul:// URI. */
|
|
54
|
+
export async function projectsDelete(client, project) {
|
|
55
|
+
const projectId = await resolveProject(client, project);
|
|
56
|
+
const data = await client.delete(`/projects/${projectId}`);
|
|
57
|
+
return {
|
|
58
|
+
summary: `Deleted project ${projectId} (soft delete).`,
|
|
59
|
+
data: { id: projectId, response: data },
|
|
60
|
+
};
|
|
61
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultralytics-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "TypeScript MCP server for the Ultralytics Platform REST API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"url": "https://github.com/amanharshx/ultralytics-mcp/issues"
|
|
21
21
|
},
|
|
22
22
|
"publishConfig": {
|
|
23
|
-
"access": "public"
|
|
23
|
+
"access": "public",
|
|
24
|
+
"provenance": true
|
|
24
25
|
},
|
|
25
26
|
"engines": {
|
|
26
27
|
"node": ">=20"
|