@askmesh/mcp 0.6.0 → 0.7.0

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.
@@ -17,6 +17,11 @@ export class AutoResponder {
17
17
  }
18
18
  async handleRequest(request) {
19
19
  console.error(`[AskMesh] Question from @${request.fromUsername}: "${request.question}"`);
20
+ // Mark thread as in_progress before processing
21
+ try {
22
+ await this.client.updateThreadStatus(request.id, 'in_progress');
23
+ }
24
+ catch { }
20
25
  // Strategy 1: MCP sampling — asks the active Claude Code to respond
21
26
  // Uses the user's existing Claude subscription, no API key needed
22
27
  if (this.mcpServer) {
@@ -3,7 +3,7 @@ export declare class AskMeshClient {
3
3
  private token;
4
4
  constructor(baseUrl: string, token: string);
5
5
  private headers;
6
- askAgent(toUsername: string, question: string, context?: string): Promise<{
6
+ askAgent(toUsername: string, question: string, context?: string, parentThreadId?: number): Promise<{
7
7
  requestId: number;
8
8
  status: string;
9
9
  }>;
@@ -84,4 +84,29 @@ export declare class AskMeshClient {
84
84
  createdAt: string;
85
85
  }>;
86
86
  }>;
87
+ updateThreadStatus(requestId: number, status: 'in_progress' | 'delegated'): Promise<{
88
+ id: number;
89
+ status: string;
90
+ }>;
91
+ getMyThreads(): Promise<{
92
+ threads: Array<{
93
+ id: number;
94
+ fromUsername: string;
95
+ toUsername: string;
96
+ question: string;
97
+ status: string;
98
+ replyCount: number;
99
+ parentThreadId: number | null;
100
+ createdAt: string;
101
+ updatedAt: string;
102
+ }>;
103
+ }>;
104
+ getTeamBoard(teamId: number): Promise<{
105
+ columns: {
106
+ pending: any[];
107
+ in_progress: any[];
108
+ active: any[];
109
+ closed: any[];
110
+ };
111
+ }>;
87
112
  }
@@ -11,11 +11,11 @@ export class AskMeshClient {
11
11
  'Content-Type': 'application/json',
12
12
  };
13
13
  }
14
- async askAgent(toUsername, question, context) {
14
+ async askAgent(toUsername, question, context, parentThreadId) {
15
15
  const res = await fetch(`${this.baseUrl}/api/v1/requests`, {
16
16
  method: 'POST',
17
17
  headers: this.headers(),
18
- body: JSON.stringify({ toUsername, question, context }),
18
+ body: JSON.stringify({ toUsername, question, context, parentThreadId }),
19
19
  });
20
20
  if (!res.ok)
21
21
  throw new Error(`askAgent failed: ${res.status} ${await res.text()}`);
@@ -108,4 +108,30 @@ export class AskMeshClient {
108
108
  throw new Error(`getThread failed: ${res.status}`);
109
109
  return res.json();
110
110
  }
111
+ async updateThreadStatus(requestId, status) {
112
+ const res = await fetch(`${this.baseUrl}/api/v1/requests/${requestId}/status`, {
113
+ method: 'PUT',
114
+ headers: this.headers(),
115
+ body: JSON.stringify({ status }),
116
+ });
117
+ if (!res.ok)
118
+ throw new Error(`updateThreadStatus failed: ${res.status} ${await res.text()}`);
119
+ return res.json();
120
+ }
121
+ async getMyThreads() {
122
+ const res = await fetch(`${this.baseUrl}/api/v1/requests/my-threads`, {
123
+ headers: this.headers(),
124
+ });
125
+ if (!res.ok)
126
+ throw new Error(`getMyThreads failed: ${res.status}`);
127
+ return res.json();
128
+ }
129
+ async getTeamBoard(teamId) {
130
+ const res = await fetch(`${this.baseUrl}/api/v1/teams/${teamId}/board`, {
131
+ headers: this.headers(),
132
+ });
133
+ if (!res.ok)
134
+ throw new Error(`getTeamBoard failed: ${res.status}`);
135
+ return res.json();
136
+ }
111
137
  }
@@ -12,20 +12,25 @@ Actions:
12
12
  - "reply" : ajouter une réponse à un thread existant
13
13
  - "thread" : voir le thread complet d'une question
14
14
  - "close" : clôturer un thread
15
+ - "my-threads" : voir tous tes threads actifs
16
+ - "board" : voir le kanban board d'une team
17
+ - "progress" : marquer un thread comme "en cours de traitement"
15
18
  - "context" : partager ton contexte projet avec ta team`, {
16
- action: z.enum(['ask', 'list', 'status', 'pending', 'inbox', 'answer', 'reply', 'thread', 'close', 'context']).describe('Action à effectuer'),
19
+ action: z.enum(['ask', 'list', 'status', 'pending', 'inbox', 'answer', 'reply', 'thread', 'close', 'my-threads', 'board', 'progress', 'context']).describe('Action à effectuer'),
17
20
  username: z.string().optional().describe("Username de l'agent cible (pour ask/status)"),
18
21
  question: z.string().optional().describe('Question à poser (pour ask)'),
19
- requestId: z.number().optional().describe('ID de la requête (pour answer/reply/thread/close)'),
22
+ requestId: z.number().optional().describe('ID de la requête (pour answer/reply/thread/close/progress)'),
20
23
  message: z.string().optional().describe('Réponse ou contexte à envoyer (pour answer/reply/context)'),
21
- }, async ({ action, username, question, requestId, message }) => {
24
+ parentThreadId: z.number().optional().describe('ID du thread parent (pour ask, lie les threads entre eux)'),
25
+ teamId: z.number().optional().describe('ID de la team (pour board)'),
26
+ }, async ({ action, username, question, requestId, message, parentThreadId, teamId }) => {
22
27
  switch (action) {
23
28
  case 'ask': {
24
29
  if (!username || !question) {
25
30
  return text("Paramètres requis : username et question");
26
31
  }
27
32
  const target = username.replace(/^@/, '');
28
- const result = await client.askAgent(target, question);
33
+ const result = await client.askAgent(target, question, undefined, parentThreadId);
29
34
  const reqId = result.requestId;
30
35
  // Poll for answer (max 60s)
31
36
  for (let i = 0; i < 20; i++) {
@@ -123,6 +128,47 @@ Actions:
123
128
  const result = await client.closeThread(requestId);
124
129
  return text(`Thread #${result.id} clôturé.`);
125
130
  }
131
+ case 'my-threads': {
132
+ const { threads } = await client.getMyThreads();
133
+ if (threads.length === 0)
134
+ return text('Aucun thread actif.');
135
+ const statusIcons = {
136
+ pending: '⏳', in_progress: '🔧', active: '💬', delegated: '📤', closed: '✅',
137
+ };
138
+ const lines = threads.map((t) => {
139
+ const icon = statusIcons[t.status] || '❓';
140
+ let line = `${icon} #${t.id} [${t.status}] @${t.fromUsername} → @${t.toUsername}: "${t.question}"`;
141
+ if (t.replyCount > 0)
142
+ line += ` (${t.replyCount} replies)`;
143
+ if (t.parentThreadId)
144
+ line += ` [lié au thread #${t.parentThreadId}]`;
145
+ return line;
146
+ });
147
+ return text(`Mes threads:\n\n${lines.join('\n')}`);
148
+ }
149
+ case 'board': {
150
+ if (!teamId)
151
+ return text("Paramètre requis : teamId");
152
+ const board = await client.getTeamBoard(teamId);
153
+ const formatCol = (name, items) => {
154
+ if (items.length === 0)
155
+ return `**${name}**: (vide)`;
156
+ return `**${name}** (${items.length}):\n${items.map((t) => ` #${t.id} @${t.fromUsername}→@${t.toUsername}: "${t.question}" [${t.replyCount} replies]`).join('\n')}`;
157
+ };
158
+ const lines = [
159
+ formatCol('Pending', board.columns.pending),
160
+ formatCol('In Progress', board.columns.in_progress),
161
+ formatCol('Active', board.columns.active),
162
+ formatCol('Closed (24h)', board.columns.closed),
163
+ ];
164
+ return text(`Team Board:\n\n${lines.join('\n\n')}`);
165
+ }
166
+ case 'progress': {
167
+ if (!requestId)
168
+ return text("Paramètre requis : requestId");
169
+ const result = await client.updateThreadStatus(requestId, 'in_progress');
170
+ return text(`Thread #${result.id} marqué comme en cours de traitement.`);
171
+ }
126
172
  case 'context': {
127
173
  if (!message)
128
174
  return text("Paramètre requis : message (le contenu du contexte)");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askmesh/mcp",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "AskMesh MCP server — connect your AI coding agent to your team's mesh network",
5
5
  "type": "module",
6
6
  "bin": {