Skip to content

Commit 2e79f63

Browse files
committed
Add features
1 parent d739114 commit 2e79f63

File tree

12 files changed

+1227
-98
lines changed

12 files changed

+1227
-98
lines changed

CONTEXT.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,15 @@ An MCP server that provides JavaScript execution capabilities with Node.js-like
66

77
### executeJS
88
- Execute JavaScript code with full Node.js-like environment
9-
- Includes: console, fs, http, timers (setTimeout/setInterval), process, and require
9+
- Includes: console, fs, http, fetch (with promises), timers (setTimeout/setInterval), process, and require
1010
- Parameters: `code` (required): JavaScript code to execute
11+
- Modules are configurable via CLI flags
12+
13+
## CLI Usage
14+
- `codebench-mcp` - Run with all modules enabled
15+
- `codebench-mcp --enabled-modules console,fs,timers` - Enable only specific modules
16+
- `codebench-mcp --disabled-modules http,fetch` - Disable specific modules
17+
- Available modules: console, fs, http, fetch, timers, process, require
1118

1219
## Build/Test Commands
1320
- `go build ./...` - Build all packages

README.md

Lines changed: 127 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ The `executeJS` tool provides:
99
- **Console API**: `console.log()`, `console.error()`, `console.warn()`
1010
- **File System**: `fs.readFileSync()`, `fs.writeFileSync()`, `fs.existsSync()`
1111
- **HTTP Server**: `http.createServer()` with request/response handling
12+
- **Fetch API**: `fetch()` with Promise support for HTTP requests
1213
- **Timers**: `setTimeout()`, `clearTimeout()`, `setInterval()`, `clearInterval()`
1314
- **Process**: `process.argv`, `process.cwd()`, `process.exit()`, `process.env`
1415
- **Module System**: `require()` for loading JavaScript modules
@@ -31,6 +32,30 @@ go install github.com/mark3labs/codebench-mcp@latest
3132
codebench-mcp
3233
```
3334

35+
#### With module configuration
36+
37+
```bash
38+
# Enable only specific modules
39+
codebench-mcp --enabled-modules console,fs,timers
40+
41+
# Disable specific modules (enable all others)
42+
codebench-mcp --disabled-modules http,fetch
43+
44+
# Show help
45+
codebench-mcp --help
46+
```
47+
48+
**Available modules:**
49+
- `console` - Console logging (console.log, console.error, console.warn)
50+
- `fs` - File system operations (fs.readFileSync, fs.writeFileSync, fs.existsSync)
51+
- `http` - HTTP server creation (http.createServer)
52+
- `fetch` - HTTP client requests (fetch API with promises)
53+
- `timers` - Timer functions (setTimeout, setInterval, clearTimeout, clearInterval)
54+
- `process` - Process information (process.argv, process.cwd, process.env, process.exit)
55+
- `require` - Module loading system
56+
57+
**Note:** The `executeJS` tool description dynamically updates to show only the enabled modules and includes detailed information about what each module provides. This helps users understand exactly what JavaScript APIs are available in the simplified VM environment.
58+
3459
#### As a library in your Go project
3560

3661
```go
@@ -57,6 +82,76 @@ func main() {
5782
}
5883
```
5984

85+
#### Using InProcessTransport
86+
87+
```go
88+
package main
89+
90+
import (
91+
"context"
92+
"log"
93+
94+
"github.com/mark3labs/codebench-mcp/jsserver"
95+
"github.com/mark3labs/mcp-go/client"
96+
"github.com/mark3labs/mcp-go/mcp"
97+
)
98+
99+
func main() {
100+
// Create the JS server
101+
jsServer, err := jsserver.NewJSServer()
102+
if err != nil {
103+
log.Fatalf("Failed to create server: %v", err)
104+
}
105+
106+
// Create an in-process client
107+
mcpClient, err := client.NewInProcessClient(jsServer)
108+
if err != nil {
109+
log.Fatalf("Failed to create client: %v", err)
110+
}
111+
defer mcpClient.Close()
112+
113+
// Start the client
114+
if err := mcpClient.Start(context.Background()); err != nil {
115+
log.Fatalf("Failed to start client: %v", err)
116+
}
117+
118+
// Initialize the client
119+
initRequest := mcp.InitializeRequest{}
120+
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
121+
initRequest.Params.ClientInfo = mcp.Implementation{
122+
Name: "my-app",
123+
Version: "1.0.0",
124+
}
125+
_, err = mcpClient.Initialize(context.Background(), initRequest)
126+
if err != nil {
127+
log.Fatalf("Failed to initialize: %v", err)
128+
}
129+
130+
// Execute JavaScript code
131+
callRequest := mcp.CallToolRequest{}
132+
callRequest.Params.Name = "executeJS"
133+
callRequest.Params.Arguments = map[string]any{
134+
"code": `
135+
console.log("Hello from JavaScript!");
136+
const result = Math.sqrt(16);
137+
console.log("Square root of 16 is:", result);
138+
result;
139+
`,
140+
}
141+
142+
result, err := mcpClient.CallTool(context.Background(), callRequest)
143+
if err != nil {
144+
log.Fatalf("Failed to call tool: %v", err)
145+
}
146+
147+
if result.IsError {
148+
log.Printf("JavaScript execution error: %v", result.Content)
149+
} else {
150+
log.Printf("JavaScript execution result: %v", result.Content)
151+
}
152+
}
153+
```
154+
60155
### Usage with Model Context Protocol
61156

62157
To integrate this server with apps that support MCP:
@@ -119,11 +214,40 @@ fs.writeFileSync("test.txt", "Hello from JS!");
119214
const content = fs.readFileSync("test.txt");
120215
console.log("File content:", content);
121216

122-
// HTTP server
217+
// Fetch API with promises
218+
fetch("https://github.com/api/users/octocat")
219+
.then(response => response.json())
220+
.then(data => {
221+
console.log("User:", data.name);
222+
console.log("Public repos:", data.public_repos);
223+
})
224+
.catch(error => console.error("Fetch error:", error));
225+
226+
// HTTP server with configurable ports and callbacks
123227
const server = http.createServer((req, res) => {
124-
res.end("Hello from HTTP server!");
228+
console.log(`${req.method} ${req.url}`);
229+
230+
res.setHeader("Content-Type", "application/json");
231+
res.writeHead(200);
232+
res.end(JSON.stringify({
233+
message: "Hello from HTTP server!",
234+
method: req.method,
235+
url: req.url
236+
}));
237+
});
238+
239+
// Multiple ways to start the server:
240+
server.listen(3000); // Port only
241+
server.listen(3000, () => console.log("Started!")); // Port + callback
242+
server.listen(3000, "localhost"); // Port + host
243+
server.listen(3000, "localhost", () => { // Port + host + callback
244+
console.log("Server running on localhost:3000");
125245
});
126-
server.listen(8080);
246+
247+
// Server management
248+
setTimeout(() => {
249+
server.close(); // Gracefully shutdown server
250+
}, 10000);
127251

128252
// Timers
129253
setTimeout(() => {

cmd/root.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
"slices"
8+
"strings"
9+
10+
"github.com/mark3labs/codebench-mcp/jsserver"
11+
"github.com/mark3labs/mcp-go/server"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
var (
16+
enabledModules []string
17+
disabledModules []string
18+
)
19+
20+
// Available modules
21+
var availableModules = []string{
22+
"console",
23+
"fs",
24+
"http",
25+
"fetch",
26+
"timers",
27+
"process",
28+
"require",
29+
}
30+
31+
// rootCmd represents the base command when called without any subcommands
32+
var rootCmd = &cobra.Command{
33+
Use: "codebench-mcp",
34+
Short: "JavaScript Executor MCP Server",
35+
Long: `A Model Context Protocol (MCP) server that provides JavaScript execution capabilities
36+
with a Node.js-like environment including console, fs, http, fetch, timers, process, and require modules.`,
37+
Run: func(cmd *cobra.Command, args []string) {
38+
// Validate module configuration
39+
if len(enabledModules) > 0 && len(disabledModules) > 0 {
40+
log.Fatal("Error: --enabled-modules and --disabled-modules are mutually exclusive")
41+
}
42+
43+
// Determine which modules to enable
44+
var modulesToEnable []string
45+
if len(enabledModules) > 0 {
46+
// Only enable specified modules
47+
for _, module := range enabledModules {
48+
if !slices.Contains(availableModules, module) {
49+
log.Fatalf("Error: unknown module '%s'. Available modules: %s",
50+
module, strings.Join(availableModules, ", "))
51+
}
52+
}
53+
modulesToEnable = enabledModules
54+
} else if len(disabledModules) > 0 {
55+
// Enable all modules except disabled ones
56+
for _, module := range disabledModules {
57+
if !slices.Contains(availableModules, module) {
58+
log.Fatalf("Error: unknown module '%s'. Available modules: %s",
59+
module, strings.Join(availableModules, ", "))
60+
}
61+
}
62+
for _, module := range availableModules {
63+
if !slices.Contains(disabledModules, module) {
64+
modulesToEnable = append(modulesToEnable, module)
65+
}
66+
}
67+
} else {
68+
// Enable all modules by default
69+
modulesToEnable = availableModules
70+
}
71+
72+
// Create server with module configuration
73+
config := jsserver.ModuleConfig{
74+
EnabledModules: modulesToEnable,
75+
}
76+
77+
jss, err := jsserver.NewJSServerWithConfig(config)
78+
if err != nil {
79+
log.Fatalf("Failed to create server: %v", err)
80+
}
81+
82+
// Serve requests
83+
if err := server.ServeStdio(jss); err != nil {
84+
log.Fatalf("Server error: %v", err)
85+
}
86+
},
87+
}
88+
89+
// Execute adds all child commands to the root command and sets flags appropriately.
90+
func Execute() {
91+
err := rootCmd.Execute()
92+
if err != nil {
93+
os.Exit(1)
94+
}
95+
}
96+
97+
func init() {
98+
rootCmd.Flags().StringSliceVar(&enabledModules, "enabled-modules", nil,
99+
fmt.Sprintf("Comma-separated list of modules to enable. Available: %s",
100+
strings.Join(availableModules, ", ")))
101+
rootCmd.Flags().StringSliceVar(&disabledModules, "disabled-modules", nil,
102+
fmt.Sprintf("Comma-separated list of modules to disable. Available: %s",
103+
strings.Join(availableModules, ", ")))
104+
105+
rootCmd.MarkFlagsMutuallyExclusive("enabled-modules", "disabled-modules")
106+
}

codebench-mcp

1.84 MB
Binary file not shown.

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.23.10
55
require (
66
github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd
77
github.com/mark3labs/mcp-go v0.31.0
8+
github.com/spf13/cobra v1.9.1
89
github.com/stretchr/testify v1.9.0
910
)
1011

@@ -14,8 +15,10 @@ require (
1415
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
1516
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 // indirect
1617
github.com/google/uuid v1.6.0 // indirect
18+
github.com/inconshreveable/mousetrap v1.1.0 // indirect
1719
github.com/pmezard/go-difflib v1.0.0 // indirect
1820
github.com/spf13/cast v1.7.1 // indirect
21+
github.com/spf13/pflag v1.0.6 // indirect
1922
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
2023
golang.org/x/text v0.20.0 // indirect
2124
gopkg.in/yaml.v3 v3.0.1 // indirect

go.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
22
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
3+
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
34
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
45
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
56
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
@@ -16,6 +17,8 @@ github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUp
1617
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
1718
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
1819
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
20+
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
21+
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
1922
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
2023
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
2124
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -26,8 +29,13 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
2629
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2730
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
2831
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
32+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
2933
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
3034
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
35+
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
36+
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
37+
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
38+
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
3139
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
3240
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
3341
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=

0 commit comments

Comments
 (0)