gum-jsx 1.3.0 → 1.3.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/package.json +7 -5
- package/scripts/mcp.js +52 -0
- package/src/render.js +2 -2
- package/scripts/server.js +0 -69
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gum-jsx",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Language for vector graphics generation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Douglas Hanley",
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"claude": "bun ./scripts/skill.js && bun ./scripts/claude.ts"
|
|
54
54
|
},
|
|
55
55
|
"bin": {
|
|
56
|
-
"gum": "./scripts/cli.js",
|
|
57
|
-
"gum-
|
|
56
|
+
"gum-cli": "./scripts/cli.js",
|
|
57
|
+
"gum-mcp": "./scripts/mcp.js"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"acorn-jsx": "^5.3.2",
|
|
@@ -64,10 +64,12 @@
|
|
|
64
64
|
},
|
|
65
65
|
"optionalDependencies": {
|
|
66
66
|
"@mathjax/src": "^4.1.0",
|
|
67
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
67
68
|
"@resvg/resvg-js": "^2.6.2",
|
|
68
|
-
"commander": "^14.0.
|
|
69
|
+
"commander": "^14.0.3",
|
|
69
70
|
"express": "^5.2.1",
|
|
70
|
-
"mathjax": "^4.1.0"
|
|
71
|
+
"mathjax": "^4.1.0",
|
|
72
|
+
"zod": "^4.3.6"
|
|
71
73
|
},
|
|
72
74
|
"devDependencies": {
|
|
73
75
|
"@anthropic-ai/sdk": "^0.72.1"
|
package/scripts/mcp.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#! /usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
4
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
5
|
+
import * as z from 'zod/v4'
|
|
6
|
+
|
|
7
|
+
import { evaluateGum } from '../src/eval.js'
|
|
8
|
+
import { rasterizeSvg } from '../src/render.js'
|
|
9
|
+
|
|
10
|
+
const mcpServer = new McpServer({
|
|
11
|
+
name: 'gum-jsx',
|
|
12
|
+
version: '1.0.0'
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
mcpServer.registerTool(
|
|
16
|
+
'render',
|
|
17
|
+
{
|
|
18
|
+
description: 'Render gum.jsx code to PNG',
|
|
19
|
+
inputSchema: {
|
|
20
|
+
code: z.string().describe('gum.jsx code to render')
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
async ({ code }) => {
|
|
24
|
+
try {
|
|
25
|
+
const elem = evaluateGum(code, { size: [1000, 750] })
|
|
26
|
+
const svg = elem.svg()
|
|
27
|
+
const png = rasterizeSvg(svg, { size: elem.size, background: 'white' })
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
content: [
|
|
31
|
+
{ type: 'image', mimeType: 'image/png', data: png.toString('base64') }
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return {
|
|
36
|
+
content: [
|
|
37
|
+
{ type: 'text', text: error.message }
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
async function main() {
|
|
45
|
+
const transport = new StdioServerTransport()
|
|
46
|
+
await mcpServer.connect(transport)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
main().catch(error => {
|
|
50
|
+
console.error('Server error:', error)
|
|
51
|
+
process.exit(1)
|
|
52
|
+
})
|
package/src/render.js
CHANGED
|
@@ -33,7 +33,7 @@ function buildFitTo(width, height) {
|
|
|
33
33
|
|
|
34
34
|
// rasterize SVG buffer/string to PNG
|
|
35
35
|
function rasterizeSvg(svg, opts = {}) {
|
|
36
|
-
let { size, width, height } = opts
|
|
36
|
+
let { size, width, height, background } = opts
|
|
37
37
|
|
|
38
38
|
// scale down intrinsic height
|
|
39
39
|
if (size != null && width != null && height != null) {
|
|
@@ -47,7 +47,7 @@ function rasterizeSvg(svg, opts = {}) {
|
|
|
47
47
|
|
|
48
48
|
// pass to resvg
|
|
49
49
|
const fitTo = buildFitTo(width, height)
|
|
50
|
-
const resvg = new Resvg(svg, { fitTo, font })
|
|
50
|
+
const resvg = new Resvg(svg, { fitTo, font, background })
|
|
51
51
|
return resvg.render().asPng()
|
|
52
52
|
}
|
|
53
53
|
|
package/scripts/server.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
#! /usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
import express from 'express'
|
|
4
|
-
import { program } from 'commander'
|
|
5
|
-
import { evaluateGum } from '../src/eval.js'
|
|
6
|
-
import { ErrorNoCode, ErrorNoReturn, ErrorNoElement } from '../src/eval.js'
|
|
7
|
-
|
|
8
|
-
function parseError(error) {
|
|
9
|
-
if (error instanceof ErrorNoCode) {
|
|
10
|
-
return 'ERR_NOCODE: No code provided'
|
|
11
|
-
} else if (error instanceof ErrorNoReturn) {
|
|
12
|
-
return 'ERR_NORETURN: No return value'
|
|
13
|
-
} else if (error instanceof ErrorNoElement) {
|
|
14
|
-
return `ERR_NOELEMENT: Return value ${JSON.stringify(error.value)}`
|
|
15
|
-
} else {
|
|
16
|
-
return `ERR_EVALUATE: ${error.message}`
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// get host and port args from cli
|
|
21
|
-
program
|
|
22
|
-
.option('-h, --host <host>', 'host to listen on', 'localhost')
|
|
23
|
-
.option('-p, --port <port>', 'port to listen on', v => parseInt(v, 10), 3000)
|
|
24
|
-
.parse()
|
|
25
|
-
const { host, port } = program.opts()
|
|
26
|
-
|
|
27
|
-
// create express app
|
|
28
|
-
const app = express()
|
|
29
|
-
app.use(express.raw({ type: '*/*', limit: '1mb' }));
|
|
30
|
-
|
|
31
|
-
// convert buffer to string for text-based routes
|
|
32
|
-
app.use((req, res, next) => {
|
|
33
|
-
if (req.method === 'POST' && Buffer.isBuffer(req.body)) {
|
|
34
|
-
req.body = req.body.toString('utf8')
|
|
35
|
-
}
|
|
36
|
-
next()
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
// status message
|
|
40
|
-
app.get('/', (req, res) => {
|
|
41
|
-
res.send('GUM')
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
// eval gum jsx to svg
|
|
45
|
-
app.post('/', (req, res) => {
|
|
46
|
-
// get params
|
|
47
|
-
const code = req.body
|
|
48
|
-
const size0 = parseInt(req.query.size ?? 500)
|
|
49
|
-
const theme = req.query.theme ?? 'light'
|
|
50
|
-
|
|
51
|
-
// evaluate code and return svg
|
|
52
|
-
let svg
|
|
53
|
-
try {
|
|
54
|
-
const elem = evaluateGum(code, { size: size0, theme })
|
|
55
|
-
svg = elem.svg()
|
|
56
|
-
} catch (error) {
|
|
57
|
-
const message = parseError(error)
|
|
58
|
-
return res.status(500).send(message)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// send svg
|
|
62
|
-
res.setHeader('Content-Type', 'image/svg+xml')
|
|
63
|
-
res.send(svg)
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
// start server
|
|
67
|
-
app.listen(port, host, () => {
|
|
68
|
-
// console.log(`Server running on http://${host}:${port}`)
|
|
69
|
-
})
|