upgrade mcp library

This commit is contained in:
2025-10-16 02:36:05 -04:00
parent 8debf3a3b3
commit 417499bf2f
5 changed files with 159 additions and 270 deletions

13
go.mod
View File

@@ -1,26 +1,19 @@
module github.com/taigrr/elevenlabs-mcp module github.com/taigrr/elevenlabs-mcp
go 1.24.6 go 1.25.2
require ( require (
github.com/gopxl/beep/v2 v2.1.1 github.com/gopxl/beep/v2 v2.1.1
github.com/mark3labs/mcp-go v0.37.0 github.com/modelcontextprotocol/go-sdk v1.0.0
github.com/taigrr/elevenlabs v0.1.18 github.com/taigrr/elevenlabs v0.1.18
) )
require ( require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/ebitengine/oto/v3 v3.3.3 // indirect github.com/ebitengine/oto/v3 v3.3.3 // indirect
github.com/ebitengine/purego v0.8.4 // indirect github.com/ebitengine/purego v0.8.4 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/jsonschema-go v0.3.0 // indirect
github.com/hajimehoshi/go-mp3 v0.3.4 // indirect github.com/hajimehoshi/go-mp3 v0.3.4 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/spf13/cast v1.9.2 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
golang.org/x/sys v0.35.0 // indirect golang.org/x/sys v0.35.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
) )

47
go.sum
View File

@@ -1,65 +1,34 @@
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/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ebitengine/oto/v3 v3.3.2 h1:VTWBsKX9eb+dXzaF4jEwQbs4yWIdXukJ0K40KgkpYlg=
github.com/ebitengine/oto/v3 v3.3.2/go.mod h1:MZeb/lwoC4DCOdiTIxYezrURTw7EvK/yF863+tmBI+U=
github.com/ebitengine/oto/v3 v3.3.3 h1:m6RV69OqoXYSWCDsHXN9rc07aDuDstGHtait7HXSM7g= github.com/ebitengine/oto/v3 v3.3.3 h1:m6RV69OqoXYSWCDsHXN9rc07aDuDstGHtait7HXSM7g=
github.com/ebitengine/oto/v3 v3.3.3/go.mod h1:MZeb/lwoC4DCOdiTIxYezrURTw7EvK/yF863+tmBI+U= github.com/ebitengine/oto/v3 v3.3.3/go.mod h1:MZeb/lwoC4DCOdiTIxYezrURTw7EvK/yF863+tmBI+U=
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
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/gopxl/beep/v2 v2.1.1 h1:6FYIYMm2qPAdWkjX+7xwKrViS1x0Po5kDMdRkq8NVbU= github.com/gopxl/beep/v2 v2.1.1 h1:6FYIYMm2qPAdWkjX+7xwKrViS1x0Po5kDMdRkq8NVbU=
github.com/gopxl/beep/v2 v2.1.1/go.mod h1:ZAm9TGQ9lvpoiFLd4zf5B1IuyxZhgRACMId1XJbaW0E= github.com/gopxl/beep/v2 v2.1.1/go.mod h1:ZAm9TGQ9lvpoiFLd4zf5B1IuyxZhgRACMId1XJbaW0E=
github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68= github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68=
github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo= github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo=
github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo= github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= github.com/modelcontextprotocol/go-sdk v1.0.0 h1:Z4MSjLi38bTgLrd/LjSmofqRqyBiVKRyQSJgw8q8V74=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/modelcontextprotocol/go-sdk v1.0.0/go.mod h1:nYtYQroQ2KQiM0/SbyEPUWQ6xs4B95gJjEalc9AQyOs=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
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/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mark3labs/mcp-go v0.37.0 h1:BywvZLPRT6Zx6mMG/MJfxLSZQkTGIcJSEGKsvr4DsoQ=
github.com/mark3labs/mcp-go v0.37.0/go.mod h1:T7tUa2jO6MavG+3P25Oy/jR7iCeJPHImCZHRymCn39g=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/taigrr/elevenlabs v0.1.18 h1:ZvRsLjzV4ov6Rdls7MNFKvcknLc527RR6beV6pjukJU= github.com/taigrr/elevenlabs v0.1.18 h1:ZvRsLjzV4ov6Rdls7MNFKvcknLc527RR6beV6pjukJU=
github.com/taigrr/elevenlabs v0.1.18/go.mod h1:ardwj7FGIQbpvRl+UpdRzVnxOuo7QQxqC3YhFcvY1gM= github.com/taigrr/elevenlabs v0.1.18/go.mod h1:ardwj7FGIQbpvRl+UpdRzVnxOuo7QQxqC3YhFcvY1gM=
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/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= 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= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/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/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -9,7 +9,7 @@ import (
"github.com/gopxl/beep/v2" "github.com/gopxl/beep/v2"
"github.com/gopxl/beep/v2/speaker" "github.com/gopxl/beep/v2/speaker"
"github.com/mark3labs/mcp-go/server" "github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/taigrr/elevenlabs/client" "github.com/taigrr/elevenlabs/client"
"github.com/taigrr/elevenlabs/client/types" "github.com/taigrr/elevenlabs/client/types"
) )
@@ -24,7 +24,7 @@ const (
) )
type Server struct { type Server struct {
mcpServer *server.MCPServer mcpServer *mcp.Server
client client.Client client client.Client
voices []types.VoiceResponseModel voices []types.VoiceResponseModel
currentVoice *types.VoiceResponseModel currentVoice *types.VoiceResponseModel
@@ -32,7 +32,7 @@ type Server struct {
playMutex sync.Mutex playMutex sync.Mutex
} }
func NewServer(mcpServer *server.MCPServer) (*Server, error) { func NewServer() (*mcp.Server, error) {
apiKey := os.Getenv("XI_API_KEY") apiKey := os.Getenv("XI_API_KEY")
if apiKey == "" { if apiKey == "" {
return nil, fmt.Errorf("XI_API_KEY environment variable is required") return nil, fmt.Errorf("XI_API_KEY environment variable is required")
@@ -40,6 +40,11 @@ func NewServer(mcpServer *server.MCPServer) (*Server, error) {
elevenClient := client.New(apiKey) elevenClient := client.New(apiKey)
mcpServer := mcp.NewServer(&mcp.Implementation{
Name: "ElevenLabs MCP Server",
Version: "1.0.0",
}, nil)
s := &Server{ s := &Server{
client: elevenClient, client: elevenClient,
mcpServer: mcpServer, mcpServer: mcpServer,
@@ -53,7 +58,9 @@ func NewServer(mcpServer *server.MCPServer) (*Server, error) {
return nil, fmt.Errorf("failed to initialize speaker: %w", err) return nil, fmt.Errorf("failed to initialize speaker: %w", err)
} }
return s, nil s.setupTools()
return mcpServer, nil
} }
func (s *Server) initializeVoices() error { func (s *Server) initializeVoices() error {

View File

@@ -5,203 +5,142 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/mark3labs/mcp-go/mcp" "github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/mark3labs/mcp-go/server"
"github.com/taigrr/elevenlabs/client/types" "github.com/taigrr/elevenlabs/client/types"
) )
func (s *Server) SetupTools() { type SayArgs struct {
s.mcpServer.AddTool(s.say()) Text string `json:"text" jsonschema:"Text to convert to speech"`
s.mcpServer.AddTool(s.read())
s.mcpServer.AddTool(s.play())
s.mcpServer.AddTool(s.setVoice())
s.mcpServer.AddTool(s.getVoices())
s.mcpServer.AddTool(s.history())
} }
func (s *Server) say() (mcp.Tool, server.ToolHandlerFunc) { type ReadArgs struct {
tool := mcp.Tool{ FilePath string `json:"file_path" jsonschema:"Path to the text file to read and convert to speech"`
}
type PlayArgs struct {
FilePath string `json:"file_path" jsonschema:"Path to the audio file to play"`
}
type SetVoiceArgs struct {
VoiceID string `json:"voice_id" jsonschema:"ID of the voice to use"`
}
func (s *Server) setupTools() {
mcp.AddTool(s.mcpServer, &mcp.Tool{
Name: "say", Name: "say",
Description: "Convert text to speech, save as MP3 file, and play the audio", Description: "Convert text to speech, save as MP3 file, and play the audio",
InputSchema: mcp.ToolInputSchema{ }, s.say)
Type: "object",
Properties: map[string]any{ mcp.AddTool(s.mcpServer, &mcp.Tool{
"text": map[string]any{ Name: "read",
"type": "string", Description: "Read a text file and convert it to speech, saving as MP3",
"description": "Text to convert to speech", }, s.read)
},
}, mcp.AddTool(s.mcpServer, &mcp.Tool{
Required: []string{"text"}, Name: "play",
}, Description: "Play an audio file",
}, s.play)
mcp.AddTool(s.mcpServer, &mcp.Tool{
Name: "set_voice",
Description: "Set the voice to use for text-to-speech generation",
}, s.setVoice)
mcp.AddTool(s.mcpServer, &mcp.Tool{
Name: "get_voices",
Description: "Get list of available voices and show the currently selected one",
}, s.getVoices)
mcp.AddTool(s.mcpServer, &mcp.Tool{
Name: "history",
Description: "List available audio files with text summaries",
}, s.history)
} }
handler := func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { func (s *Server) say(ctx context.Context, req *mcp.CallToolRequest, args SayArgs) (*mcp.CallToolResult, any, error) {
text, err := request.RequireString("text") filepath, err := s.GenerateAudio(args.Text)
if err != nil { if err != nil {
return nil, err return &mcp.CallToolResult{
} Content: []mcp.Content{
&mcp.TextContent{Text: fmt.Sprintf("Error: %v", err)},
filepath, err := s.GenerateAudio(text) },
if err != nil { IsError: true,
return nil, err }, nil, nil
} }
s.PlayAudioAsync(filepath) s.PlayAudioAsync(filepath)
return &mcp.CallToolResult{ return &mcp.CallToolResult{
Content: []mcp.Content{ Content: []mcp.Content{
mcp.TextContent{ &mcp.TextContent{Text: fmt.Sprintf("Audio generated, saved to %s, and playing", filepath)},
Type: "text",
Text: fmt.Sprintf("Audio generated, saved to %s, and playing", filepath),
}, },
}, }, nil, nil
}, nil
}
return tool, handler
} }
func (s *Server) read() (mcp.Tool, server.ToolHandlerFunc) { func (s *Server) read(ctx context.Context, req *mcp.CallToolRequest, args ReadArgs) (*mcp.CallToolResult, any, error) {
tool := mcp.Tool{ audioPath, err := s.ReadFileToAudio(args.FilePath)
Name: "read",
Description: "Read a text file and convert it to speech, saving as MP3",
InputSchema: mcp.ToolInputSchema{
Type: "object",
Properties: map[string]any{
"file_path": map[string]any{
"type": "string",
"description": "Path to the text file to read and convert to speech",
},
},
Required: []string{"file_path"},
},
}
handler := func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
filePath, err := request.RequireString("file_path")
if err != nil { if err != nil {
return nil, err return &mcp.CallToolResult{
} Content: []mcp.Content{
&mcp.TextContent{Text: fmt.Sprintf("Error: %v", err)},
audioPath, err := s.ReadFileToAudio(filePath) },
if err != nil { IsError: true,
return nil, err }, nil, nil
} }
return &mcp.CallToolResult{ return &mcp.CallToolResult{
Content: []mcp.Content{ Content: []mcp.Content{
mcp.TextContent{ &mcp.TextContent{Text: fmt.Sprintf("File '%s' converted to speech and saved to: %s", args.FilePath, audioPath)},
Type: "text",
Text: fmt.Sprintf("File '%s' converted to speech and saved to: %s", filePath, audioPath),
}, },
}, }, nil, nil
}, nil
}
return tool, handler
} }
func (s *Server) play() (mcp.Tool, server.ToolHandlerFunc) { func (s *Server) play(ctx context.Context, req *mcp.CallToolRequest, args PlayArgs) (*mcp.CallToolResult, any, error) {
tool := mcp.Tool{ s.PlayAudioAsync(args.FilePath)
Name: "play",
Description: "Play an audio file",
InputSchema: mcp.ToolInputSchema{
Type: "object",
Properties: map[string]any{
"file_path": map[string]any{
"type": "string",
"description": "Path to the audio file to play",
},
},
Required: []string{"file_path"},
},
}
handler := func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
filePath, err := request.RequireString("file_path")
if err != nil {
return nil, err
}
s.PlayAudioAsync(filePath)
return &mcp.CallToolResult{ return &mcp.CallToolResult{
Content: []mcp.Content{ Content: []mcp.Content{
mcp.TextContent{ &mcp.TextContent{Text: fmt.Sprintf("Playing audio file: %s", args.FilePath)},
Type: "text",
Text: fmt.Sprintf("Playing audio file: %s", filePath),
}, },
}, }, nil, nil
}, nil
}
return tool, handler
} }
func (s *Server) setVoice() (mcp.Tool, server.ToolHandlerFunc) { func (s *Server) setVoice(ctx context.Context, req *mcp.CallToolRequest, args SetVoiceArgs) (*mcp.CallToolResult, any, error) {
tool := mcp.Tool{ selectedVoice, err := s.SetVoice(args.VoiceID)
Name: "set_voice",
Description: "Set the voice to use for text-to-speech generation",
InputSchema: mcp.ToolInputSchema{
Type: "object",
Properties: map[string]any{
"voice_id": map[string]any{
"type": "string",
"description": "ID of the voice to use",
},
},
Required: []string{"voice_id"},
},
}
handler := func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
voiceID, err := request.RequireString("voice_id")
if err != nil { if err != nil {
return nil, err return &mcp.CallToolResult{
} Content: []mcp.Content{
&mcp.TextContent{Text: fmt.Sprintf("Error: %v", err)},
selectedVoice, err := s.SetVoice(voiceID) },
if err != nil { IsError: true,
return nil, err }, nil, nil
} }
return &mcp.CallToolResult{ return &mcp.CallToolResult{
Content: []mcp.Content{ Content: []mcp.Content{
mcp.TextContent{ &mcp.TextContent{Text: fmt.Sprintf("Voice set to: %s (%s)", selectedVoice.Name, selectedVoice.VoiceID)},
Type: "text",
Text: fmt.Sprintf("Voice set to: %s (%s)", selectedVoice.Name, selectedVoice.VoiceID),
}, },
}, }, nil, nil
}, nil
}
return tool, handler
} }
func (s *Server) getVoices() (mcp.Tool, server.ToolHandlerFunc) { func (s *Server) getVoices(ctx context.Context, req *mcp.CallToolRequest, args struct{}) (*mcp.CallToolResult, any, error) {
tool := mcp.Tool{
Name: "get_voices",
Description: "Get list of available voices and show the currently selected one",
InputSchema: mcp.ToolInputSchema{
Type: "object",
Properties: map[string]any{},
},
}
handler := func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
voices, currentVoice, err := s.GetVoices() voices, currentVoice, err := s.GetVoices()
if err != nil { if err != nil {
return nil, err return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: fmt.Sprintf("Error: %v", err)},
},
IsError: true,
}, nil, nil
} }
voiceList := s.formatVoiceList(voices, currentVoice) voiceList := s.formatVoiceList(voices, currentVoice)
return &mcp.CallToolResult{ return &mcp.CallToolResult{
Content: []mcp.Content{ Content: []mcp.Content{
mcp.TextContent{ &mcp.TextContent{Text: voiceList},
Type: "text",
Text: voiceList,
}, },
}, }, nil, nil
}, nil
}
return tool, handler
} }
func (s *Server) formatVoiceList(voices []types.VoiceResponseModel, currentVoice *types.VoiceResponseModel) string { func (s *Server) formatVoiceList(voices []types.VoiceResponseModel, currentVoice *types.VoiceResponseModel) string {
@@ -227,45 +166,32 @@ func (s *Server) formatVoiceList(voices []types.VoiceResponseModel, currentVoice
return voiceList.String() return voiceList.String()
} }
func (s *Server) history() (mcp.Tool, server.ToolHandlerFunc) { func (s *Server) history(ctx context.Context, req *mcp.CallToolRequest, args struct{}) (*mcp.CallToolResult, any, error) {
tool := mcp.Tool{
Name: "history",
Description: "List available audio files with text summaries",
InputSchema: mcp.ToolInputSchema{
Type: "object",
Properties: map[string]any{},
},
}
handler := func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
audioFiles, err := s.GetAudioHistory() audioFiles, err := s.GetAudioHistory()
if err != nil { if err != nil {
return nil, err return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: fmt.Sprintf("Error: %v", err)},
},
IsError: true,
}, nil, nil
} }
if len(audioFiles) == 0 { if len(audioFiles) == 0 {
return &mcp.CallToolResult{ return &mcp.CallToolResult{
Content: []mcp.Content{ Content: []mcp.Content{
mcp.TextContent{ &mcp.TextContent{Text: "No audio files found"},
Type: "text",
Text: "No audio files found",
}, },
}, }, nil, nil
}, nil
} }
historyList := s.formatHistoryList(audioFiles) historyList := s.formatHistoryList(audioFiles)
return &mcp.CallToolResult{ return &mcp.CallToolResult{
Content: []mcp.Content{ Content: []mcp.Content{
mcp.TextContent{ &mcp.TextContent{Text: historyList},
Type: "text",
Text: historyList,
}, },
}, }, nil, nil
}, nil
}
return tool, handler
} }
func (s *Server) formatHistoryList(audioFiles []AudioFile) string { func (s *Server) formatHistoryList(audioFiles []AudioFile) string {

14
main.go
View File

@@ -1,26 +1,20 @@
package main package main
import ( import (
"context"
"log" "log"
"github.com/mark3labs/mcp-go/server" "github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/taigrr/elevenlabs-mcp/internal/ximcp" "github.com/taigrr/elevenlabs-mcp/internal/ximcp"
) )
func main() { func main() {
mcpServer := server.NewMCPServer( server, err := ximcp.NewServer()
"ElevenLabs MCP Server",
"1.0.0",
server.WithToolCapabilities(true),
)
elevenServer, err := ximcp.NewServer(mcpServer)
if err != nil { if err != nil {
log.Fatalf("Failed to create ElevenLabs server: %v", err) log.Fatalf("Failed to create ElevenLabs server: %v", err)
} }
elevenServer.SetupTools() if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
if err := server.ServeStdio(mcpServer); err != nil {
log.Fatalf("Failed to serve MCP server: %v", err) log.Fatalf("Failed to serve MCP server: %v", err)
} }
} }