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.
53 lines
1.6 KiB
Go
53 lines
1.6 KiB
Go
package mcphttp
|
|
|
|
import (
|
|
"net/http"
|
|
"runtime"
|
|
|
|
"github.com/gordon/emby-mcp/internal/config"
|
|
"github.com/gordon/emby-mcp/internal/server"
|
|
"github.com/gordon/emby-mcp/internal/state"
|
|
"github.com/modelcontextprotocol/go-sdk/auth"
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
)
|
|
|
|
// Endpoint is the HTTP path the MCP streamable transport is served on.
|
|
const Endpoint = "/mcp"
|
|
|
|
// NewHandler builds the HTTP handler for MCP over streamable HTTP. Every
|
|
// request must carry Emby credentials: "Authorization: Basic user:pass" or
|
|
// "Authorization: Bearer <emby-token>" (optionally with X-Emby-User-Id or
|
|
// X-Emby-Username to disambiguate multi-user tokens).
|
|
func NewHandler(cfg *config.Config, hostname string) http.Handler {
|
|
a := newAuthenticator(cfg, server.Name+" for AI", server.Version, hostname, runtime.GOOS)
|
|
|
|
getServer := func(req *http.Request) *mcp.Server {
|
|
client, userID := authenticatedClient(req.Context())
|
|
if client == nil {
|
|
return nil
|
|
}
|
|
// Per-session state: library selection and search chunking are
|
|
// isolated between concurrent HTTP clients.
|
|
return server.New(state.New(client, userID, cfg.MaxChunkSize))
|
|
}
|
|
mcpHandler := mcp.NewStreamableHTTPHandler(getServer, &mcp.StreamableHTTPOptions{
|
|
SessionTimeout: cfg.SessionTimeout,
|
|
})
|
|
|
|
chain := a.translateBasic(
|
|
auth.RequireBearerToken(a.verify, &auth.RequireBearerTokenOptions{
|
|
AllowMissingExpiration: true,
|
|
})(
|
|
a.stash(mcpHandler),
|
|
),
|
|
)
|
|
|
|
mux := http.NewServeMux()
|
|
mux.Handle(Endpoint, chain)
|
|
mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte("ok\n"))
|
|
})
|
|
return mux
|
|
}
|