Skip to content

Commit eb764f9

Browse files
authored
Merge pull request #3 from selesy/feat/mcp-tools
feat(gen): expand generator to create schemas and tools
2 parents 8120add + 5320e1f commit eb764f9

File tree

12 files changed

+129
-116
lines changed

12 files changed

+129
-116
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import (
77

88
"github.com/lmittmann/tint"
99

10-
"github.com/selesy/ethereum-mcp/gen/schema/internal/generator"
11-
"github.com/selesy/ethereum-mcp/gen/schema/internal/scraper"
10+
"github.com/selesy/ethereum-mcp/gen/internal/generator"
11+
"github.com/selesy/ethereum-mcp/gen/internal/scraper"
1212
)
1313

1414
// Run scrapes Ethereum execution APIs and generates complete JSONSchemas
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Package generator writes JSONSchema method files, creates a Go embed
2+
// for each one as well as a map of method name to schema source.
3+
package generator
4+
5+
import (
6+
"context"
7+
"encoding/json"
8+
"log/slog"
9+
"os"
10+
"path/filepath"
11+
"strings"
12+
13+
"github.com/dave/jennifer/jen"
14+
15+
"github.com/selesy/ethereum-mcp/pkg/openrpc"
16+
)
17+
18+
// Generator writes JSONSchema files and Go code for each OpenRPC method
19+
// defined by the Ethereum execution APIs.
20+
type Generator struct {
21+
log *slog.Logger
22+
}
23+
24+
// New creates a new generator.
25+
func New(log *slog.Logger) *Generator {
26+
return &Generator{
27+
log: log,
28+
}
29+
}
30+
31+
// Run executes the Generator with the provided context and list of
32+
// methods.
33+
func (g *Generator) Run(ctx context.Context, methods []openrpc.Method) error {
34+
if err := os.Mkdir(filepath.Join("schema", "data"), 0o750); err != nil && !os.IsExist(err) {
35+
return err
36+
}
37+
38+
schemaFile := jen.NewFile("schema")
39+
schemaFile.HeaderComment("Code generated by /gen. DO NOT EDIT.")
40+
schemaFile.Anon("embed")
41+
schemaDict := jen.Dict{}
42+
43+
if err := os.Mkdir("mcp", 0o750); err != nil && !os.IsExist(err) {
44+
return err
45+
}
46+
47+
toolFile := jen.NewFile("mcp")
48+
toolFile.HeaderComment("Code generated by /gen. DO NOT EDIT.")
49+
toolDict := jen.Dict{}
50+
51+
for _, method := range methods {
52+
goName := goName(method.Name())
53+
goDescr := goDescription(method.Description())
54+
55+
// Save the method schemas as formatted JSON files
56+
data, err := json.MarshalIndent(method, "", " ")
57+
if err != nil {
58+
return err
59+
}
60+
61+
data = append(data, '\n')
62+
63+
if err := os.WriteFile(filepath.Join("schema", "data", method.Name()+".json"), data, 0o600); err != nil {
64+
return err
65+
}
66+
67+
// Create an embed variable for each method
68+
schemaFile.Comment(goName + "Schema " + goDescr)
69+
schemaFile.Comment("//go:embed " + filepath.Join("data", method.Name()+".json"))
70+
schemaFile.Var().Id(goName+"Schema").Qual("encoding/json", "RawMessage")
71+
72+
// Add the name/json.RawMessage to the lookup table
73+
schemaDict[jen.Lit(method.Name())] = jen.Id(goName + "Schema")
74+
75+
// Create a tool for the each method
76+
toolFile.Comment(goName + "Tool is an mcp.Tool that " + goDescr)
77+
toolFile.Var().Id(goName+"Tool").Op("=").Qual("github.com/mark3labs/mcp-go/mcp", "NewToolWithRawSchema").Call(
78+
jen.Lit(method.Name()),
79+
jen.Lit(method.Description()),
80+
jen.Qual("github.com/selesy/ethereum-mcp/pkg/schema", goName+"Schema"),
81+
)
82+
83+
// Add the name/mcp.Tool to a lookup table
84+
toolDict[jen.Lit(method.Name())] = jen.Id(goName + "Tool")
85+
}
86+
87+
schemaFile.Comment("Schemas returns a map relating method names to the associated JSONSchema.")
88+
schemaFile.Func().Id("Schemas").Params().Map(jen.String()).Qual("encoding/json", "RawMessage").Block(
89+
jen.Return(jen.Map(jen.String()).Qual("encoding/json", "RawMessage").Values(schemaDict)),
90+
)
91+
92+
if err := schemaFile.Save(filepath.Join("schema", "schema_gen.go")); err != nil {
93+
return err
94+
}
95+
96+
g.log.Info("Generated schema/schema_gen.go file")
97+
98+
toolFile.Comment("Tools returns a map relating method names to the associated tool.")
99+
toolFile.Func().Id("Tools").Params().Map(jen.String()).Qual("github.com/mark3labs/mcp-go/mcp", "Tool").Block(
100+
jen.Return(jen.Map(jen.String()).Qual("github.com/mark3labs/mcp-go/mcp", "Tool").Values(toolDict)),
101+
)
102+
103+
if err := toolFile.Save(filepath.Join("mcp", "tool_gen.go")); err != nil {
104+
return err
105+
}
106+
107+
g.log.Info("Generated mcp/tool_gen.go file")
108+
109+
return nil
110+
}
111+
112+
func goName(name string) string {
113+
tkns := strings.Split(name, "_")
114+
115+
for i, tkn := range tkns {
116+
tkns[i] = strings.ToUpper(string(tkn[0])) + tkn[1:]
117+
}
118+
119+
return strings.Join(tkns, "")
120+
}
121+
122+
func goDescription(descr string) string {
123+
return strings.ToLower(string(descr[0])) + descr[1:]
124+
}

gen/schema/internal/scraper/github_test.go renamed to gen/internal/scraper/github_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"github.com/stretchr/testify/assert"
88
"github.com/stretchr/testify/require"
99

10-
"github.com/selesy/ethereum-mcp/gen/schema/internal/scraper"
10+
"github.com/selesy/ethereum-mcp/gen/internal/scraper"
1111
)
1212

1313
// TODO: this tests the code but is NOT a unit test, requires a connection
File renamed without changes.

gen/schema/main.go renamed to gen/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package main
33
import (
44
"os"
55

6-
"github.com/selesy/ethereum-mcp/gen/schema/internal"
6+
"github.com/selesy/ethereum-mcp/gen/internal"
77
)
88

99
func main() {

gen/schema/internal/generator/generator.go

Lines changed: 0 additions & 89 deletions
This file was deleted.

go.mod

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ module github.com/selesy/ethereum-mcp
22

33
go 1.24.4
44

5-
tool (
6-
github.com/selesy/ethereum-mcp/gen/schema
7-
golang.org/x/vuln/cmd/govulncheck
8-
)
5+
tool golang.org/x/vuln/cmd/govulncheck
96

107
require (
118
github.com/invopop/yaml v0.3.1
@@ -18,16 +15,11 @@ require (
1815
require (
1916
github.com/bahlo/generic-list-go v0.2.0 // indirect
2017
github.com/buger/jsonparser v1.1.1 // indirect
21-
github.com/dave/jennifer v1.7.1 // indirect
2218
github.com/davecgh/go-spew v1.1.1 // indirect
2319
github.com/google/go-cmp v0.7.0 // indirect
24-
github.com/google/go-github/v74 v74.0.0 // indirect
25-
github.com/google/go-querystring v1.1.0 // indirect
2620
github.com/invopop/jsonschema v0.13.0 // indirect
27-
github.com/lmittmann/tint v1.1.2 // indirect
2821
github.com/mailru/easyjson v0.7.7 // indirect
2922
github.com/pmezard/go-difflib v1.0.0 // indirect
30-
github.com/selesy/ethereum-mcp/gen v0.1.0 // indirect
3123
github.com/spf13/cast v1.7.1 // indirect
3224
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
3325
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect

0 commit comments

Comments
 (0)