lunaarc-mcp 1.2.6 → 1.2.7

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.
@@ -52,10 +52,24 @@ interface AIAssignedCard {
52
52
  due_date: string | null;
53
53
  created_at: string;
54
54
  updated_at: string;
55
- agent_id: string | null;
56
55
  agent_name: string | null;
57
- agent_slug: string | null;
58
- agent_content: string | null;
56
+ }
57
+ interface ProjectAgent {
58
+ id: string;
59
+ name: string;
60
+ description: string | null;
61
+ tools: string[];
62
+ model: string;
63
+ synced_at: string;
64
+ usage_count: number;
65
+ }
66
+ interface PredefinedAgent {
67
+ id: string;
68
+ name: string;
69
+ slug: string;
70
+ description: string | null;
71
+ content: string;
72
+ icon: string | null;
59
73
  }
60
74
  interface KanbanColumn {
61
75
  id: string;
@@ -175,6 +189,14 @@ export declare class LunaArcApiClient {
175
189
  id: string;
176
190
  message: string;
177
191
  }>;
192
+ syncAgents(agents: Array<{
193
+ name: string;
194
+ description: string;
195
+ tools: string[];
196
+ model: string;
197
+ }>): Promise<number>;
198
+ listAgents(): Promise<ProjectAgent[]>;
199
+ getPredefinedAgents(): Promise<PredefinedAgent[]>;
178
200
  }
179
201
  export declare function createApiClient(): LunaArcApiClient;
180
202
  export {};
@@ -108,6 +108,20 @@ class LunaArcApiClient {
108
108
  body: JSON.stringify(params),
109
109
  });
110
110
  }
111
+ // Agent API methods
112
+ async syncAgents(agents) {
113
+ const result = await this.request('/agents/sync', {
114
+ method: 'POST',
115
+ body: JSON.stringify({ agents }),
116
+ });
117
+ return result.count;
118
+ }
119
+ async listAgents() {
120
+ return this.request('/agents');
121
+ }
122
+ async getPredefinedAgents() {
123
+ return this.request('/agents/predefined');
124
+ }
111
125
  }
112
126
  exports.LunaArcApiClient = LunaArcApiClient;
113
127
  // Create client instance from environment variables
package/dist/server.js CHANGED
@@ -8,8 +8,9 @@ const client_js_1 = require("./api/client.js");
8
8
  const wiki_js_1 = require("./tools/wiki.js");
9
9
  const kanban_js_1 = require("./tools/kanban.js");
10
10
  const todos_js_1 = require("./tools/todos.js");
11
+ const agents_js_1 = require("./tools/agents.js");
11
12
  // All available tools
12
- const allTools = [...wiki_js_1.wikiTools, ...kanban_js_1.kanbanTools, ...todos_js_1.todosTools];
13
+ const allTools = [...wiki_js_1.wikiTools, ...kanban_js_1.kanbanTools, ...todos_js_1.todosTools, ...agents_js_1.agentTools];
13
14
  // Create API client
14
15
  let apiClient;
15
16
  try {
@@ -26,7 +27,7 @@ catch (error) {
26
27
  // Create MCP server
27
28
  const server = new index_js_1.Server({
28
29
  name: 'lunaarc-mcp',
29
- version: '1.2.6',
30
+ version: '1.2.7',
30
31
  }, {
31
32
  capabilities: {
32
33
  tools: {},
@@ -52,6 +53,9 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
52
53
  else if (name.startsWith('todos_')) {
53
54
  return await (0, todos_js_1.handleTodosTool)(apiClient, name, args || {});
54
55
  }
56
+ else if (name.startsWith('agents_')) {
57
+ return await (0, agents_js_1.handleAgentTool)(apiClient, name, args || {});
58
+ }
55
59
  else {
56
60
  throw new Error(`Unknown tool: ${name}`);
57
61
  }
@@ -0,0 +1,50 @@
1
+ import { LunaArcApiClient } from '../api/client.js';
2
+ export declare const agentTools: ({
3
+ name: string;
4
+ description: string;
5
+ inputSchema: {
6
+ type: "object";
7
+ properties: {
8
+ agents_dir: {
9
+ type: string;
10
+ description: string;
11
+ };
12
+ overwrite?: undefined;
13
+ };
14
+ required: never[];
15
+ };
16
+ } | {
17
+ name: string;
18
+ description: string;
19
+ inputSchema: {
20
+ type: "object";
21
+ properties: {
22
+ agents_dir?: undefined;
23
+ overwrite?: undefined;
24
+ };
25
+ required: never[];
26
+ };
27
+ } | {
28
+ name: string;
29
+ description: string;
30
+ inputSchema: {
31
+ type: "object";
32
+ properties: {
33
+ agents_dir: {
34
+ type: string;
35
+ description: string;
36
+ };
37
+ overwrite: {
38
+ type: string;
39
+ description: string;
40
+ };
41
+ };
42
+ required: never[];
43
+ };
44
+ })[];
45
+ export declare function handleAgentTool(client: LunaArcApiClient, toolName: string, args: Record<string, unknown>): Promise<{
46
+ content: {
47
+ type: 'text';
48
+ text: string;
49
+ }[];
50
+ }>;
@@ -0,0 +1,354 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.agentTools = void 0;
37
+ exports.handleAgentTool = handleAgentTool;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ // Tool definitions for agent operations
41
+ exports.agentTools = [
42
+ {
43
+ name: 'agents_sync',
44
+ description: `Sync local Claude Code agents to LunaArc project.
45
+
46
+ Reads all agent files from .claude/agents/ directory and syncs them to the project.
47
+ Agent files must be Markdown with YAML frontmatter containing: name, description, tools, model.
48
+
49
+ Example agent file:
50
+ \`\`\`markdown
51
+ ---
52
+ name: layout-designer
53
+ description: Layout design specialist...
54
+ tools: Read, Grep, Glob, Edit
55
+ model: haiku
56
+ ---
57
+ System prompt here...
58
+ \`\`\`
59
+
60
+ This allows the project to know which agents are available for card assignment.`,
61
+ inputSchema: {
62
+ type: 'object',
63
+ properties: {
64
+ agents_dir: {
65
+ type: 'string',
66
+ description: 'Path to agents directory (default: .claude/agents/)',
67
+ },
68
+ },
69
+ required: [],
70
+ },
71
+ },
72
+ {
73
+ name: 'agents_list',
74
+ description: 'List all AI agents synced to this project. Shows agent names, descriptions, tools, and usage count.',
75
+ inputSchema: {
76
+ type: 'object',
77
+ properties: {},
78
+ required: [],
79
+ },
80
+ },
81
+ {
82
+ name: 'agents_download',
83
+ description: `Download predefined LunaArc agents to your local project.
84
+
85
+ Downloads optimized, ready-to-use agent files from LunaArc to your .claude/agents/ directory.
86
+ These agents are maintained by LunaArc and include best practices for common development tasks.
87
+
88
+ Perfect for users who want to get started quickly without creating custom agents.
89
+ After download, you can customize the agents or use them as-is.`,
90
+ inputSchema: {
91
+ type: 'object',
92
+ properties: {
93
+ agents_dir: {
94
+ type: 'string',
95
+ description: 'Target directory for agent files (default: .claude/agents/)',
96
+ },
97
+ overwrite: {
98
+ type: 'boolean',
99
+ description: 'Overwrite existing agent files (default: false)',
100
+ },
101
+ },
102
+ required: [],
103
+ },
104
+ },
105
+ ];
106
+ // Parse agent frontmatter from markdown file
107
+ function parseAgentFile(filePath) {
108
+ try {
109
+ const content = fs.readFileSync(filePath, 'utf-8');
110
+ // Extract YAML frontmatter between --- markers
111
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
112
+ if (!match) {
113
+ console.error(`No frontmatter found in ${filePath}`);
114
+ return null;
115
+ }
116
+ const frontmatter = match[1];
117
+ // Parse YAML manually (simple key: value pairs)
118
+ const lines = frontmatter.split('\n');
119
+ const data = {};
120
+ for (const line of lines) {
121
+ const colonIndex = line.indexOf(':');
122
+ if (colonIndex > 0) {
123
+ const key = line.slice(0, colonIndex).trim();
124
+ const value = line.slice(colonIndex + 1).trim();
125
+ data[key] = value;
126
+ }
127
+ }
128
+ if (!data.name) {
129
+ console.error(`Missing 'name' in ${filePath}`);
130
+ return null;
131
+ }
132
+ // Parse tools (comma-separated)
133
+ const tools = data.tools
134
+ ? data.tools.split(',').map((t) => t.trim()).filter(Boolean)
135
+ : [];
136
+ return {
137
+ name: data.name,
138
+ description: data.description || '',
139
+ tools,
140
+ model: data.model || 'sonnet',
141
+ };
142
+ }
143
+ catch (error) {
144
+ console.error(`Error parsing ${filePath}:`, error);
145
+ return null;
146
+ }
147
+ }
148
+ // Tool handlers
149
+ async function handleAgentTool(client, toolName, args) {
150
+ switch (toolName) {
151
+ case 'agents_sync': {
152
+ const agentsDir = args.agents_dir || '.claude/agents';
153
+ // Resolve path
154
+ const resolvedPath = path.isAbsolute(agentsDir)
155
+ ? agentsDir
156
+ : path.join(process.cwd(), agentsDir);
157
+ // Check if directory exists
158
+ if (!fs.existsSync(resolvedPath)) {
159
+ return {
160
+ content: [
161
+ {
162
+ type: 'text',
163
+ text: `❌ Agents directory not found: ${resolvedPath}
164
+
165
+ Create the directory and add agent files:
166
+ \`\`\`
167
+ mkdir -p .claude/agents
168
+ \`\`\`
169
+
170
+ Then create agent files like \`.claude/agents/layout-designer.md\``,
171
+ },
172
+ ],
173
+ };
174
+ }
175
+ // Read all .md files
176
+ const files = fs.readdirSync(resolvedPath).filter((f) => f.endsWith('.md'));
177
+ if (files.length === 0) {
178
+ return {
179
+ content: [
180
+ {
181
+ type: 'text',
182
+ text: `⚠️ No agent files found in ${resolvedPath}
183
+
184
+ Agent files must be Markdown files (.md) with YAML frontmatter.`,
185
+ },
186
+ ],
187
+ };
188
+ }
189
+ // Parse all agent files
190
+ const agents = [];
191
+ const errors = [];
192
+ for (const file of files) {
193
+ const filePath = path.join(resolvedPath, file);
194
+ const agent = parseAgentFile(filePath);
195
+ if (agent) {
196
+ agents.push(agent);
197
+ }
198
+ else {
199
+ errors.push(file);
200
+ }
201
+ }
202
+ if (agents.length === 0) {
203
+ return {
204
+ content: [
205
+ {
206
+ type: 'text',
207
+ text: `❌ No valid agents found.
208
+
209
+ Parsing errors in: ${errors.join(', ')}
210
+
211
+ Ensure each file has valid YAML frontmatter with at least a 'name' field.`,
212
+ },
213
+ ],
214
+ };
215
+ }
216
+ // Sync to LunaArc
217
+ const count = await client.syncAgents(agents);
218
+ let output = `✅ ${count} agent(s) synced successfully!
219
+
220
+ **Synced Agents:**
221
+ `;
222
+ for (const agent of agents) {
223
+ output += `- **${agent.name}** [${agent.model}]\n`;
224
+ if (agent.description) {
225
+ output += ` ${agent.description.slice(0, 60)}${agent.description.length > 60 ? '...' : ''}\n`;
226
+ }
227
+ if (agent.tools.length > 0) {
228
+ output += ` Tools: ${agent.tools.join(', ')}\n`;
229
+ }
230
+ }
231
+ if (errors.length > 0) {
232
+ output += `\n⚠️ Failed to parse: ${errors.join(', ')}`;
233
+ }
234
+ return {
235
+ content: [{ type: 'text', text: output }],
236
+ };
237
+ }
238
+ case 'agents_list': {
239
+ const agents = await client.listAgents();
240
+ if (agents.length === 0) {
241
+ return {
242
+ content: [
243
+ {
244
+ type: 'text',
245
+ text: `# Project AI Agents
246
+
247
+ _No agents synced yet._
248
+
249
+ To sync agents, create files in \`.claude/agents/\` and run:
250
+ \`agents_sync\`
251
+
252
+ Or download predefined agents with:
253
+ \`agents_download\``,
254
+ },
255
+ ],
256
+ };
257
+ }
258
+ let output = `# Project AI Agents (${agents.length})
259
+
260
+ `;
261
+ for (const agent of agents) {
262
+ output += `## ${agent.name} [${agent.model}]\n`;
263
+ if (agent.description) {
264
+ output += `${agent.description}\n`;
265
+ }
266
+ output += `**Tools:** ${agent.tools.join(', ') || 'all'}\n`;
267
+ output += `**Usage:** ${agent.usage_count} card(s)\n`;
268
+ output += `**Last Sync:** ${new Date(agent.synced_at).toLocaleString()}\n\n`;
269
+ }
270
+ return {
271
+ content: [{ type: 'text', text: output }],
272
+ };
273
+ }
274
+ case 'agents_download': {
275
+ const agentsDir = args.agents_dir || '.claude/agents';
276
+ const overwrite = args.overwrite || false;
277
+ // Resolve path
278
+ const resolvedPath = path.isAbsolute(agentsDir)
279
+ ? agentsDir
280
+ : path.join(process.cwd(), agentsDir);
281
+ // Fetch predefined agents from LunaArc
282
+ const predefinedAgents = await client.getPredefinedAgents();
283
+ if (predefinedAgents.length === 0) {
284
+ return {
285
+ content: [
286
+ {
287
+ type: 'text',
288
+ text: `⚠️ No predefined agents available.
289
+
290
+ There are no predefined agents configured in LunaArc yet.
291
+ You can create your own agents manually in \`.claude/agents/\`.`,
292
+ },
293
+ ],
294
+ };
295
+ }
296
+ // Create directory if it doesn't exist
297
+ if (!fs.existsSync(resolvedPath)) {
298
+ fs.mkdirSync(resolvedPath, { recursive: true });
299
+ }
300
+ const downloaded = [];
301
+ const skipped = [];
302
+ const errors = [];
303
+ for (const agent of predefinedAgents) {
304
+ const fileName = `${agent.slug}.md`;
305
+ const filePath = path.join(resolvedPath, fileName);
306
+ // Check if file already exists
307
+ if (fs.existsSync(filePath) && !overwrite) {
308
+ skipped.push(agent.name);
309
+ continue;
310
+ }
311
+ try {
312
+ // Write agent file
313
+ fs.writeFileSync(filePath, agent.content, 'utf-8');
314
+ downloaded.push(agent.name);
315
+ }
316
+ catch (err) {
317
+ errors.push(`${agent.name}: ${err instanceof Error ? err.message : 'Unknown error'}`);
318
+ }
319
+ }
320
+ let output = '';
321
+ if (downloaded.length > 0) {
322
+ output += `✅ Downloaded ${downloaded.length} agent(s) to ${resolvedPath}\n\n`;
323
+ output += `**Downloaded:**\n`;
324
+ for (const name of downloaded) {
325
+ output += `- ${name}\n`;
326
+ }
327
+ }
328
+ if (skipped.length > 0) {
329
+ output += `\n⏭️ Skipped ${skipped.length} existing agent(s):\n`;
330
+ for (const name of skipped) {
331
+ output += `- ${name}\n`;
332
+ }
333
+ output += `\n_Use \`overwrite: true\` to update existing files._`;
334
+ }
335
+ if (errors.length > 0) {
336
+ output += `\n\n❌ Errors:\n`;
337
+ for (const err of errors) {
338
+ output += `- ${err}\n`;
339
+ }
340
+ }
341
+ if (downloaded.length === 0 && skipped.length === 0) {
342
+ output = `❌ No agents were downloaded. Check the errors above.`;
343
+ }
344
+ if (downloaded.length > 0) {
345
+ output += `\n\n📌 **Next step:** Run \`agents_sync\` to sync these agents to your LunaArc project.`;
346
+ }
347
+ return {
348
+ content: [{ type: 'text', text: output }],
349
+ };
350
+ }
351
+ default:
352
+ throw new Error(`Unknown agent tool: ${toolName}`);
353
+ }
354
+ }
@@ -445,22 +445,11 @@ ${totalCards > 1 ? `_(${totalCards - 1} weitere Karte${totalCards > 2 ? 'n' : ''
445
445
 
446
446
  `;
447
447
  }
448
- // IMPORTANT: Show role FIRST if present - before any card details
449
- if (cardToShow.agent_content) {
450
- output += `## 🛑 STOP - ADOPT ROLE FIRST
451
-
452
- **You MUST confirm this role BEFORE reading or working on the task!**
453
-
454
- ### Role: ${cardToShow.agent_name || 'AI Agent'}
455
-
456
- ${cardToShow.agent_content}
457
-
458
- ---
459
-
460
- **✋ MANDATORY CONFIRMATION:**
461
- Write FIRST: "I adopt the role ${cardToShow.agent_name || 'AI Agent'}" before starting the task.
448
+ // Show agent assignment if present - AI uses local agent definition
449
+ if (cardToShow.agent_name) {
450
+ output += `## 🤖 AI Agent: ${cardToShow.agent_name}
462
451
 
463
- **Do NOT proceed without this confirmation!**
452
+ Use your local agent definition from \`.claude/agents/${cardToShow.agent_name}.md\`
464
453
 
465
454
  ---
466
455
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lunaarc-mcp",
3
- "version": "1.2.6",
3
+ "version": "1.2.7",
4
4
  "description": "MCP Server for LunaArc - Access Wiki and Kanban from AI assistants",
5
5
  "main": "dist/server.js",
6
6
  "bin": {