diff --git a/go.mod b/go.mod
index 35786eaf19b049508c2d063c23e6c301e5c377fc..a548c85bfde972ab85a26358913950cbbb4c7281 100644
--- a/go.mod
+++ b/go.mod
@@ -4,8 +4,6 @@ go 1.21
 
 replace github.com/goccy/go-json v0.10.2 => github.com/elee1766/go-json v0.10.2-1
 
-replace github.com/go-faster/jx v1.1.0 => github.com/elee1766/jx v1.1.0-1
-
 require (
 	gfx.cafe/open/websocket v1.9.2
 	gfx.cafe/util/go/bufpool v0.0.0-20230721185457-c559e86c829c
diff --git a/go.sum b/go.sum
index d8f9eeda07441c8640114e1ee558daef2741e90a..e9b7c2dc9dfd0997b722d6d37de144795c5d167e 100644
--- a/go.sum
+++ b/go.sum
@@ -13,10 +13,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/elee1766/go-json v0.10.2-1 h1:AW2+8FAs2wVgRo90yz1l7pXKEiPxC71jquZNzgebkkc=
 github.com/elee1766/go-json v0.10.2-1/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-github.com/elee1766/jx v1.1.0-1 h1:BOOlR+gpXzdvKQHv7cLkc18Mtu0x7KvMyYr1cZnFPu8=
-github.com/elee1766/jx v1.1.0-1/go.mod h1:vKDNikrKoyUmpzaJ0OkIkRQClNHFX/nF3dnTJZb3skg=
 github.com/go-faster/errors v0.6.1 h1:nNIPOBkprlKzkThvS/0YaX8Zs9KewLCOSFQS5BU06FI=
 github.com/go-faster/errors v0.6.1/go.mod h1:5MGV2/2T9yvlrbhe9pD9LO5Z/2zCSq2T8j+Jpi2LAyY=
+github.com/go-faster/jx v1.1.0 h1:ZsW3wD+snOdmTDy9eIVgQdjUpXRRV4rqW8NS3t+20bg=
+github.com/go-faster/jx v1.1.0/go.mod h1:vKDNikrKoyUmpzaJ0OkIkRQClNHFX/nF3dnTJZb3skg=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
diff --git a/pkg/jsonrpc/errors.go b/pkg/jsonrpc/errors.go
index 4757c9431a97e42dd7a0ae493ebedcb6514ec2e6..81f7e2391b3167a3385bd92ec85354ada3c1714a 100644
--- a/pkg/jsonrpc/errors.go
+++ b/pkg/jsonrpc/errors.go
@@ -44,7 +44,8 @@ type DataError interface {
 	ErrorData() any // returns the error data
 }
 
-func EncodeError(enc *jx.Encoder, err error) error {
+func MarshalError(err error) []byte {
+	enc := jx.GetEncoder()
 	enc.Obj(func(e *jx.Encoder) {
 		switch er := err.(type) {
 		case DataError:
@@ -69,7 +70,7 @@ func EncodeError(enc *jx.Encoder, err error) error {
 			e.Field("message", func(e *jx.Encoder) { e.Str(er.Error()) })
 		}
 	})
-	return nil
+	return enc.Bytes()
 }
 
 func WrapErr(data any, code int, err error) error {
diff --git a/pkg/jsonrpc/json.go b/pkg/jsonrpc/json.go
index 40055ccfe349d3d3022ed6e082b46e4bb9fadfa7..daad98b362e9d94e7e1ef245a3c335a41e2ca783 100644
--- a/pkg/jsonrpc/json.go
+++ b/pkg/jsonrpc/json.go
@@ -50,7 +50,7 @@ func MarshalMessage(m *Message, enc *jx.Encoder) (err error) {
 		}
 		if m.Error != nil {
 			e.Field("error", func(e *jx.Encoder) {
-				EncodeError(e, m.Error)
+				e.Raw(MarshalError(m.Error))
 			})
 			return
 		}
diff --git a/pkg/server/server.go b/pkg/server/server.go
index 591860c6d447cb1d122624769d926b88e4a127e2..7ce37c85d7bffdd2498f415e3d6df3cb7eab58a1 100644
--- a/pkg/server/server.go
+++ b/pkg/server/server.go
@@ -3,6 +3,7 @@ package server
 import (
 	"context"
 	"errors"
+	"io"
 	"sync"
 
 	"golang.org/x/sync/semaphore"
@@ -293,53 +294,56 @@ type callEnv struct {
 }
 
 func (c *callResponder) send(ctx context.Context, env *callEnv) (err error) {
-	enc := jx.GetEncoder()
-	defer jx.PutEncoder(enc)
+	w := c.remote
+	enc := jx.GetWriter()
+	defer jx.PutWriter(enc)
 	enc.Grow(4096)
-	enc.ResetWriter(c.remote)
-	enc.Obj(func(e *jx.Encoder) {
-		e.Field("jsonrpc", func(e *jx.Encoder) {
-			e.Str("2.0")
-		})
-		if env.id != nil {
-			e.Field("id", func(e *jx.Encoder) {
-				e.Raw(env.id.RawMessage())
-			})
-		}
-		if env.err != nil {
-			e.Field("error", func(e *jx.Encoder) {
-				jsonrpc.EncodeError(e, env.err)
-			})
-		} else {
-			// if there is no error, we try to marshal the result
-			e.Field("result", func(e *jx.Encoder) {
-				if env.v != nil {
-					switch cast := (env.v).(type) {
-					case json.RawMessage:
-						if len(cast) == 0 {
-							e.Null()
-						} else {
-							e.Raw(cast)
-						}
-					case func(e *jx.Encoder) error:
-						err = cast(e)
-					default:
-						err = json.NewEncoder(e).EncodeWithOption(cast, func(eo *json.EncodeOption) {
-							eo.DisableNewline = true
-						})
-					}
+	enc.ResetWriter(w)
+	enc.ObjStart()
+	enc.FieldStart("jsonrpc")
+	enc.Str("2.0")
+	if env.id != nil {
+		enc.Comma()
+		enc.FieldStart("id")
+		enc.Raw(env.id.RawMessage())
+	}
+	if env.err != nil {
+		enc.Comma()
+		enc.FieldStart("error")
+		enc.Raw(jsonrpc.MarshalError(env.err))
+	} else {
+		// if there is no error, we try to marshal the result
+		enc.Comma()
+		enc.FieldStart("result")
+		enc.Close()
+		enc.ResetWriter(w)
+		if env.v != nil {
+			switch cast := (env.v).(type) {
+			case json.RawMessage:
+				if len(cast) == 0 {
+					enc.Null()
 				} else {
-					e.Null()
+					enc.Raw(cast)
 				}
-			})
-		}
-		if env.err == nil && err != nil {
-			e.Field("error", func(e *jx.Encoder) {
-				jsonrpc.EncodeError(e, err)
-			})
+			case func(e io.Writer) error:
+				err = cast(w)
+			case func(e *jx.Writer) error:
+				err = cast(enc)
+			default:
+				err = json.NewEncoder(w).EncodeWithOption(cast, func(eo *json.EncodeOption) {
+					eo.DisableNewline = true
+				})
+			}
+		} else {
+			enc.Null()
 		}
-	})
-	// a json encoding error here is possibly fatal....
+	}
+	if env.err == nil && err != nil {
+		enc.Comma()
+		enc.FieldStart("error")
+		enc.Raw(jsonrpc.MarshalError(err))
+	}
+	enc.ObjEnd()
 	err = enc.Close()
 	if err != nil {
 		return err