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

91 lines
4.7 KiB
Go

package server
import (
"context"
"strings"
"github.com/gordon/emby-mcp/internal/state"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
func registerPlayerTools(s *mcp.Server, st *state.State) {
mcp.AddTool(s, &mcp.Tool{
Name: "retrieve_player_list",
Description: `Retrieve a list of media players that we can use with the supplied media type in JSON format.
A human may use any JSON field to identify a player, but do not display the 'device_id' or 'session_id'
You must only supply the 'session_id' field to identify the player when using the control_media_player or retrieve_player_queue tools.
Args:
media_type (str, optional): List only players of this media type (one of: 'Audio', 'Video', 'Photo') or an empty string to list all players
Returns:
List of dicts as JSON with keys including client_name, session_id, device_id, device_name,
device_ip_address, local_to_media_server, media_types, and now_playing_* fields.`,
}, func(ctx context.Context, req *mcp.CallToolRequest, in struct {
MediaType string `json:"media_type" jsonschema:"List only players of this media type (one of: 'Audio', 'Video', 'Photo') or empty for all"`
}) (*mcp.CallToolResult, any, error) {
sessions, err := st.Client.GetPlayerSessions(ctx, st.UserID, in.MediaType)
if err != nil {
return textResult(errf("ERROR: failed to retrieve player list because: %v", err)), nil, nil
}
return jsonResult(sessions), nil, nil
})
mcp.AddTool(s, &mcp.Tool{
Name: "retrieve_player_queue",
Description: `Retrieve a list of items in the play queue of a media player in JSON format.
Args:
session_id (str): The ID of the player session to query, obtained from tool retrieve_player_list
Returns:
List of dicts as JSON with keys including title, artists, album, genres, overview,
media_type, bitrate, run_time, item_id, playlist_item_id.`,
}, func(ctx context.Context, req *mcp.CallToolRequest, in struct {
SessionID string `json:"session_id" jsonschema:"The ID of the player session to query, obtained from tool retrieve_player_list"`
}) (*mcp.CallToolResult, any, error) {
items, err := st.Client.GetPlayQueueItems(ctx, in.SessionID)
if err != nil {
return textResult(errf("ERROR: failed to retrieve player queue items because: %v", err)), nil, nil
}
return jsonResult(items), nil, nil
})
mcp.AddTool(s, &mcp.Tool{
Name: "control_media_player",
Description: `Control the media player identified as 'session_id' by sending it a 'command'.
Valid commands are: 'PlayNow', 'Stop', 'Pause', 'Unpause', 'NextTrack', 'PreviousTrack', 'Seek', 'Rewind', 'FastForward'.
The PlayNow command requires 'item_ids' contain one or more comma separated 'item_id' obtained from the retrieve_item_list_by_genre tool.
Seek, Rewind, FastForward can specify a time in milliseconds. The 'session_id' is obtained from the retrieve_player_list tool.
Args:
session_id (str): The ID of the player session to control, obtained from tool retrieve_player_list
command (str): One of 'PlayNow', 'Stop', 'Pause', 'Unpause', 'NextTrack', 'PreviousTrack', 'Seek', 'Rewind', 'FastForward'.
item_ids (str, optional): The ID of one or more items obtained from tool search_for_item to add to the play queue as a comma separated list. Required for command 'PlayNow'.
time_milliseconds (int, optional): The time in milliseconds for commands 'Seek', 'Rewind', 'FastForward'. If 0 or None then defaults will be used.
Returns:
Str: success messsage or error message.`,
}, func(ctx context.Context, req *mcp.CallToolRequest, in struct {
SessionID string `json:"session_id" jsonschema:"The ID of the player session to control"`
Command string `json:"command" jsonschema:"One of 'PlayNow', 'Stop', 'Pause', 'Unpause', 'NextTrack', 'PreviousTrack', 'Seek', 'Rewind', 'FastForward'"`
ItemIDs string `json:"item_ids" jsonschema:"Comma separated item IDs; required for 'PlayNow'"`
TimeMilliseconds int64 `json:"time_milliseconds" jsonschema:"Time in ms for 'Seek', 'Rewind', 'FastForward'"`
}) (*mcp.CallToolResult, any, error) {
if in.SessionID == "" {
return textResult("ERROR: no session_id was supplied. Obtain session_id from tool retrieve_player_list"), nil, nil
}
if in.Command == "" {
return textResult("ERROR: no command was supplied. Valid commands are: 'PlayNow', 'Stop', 'Pause', 'Unpause', 'NextTrack', 'PreviousTrack', 'Seek', 'Rewind', 'FastForward'."), nil, nil
}
command := in.Command
if strings.EqualFold(command, "play") {
command = "PlayNow"
}
if err := st.Client.SendPlayerCommand(ctx, in.SessionID, command, in.ItemIDs, st.UserID, in.TimeMilliseconds); err != nil {
return textResult(errf("ERROR: failed to control the player because: %v", err)), nil, nil
}
return textResult("Success"), nil, nil
})
}