diff --git a/core/go.mod b/core/go.mod index c9b68c602d..1b689325bd 100644 --- a/core/go.mod +++ b/core/go.mod @@ -6,7 +6,7 @@ require ( github.com/aws/aws-sdk-go-v2 v1.36.3 github.com/aws/aws-sdk-go-v2/config v1.29.14 github.com/goccy/go-json v0.10.5 - github.com/metoro-io/mcp-golang v0.13.0 + github.com/mark3labs/mcp-go v0.32.0 github.com/valyala/fasthttp v1.60.0 golang.org/x/oauth2 v0.30.0 ) @@ -25,35 +25,11 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect github.com/aws/smithy-go v1.22.3 // indirect - github.com/bahlo/generic-list-go v0.2.0 // indirect - github.com/buger/jsonparser v1.1.1 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.8.1 // indirect - github.com/go-playground/locales v0.14.0 // indirect - github.com/go-playground/universal-translator v0.18.0 // indirect - github.com/go-playground/validator/v10 v10.10.0 // indirect - github.com/invopop/jsonschema v0.12.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/klauspost/compress v1.18.0 // indirect - github.com/leodido/go-urn v1.2.1 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.1 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/tidwall/gjson v1.18.0 // indirect - github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.1 // indirect - github.com/tidwall/sjson v1.2.5 // indirect - github.com/ugorji/go/codec v1.2.7 // indirect + github.com/spf13/cast v1.7.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect - golang.org/x/crypto v0.37.0 // indirect + github.com/yosida95/uritemplate/v3 v3.0.2 // indirect golang.org/x/net v0.39.0 // indirect - golang.org/x/sys v0.32.0 // indirect golang.org/x/text v0.24.0 // indirect - google.golang.org/protobuf v1.28.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/core/go.sum b/core/go.sum index 6f20369754..9ffb1b5af4 100644 --- a/core/go.sum +++ b/core/go.sum @@ -28,130 +28,45 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/Xv github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= -github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= -github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= -github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= -github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= -github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI= -github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/metoro-io/mcp-golang v0.13.0 h1:54TFBJIW76VRB55CJovQQje9x4GnXg0BQQwGRtXrbCE= -github.com/metoro-io/mcp-golang v0.13.0/go.mod h1:ifLP9ZzKpN1UqFWNTpAHOqSvNkMK6b7d1FSZ5Lu0lN0= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/mark3labs/mcp-go v0.32.0 h1:fgwmbfL2gbd67obg57OfV2Dnrhs1HtSdlY/i5fn7MU8= +github.com/mark3labs/mcp-go v0.32.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= -github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= -github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= -github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= -github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.60.0 h1:kBRYS0lOhVJ6V+bYN8PqAHELKHtXqwq9zNMLKx1MBsw= github.com/valyala/fasthttp v1.60.0/go.mod h1:iY4kDgV3Gc6EqhRZ8icqcmlG6bqhcDXfuHgTO4FXCvc= -github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= -github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= +github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/core/mcp.go b/core/mcp.go index bb289de495..93c191d768 100644 --- a/core/mcp.go +++ b/core/mcp.go @@ -4,17 +4,18 @@ import ( "context" "encoding/json" "fmt" - "net/http" "os" - "os/exec" + "slices" "strings" "sync" "time" "github.com/maximhq/bifrost/core/schemas" - mcp_golang "github.com/metoro-io/mcp-golang" - httpTransport "github.com/metoro-io/mcp-golang/transport/http" - "github.com/metoro-io/mcp-golang/transport/stdio" + + "github.com/mark3labs/mcp-go/client" + "github.com/mark3labs/mcp-go/client/transport" + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" ) // ============================================================================ @@ -23,7 +24,6 @@ import ( const ( // MCP defaults and identifiers - DefaultMCPServerPort = 8181 // Default port for local MCP server BifrostMCPVersion = "1.0.0" // Version identifier for Bifrost BifrostMCPClientName = "BifrostClient" // Name for internal Bifrost MCP client BifrostMCPClientKey = "bifrost-internal" // Key for internal Bifrost client in clientMap @@ -43,9 +43,8 @@ const ( // It provides a bridge between Bifrost and various MCP servers, supporting // both local tool hosting and external MCP server connections. type MCPManager struct { - server *mcp_golang.Server // Local MCP server instance for hosting tools + server *server.MCPServer // Local MCP server instance for hosting tools (STDIO-based) clientMap map[string]*MCPClient // Map of MCP client names to their configurations - serverPort *int // Port for local MCP server (only required for local server) mu sync.RWMutex // Read-write mutex for thread-safe operations serverRunning bool // Track whether local MCP server is running logger schemas.Logger // Logger instance for structured logging @@ -54,19 +53,18 @@ type MCPManager struct { // MCPClient represents a connected MCP client with its configuration and tools. type MCPClient struct { Name string // Unique name for this client - Conn *mcp_golang.Client // Active MCP client connection + Conn *client.Client // Active MCP client connection ExecutionConfig schemas.MCPClientConfig // Tool filtering settings ToolMap map[string]schemas.Tool // Available tools mapped by name - StdioCommand *exec.Cmd `json:"-"` // STDIO process command (not serialized) ConnectionInfo MCPClientConnectionInfo `json:"connection_info"` // Connection metadata for management + cancelFunc context.CancelFunc `json:"-"` // Cancel function for SSE connections (not serialized) } // MCPClientConnectionInfo stores metadata about how a client is connected. type MCPClientConnectionInfo struct { - Type schemas.MCPConnectionType `json:"type"` // Connection type (HTTP or STDIO) - HTTPConnectionURL *string `json:"http_connection_url,omitempty"` // HTTP endpoint URL (for HTTP connections) + Type schemas.MCPConnectionType `json:"type"` // Connection type (HTTP, STDIO, or SSE) + ConnectionURL *string `json:"connection_url,omitempty"` // HTTP/SSE endpoint URL (for HTTP/SSE connections) StdioCommandString *string `json:"stdio_command_string,omitempty"` // Command string for display (for STDIO connections) - ProcessID *int `json:"process_id,omitempty"` // Process ID of STDIO command } // MCPToolHandler is a generic function type for handling tool calls with typed arguments. @@ -93,9 +91,8 @@ func newMCPManager(config schemas.MCPConfig, logger schemas.Logger) (*MCPManager } manager := &MCPManager{ - serverPort: config.ServerPort, - clientMap: make(map[string]*MCPClient), - logger: logger, + clientMap: make(map[string]*MCPClient), + logger: logger, } // Process client configs: create client map entries and establish connections @@ -209,18 +206,20 @@ func (m *MCPManager) registerTool(name, description string, handler MCPToolHandl m.logger.Info(fmt.Sprintf("%s Registering typed tool: %s", MCPLogPrefix, name)) // Create MCP handler wrapper that converts between typed and MCP interfaces - mcpHandler := func(args any) (*mcp_golang.ToolResponse, error) { + mcpHandler := func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + // Extract arguments from the request using the request's methods + args := request.GetArguments() result, err := handler(args) if err != nil { - return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(fmt.Sprintf("Error: %s", err.Error()))), nil + return mcp.NewToolResultError(fmt.Sprintf("Error: %s", err.Error())), nil } - return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(result)), nil + return mcp.NewToolResultText(result), nil } - // Register with the underlying mcp-golang server - err := m.server.RegisterTool(name, description, mcpHandler) - if err != nil { - return fmt.Errorf("failed to register tool with MCP server: %w", err) + // Register the tool with the local MCP server using AddTool + if m.server != nil { + tool := mcp.NewTool(name, mcp.WithDescription(description)) + m.server.AddTool(tool, mcpHandler) } // Store tool definition for Bifrost integration @@ -230,6 +229,7 @@ func (m *MCPManager) registerTool(name, description string, handler MCPToolHandl } // setupLocalHost initializes the local MCP server and client if not already running. +// This creates a STDIO-based server for local tool hosting and a corresponding client. // This is called automatically when tools are registered or when the server is needed. // // Returns: @@ -240,14 +240,14 @@ func (m *MCPManager) setupLocalHost() error { return nil } - // Create and configure local MCP server + // Create and configure local MCP server (STDIO-based) server, err := m.createLocalMCPServer() if err != nil { return fmt.Errorf("failed to create local MCP server: %w", err) } m.server = server - // Create and configure local MCP client + // Create and configure local MCP client (STDIO-based) client, err := m.createLocalMCPClient() if err != nil { return fmt.Errorf("failed to create local MCP client: %w", err) @@ -258,60 +258,55 @@ func (m *MCPManager) setupLocalHost() error { return m.startLocalMCPServer() } -// createLocalMCPServer creates a new local MCP server instance with HTTP transport. +// createLocalMCPServer creates a new local MCP server instance with STDIO transport. // This server will host tools registered via RegisterTool function. // // Returns: -// - *mcp_golang.Server: Configured MCP server instance +// - *server.MCPServer: Configured MCP server instance // - error: Any creation error -func (m *MCPManager) createLocalMCPServer() (*mcp_golang.Server, error) { - // Use configured port or default - serverPort := m.serverPort - if serverPort == nil { - serverPort = Ptr(DefaultMCPServerPort) - } - - // Create HTTP transport for the MCP server - serverTransport := httpTransport.NewHTTPTransport("/mcp") - serverTransport.WithAddr(fmt.Sprintf(":%d", *serverPort)) - server := mcp_golang.NewServer(serverTransport) - - return server, nil +func (m *MCPManager) createLocalMCPServer() (*server.MCPServer, error) { + // Create MCP server + mcpServer := server.NewMCPServer( + "Bifrost-MCP-Server", + "1.0.0", + server.WithToolCapabilities(true), + ) + + return mcpServer, nil } -// createLocalMCPClient creates a client that connects to the local MCP server. +// createLocalMCPClient creates a client that connects to the local MCP server via STDIO. // This client is used internally by Bifrost to access locally hosted tools. // // Returns: // - *MCPClient: Configured client for local server // - error: Any creation error func (m *MCPManager) createLocalMCPClient() (*MCPClient, error) { - // Use configured port or default - serverPort := m.serverPort - if serverPort == nil { - serverPort = Ptr(DefaultMCPServerPort) - } + // For local STDIO communication, we'll use the same process + // Create a STDIO transport that communicates with our local server + // This creates an in-process communication channel + stdioTransport := transport.NewStdio( + "", // Empty command means in-process + nil, // No environment variables needed + ) - // Create HTTP client transport pointing to local server - clientTransport := httpTransport.NewHTTPClientTransport("/mcp") - clientTransport.WithBaseURL(fmt.Sprintf("http://localhost:%d", *serverPort)) - client := mcp_golang.NewClientWithInfo(clientTransport, mcp_golang.ClientInfo{ - Name: BifrostMCPClientName, - Version: BifrostMCPVersion, - }) + // Create the MCP client + mcpClient := client.NewClient(stdioTransport) return &MCPClient{ Name: BifrostMCPClientName, - Conn: client, + Conn: mcpClient, ExecutionConfig: schemas.MCPClientConfig{ Name: BifrostMCPClientName, }, ToolMap: make(map[string]schemas.Tool), + ConnectionInfo: MCPClientConnectionInfo{ + Type: schemas.MCPConnectionTypeSTDIO, + }, }, nil } -// startLocalMCPServer starts the HTTP server and initializes the client connection. -// The server runs in a separate goroutine to avoid blocking. +// startLocalMCPServer starts the STDIO server in a background goroutine. // // Returns: // - error: Any startup error @@ -328,10 +323,10 @@ func (m *MCPManager) startLocalMCPServer() error { return fmt.Errorf("server not initialized") } - // Start the HTTP server in background goroutine + // Start the STDIO server in background goroutine go func() { - if err := m.server.Serve(); err != nil && err != http.ErrServerClosed { - m.logger.Error(fmt.Errorf("%s MCP server error: %w", MCPLogPrefix, err)) + if err := server.ServeStdio(m.server); err != nil { + m.logger.Error(fmt.Errorf("MCP STDIO server error: %w", err)) m.mu.Lock() m.serverRunning = false m.mu.Unlock() @@ -349,7 +344,28 @@ func (m *MCPManager) startLocalMCPServer() error { return fmt.Errorf("bifrost client not found") } - _, err := m.clientMap[BifrostMCPClientKey].Conn.Initialize(ctx) + // Start the local client transport first + if err := m.clientMap[BifrostMCPClientKey].Conn.Start(ctx); err != nil { + m.serverRunning = false + return fmt.Errorf("failed to start local MCP client transport: %v", err) + } + + // Create proper initialize request + initRequest := mcp.InitializeRequest{ + Request: mcp.Request{ + Method: string(mcp.MethodInitialize), + }, + Params: mcp.InitializeParams{ + ProtocolVersion: mcp.LATEST_PROTOCOL_VERSION, + Capabilities: mcp.ClientCapabilities{}, + ClientInfo: mcp.Implementation{ + Name: BifrostMCPClientName, + Version: BifrostMCPVersion, + }, + }, + } + + _, err := m.clientMap[BifrostMCPClientKey].Conn.Initialize(ctx, initRequest) if err != nil { m.serverRunning = false return fmt.Errorf("failed to initialize MCP client: %v", err) @@ -390,11 +406,26 @@ func (m *MCPManager) executeTool(ctx context.Context, toolCall schemas.ToolCall) } // Call the tool via MCP client -> MCP server - toolResponse, callErr := client.Conn.CallTool(ctx, toolName, arguments) + callRequest := mcp.CallToolRequest{ + Request: mcp.Request{ + Method: string(mcp.MethodToolsCall), + }, + Params: mcp.CallToolParams{ + Name: toolName, + Arguments: arguments, + }, + } + + m.logger.Info(fmt.Sprintf("%s Starting tool execution: %s via client: %s", MCPLogPrefix, toolName, client.Name)) + + toolResponse, callErr := client.Conn.CallTool(ctx, callRequest) if callErr != nil { + m.logger.Error(fmt.Errorf("%s Tool execution failed for %s via client %s: %v", MCPLogPrefix, toolName, client.Name, callErr)) return nil, fmt.Errorf("MCP tool call failed: %v", callErr) } + m.logger.Info(fmt.Sprintf("%s Tool execution completed: %s", MCPLogPrefix, toolName)) + // Extract text from MCP response responseText := m.extractTextFromMCPResponse(toolResponse, toolName) @@ -435,9 +466,8 @@ func (m *MCPManager) connectToMCPClient(config schemas.MCPClientConfig) error { m.mu.Unlock() // Heavy operations performed outside lock - var externalClient *mcp_golang.Client + var externalClient *client.Client var connectionInfo MCPClientConnectionInfo - var stdioCommand *exec.Cmd var err error // Create appropriate transport based on connection type @@ -445,7 +475,9 @@ func (m *MCPManager) connectToMCPClient(config schemas.MCPClientConfig) error { case schemas.MCPConnectionTypeHTTP: externalClient, connectionInfo, err = m.createHTTPConnection(config) case schemas.MCPConnectionTypeSTDIO: - externalClient, connectionInfo, stdioCommand, err = m.createSTDIOConnection(config) + externalClient, connectionInfo, err = m.createSTDIOConnection(config) + case schemas.MCPConnectionTypeSSE: + externalClient, connectionInfo, err = m.createSSEConnection(config) default: return fmt.Errorf("unknown connection type: %s", config.ConnectionType) } @@ -455,11 +487,48 @@ func (m *MCPManager) connectToMCPClient(config schemas.MCPClientConfig) error { } // Initialize the external client with timeout - ctx, cancel := context.WithTimeout(context.Background(), MCPClientConnectionEstablishTimeout) - defer cancel() + // For SSE connections, we need a long-lived context, for others we can use timeout + var ctx context.Context + var cancel context.CancelFunc + + if config.ConnectionType == schemas.MCPConnectionTypeSSE { + // SSE connections need a long-lived context for the persistent stream + ctx, cancel = context.WithCancel(context.Background()) + // Don't defer cancel here - SSE needs the context to remain active + } else { + // Other connection types can use timeout context + ctx, cancel = context.WithTimeout(context.Background(), MCPClientConnectionEstablishTimeout) + defer cancel() + } + + // Start the transport first (required for STDIO and SSE clients) + if err := externalClient.Start(ctx); err != nil { + if config.ConnectionType == schemas.MCPConnectionTypeSSE { + cancel() // Cancel SSE context only on error + } + return fmt.Errorf("failed to start MCP client transport %s: %v", config.Name, err) + } + + // Create proper initialize request for external client + extInitRequest := mcp.InitializeRequest{ + Request: mcp.Request{ + Method: string(mcp.MethodInitialize), + }, + Params: mcp.InitializeParams{ + ProtocolVersion: mcp.LATEST_PROTOCOL_VERSION, + Capabilities: mcp.ClientCapabilities{}, + ClientInfo: mcp.Implementation{ + Name: fmt.Sprintf("Bifrost-%s", config.Name), + Version: "1.0.0", + }, + }, + } - _, err = externalClient.Initialize(ctx) + _, err = externalClient.Initialize(ctx, extInitRequest) if err != nil { + if config.ConnectionType == schemas.MCPConnectionTypeSSE { + cancel() // Cancel SSE context only on error + } return fmt.Errorf("failed to initialize MCP client %s: %v", config.Name, err) } @@ -480,7 +549,11 @@ func (m *MCPManager) connectToMCPClient(config schemas.MCPClientConfig) error { // Store the external client connection and details client.Conn = externalClient client.ConnectionInfo = connectionInfo - client.StdioCommand = stdioCommand + + // Store cancel function for SSE connections to enable proper cleanup + if config.ConnectionType == schemas.MCPConnectionTypeSSE { + client.cancelFunc = cancel + } // Store discovered tools for toolName, tool := range tools { @@ -495,207 +568,18 @@ func (m *MCPManager) connectToMCPClient(config schemas.MCPClientConfig) error { return nil } -// ============================================================================ -// CLEANUP -// ============================================================================ - -// cleanup performs cleanup of all MCP resources. -func (m *MCPManager) cleanup() error { - m.mu.Lock() - defer m.mu.Unlock() - - // Clean up STDIO processes - for _, client := range m.clientMap { - if client.StdioCommand != nil && client.StdioCommand.Process != nil { - m.logger.Info(fmt.Sprintf("%s Terminating STDIO process: %d", MCPLogPrefix, client.StdioCommand.Process.Pid)) - - // Attempt to kill the process and log any errors - if err := client.StdioCommand.Process.Kill(); err != nil { - m.logger.Error(fmt.Errorf("%s Failed to kill STDIO process %d: %w", MCPLogPrefix, client.StdioCommand.Process.Pid, err)) - } - - // Wait for the process to exit with a timeout to prevent blocking cleanup - done := make(chan error, 1) - go func() { - done <- client.StdioCommand.Wait() - }() - - select { - case err := <-done: - // Process exited within timeout - if err != nil && !isExpectedKillError(err) { - m.logger.Warn(fmt.Sprintf("%s STDIO process %d exited with unexpected error: %v", MCPLogPrefix, client.StdioCommand.Process.Pid, err)) - } - m.logger.Info(fmt.Sprintf("%s STDIO process %d terminated successfully", MCPLogPrefix, client.StdioCommand.Process.Pid)) - case <-time.After(10 * time.Second): - // Process didn't exit within timeout - this is concerning but we can't wait forever - m.logger.Warn(fmt.Sprintf("%s STDIO process %d did not terminate within 10 seconds after kill signal", MCPLogPrefix, client.StdioCommand.Process.Pid)) - } - } - } - - // Disconnect all clients - for name := range m.clientMap { - m.logger.Info(fmt.Sprintf("%s Disconnecting MCP client: %s", MCPLogPrefix, name)) - } - m.clientMap = make(map[string]*MCPClient) - - // Clear server reference - if m.server != nil { - m.logger.Info(MCPLogPrefix + " Clearing MCP server reference") - m.server = nil - m.serverRunning = false - } - - return nil -} - -// ============================================================================ -// HELPER METHODS -// ============================================================================ - -// isExpectedKillError checks if an error from Wait() is expected after killing a process. -func isExpectedKillError(err error) bool { - if err == nil { - return true - } - // Check if this is a typical "killed by signal" error which is expected after Process.Kill() - errStr := err.Error() - return strings.Contains(errStr, "signal:") || strings.Contains(errStr, "killed") -} - -// findMCPClientForTool safely finds a client that has the specified tool. -func (m *MCPManager) findMCPClientForTool(toolName string) *MCPClient { - m.mu.RLock() - defer m.mu.RUnlock() - - for _, client := range m.clientMap { - if _, exists := client.ToolMap[toolName]; exists { - return client - } - } - return nil -} - -// shouldIncludeClient determines if a client should be included based on filtering rules. -func (m *MCPManager) shouldIncludeClient(clientName string, includeClients, excludeClients []string) bool { - // If includeClients is specified, only include those clients (whitelist mode) - if len(includeClients) > 0 { - for _, includeName := range includeClients { - if clientName == includeName { - return true - } - } - return false // Not in include list - } - - // If excludeClients is specified, exclude those clients (blacklist mode) - if len(excludeClients) > 0 { - for _, excludeName := range excludeClients { - if clientName == excludeName { - return false - } - } - } - - // Default: include all clients - return true -} - -// createHTTPConnection creates an HTTP-based MCP client connection without holding locks. -func (m *MCPManager) createHTTPConnection(config schemas.MCPClientConfig) (*mcp_golang.Client, MCPClientConnectionInfo, error) { - if config.HTTPConnectionString == nil { - return nil, MCPClientConnectionInfo{}, fmt.Errorf("HTTP connection string is required") - } - - // Prepare connection info - connectionInfo := MCPClientConnectionInfo{ - Type: config.ConnectionType, - HTTPConnectionURL: config.HTTPConnectionString, - } - - // Create HTTP transport - clientTransport := httpTransport.NewHTTPClientTransport("/mcp") - clientTransport.WithBaseURL(*config.HTTPConnectionString) - - client := mcp_golang.NewClientWithInfo(clientTransport, mcp_golang.ClientInfo{ - Name: fmt.Sprintf("Bifrost-%s", config.Name), - Version: "1.0.0", - }) - - return client, connectionInfo, nil -} - -// createSTDIOConnection creates a STDIO-based MCP client connection without holding locks. -func (m *MCPManager) createSTDIOConnection(config schemas.MCPClientConfig) (*mcp_golang.Client, MCPClientConnectionInfo, *exec.Cmd, error) { - if config.StdioConfig == nil { - return nil, MCPClientConnectionInfo{}, nil, fmt.Errorf("stdio config is required") - } - - // Prepare STDIO command info for display - cmdString := fmt.Sprintf("%s %s", config.StdioConfig.Command, strings.Join(config.StdioConfig.Args, " ")) - - // Create and start the STDIO command - cmd := exec.Command(config.StdioConfig.Command, config.StdioConfig.Args...) - - // Get stdin/stdout pipes before starting - stdin, err := cmd.StdinPipe() - if err != nil { - return nil, MCPClientConnectionInfo{}, nil, fmt.Errorf("failed to get stdin pipe: %v", err) - } - stdout, err := cmd.StdoutPipe() - if err != nil { - stdin.Close() // Clean up stdin if stdout fails - return nil, MCPClientConnectionInfo{}, nil, fmt.Errorf("failed to get stdout pipe: %v", err) - } - - // Check if environment variables are set - for _, env := range config.StdioConfig.Envs { - if os.Getenv(env) == "" { - return nil, MCPClientConnectionInfo{}, nil, fmt.Errorf("environment variable %s is not set for MCP client %s", env, config.Name) - } - } - - // Start the command - if err := cmd.Start(); err != nil { - stdin.Close() - stdout.Close() - wd, _ := os.Getwd() - formattedError := fmt.Errorf("failed to start MCP client '%s': command '%s %s' in directory '%s': %v", - config.Name, - config.StdioConfig.Command, - strings.Join(config.StdioConfig.Args, " "), - wd, - err) - - return nil, MCPClientConnectionInfo{}, nil, formattedError - } - - // Prepare connection info - connectionInfo := MCPClientConnectionInfo{ - Type: config.ConnectionType, - StdioCommandString: &cmdString, - } - if cmd.Process != nil { - pid := cmd.Process.Pid - connectionInfo.ProcessID = &pid - } - - // Create stdio transport with the command's stdout as our stdin, and stdin as our stdout - stdioTransport := stdio.NewStdioServerTransportWithIO(stdout, stdin) - - client := mcp_golang.NewClientWithInfo(stdioTransport, mcp_golang.ClientInfo{ - Name: fmt.Sprintf("Bifrost-%s", config.Name), - Version: "1.0.0", - }) - - return client, connectionInfo, cmd, nil -} - // retrieveExternalTools retrieves and filters tools from an external MCP server without holding locks. -func (m *MCPManager) retrieveExternalTools(ctx context.Context, client *mcp_golang.Client, config schemas.MCPClientConfig) (map[string]schemas.Tool, error) { +func (m *MCPManager) retrieveExternalTools(ctx context.Context, client *client.Client, config schemas.MCPClientConfig) (map[string]schemas.Tool, error) { // Get available tools from external server - toolsResponse, err := client.ListTools(ctx, Ptr("")) + listRequest := mcp.ListToolsRequest{ + PaginatedRequest: mcp.PaginatedRequest{ + Request: mcp.Request{ + Method: string(mcp.MethodToolsList), + }, + }, + } + + toolsResponse, err := client.ListTools(ctx, listRequest) if err != nil { return nil, fmt.Errorf("failed to list tools: %v", err) } @@ -706,7 +590,7 @@ func (m *MCPManager) retrieveExternalTools(ctx context.Context, client *mcp_gola tools := make(map[string]schemas.Tool) - // Convert and filter each tool + // toolsResponse is already a ListToolsResult for _, mcpTool := range toolsResponse.Tools { // Check if tool should be skipped based on configuration if m.shouldSkipToolForConfig(mcpTool.Name, config) { @@ -744,13 +628,17 @@ func (m *MCPManager) shouldSkipToolForConfig(toolName string, config schemas.MCP } // convertMCPToolToBifrostSchema converts an MCP tool definition to Bifrost format. -func (m *MCPManager) convertMCPToolToBifrostSchema(mcpTool *mcp_golang.ToolRetType) schemas.Tool { +func (m *MCPManager) convertMCPToolToBifrostSchema(mcpTool *mcp.Tool) schemas.Tool { // Convert MCP tool schema to Bifrost tool schema properties := make(map[string]interface{}) required := []string{} - if mcpTool.InputSchema != nil { - if schemaMap, ok := mcpTool.InputSchema.(map[string]interface{}); ok { + // Handle the InputSchema - it's a struct, not a pointer + inputSchema := mcpTool.InputSchema + // Convert to map for processing (this may need adjustment based on actual structure) + if schemaBytes, err := json.Marshal(inputSchema); err == nil { + var schemaMap map[string]interface{} + if json.Unmarshal(schemaBytes, &schemaMap) == nil { if props, ok := schemaMap["properties"].(map[string]interface{}); ok { properties = props } @@ -770,10 +658,8 @@ func (m *MCPManager) convertMCPToolToBifrostSchema(mcpTool *mcp_golang.ToolRetTy properties = make(map[string]interface{}) } - description := "" - if mcpTool.Description != nil { - description = *mcpTool.Description - } + // Description is a string, not a pointer + description := mcpTool.Description return schemas.Tool{ Type: "function", @@ -790,7 +676,7 @@ func (m *MCPManager) convertMCPToolToBifrostSchema(mcpTool *mcp_golang.ToolRetTy } // extractTextFromMCPResponse extracts text content from an MCP tool response. -func (m *MCPManager) extractTextFromMCPResponse(toolResponse *mcp_golang.ToolResponse, toolName string) string { +func (m *MCPManager) extractTextFromMCPResponse(toolResponse *mcp.CallToolResult, toolName string) string { if toolResponse == nil { return fmt.Sprintf("MCP tool '%s' executed successfully", toolName) } @@ -798,8 +684,8 @@ func (m *MCPManager) extractTextFromMCPResponse(toolResponse *mcp_golang.ToolRes var responseTextBuilder strings.Builder if len(toolResponse.Content) > 0 { for _, contentBlock := range toolResponse.Content { - if contentBlock.TextContent != nil && contentBlock.TextContent.Text != "" { - responseTextBuilder.WriteString(contentBlock.TextContent.Text) + if textContent, ok := contentBlock.(*mcp.TextContent); ok && textContent.Text != "" { + responseTextBuilder.WriteString(textContent.Text) responseTextBuilder.WriteString("\n") } } @@ -867,8 +753,12 @@ func validateMCPClientConfig(config *schemas.MCPClientConfig) error { switch config.ConnectionType { case schemas.MCPConnectionTypeHTTP: - if config.HTTPConnectionString == nil { - return fmt.Errorf("HTTPConnectionString is required for HTTP connection type in client '%s'", config.Name) + if config.ConnectionString == nil { + return fmt.Errorf("ConnectionString is required for HTTP connection type in client '%s'", config.Name) + } + case schemas.MCPConnectionTypeSSE: + if config.ConnectionString == nil { + return fmt.Errorf("ConnectionString is required for SSE connection type in client '%s'", config.Name) } case schemas.MCPConnectionTypeSTDIO: if config.StdioConfig == nil { @@ -899,3 +789,166 @@ func validateMCPClientConfig(config *schemas.MCPClientConfig) error { return nil } + +// ============================================================================ +// HELPER METHODS +// ============================================================================ + +// findMCPClientForTool safely finds a client that has the specified tool. +func (m *MCPManager) findMCPClientForTool(toolName string) *MCPClient { + m.mu.RLock() + defer m.mu.RUnlock() + + for _, client := range m.clientMap { + if _, exists := client.ToolMap[toolName]; exists { + return client + } + } + return nil +} + +// shouldIncludeClient determines if a client should be included based on filtering rules. +func (m *MCPManager) shouldIncludeClient(clientName string, includeClients, excludeClients []string) bool { + // If includeClients is specified, only include those clients (whitelist mode) + if len(includeClients) > 0 { + return slices.Contains(includeClients, clientName) + } + + // If excludeClients is specified, exclude those clients (blacklist mode) + if len(excludeClients) > 0 { + return !slices.Contains(excludeClients, clientName) + } + + // Default: include all clients + return true +} + +// createHTTPConnection creates an HTTP-based MCP client connection without holding locks. +func (m *MCPManager) createHTTPConnection(config schemas.MCPClientConfig) (*client.Client, MCPClientConnectionInfo, error) { + if config.ConnectionString == nil { + return nil, MCPClientConnectionInfo{}, fmt.Errorf("HTTP connection string is required") + } + + // Prepare connection info + connectionInfo := MCPClientConnectionInfo{ + Type: config.ConnectionType, + ConnectionURL: config.ConnectionString, + } + + // Create StreamableHTTP transport + httpTransport, err := transport.NewStreamableHTTP(*config.ConnectionString) + if err != nil { + return nil, MCPClientConnectionInfo{}, fmt.Errorf("failed to create HTTP transport: %w", err) + } + + client := client.NewClient(httpTransport) + + return client, connectionInfo, nil +} + +// createSTDIOConnection creates a STDIO-based MCP client connection without holding locks. +func (m *MCPManager) createSTDIOConnection(config schemas.MCPClientConfig) (*client.Client, MCPClientConnectionInfo, error) { + if config.StdioConfig == nil { + return nil, MCPClientConnectionInfo{}, fmt.Errorf("stdio config is required") + } + + // Prepare STDIO command info for display + cmdString := fmt.Sprintf("%s %s", config.StdioConfig.Command, strings.Join(config.StdioConfig.Args, " ")) + + // Check if environment variables are set + for _, env := range config.StdioConfig.Envs { + if os.Getenv(env) == "" { + return nil, MCPClientConnectionInfo{}, fmt.Errorf("environment variable %s is not set for MCP client %s", env, config.Name) + } + } + + // Create STDIO transport + stdioTransport := transport.NewStdio( + config.StdioConfig.Command, + config.StdioConfig.Envs, + config.StdioConfig.Args..., + ) + + // Prepare connection info + connectionInfo := MCPClientConnectionInfo{ + Type: config.ConnectionType, + StdioCommandString: &cmdString, + } + + client := client.NewClient(stdioTransport) + + // Return nil for cmd since mark3labs/mcp-go manages the process internally + return client, connectionInfo, nil +} + +// createSSEConnection creates a SSE-based MCP client connection without holding locks. +func (m *MCPManager) createSSEConnection(config schemas.MCPClientConfig) (*client.Client, MCPClientConnectionInfo, error) { + if config.ConnectionString == nil { + return nil, MCPClientConnectionInfo{}, fmt.Errorf("SSE connection string is required") + } + + // Prepare connection info + connectionInfo := MCPClientConnectionInfo{ + Type: config.ConnectionType, + ConnectionURL: config.ConnectionString, // Reuse HTTPConnectionURL field for SSE URL display + } + + // Create SSE transport + sseTransport, err := transport.NewSSE(*config.ConnectionString) + if err != nil { + return nil, MCPClientConnectionInfo{}, fmt.Errorf("failed to create SSE transport: %w", err) + } + + client := client.NewClient(sseTransport) + + return client, connectionInfo, nil +} + +// cleanup performs cleanup of all MCP resources including clients and local server. +// This function safely disconnects all MCP clients (HTTP, STDIO, and SSE) and +// cleans up the local MCP server. It handles proper cancellation of SSE contexts +// and closes all transport connections. +// +// Returns: +// - error: Always returns nil, but maintains error interface for consistency +func (m *MCPManager) cleanup() error { + m.mu.Lock() + defer m.mu.Unlock() + + // Disconnect all external MCP clients + for name, client := range m.clientMap { + m.logger.Info(fmt.Sprintf("%s Disconnecting MCP client: %s", MCPLogPrefix, name)) + + // Cancel SSE context if present (required for proper SSE cleanup) + if client.cancelFunc != nil { + client.cancelFunc() + client.cancelFunc = nil + } + + // Close the client transport connection + // This handles cleanup for all transport types (HTTP, STDIO, SSE) + if client.Conn != nil { + if err := client.Conn.Close(); err != nil { + m.logger.Error(fmt.Errorf("%s Failed to close MCP client %s: %w", MCPLogPrefix, name, err)) + } + client.Conn = nil + } + + // Clear client tool map + client.ToolMap = make(map[string]schemas.Tool) + } + + // Clear the client map + m.clientMap = make(map[string]*MCPClient) + + // Clear local server reference + // Note: mark3labs/mcp-go STDIO server cleanup is handled automatically + if m.server != nil { + m.logger.Info(MCPLogPrefix + " Clearing local MCP server reference") + m.server = nil + m.serverRunning = false + } + + m.logger.Info(MCPLogPrefix + " MCP cleanup completed") + return nil +} diff --git a/core/schemas/mcp.go b/core/schemas/mcp.go index dd1aaf0d2a..22d189e5bf 100644 --- a/core/schemas/mcp.go +++ b/core/schemas/mcp.go @@ -10,20 +10,21 @@ type MCPConfig struct { // MCPClientConfig defines tool filtering for an MCP client. type MCPClientConfig struct { - Name string `json:"name"` // Client name - ConnectionType MCPConnectionType `json:"connection_type"` // How to connect (HTTP or STDIO) - HTTPConnectionString *string `json:"http_connection_string,omitempty"` // HTTP URL (required for HTTP connections) - StdioConfig *MCPStdioConfig `json:"stdio_config,omitempty"` // STDIO configuration (required for STDIO connections) - ToolsToSkip []string `json:"tools_to_skip,omitempty"` // Tools to exclude from this client - ToolsToExecute []string `json:"tools_to_execute,omitempty"` // Tools to include from this client (if specified, only these are used) + Name string `json:"name"` // Client name + ConnectionType MCPConnectionType `json:"connection_type"` // How to connect (HTTP, STDIO, or SSE) + ConnectionString *string `json:"connection_string,omitempty"` // HTTP or SSE URL (required for HTTP or SSE connections) + StdioConfig *MCPStdioConfig `json:"stdio_config,omitempty"` // STDIO configuration (required for STDIO connections) + ToolsToSkip []string `json:"tools_to_skip,omitempty"` // Tools to exclude from this client + ToolsToExecute []string `json:"tools_to_execute,omitempty"` // Tools to include from this client (if specified, only these are used) } // MCPConnectionType defines the communication protocol for MCP connections type MCPConnectionType string const ( - MCPConnectionTypeHTTP MCPConnectionType = "http" // HTTP-based MCP connection + MCPConnectionTypeHTTP MCPConnectionType = "http" // HTTP-based MCP connection (streamable) MCPConnectionTypeSTDIO MCPConnectionType = "stdio" // STDIO-based MCP connection + MCPConnectionTypeSSE MCPConnectionType = "sse" // Server-Sent Events MCP connection ) // MCPStdioConfig defines how to launch a STDIO-based MCP server. diff --git a/docs/mcp.md b/docs/mcp.md index 30af2088dc..53c7a0759a 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -43,9 +43,10 @@ The MCP Integration acts as a bridge between Bifrost and the Model Context Proto ### πŸ”Œ **Connection Types** -- **HTTP**: Connect to web-based MCP servers +- **HTTP**: Connect to web-based MCP servers with streaming support - **STDIO**: Launch and communicate with command-line MCP tools -- **Process Management**: Automatic cleanup of STDIO processes +- **SSE**: Connect to Server-Sent Events based MCP services +- **Process Management**: Automatic cleanup of STDIO processes and SSE connections ## Quick Start @@ -62,7 +63,6 @@ import ( func main() { // Create MCP configuration mcpConfig := &schemas.MCPConfig{ - ServerPort: bifrost.Ptr(8181), // (only required for local tool setup) ClientConfigs: []schemas.MCPClientConfig{ { Name: "weather-service", @@ -131,10 +131,10 @@ mcpConfig := &schemas.MCPConfig{ ClientConfigs: []schemas.MCPClientConfig{ // HTTP-based MCP server { - Name: "weather-service", - ConnectionType: schemas.MCPConnectionTypeHTTP, - HTTPConnectionString: &[]string{"http://localhost:3000"}[0], - ToolsToSkip: []string{}, // No tools to skip + Name: "weather-service", + ConnectionType: schemas.MCPConnectionTypeHTTP, + ConnectionString: &[]string{"http://localhost:3000"}[0], + ToolsToSkip: []string{}, // No tools to skip }, // STDIO-based MCP tool { @@ -189,7 +189,14 @@ Configure MCP in your JSON configuration file when using Bifrost HTTP Transport: { "name": "web-search", "connection_type": "http", - "http_connection_string": "http://localhost:3001/mcp", + "connection_string": "http://localhost:3001/mcp", + "tools_to_skip": [], + "tools_to_execute": [] + }, + { + "name": "real-time-data", + "connection_type": "sse", + "connection_string": "http://localhost:3002/sse", "tools_to_skip": [], "tools_to_execute": [] } @@ -401,7 +408,6 @@ type BifrostConfig struct { ```go type MCPConfig struct { - ServerPort *int `json:"server_port,omitempty"` // Port for local MCP server (only required for local tool setup, defaults to 8181) ClientConfigs []MCPClientConfig `json:"client_configs,omitempty"` // MCP client configurations (connection + filtering) } ``` @@ -410,12 +416,12 @@ type MCPConfig struct { ```go type MCPClientConfig struct { - Name string `json:"name"` // Client name - ConnectionType MCPConnectionType `json:"connection_type"` // How to connect (HTTP or STDIO) - HTTPConnectionString *string `json:"http_connection_string,omitempty"` // HTTP URL (required for HTTP connections) - StdioConfig *MCPStdioConfig `json:"stdio_config,omitempty"` // STDIO configuration (required for STDIO connections) - ToolsToSkip []string `json:"tools_to_skip,omitempty"` // Tools to exclude from this client - ToolsToExecute []string `json:"tools_to_execute,omitempty"` // Tools to include from this client (if specified, only these are used) + Name string `json:"name"` // Client name + ConnectionType MCPConnectionType `json:"connection_type"` // How to connect (HTTP, STDIO, or SSE) + ConnectionString *string `json:"connection_string,omitempty"` // HTTP or SSE URL (required for HTTP or SSE connections) + StdioConfig *MCPStdioConfig `json:"stdio_config,omitempty"` // STDIO configuration (required for STDIO connections) + ToolsToSkip []string `json:"tools_to_skip,omitempty"` // Tools to exclude from this client + ToolsToExecute []string `json:"tools_to_execute,omitempty"` // Tools to include from this client (if specified, only these are used) } ``` @@ -425,8 +431,9 @@ type MCPClientConfig struct { type MCPConnectionType string const ( - MCPConnectionTypeHTTP MCPConnectionType = "http" // HTTP-based MCP connection + MCPConnectionTypeHTTP MCPConnectionType = "http" // HTTP-based MCP connection (streamable) MCPConnectionTypeSTDIO MCPConnectionType = "stdio" // STDIO-based MCP connection + MCPConnectionTypeSSE MCPConnectionType = "sse" // Server-Sent Events MCP connection ) ``` @@ -436,6 +443,7 @@ const ( type MCPStdioConfig struct { Command string `json:"command"` // Executable command to run Args []string `json:"args"` // Command line arguments + Envs []string `json:"envs"` // Environment variables required } ``` @@ -445,10 +453,10 @@ type MCPStdioConfig struct { mcpConfig := &schemas.MCPConfig{ ClientConfigs: []schemas.MCPClientConfig{ { - Name: "weather-service", - ConnectionType: schemas.MCPConnectionTypeHTTP, - HTTPConnectionString: &[]string{"http://localhost:3000"}[0], - ToolsToExecute: []string{"get_weather", "get_forecast"}, // Only these tools + Name: "weather-service", + ConnectionType: schemas.MCPConnectionTypeHTTP, + ConnectionString: &[]string{"http://localhost:3000"}[0], + ToolsToExecute: []string{"get_weather", "get_forecast"}, // Only these tools }, { Name: "filesystem-tools", @@ -879,6 +887,7 @@ For a complete working example, see `tests/core-chatbot/main.go` in the reposito β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - Host Tools β”‚ β”‚ - HTTP Clients β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - HTTP Server β”‚ β”‚ - STDIO Procs β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ - SSE Clients β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - Tool Reg. β”‚ β”‚ - Tool Discoveryβ”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ @@ -938,6 +947,13 @@ User Request - Automatic process lifecycle management - Process cleanup on Bifrost shutdown +#### SSE Connections + +- Connect to Server-Sent Events streams +- Persistent, long-lived connections for real-time data +- Automatic connection management and reconnection +- Proper context cleanup on shutdown + ## API Reference ### Core Integration Methods @@ -962,7 +978,6 @@ Main configuration for MCP integration. ```go type MCPConfig struct { - ServerPort *int `json:"server_port,omitempty"` // Port for local MCP server (only required for local tool setup, defaults to 8181) ClientConfigs []MCPClientConfig `json:"client_configs,omitempty"` // MCP client configurations (connection + filtering) } ``` @@ -973,12 +988,12 @@ Configuration for individual MCP clients, including both connection details and ```go type MCPClientConfig struct { - Name string `json:"name"` // Client name - ConnectionType MCPConnectionType `json:"connection_type"` // How to connect (HTTP or STDIO) - HTTPConnectionString *string `json:"http_connection_string,omitempty"` // HTTP URL (required for HTTP connections) - StdioConfig *MCPStdioConfig `json:"stdio_config,omitempty"` // STDIO configuration (required for STDIO connections) - ToolsToSkip []string `json:"tools_to_skip,omitempty"` // Tools to exclude from this client - ToolsToExecute []string `json:"tools_to_execute,omitempty"` // Tools to include from this client (if specified, only these are used) + Name string `json:"name"` // Client name + ConnectionType MCPConnectionType `json:"connection_type"` // How to connect (HTTP, STDIO, or SSE) + ConnectionString *string `json:"connection_string,omitempty"` // HTTP or SSE URL (required for HTTP or SSE connections) + StdioConfig *MCPStdioConfig `json:"stdio_config,omitempty"` // STDIO configuration (required for STDIO connections) + ToolsToSkip []string `json:"tools_to_skip,omitempty"` // Tools to exclude from this client + ToolsToExecute []string `json:"tools_to_execute,omitempty"` // Tools to include from this client (if specified, only these are used) } ``` @@ -990,6 +1005,7 @@ STDIO-specific configuration for launching external MCP tools. type MCPStdioConfig struct { Command string `json:"command"` // Executable command to run Args []string `json:"args"` // Command line arguments + Envs []string `json:"envs"` // Environment variables required } ``` @@ -1001,8 +1017,9 @@ Enumeration of supported connection types. type MCPConnectionType string const ( - MCPConnectionTypeHTTP MCPConnectionType = "http" // HTTP-based MCP connection + MCPConnectionTypeHTTP MCPConnectionType = "http" // HTTP-based MCP connection (streamable) MCPConnectionTypeSTDIO MCPConnectionType = "stdio" // STDIO-based MCP connection + MCPConnectionTypeSSE MCPConnectionType = "sse" // Server-Sent Events MCP connection ) ``` @@ -1038,10 +1055,10 @@ mcpConfig := &schemas.MCPConfig{ // ToolsToExecute: []string{"read_file", "list_files", "write_file"}, }, { - Name: "safe-tools", - ConnectionType: schemas.MCPConnectionTypeHTTP, - HTTPConnectionString: &[]string{"http://localhost:3000"}[0], - ToolsToExecute: []string{"search", "weather"}, // Only safe operations + Name: "safe-tools", + ConnectionType: schemas.MCPConnectionTypeHTTP, + ConnectionString: &[]string{"http://localhost:3000"}[0], + ToolsToExecute: []string{"search", "weather"}, // Only safe operations }, }, } @@ -1090,9 +1107,9 @@ mcpConfig := &schemas.MCPConfig{ ToolsToExecute: []string{"read_file", "list_files"}, // Only safe read operations }, { - Name: "weather", - ConnectionType: schemas.MCPConnectionTypeHTTP, - HTTPConnectionString: &[]string{"http://localhost:3000"}[0], + Name: "weather", + ConnectionType: schemas.MCPConnectionTypeHTTP, + ConnectionString: &[]string{"http://localhost:3000"}[0], // All tools available (no filtering) }, { @@ -1208,10 +1225,27 @@ For web-based MCP services: mcpConfig := &schemas.MCPConfig{ ClientConfigs: []schemas.MCPClientConfig{ { - Name: "cloud-service", - ConnectionType: schemas.MCPConnectionTypeHTTP, - HTTPConnectionString: &[]string{"https://api.example.com/mcp"}[0], - ToolsToSkip: []string{}, // No tools to skip + Name: "cloud-service", + ConnectionType: schemas.MCPConnectionTypeHTTP, + ConnectionString: &[]string{"https://api.example.com/mcp"}[0], + ToolsToSkip: []string{}, // No tools to skip + }, + }, +} +``` + +#### Connecting to SSE-based MCP Servers + +For Server-Sent Events based MCP services: + +```go +mcpConfig := &schemas.MCPConfig{ + ClientConfigs: []schemas.MCPClientConfig{ + { + Name: "real-time-service", + ConnectionType: schemas.MCPConnectionTypeSSE, + ConnectionString: &[]string{"https://api.example.com/sse"}[0], + ToolsToSkip: []string{}, // No tools to skip }, }, } @@ -1238,17 +1272,17 @@ clientConfig := schemas.MCPClientConfig{ ```go func getSecureMCPConfig(environment string) *schemas.MCPConfig { - config := &schemas.MCPConfig{ServerPort: ":8181"} + config := &schemas.MCPConfig{} switch environment { case "production": // Minimal tools in production config.ClientConfigs = []schemas.MCPClientConfig{ { - Name: "search", - ConnectionType: schemas.MCPConnectionTypeHTTP, - HTTPConnectionString: &[]string{"http://search-service:3000"}[0], - ToolsToExecute: []string{"web_search"}, + Name: "search", + ConnectionType: schemas.MCPConnectionTypeHTTP, + ConnectionString: &[]string{"http://search-service:3000"}[0], + ToolsToExecute: []string{"web_search"}, }, } case "development": @@ -1264,9 +1298,9 @@ func getSecureMCPConfig(environment string) *schemas.MCPConfig { ToolsToSkip: []string{"rm", "delete"}, }, { - Name: "search", - ConnectionType: schemas.MCPConnectionTypeHTTP, - HTTPConnectionString: &[]string{"http://localhost:3000"}[0], + Name: "search", + ConnectionType: schemas.MCPConnectionTypeHTTP, + ConnectionString: &[]string{"http://localhost:3000"}[0], }, } } @@ -1331,6 +1365,20 @@ Error: failed to initialize external MCP client: connection refused - Verify network connectivity - Check firewall settings +**SSE Connection Issues:** + +``` +Error: SSE stream error: context canceled +``` + +**Solutions:** + +- Verify the SSE server is running and accessible +- Check the SSE endpoint URL is correct +- Ensure the server supports Server-Sent Events protocol +- Check for network connectivity issues +- Verify the SSE stream is properly formatted + #### 2. Tool Registration Failures **Tool Already Exists:** diff --git a/tests/core-chatbot/go.mod b/tests/core-chatbot/go.mod index a4f23d77be..e8f0625c27 100644 --- a/tests/core-chatbot/go.mod +++ b/tests/core-chatbot/go.mod @@ -28,9 +28,11 @@ require ( github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.10.0 // indirect github.com/goccy/go-json v0.10.5 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/invopop/jsonschema v0.12.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.18.0 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.14 // indirect diff --git a/tests/core-chatbot/go.sum b/tests/core-chatbot/go.sum index 107f97329b..c7fbda48d7 100644 --- a/tests/core-chatbot/go.sum +++ b/tests/core-chatbot/go.sum @@ -51,8 +51,9 @@ github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXS github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI= github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= @@ -63,8 +64,9 @@ github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zt github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -91,8 +93,9 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -141,7 +144,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= diff --git a/tests/core-chatbot/main.go b/tests/core-chatbot/main.go index e1970e6b3a..2a734f1c7e 100644 --- a/tests/core-chatbot/main.go +++ b/tests/core-chatbot/main.go @@ -247,7 +247,13 @@ func NewChatSession(config ChatbotConfig) (*ChatSession, error) { Envs: []string{"SERPER_API_KEY"}, }, ToolsToSkip: []string{}, // No tools to skip for this client - }) + }, + schemas.MCPClientConfig{ + Name: "gmail-mcp", + ConnectionType: schemas.MCPConnectionTypeSSE, + ConnectionString: bifrost.Ptr("https://mcp.composio.dev/composio/server/654c1e3f-ea7d-47b6-9e31-398d00449654/sse"), + }, + ) fmt.Println("πŸ”Œ Configuring Context7 MCP server...") mcpConfig.ClientConfigs = append(mcpConfig.ClientConfigs, schemas.MCPClientConfig{ diff --git a/transports/README.md b/transports/README.md index dfde0f1c45..8a519ce5b0 100644 --- a/transports/README.md +++ b/transports/README.md @@ -118,7 +118,14 @@ Bifrost supports MCP integration for tool usage with AI models. You can configur { "name": "web-search", "connection_type": "http", - "http_connection_string": "http://localhost:3001/mcp", + "connection_string": "http://localhost:3001/mcp", + "tools_to_skip": [], + "tools_to_execute": [] + }, + { + "name": "real-time-data", + "connection_type": "sse", + "connection_string": "http://localhost:3002/sse", "tools_to_skip": [], "tools_to_execute": [] }