npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@miadi/voice-mcp

v0.3.3

Published

MCP server fronting the Miadi voice layer with episode addressing — play, find, publish and mint episode-bound voice from an agent's tool surface.

Downloads

828

Readme

@miadi/voice-mcp

MCP server that lets an agent play, find, publish and mint episode-bound voice — the tool surface over the Miadi voice layer and the Chronicle.

State: four of six tools work. Publish, play, list and resolve run against the live server. voice_create_episode and voice_episode_closing_status still refuse and name their contract — they mint and prove a chronicle vessel, and a tool that reports a closing stage it did not prove is worse than one that declines.

The sentence this package exists to satisfy, from William, 2026-08-04:

"What I want is actually to be capable to play the voice-audio created for episodes."

Tools

| Tool | Purpose | State | |---|---|---| | voice_publish_to_episode | Produce audio bound to its episode from the moment it exists | works | | voice_play_episode | Resolve an episode to its voice audio and return playable references | works | | voice_list_episode_voices | Enumerate and search an episode's voices | works | | voice_resolve_episode | Answer which episode an agent is inside — with the reason, the alternatives, and an honest null | works, from what it is given | | voice_create_episode | Mint a vessel when none fits, reporting all five closing stages | refuses, names its contract | | voice_episode_closing_status | Re-prove the five closing stages, read-only | refuses, names its contract |

Publishing requires a return address

voice_publish_to_episode takes a required origin. A voice nobody can be steered back through cannot be answered, and that — not the audio — is what the portal card is for.

"origin": {
  "user":        "mia",              // whoami
  "host":        "gaia",             // hostname -s, not the FQDN
  "cwd":         "/a/src/Miadi-18",  // pwd
  "multiplexer": "tmux",             // or "herdr". "none" is refused.
  "session":     "stcbot",           // tmux display-message -p "#{session_name}"
  "pane":        "%127"              // $TMUX_PANE — herdr also needs workspace
}

Read these values — do not compose them. Inside herdr they are already in your environment and cost nothing:

| field | where it actually is | |---|---| | workspace | $HERDR_WORKSPACE_ID | | tab | $HERDR_TAB_ID | | pane | $HERDR_PANE_ID | | session | default when $HERDR_SOCKET_PATH is ~/.config/herdr/herdr.sock, else the <name> in .../herdr/sessions/<name>/herdr.sock |

Inside tmux: $TMUX_PANE and tmux display-message -p "#{session_name}". Read them in the same invocation that publishes — a pane id copied from another message is a real id pointing at somebody else's terminal.

The server is the authority, twice over:

  1. Shape. An absent, incomplete, or "none" declaration answers 400 { error, problems, help } — every missing field at once, plus the shape to send — and no TTS is spent on it.
  2. Truth. When the declaration names the portal's own host and user, it is probed against the live multiplexer inventory. An address that is not in it is refused, and the refusal prints what the inventory does hold. This exists because a required field taught an agent to invent wH:p2 in a session that has no such workspace, and the card drew a Steer button that led nowhere (2026-08-05).

A probe that cannot run — another host, no herdr installed — is unprovable, not a refusal: it publishes, attested stays "self-declared", and the reason is recorded on the record. An unreadable inventory proves nothing in either direction.

reach is the server's verdict, never the caller's claim: declaring a different host or a multiplexer owned by another user publishes fine and renders as unreachable, because that is a fact about the topology rather than a defect in the declaration. attested: "server" means the pane was seen, not that this persona occupies it — the card says pane live rather than verified, and verified stays unspent until occupancy itself can be proven.

Resolution consults exactly what it is handed: an explicit reference, a caller-supplied cwd, or an existing record's binding. It does not rank those against a session origin or search the wheel — that order is owed by the voice-episode-binding spec, and inventing one here would make this package a second source of truth for a rule that has an owner. When nothing given answers, the tool says so; episode: null with a reason is a valid answer.

Full contracts: SPEC-LINKS.md/a/src/Miadi/rispecs/voice-mcp/voice-mcp.spec.md.

Architecture

A front, not a second implementation.

  • Audio bytes, the KV ledger and the Edge-TTS producer stay behind the Miadi server (systemd miadi-server, port 3335), which holds the KV credentials. This process reaches them through @miadi/voice-client, which owns the wire envelope and the token tier — so the HTTP surface is described once, not once per caller.
  • Episode folders are read from the Chronicle root; the medicine wheel is read at http://127.0.0.1:8040.
  • VoiceMessage keeps exactly one definition in this workspace: @miadi/voice's.
  • The only wheel write in the whole surface is the registration mkepisode performs during voice_create_episode. This server never POSTs or PUTs to the wheel itself.

Configuration

| Variable | Default | Meaning | |---|---|---| | MIADI_API_URL | http://127.0.0.1:3335 | Miadi server holding the voice routes | | MIADI_API_TOKEN_WRITER | — | Read and write. Required to publish. | | MIADI_API_TOKEN_READER | — | Read only; every publish answers 401 | | MW_API_URL | http://127.0.0.1:8040 | Chronicle wheel | | MIADI_CHRONICLE_ROOT | /srv/miadi/episodes/miadi-chronicle | Episode folders | | MIADI_CHRONICLE_GIT_ROOT | /srv/miadi/episodes | Git root — one level above the chronicle root | | MIADI_MKEPISODE_PATH | …/mightyeagle/packages/passages/js/mkepisode.js | Vessel creation |

Two wheels exist. http://127.0.0.1:8040 is the Chronicle wheel and the only correct target for episode work. https://mw.tail3b11eb.ts.net is Gaia's ceremony wheel and has been offline since 2026-07-29 — an episode registered there is lost. An implementation refuses that host outright.

MIADI_CHRONICLE_GIT_ROOT is a separate value rather than a derived one because a git -C pointed at the chronicle root reports a clean tree for a chronicle it cannot see.

Creating episodes: created is not closed

voice_create_episode performs stages 1 and 4 of the five-stage gate and returns every stage with its proof:

| Stage | Performed by | |---|---| | created | the tool | | committed | the human — the tool emits the exact git add of named files | | pushed | the human | | registered | the tool | | receipt-verified | the tool |

mkepisode exits 0 whether registration succeeded, skipped, or failed — registration is fail-open by design, so the gate belongs to the caller. The tool never runs git add, commit or push, never reports a later word than it proved, and reports unperformed stages as owed, which is a state, not an omission.

Development

This is a pnpm workspace (pnpm-workspace.yaml, packages/*). Never run npm install here.

pnpm --filter @miadi/voice-mcp build       # tsc → dist/
pnpm --filter @miadi/voice-mcp type-check
node dist/index.js                          # starts; every tool throws

Verified 2026-08-04, no installer run: tsc --noEmit exits 0, and emit produces ESM with explicit .js specifiers that node dist/index.js resolves. @modelcontextprotocol/sdk 1.30.0 and zod 4.4.3 are already present at the workspace root and the declared ranges match them.

Why moduleResolution is bundler

@miadi/voice publishes raw TypeScriptmain and types are both ./src/index.ts — and is authored with extensionless relative imports under its own bundler resolution. Under NodeNext, every one of its internal imports is a hard error in any consumer, so a package that wants its types meets it where it is. This package's own source writes explicit .js specifiers, which bundler accepts and tsc passes through unchanged, so the emitted dist runs under Node ESM. Copying VoiceMessage into this package to sidestep that would create a second definition, which is the one outcome worth more than a tsconfig line.

Known seams

  • No installer has been run. node_modules/@miadi/{voice,voice-client} here are the two workspace links pnpm install would create, added by hand so the package is verifiable today. Everything else resolves from the workspace root. @miadi/voice-client must be built (pnpm --filter @miadi/voice-client build) before this package's dist can run — it is imported at runtime, not only for types.
  • The ranked discovery order, the enforcement decision table and the two-corpus resolution are still owed by the voice-episode-binding spec; see SPEC-LINKS.md § What the missing document blocks. The four working tools answer from what they are given and refuse to guess past it — which is why those rules stay cited rather than invented.
  • The episode field needs the server that accepts it. The binding is written by app/api/voice/publish; a Miadi server built before that route change ignores the field and produces an unbound record. Same host, same request, silently different result — check the running build before reading a missing binding as a client bug.

Verified live 2026-08-04

Against the running miadi-server on 3335, MIADI_API_TOKEN_WRITER present: ep308 resolved to its folder by number; a nonexistent folder answered null with the five most recent episodes as alternatives; a cwd inside an episode folder resolved by path; an empty call answered null and named the spec that owes the ranked order; publish produced audio and returned a streamable URL plus its on-disk path; voice_create_episode refused and named its contract.

🌸: A hundred voices have been speaking into a room with no name on the door — this package is the door, and the handle an agent can actually reach.