@olivaresai/alma-mcp 1.3.1 → 1.3.3
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 +28 -3
- package/dist/index.js +679 -4
- package/package.json +26 -12
package/README.md
CHANGED
|
@@ -47,11 +47,11 @@ Then use `"command": "alma-mcp"` instead of `npx`.
|
|
|
47
47
|
| Variable | Required | Description |
|
|
48
48
|
|----------|----------|-------------|
|
|
49
49
|
| `ALMA_API_KEY` | Yes | Your Alma API key ([get one here](https://alma.olivares.ai/settings)) |
|
|
50
|
-
| `
|
|
50
|
+
| `ALMA_API_URL` | No | API base URL (default: `https://alma.olivares.ai/api/v1`). `ALMA_BASE_URL` is accepted as an alias. |
|
|
51
51
|
| `ALMA_ENVIRONMENT_ID` | No | Default environment ID |
|
|
52
52
|
| `ALMA_DEBUG` | No | Enable debug logging |
|
|
53
53
|
|
|
54
|
-
## Tools (
|
|
54
|
+
## Tools (35)
|
|
55
55
|
|
|
56
56
|
### Context
|
|
57
57
|
|
|
@@ -94,7 +94,31 @@ Then use `"command": "alma-mcp"` instead of `npx`.
|
|
|
94
94
|
| `alma_list_environments` | List all environments |
|
|
95
95
|
| `alma_create_environment` | Create a new environment |
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
### Cowork (Collaborative Workspaces)
|
|
98
|
+
|
|
99
|
+
| Tool | Description |
|
|
100
|
+
|------|-------------|
|
|
101
|
+
| `cowork_list_workspaces` | List all collaborative workspaces |
|
|
102
|
+
| `cowork_create_workspace` | Create a new collaborative workspace |
|
|
103
|
+
| `cowork_list_files` | List all files in a workspace |
|
|
104
|
+
| `cowork_read_file` | Read the content of a workspace file |
|
|
105
|
+
| `cowork_write_file` | Write or update a file in a workspace |
|
|
106
|
+
| `cowork_search` | Search across all files in a workspace |
|
|
107
|
+
| `cowork_execute_skill` | Run an AI skill on a file (explain, refactor, review, test, fix, document, search, commit) |
|
|
108
|
+
| `cowork_apply_diff` | Apply a pending operation diff to a workspace file |
|
|
109
|
+
|
|
110
|
+
### Video
|
|
111
|
+
|
|
112
|
+
| Tool | Description |
|
|
113
|
+
|------|-------------|
|
|
114
|
+
| `video_generate` | Start an AI video generation job |
|
|
115
|
+
| `video_check_job` | Check the status of a video generation job |
|
|
116
|
+
| `video_list_projects` | List all video projects |
|
|
117
|
+
| `video_create_project` | Create a new video project |
|
|
118
|
+
| `video_plan_scenes` | Use AI to plan scenes for a video project |
|
|
119
|
+
| `video_stitch` | Stitch multiple video scenes into a single final video |
|
|
120
|
+
|
|
121
|
+
## Resources (10)
|
|
98
122
|
|
|
99
123
|
| Resource URI | Description |
|
|
100
124
|
|-------------|-------------|
|
|
@@ -104,6 +128,7 @@ Then use `"command": "alma-mcp"` instead of `npx`.
|
|
|
104
128
|
| `alma://environments` | All available environments |
|
|
105
129
|
| `alma://conversations` | Recent conversations (last 20) |
|
|
106
130
|
| `alma://budget` | Token budget status |
|
|
131
|
+
| `alma://video-budget` | Video generation credits and usage |
|
|
107
132
|
| `alma://blocks` | All soul memory blocks |
|
|
108
133
|
| `alma://episodes` | Recent episodes (last 20) |
|
|
109
134
|
| `alma://procedures` | All stored procedures (limit 50) |
|
package/dist/index.js
CHANGED
|
@@ -18,10 +18,40 @@ function loadConfig() {
|
|
|
18
18
|
console.error("[alma-mcp] ALMA_API_URL must use HTTPS for non-localhost connections. Got:", cleanUrl);
|
|
19
19
|
process.exit(1);
|
|
20
20
|
}
|
|
21
|
+
if (!cleanUrl.startsWith("https://") && process.env.NODE_ENV !== "development" && process.env.ALMA_DEV !== "1") {
|
|
22
|
+
console.error(
|
|
23
|
+
`[alma-mcp] WARNING: ALMA_API_URL points at ${cleanUrl}. This is only correct for local development. For Alma cloud use https://alma.olivares.ai/api/v1 (or set NODE_ENV=development to silence this warning).`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
21
26
|
return { baseUrl: cleanUrl, apiKey, environmentId };
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
// src/client.ts
|
|
30
|
+
function formatApiError(method, path, status, detail, code) {
|
|
31
|
+
const tail = detail ? ` ${detail}` : "";
|
|
32
|
+
if (status === 401) {
|
|
33
|
+
return `Alma rejected the API key. Generate a new one at https://alma.olivares.ai/settings (API Keys) and set ALMA_API_KEY in your MCP config.${tail}`;
|
|
34
|
+
}
|
|
35
|
+
if (status === 402) {
|
|
36
|
+
if (code === "BUDGET_EXHAUSTED" || /budget/i.test(detail)) {
|
|
37
|
+
return `Your Alma weekly AI budget is exhausted. Top up at https://alma.olivares.ai/settings/billing or wait until Monday 00:00 UTC for the budget to reset.${tail}`;
|
|
38
|
+
}
|
|
39
|
+
return `This action requires an active Alma plan or additional credits. Manage your subscription at https://alma.olivares.ai/settings/billing.${tail}`;
|
|
40
|
+
}
|
|
41
|
+
if (status === 403) {
|
|
42
|
+
if (code === "PLAN_API_RESTRICTED" || /api/i.test(detail)) {
|
|
43
|
+
return `The Alma API is available on the Max plan. Upgrade at https://alma.olivares.ai/settings/billing.${tail}`;
|
|
44
|
+
}
|
|
45
|
+
if (code === "BYOK_REQUIRED_PLAN") {
|
|
46
|
+
return `BYOK (bring your own keys) is available on the Max plan. Upgrade at https://alma.olivares.ai/settings/billing.${tail}`;
|
|
47
|
+
}
|
|
48
|
+
return `Forbidden \u2014 your plan does not include this feature. See https://alma.olivares.ai/settings/billing.${tail}`;
|
|
49
|
+
}
|
|
50
|
+
if (status === 429) {
|
|
51
|
+
return `Alma rate limit hit. Slow down and retry in a minute.${tail}`;
|
|
52
|
+
}
|
|
53
|
+
return `Alma API ${method} ${path} failed (${status}):${tail}`;
|
|
54
|
+
}
|
|
25
55
|
var AlmaClient = class _AlmaClient {
|
|
26
56
|
constructor(config2) {
|
|
27
57
|
this.config = config2;
|
|
@@ -83,12 +113,14 @@ var AlmaClient = class _AlmaClient {
|
|
|
83
113
|
if (!res.ok) {
|
|
84
114
|
const text = await res.text().catch(() => "");
|
|
85
115
|
let detail = text;
|
|
116
|
+
let code;
|
|
86
117
|
try {
|
|
87
118
|
const json = JSON.parse(text);
|
|
88
119
|
detail = json.error?.message ?? json.message ?? text;
|
|
120
|
+
code = json.error?.code ?? json.code;
|
|
89
121
|
} catch {
|
|
90
122
|
}
|
|
91
|
-
throw new Error(
|
|
123
|
+
throw new Error(formatApiError(method, path, res.status, detail, code));
|
|
92
124
|
}
|
|
93
125
|
return res.json();
|
|
94
126
|
}
|
|
@@ -185,7 +217,15 @@ var AlmaClient = class _AlmaClient {
|
|
|
185
217
|
);
|
|
186
218
|
if (!res.ok) {
|
|
187
219
|
const text2 = await res.text().catch(() => "");
|
|
188
|
-
|
|
220
|
+
let detail = text2;
|
|
221
|
+
let code;
|
|
222
|
+
try {
|
|
223
|
+
const json = JSON.parse(text2);
|
|
224
|
+
detail = json.error?.message ?? json.message ?? text2;
|
|
225
|
+
code = json.error?.code ?? json.code;
|
|
226
|
+
} catch {
|
|
227
|
+
}
|
|
228
|
+
throw new Error(formatApiError("POST", `/chat/conversations/${conversationId}/messages`, res.status, detail, code));
|
|
189
229
|
}
|
|
190
230
|
const text = await res.text();
|
|
191
231
|
const rawEvents = text.split("\n\n");
|
|
@@ -328,6 +368,87 @@ var AlmaClient = class _AlmaClient {
|
|
|
328
368
|
async createProcedure(params) {
|
|
329
369
|
return this.request("POST", "/procedures", params);
|
|
330
370
|
}
|
|
371
|
+
// ─── Cowork (Collaborative Workspaces) ─────────────
|
|
372
|
+
async listWorkspaces() {
|
|
373
|
+
return this.request("GET", "/cowork/workspaces");
|
|
374
|
+
}
|
|
375
|
+
async createWorkspace(params) {
|
|
376
|
+
return this.request("POST", "/cowork/workspaces", params);
|
|
377
|
+
}
|
|
378
|
+
async listWorkspaceFiles(workspaceId) {
|
|
379
|
+
return this.request(
|
|
380
|
+
"GET",
|
|
381
|
+
`/cowork/workspaces/${encodeURIComponent(workspaceId)}/files`
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
async readWorkspaceFile(workspaceId, filePath) {
|
|
385
|
+
const qs = new URLSearchParams({ path: filePath });
|
|
386
|
+
return this.request(
|
|
387
|
+
"GET",
|
|
388
|
+
`/cowork/workspaces/${encodeURIComponent(workspaceId)}/files/read?${qs}`
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
async writeWorkspaceFile(workspaceId, filePath, content) {
|
|
392
|
+
return this.request(
|
|
393
|
+
"POST",
|
|
394
|
+
`/cowork/workspaces/${encodeURIComponent(workspaceId)}/files/write`,
|
|
395
|
+
{ path: filePath, content }
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
async searchWorkspace(workspaceId, query) {
|
|
399
|
+
const qs = new URLSearchParams({ q: query });
|
|
400
|
+
return this.request(
|
|
401
|
+
"GET",
|
|
402
|
+
`/cowork/workspaces/${encodeURIComponent(workspaceId)}/search?${qs}`
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
async executeSkill(workspaceId, params) {
|
|
406
|
+
return this.request(
|
|
407
|
+
"POST",
|
|
408
|
+
`/cowork/workspaces/${encodeURIComponent(workspaceId)}/skills`,
|
|
409
|
+
params
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
async applyDiff(workspaceId, operationId) {
|
|
413
|
+
return this.request(
|
|
414
|
+
"POST",
|
|
415
|
+
`/cowork/workspaces/${encodeURIComponent(workspaceId)}/apply`,
|
|
416
|
+
{ operationId }
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
// ─── Video ────────────────────────────────────────
|
|
420
|
+
async generateVideo(params) {
|
|
421
|
+
return this.request("POST", "/video/generate", params);
|
|
422
|
+
}
|
|
423
|
+
async checkVideoJob(jobId) {
|
|
424
|
+
return this.request(
|
|
425
|
+
"GET",
|
|
426
|
+
`/video/jobs/${encodeURIComponent(jobId)}`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
async listVideoProjects() {
|
|
430
|
+
return this.request("GET", "/video/projects");
|
|
431
|
+
}
|
|
432
|
+
async createVideoProject(params) {
|
|
433
|
+
return this.request("POST", "/video/projects", params);
|
|
434
|
+
}
|
|
435
|
+
async planVideoScenes(projectId, params) {
|
|
436
|
+
return this.request(
|
|
437
|
+
"POST",
|
|
438
|
+
`/video/projects/${encodeURIComponent(projectId)}/plan`,
|
|
439
|
+
params
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
async stitchVideo(projectId, jobIds) {
|
|
443
|
+
return this.request(
|
|
444
|
+
"POST",
|
|
445
|
+
`/video/projects/${encodeURIComponent(projectId)}/stitch`,
|
|
446
|
+
{ jobIds }
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
async getVideoBudget() {
|
|
450
|
+
return this.request("GET", "/video/budget");
|
|
451
|
+
}
|
|
331
452
|
};
|
|
332
453
|
|
|
333
454
|
// src/tools/assemble.ts
|
|
@@ -1232,12 +1353,470 @@ ${JSON.stringify(result.memory, null, 2)}`
|
|
|
1232
1353
|
);
|
|
1233
1354
|
}
|
|
1234
1355
|
|
|
1356
|
+
// src/tools/cowork.ts
|
|
1357
|
+
import { z as z18 } from "zod";
|
|
1358
|
+
function registerCoworkTools(server2, client2) {
|
|
1359
|
+
server2.registerTool(
|
|
1360
|
+
"cowork_list_workspaces",
|
|
1361
|
+
{
|
|
1362
|
+
title: "List Workspaces",
|
|
1363
|
+
description: "List all collaborative workspaces available to the user. Returns workspace IDs, names, and metadata.",
|
|
1364
|
+
inputSchema: {}
|
|
1365
|
+
},
|
|
1366
|
+
async () => {
|
|
1367
|
+
try {
|
|
1368
|
+
const result = await client2.listWorkspaces();
|
|
1369
|
+
if (result.workspaces.length === 0) {
|
|
1370
|
+
return {
|
|
1371
|
+
content: [{ type: "text", text: "No workspaces found." }]
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
return {
|
|
1375
|
+
content: [
|
|
1376
|
+
{
|
|
1377
|
+
type: "text",
|
|
1378
|
+
text: JSON.stringify(result, null, 2)
|
|
1379
|
+
}
|
|
1380
|
+
]
|
|
1381
|
+
};
|
|
1382
|
+
} catch (err) {
|
|
1383
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1384
|
+
return {
|
|
1385
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1386
|
+
isError: true
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
);
|
|
1391
|
+
server2.registerTool(
|
|
1392
|
+
"cowork_create_workspace",
|
|
1393
|
+
{
|
|
1394
|
+
title: "Create Workspace",
|
|
1395
|
+
description: "Create a new collaborative workspace. Workspaces hold files that Alma can read, write, and operate on.",
|
|
1396
|
+
inputSchema: {
|
|
1397
|
+
name: z18.string().min(1).max(200).describe("Name for the workspace"),
|
|
1398
|
+
description: z18.string().max(1e3).optional().describe("Optional description of the workspace purpose"),
|
|
1399
|
+
sourceType: z18.string().optional().describe('Source type for the workspace (e.g. "blank", "template", "import")')
|
|
1400
|
+
}
|
|
1401
|
+
},
|
|
1402
|
+
async ({ name, description, sourceType }) => {
|
|
1403
|
+
try {
|
|
1404
|
+
const result = await client2.createWorkspace({ name, description, sourceType });
|
|
1405
|
+
return {
|
|
1406
|
+
content: [
|
|
1407
|
+
{
|
|
1408
|
+
type: "text",
|
|
1409
|
+
text: `Workspace created successfully.
|
|
1410
|
+
${JSON.stringify(result.workspace, null, 2)}`
|
|
1411
|
+
}
|
|
1412
|
+
]
|
|
1413
|
+
};
|
|
1414
|
+
} catch (err) {
|
|
1415
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1416
|
+
return {
|
|
1417
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1418
|
+
isError: true
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
);
|
|
1423
|
+
server2.registerTool(
|
|
1424
|
+
"cowork_list_files",
|
|
1425
|
+
{
|
|
1426
|
+
title: "List Workspace Files",
|
|
1427
|
+
description: "List all files in a collaborative workspace. Returns file paths, sizes, and metadata.",
|
|
1428
|
+
inputSchema: {
|
|
1429
|
+
workspaceId: z18.string().min(1).describe("The workspace ID to list files from")
|
|
1430
|
+
}
|
|
1431
|
+
},
|
|
1432
|
+
async ({ workspaceId }) => {
|
|
1433
|
+
try {
|
|
1434
|
+
const result = await client2.listWorkspaceFiles(workspaceId);
|
|
1435
|
+
if (result.files.length === 0) {
|
|
1436
|
+
return {
|
|
1437
|
+
content: [{ type: "text", text: "No files found in this workspace." }]
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
return {
|
|
1441
|
+
content: [
|
|
1442
|
+
{
|
|
1443
|
+
type: "text",
|
|
1444
|
+
text: JSON.stringify(result, null, 2)
|
|
1445
|
+
}
|
|
1446
|
+
]
|
|
1447
|
+
};
|
|
1448
|
+
} catch (err) {
|
|
1449
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1450
|
+
return {
|
|
1451
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1452
|
+
isError: true
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
);
|
|
1457
|
+
server2.registerTool(
|
|
1458
|
+
"cowork_read_file",
|
|
1459
|
+
{
|
|
1460
|
+
title: "Read Workspace File",
|
|
1461
|
+
description: "Read the content of a file in a collaborative workspace. Returns the file content and metadata.",
|
|
1462
|
+
inputSchema: {
|
|
1463
|
+
workspaceId: z18.string().min(1).describe("The workspace ID containing the file"),
|
|
1464
|
+
filePath: z18.string().min(1).describe("Path of the file within the workspace")
|
|
1465
|
+
}
|
|
1466
|
+
},
|
|
1467
|
+
async ({ workspaceId, filePath }) => {
|
|
1468
|
+
try {
|
|
1469
|
+
const result = await client2.readWorkspaceFile(workspaceId, filePath);
|
|
1470
|
+
return {
|
|
1471
|
+
content: [
|
|
1472
|
+
{
|
|
1473
|
+
type: "text",
|
|
1474
|
+
text: JSON.stringify(result.file, null, 2)
|
|
1475
|
+
}
|
|
1476
|
+
]
|
|
1477
|
+
};
|
|
1478
|
+
} catch (err) {
|
|
1479
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1480
|
+
return {
|
|
1481
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1482
|
+
isError: true
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
);
|
|
1487
|
+
server2.registerTool(
|
|
1488
|
+
"cowork_write_file",
|
|
1489
|
+
{
|
|
1490
|
+
title: "Write Workspace File",
|
|
1491
|
+
description: "Write or update a file in a collaborative workspace. Creates the file if it does not exist.",
|
|
1492
|
+
inputSchema: {
|
|
1493
|
+
workspaceId: z18.string().min(1).describe("The workspace ID to write the file in"),
|
|
1494
|
+
filePath: z18.string().min(1).describe("Path of the file within the workspace"),
|
|
1495
|
+
content: z18.string().describe("The content to write to the file")
|
|
1496
|
+
}
|
|
1497
|
+
},
|
|
1498
|
+
async ({ workspaceId, filePath, content }) => {
|
|
1499
|
+
try {
|
|
1500
|
+
const result = await client2.writeWorkspaceFile(workspaceId, filePath, content);
|
|
1501
|
+
return {
|
|
1502
|
+
content: [
|
|
1503
|
+
{
|
|
1504
|
+
type: "text",
|
|
1505
|
+
text: `File written successfully.
|
|
1506
|
+
${JSON.stringify(result.file, null, 2)}`
|
|
1507
|
+
}
|
|
1508
|
+
]
|
|
1509
|
+
};
|
|
1510
|
+
} catch (err) {
|
|
1511
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1512
|
+
return {
|
|
1513
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1514
|
+
isError: true
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
);
|
|
1519
|
+
server2.registerTool(
|
|
1520
|
+
"cowork_search",
|
|
1521
|
+
{
|
|
1522
|
+
title: "Search Workspace",
|
|
1523
|
+
description: "Search across all files in a collaborative workspace. Returns matching files and content snippets.",
|
|
1524
|
+
inputSchema: {
|
|
1525
|
+
workspaceId: z18.string().min(1).describe("The workspace ID to search in"),
|
|
1526
|
+
query: z18.string().min(1).describe("Search query string")
|
|
1527
|
+
}
|
|
1528
|
+
},
|
|
1529
|
+
async ({ workspaceId, query }) => {
|
|
1530
|
+
try {
|
|
1531
|
+
const result = await client2.searchWorkspace(workspaceId, query);
|
|
1532
|
+
if (result.results.length === 0) {
|
|
1533
|
+
return {
|
|
1534
|
+
content: [{ type: "text", text: `No results found for: ${query}` }]
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
return {
|
|
1538
|
+
content: [
|
|
1539
|
+
{
|
|
1540
|
+
type: "text",
|
|
1541
|
+
text: JSON.stringify(result, null, 2)
|
|
1542
|
+
}
|
|
1543
|
+
]
|
|
1544
|
+
};
|
|
1545
|
+
} catch (err) {
|
|
1546
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1547
|
+
return {
|
|
1548
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1549
|
+
isError: true
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
);
|
|
1554
|
+
server2.registerTool(
|
|
1555
|
+
"cowork_execute_skill",
|
|
1556
|
+
{
|
|
1557
|
+
title: "Execute Skill",
|
|
1558
|
+
description: "Run an AI skill on a file or input within a collaborative workspace. Available skills: explain, refactor, review, test, fix, document, search, commit.",
|
|
1559
|
+
inputSchema: {
|
|
1560
|
+
workspaceId: z18.string().min(1).describe("The workspace ID to execute the skill in"),
|
|
1561
|
+
skill: z18.enum(["explain", "refactor", "review", "test", "fix", "document", "search", "commit"]).describe("The skill to execute"),
|
|
1562
|
+
targetPath: z18.string().optional().describe("Optional file path to target with the skill"),
|
|
1563
|
+
input: z18.string().min(1).describe("Input or instructions for the skill execution")
|
|
1564
|
+
}
|
|
1565
|
+
},
|
|
1566
|
+
async ({ workspaceId, skill, targetPath, input }) => {
|
|
1567
|
+
try {
|
|
1568
|
+
const result = await client2.executeSkill(workspaceId, { skill, targetPath, input });
|
|
1569
|
+
return {
|
|
1570
|
+
content: [
|
|
1571
|
+
{
|
|
1572
|
+
type: "text",
|
|
1573
|
+
text: `Skill "${skill}" executed successfully.
|
|
1574
|
+
${JSON.stringify(result.result, null, 2)}`
|
|
1575
|
+
}
|
|
1576
|
+
]
|
|
1577
|
+
};
|
|
1578
|
+
} catch (err) {
|
|
1579
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1580
|
+
return {
|
|
1581
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1582
|
+
isError: true
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
);
|
|
1587
|
+
server2.registerTool(
|
|
1588
|
+
"cowork_apply_diff",
|
|
1589
|
+
{
|
|
1590
|
+
title: "Apply Operation Diff",
|
|
1591
|
+
description: "Apply a pending operation diff to a workspace file. Use after executing a skill that produces changes.",
|
|
1592
|
+
inputSchema: {
|
|
1593
|
+
workspaceId: z18.string().min(1).describe("The workspace ID containing the operation"),
|
|
1594
|
+
operationId: z18.string().min(1).describe("The operation ID whose diff should be applied")
|
|
1595
|
+
}
|
|
1596
|
+
},
|
|
1597
|
+
async ({ workspaceId, operationId }) => {
|
|
1598
|
+
try {
|
|
1599
|
+
const result = await client2.applyDiff(workspaceId, operationId);
|
|
1600
|
+
return {
|
|
1601
|
+
content: [
|
|
1602
|
+
{
|
|
1603
|
+
type: "text",
|
|
1604
|
+
text: result.applied ? `Diff applied successfully.
|
|
1605
|
+
${JSON.stringify(result.result, null, 2)}` : "Diff could not be applied. The operation may have already been applied or expired."
|
|
1606
|
+
}
|
|
1607
|
+
]
|
|
1608
|
+
};
|
|
1609
|
+
} catch (err) {
|
|
1610
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1611
|
+
return {
|
|
1612
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1613
|
+
isError: true
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
);
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
// src/tools/video.ts
|
|
1621
|
+
import { z as z19 } from "zod";
|
|
1622
|
+
function registerVideoTools(server2, client2) {
|
|
1623
|
+
server2.registerTool(
|
|
1624
|
+
"video_generate",
|
|
1625
|
+
{
|
|
1626
|
+
title: "Generate Video",
|
|
1627
|
+
description: "Start an AI video generation job. Returns a job ID to track progress. Requires a paid plan with video credits.",
|
|
1628
|
+
inputSchema: {
|
|
1629
|
+
prompt: z19.string().min(1).max(2e3).describe("Text description of the video to generate"),
|
|
1630
|
+
model: z19.string().min(1).describe('Video generation model to use (e.g. "runway-gen3", "kling-v1", "pika-v2")'),
|
|
1631
|
+
durationSeconds: z19.number().int().min(1).max(60).describe("Duration of the video in seconds (1-60)"),
|
|
1632
|
+
aspectRatio: z19.enum(["16:9", "9:16", "1:1", "4:3", "3:4"]).optional().describe("Aspect ratio of the video (default: 16:9)"),
|
|
1633
|
+
style: z19.string().max(200).optional().describe('Optional style modifier (e.g. "cinematic", "anime", "documentary")')
|
|
1634
|
+
}
|
|
1635
|
+
},
|
|
1636
|
+
async ({ prompt, model, durationSeconds, aspectRatio, style }) => {
|
|
1637
|
+
try {
|
|
1638
|
+
const result = await client2.generateVideo({ prompt, model, durationSeconds, aspectRatio, style });
|
|
1639
|
+
return {
|
|
1640
|
+
content: [
|
|
1641
|
+
{
|
|
1642
|
+
type: "text",
|
|
1643
|
+
text: `Video generation started.
|
|
1644
|
+
${JSON.stringify(result.job, null, 2)}`
|
|
1645
|
+
}
|
|
1646
|
+
]
|
|
1647
|
+
};
|
|
1648
|
+
} catch (err) {
|
|
1649
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1650
|
+
return {
|
|
1651
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1652
|
+
isError: true
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
);
|
|
1657
|
+
server2.registerTool(
|
|
1658
|
+
"video_check_job",
|
|
1659
|
+
{
|
|
1660
|
+
title: "Check Video Job",
|
|
1661
|
+
description: "Check the status of a video generation job. Returns status, progress percentage, and result URL when complete.",
|
|
1662
|
+
inputSchema: {
|
|
1663
|
+
jobId: z19.string().min(1).describe("The video generation job ID to check")
|
|
1664
|
+
}
|
|
1665
|
+
},
|
|
1666
|
+
async ({ jobId }) => {
|
|
1667
|
+
try {
|
|
1668
|
+
const result = await client2.checkVideoJob(jobId);
|
|
1669
|
+
return {
|
|
1670
|
+
content: [
|
|
1671
|
+
{
|
|
1672
|
+
type: "text",
|
|
1673
|
+
text: JSON.stringify(result.job, null, 2)
|
|
1674
|
+
}
|
|
1675
|
+
]
|
|
1676
|
+
};
|
|
1677
|
+
} catch (err) {
|
|
1678
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1679
|
+
return {
|
|
1680
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1681
|
+
isError: true
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
);
|
|
1686
|
+
server2.registerTool(
|
|
1687
|
+
"video_list_projects",
|
|
1688
|
+
{
|
|
1689
|
+
title: "List Video Projects",
|
|
1690
|
+
description: "List all video projects for the user. Projects organize multiple video scenes into a cohesive production.",
|
|
1691
|
+
inputSchema: {}
|
|
1692
|
+
},
|
|
1693
|
+
async () => {
|
|
1694
|
+
try {
|
|
1695
|
+
const result = await client2.listVideoProjects();
|
|
1696
|
+
if (result.projects.length === 0) {
|
|
1697
|
+
return {
|
|
1698
|
+
content: [{ type: "text", text: "No video projects found." }]
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
return {
|
|
1702
|
+
content: [
|
|
1703
|
+
{
|
|
1704
|
+
type: "text",
|
|
1705
|
+
text: JSON.stringify(result, null, 2)
|
|
1706
|
+
}
|
|
1707
|
+
]
|
|
1708
|
+
};
|
|
1709
|
+
} catch (err) {
|
|
1710
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1711
|
+
return {
|
|
1712
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1713
|
+
isError: true
|
|
1714
|
+
};
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
);
|
|
1718
|
+
server2.registerTool(
|
|
1719
|
+
"video_create_project",
|
|
1720
|
+
{
|
|
1721
|
+
title: "Create Video Project",
|
|
1722
|
+
description: "Create a new video project. Projects let you plan scenes, generate clips, and stitch them into a final video.",
|
|
1723
|
+
inputSchema: {
|
|
1724
|
+
name: z19.string().min(1).max(200).describe("Name for the video project"),
|
|
1725
|
+
description: z19.string().max(1e3).optional().describe("Optional description of the video project")
|
|
1726
|
+
}
|
|
1727
|
+
},
|
|
1728
|
+
async ({ name, description }) => {
|
|
1729
|
+
try {
|
|
1730
|
+
const result = await client2.createVideoProject({ name, description });
|
|
1731
|
+
return {
|
|
1732
|
+
content: [
|
|
1733
|
+
{
|
|
1734
|
+
type: "text",
|
|
1735
|
+
text: `Video project created successfully.
|
|
1736
|
+
${JSON.stringify(result.project, null, 2)}`
|
|
1737
|
+
}
|
|
1738
|
+
]
|
|
1739
|
+
};
|
|
1740
|
+
} catch (err) {
|
|
1741
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1742
|
+
return {
|
|
1743
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1744
|
+
isError: true
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
);
|
|
1749
|
+
server2.registerTool(
|
|
1750
|
+
"video_plan_scenes",
|
|
1751
|
+
{
|
|
1752
|
+
title: "Plan Video Scenes",
|
|
1753
|
+
description: "Use AI to plan scenes for a video project. Breaks a description into individual scene prompts with timing and transitions.",
|
|
1754
|
+
inputSchema: {
|
|
1755
|
+
projectId: z19.string().min(1).describe("The video project ID to plan scenes for"),
|
|
1756
|
+
description: z19.string().min(1).max(5e3).describe("Description of the video to plan (story, concept, or script)"),
|
|
1757
|
+
sceneCount: z19.number().int().min(1).max(20).optional().describe("Number of scenes to plan (default: auto-determined by AI)")
|
|
1758
|
+
}
|
|
1759
|
+
},
|
|
1760
|
+
async ({ projectId, description, sceneCount }) => {
|
|
1761
|
+
try {
|
|
1762
|
+
const result = await client2.planVideoScenes(projectId, { description, sceneCount });
|
|
1763
|
+
return {
|
|
1764
|
+
content: [
|
|
1765
|
+
{
|
|
1766
|
+
type: "text",
|
|
1767
|
+
text: `Scene plan created (${result.scenes.length} scenes).
|
|
1768
|
+
${JSON.stringify(result.scenes, null, 2)}`
|
|
1769
|
+
}
|
|
1770
|
+
]
|
|
1771
|
+
};
|
|
1772
|
+
} catch (err) {
|
|
1773
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1774
|
+
return {
|
|
1775
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1776
|
+
isError: true
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
);
|
|
1781
|
+
server2.registerTool(
|
|
1782
|
+
"video_stitch",
|
|
1783
|
+
{
|
|
1784
|
+
title: "Stitch Video",
|
|
1785
|
+
description: "Stitch multiple generated video scenes into a single final video. All referenced jobs must be complete.",
|
|
1786
|
+
inputSchema: {
|
|
1787
|
+
projectId: z19.string().min(1).describe("The video project ID"),
|
|
1788
|
+
jobIds: z19.array(z19.string().min(1)).min(1).max(20).describe("Ordered list of completed video job IDs to stitch together")
|
|
1789
|
+
}
|
|
1790
|
+
},
|
|
1791
|
+
async ({ projectId, jobIds }) => {
|
|
1792
|
+
try {
|
|
1793
|
+
const result = await client2.stitchVideo(projectId, jobIds);
|
|
1794
|
+
return {
|
|
1795
|
+
content: [
|
|
1796
|
+
{
|
|
1797
|
+
type: "text",
|
|
1798
|
+
text: `Video stitch job started.
|
|
1799
|
+
${JSON.stringify(result.job, null, 2)}`
|
|
1800
|
+
}
|
|
1801
|
+
]
|
|
1802
|
+
};
|
|
1803
|
+
} catch (err) {
|
|
1804
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1805
|
+
return {
|
|
1806
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1807
|
+
isError: true
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
);
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1235
1814
|
// src/index.ts
|
|
1236
1815
|
var config = loadConfig();
|
|
1237
1816
|
var client = new AlmaClient(config);
|
|
1238
1817
|
var server = new McpServer({
|
|
1239
1818
|
name: "alma-mcp",
|
|
1240
|
-
version: "1.3.
|
|
1819
|
+
version: "1.3.2"
|
|
1241
1820
|
});
|
|
1242
1821
|
registerAssembleTool(server, client);
|
|
1243
1822
|
registerRememberTool(server, client);
|
|
@@ -1260,6 +1839,8 @@ registerListProceduresTool(server, client);
|
|
|
1260
1839
|
registerCreateProcedureTool(server, client);
|
|
1261
1840
|
registerPreviewContextTool(server, client);
|
|
1262
1841
|
registerUpdateMemoryTool(server, client);
|
|
1842
|
+
registerCoworkTools(server, client);
|
|
1843
|
+
registerVideoTools(server, client);
|
|
1263
1844
|
server.registerResource(
|
|
1264
1845
|
"soul",
|
|
1265
1846
|
"alma://soul",
|
|
@@ -1400,6 +1981,34 @@ server.registerResource(
|
|
|
1400
1981
|
}
|
|
1401
1982
|
}
|
|
1402
1983
|
);
|
|
1984
|
+
server.registerResource(
|
|
1985
|
+
"video-budget",
|
|
1986
|
+
"alma://video-budget",
|
|
1987
|
+
{
|
|
1988
|
+
title: "Video Budget",
|
|
1989
|
+
description: "Current video generation credits, usage, and remaining quota",
|
|
1990
|
+
mimeType: "application/json"
|
|
1991
|
+
},
|
|
1992
|
+
async (uri) => {
|
|
1993
|
+
try {
|
|
1994
|
+
const result = await client.getVideoBudget();
|
|
1995
|
+
return {
|
|
1996
|
+
contents: [
|
|
1997
|
+
{
|
|
1998
|
+
uri: uri.href,
|
|
1999
|
+
mimeType: "application/json",
|
|
2000
|
+
text: JSON.stringify(result, null, 2)
|
|
2001
|
+
}
|
|
2002
|
+
]
|
|
2003
|
+
};
|
|
2004
|
+
} catch (err) {
|
|
2005
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
2006
|
+
return {
|
|
2007
|
+
contents: [{ uri: uri.href, mimeType: "text/plain", text: `Error loading resource: ${message}` }]
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
);
|
|
1403
2012
|
server.registerResource(
|
|
1404
2013
|
"memories-by-category",
|
|
1405
2014
|
new ResourceTemplate("alma://memories/{category}", {
|
|
@@ -1523,10 +2132,76 @@ server.registerResource(
|
|
|
1523
2132
|
}
|
|
1524
2133
|
}
|
|
1525
2134
|
);
|
|
2135
|
+
server.prompt(
|
|
2136
|
+
"alma-context",
|
|
2137
|
+
"Assemble Alma's full context: soul identity, relevant memories, recent episodes, and procedures. Use this to give the AI persistent memory about the user.",
|
|
2138
|
+
{},
|
|
2139
|
+
async () => {
|
|
2140
|
+
try {
|
|
2141
|
+
const result = await client.assembleContext({ message: "general conversation" });
|
|
2142
|
+
return {
|
|
2143
|
+
messages: [
|
|
2144
|
+
{
|
|
2145
|
+
role: "user",
|
|
2146
|
+
content: {
|
|
2147
|
+
type: "text",
|
|
2148
|
+
text: `The following is your persistent memory and identity context from Alma. Use it to personalize your responses:
|
|
2149
|
+
|
|
2150
|
+
${result.system_prompt}`
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
]
|
|
2154
|
+
};
|
|
2155
|
+
} catch (err) {
|
|
2156
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
2157
|
+
return {
|
|
2158
|
+
messages: [
|
|
2159
|
+
{
|
|
2160
|
+
role: "user",
|
|
2161
|
+
content: {
|
|
2162
|
+
type: "text",
|
|
2163
|
+
text: `[Alma context unavailable: ${message}]`
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
]
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
);
|
|
2171
|
+
server.prompt(
|
|
2172
|
+
"alma-recall",
|
|
2173
|
+
"Search Alma's memory for information about a specific topic.",
|
|
2174
|
+
{ topic: { description: "The topic to recall memories about", required: true } },
|
|
2175
|
+
async (args) => {
|
|
2176
|
+
try {
|
|
2177
|
+
const result = await client.searchMemories({ q: args.topic ?? "", limit: 10 });
|
|
2178
|
+
const memoryText = result.memories.length > 0 ? result.memories.map((m) => `- ${m.content} (${m.category}, importance: ${m.importance})`).join("\n") : "No memories found for this topic.";
|
|
2179
|
+
return {
|
|
2180
|
+
messages: [
|
|
2181
|
+
{
|
|
2182
|
+
role: "user",
|
|
2183
|
+
content: {
|
|
2184
|
+
type: "text",
|
|
2185
|
+
text: `Here is what Alma remembers about "${args.topic}":
|
|
2186
|
+
|
|
2187
|
+
${memoryText}`
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
]
|
|
2191
|
+
};
|
|
2192
|
+
} catch {
|
|
2193
|
+
return {
|
|
2194
|
+
messages: [
|
|
2195
|
+
{ role: "user", content: { type: "text", text: `[Could not recall memories for "${args.topic}"]` } }
|
|
2196
|
+
]
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
);
|
|
1526
2201
|
async function main() {
|
|
1527
2202
|
const transport = new StdioServerTransport();
|
|
1528
2203
|
await server.connect(transport);
|
|
1529
|
-
console.error("[alma-mcp] Server started on stdio (v1.3.
|
|
2204
|
+
console.error("[alma-mcp] Server started on stdio (v1.3.2, 35 tools, 10 resources, 2 prompts)");
|
|
1530
2205
|
if (process.env.ALMA_DEBUG) {
|
|
1531
2206
|
console.error(`[alma-mcp] API: ${config.baseUrl}`);
|
|
1532
2207
|
if (config.environmentId) {
|
package/package.json
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@olivaresai/alma-mcp",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"description": "MCP Server for Alma — persistent memory for AI agents",
|
|
3
|
+
"version": "1.3.3",
|
|
4
|
+
"description": "MCP Server for Alma by OlivaresAI — persistent memory for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"alma-mcp": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"main": "dist/index.js",
|
|
10
|
-
"files": [
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mcp",
|
|
17
|
+
"alma",
|
|
18
|
+
"ai",
|
|
19
|
+
"memory",
|
|
20
|
+
"persistent-memory",
|
|
21
|
+
"ai-agent",
|
|
22
|
+
"model-context-protocol",
|
|
23
|
+
"claude",
|
|
24
|
+
"cursor",
|
|
25
|
+
"windsurf",
|
|
26
|
+
"llm"
|
|
27
|
+
],
|
|
18
28
|
"dependencies": {
|
|
19
29
|
"@modelcontextprotocol/sdk": "^1.27.0",
|
|
20
30
|
"zod": "^3.24.0"
|
|
@@ -35,5 +45,9 @@
|
|
|
35
45
|
},
|
|
36
46
|
"author": "OlivaresAI",
|
|
37
47
|
"license": "SEE LICENSE IN LICENSE",
|
|
38
|
-
"
|
|
39
|
-
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsup",
|
|
50
|
+
"dev": "tsx src/index.ts",
|
|
51
|
+
"typecheck": "tsc --noEmit"
|
|
52
|
+
}
|
|
53
|
+
}
|