Pins this version in aipm.package.json and aipm-lock.json.
aipm add @cursor/add-dictation@1.0.0
Without project tracking
Writes the skill files only. Add --no-init to skip aipm.package.json and the lockfile.
aipm add @cursor/add-dictation@1.0.0 --no-init
How to use
Install the skill above, then run this in your AI tool:
/add-dictation <your description>
About
Add Grok Speech to Text to an existing app: a mic button that dictates into the composer, live captions, or transcripts of recorded audio. Run on `/add-dictation`, typed **Dictate**, or clear “transcribe” intent. Cursor has no mic; wire the **app**, not the IDE.
Source
What gets added to your project
Preview every file this package writes on install before you run the command above.
Summary
>-
Skill instructions
Add Dictation
Add Grok Speech to Text to an existing app: a mic button that dictates into the composer, live captions, or transcripts of recorded audio. Run on /add-dictation, typed Dictate, or clear “transcribe” intent. Cursor has no mic; wire the app, not the IDE.
Before any STT call, read the model-selection section and pass the latest listed model as model. Do not invent ids or reuse a stale one from this skill. REST: form field before file. Streaming: query param. Official examples currently send grok-voice-transcribe-2.0. Listed today: grok-voice-transcribe-2.0 (best), grok-voice-transcribe-1.0 (original; API default when model is omitted).
Pick the path
Need
Path
Tap, speak, tap, text appears. Uploaded files. URLs.
BatchPOST https://api.x.ai/v1/stt (default)
Text appears while speaking: captions, long dictation, push-to-talk
Streamingwss://api.x.ai/v1/stt through a backend relay
Batch is the default for a composer mic button: one request, no socket, the key never leaves the server. Go streaming only when the UX needs interim text.
Auth
Bearer XAI_API_KEY, server side only. The STT docs document no ephemeral-token flow, and browsers cannot set WebSocket headers, so browser streaming goes through your backend relay. Do not invent a token flow.
Never put the key in a client bundle. Do not paste keys in chat.
Steps
Map the app
Composer or input component, where the text should land (insert at cursor vs replace), server framework, package manager.
The microphone icon belongs to dictation. If /add-voice is installed, its waveform primary button stays as is; add the mic as a secondary ghost button beside it.
Existing mic capture? If /add-voice ran, its PCM capture can feed streaming STT; pass its rate as sample_rate. 16 kHz is the model’s native rate; other supported rates (8000, 16000, 22050, 24000, 44100, 48000) are resampled server side.
Batch path (default)
Client: MediaRecorder → Blob → POST to your own route. The endpoint auto-detects containers (WAV, MP3, OGG, Opus, FLAC, AAC, MP4, M4A, MKV, WebM), so send whatever MediaRecorder produces.
Server: forward as multipart/form-data. Option fields first, file last; fields after file may be ignored. file or url, max 500 MB. model from docs (latest listed).
ts
// server (any runtime with fetch + FormData)exportasyncfunctiontranscribe(blob: Blob, filename: string) {
const form = newFormData();
form.append("model", "grok-voice-transcribe-2.0"); // latest from docs; re-read model-selection
form.append("format", "true"); // written-form numbers/currency; requires language
form.append("language", "en");
// form.append("keyterm", "Acme"); // repeat per term, ≤100 terms × 50 chars
form.append("file", blob, filename); // lastconst res = awaitfetch("https://api.x.ai/v1/stt", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.XAI_API_KEY}` },
body: form,
});
if (!res.ok) thrownewError(`STT ${res.status}`); // 400 bad input, 413 >500 MB, 429 back off, 502 url fetch failed, 503 retryreturn (await res.json()) as {
text: string; language: string; duration: number;
words?: { text: string; start: number; end: number; speaker?: number }[];
channels?: { index: number; text: string; words: unknown[] }[];
};
}
Relay: server holds the key, upgrades the browser socket, forwards binary frames and client control messages up, JSON events down. Build the query string server side. model from docs (latest listed).
Browser capture: PCM16 little-endian, mono, 16 kHz, 100 ms frames = 3,200 bytes, raw binary, no base64. Wait for transcript.created before sending. MediaRecorder output is a container, not raw frames; do not stream it.
ts
const ws = newWebSocket(relayUrl); ws.binaryType = "arraybuffer";
const ctx = newAudioContext({ sampleRate: 16000 }); // if ctx.sampleRate !== 16000, downsample in the workletawait ctx.audioWorklet.addModule("/pcm16-worklet.js"); // Float32 → Int16LE, posts one 3,200-byte frame per 100 msconst node = newAudioWorkletNode(ctx, "pcm16");
ctx.createMediaStreamSource(stream).connect(node);
let ready = false;
node.port.onmessage = (e) => ready && ws.readyState === WebSocket.OPEN && ws.send(e.data);
let committed = "", locked = "", live = "";
ws.addEventListener("message", (e) => {
const ev = JSON.parse(e.data);
if (ev.type === "transcript.created") ready = true;
elseif (ev.type === "transcript.partial") {
if (ev.speech_final) { committed += ev.text + " "; locked = ""; live = ""; } // complete stitched utteranceelseif (ev.is_final) { locked += ev.text + " "; live = ""; } // chunk final: text will not changeelse live = ev.text; // interim: may changerender(committed + locked + live);
} elseif (ev.type === "transcript.done") ws.close(); // after audio.doneelseif (ev.type === "error") showError(ev.message); // most errors close the socket
});
// stop: ws.send(JSON.stringify({ type: "audio.done" }))// push-to-talk release: ws.send(JSON.stringify({ type: "Finalize" })) then keep streaming (docs show both `finalize` and `Finalize`; the examples use `Finalize`)
Options (query params for streaming, form fields for batch)
Want
Set
Latest STT
Read docs #model-selection, pass that model. Snapshot: grok-voice-transcribe-2.0. Omit → grok-voice-transcribe-1.0. Batch: form field. Streaming: query param.
import os, requests
r = requests.post(
"https://api.x.ai/v1/stt",
headers={"Authorization": f"Bearer {os.environ['XAI_API_KEY']}"},
data=[("model", "grok-voice-transcribe-2.0"), ("format", "true"), ("language", "en")],
files={"file": ("dictation.webm", blob, "audio/webm")}, # requests sends data fields before files
)
r.raise_for_status(); text = r.json()["text"]
# streaming: websockets.connect(url, additional_headers={"Authorization": f"Bearer {key}"}); await ws.send(pcm_bytes)
Smoke
Batch: latest model from docs (currently grok-voice-transcribe-2.0): curl -X POST https://api.x.ai/v1/stt -H "Authorization: Bearer $XAI_API_KEY" -F model=grok-voice-transcribe-2.0 -F language=en -F file=@short.wav → 200 with text. Same call with -F format=true and no language → 400.
Streaming: dictate two sentences with a pause between them. Expect interim text, then a final; no duplicated or vanished words at the utterance boundary (if words vanish, the stitched speech_final text did not include the chunk finals: append instead of replacing locked). audio.done → transcript.done, socket closes.
Search the client bundle for XAI_API_KEY; it must not be there.
Debug from logs with /debug-voice; swap its hook points to transcript.* events.
Out of scope
Speech that talks back (/add-voice), speaking text (/add-read-aloud)
Inventing an STT token flow, endpoints, model ids, or event names not in the docs
Add this badge to a GitHub README so readers can open the package page and install the same version.
[](https://www.aipm-registry.com/skills/cursor/add-dictation/1.0.0?utm_source=github&utm_medium=readme&utm_campaign=package_badge)
This imported publisher has not claimed its AIPM account. A connected account confirms account control, but it does not guarantee that a package is safe.
AI assistant context
What this skill is for
@cursor/add-dictation@1.0.0 is an AIPM skill package for All tools. Use it when a project needs the reusable AI behavior described as: >-
Work with Google Drive through its MCP tools - search, read, create, organize, share, comment, and export files. Use when creating files in specific folders, moving or renaming, converting Markdown/CSV into Docs/Sheets, managing comments...
Author and edit Google Docs with the google-docs tools - structured writing, character and paragraph styling, lists, tables, images, headers/footers. Use when creating a formatted document, editing document content, or applying styles.
Run aipm add @cursor/add-dictation@1.0.0 in a project that has AIPM initialized.
Supported tools
@cursor/add-dictation@1.0.0 supports All tools.
Before installing
AIPM skills can add files to your project. Review the package name, target, description, license, and publisher before installing. Use a clean branch when trying a new skill.