Skip to main content

Developers

Transcription MCP server for your account

Connect the transcription MCP server to your Hushscript account to work with recordings and transcripts through scoped assistant tools.

The Hushscript MCP server connects an assistant to your recordings and transcripts using the Model Context Protocol. Approve an OAuth connection in your browser, or supply a personal access token. Transcription and paid actions follow your account’s personal or eligible workspace funding rules.

Two servers

Hushscript actually runs two separate MCP servers, and most readers want only one of them.

https://mcp.hushscript.com/mcp is the account server the rest of this page describes. It accepts OAuth or a personal access token, and it is where the account work happens: upload, transcribe, edit, export.

POST https://hushscript.com/mcp is a separate free documentation server. No account, no token, nothing to sign in to. It is read-only and anonymous: it cannot open an account, run a transcription, or touch anything belonging to one. It answers questions about pricing, capabilities, page content, and how to connect, through five tools (get_pricing, get_capabilities, how_to_connect, list_pages, get_page) and the same information as three resources (hushscript://pricing, hushscript://capabilities, hushscript://connect).

If you are an agent that has just found Hushscript, hold no token, and are still deciding whether it fits before you spend anything, start with the documentation server. Come back to the rest of this page once you are ready to connect an account.

Connect

The server speaks Streamable HTTP at a single endpoint:

https://mcp.hushscript.com/mcp

There is no SSE endpoint and no Hushscript software to install locally. In Claude Code:

claude mcp add --transport http hushscript https://mcp.hushscript.com/mcp

In a client that takes a JSON config:

{
  "mcpServers": {
    "hushscript": {
      "type": "http",
      "url": "https://mcp.hushscript.com/mcp"
    }
  }
}

An OAuth-capable client reads /.well-known/oauth-authorization-server to discover the endpoints. It may register through /oauth/register or use a client ID metadata document, then opens a browser window for sign-in.

What you approve

Authorization is OAuth 2.1 with PKCE. The browser window is a Hushscript consent screen, not a password box for the client: it names the application asking, lists in plain language what it wants to do, and waits for you. Nothing is granted until you approve it there.

Access tokens last one hour, and refresh tokens last thirty days. With OAuth, your client handles those credentials for you; you never paste a personal access token into the config.

Without a browser

An OAuth connection requires browser approval. The same server also accepts a personal access token directly, with no browser step at connection time. A person creates one under Tokens on the MCP page in the account, picks its scopes, and decides whether it may buy minutes. The token is shown once, looks like hsr1:<region>:pat.<id>.<secret>, and goes in Authorization: Bearer <token> on every request: no OAuth round trip, no consent screen, no client registration. The same token works against the REST API.

A token can also belong to a machine account with no human account holder. An agent starts a paid signup with POST /v1/agent/accounts, completes hosted Stripe Checkout in a browser it may drive, and claims the account with a one-time secret at POST /v1/agent/accounts/{signup_id}/claim to get its first token. That token connects here exactly like any other. See Automated usage for the flow end to end.

A token cannot create, list, or revoke other tokens. An account holds at most 25 active tokens. The machine account’s first token can rotate itself through the REST API; the flow is on Automated usage.

Permissions

A connection holds a set of scopes. An OAuth request that names none defaults to ten of the eleven caller-selectable scopes, all except billing. A person chooses the scopes on a token created in the app. A machine account’s first token has only transcripts:read, transcripts:write, transcribe, account:read, export, billing, and the separate pat:rotate scope for self-rotation. Its claim enables both billing switches; a person-created token needs them enabled deliberately. When scopes are known, the server advertises matching tools; the backend enforces them on every call.

Scope What it allows
transcripts:read Read and export transcripts, their metadata, and the deleted list
transcripts:write Edit, tag, translate, restore, and delete transcripts
transcribe Upload recordings, import from a link, and run transcriptions
insights Read, generate, and delete Insights
context Manage saved dictionaries and prompt presets
account:read Read balance, usage, transactions, and holds
settings:write Change account settings
org:read Read workspace members, ledger, invoices, and audit log
org:write Change member rights, place and lift legal holds, and schedule the move to firm (permanent) custody
export Request a full account export, check its status, and download it
billing Save or manage payment methods and buy minute packs

billing is the exception in every direction. It is never granted implicitly, the consent screen warns you before you approve it, and a purchase tool needs both the token’s billing flag and the account-wide billing switch enabled. pat:rotate cannot be selected in an OAuth request or the app’s token form.

What the tools cover

56 tools, grouped by what they touch:

Group Tools Examples
Transcripts, reading 4 list_transcripts, get_transcript, list_deleted
Transcripts, writing 10 update_body, revert_body, retranslate, tag_transcripts
Transcribing 8 create_upload, transcribe_from_url, list_jobs, cancel_job
Account 6 get_balance, get_usage, list_transactions
Insights 3 get_insights, generate_insights, delete_insights
Export 4 export_transcript, request_account_export
Transcription context 5 save_dictionary, save_prompt_preset
Workspace 10 get_org, list_org_members, get_org_audit_log, place_transcript_hold
Billing 6 list_packs, purchase_pack, list_payment_methods, add_payment_method

export_transcript returns TXT, SRT, VTT, Markdown, or JSON. The full set of 21 export formats lives in the app.

Beside the tools, the server publishes three resources and an instructions string on connect: the minute packs, the credit and scope model, and the complete error-code catalog, so a client can read the rules before it calls anything. Paginated list tools take a limit and an opaque cursor from the previous page’s next_cursor; list_packs and list_payment_methods return their lists without a cursor.

MCP uploads send prepared audio. For a local video, prepare its audio on your device first; transcribe_from_url fetches supported audio links server-side and does not import video links. Jobs started through MCP use standard mode: their transcripts are stored under the account’s retention setting. The one-time private mode is available in the app, not through MCP tokens.

Examples

A client doing the OAuth flow gets a normal MCP session and never sees any of this directly: its SDK builds the JSON-RPC underneath. This is the same protocol a token-holding client sends by hand, and it works as a debugging aid either way.

Connect with a token by putting it straight on the request, no handshake beyond initialize:

curl -s https://mcp.hushscript.com/mcp \
  -H "Authorization: Bearer hsr1:us:pat.4f2a1c.9e7b3d2a5c8f1e6b4a0d7c3f2e1b9a8d" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2025-06-18",
      "capabilities": {},
      "clientInfo": { "name": "example-client", "version": "1.0.0" }
    }
  }'

Send the same request again with a different body to see every tool the token’s scopes cover:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list"
}

Each entry carries a name, a description and a JSON Schema inputSchema, matching the groups above. Calling a tool looks like this, create_upload reserving credit and starting an upload:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "create_upload",
    "arguments": {
      "size_bytes": 52428800,
      "duration_seconds": 1800,
      "title": "Team standup 2026-09-10",
      "transcription_options": { "speaker_detection": true }
    }
  }
}

Structured tools such as create_upload return a content text block with stringified JSON. For example:

{
  "content": [
    {
      "type": "text",
      "text": "{\n  \"job_id\": \"job_8f3a1c2b9e\",\n  \"balance_seconds\": 5400,\n  \"held_seconds\": 1800,\n  \"upload\": {\n    \"part_size_bytes\": 33554432,\n    \"total_parts\": 2,\n    \"part_urls\": [\n      { \"n\": 1, \"url\": \"https://...\" }\n    ],\n    \"next_part_number\": 2\n  }\n}"
    }
  ]
}

The response above shows the upload fields; the full response also includes limits, funding, and state. Upload the bytes to those URLs, call complete_upload with each part’s ETag, then poll get_job until its state is done. The structured examples below show inner JSON directly, already parsed, and drop the {"jsonrpc":"2.0","id":...} request wrapper since it never changes: only name and arguments do.

export_transcript returns the rendered format as text, and download_account_export_chunk returns raw bytes encoded as base64 text. Parse a result’s text as JSON only when the tool returns structured data.

Listing and reading transcripts:

{
  "name": "list_transcripts",
  "arguments": { "limit": 5 }
}
{
  "transcripts": [
    {
      "id": "t_9c2b7f10",
      "title": "Team standup 2026-09-10",
      "language_detected": "en",
      "duration_seconds": 1800,
      "tags": ["standup"],
      "insights_state": "none"
    }
  ],
  "next_cursor": null
}

Generating Insights, free on the first call for a transcript. This example shows the balance after a new generation starts; reused is false:

{
  "name": "generate_insights",
  "arguments": { "id": "t_9c2b7f10" }
}
{
  "ok": true,
  "reused": false,
  "balance_seconds": 5400,
  "held_seconds": 0
}

Buying minutes, idempotency_key required so a retry after a dropped response cannot double-charge:

{
  "name": "purchase_pack",
  "arguments": {
    "pack": "300min",
    "payment_method_id": "pm_1PxYzABC",
    "idempotency_key": "agent-2026-09-11-refill-01"
  }
}
{
  "status": "ok",
  "balance_seconds": 23400
}

Resources answer without a tool call, resources/read naming one of the three URIs:

{
  "jsonrpc": "2.0",
  "id": 9,
  "method": "resources/read",
  "params": { "uri": "hushscript://pricing" }
}

Abbreviated response (the resource’s explanatory note is omitted):

{
  "contents": [
    {
      "uri": "hushscript://pricing",
      "mimeType": "application/json",
      "text": "{\"packs\":[{\"id\":\"45min\",\"minutes\":45},{\"id\":\"300min\",\"minutes\":300},{\"id\":\"900min\",\"minutes\":900},{\"id\":\"1800min\",\"minutes\":1800},{\"id\":\"6000min\",\"minutes\":6000}]}"
    }
  ]
}

Use list_packs for current pack amounts and currencies.

Every backend failure carries a stable slug and a numeric code, the same {"error": "<slug>", "code": <number>} body documented in hushscript://error-codes. A tool call surfaces that same slug and code, folded into a readable message instead of a raw error object, and flags the result with isError rather than throwing:

{
  "content": [
    {
      "type": "text",
      "text": "Not enough transcription credit. This job needs 1800 seconds and the balance is 900 seconds. Top up at https://app.hushscript.com/billing (insufficient_balance, E3001)"
    }
  ],
  "isError": true
}

What costs minutes

Reading account data does not spend transcription minutes. Priced actions use the same rates as the app. Personal prepaid minutes usually fund them; a workspace transcription can use a Teams pool when the active pool alone covers the whole job and no translation target is selected. A job with translation targets or an imported audio URL uses personal minutes. Funding does not change transcript custody.

Eligible personal accounts get 30 free minutes after card verification; a machine account starts with the pack it buys at signup.

See pricing for the packs themselves.

Reviewing and revoking

For an account you can sign in to, OAuth connections appear under Connected apps on the MCP page in your account, with their client and scopes. Tokens are listed there too; revoking one cuts off its calls. Removing a client config only stops that client from asking. A machine account has no usable login mailbox, so its token instead rotates itself through the agent account flow.

Limits

Human-account tokens allow 1,200 requests a minute each. An agent account’s token lineage shares that budget across rotations. Stripe-calling billing routes reached through a token share 60 requests a minute per account: card setup, purchase, default-card changes, card removal, and card-list cache misses. Agent-account purchases and human-account token purchases are capped at three attempts per rolling 24 hours per account. The paid agent signup counts, as do failures after an attempt is reserved; human browser-session purchases are exempt. A cap refusal is HTTP 429 / code 4038 agent_purchase_capped, distinct from ordinary HTTP 429 / code 5000 rate_limited. Both provide Retry-After.

OAuth authorization and completion share a 30-per-minute address bucket. Registration has its own 30-per-minute bucket; /oauth/token has a separate 600-per-minute bucket. Each uses one IPv4 address or one IPv6 /64 as its key. Raw-token introspection cache misses have another 30-per-minute bucket on the same key; that is not a limit on every MCP tool call.

Start transcribing with 30 free minutes

Preview up to 5 minutes before you create an account, and a new personal account gets a free preview of its first upload in the app. Full transcription needs an account and minutes; eligible personal accounts get 30 free minutes once, after card verification or with a first purchase. No subscription.

Get 30 free minutes