conductor-figma 3.0.2 → 3.0.3
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/package.json +1 -1
- package/src/server.js +59 -45
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "conductor-figma",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "Design-intelligent MCP server for Figma. 201 design-intelligent tools for Figma. Every tool knows typography, spacing, color, accessibility. Not a shape proxy — a design engine.",
|
|
5
5
|
"author": "0xDragoon",
|
|
6
6
|
"license": "MIT",
|
package/src/server.js
CHANGED
|
@@ -6,96 +6,110 @@ import { TOOL_LIST, TOOL_COUNT, getTool, CATEGORIES } from './tools/registry.js'
|
|
|
6
6
|
import { handleTool } from './tools/handlers.js'
|
|
7
7
|
import { createBridge } from './bridge.js'
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
var VERSION = '3.0.2'
|
|
10
|
+
var bridge = null
|
|
11
|
+
var bridgeStarted = false
|
|
12
12
|
|
|
13
|
-
function log(
|
|
14
|
-
|
|
13
|
+
function log() { process.stderr.write('[conductor] ' + Array.prototype.join.call(arguments, ' ') + '\n') }
|
|
14
|
+
|
|
15
|
+
function ensureBridge() {
|
|
16
|
+
if (!bridgeStarted) {
|
|
17
|
+
bridgeStarted = true
|
|
18
|
+
bridge = createBridge()
|
|
19
|
+
bridge.start()
|
|
20
|
+
}
|
|
21
|
+
return bridge
|
|
22
|
+
}
|
|
15
23
|
|
|
16
24
|
// ─── JSON-RPC over stdio ───
|
|
17
|
-
|
|
25
|
+
var buffer = ''
|
|
18
26
|
|
|
19
27
|
process.stdin.setEncoding('utf8')
|
|
20
|
-
process.stdin.on('data', chunk
|
|
28
|
+
process.stdin.on('data', function(chunk) {
|
|
21
29
|
buffer += chunk
|
|
22
30
|
while (true) {
|
|
23
|
-
|
|
31
|
+
var headerEnd = buffer.indexOf('\r\n\r\n')
|
|
24
32
|
if (headerEnd === -1) break
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
var header = buffer.slice(0, headerEnd)
|
|
34
|
+
var match = header.match(/Content-Length:\s*(\d+)/i)
|
|
27
35
|
if (!match) { buffer = buffer.slice(headerEnd + 4); continue }
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
var len = parseInt(match[1])
|
|
37
|
+
var bodyStart = headerEnd + 4
|
|
30
38
|
if (buffer.length < bodyStart + len) break
|
|
31
|
-
|
|
39
|
+
var body = buffer.slice(bodyStart, bodyStart + len)
|
|
32
40
|
buffer = buffer.slice(bodyStart + len)
|
|
33
41
|
try {
|
|
34
|
-
|
|
42
|
+
var msg = JSON.parse(body)
|
|
35
43
|
handleMessage(msg)
|
|
36
44
|
} catch (e) { log('Parse error:', e.message) }
|
|
37
45
|
}
|
|
38
46
|
})
|
|
39
47
|
|
|
40
48
|
function send(msg) {
|
|
41
|
-
|
|
42
|
-
|
|
49
|
+
var json = JSON.stringify(msg)
|
|
50
|
+
var out = 'Content-Length: ' + Buffer.byteLength(json) + '\r\n\r\n' + json
|
|
43
51
|
process.stdout.write(out)
|
|
44
52
|
}
|
|
45
53
|
|
|
46
|
-
function respond(id, result) { send({ jsonrpc: '2.0', id, result }) }
|
|
47
|
-
function respondError(id, code, message) { send({ jsonrpc: '2.0', id, error: { code, message } }) }
|
|
54
|
+
function respond(id, result) { send({ jsonrpc: '2.0', id: id, result: result }) }
|
|
55
|
+
function respondError(id, code, message) { send({ jsonrpc: '2.0', id: id, error: { code: code, message: message } }) }
|
|
48
56
|
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
function handleMessage(msg) {
|
|
58
|
+
var id = msg.id
|
|
59
|
+
var method = msg.method
|
|
60
|
+
var params = msg.params
|
|
51
61
|
|
|
52
62
|
switch (method) {
|
|
53
63
|
case 'initialize':
|
|
54
|
-
|
|
64
|
+
// Start bridge in background, respond immediately
|
|
65
|
+
ensureBridge()
|
|
66
|
+
log('Conductor v' + VERSION + ' ready — ' + TOOL_COUNT + ' tools')
|
|
67
|
+
respond(id, {
|
|
55
68
|
protocolVersion: '2024-11-05',
|
|
56
69
|
capabilities: { tools: { listChanged: false } },
|
|
57
70
|
serverInfo: { name: 'conductor-figma', version: VERSION },
|
|
58
71
|
})
|
|
72
|
+
return
|
|
59
73
|
|
|
60
74
|
case 'notifications/initialized':
|
|
61
|
-
log(`Conductor v${VERSION} ready — ${TOOL_COUNT} tools across ${Object.keys(CATEGORIES).length} categories`)
|
|
62
75
|
return
|
|
63
76
|
|
|
64
77
|
case 'tools/list':
|
|
65
|
-
|
|
66
|
-
tools: TOOL_LIST.map(t
|
|
67
|
-
name: t.name,
|
|
68
|
-
|
|
69
|
-
inputSchema: t.inputSchema,
|
|
70
|
-
}))
|
|
78
|
+
respond(id, {
|
|
79
|
+
tools: TOOL_LIST.map(function(t) {
|
|
80
|
+
return { name: t.name, description: t.description, inputSchema: t.inputSchema }
|
|
81
|
+
})
|
|
71
82
|
})
|
|
83
|
+
return
|
|
72
84
|
|
|
73
|
-
case 'tools/call':
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
85
|
+
case 'tools/call':
|
|
86
|
+
var name = params.name
|
|
87
|
+
var args = params.arguments || {}
|
|
88
|
+
var tool = getTool(name)
|
|
89
|
+
if (!tool) { respondError(id, -32601, 'Unknown tool: ' + name); return }
|
|
77
90
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
91
|
+
var b = ensureBridge()
|
|
92
|
+
handleTool(name, args, b).then(function(result) {
|
|
93
|
+
respond(id, {
|
|
81
94
|
content: [{ type: 'text', text: typeof result === 'string' ? result : JSON.stringify(result, null, 2) }]
|
|
82
95
|
})
|
|
83
|
-
}
|
|
84
|
-
log(
|
|
85
|
-
|
|
86
|
-
content: [{ type: 'text', text:
|
|
96
|
+
}).catch(function(e) {
|
|
97
|
+
log('Tool error [' + name + ']:', e.message)
|
|
98
|
+
respond(id, {
|
|
99
|
+
content: [{ type: 'text', text: 'Error: ' + e.message }],
|
|
87
100
|
isError: true,
|
|
88
101
|
})
|
|
89
|
-
}
|
|
90
|
-
|
|
102
|
+
})
|
|
103
|
+
return
|
|
91
104
|
|
|
92
105
|
case 'ping':
|
|
93
|
-
|
|
106
|
+
respond(id, {})
|
|
107
|
+
return
|
|
94
108
|
|
|
95
109
|
default:
|
|
96
|
-
if (id) respondError(id, -32601,
|
|
110
|
+
if (id) respondError(id, -32601, 'Unknown method: ' + method)
|
|
97
111
|
}
|
|
98
112
|
}
|
|
99
113
|
|
|
100
|
-
process.on('SIGINT', ()
|
|
101
|
-
process.on('SIGTERM', ()
|
|
114
|
+
process.on('SIGINT', function() { if (bridge) bridge.stop(); process.exit(0) })
|
|
115
|
+
process.on('SIGTERM', function() { if (bridge) bridge.stop(); process.exit(0) })
|