@agilemonster/mcp-server 1.0.7 → 1.0.8

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/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { AgileMonsterProjectConfig, Board, BoardAgentConfig, Card, ContextResponse, PulseResponse } from './types.js';
2
- export declare const MCP_SERVER_VERSION = "1.0.7";
1
+ import { AgileMonsterProjectConfig, Board, BoardAgentConfig, Card, ContextResponse, DiscoveredProject, PulseResponse } from './types.js';
2
+ export declare const MCP_SERVER_VERSION = "1.0.8";
3
3
  export declare class AgileMonsterClient {
4
4
  private apiKey;
5
5
  private baseUrl;
@@ -35,7 +35,13 @@ export declare class AgileMonsterClient {
35
35
  provider?: string;
36
36
  projectName?: string;
37
37
  projectPath?: string;
38
+ discoveredProjects?: DiscoveredProject[];
38
39
  }): Promise<PulseResponse>;
40
+ /**
41
+ * Scan the developer's local workspace roots (sibling folders in parent dev dir, ~/dev, ~/Projects, etc.)
42
+ * to discover real project folders so the web UI can auto-match and let the user pick from a dropdown.
43
+ */
44
+ discoverLocalProjects(): DiscoveredProject[];
39
45
  listAssigned(provider?: string, status?: string, boardId?: string, workspaceId?: string): Promise<{
40
46
  provider: string;
41
47
  cards: Card[];
package/dist/client.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- export const MCP_SERVER_VERSION = '1.0.7';
3
+ import { execSync } from 'node:child_process';
4
+ export const MCP_SERVER_VERSION = '1.0.8';
4
5
  export class AgileMonsterClient {
5
6
  apiKey = '';
6
7
  baseUrl;
@@ -194,9 +195,98 @@ export class AgileMonsterClient {
194
195
  project_name: options.projectName,
195
196
  project_path: options.projectPath,
196
197
  mcp_version: MCP_SERVER_VERSION,
198
+ discovered_projects: options.discoveredProjects,
197
199
  }),
198
200
  });
199
201
  }
202
+ /**
203
+ * Scan the developer's local workspace roots (sibling folders in parent dev dir, ~/dev, ~/Projects, etc.)
204
+ * to discover real project folders so the web UI can auto-match and let the user pick from a dropdown.
205
+ */
206
+ discoverLocalProjects() {
207
+ const roots = new Set();
208
+ const parentDir = path.dirname(this.cwd);
209
+ if (parentDir && parentDir !== '/' && parentDir !== this.cwd) {
210
+ roots.add(parentDir);
211
+ }
212
+ const homedir = process.env.HOME || process.env.USERPROFILE || '';
213
+ if (homedir) {
214
+ for (const sub of ['dev', 'Projects', 'Code', 'repos', 'Developer']) {
215
+ const candidate = path.join(homedir, sub);
216
+ if (fs.existsSync(candidate)) {
217
+ roots.add(candidate);
218
+ }
219
+ }
220
+ }
221
+ const projectMarkers = [
222
+ '.git',
223
+ '.agilemonster.json',
224
+ 'package.json',
225
+ 'pubspec.yaml',
226
+ 'composer.json',
227
+ 'pyproject.toml',
228
+ 'requirements.txt',
229
+ 'Cargo.toml',
230
+ 'go.mod',
231
+ 'Podfile',
232
+ 'CMakeLists.txt',
233
+ 'frontend-flutter',
234
+ 'frontend-nextjs',
235
+ 'backend-php',
236
+ 'src',
237
+ 'lib',
238
+ 'app',
239
+ ];
240
+ const ignoreNames = /^(untitled folder.*|node_modules|vendor|dist|build|archive.*|.*backup.*|\..*)$/i;
241
+ const found = new Map();
242
+ for (const root of roots) {
243
+ let entries = [];
244
+ try {
245
+ entries = fs.readdirSync(root, { withFileTypes: true });
246
+ }
247
+ catch {
248
+ continue;
249
+ }
250
+ for (const entry of entries) {
251
+ if (!entry.isDirectory())
252
+ continue;
253
+ const name = entry.name.trim();
254
+ if (!name || ignoreNames.test(name))
255
+ continue;
256
+ if (/\s\d+$/.test(name))
257
+ continue; // skip "Folder 2", "Folder 3" copies
258
+ const fullPath = path.join(root, name);
259
+ let isProject = false;
260
+ for (const marker of projectMarkers) {
261
+ if (fs.existsSync(path.join(fullPath, marker))) {
262
+ isProject = true;
263
+ break;
264
+ }
265
+ }
266
+ if (!isProject)
267
+ continue;
268
+ let gitRemote = null;
269
+ if (fs.existsSync(path.join(fullPath, '.git'))) {
270
+ try {
271
+ gitRemote =
272
+ execSync('git config --get remote.origin.url', {
273
+ cwd: fullPath,
274
+ stdio: ['ignore', 'pipe', 'ignore'],
275
+ })
276
+ .toString()
277
+ .trim() || null;
278
+ }
279
+ catch { }
280
+ }
281
+ found.set(fullPath, {
282
+ name,
283
+ path: fullPath,
284
+ git_remote: gitRemote,
285
+ });
286
+ }
287
+ }
288
+ return Array.from(found.values()).sort((a, b) => a.name.localeCompare(b.name));
289
+ }
200
290
  async listAssigned(provider = 'any', status, boardId, workspaceId) {
201
291
  const params = new URLSearchParams();
202
292
  if (provider)
package/dist/server.js CHANGED
@@ -789,18 +789,82 @@ function triggerBackgroundSelfUpdate() {
789
789
  export function startBuiltInPullPulse(server, client, intervalMs = 20000) {
790
790
  const notifiedCardIds = new Set();
791
791
  let updateTriggered = false;
792
+ let pulseCount = 0;
793
+ const autoProvisionSavedBindings = (apiKey, bindings) => {
794
+ for (const b of bindings) {
795
+ const projPath = (b.project_path || '').trim();
796
+ if (!projPath || !fs.existsSync(projPath))
797
+ continue;
798
+ const amConfigPath = path.join(projPath, '.agilemonster.json');
799
+ let needsWrite = true;
800
+ if (fs.existsSync(amConfigPath)) {
801
+ try {
802
+ const existing = JSON.parse(fs.readFileSync(amConfigPath, 'utf8'));
803
+ if (existing?.board_id === b.board_id) {
804
+ needsWrite = false;
805
+ }
806
+ }
807
+ catch { }
808
+ }
809
+ if (needsWrite) {
810
+ try {
811
+ fs.writeFileSync(amConfigPath, JSON.stringify({
812
+ workspace_id: b.workspace_id,
813
+ board_id: b.board_id,
814
+ board_name: b.board_name,
815
+ project_name: b.project_name || path.basename(projPath),
816
+ project_path: projPath,
817
+ provider: b.provider || 'cursor',
818
+ review_list: 'In Review',
819
+ done_list: 'Done',
820
+ api_key: apiKey,
821
+ }, null, 2), 'utf8');
822
+ // Also ensure .cursor/mcp.json and .mcp.json exist in that bound project folder
823
+ const mcpPayload = JSON.stringify({
824
+ mcpServers: {
825
+ agilemonster: {
826
+ command: 'npx',
827
+ args: ['-y', '@agilemonster/mcp-server@latest'],
828
+ env: {
829
+ AGILEMONSTER_API_KEY: apiKey,
830
+ AGILEMONSTER_API_URL: 'https://api.agilemonster.com',
831
+ },
832
+ },
833
+ },
834
+ }, null, 2);
835
+ const cursorDir = path.join(projPath, '.cursor');
836
+ if (!fs.existsSync(cursorDir))
837
+ fs.mkdirSync(cursorDir, { recursive: true });
838
+ const cursorMcp = path.join(cursorDir, 'mcp.json');
839
+ if (!fs.existsSync(cursorMcp))
840
+ fs.writeFileSync(cursorMcp, mcpPayload, 'utf8');
841
+ const claudeMcp = path.join(projPath, '.mcp.json');
842
+ if (!fs.existsSync(claudeMcp))
843
+ fs.writeFileSync(claudeMcp, mcpPayload, 'utf8');
844
+ }
845
+ catch { }
846
+ }
847
+ }
848
+ };
792
849
  const runPulse = async () => {
793
850
  try {
794
851
  const config = client.loadProjectConfig();
795
- if (!config?.board_id || !client.getApiKey()) {
852
+ const apiKey = client.getApiKey();
853
+ if (!config?.board_id || !apiKey) {
796
854
  return;
797
855
  }
856
+ const includeDiscovery = pulseCount === 0 || pulseCount % 15 === 0;
857
+ pulseCount++;
798
858
  const res = await client.pulse({
799
859
  boardId: config.board_id,
800
860
  provider: 'any',
801
861
  projectName: config.project_name || path.basename(process.cwd()),
802
862
  projectPath: config.project_path || process.cwd(),
863
+ discoveredProjects: includeDiscovery ? client.discoverLocalProjects() : undefined,
803
864
  });
865
+ if (res.saved_bindings && res.saved_bindings.length > 0) {
866
+ autoProvisionSavedBindings(apiKey, res.saved_bindings);
867
+ }
804
868
  const queued = res.queued_cards || [];
805
869
  syncIdeTaskRules(config.board_name || 'Board', queued);
806
870
  for (const card of queued) {
package/dist/types.d.ts CHANGED
@@ -75,12 +75,25 @@ export interface PulseVersionInfo {
75
75
  force_update_requested: boolean;
76
76
  update_command: string;
77
77
  }
78
+ export interface DiscoveredProject {
79
+ name: string;
80
+ path: string;
81
+ git_remote?: string | null;
82
+ }
78
83
  export interface PulseResponse {
79
84
  board_id: string;
80
85
  pulsed_at: string;
81
86
  queued_cards: Card[];
82
87
  in_progress_cards: Card[];
83
88
  version: PulseVersionInfo;
89
+ saved_bindings?: Array<{
90
+ board_id: string;
91
+ board_name: string;
92
+ workspace_id: string;
93
+ provider: string;
94
+ project_name: string;
95
+ project_path?: string | null;
96
+ }>;
84
97
  }
85
98
  export interface ContextResponse {
86
99
  matched_board: Board | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agilemonster/mcp-server",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "type": "module",
5
5
  "description": "Official Model Context Protocol (MCP) server & Orchestrator for Agile Monster (Cursor, Claude Code, Antigravity, Codex, Hermes, Windsurf)",
6
6
  "main": "dist/server.js",