harmony-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 (4) hide show
  1. package/README.md +106 -15
  2. package/dist/cli.js +179 -28
  3. package/dist/index.js +88 -10
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -8,6 +8,7 @@ MCP (Model Context Protocol) server for Harmony Kanban board. Enables AI coding
8
8
  - **Card Linking** - create relationships between cards (blocks, relates_to, duplicates, is_part_of)
9
9
  - **Prompt Builder** - generate AI-ready prompts from cards with context
10
10
  - **Agent Session Tracking** - track work progress with timer badges
11
+ - **Auto-Assignment** - automatically assign cards to you when starting agent sessions
11
12
  - **Multi-Agent Support** - works with Claude Code, Codex, Cursor, Windsurf
12
13
  - **One-Command Setup** - auto-configure all supported agents
13
14
  - **Natural Language Processing** via voice-nlu edge function
@@ -32,6 +33,9 @@ npm install -g harmony-mcp
32
33
 
33
34
  ```bash
34
35
  harmony-mcp configure --api-key hmy_your_key_here
36
+
37
+ # Optional: Set your email for auto-assignment
38
+ harmony-mcp configure --api-key hmy_your_key_here --user-email you@example.com
35
39
  ```
36
40
 
37
41
  ### 4. Initialize for Your AI Agents
@@ -86,23 +90,30 @@ When you start working on a card (e.g., `/hmy #42`):
86
90
  1. **Find** - Locates the card by short ID, UUID, or name
87
91
  2. **Move** - Moves the card to "In Progress" column
88
92
  3. **Label** - Adds the "agent" label to indicate AI is working
89
- 4. **Track** - Starts a session timer visible in the UI
90
- 5. **Implement** - Work on the task with progress updates
91
- 6. **Complete** - Move to "Review" when done
93
+ 4. **Assign** - Auto-assigns the card to you (if `userEmail` is configured)
94
+ 5. **Track** - Starts a session timer visible in the UI
95
+ 6. **Implement** - Work on the task with progress updates
96
+ 7. **Complete** - Move to "Review" when done
92
97
 
93
98
  ## CLI Commands
94
99
 
95
100
  ```bash
96
- harmony-mcp configure # Set up API key
97
- harmony-mcp init # Initialize for AI agents (interactive)
98
- harmony-mcp init --all # Configure all supported agents
99
- harmony-mcp init --detect # Auto-detect and configure installed agents
100
- harmony-mcp init --agent X Y # Configure specific agents
101
- harmony-mcp status # Show current config
102
- harmony-mcp reset # Clear configuration
103
- harmony-mcp set-workspace ID # Set active workspace
104
- harmony-mcp set-project ID # Set active project
105
- harmony-mcp serve # Start MCP server
101
+ harmony-mcp configure # Set up API key (interactive)
102
+ harmony-mcp configure -k KEY -e EMAIL # Set up with API key and email
103
+ harmony-mcp init # Initialize for AI agents (interactive)
104
+ harmony-mcp init --all # Configure all supported agents
105
+ harmony-mcp init --detect # Auto-detect and configure installed agents
106
+ harmony-mcp init --agent X Y # Configure specific agents
107
+ harmony-mcp init -w WS -p PROJ # Initialize with local workspace/project context
108
+ harmony-mcp status # Show current config (global and local)
109
+ harmony-mcp reset # Clear configuration
110
+ harmony-mcp set-workspace ID # Set active workspace (global)
111
+ harmony-mcp set-workspace ID -l # Set active workspace (local project)
112
+ harmony-mcp set-project ID # Set active project (global)
113
+ harmony-mcp set-project ID -l # Set active project (local project)
114
+ harmony-mcp set-user-email EMAIL # Set email for auto-assignment
115
+ harmony-mcp clear-user-email # Disable auto-assignment
116
+ harmony-mcp serve # Start MCP server
106
117
  ```
107
118
 
108
119
  ## Available Tools
@@ -233,17 +244,97 @@ curl -X GET "https://gethmy.com/api/workspaces" \
233
244
 
234
245
  ## Configuration
235
246
 
236
- Your configuration is stored in `~/.harmony-mcp/config.json`:
247
+ ### Global Configuration
248
+
249
+ Your global configuration is stored in `~/.harmony-mcp/config.json`:
237
250
 
238
251
  ```json
239
252
  {
240
253
  "apiKey": "hmy_...",
241
254
  "apiUrl": "https://gethmy.com/api",
242
255
  "activeWorkspaceId": null,
243
- "activeProjectId": null
256
+ "activeProjectId": null,
257
+ "userEmail": "you@example.com"
244
258
  }
245
259
  ```
246
260
 
261
+ ### Auto-Assignment
262
+
263
+ When `userEmail` is configured, cards are automatically assigned to you when you start an agent session (e.g., via `/hmy #42`). This helps track who is working on what.
264
+
265
+ To enable auto-assignment:
266
+ ```bash
267
+ harmony-mcp set-user-email you@example.com
268
+ ```
269
+
270
+ To disable auto-assignment:
271
+ ```bash
272
+ harmony-mcp clear-user-email
273
+ ```
274
+
275
+ The email must match your Harmony account email to work correctly.
276
+
277
+ ### Local Project Configuration
278
+
279
+ For multi-project workflows, you can set project-specific context using local configuration. This is stored in `.harmony-mcp.json` in your project root:
280
+
281
+ ```json
282
+ {
283
+ "workspaceId": "uuid-here",
284
+ "projectId": "uuid-here"
285
+ }
286
+ ```
287
+
288
+ **Priority**: Local config overrides global config for workspace and project context.
289
+
290
+ **Security**: API key is never stored locally (only in global config) to prevent accidental commits.
291
+
292
+ #### Setting Local Context
293
+
294
+ ```bash
295
+ # During initialization
296
+ harmony-mcp init --workspace <ws-id> --project <proj-id>
297
+
298
+ # Or separately
299
+ harmony-mcp set-workspace <id> --local
300
+ harmony-mcp set-project <id> --local
301
+ ```
302
+
303
+ #### Viewing Configuration
304
+
305
+ ```bash
306
+ harmony-mcp status
307
+ ```
308
+
309
+ Example output:
310
+
311
+ ```
312
+ Status: Configured
313
+ API Key: hmy_abc1...
314
+ API URL: https://gethmy.com/api
315
+ User Email: y***@example.com
316
+
317
+ Global Context:
318
+ Workspace: <global-ws-id>
319
+ Project: <global-proj-id>
320
+
321
+ Local Context (.harmony-mcp.json):
322
+ Workspace: <local-ws-id>
323
+ Project: <local-proj-id>
324
+
325
+ Active (effective):
326
+ Workspace: <local-ws-id> ← local
327
+ Project: <local-proj-id> ← local
328
+ ```
329
+
330
+ #### Recommended .gitignore
331
+
332
+ Add this to prevent accidentally committing local config:
333
+
334
+ ```
335
+ .harmony-mcp.json
336
+ ```
337
+
247
338
  ## Architecture
248
339
 
249
340
  ```
package/dist/cli.js CHANGED
@@ -25234,12 +25234,16 @@ import { homedir as homedir2 } from "node:os";
25234
25234
  import { join as join2 } from "node:path";
25235
25235
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
25236
25236
  var DEFAULT_API_URL = "https://gethmy.com/api";
25237
+ var LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
25237
25238
  function getConfigDir() {
25238
25239
  return join2(homedir2(), ".harmony-mcp");
25239
25240
  }
25240
25241
  function getConfigPath() {
25241
25242
  return join2(getConfigDir(), "config.json");
25242
25243
  }
25244
+ function getLocalConfigPath(cwd) {
25245
+ return join2(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
25246
+ }
25243
25247
  function loadConfig() {
25244
25248
  const configPath = getConfigPath();
25245
25249
  if (!existsSync2(configPath)) {
@@ -25247,7 +25251,8 @@ function loadConfig() {
25247
25251
  apiKey: null,
25248
25252
  apiUrl: DEFAULT_API_URL,
25249
25253
  activeWorkspaceId: null,
25250
- activeProjectId: null
25254
+ activeProjectId: null,
25255
+ userEmail: null
25251
25256
  };
25252
25257
  }
25253
25258
  try {
@@ -25257,14 +25262,16 @@ function loadConfig() {
25257
25262
  apiKey: config2.apiKey || null,
25258
25263
  apiUrl: config2.apiUrl || DEFAULT_API_URL,
25259
25264
  activeWorkspaceId: config2.activeWorkspaceId || null,
25260
- activeProjectId: config2.activeProjectId || null
25265
+ activeProjectId: config2.activeProjectId || null,
25266
+ userEmail: config2.userEmail || null
25261
25267
  };
25262
25268
  } catch {
25263
25269
  return {
25264
25270
  apiKey: null,
25265
25271
  apiUrl: DEFAULT_API_URL,
25266
25272
  activeWorkspaceId: null,
25267
- activeProjectId: null
25273
+ activeProjectId: null,
25274
+ userEmail: null
25268
25275
  };
25269
25276
  }
25270
25277
  }
@@ -25278,6 +25285,39 @@ function saveConfig(config2) {
25278
25285
  const newConfig = { ...existingConfig, ...config2 };
25279
25286
  writeFileSync2(configPath, JSON.stringify(newConfig, null, 2), { mode: 384 });
25280
25287
  }
25288
+ function loadLocalConfig(cwd) {
25289
+ const localConfigPath = getLocalConfigPath(cwd);
25290
+ if (!existsSync2(localConfigPath)) {
25291
+ return null;
25292
+ }
25293
+ try {
25294
+ const data = readFileSync2(localConfigPath, "utf-8");
25295
+ const config2 = JSON.parse(data);
25296
+ return {
25297
+ workspaceId: config2.workspaceId || null,
25298
+ projectId: config2.projectId || null
25299
+ };
25300
+ } catch {
25301
+ return null;
25302
+ }
25303
+ }
25304
+ function saveLocalConfig(config2, cwd) {
25305
+ const localConfigPath = getLocalConfigPath(cwd);
25306
+ const existingConfig = loadLocalConfig(cwd) || {
25307
+ workspaceId: null,
25308
+ projectId: null
25309
+ };
25310
+ const newConfig = { ...existingConfig, ...config2 };
25311
+ const cleanConfig = {};
25312
+ if (newConfig.workspaceId)
25313
+ cleanConfig.workspaceId = newConfig.workspaceId;
25314
+ if (newConfig.projectId)
25315
+ cleanConfig.projectId = newConfig.projectId;
25316
+ writeFileSync2(localConfigPath, JSON.stringify(cleanConfig, null, 2));
25317
+ }
25318
+ function hasLocalConfig(cwd) {
25319
+ return existsSync2(getLocalConfigPath(cwd));
25320
+ }
25281
25321
  function getApiKey() {
25282
25322
  const config2 = loadConfig();
25283
25323
  if (!config2.apiKey) {
@@ -25290,16 +25330,39 @@ function getApiUrl() {
25290
25330
  const config2 = loadConfig();
25291
25331
  return config2.apiUrl;
25292
25332
  }
25293
- function setActiveWorkspace(workspaceId) {
25294
- saveConfig({ activeWorkspaceId: workspaceId });
25333
+ function getUserEmail() {
25334
+ const config2 = loadConfig();
25335
+ return config2.userEmail;
25336
+ }
25337
+ function setUserEmail(email2) {
25338
+ saveConfig({ userEmail: email2 });
25295
25339
  }
25296
- function setActiveProject(projectId) {
25297
- saveConfig({ activeProjectId: projectId });
25340
+ function setActiveWorkspace(workspaceId, options) {
25341
+ if (options?.local) {
25342
+ saveLocalConfig({ workspaceId }, options.cwd);
25343
+ } else {
25344
+ saveConfig({ activeWorkspaceId: workspaceId });
25345
+ }
25346
+ }
25347
+ function setActiveProject(projectId, options) {
25348
+ if (options?.local) {
25349
+ saveLocalConfig({ projectId }, options.cwd);
25350
+ } else {
25351
+ saveConfig({ activeProjectId: projectId });
25352
+ }
25298
25353
  }
25299
- function getActiveWorkspaceId() {
25354
+ function getActiveWorkspaceId(cwd) {
25355
+ const localConfig = loadLocalConfig(cwd);
25356
+ if (localConfig?.workspaceId) {
25357
+ return localConfig.workspaceId;
25358
+ }
25300
25359
  return loadConfig().activeWorkspaceId;
25301
25360
  }
25302
- function getActiveProjectId() {
25361
+ function getActiveProjectId(cwd) {
25362
+ const localConfig = loadLocalConfig(cwd);
25363
+ if (localConfig?.projectId) {
25364
+ return localConfig.projectId;
25365
+ }
25303
25366
  return loadConfig().activeProjectId;
25304
25367
  }
25305
25368
  function isConfigured() {
@@ -26468,6 +26531,21 @@ Include: cards moved recently, current in-progress items, blocked or high-priori
26468
26531
  const cardId = exports_external.string().uuid().parse(args.cardId);
26469
26532
  const agentIdentifier = exports_external.string().min(1).parse(args.agentIdentifier);
26470
26533
  const agentName = exports_external.string().min(1).parse(args.agentName);
26534
+ let assignedTo = null;
26535
+ const userEmail = getUserEmail();
26536
+ if (userEmail) {
26537
+ try {
26538
+ const workspaceId = getActiveWorkspaceId();
26539
+ if (workspaceId) {
26540
+ const { members } = await client2.getWorkspaceMembers(workspaceId);
26541
+ const user = members.find((m) => m.email === userEmail);
26542
+ if (user) {
26543
+ await client2.updateCard(cardId, { assigneeId: user.id });
26544
+ assignedTo = user.email;
26545
+ }
26546
+ }
26547
+ } catch {}
26548
+ }
26471
26549
  const result = await client2.startAgentSession(cardId, {
26472
26550
  agentIdentifier,
26473
26551
  agentName,
@@ -26475,7 +26553,7 @@ Include: cards moved recently, current in-progress items, blocked or high-priori
26475
26553
  currentTask: args.currentTask,
26476
26554
  estimatedMinutesRemaining: args.estimatedMinutesRemaining
26477
26555
  });
26478
- return { success: true, ...result };
26556
+ return { success: true, assignedTo, ...result };
26479
26557
  }
26480
26558
  case "harmony_update_agent_progress": {
26481
26559
  const cardId = exports_external.string().uuid().parse(args.cardId);
@@ -26590,10 +26668,11 @@ program.command("serve").description("Start the MCP server (stdio transport)").a
26590
26668
  const server = new HarmonyMCPServer;
26591
26669
  await server.run();
26592
26670
  });
26593
- program.command("configure").description("Configure the MCP server with your Harmony API key").option("-k, --api-key <key>", "API key (generate at gethmy.com → Settings → API Keys)").option("-u, --api-url <url>", "API URL (optional, for self-hosted instances)").action(async (options) => {
26671
+ program.command("configure").description("Configure the MCP server with your Harmony API key").option("-k, --api-key <key>", "API key (generate at gethmy.com → Settings → API Keys)").option("-u, --api-url <url>", "API URL (optional, for self-hosted instances)").option("-e, --user-email <email>", "Your email for auto-assignment when starting agent sessions").action(async (options) => {
26594
26672
  try {
26595
26673
  let apiKey = options.apiKey;
26596
26674
  let apiUrl = options.apiUrl;
26675
+ let userEmail = options.userEmail;
26597
26676
  if (!apiKey) {
26598
26677
  console.log(`Configure Harmony MCP Server
26599
26678
  `);
@@ -26610,10 +26689,16 @@ program.command("configure").description("Configure the MCP server with your Har
26610
26689
  if (apiUrl) {
26611
26690
  config2.apiUrl = apiUrl;
26612
26691
  }
26692
+ if (userEmail) {
26693
+ config2.userEmail = userEmail;
26694
+ }
26613
26695
  saveConfig(config2);
26614
26696
  console.log(`
26615
26697
  Configuration saved successfully!`);
26616
26698
  console.log(`Config file: ${getConfigPath()}`);
26699
+ if (userEmail) {
26700
+ console.log(`User email: ${userEmail}`);
26701
+ }
26617
26702
  console.log(`
26618
26703
  You can now use the MCP server with Claude Code.`);
26619
26704
  console.log(`Add this to your ~/.claude/settings.json:
@@ -26634,19 +26719,43 @@ You can now use the MCP server with Claude Code.`);
26634
26719
  }
26635
26720
  });
26636
26721
  program.command("status").description("Show configuration status").action(() => {
26637
- const config2 = loadConfig();
26722
+ const globalConfig2 = loadConfig();
26723
+ const localConfig = loadLocalConfig();
26724
+ const hasLocal = hasLocalConfig();
26725
+ const maskEmail = (email2) => {
26726
+ const [local, domain] = email2.split("@");
26727
+ if (!domain)
26728
+ return email2;
26729
+ return `${local[0]}***@${domain}`;
26730
+ };
26638
26731
  if (isConfigured()) {
26639
26732
  console.log("Status: Configured");
26640
- console.log(`API Key: ${config2.apiKey?.slice(0, 8)}...`);
26641
- console.log(`API URL: ${config2.apiUrl}`);
26642
- if (config2.activeWorkspaceId) {
26643
- console.log(`Active Workspace: ${config2.activeWorkspaceId}`);
26644
- }
26645
- if (config2.activeProjectId) {
26646
- console.log(`Active Project: ${config2.activeProjectId}`);
26733
+ console.log(`API Key: ${globalConfig2.apiKey?.slice(0, 8)}...`);
26734
+ console.log(`API URL: ${globalConfig2.apiUrl}`);
26735
+ console.log(`User Email: ${globalConfig2.userEmail ? maskEmail(globalConfig2.userEmail) : "(not set)"}`);
26736
+ console.log(`
26737
+ Global Context:`);
26738
+ console.log(` Workspace: ${globalConfig2.activeWorkspaceId || "(not set)"}`);
26739
+ console.log(` Project: ${globalConfig2.activeProjectId || "(not set)"}`);
26740
+ if (hasLocal) {
26741
+ console.log(`
26742
+ Local Context (${getLocalConfigPath()}):`);
26743
+ console.log(` Workspace: ${localConfig?.workspaceId || "(not set)"}`);
26744
+ console.log(` Project: ${localConfig?.projectId || "(not set)"}`);
26647
26745
  }
26746
+ const effectiveWorkspace = getActiveWorkspaceId();
26747
+ const effectiveProject = getActiveProjectId();
26748
+ console.log(`
26749
+ Active (effective):`);
26750
+ const wsSource = localConfig?.workspaceId ? "local" : globalConfig2.activeWorkspaceId ? "global" : "";
26751
+ const projSource = localConfig?.projectId ? "local" : globalConfig2.activeProjectId ? "global" : "";
26752
+ console.log(` Workspace: ${effectiveWorkspace || "(not set)"}${wsSource ? ` ← ${wsSource}` : ""}`);
26753
+ console.log(` Project: ${effectiveProject || "(not set)"}${projSource ? ` ← ${projSource}` : ""}`);
26648
26754
  console.log(`
26649
- Config file: ${getConfigPath()}`);
26755
+ Global config: ${getConfigPath()}`);
26756
+ if (hasLocal) {
26757
+ console.log(`Local config: ${getLocalConfigPath()}`);
26758
+ }
26650
26759
  } else {
26651
26760
  console.log("Status: Not configured");
26652
26761
  console.log(`
@@ -26658,19 +26767,48 @@ program.command("reset").description("Remove stored configuration").action(() =>
26658
26767
  saveConfig({
26659
26768
  apiKey: null,
26660
26769
  activeWorkspaceId: null,
26661
- activeProjectId: null
26770
+ activeProjectId: null,
26771
+ userEmail: null
26662
26772
  });
26663
26773
  console.log("Configuration reset successfully");
26664
26774
  });
26665
- program.command("set-workspace <workspaceId>").description("Set the active workspace context").action((workspaceId) => {
26666
- setActiveWorkspace(workspaceId);
26667
- console.log(`Active workspace set to: ${workspaceId}`);
26775
+ program.command("set-workspace <workspaceId>").description("Set the active workspace context").option("-l, --local", "Save to local project config (.harmony-mcp.json) instead of global").action((workspaceId, options) => {
26776
+ const isLocal = options.local;
26777
+ setActiveWorkspace(workspaceId, { local: isLocal });
26778
+ if (isLocal) {
26779
+ console.log(`Local workspace set to: ${workspaceId}`);
26780
+ console.log(`Config file: ${getLocalConfigPath()}`);
26781
+ } else {
26782
+ console.log(`Global workspace set to: ${workspaceId}`);
26783
+ }
26668
26784
  });
26669
- program.command("set-project <projectId>").description("Set the active project context").action((projectId) => {
26670
- setActiveProject(projectId);
26671
- console.log(`Active project set to: ${projectId}`);
26785
+ program.command("set-project <projectId>").description("Set the active project context").option("-l, --local", "Save to local project config (.harmony-mcp.json) instead of global").action((projectId, options) => {
26786
+ const isLocal = options.local;
26787
+ setActiveProject(projectId, { local: isLocal });
26788
+ if (isLocal) {
26789
+ console.log(`Local project set to: ${projectId}`);
26790
+ console.log(`Config file: ${getLocalConfigPath()}`);
26791
+ } else {
26792
+ console.log(`Global project set to: ${projectId}`);
26793
+ }
26794
+ });
26795
+ program.command("set-user-email <email>").description("Set your email for auto-assignment when starting agent sessions").action((email2) => {
26796
+ if (!email2.includes("@") || !email2.includes(".")) {
26797
+ console.error("Error: Invalid email format");
26798
+ process.exit(1);
26799
+ }
26800
+ setUserEmail(email2);
26801
+ console.log(`User email set to: ${email2}`);
26802
+ console.log(`
26803
+ Cards will be automatically assigned to you when starting agent sessions.`);
26672
26804
  });
26673
- program.command("init").description("Initialize Harmony MCP for AI coding agents (Claude Code, Codex, Cursor, Windsurf)").option("-a, --agent <agents...>", `Agent(s) to configure: ${SUPPORTED_AGENTS.join(", ")}`).option("--all", "Configure all supported agents").option("--detect", "Auto-detect installed agents and configure them").option("-f, --force", "Overwrite existing configuration files").option("-d, --directory <path>", "Project directory (default: current directory)").action(async (options) => {
26805
+ program.command("clear-user-email").description("Clear the configured user email (disables auto-assignment)").action(() => {
26806
+ setUserEmail(null);
26807
+ console.log("User email cleared");
26808
+ console.log(`
26809
+ Auto-assignment is now disabled.`);
26810
+ });
26811
+ program.command("init").description("Initialize Harmony MCP for AI coding agents (Claude Code, Codex, Cursor, Windsurf)").option("-a, --agent <agents...>", `Agent(s) to configure: ${SUPPORTED_AGENTS.join(", ")}`).option("--all", "Configure all supported agents").option("--detect", "Auto-detect installed agents and configure them").option("-f, --force", "Overwrite existing configuration files").option("-d, --directory <path>", "Project directory (default: current directory)").option("-w, --workspace <id>", "Set local workspace context for this project").option("-p, --project <id>", "Set local project context for this project").action(async (options) => {
26674
26812
  console.log(`Harmony MCP Initialization
26675
26813
  `);
26676
26814
  let agents = [];
@@ -26768,6 +26906,19 @@ ${result.agent.toUpperCase()}:`);
26768
26906
  console.log(" No changes made");
26769
26907
  }
26770
26908
  }
26909
+ if (options.workspace || options.project) {
26910
+ console.log(`
26911
+ LOCAL CONTEXT:`);
26912
+ if (options.workspace) {
26913
+ saveLocalConfig({ workspaceId: options.workspace }, cwd);
26914
+ console.log(` ✓ Workspace: ${options.workspace}`);
26915
+ }
26916
+ if (options.project) {
26917
+ saveLocalConfig({ projectId: options.project }, cwd);
26918
+ console.log(` ✓ Project: ${options.project}`);
26919
+ }
26920
+ console.log(` Config file: ${getLocalConfigPath(cwd)}`);
26921
+ }
26771
26922
  console.log(`
26772
26923
  ` + "─".repeat(60));
26773
26924
  if (!isConfigured()) {
package/dist/index.js CHANGED
@@ -22996,12 +22996,16 @@ import { homedir } from "node:os";
22996
22996
  import { join } from "node:path";
22997
22997
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
22998
22998
  var DEFAULT_API_URL = "https://gethmy.com/api";
22999
+ var LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
22999
23000
  function getConfigDir() {
23000
23001
  return join(homedir(), ".harmony-mcp");
23001
23002
  }
23002
23003
  function getConfigPath() {
23003
23004
  return join(getConfigDir(), "config.json");
23004
23005
  }
23006
+ function getLocalConfigPath(cwd) {
23007
+ return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
23008
+ }
23005
23009
  function loadConfig() {
23006
23010
  const configPath = getConfigPath();
23007
23011
  if (!existsSync(configPath)) {
@@ -23009,7 +23013,8 @@ function loadConfig() {
23009
23013
  apiKey: null,
23010
23014
  apiUrl: DEFAULT_API_URL,
23011
23015
  activeWorkspaceId: null,
23012
- activeProjectId: null
23016
+ activeProjectId: null,
23017
+ userEmail: null
23013
23018
  };
23014
23019
  }
23015
23020
  try {
@@ -23019,14 +23024,16 @@ function loadConfig() {
23019
23024
  apiKey: config2.apiKey || null,
23020
23025
  apiUrl: config2.apiUrl || DEFAULT_API_URL,
23021
23026
  activeWorkspaceId: config2.activeWorkspaceId || null,
23022
- activeProjectId: config2.activeProjectId || null
23027
+ activeProjectId: config2.activeProjectId || null,
23028
+ userEmail: config2.userEmail || null
23023
23029
  };
23024
23030
  } catch {
23025
23031
  return {
23026
23032
  apiKey: null,
23027
23033
  apiUrl: DEFAULT_API_URL,
23028
23034
  activeWorkspaceId: null,
23029
- activeProjectId: null
23035
+ activeProjectId: null,
23036
+ userEmail: null
23030
23037
  };
23031
23038
  }
23032
23039
  }
@@ -23040,6 +23047,39 @@ function saveConfig(config2) {
23040
23047
  const newConfig = { ...existingConfig, ...config2 };
23041
23048
  writeFileSync(configPath, JSON.stringify(newConfig, null, 2), { mode: 384 });
23042
23049
  }
23050
+ function loadLocalConfig(cwd) {
23051
+ const localConfigPath = getLocalConfigPath(cwd);
23052
+ if (!existsSync(localConfigPath)) {
23053
+ return null;
23054
+ }
23055
+ try {
23056
+ const data = readFileSync(localConfigPath, "utf-8");
23057
+ const config2 = JSON.parse(data);
23058
+ return {
23059
+ workspaceId: config2.workspaceId || null,
23060
+ projectId: config2.projectId || null
23061
+ };
23062
+ } catch {
23063
+ return null;
23064
+ }
23065
+ }
23066
+ function saveLocalConfig(config2, cwd) {
23067
+ const localConfigPath = getLocalConfigPath(cwd);
23068
+ const existingConfig = loadLocalConfig(cwd) || {
23069
+ workspaceId: null,
23070
+ projectId: null
23071
+ };
23072
+ const newConfig = { ...existingConfig, ...config2 };
23073
+ const cleanConfig = {};
23074
+ if (newConfig.workspaceId)
23075
+ cleanConfig.workspaceId = newConfig.workspaceId;
23076
+ if (newConfig.projectId)
23077
+ cleanConfig.projectId = newConfig.projectId;
23078
+ writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
23079
+ }
23080
+ function hasLocalConfig(cwd) {
23081
+ return existsSync(getLocalConfigPath(cwd));
23082
+ }
23043
23083
  function getApiKey() {
23044
23084
  const config2 = loadConfig();
23045
23085
  if (!config2.apiKey) {
@@ -23052,16 +23092,39 @@ function getApiUrl() {
23052
23092
  const config2 = loadConfig();
23053
23093
  return config2.apiUrl;
23054
23094
  }
23055
- function setActiveWorkspace(workspaceId) {
23056
- saveConfig({ activeWorkspaceId: workspaceId });
23095
+ function getUserEmail() {
23096
+ const config2 = loadConfig();
23097
+ return config2.userEmail;
23098
+ }
23099
+ function setUserEmail(email2) {
23100
+ saveConfig({ userEmail: email2 });
23057
23101
  }
23058
- function setActiveProject(projectId) {
23059
- saveConfig({ activeProjectId: projectId });
23102
+ function setActiveWorkspace(workspaceId, options) {
23103
+ if (options?.local) {
23104
+ saveLocalConfig({ workspaceId }, options.cwd);
23105
+ } else {
23106
+ saveConfig({ activeWorkspaceId: workspaceId });
23107
+ }
23060
23108
  }
23061
- function getActiveWorkspaceId() {
23109
+ function setActiveProject(projectId, options) {
23110
+ if (options?.local) {
23111
+ saveLocalConfig({ projectId }, options.cwd);
23112
+ } else {
23113
+ saveConfig({ activeProjectId: projectId });
23114
+ }
23115
+ }
23116
+ function getActiveWorkspaceId(cwd) {
23117
+ const localConfig = loadLocalConfig(cwd);
23118
+ if (localConfig?.workspaceId) {
23119
+ return localConfig.workspaceId;
23120
+ }
23062
23121
  return loadConfig().activeWorkspaceId;
23063
23122
  }
23064
- function getActiveProjectId() {
23123
+ function getActiveProjectId(cwd) {
23124
+ const localConfig = loadLocalConfig(cwd);
23125
+ if (localConfig?.projectId) {
23126
+ return localConfig.projectId;
23127
+ }
23065
23128
  return loadConfig().activeProjectId;
23066
23129
  }
23067
23130
  function isConfigured() {
@@ -24230,6 +24293,21 @@ Include: cards moved recently, current in-progress items, blocked or high-priori
24230
24293
  const cardId = exports_external.string().uuid().parse(args.cardId);
24231
24294
  const agentIdentifier = exports_external.string().min(1).parse(args.agentIdentifier);
24232
24295
  const agentName = exports_external.string().min(1).parse(args.agentName);
24296
+ let assignedTo = null;
24297
+ const userEmail = getUserEmail();
24298
+ if (userEmail) {
24299
+ try {
24300
+ const workspaceId = getActiveWorkspaceId();
24301
+ if (workspaceId) {
24302
+ const { members } = await client2.getWorkspaceMembers(workspaceId);
24303
+ const user = members.find((m) => m.email === userEmail);
24304
+ if (user) {
24305
+ await client2.updateCard(cardId, { assigneeId: user.id });
24306
+ assignedTo = user.email;
24307
+ }
24308
+ }
24309
+ } catch {}
24310
+ }
24233
24311
  const result = await client2.startAgentSession(cardId, {
24234
24312
  agentIdentifier,
24235
24313
  agentName,
@@ -24237,7 +24315,7 @@ Include: cards moved recently, current in-progress items, blocked or high-priori
24237
24315
  currentTask: args.currentTask,
24238
24316
  estimatedMinutesRemaining: args.estimatedMinutesRemaining
24239
24317
  });
24240
- return { success: true, ...result };
24318
+ return { success: true, assignedTo, ...result };
24241
24319
  }
24242
24320
  case "harmony_update_agent_progress": {
24243
24321
  const cardId = exports_external.string().uuid().parse(args.cardId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harmony-mcp",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "MCP server for Harmony Kanban board - enables AI coding agents to manage your boards",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",