Skip to content

Commit 0df3bd2

Browse files
authored
fix kafka consumer not commit correct offsets (#14)
fix #11 support schema registry integration for kafka consume and kafka produce
1 parent 9f72f10 commit 0df3bd2

File tree

6 files changed

+182
-178
lines changed

6 files changed

+182
-178
lines changed

go.mod

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/99designs/keyring v1.2.2
77
github.com/apache/pulsar-client-go v0.13.1
88
github.com/dgrijalva/jwt-go v3.2.0+incompatible
9+
github.com/hamba/avro/v2 v2.28.0
910
github.com/mark3labs/mcp-go v0.26.0
1011
github.com/mitchellh/go-homedir v1.1.0
1112
github.com/pkg/errors v0.9.1
@@ -43,7 +44,6 @@ require (
4344
github.com/golang/protobuf v1.5.4 // indirect
4445
github.com/google/uuid v1.6.0 // indirect
4546
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
46-
github.com/hamba/avro/v2 v2.26.0 // indirect
4747
github.com/hashicorp/errwrap v1.1.0 // indirect
4848
github.com/hashicorp/go-multierror v1.1.1 // indirect
4949
github.com/inconshreveable/mousetrap v1.1.0 // indirect
@@ -75,10 +75,11 @@ require (
7575
github.com/twmb/franz-go/pkg/kmsg v1.9.0 // indirect
7676
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
7777
go.uber.org/atomic v1.11.0 // indirect
78-
golang.org/x/crypto v0.32.0 // indirect
79-
golang.org/x/mod v0.20.0 // indirect
80-
golang.org/x/sys v0.29.0 // indirect
81-
golang.org/x/term v0.28.0 // indirect
78+
golang.org/x/crypto v0.36.0 // indirect
79+
golang.org/x/mod v0.23.0 // indirect
80+
golang.org/x/net v0.38.0 // indirect
81+
golang.org/x/sys v0.31.0 // indirect
82+
golang.org/x/term v0.30.0 // indirect
8283
google.golang.org/protobuf v1.35.1 // indirect
8384
)
8485

go.sum

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
9898
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
9999
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
100100
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
101-
github.com/hamba/avro/v2 v2.26.0 h1:IaT5l6W3zh7K67sMrT2+RreJyDTllBGVJm4+Hedk9qE=
102-
github.com/hamba/avro/v2 v2.26.0/go.mod h1:I8glyswHnpED3Nlx2ZdUe+4LJnCOOyiCzLMno9i/Uu0=
101+
github.com/hamba/avro/v2 v2.28.0 h1:E8J5D27biyAulWKNiEBhV85QPc9xRMCUCGJewS0KYCE=
102+
github.com/hamba/avro/v2 v2.28.0/go.mod h1:9TVrlt1cG1kkTUtm9u2eO5Qb7rZXlYzoKqPt8TSH+TA=
103103
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
104104
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
105105
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -251,16 +251,16 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
251251
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
252252
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
253253
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
254-
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
255-
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
256-
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
257-
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
254+
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
255+
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
256+
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
257+
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
258258
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
259259
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
260260
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
261261
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
262-
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
263-
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
262+
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
263+
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
264264
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
265265
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
266266
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -272,16 +272,16 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
272272
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
273273
golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
274274
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
275-
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
276-
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
275+
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
276+
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
277277
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
278-
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
279-
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
278+
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
279+
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
280280
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
281281
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
282282
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
283-
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
284-
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
283+
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
284+
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
285285
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
286286
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
287287
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

go.work.sum

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -136,25 +136,18 @@ go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kT
136136
go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A=
137137
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
138138
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
139-
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
140-
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
141139
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y=
142140
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
143141
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
144142
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
145143
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
146-
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
147144
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
148145
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
149146
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
147+
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
148+
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
150149
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642 h1:B6caxRw+hozq68X2MY7jEpZh/cr4/aHLv9xU8Kkadrw=
151-
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
152-
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
153-
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
154-
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
155150
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
156-
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
157-
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
158151
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
159152
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
160153
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -163,6 +156,8 @@ golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
163156
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d h1:W07d4xkoAUSNOkOzdzXCdFGxT7o2rW4q8M34tB2i//k=
164157
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
165158
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
159+
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
160+
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
166161
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
167162
google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w=
168163
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=

pkg/mcp/kafka_admin_groups_tools.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func KafkaAdminAddGroupsTools(s *server.MCPServer, readOnly bool, features []str
109109
mcp.Description("The name of the Kafka topic to operate on. "+
110110
"Required for the 'delete' operation. "+
111111
"Must be an existing topic name in the Kafka cluster.")),
112-
mcp.WithString("partition",
112+
mcp.WithNumber("partition",
113113
mcp.Description("The partition number to set offset for. "+
114114
"Required for the 'set-offset' operation. "+
115115
"Must be a valid partition number for the specified topic.")),
@@ -191,7 +191,17 @@ func handleKafkaGroupDescribe(ctx context.Context, admin *kadm.Client, request m
191191
return mcp.NewToolResultError(fmt.Sprintf("Failed to describe group: %v", err)), nil
192192
}
193193

194-
jsonBytes, err := json.Marshal(response)
194+
lags, err := admin.Lag(ctx, groupName)
195+
if err != nil {
196+
return mcp.NewToolResultError(fmt.Sprintf("Failed to get lag: %v", err)), nil
197+
}
198+
199+
result := map[string]interface{}{
200+
"group": response,
201+
"lag": lags,
202+
}
203+
204+
jsonBytes, err := json.Marshal(result)
195205
if err != nil {
196206
return mcp.NewToolResultError(fmt.Sprintf("Failed to marshal response: %v", err)), nil
197207
}

pkg/mcp/kafka_client_consume_tools.go

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ import (
2424
"slices"
2525
"time"
2626

27+
"github.com/hamba/avro/v2"
2728
"github.com/mark3labs/mcp-go/mcp"
2829
"github.com/mark3labs/mcp-go/server"
2930
"github.com/sirupsen/logrus"
3031
"github.com/streamnative/streamnative-mcp-server/pkg/kafka"
3132
"github.com/twmb/franz-go/pkg/kgo"
33+
"github.com/twmb/franz-go/pkg/sr"
3234
)
3335

3436
var logger *logrus.Logger
@@ -52,7 +54,7 @@ func KafkaClientAddConsumeTools(s *server.MCPServer, _ bool, logrusLogger *logru
5254
"1. Basic consumption - Get 10 earliest messages from a topic:\n" +
5355
" topic: \"my-topic\"\n" +
5456
" max-messages: 10\n\n" +
55-
"2. Consume from beginning - Get messages from the start of a topic:\n" +
57+
"2. Consumer group - Join an existing consumer group and resume from committed offset:\n" +
5658
" topic: \"my-topic\"\n" +
5759
" offset: \"atstart\"\n" +
5860
" max-messages: 20\n\n" +
@@ -132,6 +134,14 @@ func handleKafkaConsume(ctx context.Context, request mcp.CallToolRequest) (*mcp.
132134
timeoutSec = 10 // Default to 10 seconds
133135
}
134136

137+
group, hasGroup := optionalParam[string](request.Params.Arguments, "group")
138+
if !hasGroup {
139+
group = ""
140+
}
141+
if group != "" {
142+
opts = append(opts, kgo.ConsumerGroup(group))
143+
}
144+
135145
offsetStr, hasOffset := optionalParam[string](request.Params.Arguments, "offset")
136146
if !hasOffset {
137147
offsetStr = "atstart" // Default to starting at the beginning
@@ -148,10 +158,8 @@ func handleKafkaConsume(ctx context.Context, request mcp.CallToolRequest) (*mcp.
148158
default:
149159
offset = kgo.NewOffset().AtStart()
150160
}
151-
152161
opts = append(opts, kgo.ConsumeResetOffset(offset))
153-
154-
logger.Infof("Consuming from topic: %s, offset: %s, max-messages: %d, timeout: %d", topicName, offsetStr, int(maxMessages), int(timeoutSec))
162+
logger.Infof("Consuming from topic: %s, group: %s, max-messages: %d, timeout: %d", topicName, group, int(maxMessages), int(timeoutSec))
155163

156164
// Create Kafka client using the new Kafka package
157165
kafkaClient, err := kafka.GetKafkaClient(opts...)
@@ -160,6 +168,13 @@ func handleKafkaConsume(ctx context.Context, request mcp.CallToolRequest) (*mcp.
160168
}
161169
defer kafkaClient.Close()
162170

171+
srClient, err := kafka.GetKafkaSchemaRegistryClient()
172+
schemaReady := false
173+
var serde sr.Serde
174+
if err == nil && srClient != nil {
175+
schemaReady = true
176+
}
177+
163178
// Set timeout
164179
timeoutCtx, cancel := context.WithTimeout(ctx, time.Duration(timeoutSec)*time.Second)
165180
defer cancel()
@@ -168,10 +183,44 @@ func handleKafkaConsume(ctx context.Context, request mcp.CallToolRequest) (*mcp.
168183
return mcp.NewToolResultError(fmt.Sprintf("Failed to ping Kafka cluster: %v", err)), nil
169184
}
170185

171-
topics := kafkaClient.OptValue("ConsumeTopics")
172-
logger.Infof("Consuming from topics: %v\n", topics)
186+
if schemaReady {
187+
subjSchema, err := srClient.SchemaByVersion(timeoutCtx, topicName+"-value", -1)
188+
if err != nil {
189+
return mcp.NewToolResultError(fmt.Sprintf("Failed to get schema: %v", err)), nil
190+
}
191+
logger.Infof("Schema ID: %d", subjSchema.ID)
192+
switch subjSchema.Type {
193+
case sr.TypeAvro:
194+
avroSchema, err := avro.Parse(subjSchema.Schema.Schema)
195+
if err != nil {
196+
return mcp.NewToolResultError(fmt.Sprintf("Failed to parse avro schema: %v", err)), nil
197+
}
198+
serde.Register(
199+
subjSchema.ID,
200+
map[string]any{},
201+
sr.EncodeFn(func(v any) ([]byte, error) {
202+
return avro.Marshal(avroSchema, v)
203+
}),
204+
sr.DecodeFn(func(data []byte, v any) error {
205+
return avro.Unmarshal(avroSchema, data, v)
206+
}),
207+
)
208+
case sr.TypeJSON:
209+
serde.Register(
210+
subjSchema.ID,
211+
map[string]any{},
212+
sr.EncodeFn(json.Marshal),
213+
sr.DecodeFn(json.Unmarshal),
214+
)
215+
case sr.TypeProtobuf:
216+
default:
217+
// TODO: support other schema types
218+
logger.Infof("Unsupported schema type: %s", subjSchema.Type)
219+
schemaReady = false
220+
}
221+
}
173222

174-
results := make([]*kgo.Record, 0)
223+
results := make([]any, 0)
175224
consumerLoop:
176225
for {
177226
fetches := kafkaClient.PollRecords(timeoutCtx, int(maxMessages))
@@ -186,13 +235,31 @@ consumerLoop:
186235

187236
for !iter.Done() {
188237
record := iter.Next()
189-
results = append(results, record)
238+
if schemaReady {
239+
var value map[string]any
240+
err := serde.Decode(record.Value, &value)
241+
if err != nil {
242+
logger.Infof("Failed to decode value: %v", err)
243+
results = append(results, record.Value)
244+
} else {
245+
results = append(results, value)
246+
}
247+
} else {
248+
results = append(results, record.Value)
249+
}
190250
if len(results) >= int(maxMessages) {
191251
break consumerLoop
192252
}
193253
}
194254
}
195255

256+
err = kafkaClient.CommitUncommittedOffsets(timeoutCtx)
257+
if err != nil {
258+
if err != context.Canceled {
259+
logger.Infof("Failed to commit offsets: %v", err)
260+
}
261+
}
262+
196263
jsonResults, err := json.Marshal(results)
197264
if err != nil {
198265
return mcp.NewToolResultError(fmt.Sprintf("Failed to marshal results: %v", err)), nil

0 commit comments

Comments
 (0)