@askmesh/mcp 0.11.2 → 0.12.1
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/agent/auto_responder.d.ts +13 -0
- package/dist/agent/auto_responder.js +48 -9
- package/dist/client/askmesh_client.d.ts +1 -0
- package/dist/index.js +15 -3
- package/dist/sse/sse_listener.d.ts +1 -0
- package/dist/tools/askmesh.d.ts +17 -0
- package/dist/tools/askmesh.js +42 -1
- package/package.json +1 -1
|
@@ -11,9 +11,22 @@ export declare class AutoResponder {
|
|
|
11
11
|
agentType: string;
|
|
12
12
|
allowedScopes: string[];
|
|
13
13
|
}): void;
|
|
14
|
+
/**
|
|
15
|
+
* Filter scopes by the requester's role.
|
|
16
|
+
*
|
|
17
|
+
* Scopes can be encoded with a minimum-role prefix:
|
|
18
|
+
* "code" → all members
|
|
19
|
+
* "lead:db-schema" → lead, admin, owner only
|
|
20
|
+
* "admin:db-data" → admin, owner only
|
|
21
|
+
* "owner:secrets" → owner only
|
|
22
|
+
*
|
|
23
|
+
* Returns the bare scope names that this requester is allowed to use.
|
|
24
|
+
*/
|
|
25
|
+
private filterScopesByRole;
|
|
14
26
|
/**
|
|
15
27
|
* Build a scope-aware extension to the system prompt.
|
|
16
28
|
* Only applies for server agents — dev/ci agents are unrestricted.
|
|
29
|
+
* `requesterRole` is used to filter role-tiered scopes per request.
|
|
17
30
|
*/
|
|
18
31
|
private scopePrompt;
|
|
19
32
|
/**
|
|
@@ -32,23 +32,62 @@ export class AutoResponder {
|
|
|
32
32
|
setProfile(profile) {
|
|
33
33
|
this.profile = profile;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Filter scopes by the requester's role.
|
|
37
|
+
*
|
|
38
|
+
* Scopes can be encoded with a minimum-role prefix:
|
|
39
|
+
* "code" → all members
|
|
40
|
+
* "lead:db-schema" → lead, admin, owner only
|
|
41
|
+
* "admin:db-data" → admin, owner only
|
|
42
|
+
* "owner:secrets" → owner only
|
|
43
|
+
*
|
|
44
|
+
* Returns the bare scope names that this requester is allowed to use.
|
|
45
|
+
*/
|
|
46
|
+
filterScopesByRole(rawScopes, requesterRole) {
|
|
47
|
+
const ROLE_RANK = { member: 1, lead: 2, admin: 3, owner: 4 };
|
|
48
|
+
const requesterRank = requesterRole ? (ROLE_RANK[requesterRole] || 0) : 0;
|
|
49
|
+
const result = [];
|
|
50
|
+
for (const raw of rawScopes) {
|
|
51
|
+
const idx = raw.indexOf(':');
|
|
52
|
+
if (idx === -1) {
|
|
53
|
+
// No prefix → available to everyone
|
|
54
|
+
result.push(raw);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const minRole = raw.slice(0, idx);
|
|
58
|
+
const scope = raw.slice(idx + 1);
|
|
59
|
+
const minRank = ROLE_RANK[minRole];
|
|
60
|
+
if (!minRank) {
|
|
61
|
+
// Unknown prefix → treat as plain scope (e.g. "http:something")
|
|
62
|
+
result.push(raw);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (requesterRank >= minRank) {
|
|
66
|
+
result.push(scope);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
35
71
|
/**
|
|
36
72
|
* Build a scope-aware extension to the system prompt.
|
|
37
73
|
* Only applies for server agents — dev/ci agents are unrestricted.
|
|
74
|
+
* `requesterRole` is used to filter role-tiered scopes per request.
|
|
38
75
|
*/
|
|
39
|
-
scopePrompt() {
|
|
76
|
+
scopePrompt(requesterRole = null) {
|
|
40
77
|
if (!this.profile || this.profile.agentType !== 'server')
|
|
41
78
|
return '';
|
|
42
|
-
const
|
|
43
|
-
|
|
79
|
+
const allScopes = this.profile.allowedScopes || [];
|
|
80
|
+
const scopes = this.filterScopesByRole(allScopes, requesterRole);
|
|
81
|
+
const roleHint = requesterRole ? ` (requester role: ${requesterRole})` : ' (requester role: unknown)';
|
|
82
|
+
if (scopes.length === 0) {
|
|
44
83
|
return [
|
|
45
84
|
'',
|
|
46
|
-
|
|
47
|
-
'No allowed scopes
|
|
48
|
-
'Reply with: "This is
|
|
85
|
+
`SERVER AGENT — RESTRICTIVE MODE${roleHint}:`,
|
|
86
|
+
'No allowed scopes apply for this requester. Refuse to share any project content.',
|
|
87
|
+
'Reply with: "This server agent is not authorized to share information at your role level."',
|
|
49
88
|
].join('\n');
|
|
50
89
|
}
|
|
51
|
-
const lines = ['',
|
|
90
|
+
const lines = ['', `SERVER AGENT — ALLOWED SCOPES${roleHint}:`];
|
|
52
91
|
lines.push('You are a restricted server agent. You may ONLY share information that falls within these scopes:');
|
|
53
92
|
for (const s of scopes) {
|
|
54
93
|
lines.push(` • ${s}`);
|
|
@@ -102,7 +141,7 @@ export class AutoResponder {
|
|
|
102
141
|
if (this.mcpServer) {
|
|
103
142
|
try {
|
|
104
143
|
const context = readLocalContext();
|
|
105
|
-
const scopeRules = this.scopePrompt();
|
|
144
|
+
const scopeRules = this.scopePrompt(request.fromUserRole || null);
|
|
106
145
|
const result = (await this.mcpServer.request({
|
|
107
146
|
method: 'sampling/createMessage',
|
|
108
147
|
params: {
|
|
@@ -181,7 +220,7 @@ export class AutoResponder {
|
|
|
181
220
|
}
|
|
182
221
|
async callAnthropicAPI(apiKey, request, context) {
|
|
183
222
|
const model = process.env.ANTHROPIC_MODEL || 'claude-sonnet-4-20250514';
|
|
184
|
-
const fullSystem = SYSTEM_PROMPT + this.scopePrompt();
|
|
223
|
+
const fullSystem = SYSTEM_PROMPT + this.scopePrompt(request.fromUserRole || null);
|
|
185
224
|
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
186
225
|
method: 'POST',
|
|
187
226
|
headers: {
|
package/dist/index.js
CHANGED
|
@@ -5,18 +5,29 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
5
5
|
import { AskMeshClient } from './client/askmesh_client.js';
|
|
6
6
|
import { SseListener } from './sse/sse_listener.js';
|
|
7
7
|
import { AutoResponder } from './agent/auto_responder.js';
|
|
8
|
-
import { registerAskMesh, registerSetupOnly } from './tools/askmesh.js';
|
|
8
|
+
import { registerAskMesh, registerSetupOnly, bootstrapSetupSkill } from './tools/askmesh.js';
|
|
9
9
|
import * as statusCache from './statusline/cache.js';
|
|
10
10
|
const TOKEN = process.env.ASKMESH_TOKEN;
|
|
11
11
|
const URL = process.env.ASKMESH_URL || 'https://api.askmesh.dev';
|
|
12
12
|
const server = new McpServer({
|
|
13
13
|
name: 'askmesh',
|
|
14
|
-
version: '0.
|
|
14
|
+
version: '0.12.1',
|
|
15
15
|
});
|
|
16
16
|
if (!TOKEN) {
|
|
17
17
|
// No token — start in setup-only mode
|
|
18
18
|
console.error('[AskMesh] No ASKMESH_TOKEN found. Starting in setup mode.');
|
|
19
19
|
registerSetupOnly(server);
|
|
20
|
+
// Bootstrap: write the /ask-setup slash command file so the user can
|
|
21
|
+
// discover it without having to know the natural-language path.
|
|
22
|
+
const boot = bootstrapSetupSkill();
|
|
23
|
+
if (boot.created) {
|
|
24
|
+
console.error(`[AskMesh] ✨ Created ${boot.path}`);
|
|
25
|
+
console.error('[AskMesh] Restart Claude Code to enable the /ask-setup slash command,');
|
|
26
|
+
console.error('[AskMesh] or just type "setup askmesh" right now to start the wizard.');
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
console.error('[AskMesh] Tip: type "setup askmesh" to start the configuration wizard.');
|
|
30
|
+
}
|
|
20
31
|
}
|
|
21
32
|
else {
|
|
22
33
|
// Full mode
|
|
@@ -61,7 +72,8 @@ else {
|
|
|
61
72
|
await autoResponder.handleRequest({
|
|
62
73
|
id: req.id,
|
|
63
74
|
fromAgentId: req.fromAgentId,
|
|
64
|
-
fromUsername: `agent#${req.fromAgentId}`,
|
|
75
|
+
fromUsername: req.fromUsername || `agent#${req.fromAgentId}`,
|
|
76
|
+
fromUserRole: req.fromUserRole || null,
|
|
65
77
|
question: req.question,
|
|
66
78
|
context: req.context,
|
|
67
79
|
});
|
package/dist/tools/askmesh.d.ts
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { AskMeshClient } from '../client/askmesh_client.js';
|
|
3
3
|
export declare function registerAskMesh(server: McpServer, client: AskMeshClient): void;
|
|
4
|
+
/**
|
|
5
|
+
* Bootstrap: write the minimum file needed to expose /ask-setup as a slash
|
|
6
|
+
* command before any setup has run. Called automatically on first MCP start.
|
|
7
|
+
*
|
|
8
|
+
* Without this, there's a chicken-and-egg problem: /ask-setup is the skill
|
|
9
|
+
* that installs all the other skills, including itself. After a fresh install,
|
|
10
|
+
* the user has no slash command and has to discover the natural-language path
|
|
11
|
+
* "setup askmesh".
|
|
12
|
+
*
|
|
13
|
+
* After this bootstrap, the user only needs to (a) restart Claude Code to
|
|
14
|
+
* pick up the new slash command, or (b) just type "setup askmesh" in the
|
|
15
|
+
* current session — both work.
|
|
16
|
+
*/
|
|
17
|
+
export declare function bootstrapSetupSkill(): {
|
|
18
|
+
created: boolean;
|
|
19
|
+
path: string;
|
|
20
|
+
};
|
|
4
21
|
export declare function registerSetupOnly(server: McpServer): void;
|
package/dist/tools/askmesh.js
CHANGED
|
@@ -256,6 +256,47 @@ Actions disponibles :
|
|
|
256
256
|
}
|
|
257
257
|
});
|
|
258
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* Bootstrap: write the minimum file needed to expose /ask-setup as a slash
|
|
261
|
+
* command before any setup has run. Called automatically on first MCP start.
|
|
262
|
+
*
|
|
263
|
+
* Without this, there's a chicken-and-egg problem: /ask-setup is the skill
|
|
264
|
+
* that installs all the other skills, including itself. After a fresh install,
|
|
265
|
+
* the user has no slash command and has to discover the natural-language path
|
|
266
|
+
* "setup askmesh".
|
|
267
|
+
*
|
|
268
|
+
* After this bootstrap, the user only needs to (a) restart Claude Code to
|
|
269
|
+
* pick up the new slash command, or (b) just type "setup askmesh" in the
|
|
270
|
+
* current session — both work.
|
|
271
|
+
*/
|
|
272
|
+
export function bootstrapSetupSkill() {
|
|
273
|
+
const cwd = process.cwd();
|
|
274
|
+
const commandsDir = join(cwd, '.claude', 'commands');
|
|
275
|
+
const filePath = join(commandsDir, 'ask-setup.md');
|
|
276
|
+
if (existsSync(filePath)) {
|
|
277
|
+
return { created: false, path: filePath };
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
mkdirSync(commandsDir, { recursive: true });
|
|
281
|
+
const content = `Utilise l'outil MCP askmesh avec l'action "setup" pour installer ou mettre à jour la configuration AskMesh dans ce projet.
|
|
282
|
+
|
|
283
|
+
Cela va :
|
|
284
|
+
- Créer ou vérifier le .env avec le token AskMesh
|
|
285
|
+
- Installer les slash commands (/ask-inbox, /ask-broadcast, etc.)
|
|
286
|
+
- Configurer la status line
|
|
287
|
+
- Ajouter .env au .gitignore
|
|
288
|
+
- Créer un .askmeshignore template
|
|
289
|
+
|
|
290
|
+
Si l'utilisateur fournit un token dans les arguments ($ARGUMENTS), passe-le en paramètre "token".
|
|
291
|
+
Sinon, demande-lui d'aller sur https://askmesh.dev > Settings pour récupérer son API token.
|
|
292
|
+
`;
|
|
293
|
+
writeFileSync(filePath, content);
|
|
294
|
+
return { created: true, path: filePath };
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
return { created: false, path: filePath };
|
|
298
|
+
}
|
|
299
|
+
}
|
|
259
300
|
export function registerSetupOnly(server) {
|
|
260
301
|
server.tool('askmesh', `AskMesh — réseau de communication entre développeurs et agents IA.
|
|
261
302
|
|
|
@@ -282,7 +323,7 @@ Si l'utilisateur n'a pas encore de compte, dirige-le vers https://askmesh.dev po
|
|
|
282
323
|
return text('Action inconnue.');
|
|
283
324
|
});
|
|
284
325
|
}
|
|
285
|
-
const CURRENT_VERSION = '0.
|
|
326
|
+
const CURRENT_VERSION = '0.12.1';
|
|
286
327
|
async function checkUpdateAndSetup() {
|
|
287
328
|
const lines = [];
|
|
288
329
|
lines.push(`Version actuelle : ${CURRENT_VERSION}`);
|