Files
gronod d40646211f
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
Initial Go implementation of Emby.MCP
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.
2026-09-20 22:52:46 +01:00

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
}