chord-mcp 1.0.1 → 1.0.3

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
@@ -19,3 +19,5 @@ node dist/index.js
19
19
  ```
20
20
 
21
21
  All music theory runs locally.
22
+
23
+
package/dist/api.js CHANGED
@@ -42,3 +42,43 @@ export async function scaleNotes(args) {
42
42
  const notes = intervals.map((iv) => NOTES[(r + iv) % 12]);
43
43
  return `${root} ${scale}: ${notes.join(" - ")}`;
44
44
  }
45
+ // ---- muxA tools:
46
+ const INTERVAL_NAMES = [
47
+ "unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th",
48
+ "tritone", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th",
49
+ "octave",
50
+ ];
51
+ // The 17 standard key signatures, by tonic spelling. A key signature is really
52
+ // a position on the circle of fifths, so a key is stored as a signed accidental
53
+ // count: positive is sharps, negative is flats.
54
+ const MAJOR_SHARPS_BY_NAME = {
55
+ C: 0, G: 1, D: 2, A: 3, E: 4, B: 5, "F#": 6, "C#": 7, "D#": 9, "A#": 10,
56
+ F: -1, Bb: -2, Eb: -3, Ab: -4, Db: -5, Gb: -6,
57
+ };
58
+ // Same thing indexed by pitch class, for tonics the name table does not cover.
59
+ const MAJOR_SHARPS_BY_CLASS = {
60
+ 5: -1, 0: 0, 7: 1, 2: 2, 9: 3, 4: 4, 11: 5, 6: 6, 1: 7, 8: 8, 3: 9, 10: 10,
61
+ };
62
+ const SHARP_ORDER = ["F", "C", "G", "D", "A", "E", "B"];
63
+ const FLAT_ORDER = ["B", "E", "A", "D", "G", "C", "F"];
64
+ function sharpsForMajor(root, pitchClass) {
65
+ const named = MAJOR_SHARPS_BY_NAME[root];
66
+ if (named !== undefined)
67
+ return named;
68
+ const byClass = MAJOR_SHARPS_BY_CLASS[pitchClass];
69
+ // Beyond the standard signatures the count keeps climbing by fifths; say so
70
+ // rather than inventing a signature nobody uses.
71
+ if (byClass === undefined) {
72
+ throw new ChordError(`No standard key signature for ${root}. Try a tonic like C#, F# or Bb.`);
73
+ }
74
+ return byClass;
75
+ }
76
+ function accidentals(count) {
77
+ if (count === 0)
78
+ return "none";
79
+ const order = count > 0 ? SHARP_ORDER : FLAT_ORDER;
80
+ return order.slice(0, Math.abs(count)).map((n) => `${n}${count > 0 ? "#" : "b"}`).join(" ");
81
+ }
82
+ function spell(index) {
83
+ return NOTES[((index % 12) + 12) % 12];
84
+ }
package/dist/server.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { z } from "zod";
3
- import { chordNotes } from "./api.js";
4
- import { scaleNotes } from "./api.js";
3
+ import { chordNotes, scaleNotes } from "./api.js";
5
4
  const text = (value) => ({ content: [{ type: "text", text: value }] });
6
5
  const textError = (t) => ({ content: [{ type: "text", text: t }], isError: true });
7
6
  const READ_ONLY = { readOnlyHint: true, openWorldHint: true };
@@ -18,7 +17,7 @@ export function createServer() {
18
17
  return text(await chordNotes(args));
19
18
  }
20
19
  catch (e) {
21
- return textError(error(e));
20
+ return textError(String(e));
22
21
  }
23
22
  });
24
23
  server.registerTool("scale_notes", {
@@ -31,7 +30,35 @@ export function createServer() {
31
30
  return text(await scaleNotes(args));
32
31
  }
33
32
  catch (e) {
34
- return textError(error(e));
33
+ return textError(String(e));
34
+ }
35
+ });
36
+ server.registerTool("interval_between", {
37
+ title: "Interval between notes",
38
+ description: "Name and size of the interval between two notes, in semitones and cents.",
39
+ inputSchema: z.object({ from: z.string().describe("Lower note, e.g. C."), to: z.string().describe("Upper note, e.g. G.") }),
40
+ annotations: READ_ONLY,
41
+ }, async (args) => {
42
+ // TODO: implement between locally (paywall removed)
43
+ try {
44
+ return textError("Not yet implemented: between");
45
+ }
46
+ catch (e) {
47
+ return textError(String(e));
48
+ }
49
+ });
50
+ server.registerTool("key_signature", {
51
+ title: "Key signature",
52
+ description: "Accidentals in a key signature, the sharps or flats by name, and the relative key.",
53
+ inputSchema: z.object({ root: z.string().describe("Tonic, e.g. D or Bb."), scale: z.string().describe("major or minor.").optional() }),
54
+ annotations: READ_ONLY,
55
+ }, async (args) => {
56
+ // TODO: implement signature locally (paywall removed)
57
+ try {
58
+ return textError("Not yet implemented: signature");
59
+ }
60
+ catch (e) {
61
+ return textError(String(e));
35
62
  }
36
63
  });
37
64
  return server;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.1",
2
+ "version": "1.0.3",
3
3
  "type": "module",
4
4
  "repository": {
5
5
  "type": "git",
@@ -45,4 +45,4 @@
45
45
  "engines": {
46
46
  "node": ">=20"
47
47
  }
48
- }
48
+ }