@artyfacts/claude 1.3.8 → 1.3.10

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/index.js CHANGED
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  ClaudeExecutor: () => ClaudeExecutor,
35
35
  ClaudeExecutorWithTools: () => ClaudeExecutorWithTools,
36
36
  ContextFetcher: () => ContextFetcher,
37
+ McpHandler: () => McpHandler,
37
38
  buildPromptWithContext: () => buildPromptWithContext,
38
39
  clearCredentials: () => clearCredentials,
39
40
  createApiClient: () => createApiClient,
@@ -41,6 +42,7 @@ __export(index_exports, {
41
42
  createExecutor: () => createExecutor,
42
43
  createExecutorWithTools: () => createExecutorWithTools,
43
44
  createListener: () => createListener,
45
+ createMcpHandler: () => createMcpHandler,
44
46
  executeTool: () => executeTool,
45
47
  getAllToolSchemas: () => getAllToolSchemas,
46
48
  getCredentials: () => getCredentials,
@@ -651,7 +653,9 @@ var EVENT_TYPES = [
651
653
  "task_assigned",
652
654
  "task_unblocked",
653
655
  "blocker_resolved",
654
- "notification"
656
+ "notification",
657
+ "mcp_connect_request",
658
+ "connection_status"
655
659
  ];
656
660
  var ArtyfactsListener = class {
657
661
  config;
@@ -6010,12 +6014,201 @@ ${DEFAULT_SYSTEM_PROMPT2}` : DEFAULT_SYSTEM_PROMPT2;
6010
6014
  function createExecutorWithTools(config) {
6011
6015
  return new ClaudeExecutorWithTools(config);
6012
6016
  }
6017
+
6018
+ // src/mcp.ts
6019
+ var import_child_process2 = require("child_process");
6020
+ var DEFAULT_BASE_URL3 = "https://artyfacts.dev/api/v1";
6021
+ var OAUTH_MCP_SERVERS = {
6022
+ supabase: {
6023
+ url: "https://mcp.supabase.com/mcp",
6024
+ name: "supabase"
6025
+ },
6026
+ figma: {
6027
+ url: "https://mcp.figma.com/mcp",
6028
+ name: "figma"
6029
+ }
6030
+ };
6031
+ var CREDENTIAL_MCP_CONFIGS = {
6032
+ postgres: (config) => ({
6033
+ command: "npx",
6034
+ args: ["-y", "@modelcontextprotocol/server-postgres", config.connection_string]
6035
+ }),
6036
+ github: (config) => ({
6037
+ command: "npx",
6038
+ args: ["-y", "@modelcontextprotocol/server-github"],
6039
+ env: {
6040
+ GITHUB_PERSONAL_ACCESS_TOKEN: config.api_key
6041
+ }
6042
+ }),
6043
+ filesystem: (config) => ({
6044
+ command: "npx",
6045
+ args: ["-y", "@modelcontextprotocol/server-filesystem", ...config.paths || []]
6046
+ })
6047
+ };
6048
+ function addMcpServer(options) {
6049
+ const { name, transport, url, command, args = [], env = {}, scope = "user" } = options;
6050
+ const cliArgs = ["mcp", "add", "-s", scope, "-t", transport];
6051
+ for (const [key, value] of Object.entries(env)) {
6052
+ cliArgs.push("-e", `${key}=${value}`);
6053
+ }
6054
+ cliArgs.push(name);
6055
+ if (transport === "http" && url) {
6056
+ cliArgs.push(url);
6057
+ } else if (transport === "stdio" && command) {
6058
+ cliArgs.push("--", command, ...args);
6059
+ } else {
6060
+ return { success: false, error: "Invalid configuration: missing url or command" };
6061
+ }
6062
+ console.log(`[MCP] Running: claude ${cliArgs.join(" ")}`);
6063
+ const result = (0, import_child_process2.spawnSync)("claude", cliArgs, {
6064
+ encoding: "utf-8",
6065
+ stdio: ["pipe", "pipe", "pipe"]
6066
+ });
6067
+ if (result.status === 0) {
6068
+ return { success: true };
6069
+ } else {
6070
+ return {
6071
+ success: false,
6072
+ error: result.stderr || result.stdout || `Exit code ${result.status}`
6073
+ };
6074
+ }
6075
+ }
6076
+ var McpHandler = class {
6077
+ config;
6078
+ constructor(config) {
6079
+ this.config = {
6080
+ ...config,
6081
+ baseUrl: config.baseUrl || DEFAULT_BASE_URL3
6082
+ };
6083
+ }
6084
+ /**
6085
+ * Handle an MCP connect request event
6086
+ * Uses `claude mcp add` to configure the server dynamically
6087
+ */
6088
+ async handleConnectRequest(event) {
6089
+ const { connection_id, platform, config: mcpConfig } = event.data;
6090
+ try {
6091
+ const serverName = mcpConfig?.server_name || platform;
6092
+ const oauthServer = OAUTH_MCP_SERVERS[platform];
6093
+ if (oauthServer) {
6094
+ const result = addMcpServer({
6095
+ name: serverName,
6096
+ transport: "http",
6097
+ url: oauthServer.url
6098
+ });
6099
+ if (!result.success) {
6100
+ throw new Error(`Failed to add ${platform} MCP: ${result.error}`);
6101
+ }
6102
+ console.log(`[MCP] Added ${serverName} \u2192 ${oauthServer.url}`);
6103
+ console.log(`[MCP] OAuth will prompt when you use ${platform} tools`);
6104
+ } else {
6105
+ const configBuilder = CREDENTIAL_MCP_CONFIGS[platform];
6106
+ if (!configBuilder) {
6107
+ throw new Error(`Unsupported MCP platform: ${platform}. Supported: ${[...Object.keys(OAUTH_MCP_SERVERS), ...Object.keys(CREDENTIAL_MCP_CONFIGS)].join(", ")}`);
6108
+ }
6109
+ if (!mcpConfig) {
6110
+ throw new Error(`Platform ${platform} requires configuration (connection_string or api_key)`);
6111
+ }
6112
+ const serverConfig = configBuilder(mcpConfig);
6113
+ const result = addMcpServer({
6114
+ name: serverName,
6115
+ transport: "stdio",
6116
+ command: serverConfig.command,
6117
+ args: serverConfig.args,
6118
+ env: serverConfig.env
6119
+ });
6120
+ if (!result.success) {
6121
+ throw new Error(`Failed to add ${platform} MCP: ${result.error}`);
6122
+ }
6123
+ console.log(`[MCP] Added ${serverName} for ${platform}`);
6124
+ }
6125
+ await this.updateConnectionStatus(connection_id, {
6126
+ status: "active",
6127
+ mcp_configured: true
6128
+ });
6129
+ this.config.onConfigured?.(connection_id, platform);
6130
+ } catch (err) {
6131
+ console.error(`[MCP] Failed to configure ${platform}:`, err);
6132
+ await this.updateConnectionStatus(connection_id, {
6133
+ status: "error",
6134
+ mcp_configured: false,
6135
+ error_message: err.message
6136
+ });
6137
+ this.config.onError?.(err, connection_id);
6138
+ }
6139
+ }
6140
+ /**
6141
+ * Check if a platform supports OAuth (no credentials needed)
6142
+ */
6143
+ static supportsOAuth(platform) {
6144
+ return platform in OAUTH_MCP_SERVERS;
6145
+ }
6146
+ /**
6147
+ * Get list of platforms with OAuth support
6148
+ */
6149
+ static getOAuthPlatforms() {
6150
+ return Object.keys(OAUTH_MCP_SERVERS);
6151
+ }
6152
+ /**
6153
+ * Update connection status in Artyfacts
6154
+ */
6155
+ async updateConnectionStatus(connectionId, update) {
6156
+ try {
6157
+ const response = await fetch(`${this.config.baseUrl}/connections/${connectionId}`, {
6158
+ method: "PATCH",
6159
+ headers: {
6160
+ "Authorization": `Bearer ${this.config.apiKey}`,
6161
+ "Content-Type": "application/json"
6162
+ },
6163
+ body: JSON.stringify(update)
6164
+ });
6165
+ if (!response.ok) {
6166
+ console.error(`[MCP] Failed to update connection status: ${response.status}`);
6167
+ }
6168
+ } catch (err) {
6169
+ console.error("[MCP] Failed to update connection status:", err);
6170
+ }
6171
+ }
6172
+ /**
6173
+ * List configured MCP servers using `claude mcp list`
6174
+ */
6175
+ listServers() {
6176
+ const result = (0, import_child_process2.spawnSync)("claude", ["mcp", "list"], {
6177
+ encoding: "utf-8",
6178
+ stdio: ["pipe", "pipe", "pipe"]
6179
+ });
6180
+ if (result.status === 0 && result.stdout) {
6181
+ return result.stdout.trim().split("\n").filter(Boolean);
6182
+ }
6183
+ return [];
6184
+ }
6185
+ /**
6186
+ * Remove an MCP server using `claude mcp remove`
6187
+ */
6188
+ removeServer(serverName) {
6189
+ const result = (0, import_child_process2.spawnSync)("claude", ["mcp", "remove", serverName], {
6190
+ encoding: "utf-8",
6191
+ stdio: ["pipe", "pipe", "pipe"]
6192
+ });
6193
+ if (result.status === 0) {
6194
+ console.log(`[MCP] Removed server: ${serverName}`);
6195
+ return true;
6196
+ } else {
6197
+ console.error(`[MCP] Failed to remove ${serverName}: ${result.stderr || result.stdout}`);
6198
+ return false;
6199
+ }
6200
+ }
6201
+ };
6202
+ function createMcpHandler(config) {
6203
+ return new McpHandler(config);
6204
+ }
6013
6205
  // Annotate the CommonJS export names for ESM import in node:
6014
6206
  0 && (module.exports = {
6015
6207
  ArtyfactsListener,
6016
6208
  ClaudeExecutor,
6017
6209
  ClaudeExecutorWithTools,
6018
6210
  ContextFetcher,
6211
+ McpHandler,
6019
6212
  buildPromptWithContext,
6020
6213
  clearCredentials,
6021
6214
  createApiClient,
@@ -6023,6 +6216,7 @@ function createExecutorWithTools(config) {
6023
6216
  createExecutor,
6024
6217
  createExecutorWithTools,
6025
6218
  createListener,
6219
+ createMcpHandler,
6026
6220
  executeTool,
6027
6221
  getAllToolSchemas,
6028
6222
  getCredentials,
package/dist/index.mjs CHANGED
@@ -2,17 +2,19 @@ import {
2
2
  ArtyfactsListener,
3
3
  ClaudeExecutor,
4
4
  ContextFetcher,
5
+ McpHandler,
5
6
  buildPromptWithContext,
6
7
  clearCredentials,
7
8
  createContextFetcher,
8
9
  createExecutor,
9
10
  createListener,
11
+ createMcpHandler,
10
12
  getCredentials,
11
13
  loadCredentials,
12
14
  promptForApiKey,
13
15
  runDeviceAuth,
14
16
  saveCredentials
15
- } from "./chunk-G6HSQFKJ.mjs";
17
+ } from "./chunk-VGAUMCRW.mjs";
16
18
 
17
19
  // node_modules/@anthropic-ai/sdk/internal/tslib.mjs
18
20
  function __classPrivateFieldSet(receiver, state, value, kind, f) {
@@ -5179,6 +5181,7 @@ export {
5179
5181
  ClaudeExecutor,
5180
5182
  ClaudeExecutorWithTools,
5181
5183
  ContextFetcher,
5184
+ McpHandler,
5182
5185
  buildPromptWithContext,
5183
5186
  clearCredentials,
5184
5187
  createApiClient,
@@ -5186,6 +5189,7 @@ export {
5186
5189
  createExecutor,
5187
5190
  createExecutorWithTools,
5188
5191
  createListener,
5192
+ createMcpHandler,
5189
5193
  executeTool,
5190
5194
  getAllToolSchemas,
5191
5195
  getCredentials,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@artyfacts/claude",
3
- "version": "1.3.8",
3
+ "version": "1.3.10",
4
4
  "description": "Claude adapter for Artyfacts - Execute tasks using Claude Code CLI",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
package/src/cli.ts CHANGED
@@ -13,7 +13,8 @@ import { Command } from 'commander';
13
13
  import { execSync, spawnSync } from 'child_process';
14
14
  import { getCredentials, clearCredentials, loadCredentials, promptForApiKey } from './auth';
15
15
  import { ClaudeExecutor, createExecutor, TaskContext } from './executor';
16
- import { ArtyfactsListener, createListener, TaskAssignedEvent } from './listener';
16
+ import { ArtyfactsListener, createListener, TaskAssignedEvent, McpConnectRequestEvent } from './listener';
17
+ import { McpHandler, createMcpHandler } from './mcp';
17
18
 
18
19
  // ============================================================================
19
20
  // Constants
@@ -305,13 +306,21 @@ async function checkAndClaimTasks(
305
306
  });
306
307
 
307
308
  if (result.success) {
308
- await completeTask({
309
- baseUrl,
310
- apiKey,
311
- taskId: taskUuid, // Use UUID instead of section_id
312
- output: result.output,
313
- summary: result.summary,
314
- });
309
+ try {
310
+ await completeTask({
311
+ baseUrl,
312
+ apiKey,
313
+ taskId: taskUuid, // Use UUID instead of section_id
314
+ output: result.output,
315
+ summary: result.summary,
316
+ });
317
+ } catch (err) {
318
+ // Ignore "already complete" - means Claude completed it via MCP
319
+ const errMsg = err instanceof Error ? err.message : String(err);
320
+ if (!errMsg.includes('already complete')) {
321
+ throw err;
322
+ }
323
+ }
315
324
  console.log(` → ✅ Completed! ${result.summary}`);
316
325
  } else {
317
326
  console.log(` → ❌ Failed: ${result.error}`);
@@ -423,6 +432,25 @@ async function runAgent(options: {
423
432
  // Track active tasks to prevent duplicate processing
424
433
  const activeTasks = new Set<string>();
425
434
 
435
+ // Create MCP handler for connection requests
436
+ const mcpHandler = createMcpHandler({
437
+ apiKey: credentials.apiKey,
438
+ baseUrl: options.baseUrl,
439
+ onConfigured: (connectionId, platform) => {
440
+ console.log(`\n🔧 [MCP] Configured ${platform} connection: ${connectionId}`);
441
+ console.log(' ⚠️ Restart Claude Desktop to use the new MCP server');
442
+ },
443
+ onError: (error, connectionId) => {
444
+ console.error(`\n❌ [MCP] Failed to configure connection ${connectionId}:`, error.message);
445
+ },
446
+ });
447
+
448
+ // Handle MCP connection requests
449
+ listener.on<McpConnectRequestEvent>('mcp_connect_request', async (event) => {
450
+ console.log(`\n🔗 [MCP] Connection request for ${event.data.platform}`);
451
+ await mcpHandler.handleConnectRequest(event);
452
+ });
453
+
426
454
  // Handle task assignments
427
455
  listener.on<TaskAssignedEvent>('task_assigned', async (event) => {
428
456
  const task = event.data;
@@ -460,14 +488,22 @@ async function runAgent(options: {
460
488
  const result = await executor!.execute(taskContext);
461
489
 
462
490
  if (result.success) {
463
- // Complete task via API
464
- await completeTask({
465
- baseUrl: options.baseUrl,
466
- apiKey: credentials.apiKey,
467
- taskId: task.taskId,
468
- output: result.output,
469
- summary: result.summary,
470
- });
491
+ // Complete task via API (Claude may have already done this via MCP tool)
492
+ try {
493
+ await completeTask({
494
+ baseUrl: options.baseUrl,
495
+ apiKey: credentials.apiKey,
496
+ taskId: task.taskId,
497
+ output: result.output,
498
+ summary: result.summary,
499
+ });
500
+ } catch (err) {
501
+ // Ignore "already complete" - means Claude completed it via MCP
502
+ const errMsg = err instanceof Error ? err.message : String(err);
503
+ if (!errMsg.includes('already complete')) {
504
+ throw err;
505
+ }
506
+ }
471
507
 
472
508
  console.log(` → ✅ Completed! ${result.summary}`);
473
509
  } else {
@@ -491,9 +527,19 @@ async function runAgent(options: {
491
527
  // Start listening
492
528
  listener.connect();
493
529
 
530
+ // Poll for tasks periodically (every 30 seconds) as a backup to SSE
531
+ // This catches tasks that were created before we connected or if SSE events are missed
532
+ const POLL_INTERVAL_MS = 30_000;
533
+ const pollInterval = setInterval(() => {
534
+ if (!options.dryRun) {
535
+ checkAndClaimTasks(options.baseUrl, credentials.apiKey, credentials.agentId, activeTasks, executor!, options.dryRun);
536
+ }
537
+ }, POLL_INTERVAL_MS);
538
+
494
539
  // Handle shutdown
495
540
  const shutdown = () => {
496
541
  console.log('\n👋 Disconnecting...');
542
+ clearInterval(pollInterval);
497
543
  listener.disconnect();
498
544
  process.exit(0);
499
545
  };
package/src/index.ts CHANGED
@@ -73,6 +73,7 @@ export type {
73
73
  TaskAssignedEvent,
74
74
  HeartbeatEvent,
75
75
  ConnectedEvent,
76
+ McpConnectRequestEvent,
76
77
  ArtyfactsEvent,
77
78
  EventCallback,
78
79
  ListenerConfig,
@@ -135,3 +136,16 @@ export type {
135
136
  ToolCall,
136
137
  ToolCallResult,
137
138
  } from './tools';
139
+
140
+ // MCP Handler - Types
141
+ export type {
142
+ McpServerConfig,
143
+ ClaudeSettingsJson,
144
+ McpHandlerConfig,
145
+ } from './mcp';
146
+
147
+ // MCP Handler - Classes
148
+ export {
149
+ McpHandler,
150
+ createMcpHandler,
151
+ } from './mcp';
package/src/listener.ts CHANGED
@@ -27,6 +27,21 @@ export interface TaskAssignedEvent {
27
27
  };
28
28
  }
29
29
 
30
+ export interface McpConnectRequestEvent {
31
+ type: 'mcp_connect_request';
32
+ timestamp: string;
33
+ data: {
34
+ connection_id: string;
35
+ platform: string;
36
+ config: {
37
+ server_name?: string;
38
+ connection_string?: string;
39
+ api_key?: string;
40
+ [key: string]: unknown;
41
+ };
42
+ };
43
+ }
44
+
30
45
  export interface HeartbeatEvent {
31
46
  type: 'heartbeat';
32
47
  timestamp: string;
@@ -40,7 +55,7 @@ export interface ConnectedEvent {
40
55
  };
41
56
  }
42
57
 
43
- export type ArtyfactsEvent = TaskAssignedEvent | HeartbeatEvent | ConnectedEvent | {
58
+ export type ArtyfactsEvent = TaskAssignedEvent | HeartbeatEvent | ConnectedEvent | McpConnectRequestEvent | {
44
59
  type: string;
45
60
  timestamp: string;
46
61
  data?: Record<string, unknown>;
@@ -77,6 +92,8 @@ const EVENT_TYPES = [
77
92
  'task_unblocked',
78
93
  'blocker_resolved',
79
94
  'notification',
95
+ 'mcp_connect_request',
96
+ 'connection_status',
80
97
  ] as const;
81
98
 
82
99
  // ============================================================================