Build and publish / Test and build (darwin) (push) Failing after 1m42s
Build and publish / Test and build (linux) (push) Failing after 1m53s
Build and publish / Test and build (windows) (push) Failing after 2m50s
Build and publish / Build and publish Docker image (push) Skipped
Go rewrite of the Python Emby.MCP server with tool parity (20 tools), plus a streamable-HTTP transport with per-request Emby authentication (Basic / Bearer token passthrough), a multi-arch Docker build, and a Gitea CI workflow.
120 lines
3.9 KiB
Go
120 lines
3.9 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/gordon/emby-mcp/internal/emby"
|
|
"github.com/gordon/emby-mcp/internal/state"
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
)
|
|
|
|
func registerLibraryTools(s *mcp.Server, st *state.State) {
|
|
mcp.AddTool(s, &mcp.Tool{
|
|
Name: "retrieve_library_list",
|
|
Description: `Retrieve a list of libraries from the Emby media server in JSON format.
|
|
|
|
Returns:
|
|
List of Dict: as JSON with keys:
|
|
name (str): library name
|
|
id (str): library unique identifier
|
|
type (str): library media type`,
|
|
}, func(ctx context.Context, req *mcp.CallToolRequest, in struct{}) (*mcp.CallToolResult, any, error) {
|
|
libs, err := st.Client.GetLibraryList(ctx)
|
|
if err != nil {
|
|
st.SetLibraries(nil)
|
|
return textResult(errf("ERROR: failed to retrieve library list because: %v", err)), nil, nil
|
|
}
|
|
st.SetLibraries(libs)
|
|
return jsonResult(libs), nil, nil
|
|
})
|
|
|
|
mcp.AddTool(s, &mcp.Tool{
|
|
Name: "select_library",
|
|
Description: `Select a library on the Emby media server by supplying the library's name.
|
|
|
|
Args:
|
|
library_name (str): The name of the library to select, obtained from tool retrieve_library_list
|
|
|
|
Returns:
|
|
Str: "Success" or an error message`,
|
|
}, func(ctx context.Context, req *mcp.CallToolRequest, in struct {
|
|
LibraryName string `json:"library_name" jsonschema:"The name of the library to select, obtained from tool retrieve_library_list"`
|
|
}) (*mcp.CallToolResult, any, error) {
|
|
if in.LibraryName == "" {
|
|
return textResult("ERROR: no library name was supplied. Obtain library names from tool retrieve_library_list"), nil, nil
|
|
}
|
|
libs := st.Libraries()
|
|
if len(libs) == 0 {
|
|
fresh, err := st.Client.GetLibraryList(ctx)
|
|
if err != nil {
|
|
st.SetLibraries(nil)
|
|
return textResult(errf("ERROR: failed to retrieve library list because: %v", err)), nil, nil
|
|
}
|
|
st.SetLibraries(fresh)
|
|
libs = fresh
|
|
}
|
|
if len(libs) == 0 {
|
|
return textResult("ERROR: No available libraries found. Use tool retrieve_library_list to obtain a list of libraries."), nil, nil
|
|
}
|
|
for _, l := range libs {
|
|
if strings.EqualFold(l.Name, in.LibraryName) {
|
|
lib := l
|
|
st.SetCurrentLibrary(&lib)
|
|
return textResult("Success"), nil, nil
|
|
}
|
|
}
|
|
return textResult("ERROR: Library not found: " + in.LibraryName), nil, nil
|
|
})
|
|
|
|
mcp.AddTool(s, &mcp.Tool{
|
|
Name: "retrieve_current_library",
|
|
Description: `Retrieve the name of the currently selected library on the Emby media server in JSON format.
|
|
|
|
Returns:
|
|
Dict: as JSON with keys:
|
|
name (str): library name
|
|
id (str): library unique identifier
|
|
type (str): library media type`,
|
|
}, func(ctx context.Context, req *mcp.CallToolRequest, in struct{}) (*mcp.CallToolResult, any, error) {
|
|
cur := st.CurrentLibrary()
|
|
if cur == nil {
|
|
return textResult("ERROR: no library is currently selected. Select library using tool select_library"), nil, nil
|
|
}
|
|
return jsonResult(cur), nil, nil
|
|
})
|
|
|
|
mcp.AddTool(s, &mcp.Tool{
|
|
Name: "retrieve_genre_list",
|
|
Description: `Retrieve a list of item genres available in the current library on the Emby media server in JSON format.
|
|
|
|
Returns:
|
|
List of str: as JSON`,
|
|
}, func(ctx context.Context, req *mcp.CallToolRequest, in struct{}) (*mcp.CallToolResult, any, error) {
|
|
cur := st.CurrentLibrary()
|
|
if cur == nil {
|
|
return textResult("ERROR: no library is currently selected. Select library using tool select_library"), nil, nil
|
|
}
|
|
genres, err := st.Client.GetGenreList(ctx, cur.ID)
|
|
if err != nil {
|
|
return textResult(errf("ERROR: failed to retrieve genre list because: %v", err)), nil, nil
|
|
}
|
|
return jsonResult(genres), nil, nil
|
|
})
|
|
}
|
|
|
|
// ensureLibraries returns the cached library list, fetching it if empty.
|
|
func ensureLibraries(ctx context.Context, st *state.State) ([]emby.Library, error) {
|
|
libs := st.Libraries()
|
|
if len(libs) == 0 {
|
|
fresh, err := st.Client.GetLibraryList(ctx)
|
|
if err != nil {
|
|
st.SetLibraries(nil)
|
|
return nil, err
|
|
}
|
|
st.SetLibraries(fresh)
|
|
libs = fresh
|
|
}
|
|
return libs, nil
|
|
}
|