@agilemonster/mcp-server 1.0.4 → 1.0.6

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.
@@ -26,8 +26,19 @@ async function main() {
26
26
  case 'bootstrap':
27
27
  case 'setup':
28
28
  case 'init': {
29
- const ideArg = args.find((a) => a.startsWith('--ide='))?.split('=')[1] || args[1];
30
- await orchestrator.initInteractive({ ide: ideArg });
29
+ const ideArg = args.find((a) => a.startsWith('--ide='))?.split('=').slice(1).join('=');
30
+ const boardArg = args
31
+ .find((a) => a.startsWith('--board='))
32
+ ?.split('=')
33
+ .slice(1)
34
+ .join('=')
35
+ ?.replace(/^["']|["']$/g, '');
36
+ const providerArg = args.find((a) => a.startsWith('--provider='))?.split('=').slice(1).join('=');
37
+ await orchestrator.initInteractive({
38
+ ide: ideArg,
39
+ board: boardArg,
40
+ provider: providerArg,
41
+ });
31
42
  break;
32
43
  }
33
44
 
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AgileMonsterProjectConfig, Card, ContextResponse } from './types.js';
1
+ import { AgileMonsterProjectConfig, Board, BoardAgentConfig, Card, ContextResponse } from './types.js';
2
2
  export declare class AgileMonsterClient {
3
3
  private apiKey;
4
4
  private baseUrl;
@@ -24,12 +24,16 @@ export declare class AgileMonsterClient {
24
24
  first_name?: string;
25
25
  last_name?: string;
26
26
  }>;
27
- getContext(projectName?: string, boardId?: string): Promise<ContextResponse>;
27
+ getContext(projectName?: string, boardId?: string, gitRemote?: string, detectedIdes?: string[]): Promise<ContextResponse>;
28
+ createBoard(workspaceId: string, name: string, description?: string): Promise<Board>;
29
+ saveBoardAgents(boardId: string, agents: BoardAgentConfig[], projectName?: string, projectPath?: string, gitRemote?: string): Promise<{
30
+ agents: BoardAgentConfig[];
31
+ }>;
28
32
  listAssigned(provider?: string, status?: string, boardId?: string, workspaceId?: string): Promise<{
29
33
  provider: string;
30
34
  cards: Card[];
31
35
  }>;
32
- claimTask(cardId: string, provider: string, runRef?: string): Promise<Card>;
36
+ claimTask(cardId: string, provider: string, runRef?: string, expectedBoardId?: string): Promise<Card>;
33
37
  askQuestion(cardId: string, provider: string, question: string): Promise<Card>;
34
38
  addProgress(cardId: string, provider: string, update: string, tokensUsed?: number, costUsd?: number): Promise<Card>;
35
39
  recordSpend(cardId: string, provider: string, tokens: number, costUsd: number, note?: string): Promise<Card>;
package/dist/client.js CHANGED
@@ -145,15 +145,43 @@ export class AgileMonsterClient {
145
145
  async me() {
146
146
  return this.request('/api/agent/me');
147
147
  }
148
- async getContext(projectName, boardId) {
148
+ async getContext(projectName, boardId, gitRemote, detectedIdes) {
149
149
  const params = new URLSearchParams();
150
150
  if (projectName)
151
151
  params.set('project_name', projectName);
152
152
  if (boardId)
153
153
  params.set('board_id', boardId);
154
+ if (gitRemote)
155
+ params.set('git_remote', gitRemote);
156
+ if (detectedIdes && detectedIdes.length > 0) {
157
+ params.set('detected_ides', detectedIdes.join(','));
158
+ }
154
159
  const qs = params.toString();
155
160
  return this.request(`/api/agent/context${qs ? `?${qs}` : ''}`);
156
161
  }
162
+ async createBoard(workspaceId, name, description) {
163
+ return this.request('/api/boards', {
164
+ method: 'POST',
165
+ body: JSON.stringify({
166
+ workspace_id: workspaceId,
167
+ name,
168
+ description,
169
+ default_lists: true,
170
+ }),
171
+ });
172
+ }
173
+ async saveBoardAgents(boardId, agents, projectName, projectPath, gitRemote) {
174
+ return this.request(`/api/boards/${boardId}/agents`, {
175
+ method: 'POST',
176
+ body: JSON.stringify({
177
+ agents,
178
+ project_name: projectName,
179
+ project_path: projectPath,
180
+ git_remote: gitRemote,
181
+ connected: true,
182
+ }),
183
+ });
184
+ }
157
185
  async listAssigned(provider = 'any', status, boardId, workspaceId) {
158
186
  const params = new URLSearchParams();
159
187
  if (provider)
@@ -167,10 +195,11 @@ export class AgileMonsterClient {
167
195
  const qs = params.toString();
168
196
  return this.request(`/api/agent/assigned${qs ? `?${qs}` : ''}`);
169
197
  }
170
- async claimTask(cardId, provider, runRef) {
198
+ async claimTask(cardId, provider, runRef, expectedBoardId) {
199
+ const boardId = expectedBoardId || this.loadProjectConfig()?.board_id;
171
200
  return this.request(`/api/agent/cards/${cardId}/claim`, {
172
201
  method: 'POST',
173
- body: JSON.stringify({ provider, run_ref: runRef }),
202
+ body: JSON.stringify({ provider, run_ref: runRef, board_id: boardId }),
174
203
  });
175
204
  }
176
205
  async askQuestion(cardId, provider, question) {
@@ -15,13 +15,17 @@ export declare class AgileMonsterOrchestrator {
15
15
  }): Promise<Card[]>;
16
16
  printDashboard(): Promise<void>;
17
17
  runDaemon(options?: OrchestratorOptions): Promise<void>;
18
+ private detectGitRemote;
19
+ private detectInstalledIdes;
18
20
  initInteractive(options?: {
19
21
  provider?: string;
20
22
  ide?: string;
23
+ board?: string;
21
24
  }): Promise<void>;
22
25
  bootstrapInteractive(options?: {
23
26
  provider?: string;
24
27
  ide?: string;
28
+ board?: string;
25
29
  }): Promise<void>;
26
30
  configureIdes(apiKey: string, selectedIde?: string): void;
27
31
  private ensureGitignore;
@@ -1,5 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import fs from 'node:fs';
3
+ import { execSync } from 'node:child_process';
3
4
  import { AgileMonsterClient } from './client.js';
4
5
  import { AGENT_GUIDELINES_MARKDOWN } from './guidelines.js';
5
6
  export class AgileMonsterOrchestrator {
@@ -11,6 +12,10 @@ export class AgileMonsterOrchestrator {
11
12
  const projectConfig = this.client.loadProjectConfig();
12
13
  const provider = options?.provider || projectConfig?.provider || 'any';
13
14
  const boardId = options?.boardId || projectConfig?.board_id;
15
+ // STRICT BOARD SCOPE GUARD: Never poll across all boards when current folder is unbound!
16
+ if (!boardId) {
17
+ return [];
18
+ }
14
19
  const result = await this.client.listAssigned(provider, undefined, boardId);
15
20
  return result.cards || [];
16
21
  }
@@ -33,8 +38,10 @@ export class AgileMonsterOrchestrator {
33
38
  console.log(`🤖 Default Provider: ${config.provider || 'any'}`);
34
39
  }
35
40
  else {
36
- console.log(`ℹ️ No .agilemonster.json found in current directory (${cwdName}).`);
37
- console.log(' Run "agilemonster-orchestrator init" to bind this repo to a board.\n');
41
+ console.log(`⚠️ [UNBOUND REPOSITORY] No .agilemonster.json found in "${cwdName}".`);
42
+ console.log(' To prevent agents from picking up tasks from the wrong board and burning tokens:');
43
+ console.log(' Run "npx -y @agilemonster/mcp-server bootstrap" in this folder first!\n');
44
+ return;
38
45
  }
39
46
  console.log('\n🔍 Checking for assigned agent tasks...');
40
47
  const tasks = await this.checkTasks();
@@ -99,17 +106,54 @@ export class AgileMonsterOrchestrator {
99
106
  await poll();
100
107
  setInterval(poll, interval);
101
108
  }
109
+ detectGitRemote() {
110
+ try {
111
+ const out = execSync('git config --get remote.origin.url', {
112
+ cwd: process.cwd(),
113
+ stdio: ['ignore', 'pipe', 'ignore'],
114
+ })
115
+ .toString()
116
+ .trim();
117
+ return out || undefined;
118
+ }
119
+ catch {
120
+ return undefined;
121
+ }
122
+ }
123
+ detectInstalledIdes() {
124
+ const homedir = process.env.HOME || process.env.USERPROFILE || '';
125
+ const detected = [];
126
+ if (fs.existsSync(path.resolve(process.cwd(), '.cursor')) ||
127
+ (homedir && fs.existsSync(path.join(homedir, '.cursor')))) {
128
+ detected.push('cursor');
129
+ }
130
+ if (fs.existsSync(path.resolve(process.cwd(), '.mcp.json')) ||
131
+ (homedir && fs.existsSync(path.join(homedir, '.claude')))) {
132
+ detected.push('claude_code');
133
+ }
134
+ if (homedir && fs.existsSync(path.join(homedir, '.gemini'))) {
135
+ detected.push('antigravity');
136
+ }
137
+ if (homedir && fs.existsSync(path.join(homedir, '.codeium', 'windsurf'))) {
138
+ detected.push('windsurf');
139
+ }
140
+ if (fs.existsSync(path.resolve(process.cwd(), '.vscode'))) {
141
+ detected.push('codex');
142
+ }
143
+ return detected.length > 0 ? detected : ['cursor', 'claude_code', 'antigravity'];
144
+ }
102
145
  async initInteractive(options) {
103
146
  console.log('\n======================================================');
104
147
  console.log(' 👾 AGILE MONSTER 1-CLICK AGENT SETUP ');
105
148
  console.log('======================================================\n');
149
+ const readline = await import('node:readline/promises');
150
+ const isInteractive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
106
151
  let apiKey = this.client.getApiKey();
107
152
  if (!apiKey) {
108
153
  console.log('🔑 No Agile Monster API key detected.');
109
154
  console.log(' 1. Open: https://app.agilemonster.com/profile?setup=agents');
110
155
  console.log(' 2. Click "Generate API key", then "Copy API key"');
111
156
  console.log(' 3. Paste it below\n');
112
- const readline = await import('node:readline/promises');
113
157
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
114
158
  try {
115
159
  const inputKey = await rl.question('👉 Paste your Personal API Key (am_live_...): ');
@@ -128,23 +172,148 @@ export class AgileMonsterOrchestrator {
128
172
  rl.close();
129
173
  }
130
174
  }
131
- console.log('\n🔍 Discovering Agile Monster boards for this repository...');
132
- const context = await this.client.getContext(path.basename(process.cwd()));
133
- if (!context.available_boards || context.available_boards.length === 0) {
134
- console.log('No boards found in your account. Please create a board first on https://app.agilemonster.com');
175
+ const folderName = path.basename(process.cwd());
176
+ const gitRemote = this.detectGitRemote();
177
+ const detectedIdes = this.detectInstalledIdes();
178
+ console.log(`\n🔍 Analyzing repository "${folderName}" with Agile Monster AI...`);
179
+ const context = await this.client.getContext(folderName, options?.board, gitRemote, detectedIdes);
180
+ const boards = context.available_boards || [];
181
+ const aiMatch = context.ai_match;
182
+ const suggestedNewBoardName = aiMatch?.suggested_new_board_name || folderName.replace(/\.(com|org|net)$/i, '');
183
+ let targetBoard = null;
184
+ if (options?.board) {
185
+ const needle = options.board.trim().toLowerCase();
186
+ targetBoard =
187
+ boards.find((b) => b.id.toLowerCase() === needle || b.name.trim().toLowerCase() === needle) || context.matched_board;
188
+ }
189
+ if (!targetBoard) {
190
+ if (aiMatch?.reason) {
191
+ console.log(`\n🤖 AI Guidance: ${aiMatch.reason}\n`);
192
+ }
193
+ if (isInteractive) {
194
+ console.log('📋 Which Agile Monster board should connect to this project folder?');
195
+ boards.forEach((b, idx) => {
196
+ const isAiMatched = aiMatch?.matched_board_id === b.id;
197
+ const badge = isAiMatched ? ` ✨ [AI Recommended Match]` : '';
198
+ console.log(` [${idx + 1}] ${b.name} (${b.workspace_name || 'Workspace'})${badge}`);
199
+ });
200
+ console.log(` [N] + Create new board "${suggestedNewBoardName}"`);
201
+ const defaultIdx = aiMatch?.matched_board_id
202
+ ? boards.findIndex((b) => b.id === aiMatch.matched_board_id) + 1
203
+ : 0;
204
+ const defaultChoice = defaultIdx > 0 ? String(defaultIdx) : 'N';
205
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
206
+ try {
207
+ const answer = (await rl.question(`\n👉 Select board [1-${boards.length} or N] (default: ${defaultChoice}): `))
208
+ .trim()
209
+ .toUpperCase();
210
+ const choice = answer || defaultChoice;
211
+ if (choice === 'N' || boards.length === 0) {
212
+ const customTitle = (await rl.question(`👉 New board name (Press Enter for "${suggestedNewBoardName}"): `)).trim();
213
+ const finalBoardName = customTitle || suggestedNewBoardName;
214
+ const workspaceId = boards[0]?.workspace_id || context.workspaces?.[0]?.id || '';
215
+ if (!workspaceId) {
216
+ console.error('❌ No workspace found in your account. Please create a workspace on https://app.agilemonster.com first.');
217
+ return;
218
+ }
219
+ console.log(`✨ Creating board "${finalBoardName}" with default Kanban columns...`);
220
+ targetBoard = await this.client.createBoard(workspaceId, finalBoardName, `Connected to repository ${folderName}`);
221
+ console.log(`✅ Created board "${targetBoard.name}" (${targetBoard.id})`);
222
+ }
223
+ else {
224
+ const num = parseInt(choice, 10);
225
+ if (!Number.isNaN(num) && num >= 1 && num <= boards.length) {
226
+ const candidate = boards[num - 1];
227
+ // Safety confirmation if user picks an existing board that AI said didn't match this folder
228
+ if (aiMatch &&
229
+ aiMatch.matched_board_id !== candidate.id &&
230
+ candidate.name.toLowerCase() !== folderName.toLowerCase()) {
231
+ const confirm = (await rl.question(`⚠️ Safety Check: Folder is "${folderName}" but board is "${candidate.name}". Are you sure they are the same project? [y/N]: `))
232
+ .trim()
233
+ .toLowerCase();
234
+ if (confirm !== 'y' && confirm !== 'yes') {
235
+ console.log('❌ Cancelled to prevent connecting the wrong board.');
236
+ return;
237
+ }
238
+ }
239
+ targetBoard = candidate;
240
+ }
241
+ else {
242
+ console.error(`❌ Invalid selection "${choice}". Aborting so we never bind the wrong board.`);
243
+ return;
244
+ }
245
+ }
246
+ }
247
+ finally {
248
+ rl.close();
249
+ }
250
+ }
251
+ else {
252
+ // Non-interactive mode: ONLY auto-bind if AI matched with high confidence OR --board was explicitly passed.
253
+ // NEVER auto-pick a board or create a board silently if ambiguous!
254
+ if (context.matched_board && aiMatch?.confidence === 'high') {
255
+ targetBoard = context.matched_board;
256
+ }
257
+ else {
258
+ console.error(`❌ Ambiguous board match for folder "${folderName}" in non-interactive mode.`);
259
+ console.error(` Please run interactively in your terminal or pass --board="<Board Name>" explicitly so we never connect the wrong board.`);
260
+ return;
261
+ }
262
+ }
263
+ }
264
+ if (!targetBoard) {
265
+ console.log('No board selected. Run "npx -y @agilemonster/mcp-server bootstrap" again to connect a board.');
135
266
  return;
136
267
  }
137
- const targetBoard = context.matched_board || context.available_boards[0];
268
+ // Prompt for optional short custom agent tag on this board
269
+ let shortTag = '';
270
+ if (isInteractive) {
271
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
272
+ try {
273
+ console.log(`\n🏷️ On board "${targetBoard.name}", your agents will appear as:`);
274
+ console.log(` "Cursor: ${targetBoard.name}", "Claude Code: ${targetBoard.name}", "Antigravity: ${targetBoard.name}"`);
275
+ const tagInput = (await rl.question(`👉 Optional: Enter a shorter project tag (e.g. "PG" -> "Cursor-PG"), or press Enter to keep "${targetBoard.name}": `)).trim();
276
+ shortTag = tagInput;
277
+ }
278
+ finally {
279
+ rl.close();
280
+ }
281
+ }
282
+ const providerTitles = {
283
+ cursor: 'Cursor',
284
+ claude_code: 'Claude Code',
285
+ antigravity: 'Antigravity',
286
+ windsurf: 'Windsurf',
287
+ codex: 'Codex',
288
+ };
289
+ try {
290
+ const agentEntries = Object.entries(providerTitles).map(([prov, label]) => ({
291
+ provider: prov,
292
+ custom_name: `${label}: ${targetBoard.name}`,
293
+ short_name: shortTag ? `${label}-${shortTag}` : null,
294
+ project_name: folderName,
295
+ project_path: process.cwd(),
296
+ git_remote: gitRemote || null,
297
+ connected: true,
298
+ }));
299
+ await this.client.saveBoardAgents(targetBoard.id, agentEntries, folderName, process.cwd(), gitRemote);
300
+ console.log(`🔗 Registered board agents for "${targetBoard.name}" on Agile Monster.`);
301
+ }
302
+ catch {
303
+ // Non-blocking if backend is older
304
+ }
138
305
  const savedPath = this.client.saveProjectConfig({
139
306
  workspace_id: targetBoard.workspace_id,
140
307
  board_id: targetBoard.id,
141
308
  board_name: targetBoard.name,
142
- provider: 'antigravity',
309
+ project_name: folderName,
310
+ project_path: process.cwd(),
311
+ provider: options?.provider || 'antigravity',
143
312
  review_list: 'In Review',
144
313
  done_list: 'Done',
145
314
  api_key: apiKey,
146
315
  });
147
- console.log(`✅ Bound repository to board "${targetBoard.name}" (${targetBoard.id})`);
316
+ console.log(`✅ Bound repository "${folderName}" -> Board "${targetBoard.name}" (${targetBoard.id})`);
148
317
  console.log(` Config written to: ${savedPath}`);
149
318
  // Ensure AGENTS.md exists
150
319
  const agentsPath = path.resolve(process.cwd(), 'AGENTS.md');
@@ -156,19 +325,18 @@ export class AgileMonsterOrchestrator {
156
325
  console.log(`📄 AGENTS.md already exists in current directory.`);
157
326
  }
158
327
  // Configure IDEs (Cursor, Claude Code, Windsurf, Antigravity, VS Code)
159
- this.configureIdes(apiKey, options?.provider);
328
+ this.configureIdes(apiKey, options?.ide || options?.provider);
160
329
  this.ensureGitignore();
161
- console.log('\n🎉 Project successfully configured for AI Agents!');
330
+ console.log('\n🎉 Project successfully connected to Agile Monster!');
162
331
  console.log('\n======================================================');
163
332
  console.log(' 🚀 HOW TO USE IT NOW ');
164
333
  console.log('======================================================');
165
- console.log(`1. In Agile Monster (https://app.agilemonster.com), open board "${targetBoard.name}".`);
166
- console.log('2. Create or open any card and assign it to your AI Agent (e.g. Antigravity, Cursor, Claude Code).');
167
- console.log('3. In your AI IDE chat, simply say:');
334
+ console.log(`1. Open board "${targetBoard.name}" on https://app.agilemonster.com`);
335
+ console.log(`2. Open any card and assign Worker to "${shortTag ? `Cursor-${shortTag}` : `Cursor: ${targetBoard.name}`}" (or Claude Code / Antigravity).`);
336
+ console.log('3. In your AI IDE chat inside this project, simply say:');
168
337
  console.log(' "Check my Agile Monster tasks and start the next queued card."');
169
- console.log(' Or from your terminal anytime:');
170
- console.log(' • Check board & queued tasks: npx -y @agilemonster/mcp-server status');
171
- console.log(' • Auto-watch & run daemon: npx -y @agilemonster/mcp-server watch\n');
338
+ console.log(' Or connect another project folder (e.g. Planogram.ai in Cursor) anytime by running:');
339
+ console.log(' cd /path/to/other-project && npx -y @agilemonster/mcp-server bootstrap\n');
172
340
  }
173
341
  async bootstrapInteractive(options) {
174
342
  return this.initInteractive(options);
package/dist/server.js CHANGED
@@ -512,12 +512,30 @@ Please strictly follow the Agile Monster Task Lifecycle Protocol:
512
512
  const status = args.status;
513
513
  const boardId = args.board_id || projectConfig?.board_id;
514
514
  const workspaceId = projectConfig?.workspace_id;
515
+ if (!boardId) {
516
+ return {
517
+ isError: true,
518
+ content: [
519
+ {
520
+ type: 'text',
521
+ text: `⚠️ [BOARD SCOPE GUARD] Current project folder "${path.basename(process.cwd())}" is not bound to an Agile Monster board (.agilemonster.json is missing).\n` +
522
+ `Refusing to list tasks across all boards so you never accidentally pick up a task from the wrong project and burn tokens.\n` +
523
+ `Please run "npx -y @agilemonster/mcp-server bootstrap" in this folder or call agilemonster_get_context first to bind the right board.`,
524
+ },
525
+ ],
526
+ };
527
+ }
515
528
  const result = await client.listAssigned(provider, status, boardId, workspaceId);
516
529
  return {
517
530
  content: [
518
531
  {
519
532
  type: 'text',
520
- text: JSON.stringify(result, null, 2),
533
+ text: JSON.stringify({
534
+ bound_board_id: boardId,
535
+ bound_board_name: projectConfig?.board_name,
536
+ bound_project_name: projectConfig?.project_name || path.basename(process.cwd()),
537
+ ...result,
538
+ }, null, 2),
521
539
  },
522
540
  ],
523
541
  };
@@ -526,12 +544,25 @@ Please strictly follow the Agile Monster Task Lifecycle Protocol:
526
544
  const cardId = args.card_id;
527
545
  const provider = args.provider || projectConfig?.provider || 'antigravity';
528
546
  const runRef = args.run_ref;
529
- const card = await client.claimTask(cardId, provider, runRef);
547
+ const expectedBoardId = projectConfig?.board_id;
548
+ if (!expectedBoardId) {
549
+ return {
550
+ isError: true,
551
+ content: [
552
+ {
553
+ type: 'text',
554
+ text: `⚠️ [BOARD SCOPE GUARD] Cannot claim card ${cardId} because this repository folder ("${path.basename(process.cwd())}") has no .agilemonster.json board binding.\n` +
555
+ `Run "npx -y @agilemonster/mcp-server bootstrap" first so we verify this folder matches the card's board.`,
556
+ },
557
+ ],
558
+ };
559
+ }
560
+ const card = await client.claimTask(cardId, provider, runRef, expectedBoardId);
530
561
  return {
531
562
  content: [
532
563
  {
533
564
  type: 'text',
534
- text: `Task claimed successfully. Status is now "in_progress".\n${JSON.stringify(card, null, 2)}`,
565
+ text: `Task claimed successfully on board "${card.board_name || expectedBoardId}". Status is now "in_progress".\n${JSON.stringify(card, null, 2)}`,
535
566
  },
536
567
  ],
537
568
  };
package/dist/types.d.ts CHANGED
@@ -2,6 +2,8 @@ export interface AgileMonsterProjectConfig {
2
2
  workspace_id?: string;
3
3
  board_id?: string;
4
4
  board_name?: string;
5
+ project_name?: string;
6
+ project_path?: string;
5
7
  provider?: string;
6
8
  review_list?: string;
7
9
  done_list?: string;
@@ -38,10 +40,38 @@ export interface ColumnList {
38
40
  name: string;
39
41
  position: number;
40
42
  }
43
+ export interface AiBoardMatch {
44
+ matched_board_id: string | null;
45
+ confidence: 'high' | 'medium' | 'none';
46
+ reason: string;
47
+ suggested_new_board_name: string;
48
+ suggested_short_code: string;
49
+ }
50
+ export interface BoardAgentConfig {
51
+ provider: string;
52
+ provider_label?: string;
53
+ board_id?: string;
54
+ board_name?: string;
55
+ default_name?: string;
56
+ custom_name?: string;
57
+ short_name?: string | null;
58
+ display_name?: string;
59
+ project_name?: string | null;
60
+ project_path?: string | null;
61
+ git_remote?: string | null;
62
+ connected?: boolean;
63
+ last_connected_at?: string | null;
64
+ }
41
65
  export interface ContextResponse {
42
66
  matched_board: Board | null;
67
+ ai_match?: AiBoardMatch | null;
43
68
  available_boards: Board[];
69
+ workspaces?: Array<{
70
+ id: string;
71
+ name: string;
72
+ }>;
44
73
  lists: ColumnList[];
74
+ board_agents?: BoardAgentConfig[];
45
75
  active_agent_tasks: Card[];
46
76
  supported_providers: string[];
47
77
  }
package/dist/worker.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { spawn } from 'node:child_process';
2
+ import path from 'node:path';
2
3
  import { AgileMonsterClient } from './client.js';
3
4
  export class AgileMonsterWorker {
4
5
  client;
@@ -59,8 +60,11 @@ export class AgileMonsterWorker {
59
60
  const projectConfig = this.client.loadProjectConfig();
60
61
  const provider = options.provider || projectConfig?.provider || 'antigravity';
61
62
  const boardId = options.boardId || projectConfig?.board_id;
63
+ if (!boardId) {
64
+ throw new Error(`[Board Scope Guard] No .agilemonster.json board binding found in "${path.basename(process.cwd())}". Refusing to poll tasks without an explicit board binding.`);
65
+ }
62
66
  const res = await this.client.listAssigned(provider, 'queued', boardId);
63
- const tasks = res.cards || [];
67
+ const tasks = (res.cards || []).filter((c) => !c.board_id || c.board_id === boardId);
64
68
  if (tasks.length === 0) {
65
69
  return;
66
70
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agilemonster/mcp-server",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
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",