@olivaresai/alma-mcp 1.3.0 → 1.3.2

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.
Files changed (3) hide show
  1. package/README.md +90 -93
  2. package/dist/index.js +637 -2
  3. package/package.json +26 -12
package/README.md CHANGED
@@ -1,36 +1,52 @@
1
+ <div align="center">
2
+
1
3
  # @olivaresai/alma-mcp
2
4
 
3
- MCP (Model Context Protocol) server for [Alma](https://alma.olivares.ai) — persistent memory for AI agents. Gives any MCP-compatible client (Claude Desktop, Cursor, Windsurf, etc.) full access to Alma's memory, context, and chat capabilities.
5
+ **MCP Server for [Alma](https://alma.olivares.ai) — persistent memory for AI agents.**
4
6
 
5
- ## Setup
7
+ [![npm](https://img.shields.io/npm/v/@olivaresai/alma-mcp?color=FE9A37)](https://www.npmjs.com/package/@olivaresai/alma-mcp)
8
+ [![MCP](https://img.shields.io/badge/MCP-compatible-3178c6)](https://modelcontextprotocol.io)
9
+ [![Node](https://img.shields.io/badge/Node.js-18+-339933)](https://nodejs.org)
6
10
 
7
- ```bash
8
- npm install -g @olivaresai/alma-mcp
9
- ```
11
+ </div>
12
+
13
+ ---
14
+
15
+ Gives any MCP-compatible AI client full access to Alma's persistent memory, context assembly, chat, and data management. Your AI remembers everything — across sessions, tools, and platforms.
16
+
17
+ ## Setup
10
18
 
11
19
  ### Claude Desktop / Cursor / Windsurf
12
20
 
13
- Add to your MCP configuration (`claude_desktop_config.json` or equivalent):
21
+ Add to your MCP configuration:
14
22
 
15
23
  ```json
16
24
  {
17
25
  "mcpServers": {
18
26
  "alma": {
19
- "command": "alma-mcp",
27
+ "command": "npx",
28
+ "args": ["@olivaresai/alma-mcp"],
20
29
  "env": {
21
- "ALMA_API_KEY": "your-api-key",
22
- "ALMA_BASE_URL": "https://alma.olivares.ai/api/v1"
30
+ "ALMA_API_KEY": "alma_sk_..."
23
31
  }
24
32
  }
25
33
  }
26
34
  }
27
35
  ```
28
36
 
37
+ ### Global install (alternative)
38
+
39
+ ```bash
40
+ npm install -g @olivaresai/alma-mcp
41
+ ```
42
+
43
+ Then use `"command": "alma-mcp"` instead of `npx`.
44
+
29
45
  ### Environment Variables
30
46
 
31
47
  | Variable | Required | Description |
32
48
  |----------|----------|-------------|
33
- | `ALMA_API_KEY` | Yes | Your Alma API key |
49
+ | `ALMA_API_KEY` | Yes | Your Alma API key ([get one here](https://alma.olivares.ai/settings)) |
34
50
  | `ALMA_BASE_URL` | No | API base URL (default: `https://alma.olivares.ai/api/v1`) |
35
51
  | `ALMA_ENVIRONMENT_ID` | No | Default environment ID |
36
52
  | `ALMA_DEBUG` | No | Enable debug logging |
@@ -39,107 +55,88 @@ Add to your MCP configuration (`claude_desktop_config.json` or equivalent):
39
55
 
40
56
  ### Context
41
57
 
42
- | Tool | Description | Parameters |
43
- |------|-------------|------------|
44
- | `alma_assemble` | Build a complete system prompt from Alma includes identity (soul), relevant memories, recent episodes, and learned procedures. Use this before sending a message to an LLM. | `message` (required), `environment_id`, `token_budget` |
45
- | `alma_preview_context` | Preview the full system prompt that Alma would assemble. Shows exactly what the LLM sees. Useful for debugging. | `message` |
46
- | `alma_focus` | Update Alma's active context focus. Tell Alma what you're currently working on so it can prioritize relevant memories. | `context` (required, max 2000 chars), `source` (web/extension/mcp/api) |
47
- | `alma_continue_session` | Continue a session after context limit, new session, or error. Generates a summary and continuation prompt. | `conversation_text` (required), `reason` (required: context_limit/new_session/error), `environment_id` |
58
+ | Tool | Description |
59
+ |------|-------------|
60
+ | `alma_assemble` | Build a complete system prompt — soul, memories, episodes, procedures |
61
+ | `alma_preview_context` | Preview the exact system prompt the LLM would see |
62
+ | `alma_focus` | Update active context focus (what you're working on) |
63
+ | `alma_continue_session` | Continue after context limit, new session, or error |
48
64
 
49
65
  ### Memory
50
66
 
51
- | Tool | Description | Parameters |
52
- |------|-------------|------------|
53
- | `alma_remember` | Store a new memory in Alma. | `content` (required, max 50000 chars), `category` (general/preference/fact/decision/project), `importance` (0-1) |
54
- | `alma_recall` | Search memories by keyword. | `query` (required), `limit` (1-50, default 10) |
55
- | `alma_search` | Advanced memory search with filters — keyword, semantic, or hybrid mode with date ranges and importance thresholds. | `query` (required), `mode` (keyword/semantic/hybrid), `from`/`to` (ISO dates), `category`, `min_importance` (0-1), `limit` (1-50) |
56
- | `alma_delete_memory` | Delete a specific memory by ID. | `id` (required) |
57
- | `alma_extract` | Extract memories and episodes from a block of text (conversation, meeting notes, etc.). | `text` (required, 10-200K chars), `save` (default true), `environment_id` |
58
-
59
- ### Episodes
60
-
61
- | Tool | Description | Parameters |
62
- |------|-------------|------------|
63
- | `alma_list_episodes` | List conversation episodes (summaries with topics and outcomes). | `limit` (1-100, default 20), `offset` |
64
- | `alma_search_episodes` | Search episodes by topic. | `topic` (required), `limit` (1-50, default 10) |
65
- | `alma_create_episode` | Create a new episode manually. | `summary` (required, max 5000 chars), `topics` (string array), `outcome` |
66
-
67
- ### Procedures
68
-
69
- | Tool | Description | Parameters |
70
- |------|-------------|------------|
71
- | `alma_list_procedures` | List stored procedures (trigger-action rules). | `limit` (1-100, default 50), `offset` |
72
- | `alma_create_procedure` | Create a new procedure. | `trigger_pattern` (required), `action` (required), `context` |
73
-
74
- ### Soul & Blocks
75
-
76
- | Tool | Description | Parameters |
77
- |------|-------------|------------|
78
- | `alma_update_block` | Update a soul memory block (identity, rules, language, etc.). | `label` (required), `value` (required) |
79
-
80
- ### Chat
81
-
82
- | Tool | Description | Parameters |
83
- |------|-------------|------------|
84
- | `alma_chat` | Send a message to Alma and get a response. Supports 3 Claude models. | `message` (required), `conversation_id`, `environment_id`, `model` |
85
-
86
- ### Images
87
-
88
- | Tool | Description | Parameters |
89
- |------|-------------|------------|
90
- | `alma_generate_image` | Generate an image via Alma's Image Studio (Replicate Flux / Leonardo). Requires a paid plan. | `prompt` (required), `style` (natural/vivid/artistic), `size` (square/landscape/portrait) |
91
-
92
- ### Memory Management
93
-
94
- | Tool | Description | Parameters |
95
- |------|-------------|------------|
96
- | `alma_update_memory` | Update an existing memory's content, category, or importance. | `id` (required), `content`, `category`, `importance` (0-1) |
97
-
98
- ### Environments
99
-
100
- | Tool | Description | Parameters |
101
- |------|-------------|------------|
102
- | `alma_list_environments` | List all available environments. | _(none)_ |
103
- | `alma_create_environment` | Create a new environment. | `name` (required, max 100 chars) |
104
-
105
- ### Data
106
-
107
- | Tool | Description | Parameters |
108
- |------|-------------|------------|
109
- | `alma_export` | Export data from Alma (conversations, memories, soul, or all). | `type` (required: conversation/memories/soul/all), `format` (md/json/html), `conversation_id` |
67
+ | Tool | Description |
68
+ |------|-------------|
69
+ | `alma_remember` | Store a new memory |
70
+ | `alma_recall` | Quick keyword search across memories |
71
+ | `alma_search` | Advanced search — keyword, semantic, or hybrid with filters |
72
+ | `alma_update_memory` | Update an existing memory's content, category, or importance |
73
+ | `alma_delete_memory` | Delete a specific memory |
74
+ | `alma_extract` | Extract memories and episodes from text (conversations, notes) |
75
+
76
+ ### Episodes & Procedures
77
+
78
+ | Tool | Description |
79
+ |------|-------------|
80
+ | `alma_list_episodes` | List conversation episode summaries |
81
+ | `alma_search_episodes` | Search episodes by topic |
82
+ | `alma_create_episode` | Create a new episode manually |
83
+ | `alma_list_procedures` | List trigger-action behavioral rules |
84
+ | `alma_create_procedure` | Create a new procedure |
85
+
86
+ ### Soul, Chat & More
87
+
88
+ | Tool | Description |
89
+ |------|-------------|
90
+ | `alma_update_block` | Update a soul memory block (identity, rules, etc.) |
91
+ | `alma_chat` | Send a message to Alma and get a response |
92
+ | `alma_generate_image` | Generate an image via Image Studio |
93
+ | `alma_export` | Export data (conversations, memories, soul, all) |
94
+ | `alma_list_environments` | List all environments |
95
+ | `alma_create_environment` | Create a new environment |
110
96
 
111
97
  ## Resources (9)
112
98
 
113
99
  | Resource URI | Description |
114
100
  |-------------|-------------|
115
- | `alma://soul` | Current soul configuration (identity, personality, tone, rules) |
101
+ | `alma://soul` | Current soul configuration |
116
102
  | `alma://memories` | Recent memories (last 50) |
117
- | `alma://memories/{category}` | Memories filtered by category (general, preference, fact, decision, project) |
103
+ | `alma://memories/{category}` | Memories by category (general, preference, fact, decision, project) |
118
104
  | `alma://environments` | All available environments |
119
- | `alma://conversations` | Recent chat conversations (last 20) |
120
- | `alma://budget` | Current token budget status (usage, remaining, purchased) |
121
- | `alma://blocks` | All memory blocks (identity, worldview, style, rules, etc.) |
105
+ | `alma://conversations` | Recent conversations (last 20) |
106
+ | `alma://budget` | Token budget status |
107
+ | `alma://blocks` | All soul memory blocks |
122
108
  | `alma://episodes` | Recent episodes (last 20) |
123
109
  | `alma://procedures` | All stored procedures (limit 50) |
124
110
 
125
- ## Supported Models
111
+ ## Models
126
112
 
127
- 3 models (Anthropic Claude):
113
+ 3 Claude models available via `alma_chat`:
128
114
 
129
- | Model | Tier | Best For |
130
- |-------|------|----------|
131
- | `claude-haiku` | Normal | Fast responses, simple tasks |
132
- | `claude-sonnet` | Advanced | Balanced quality and speed |
133
- | `claude-opus` | Complex | Highest quality, deep reasoning |
115
+ | Model | Best For |
116
+ |-------|----------|
117
+ | `claude-haiku` | Fast responses, simple tasks |
118
+ | `claude-sonnet` | Balanced quality and speed |
119
+ | `claude-opus` | Deep reasoning, complex work |
120
+
121
+ ## Compatible Clients
122
+
123
+ - **Claude Desktop** — Anthropic's official desktop app
124
+ - **Cursor** — AI-powered code editor
125
+ - **Windsurf** — AI development environment
126
+ - Any [MCP-compatible](https://modelcontextprotocol.io) client
134
127
 
135
128
  ## Links
136
129
 
137
- - [Alma Platform](https://alma.olivares.ai)
138
- - [Documentation](https://alma.olivares.ai/docs)
139
- - [SDK](https://www.npmjs.com/package/@olivaresai/alma-sdk)
130
+ - [Alma App](https://alma.olivares.ai)
131
+ - [Documentation](https://olivares.ai/docs)
132
+ - [JavaScript SDK](https://www.npmjs.com/package/@olivaresai/alma-sdk)
140
133
  - [VSCode Extension](https://marketplace.visualstudio.com/items?itemName=olivares.alma-vscode)
141
- - [OlivaresAI](https://olivares.ai)
134
+ - [GitHub](https://github.com/olivaresai/alma)
135
+
136
+ ---
137
+
138
+ <div align="center">
142
139
 
143
- ## License
140
+ *Built by [olivares.ai](https://olivares.ai) — Give your AI a soul.*
144
141
 
145
- Proprietary. See [LICENSE](https://github.com/olivaresai/alma/blob/main/LICENSE) for details.
142
+ </div>
package/dist/index.js CHANGED
@@ -328,6 +328,87 @@ var AlmaClient = class _AlmaClient {
328
328
  async createProcedure(params) {
329
329
  return this.request("POST", "/procedures", params);
330
330
  }
331
+ // ─── Cowork (Collaborative Workspaces) ─────────────
332
+ async listWorkspaces() {
333
+ return this.request("GET", "/cowork/workspaces");
334
+ }
335
+ async createWorkspace(params) {
336
+ return this.request("POST", "/cowork/workspaces", params);
337
+ }
338
+ async listWorkspaceFiles(workspaceId) {
339
+ return this.request(
340
+ "GET",
341
+ `/cowork/workspaces/${encodeURIComponent(workspaceId)}/files`
342
+ );
343
+ }
344
+ async readWorkspaceFile(workspaceId, filePath) {
345
+ const qs = new URLSearchParams({ path: filePath });
346
+ return this.request(
347
+ "GET",
348
+ `/cowork/workspaces/${encodeURIComponent(workspaceId)}/files/read?${qs}`
349
+ );
350
+ }
351
+ async writeWorkspaceFile(workspaceId, filePath, content) {
352
+ return this.request(
353
+ "POST",
354
+ `/cowork/workspaces/${encodeURIComponent(workspaceId)}/files/write`,
355
+ { path: filePath, content }
356
+ );
357
+ }
358
+ async searchWorkspace(workspaceId, query) {
359
+ const qs = new URLSearchParams({ q: query });
360
+ return this.request(
361
+ "GET",
362
+ `/cowork/workspaces/${encodeURIComponent(workspaceId)}/search?${qs}`
363
+ );
364
+ }
365
+ async executeSkill(workspaceId, params) {
366
+ return this.request(
367
+ "POST",
368
+ `/cowork/workspaces/${encodeURIComponent(workspaceId)}/skills`,
369
+ params
370
+ );
371
+ }
372
+ async applyDiff(workspaceId, operationId) {
373
+ return this.request(
374
+ "POST",
375
+ `/cowork/workspaces/${encodeURIComponent(workspaceId)}/apply`,
376
+ { operationId }
377
+ );
378
+ }
379
+ // ─── Video ────────────────────────────────────────
380
+ async generateVideo(params) {
381
+ return this.request("POST", "/video/generate", params);
382
+ }
383
+ async checkVideoJob(jobId) {
384
+ return this.request(
385
+ "GET",
386
+ `/video/jobs/${encodeURIComponent(jobId)}`
387
+ );
388
+ }
389
+ async listVideoProjects() {
390
+ return this.request("GET", "/video/projects");
391
+ }
392
+ async createVideoProject(params) {
393
+ return this.request("POST", "/video/projects", params);
394
+ }
395
+ async planVideoScenes(projectId, params) {
396
+ return this.request(
397
+ "POST",
398
+ `/video/projects/${encodeURIComponent(projectId)}/plan`,
399
+ params
400
+ );
401
+ }
402
+ async stitchVideo(projectId, jobIds) {
403
+ return this.request(
404
+ "POST",
405
+ `/video/projects/${encodeURIComponent(projectId)}/stitch`,
406
+ { jobIds }
407
+ );
408
+ }
409
+ async getVideoBudget() {
410
+ return this.request("GET", "/video/budget");
411
+ }
331
412
  };
332
413
 
333
414
  // src/tools/assemble.ts
@@ -1232,12 +1313,470 @@ ${JSON.stringify(result.memory, null, 2)}`
1232
1313
  );
1233
1314
  }
1234
1315
 
1316
+ // src/tools/cowork.ts
1317
+ import { z as z18 } from "zod";
1318
+ function registerCoworkTools(server2, client2) {
1319
+ server2.registerTool(
1320
+ "cowork_list_workspaces",
1321
+ {
1322
+ title: "List Workspaces",
1323
+ description: "List all collaborative workspaces available to the user. Returns workspace IDs, names, and metadata.",
1324
+ inputSchema: {}
1325
+ },
1326
+ async () => {
1327
+ try {
1328
+ const result = await client2.listWorkspaces();
1329
+ if (result.workspaces.length === 0) {
1330
+ return {
1331
+ content: [{ type: "text", text: "No workspaces found." }]
1332
+ };
1333
+ }
1334
+ return {
1335
+ content: [
1336
+ {
1337
+ type: "text",
1338
+ text: JSON.stringify(result, null, 2)
1339
+ }
1340
+ ]
1341
+ };
1342
+ } catch (err) {
1343
+ const message = err instanceof Error ? err.message : "Unknown error";
1344
+ return {
1345
+ content: [{ type: "text", text: `Error: ${message}` }],
1346
+ isError: true
1347
+ };
1348
+ }
1349
+ }
1350
+ );
1351
+ server2.registerTool(
1352
+ "cowork_create_workspace",
1353
+ {
1354
+ title: "Create Workspace",
1355
+ description: "Create a new collaborative workspace. Workspaces hold files that Alma can read, write, and operate on.",
1356
+ inputSchema: {
1357
+ name: z18.string().min(1).max(200).describe("Name for the workspace"),
1358
+ description: z18.string().max(1e3).optional().describe("Optional description of the workspace purpose"),
1359
+ sourceType: z18.string().optional().describe('Source type for the workspace (e.g. "blank", "template", "import")')
1360
+ }
1361
+ },
1362
+ async ({ name, description, sourceType }) => {
1363
+ try {
1364
+ const result = await client2.createWorkspace({ name, description, sourceType });
1365
+ return {
1366
+ content: [
1367
+ {
1368
+ type: "text",
1369
+ text: `Workspace created successfully.
1370
+ ${JSON.stringify(result.workspace, null, 2)}`
1371
+ }
1372
+ ]
1373
+ };
1374
+ } catch (err) {
1375
+ const message = err instanceof Error ? err.message : "Unknown error";
1376
+ return {
1377
+ content: [{ type: "text", text: `Error: ${message}` }],
1378
+ isError: true
1379
+ };
1380
+ }
1381
+ }
1382
+ );
1383
+ server2.registerTool(
1384
+ "cowork_list_files",
1385
+ {
1386
+ title: "List Workspace Files",
1387
+ description: "List all files in a collaborative workspace. Returns file paths, sizes, and metadata.",
1388
+ inputSchema: {
1389
+ workspaceId: z18.string().min(1).describe("The workspace ID to list files from")
1390
+ }
1391
+ },
1392
+ async ({ workspaceId }) => {
1393
+ try {
1394
+ const result = await client2.listWorkspaceFiles(workspaceId);
1395
+ if (result.files.length === 0) {
1396
+ return {
1397
+ content: [{ type: "text", text: "No files found in this workspace." }]
1398
+ };
1399
+ }
1400
+ return {
1401
+ content: [
1402
+ {
1403
+ type: "text",
1404
+ text: JSON.stringify(result, null, 2)
1405
+ }
1406
+ ]
1407
+ };
1408
+ } catch (err) {
1409
+ const message = err instanceof Error ? err.message : "Unknown error";
1410
+ return {
1411
+ content: [{ type: "text", text: `Error: ${message}` }],
1412
+ isError: true
1413
+ };
1414
+ }
1415
+ }
1416
+ );
1417
+ server2.registerTool(
1418
+ "cowork_read_file",
1419
+ {
1420
+ title: "Read Workspace File",
1421
+ description: "Read the content of a file in a collaborative workspace. Returns the file content and metadata.",
1422
+ inputSchema: {
1423
+ workspaceId: z18.string().min(1).describe("The workspace ID containing the file"),
1424
+ filePath: z18.string().min(1).describe("Path of the file within the workspace")
1425
+ }
1426
+ },
1427
+ async ({ workspaceId, filePath }) => {
1428
+ try {
1429
+ const result = await client2.readWorkspaceFile(workspaceId, filePath);
1430
+ return {
1431
+ content: [
1432
+ {
1433
+ type: "text",
1434
+ text: JSON.stringify(result.file, null, 2)
1435
+ }
1436
+ ]
1437
+ };
1438
+ } catch (err) {
1439
+ const message = err instanceof Error ? err.message : "Unknown error";
1440
+ return {
1441
+ content: [{ type: "text", text: `Error: ${message}` }],
1442
+ isError: true
1443
+ };
1444
+ }
1445
+ }
1446
+ );
1447
+ server2.registerTool(
1448
+ "cowork_write_file",
1449
+ {
1450
+ title: "Write Workspace File",
1451
+ description: "Write or update a file in a collaborative workspace. Creates the file if it does not exist.",
1452
+ inputSchema: {
1453
+ workspaceId: z18.string().min(1).describe("The workspace ID to write the file in"),
1454
+ filePath: z18.string().min(1).describe("Path of the file within the workspace"),
1455
+ content: z18.string().describe("The content to write to the file")
1456
+ }
1457
+ },
1458
+ async ({ workspaceId, filePath, content }) => {
1459
+ try {
1460
+ const result = await client2.writeWorkspaceFile(workspaceId, filePath, content);
1461
+ return {
1462
+ content: [
1463
+ {
1464
+ type: "text",
1465
+ text: `File written successfully.
1466
+ ${JSON.stringify(result.file, null, 2)}`
1467
+ }
1468
+ ]
1469
+ };
1470
+ } catch (err) {
1471
+ const message = err instanceof Error ? err.message : "Unknown error";
1472
+ return {
1473
+ content: [{ type: "text", text: `Error: ${message}` }],
1474
+ isError: true
1475
+ };
1476
+ }
1477
+ }
1478
+ );
1479
+ server2.registerTool(
1480
+ "cowork_search",
1481
+ {
1482
+ title: "Search Workspace",
1483
+ description: "Search across all files in a collaborative workspace. Returns matching files and content snippets.",
1484
+ inputSchema: {
1485
+ workspaceId: z18.string().min(1).describe("The workspace ID to search in"),
1486
+ query: z18.string().min(1).describe("Search query string")
1487
+ }
1488
+ },
1489
+ async ({ workspaceId, query }) => {
1490
+ try {
1491
+ const result = await client2.searchWorkspace(workspaceId, query);
1492
+ if (result.results.length === 0) {
1493
+ return {
1494
+ content: [{ type: "text", text: `No results found for: ${query}` }]
1495
+ };
1496
+ }
1497
+ return {
1498
+ content: [
1499
+ {
1500
+ type: "text",
1501
+ text: JSON.stringify(result, null, 2)
1502
+ }
1503
+ ]
1504
+ };
1505
+ } catch (err) {
1506
+ const message = err instanceof Error ? err.message : "Unknown error";
1507
+ return {
1508
+ content: [{ type: "text", text: `Error: ${message}` }],
1509
+ isError: true
1510
+ };
1511
+ }
1512
+ }
1513
+ );
1514
+ server2.registerTool(
1515
+ "cowork_execute_skill",
1516
+ {
1517
+ title: "Execute Skill",
1518
+ description: "Run an AI skill on a file or input within a collaborative workspace. Available skills: explain, refactor, review, test, fix, document, search, commit.",
1519
+ inputSchema: {
1520
+ workspaceId: z18.string().min(1).describe("The workspace ID to execute the skill in"),
1521
+ skill: z18.enum(["explain", "refactor", "review", "test", "fix", "document", "search", "commit"]).describe("The skill to execute"),
1522
+ targetPath: z18.string().optional().describe("Optional file path to target with the skill"),
1523
+ input: z18.string().min(1).describe("Input or instructions for the skill execution")
1524
+ }
1525
+ },
1526
+ async ({ workspaceId, skill, targetPath, input }) => {
1527
+ try {
1528
+ const result = await client2.executeSkill(workspaceId, { skill, targetPath, input });
1529
+ return {
1530
+ content: [
1531
+ {
1532
+ type: "text",
1533
+ text: `Skill "${skill}" executed successfully.
1534
+ ${JSON.stringify(result.result, null, 2)}`
1535
+ }
1536
+ ]
1537
+ };
1538
+ } catch (err) {
1539
+ const message = err instanceof Error ? err.message : "Unknown error";
1540
+ return {
1541
+ content: [{ type: "text", text: `Error: ${message}` }],
1542
+ isError: true
1543
+ };
1544
+ }
1545
+ }
1546
+ );
1547
+ server2.registerTool(
1548
+ "cowork_apply_diff",
1549
+ {
1550
+ title: "Apply Operation Diff",
1551
+ description: "Apply a pending operation diff to a workspace file. Use after executing a skill that produces changes.",
1552
+ inputSchema: {
1553
+ workspaceId: z18.string().min(1).describe("The workspace ID containing the operation"),
1554
+ operationId: z18.string().min(1).describe("The operation ID whose diff should be applied")
1555
+ }
1556
+ },
1557
+ async ({ workspaceId, operationId }) => {
1558
+ try {
1559
+ const result = await client2.applyDiff(workspaceId, operationId);
1560
+ return {
1561
+ content: [
1562
+ {
1563
+ type: "text",
1564
+ text: result.applied ? `Diff applied successfully.
1565
+ ${JSON.stringify(result.result, null, 2)}` : "Diff could not be applied. The operation may have already been applied or expired."
1566
+ }
1567
+ ]
1568
+ };
1569
+ } catch (err) {
1570
+ const message = err instanceof Error ? err.message : "Unknown error";
1571
+ return {
1572
+ content: [{ type: "text", text: `Error: ${message}` }],
1573
+ isError: true
1574
+ };
1575
+ }
1576
+ }
1577
+ );
1578
+ }
1579
+
1580
+ // src/tools/video.ts
1581
+ import { z as z19 } from "zod";
1582
+ function registerVideoTools(server2, client2) {
1583
+ server2.registerTool(
1584
+ "video_generate",
1585
+ {
1586
+ title: "Generate Video",
1587
+ description: "Start an AI video generation job. Returns a job ID to track progress. Requires a paid plan with video credits.",
1588
+ inputSchema: {
1589
+ prompt: z19.string().min(1).max(2e3).describe("Text description of the video to generate"),
1590
+ model: z19.string().min(1).describe('Video generation model to use (e.g. "runway-gen3", "kling-v1", "pika-v2")'),
1591
+ durationSeconds: z19.number().int().min(1).max(60).describe("Duration of the video in seconds (1-60)"),
1592
+ aspectRatio: z19.enum(["16:9", "9:16", "1:1", "4:3", "3:4"]).optional().describe("Aspect ratio of the video (default: 16:9)"),
1593
+ style: z19.string().max(200).optional().describe('Optional style modifier (e.g. "cinematic", "anime", "documentary")')
1594
+ }
1595
+ },
1596
+ async ({ prompt, model, durationSeconds, aspectRatio, style }) => {
1597
+ try {
1598
+ const result = await client2.generateVideo({ prompt, model, durationSeconds, aspectRatio, style });
1599
+ return {
1600
+ content: [
1601
+ {
1602
+ type: "text",
1603
+ text: `Video generation started.
1604
+ ${JSON.stringify(result.job, null, 2)}`
1605
+ }
1606
+ ]
1607
+ };
1608
+ } catch (err) {
1609
+ const message = err instanceof Error ? err.message : "Unknown error";
1610
+ return {
1611
+ content: [{ type: "text", text: `Error: ${message}` }],
1612
+ isError: true
1613
+ };
1614
+ }
1615
+ }
1616
+ );
1617
+ server2.registerTool(
1618
+ "video_check_job",
1619
+ {
1620
+ title: "Check Video Job",
1621
+ description: "Check the status of a video generation job. Returns status, progress percentage, and result URL when complete.",
1622
+ inputSchema: {
1623
+ jobId: z19.string().min(1).describe("The video generation job ID to check")
1624
+ }
1625
+ },
1626
+ async ({ jobId }) => {
1627
+ try {
1628
+ const result = await client2.checkVideoJob(jobId);
1629
+ return {
1630
+ content: [
1631
+ {
1632
+ type: "text",
1633
+ text: JSON.stringify(result.job, null, 2)
1634
+ }
1635
+ ]
1636
+ };
1637
+ } catch (err) {
1638
+ const message = err instanceof Error ? err.message : "Unknown error";
1639
+ return {
1640
+ content: [{ type: "text", text: `Error: ${message}` }],
1641
+ isError: true
1642
+ };
1643
+ }
1644
+ }
1645
+ );
1646
+ server2.registerTool(
1647
+ "video_list_projects",
1648
+ {
1649
+ title: "List Video Projects",
1650
+ description: "List all video projects for the user. Projects organize multiple video scenes into a cohesive production.",
1651
+ inputSchema: {}
1652
+ },
1653
+ async () => {
1654
+ try {
1655
+ const result = await client2.listVideoProjects();
1656
+ if (result.projects.length === 0) {
1657
+ return {
1658
+ content: [{ type: "text", text: "No video projects found." }]
1659
+ };
1660
+ }
1661
+ return {
1662
+ content: [
1663
+ {
1664
+ type: "text",
1665
+ text: JSON.stringify(result, null, 2)
1666
+ }
1667
+ ]
1668
+ };
1669
+ } catch (err) {
1670
+ const message = err instanceof Error ? err.message : "Unknown error";
1671
+ return {
1672
+ content: [{ type: "text", text: `Error: ${message}` }],
1673
+ isError: true
1674
+ };
1675
+ }
1676
+ }
1677
+ );
1678
+ server2.registerTool(
1679
+ "video_create_project",
1680
+ {
1681
+ title: "Create Video Project",
1682
+ description: "Create a new video project. Projects let you plan scenes, generate clips, and stitch them into a final video.",
1683
+ inputSchema: {
1684
+ name: z19.string().min(1).max(200).describe("Name for the video project"),
1685
+ description: z19.string().max(1e3).optional().describe("Optional description of the video project")
1686
+ }
1687
+ },
1688
+ async ({ name, description }) => {
1689
+ try {
1690
+ const result = await client2.createVideoProject({ name, description });
1691
+ return {
1692
+ content: [
1693
+ {
1694
+ type: "text",
1695
+ text: `Video project created successfully.
1696
+ ${JSON.stringify(result.project, null, 2)}`
1697
+ }
1698
+ ]
1699
+ };
1700
+ } catch (err) {
1701
+ const message = err instanceof Error ? err.message : "Unknown error";
1702
+ return {
1703
+ content: [{ type: "text", text: `Error: ${message}` }],
1704
+ isError: true
1705
+ };
1706
+ }
1707
+ }
1708
+ );
1709
+ server2.registerTool(
1710
+ "video_plan_scenes",
1711
+ {
1712
+ title: "Plan Video Scenes",
1713
+ description: "Use AI to plan scenes for a video project. Breaks a description into individual scene prompts with timing and transitions.",
1714
+ inputSchema: {
1715
+ projectId: z19.string().min(1).describe("The video project ID to plan scenes for"),
1716
+ description: z19.string().min(1).max(5e3).describe("Description of the video to plan (story, concept, or script)"),
1717
+ sceneCount: z19.number().int().min(1).max(20).optional().describe("Number of scenes to plan (default: auto-determined by AI)")
1718
+ }
1719
+ },
1720
+ async ({ projectId, description, sceneCount }) => {
1721
+ try {
1722
+ const result = await client2.planVideoScenes(projectId, { description, sceneCount });
1723
+ return {
1724
+ content: [
1725
+ {
1726
+ type: "text",
1727
+ text: `Scene plan created (${result.scenes.length} scenes).
1728
+ ${JSON.stringify(result.scenes, null, 2)}`
1729
+ }
1730
+ ]
1731
+ };
1732
+ } catch (err) {
1733
+ const message = err instanceof Error ? err.message : "Unknown error";
1734
+ return {
1735
+ content: [{ type: "text", text: `Error: ${message}` }],
1736
+ isError: true
1737
+ };
1738
+ }
1739
+ }
1740
+ );
1741
+ server2.registerTool(
1742
+ "video_stitch",
1743
+ {
1744
+ title: "Stitch Video",
1745
+ description: "Stitch multiple generated video scenes into a single final video. All referenced jobs must be complete.",
1746
+ inputSchema: {
1747
+ projectId: z19.string().min(1).describe("The video project ID"),
1748
+ jobIds: z19.array(z19.string().min(1)).min(1).max(20).describe("Ordered list of completed video job IDs to stitch together")
1749
+ }
1750
+ },
1751
+ async ({ projectId, jobIds }) => {
1752
+ try {
1753
+ const result = await client2.stitchVideo(projectId, jobIds);
1754
+ return {
1755
+ content: [
1756
+ {
1757
+ type: "text",
1758
+ text: `Video stitch job started.
1759
+ ${JSON.stringify(result.job, null, 2)}`
1760
+ }
1761
+ ]
1762
+ };
1763
+ } catch (err) {
1764
+ const message = err instanceof Error ? err.message : "Unknown error";
1765
+ return {
1766
+ content: [{ type: "text", text: `Error: ${message}` }],
1767
+ isError: true
1768
+ };
1769
+ }
1770
+ }
1771
+ );
1772
+ }
1773
+
1235
1774
  // src/index.ts
1236
1775
  var config = loadConfig();
1237
1776
  var client = new AlmaClient(config);
1238
1777
  var server = new McpServer({
1239
1778
  name: "alma-mcp",
1240
- version: "1.3.0"
1779
+ version: "1.3.2"
1241
1780
  });
1242
1781
  registerAssembleTool(server, client);
1243
1782
  registerRememberTool(server, client);
@@ -1260,6 +1799,8 @@ registerListProceduresTool(server, client);
1260
1799
  registerCreateProcedureTool(server, client);
1261
1800
  registerPreviewContextTool(server, client);
1262
1801
  registerUpdateMemoryTool(server, client);
1802
+ registerCoworkTools(server, client);
1803
+ registerVideoTools(server, client);
1263
1804
  server.registerResource(
1264
1805
  "soul",
1265
1806
  "alma://soul",
@@ -1400,6 +1941,34 @@ server.registerResource(
1400
1941
  }
1401
1942
  }
1402
1943
  );
1944
+ server.registerResource(
1945
+ "video-budget",
1946
+ "alma://video-budget",
1947
+ {
1948
+ title: "Video Budget",
1949
+ description: "Current video generation credits, usage, and remaining quota",
1950
+ mimeType: "application/json"
1951
+ },
1952
+ async (uri) => {
1953
+ try {
1954
+ const result = await client.getVideoBudget();
1955
+ return {
1956
+ contents: [
1957
+ {
1958
+ uri: uri.href,
1959
+ mimeType: "application/json",
1960
+ text: JSON.stringify(result, null, 2)
1961
+ }
1962
+ ]
1963
+ };
1964
+ } catch (err) {
1965
+ const message = err instanceof Error ? err.message : "Unknown error";
1966
+ return {
1967
+ contents: [{ uri: uri.href, mimeType: "text/plain", text: `Error loading resource: ${message}` }]
1968
+ };
1969
+ }
1970
+ }
1971
+ );
1403
1972
  server.registerResource(
1404
1973
  "memories-by-category",
1405
1974
  new ResourceTemplate("alma://memories/{category}", {
@@ -1523,10 +2092,76 @@ server.registerResource(
1523
2092
  }
1524
2093
  }
1525
2094
  );
2095
+ server.prompt(
2096
+ "alma-context",
2097
+ "Assemble Alma's full context: soul identity, relevant memories, recent episodes, and procedures. Use this to give the AI persistent memory about the user.",
2098
+ {},
2099
+ async () => {
2100
+ try {
2101
+ const result = await client.assembleContext({ message: "general conversation" });
2102
+ return {
2103
+ messages: [
2104
+ {
2105
+ role: "user",
2106
+ content: {
2107
+ type: "text",
2108
+ text: `The following is your persistent memory and identity context from Alma. Use it to personalize your responses:
2109
+
2110
+ ${result.system_prompt}`
2111
+ }
2112
+ }
2113
+ ]
2114
+ };
2115
+ } catch (err) {
2116
+ const message = err instanceof Error ? err.message : "Unknown error";
2117
+ return {
2118
+ messages: [
2119
+ {
2120
+ role: "user",
2121
+ content: {
2122
+ type: "text",
2123
+ text: `[Alma context unavailable: ${message}]`
2124
+ }
2125
+ }
2126
+ ]
2127
+ };
2128
+ }
2129
+ }
2130
+ );
2131
+ server.prompt(
2132
+ "alma-recall",
2133
+ "Search Alma's memory for information about a specific topic.",
2134
+ { topic: { description: "The topic to recall memories about", required: true } },
2135
+ async (args) => {
2136
+ try {
2137
+ const result = await client.searchMemories({ q: args.topic ?? "", limit: 10 });
2138
+ 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.";
2139
+ return {
2140
+ messages: [
2141
+ {
2142
+ role: "user",
2143
+ content: {
2144
+ type: "text",
2145
+ text: `Here is what Alma remembers about "${args.topic}":
2146
+
2147
+ ${memoryText}`
2148
+ }
2149
+ }
2150
+ ]
2151
+ };
2152
+ } catch {
2153
+ return {
2154
+ messages: [
2155
+ { role: "user", content: { type: "text", text: `[Could not recall memories for "${args.topic}"]` } }
2156
+ ]
2157
+ };
2158
+ }
2159
+ }
2160
+ );
1526
2161
  async function main() {
1527
2162
  const transport = new StdioServerTransport();
1528
2163
  await server.connect(transport);
1529
- console.error("[alma-mcp] Server started on stdio (v1.3.0, 21 tools, 9 resources)");
2164
+ console.error("[alma-mcp] Server started on stdio (v1.3.2, 35 tools, 10 resources, 2 prompts)");
1530
2165
  if (process.env.ALMA_DEBUG) {
1531
2166
  console.error(`[alma-mcp] API: ${config.baseUrl}`);
1532
2167
  if (config.environmentId) {
package/package.json CHANGED
@@ -1,20 +1,30 @@
1
1
  {
2
2
  "name": "@olivaresai/alma-mcp",
3
- "version": "1.3.0",
4
- "description": "MCP Server for Alma — persistent memory for AI agents",
3
+ "version": "1.3.2",
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": ["dist", "README.md", "LICENSE"],
11
- "scripts": {
12
- "build": "tsup",
13
- "dev": "tsx src/index.ts",
14
- "typecheck": "tsc --noEmit",
15
- "prepublishOnly": "npm run build"
16
- },
17
- "keywords": ["mcp", "alma", "ai", "memory", "persistent-memory", "ai-agent", "model-context-protocol", "claude", "cursor", "windsurf", "llm"],
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
- "pnpm": {}
39
- }
48
+ "scripts": {
49
+ "build": "tsup",
50
+ "dev": "tsx src/index.ts",
51
+ "typecheck": "tsc --noEmit"
52
+ }
53
+ }