# Guitar Practice Routine App — Chord Charts [Health: Active]

**Category:** 💻 Developer Tools  
**Repository:** https://github.com/slshults/gpra-chord-charts-mcp  
**GitHub Stars:** 0  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/guitar-practice-routine-app-chord-charts

## Description
Look up a guitar chord by name and get a text chord diagram. 12,708 standard-tuning names.

## Tools
Capabilities this server exposes over MCP:

- **get_chord_chart_by_name** — One chord name (`G`, `Am7`, `D/F#`) → the one chart the website shows for it.
- **get_chord_chart_by_id** — The same chart by its numeric id, for re-rendering something already returned.
- **get_chord_of_the_day** — Today's Chord of the Day — the same chord GPRA posts to Bluesky and Facebook.

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `npx` (confidence: high):

```json
"mcpServers": {
  "guitar-practice-routine-app-chord-charts": {
    "command": "npx",
    "args": ["-y","mcp-remote","https://mcp.guitarpracticeroutine.com/mcp"]
  }
}
```

## Documentation & README

# Guitar chord charts — MCP server

A small, read-only [Model Context Protocol](https://modelcontextprotocol.io)
server that lets MCP-capable AI clients look up guitar chord charts from
[Guitar Practice Routine App (GPRA)](https://guitarpracticeroutine.com) and show
them to people as text chord diagrams.

It serves exactly what
[guitarpracticeroutine.com/find-a-chord-chart](https://guitarpracticeroutine.com/find-a-chord-chart)
serves: one chord name in, one chart out, from a bundled snapshot of the same
chord library. No database, no authentication, no secrets.

```
Am

    E  A  D  G  B  E
    x  o           o
   ==================
 1  |  |  |  |  1  |
   -+--+--+--+--+--+-
 2  |  |  3  2  |  |
   -+--+--+--+--+--+-
 3  |  |  |  |  |  |
   -+--+--+--+--+--+-
 4  |  |  |  |  |  |
   -+--+--+--+--+--+-
 5  |  |  |  |  |  |
   -+--+--+--+--+--+-

x = muted   o = open   digits in grid = fingers (1 index, 2 middle, 3 ring, 4 pinky)
EADGBE
```

Frets run top to bottom from the nut, and string 1 — the highest-pitched
string — is the rightmost column, matching standard chord-box convention and
the charts on the site.

## Tools

| Tool | What it does |
| --- | --- |
| `get_chord_chart_by_name` | One chord name (`G`, `Am7`, `D/F#`) → the one chart the website shows for it. |
| `get_chord_chart_by_id` | The same chart by its numeric id, for re-rendering something already returned. |
| `get_chord_of_the_day` | Today's Chord of the Day — the same chord GPRA posts to Bluesky and Facebook. |

Each result **leads with a direct PNG URL** for the chart, then the same chart as
a fenced text grid. The URL is the picture in the form that survives every
surface: it renders where markdown images work, stays clickable where they
don't, and can be dropped into an artifact, an HTML page, or a saved file. The
text grid is the fallback for anywhere neither is true.

Inline PNG bytes are **opt-in** via `format: "image"` or `format: "both"`. They
cost image tokens and several clients bury them in a collapsed tool drawer, so
they're not worth sending by default now that a URL does the job. `format`
defaults to `"text"`, which still includes the image URL. Attribution is kept in
every mode.

Every tool also takes a `context` argument describing why it's being called.
That's injected by PostHog's MCP SDK and populates agent intent in analytics;
nothing in the response depends on it.

### How a chart reaches a person

There is no capability negotiation for "can you show an image", so the result
carries several layers and lets the host use whichever it understands:

| Layer | Where it lands |
| --- | --- |
| Text grid + `Chart image:` URL | Everywhere. This is the floor. |
| MCP Apps widget (`_meta.ui.resourceUri`) | Hosts that render MCP Apps — Claude Code, Cowork, ChatGPT, PostHog Desktop. |
| Inline PNG bytes (`format: "image"`) | Hosts that surface image content blocks, on request. |

The widget is a `ui://gpra-chord-charts/chart/{id}` resource resolving to a
self-contained HTML page with the chord already baked in — no JavaScript, no
postMessage bridge, no client bundle. A host that ignores it simply shows no
widget, and the text answer is untouched.

Measured on claude.ai, for anyone tempted to try these again: bare markdown
images render as a click-through placeholder that opens a browser tab rather
than loading inline; images wrapped in links collapse to the link; raw HTML is
escaped to literal text; and the MCP Apps iframe is fetched but never mounted
([ext-apps#671](https://github.com/modelcontextprotocol/ext-apps/issues/671)).
On that surface the URL is the whole story, which is why it leads the text.

Results deliberately carry **no `structuredContent`**. A client that understands
it may render it *instead of* the content blocks — one measurably did, reducing
the answer to three JSON fields and dropping the chart, the attribution and the
call to action. Nothing goes in a result that can displace the text.

## What's in the library

12,708 standard-tuning (EADGBE) voicings, **exactly one per chord name**. This
server does no fuzzy matching and no query cleanup — it passes the name through
the same way the website does, so the tool description asks the calling
assistant to send a plain chord name ("G", not "how do I play G major").

Charts are drawn on a five-fret grid starting at the nut, matching the site —
which means a voicing with notes above the fifth fret has them fall outside the
grid. The **image** drops them, exactly as the website does; the **text** names
them underneath ("Also fretted, past this five-fret grid: string 5 (A) fret 6").
So the pair is honest even where the picture alone isn't.

Images use black ink on white rather than the site's white-on-transparent: the
site can assume its own dark UI, and a chat client's background is unknown.

## Connecting a client

Remote MCP means users add a **URL**, no install:

```
https://mcp.guitarpracticeroutine.com/mcp
```

For clients configured with a JSON config file, use the streamable-HTTP
transport:

```json
{
  "mcpServers": {
    "gpra-chord-charts": {
      "type": "streamable-http",
      "url": "https://mcp.guitarpracticeroutine.com/mcp"
    }
  }
}
```

For a client that only speaks stdio, bridge with
[`mcp-remote`](https://www.npmjs.com/package/mcp-remote):

```json
{
  "mcpServers": {
    "gpra-chord-charts": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.guitarpracticeroutine.com/mcp"]
    }
  }
}
```

## Run locally

```bash
npm install
npm run build
npm test

npm start          # stdio transport
npm run start:http # HTTP, listens on 127.0.0.1:2112, POST /mcp (set PORT / HOST)
```

Health check: `GET /health` → status, version, chord count.

Preview charts from the terminal without a client:

```bash
npx tsx scripts/preview.ts G Am C F Bm D/F#
```

No CORS headers are set. Claude and connector directories fetch server-side, so
they don't need them — a browser-based MCP client would.

### Rebuilding the chord snapshot

Requires access to a GPRA database. The source table changes rarely, so this is
a manual step:

```bash
psql "$DATABASE_URL" -Atf scripts/dump-chords.sql > data/common-chords.raw.json
npm run build:index
npm run build && npm test
```

`build:index` prints the library's fret range to the terminal and reports data
anomalies (voicings with no fretted notes, missing finger numbers, notes dropped
as unplayable) rather than quietly normalizing them away.

## Privacy

The hosted server records anonymous usage analytics through
[`@posthog/mcp`](https://www.npmjs.com/package/@posthog/mcp): which tool was
called, how long it took, whether it errored, the calling client's identity
(name and version on connect, user-agent on each call), and the `context` string
the agent supplied describing why it called. No personal identifier is stored and
no person profile is created.

Analytics are off entirely unless `POSTHOG_API_KEY` is set, so running this
yourself — including over stdio — sends nothing anywhere.

## Deployment

`deploy/` contains a systemd unit and an nginx reverse-proxy config, if you want
to host your own instance.

## Credits

The chord library exists because of other people's work:

- **[SVGuitar-ChordCollection](https://github.com/TormodKv/SVGuitar-ChordCollection)**
  by [@TormodKv](https://github.com/TormodKv) — the chord database this is built
  from, itself based on
  [chord-collection](https://github.com/T-vK/chord-collection) by
  [@T-vK](https://github.com/T-vK).
- **[SVGuitar](https://github.com/omnibrain/svguitar)** by
  [@omnibrain](https://github.com/omnibrain) — the chord-diagram renderer whose
  data format this uses, and which draws the charts on
  [guitarpracticeroutine.com](https://guitarpracticeroutine.com). (This server
  renders text, not SVG.)

## A note on `npm audit`

`npm audit` reports three high-severity advisories against `image-size`, pulled
in transitively by `svguitar` → `svgdom`. They are denial-of-service infinite
loops in the ICNS, JXL and HEIF **file** parsers, reached only through
`svgdom`'s `HTMLImageElement`, which calls `imageSizeFromFile` when an `<img>`
is given a source.

Nothing here constructs one. Chart rendering draws vector primitives from a
chord name and an integer id; the only inputs that cross the boundary are a
string of at most 64 characters and a positive integer, and no code path
accepts, fetches or decodes an image file. There is no upstream fix, so the
advisories will keep showing until `svgdom` moves off `image-size`.

## License

- **Code:** MIT — see `LICENSE`.
- **Provenance of the chord data:** see `NOTICE`.
- **Chord data:** the voicings originate from SVGuitar-ChordCollection, which
  carries no stated licence, so no licence is asserted over them here. The
  fingerings themselves are factual descriptions of where fingers go on a
  fretboard. Please credit the projects above if you reuse the data.

---

[![gpra-chord-charts-mcp MCP server](https://glama.ai/mcp/servers/slshults/gpra-chord-charts-mcp/badges/score.svg)](https://glama.ai/mcp/servers/slshults/gpra-chord-charts-mcp)

