Build and publish / Test and build (darwin) (push) Successful in 1m43s
Build and publish / Test and build (linux) (push) Successful in 2m30s
Build and publish / Test and build (windows) (push) Successful in 3m5s
Build and publish / Build and publish Docker image (push) Successful in 1m46s
Implements the three roadmap feature sections:
- Browse: retrieve_item_children, retrieve_season_list, retrieve_episode_list;
search_for_item gains item_types (e.g. "Series") so shows are findable
without a selected library. Items now expose item_type, series_name, and
per-user played state/resume position.
- retrieve_next_episode: next_unplayed (Emby NextUp, resume counts) or latest
(most recent present episode; virtual placeholders skipped).
- retrieve_now_playing + set_subtitle/set_audio_track: stream listing and
track switching via SetSubtitleStreamIndex/SetAudioStreamIndex GeneralCommands,
with a PlayNow restart-at-position fallback for sessions that cannot switch
mid-play (DLNA/Chromecast bridges).
Conformance manifest grows to 34 call sites with three new documented spec
gaps (StartPositionTicks on PlayRequest, missing /Shows/{Id}/Episodes response
schema, undeclared Fields/EnableUserData on /Users/{UserId}/Items/{Id}).
60 lines
1.8 KiB
Go
60 lines
1.8 KiB
Go
// Package server wires the Emby tools into an MCP server using the official
|
|
// modelcontextprotocol go-sdk.
|
|
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/gordon/emby-mcp/internal/state"
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
)
|
|
|
|
const (
|
|
Name = "Emby.MCP"
|
|
Version = "1.0.2"
|
|
Purpose = `These MCP tools allow you to control an Emby media server. Using them you can retrieve
|
|
a list of libraries, genres, playlists, audio & video items, and player sessions.
|
|
You can browse series, seasons and episodes, find the next episode to watch,
|
|
inspect a player's now-playing audio/subtitle tracks, add items to playlists,
|
|
and play, pause, seek and stop items on a player session.`
|
|
)
|
|
|
|
// New builds the MCP server and registers all tools against the shared state.
|
|
func New(st *state.State) *mcp.Server {
|
|
s := mcp.NewServer(
|
|
&mcp.Implementation{Name: Name, Version: Version},
|
|
&mcp.ServerOptions{Instructions: Purpose},
|
|
)
|
|
registerUserTools(s, st)
|
|
registerLibraryTools(s, st)
|
|
registerItemTools(s, st)
|
|
registerBrowseTools(s, st)
|
|
registerPlaylistTools(s, st)
|
|
registerPlayerTools(s, st)
|
|
return s
|
|
}
|
|
|
|
// errf logs to stderr and returns the message in the Python "ERROR: ..." style.
|
|
func errf(format string, args ...any) string {
|
|
msg := fmt.Sprintf(format, args...)
|
|
log.Printf("%s", msg)
|
|
return msg
|
|
}
|
|
|
|
// textResult wraps a string as MCP text content (the Python tools all return
|
|
// bare strings — JSON documents or error messages).
|
|
func textResult(s string) *mcp.CallToolResult {
|
|
return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: s}}}
|
|
}
|
|
|
|
// jsonResult marshals v to JSON text; on failure returns an error string.
|
|
func jsonResult(v any) *mcp.CallToolResult {
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
return textResult(errf("ERROR: failed to encode result as JSON: %v", err))
|
|
}
|
|
return textResult(string(b))
|
|
}
|