mcp-lyricscom 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![CI](https://github.com/smeet666/mcp-lyricscom/actions/workflows/ci.yml/badge.svg)](https://github.com/smeet666/mcp-lyricscom/actions/workflows/ci.yml)
5
5
  [![license](https://img.shields.io/npm/l/mcp-lyricscom.svg)](./LICENSE)
6
6
  [![MCP Registry](https://img.shields.io/badge/MCP_Registry-listed-6E56CF)](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.smeet666/mcp-lyricscom)
7
+ [![Glama](https://glama.ai/mcp/servers/smeet666/mcp-lyricscom/badges/score.svg)](https://glama.ai/mcp/servers/smeet666/mcp-lyricscom)
7
8
 
8
9
  An [MCP](https://modelcontextprotocol.io) server for [lyrics.com](https://www.lyrics.com).
9
10
  Search songs by a word in their lyrics, by title, and read the full text.
@@ -41,6 +42,14 @@ claude mcp add lyricscom -- npx -y mcp-lyricscom
41
42
 
42
43
  That is the whole setup. There is nothing to sign up for.
43
44
 
45
+ **Bundle, without npm**
46
+
47
+ Download `mcp-lyricscom-<version>.mcpb` from
48
+ [the latest release](https://github.com/smeet666/mcp-lyricscom/releases/latest) and open
49
+ it. A client that supports MCP bundles installs it on its own, with no npm and
50
+ no configuration file to edit. The bundle carries its dependencies, so nothing
51
+ is fetched at install time.
52
+
44
53
  ## Tools
45
54
 
46
55
  | Tool | What it does | Key parameters |
@@ -240,6 +249,14 @@ claude mcp add lyricscom -- npx -y mcp-lyricscom
240
249
 
241
250
  C'est toute l'installation. Il n'y a aucune inscription.
242
251
 
252
+ **Bundle, sans npm**
253
+
254
+ Téléchargez `mcp-lyricscom-<version>.mcpb` depuis
255
+ [la dernière release](https://github.com/smeet666/mcp-lyricscom/releases/latest) et
256
+ ouvrez-le. Un client compatible avec les bundles MCP l'installe seul, sans npm
257
+ ni fichier de configuration à modifier. Le bundle embarque ses dépendances,
258
+ donc rien n'est téléchargé à l'installation.
259
+
243
260
  ## Outils
244
261
 
245
262
  | Outil | Rôle | Paramètres principaux |
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
 
6
6
  // src/version.ts
7
- var PKG_VERSION = "1.2.0";
7
+ var PKG_VERSION = "1.4.0";
8
8
  var REPO_URL = "https://github.com/smeet666/mcp-lyricscom";
9
9
 
10
10
  // src/config.ts
@@ -108,7 +108,7 @@ function createLogger(level) {
108
108
 
109
109
  // src/server.ts
110
110
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
111
- import { z as z5 } from "zod";
111
+ import { z as z6 } from "zod";
112
112
 
113
113
  // src/lyricscom/cache.ts
114
114
  var TtlLruCache = class {
@@ -679,7 +679,7 @@ var LyricsComClient = class {
679
679
  };
680
680
 
681
681
  // src/tools/getLyrics.ts
682
- import { z as z2 } from "zod";
682
+ import { z as z3 } from "zod";
683
683
 
684
684
  // src/text/keywordIndex.ts
685
685
  var WORD_DELIMITERS = "[\\s,;:'\".!?()\\[\\]&\u2019`@-]";
@@ -734,17 +734,69 @@ function matchedLineExcerpt(text, keyword, options = {}) {
734
734
  return `${start > 0 ? "\u2026" : ""}${slice}${end < line.length ? "\u2026" : ""}`;
735
735
  }
736
736
 
737
- // src/tools/shared.ts
737
+ // src/tools/arguments.ts
738
738
  import { z } from "zod";
739
+ var INVALID_INPUT = "invalid_input";
740
+ function strictInput(shape) {
741
+ const declared = Object.keys(shape);
742
+ const errorMap = (issue, ctx) => issue.code === z.ZodIssueCode.unrecognized_keys ? { message: unknownArgumentMessage(issue.keys, declared) } : { message: ctx.defaultError };
743
+ return z.strictObject(shape, { errorMap });
744
+ }
745
+ function unknownArgumentMessage(keys, declared) {
746
+ const named = keys.map((key) => {
747
+ const near = nearestArgument(key, declared);
748
+ return near ? `'${key}' (did you mean '${near}'?)` : `'${key}'`;
749
+ }).join(", ");
750
+ return `[${INVALID_INPUT}] Unknown ${keys.length > 1 ? "arguments" : "argument"} ${named}. This tool takes: ${declared.join(", ")}.`;
751
+ }
752
+ function nearestArgument(key, declared) {
753
+ const flatten = (name) => name.toLowerCase().replace(/[^a-z0-9]/g, "");
754
+ const flat = flatten(key);
755
+ if (flat.length === 0) return void 0;
756
+ const sameName = declared.find((name) => flatten(name) === flat);
757
+ if (sameName) return sameName;
758
+ const overlapping = declared.find((name) => {
759
+ const other = flatten(name);
760
+ const [shorter, longer] = other.length < flat.length ? [other, flat] : [flat, other];
761
+ return shorter.length >= 3 && (longer.startsWith(shorter) || longer.endsWith(shorter));
762
+ });
763
+ if (overlapping) return overlapping;
764
+ let closest;
765
+ let shortest = Number.POSITIVE_INFINITY;
766
+ for (const name of declared) {
767
+ const distance = editDistance(flat, flatten(name));
768
+ if (distance < shortest) {
769
+ shortest = distance;
770
+ closest = name;
771
+ }
772
+ }
773
+ return shortest <= Math.max(1, Math.floor(flat.length / 3)) ? closest : void 0;
774
+ }
775
+ function editDistance(left, right) {
776
+ let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
777
+ for (let row = 1; row <= left.length; row += 1) {
778
+ const current = [row];
779
+ for (let column = 1; column <= right.length; column += 1) {
780
+ const substitution = previous[column - 1] + (left[row - 1] === right[column - 1] ? 0 : 1);
781
+ current[column] = Math.min(substitution, previous[column] + 1, current[column - 1] + 1);
782
+ }
783
+ previous = current;
784
+ }
785
+ return previous[right.length];
786
+ }
787
+
788
+ // src/tools/shared.ts
789
+ import { z as z2 } from "zod";
739
790
  var MAX_TEXT_MIRROR_CHARS = 1500;
740
- var songResultSchema = z.object({
741
- id: z.string().describe("lyrics.com song id. Pass this to get_lyrics."),
742
- title: z.string(),
743
- artist: z.string(),
744
- album: z.string().nullable(),
745
- year: z.number().int().nullable(),
746
- source_url: z.string().describe("Canonical lyrics.com page for this song."),
747
- excerpt: z.string().nullable().describe("Short lyric line showing where the query appears, when available.")
791
+ var MAX_PAGE = 20;
792
+ var songResultSchema = z2.object({
793
+ id: z2.string().describe("lyrics.com song id. Pass this to get_lyrics."),
794
+ title: z2.string(),
795
+ artist: z2.string(),
796
+ album: z2.string().nullable(),
797
+ year: z2.number().int().nullable(),
798
+ source_url: z2.string().describe("Canonical lyrics.com page for this song."),
799
+ excerpt: z2.string().nullable().describe("Short lyric line showing where the query appears, when available.")
748
800
  });
749
801
  function toSongResultOut(song, excerpt) {
750
802
  return {
@@ -757,9 +809,17 @@ function toSongResultOut(song, excerpt) {
757
809
  excerpt
758
810
  };
759
811
  }
760
- function ok(structured, text) {
812
+ function indentMarkerLines(body) {
813
+ return body.replace(/^(Note:)/gm, " $1");
814
+ }
815
+ function ok(structured, text, notes = []) {
816
+ const trailer = notes.map((note) => `Note: ${note}`).join("\n");
817
+ const budget = MAX_TEXT_MIRROR_CHARS - (trailer ? trailer.length + 2 : 0);
818
+ const body = truncate(indentMarkerLines(text), Math.max(0, budget));
761
819
  return {
762
- content: [{ type: "text", text: truncate(text, MAX_TEXT_MIRROR_CHARS) }],
820
+ content: [{ type: "text", text: trailer ? `${body}
821
+
822
+ ${trailer}` : body }],
763
823
  structuredContent: structured
764
824
  };
765
825
  }
@@ -793,38 +853,39 @@ var getLyricsDescription = [
793
853
  "lyrics field, which is a valid answer and not an error, so do not retry them.",
794
854
  "Always cite 'source_url' and the artist when showing lyrics to a user."
795
855
  ].join(" ");
796
- var getLyricsInputShape = {
797
- id: z2.string().regex(/^\d+$/, "Song ids are digits only.").optional().describe("lyrics.com numeric song id, as returned by the search tools. Preferred over 'url'."),
798
- url: z2.string().url().optional().describe(
856
+ var getLyricsInput = strictInput({
857
+ id: z3.string().regex(/^\d+$/, "Song ids are digits only.").optional().describe("lyrics.com numeric song id, as returned by the search tools. Preferred over 'url'."),
858
+ url: z3.string().url().optional().describe(
799
859
  "Full lyrics.com song URL. Only www.lyrics.com URLs are accepted. Ignored when 'id' is given."
800
860
  ),
801
- max_chars: z2.number().int().min(200).max(2e4).default(6e3).describe("Maximum characters of lyrics text to return in this call."),
802
- offset: z2.number().int().min(0).default(0).describe("Character offset to resume from, for lyrics longer than max_chars."),
803
- highlight: z2.string().max(120).optional().describe(
861
+ max_chars: z3.number().int().min(200).max(2e4).default(6e3).describe("Maximum characters of lyrics text to return in this call."),
862
+ offset: z3.number().int().min(0).default(0).describe("Character offset to resume from, for lyrics longer than max_chars."),
863
+ highlight: z3.string().max(120).optional().describe(
804
864
  "Optional word. When set, the response reports whether it appears in the lyrics and on which line."
805
865
  )
806
- };
866
+ });
807
867
  var getLyricsOutputShape = {
808
- status: z2.enum(["ok", "no_lyrics"]),
809
- id: z2.string(),
810
- title: z2.string().nullable(),
811
- artist: z2.string().nullable(),
812
- source_url: z2.string(),
813
- lyrics: z2.string().describe("Slice of the lyrics text. Empty when status is 'no_lyrics'."),
814
- total_chars: z2.number().int(),
815
- returned_chars: z2.number().int(),
816
- offset: z2.number().int(),
817
- next_offset: z2.number().int().nullable(),
818
- truncated: z2.boolean(),
819
- line_count: z2.number().int(),
820
- highlight: z2.object({
821
- word: z2.string(),
822
- found: z2.boolean(),
823
- line_number: z2.number().int().nullable(),
824
- line: z2.string().nullable()
868
+ status: z3.enum(["ok", "no_lyrics"]),
869
+ id: z3.string(),
870
+ title: z3.string().nullable(),
871
+ artist: z3.string().nullable(),
872
+ source_url: z3.string(),
873
+ lyrics: z3.string().describe("Slice of the lyrics text. Empty when status is 'no_lyrics'."),
874
+ total_chars: z3.number().int(),
875
+ returned_chars: z3.number().int(),
876
+ offset: z3.number().int(),
877
+ next_offset: z3.number().int().nullable(),
878
+ truncated: z3.boolean(),
879
+ line_count: z3.number().int(),
880
+ highlight: z3.object({
881
+ word: z3.string(),
882
+ found: z3.boolean(),
883
+ line_number: z3.number().int().nullable(),
884
+ line: z3.string().nullable()
825
885
  }).nullable(),
826
- attribution: z2.string().describe("Ready-to-display credit line."),
827
- source: z2.literal("lyrics.com")
886
+ attribution: z3.string().describe("Ready-to-display credit line."),
887
+ source: z3.literal("lyrics.com"),
888
+ notes: z3.array(z3.string())
828
889
  };
829
890
  function sliceAtLineBoundary(text, offset, maxChars) {
830
891
  if (offset >= text.length) return { slice: "", nextOffset: null };
@@ -840,7 +901,9 @@ async function runGetLyrics(client, args) {
840
901
  const ref = {};
841
902
  if (args.id) ref.id = args.id;
842
903
  else if (args.url) ref.url = args.url;
843
- const { data } = await client.getSong(ref);
904
+ const { data, cached } = await client.getSong(ref);
905
+ const notes = [];
906
+ if (cached) notes.push("Served from this server's short-lived in-memory cache.");
844
907
  const credit = [data.title, data.artist].filter(Boolean).join(" \u2014 ");
845
908
  const attribution = `${credit || "Lyrics"} via lyrics.com \u2014 ${data.url}`;
846
909
  if (!data.hasLyrics) {
@@ -856,6 +919,7 @@ async function runGetLyrics(client, args) {
856
919
  offset: 0,
857
920
  next_offset: null,
858
921
  truncated: false,
922
+ notes,
859
923
  line_count: 0,
860
924
  highlight: null,
861
925
  attribution,
@@ -889,24 +953,31 @@ ${attribution}`
889
953
  line: match?.line ?? null
890
954
  } : null,
891
955
  attribution,
892
- source: "lyrics.com"
956
+ source: "lyrics.com",
957
+ notes
893
958
  };
959
+ const pastTheEnd = args.offset > 0 && slice === "" && data.lyrics.length > 0;
960
+ if (pastTheEnd) {
961
+ notes.push(
962
+ `offset=${args.offset} is past the end of a body of ${data.lyrics.length} characters. Call again with offset=0 to read it from the start.`
963
+ );
964
+ }
894
965
  const summary = [
895
966
  attribution,
896
967
  `${structured.line_count} lines, ${structured.total_chars} characters.`,
897
- nextOffset !== null ? `Truncated: call again with offset=${nextOffset} to continue.` : "Complete.",
968
+ pastTheEnd ? `Nothing at offset=${args.offset}: that is past the end. Call again with offset=0.` : nextOffset !== null ? `Truncated: call again with offset=${nextOffset} to continue.` : "Complete.",
898
969
  structured.highlight ? structured.highlight.found ? `"${structured.highlight.word}" appears on line ${structured.highlight.line_number}.` : `"${structured.highlight.word}" does not appear in these lyrics.` : ""
899
970
  ].filter(Boolean).join("\n");
900
971
  return ok(structured, truncate(`${summary}
901
972
 
902
- ${slice}`, 4e3));
973
+ ${slice}`, 4e3), notes);
903
974
  } catch (error) {
904
975
  return toToolError(error);
905
976
  }
906
977
  }
907
978
 
908
979
  // src/tools/searchLyrics.ts
909
- import { z as z3 } from "zod";
980
+ import { z as z4 } from "zod";
910
981
 
911
982
  // src/text/dedupe.ts
912
983
  function songKey(artist, title) {
@@ -938,25 +1009,25 @@ var searchLyricsDescription = [
938
1009
  "lyrics.com also returns loose and title-only matches, so results are filtered locally to keep only songs",
939
1010
  `where the word genuinely appears in the lyrics. Set 'verify' to "none" to see the raw, unfiltered list.`
940
1011
  ].join(" ");
941
- var searchLyricsInputShape = {
942
- query: z3.string().min(1).max(120).describe("Word or short phrase to look for inside the lyrics, for example 'autrefois'."),
943
- limit: z3.number().int().min(1).max(50).default(10).describe("Maximum songs to return from this page."),
944
- page: z3.number().int().min(1).max(20).default(1).describe("Result page on lyrics.com. Each page holds up to 24 raw results."),
945
- verify: z3.enum(["snippet", "full", "none"]).default("snippet").describe(
1012
+ var searchLyricsInput = strictInput({
1013
+ query: z4.string().min(1).max(120).describe("Word or short phrase to look for inside the lyrics, for example 'autrefois'."),
1014
+ limit: z4.number().int().min(1).max(50).default(10).describe("Maximum songs to return from this page."),
1015
+ page: z4.number().int().min(1).max(20).default(1).describe("Result page on lyrics.com. Each page holds up to 24 raw results."),
1016
+ verify: z4.enum(["snippet", "full", "none"]).default("snippet").describe(
946
1017
  "How to confirm the query really appears in the lyrics. 'snippet' checks the excerpt lyrics.com already returned and costs nothing. 'full' fetches up to 5 song pages and checks the complete lyrics, which is slow and can trigger rate limiting. 'none' disables filtering."
947
1018
  ),
948
- include_excerpt: z3.boolean().default(true).describe("Include the matching lyric line excerpt in each result.")
949
- };
1019
+ include_excerpt: z4.boolean().default(true).describe("Include the matching lyric line excerpt in each result.")
1020
+ });
950
1021
  var searchLyricsOutputShape = {
951
- query: z3.string(),
952
- page: z3.number().int(),
953
- results: z3.array(songResultSchema),
954
- raw_result_count: z3.number().int().describe("Rows lyrics.com returned before local filtering and deduplication."),
955
- filtered_out: z3.number().int().describe("Rows dropped because the query was not found in their lyrics."),
956
- has_more: z3.boolean(),
957
- next_page: z3.number().int().nullable(),
958
- source: z3.literal("lyrics.com"),
959
- notes: z3.array(z3.string()).describe("Caveats worth knowing, such as a cache hit or partial verification.")
1022
+ query: z4.string(),
1023
+ page: z4.number().int(),
1024
+ results: z4.array(songResultSchema),
1025
+ raw_result_count: z4.number().int().describe("Rows lyrics.com returned before local filtering and deduplication."),
1026
+ filtered_out: z4.number().int().describe("Rows dropped because the query was not found in their lyrics."),
1027
+ has_more: z4.boolean(),
1028
+ next_page: z4.number().int().nullable(),
1029
+ source: z4.literal("lyrics.com"),
1030
+ notes: z4.array(z4.string()).describe("Caveats worth knowing, such as a cache hit or partial verification.")
960
1031
  };
961
1032
  async function runSearchLyrics(client, args) {
962
1033
  try {
@@ -1006,14 +1077,16 @@ async function runSearchLyrics(client, args) {
1006
1077
  raw_result_count: data.rawCount,
1007
1078
  filtered_out: filteredOut,
1008
1079
  has_more: data.hasMore,
1009
- next_page: data.hasMore ? args.page + 1 : null,
1080
+ next_page: data.hasMore && args.page < MAX_PAGE ? args.page + 1 : null,
1010
1081
  source: "lyrics.com",
1011
1082
  notes
1012
1083
  };
1013
1084
  const header = results.length > 0 ? `${results.length} song(s) on page ${args.page} for "${args.query}":` : `No song on page ${args.page} matched "${args.query}".`;
1014
- const footer = data.hasMore ? `
1085
+ const footer = data.hasMore && args.page < MAX_PAGE ? `
1086
+
1087
+ More results available: call again with page=${args.page + 1}.` : data.hasMore ? `
1015
1088
 
1016
- More results available: call again with page=${args.page + 1}.` : "";
1089
+ lyrics.com holds more, but page ${MAX_PAGE} is as far as this tool reads. Narrow the query instead.` : "";
1017
1090
  return ok(structured, `${header}
1018
1091
  ${renderResultList(results)}${footer}`);
1019
1092
  } catch (error) {
@@ -1022,7 +1095,7 @@ ${renderResultList(results)}${footer}`);
1022
1095
  }
1023
1096
 
1024
1097
  // src/tools/searchSongs.ts
1025
- import { z as z4 } from "zod";
1098
+ import { z as z5 } from "zod";
1026
1099
  var searchSongsDescription = [
1027
1100
  "Find songs on lyrics.com by their title.",
1028
1101
  "Returns candidate songs with artist, album, year, and the lyrics.com id and URL.",
@@ -1031,26 +1104,26 @@ var searchSongsDescription = [
1031
1104
  "lyrics.com searches titles and lyrics together, so results are ranked and filtered locally by title match;",
1032
1105
  'use match="strict" to keep only genuine title matches.'
1033
1106
  ].join(" ");
1034
- var searchSongsInputShape = {
1035
- title: z4.string().min(1).max(120).describe("Song title, or part of it."),
1036
- artist: z4.string().max(120).optional().describe("Optional artist name, matched case-insensitively against each result."),
1037
- limit: z4.number().int().min(1).max(50).default(10),
1038
- page: z4.number().int().min(1).max(20).default(1),
1039
- match: z4.enum(["loose", "strict"]).default("loose").describe(
1107
+ var searchSongsInput = strictInput({
1108
+ title: z5.string().min(1).max(120).describe("Song title, or part of it."),
1109
+ artist: z5.string().max(120).optional().describe("Optional artist name, matched case-insensitively against each result."),
1110
+ limit: z5.number().int().min(1).max(50).default(10),
1111
+ page: z5.number().int().min(1).max(20).default(1),
1112
+ match: z5.enum(["loose", "strict"]).default("loose").describe(
1040
1113
  "'strict' keeps only results whose title matches the query as a whole title, ignoring suffixes such as '(Remastered)'. 'loose' keeps anything lyrics.com returned, best match first."
1041
1114
  )
1042
- };
1115
+ });
1043
1116
  var searchSongsOutputShape = {
1044
- title: z4.string(),
1045
- artist_filter: z4.string().nullable(),
1046
- page: z4.number().int(),
1047
- results: z4.array(songResultSchema),
1048
- raw_result_count: z4.number().int(),
1049
- filtered_out: z4.number().int(),
1050
- has_more: z4.boolean(),
1051
- next_page: z4.number().int().nullable(),
1052
- source: z4.literal("lyrics.com"),
1053
- notes: z4.array(z4.string())
1117
+ title: z5.string(),
1118
+ artist_filter: z5.string().nullable(),
1119
+ page: z5.number().int(),
1120
+ results: z5.array(songResultSchema),
1121
+ raw_result_count: z5.number().int(),
1122
+ filtered_out: z5.number().int(),
1123
+ has_more: z5.boolean(),
1124
+ next_page: z5.number().int().nullable(),
1125
+ source: z5.literal("lyrics.com"),
1126
+ notes: z5.array(z5.string())
1054
1127
  };
1055
1128
  function normalizeTitle(title) {
1056
1129
  return title.replace(/\s*[[(].*?[\])]/g, "").replace(/\s+/g, " ").trim().toLowerCase();
@@ -1097,16 +1170,24 @@ async function runSearchSongs(client, args) {
1097
1170
  raw_result_count: data.rawCount,
1098
1171
  filtered_out: filteredOut,
1099
1172
  has_more: data.hasMore,
1100
- next_page: data.hasMore ? args.page + 1 : null,
1173
+ next_page: data.hasMore && args.page < MAX_PAGE ? args.page + 1 : null,
1101
1174
  source: "lyrics.com",
1102
1175
  notes
1103
1176
  };
1104
- const header = results.length > 0 ? `${results.length} song(s) on page ${args.page} matching "${args.title}":` : `No song on page ${args.page} matched "${args.title}".`;
1105
- const footer = data.hasMore ? `
1177
+ const loose = args.match !== "strict";
1178
+ const header = results.length > 0 ? loose ? `${results.length} song(s) lyrics.com offers on page ${args.page} for "${args.title}":` : `${results.length} song(s) on page ${args.page} matching "${args.title}":` : `No song on page ${args.page} matched "${args.title}".`;
1179
+ const footer = data.hasMore && args.page < MAX_PAGE ? `
1180
+
1181
+ More results available: call again with page=${args.page + 1}.` : data.hasMore ? `
1106
1182
 
1107
- More results available: call again with page=${args.page + 1}.` : "";
1183
+ lyrics.com holds more, but page ${MAX_PAGE} is as far as this tool reads. Narrow the query instead.` : "";
1184
+ if (results.length > 0 && loose) {
1185
+ notes.push(
1186
+ `These are lyrics.com's own suggestions for "${args.title}", not rows whose title matches it. Check each title before quoting one, or ask again with match="strict".`
1187
+ );
1188
+ }
1108
1189
  return ok(structured, `${header}
1109
- ${renderResultList(results)}${footer}`);
1190
+ ${renderResultList(results)}${footer}`, notes);
1110
1191
  } catch (error) {
1111
1192
  return toToolError(error);
1112
1193
  }
@@ -1138,8 +1219,8 @@ function createServer(options = {}) {
1138
1219
  {
1139
1220
  title: "Search lyrics by word",
1140
1221
  description: searchLyricsDescription,
1141
- inputSchema: z5.object(searchLyricsInputShape),
1142
- outputSchema: z5.object(searchLyricsOutputShape),
1222
+ inputSchema: searchLyricsInput,
1223
+ outputSchema: z6.object(searchLyricsOutputShape),
1143
1224
  annotations: READ_ONLY
1144
1225
  },
1145
1226
  async (args) => runSearchLyrics(client, args)
@@ -1149,8 +1230,8 @@ function createServer(options = {}) {
1149
1230
  {
1150
1231
  title: "Search songs by title",
1151
1232
  description: searchSongsDescription,
1152
- inputSchema: z5.object(searchSongsInputShape),
1153
- outputSchema: z5.object(searchSongsOutputShape),
1233
+ inputSchema: searchSongsInput,
1234
+ outputSchema: z6.object(searchSongsOutputShape),
1154
1235
  annotations: READ_ONLY
1155
1236
  },
1156
1237
  async (args) => runSearchSongs(client, args)
@@ -1160,8 +1241,8 @@ function createServer(options = {}) {
1160
1241
  {
1161
1242
  title: "Get full lyrics",
1162
1243
  description: getLyricsDescription,
1163
- inputSchema: z5.object(getLyricsInputShape),
1164
- outputSchema: z5.object(getLyricsOutputShape),
1244
+ inputSchema: getLyricsInput,
1245
+ outputSchema: z6.object(getLyricsOutputShape),
1165
1246
  annotations: READ_ONLY
1166
1247
  },
1167
1248
  async (args) => runGetLyrics(client, args)