---
title: "Turn recordings into data your AI can use: ScribeSTT transcription, NAS integration, and MCP/CLI"
description: "Four scenarios: upload media files for transcripts, connect a NAS for batch transcription, let an AI agent discover the service over MCP, and drive transcription from scripts and schedules with the CLI"
canonical_url: "https://docs.ardge.com/en/knowledge/scribestt/mcp-cli-integration"
---
# Turn recordings into data your AI can use: ScribeSTT transcription, NAS integration, and MCP/CLI

> Four scenarios: upload media files for transcripts, connect a NAS for batch transcription, let an AI agent discover the service over MCP, and drive transcription from scripts and schedules with the CLI

## Overview

ScribeSTT is the audio and video transcription application on the ARX platform. It turns the spoken content of media files into transcripts with matching timestamps.

A knowledge base is built out of text. Turning recordings into timestamped transcripts is what lets an AI search across audio and video at all.

This article covers four scenarios. They're independent — read the ones you need:

- **Scenario 1 · Transcribe uploaded files**: send media files into ScribeSTT and get transcripts back
- **Scenario 2 · Connect a NAS**: connect your existing file server and batch transcribe what's already there
- **Scenario 3 · MCP integration**: let an AI agent connect to ScribeSTT and discover where to send audio
- **Scenario 4 · CLI integration**: drive transcription from scripts, schedules, or CI pipelines

## Applies to

- ARX OS 1.0 and later
- ScribeSTT 1.0 and later

## Before you begin

Confirm both of the following:

- ScribeSTT is installed from App Market
- Your account has access to ScribeSTT, so the application appears on your desktop

---

## Scenario 1. Transcribe uploaded files and get the transcripts

This scenario suits having a handful of files on hand and wanting to check the transcription quality first. The whole flow happens on the **Transcriptions** page, with nothing to configure.

### 1. Upload files

1. In the **Workspace** group in the left navigation, go to the **Transcriptions** page
2. Click **New**
3. Select the files to transcribe, or drag them onto the drop area
4. Set the language spoken in the media, to improve the accuracy of the transcript
5. Set a **Glossary** to help with proper nouns, which also improves accuracy
6. Click **Create** to start

> **Supported formats**: mp3, wav, ogg, flac, wma, opus, mp4, webm, avi, flv, wmv, 3gp
> **Per-upload limit**: 20 files; 1 GB per file

### 2. Review the transcript

1. On the **Transcriptions** page, click the row of the file you want to review
2. A dialog opens, offering three formats: srt, txt, json

### 3. Download the transcript

1. On the **Transcriptions** page, select the files to download
2. Click the **Download** dropdown button and click the transcript format you want

---

## Scenario 2. Connect a NAS and batch transcribe the recordings you already have

When your company recordings already sit on a NAS, there's no need to upload them one by one. ScribeSTT connects to the NAS over SMB to read the files directly and writes the transcripts into another folder you specify. The original data stays untouched, and the knowledge base's source text is built at the same time.

### 1. Set up the NAS connection

1. In the **Workspace** group in the left navigation, go to the **Remote data sources** page
2. Click **New** to open the **New data source** dialog
3. Enter the connection name, the NAS IP address, and the username and password
4. Click **Test connection**, then pick the folder in the **Shared folder** dropdown

### 2. Create the transcription task

1. Go to the **Tasks** page
2. Click **New**
3. Set the task name, source folder, target folder, output format, and scan depth

The fields mean:

- **Source folder**: where the recordings this task processes live
- **Target folder**: where the transcripts go once they're produced
- **Scan depth**: how many folder levels down to scan

### 3. Run the task

1. Go to the **Tasks** page
2. On the row of the task to run, click **Run**
3. Wait for that task row's Last Result to read Completed

---

## Scenario 3. An AI agent connects to ScribeSTT over MCP

The first two scenarios are all human work in the web interface. To let an AI agent — Claude Code, Cursor, or one your company built — reach ScribeSTT by itself, connect it through ScribeSTT's **HTTP MCP** interface.

Be clear about what MCP covers today: it is how an agent confirms the connection and discovers where to send audio. The transcription work itself runs over the REST API or the CLI (Scenario 4).

Once connected, the agent can call these two tools:

<table>
<thead>
  <tr>
    <th>
      Tool
    </th>
    
    <th>
      What it does
    </th>
    
    <th>
      Permission level
    </th>
    
    <th>
      Data source restriction
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        arxstt_get_system_version
      </code>
    </td>
    
    <td>
      Returns the server version, git SHA and build time — use it to confirm the connection works
    </td>
    
    <td>
      Any scope
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        arxstt_list_capabilities
      </code>
    </td>
    
    <td>
      Lists the tools this credential can call, plus the audio upload endpoint when the credential is allowed to upload. Call it first in every session
    </td>
    
    <td>
      Any scope
    </td>
    
    <td>
      —
    </td>
  </tr>
</tbody>
</table>

> ⚠️ **MCP is off by default**
> Every deployment has to opt in, because the endpoint is a new attack surface. Turn it on with the `mcp.enabled` setting or the `ARXSTT_MCP_ENABLED` environment variable.

> ⚠️ **Audio never travels through MCP**
> An MCP request body is capped at 256 KB, and that budget covers the tool call's arguments only. Audio goes through a separate REST endpoint, so the environment driving the agent has to be able to make HTTP requests (`curl`, Python `requests`, or a workflow engine's HTTP node all work).
> A pure chat interface that can't run code or send HTTP requests can't submit audio at all.

### 1. Create a PAT for access

The MCP connection authenticates with a **PAT (personal access token)**, issued by a super admin in the web interface.

1. Open ScribeSTT and go to the Users page
2. In the Action column of the user you're authorizing, click "Manage Personal Access Token"
3. In the "Personal Access Tokens" dialog, click the "Create" button
4. In the "Create Personal Access Token" dialog, set Token Name, the operation permissions, the folders it may access, and the expiry
5. Take the generated token

> ⚠️ **Store the token immediately**
> As soon as the token is created, put it somewhere the agent can read at startup. Lose it and your only option is to create another.

Creating a PAT means deciding two kinds of scope:

- **Operation scope**: `read_only` for queries, `default` to add create, update, cancel, retry and trigger, `destructive` to add delete. Every tool declares which of these it accepts
- **Data access scope**: which data sources this PAT can reach. Left empty, **everything is denied**

### 2. Configure the MCP server in your AI agent

Put these connection details into the agent's MCP configuration:

<table>
<thead>
  <tr>
    <th>
      Item
    </th>
    
    <th>
      Value
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Endpoint
    </td>
    
    <td>
      <code>
        https://<ScribeSTT-host>/mcp
      </code>
      
       — the default path, adjustable through the <code>
        mcp.endpoint
      </code>
      
       setting or the <code>
        ARXSTT_MCP_ENDPOINT
      </code>
      
       environment variable
    </td>
  </tr>
  
  <tr>
    <td>
      Transport
    </td>
    
    <td>
      Streamable HTTP
    </td>
  </tr>
  
  <tr>
    <td>
      Authentication
    </td>
    
    <td>
      <code>
        Authorization: Bearer <YOUR_PAT>
      </code>
      
      , where a PAT looks like <code>
        arxstt_pat_…
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Per-request limit
    </td>
    
    <td>
      256 KB, set by <code>
        mcp.maxRequestBodyBytes
      </code>
      
       or <code>
        ARXSTT_MCP_MAXREQUESTBODYBYTES
      </code>
      
      . It counts the tool call's arguments — audio never goes through this endpoint
    </td>
  </tr>
</tbody>
</table>

> 💡 **How to find <ScribeSTT-host>**
> Go to the ARX desktop, right-click the ScribeSTT icon, choose to open it in a new tab, and copy the domain from the address bar.

For Claude Code, the configuration looks like this:

```json
{
  "mcpServers": {
    "ScribeSTT": {
      "type": "http",
      "url": "https://<ScribeSTT-host>/mcp",
      "headers": {
        "Authorization": "Bearer <YOUR_PAT>"
      }
    }
  }
}
```

### 3. Upload audio and start a transcription

Call `arxstt_list_capabilities` first. When the PAT's scope is `default` or above, the response carries an `uploadEndpoint` descriptor naming the path to post to and the fields it accepts — read the path from there rather than hard-coding it. A `read_only` PAT gets no upload endpoint at all.

<table>
<thead>
  <tr>
    <th>
      Item
    </th>
    
    <th>
      Value
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Upload endpoint
    </td>
    
    <td>
      <code>
        POST /api/v1/transcriptions/upload
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Authentication
    </td>
    
    <td>
      The same Bearer PAT as the MCP connection
    </td>
  </tr>
  
  <tr>
    <td>
      Content type
    </td>
    
    <td>
      <code>
        multipart/form-data
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      File size limit
    </td>
    
    <td>
      1 GiB by default, set by <code>
        upload.maxFileSizeBytes
      </code>
      
       or <code>
        ARXSTT_UPLOAD_MAXFILESIZEBYTES
      </code>
    </td>
  </tr>
</tbody>
</table>

The form fields are `audioFile` (required), and optionally `language` (`zh`, `en`, `ja` or `ko`; omit it to auto-detect), `glossaryId`, and `outputFormats` (`txt`, `srt`, `json`; omit it to produce all three).

An upload example:

```bash
curl -X POST https://<ScribeSTT-host>/api/v1/transcriptions/upload \
  -H "Authorization: Bearer <YOUR_PAT>" \
  -F "audioFile=@meeting.mp3" \
  -F "language=en"
```

The response carries back the transcription's `id`.

> ⚠️ **No MCP tool returns a transcript**
> The two tools above cover connectivity and discovery only. Once the upload is accepted, follow the transcription's status and fetch its text over the REST API or with the CLI — see Scenario 4.

---

## Scenario 4. An AI agent transcribes audio with ScribeSTT over the CLI

The CLI is a single executable, suited to scripts, scheduled tasks, CI pipelines, or an agent with shell access issuing commands directly. It's designed for automation: every command supports `--json` output and classifies errors by exit code, so a program can decide what to do next.

The commands at a glance:

<table>
<thead>
  <tr>
    <th>
      Group
    </th>
    
    <th>
      Commands
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Authentication
    </td>
    
    <td>
      <code>
        auth login
      </code>
      
      , <code>
        auth status
      </code>
      
      , <code>
        auth logout
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Transcriptions
    </td>
    
    <td>
      <code>
        transcription upload
      </code>
      
      , <code>
        transcription get
      </code>
      
      , <code>
        transcription list
      </code>
      
      , <code>
        transcription output
      </code>
      
      , <code>
        transcription cancel
      </code>
      
      , <code>
        transcription retry
      </code>
      
      , <code>
        transcription delete
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Batch tasks
    </td>
    
    <td>
      <code>
        task create
      </code>
      
      , <code>
        task get
      </code>
      
      , <code>
        task list
      </code>
      
      , <code>
        task trigger
      </code>
      
      , <code>
        task delete
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Batch runs
    </td>
    
    <td>
      <code>
        run get
      </code>
      
      , <code>
        run list
      </code>
      
      , <code>
        run cancel
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Data sources
    </td>
    
    <td>
      <code>
        datasource list
      </code>
      
      , <code>
        datasource get
      </code>
      
      , <code>
        datasource entries
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Glossaries
    </td>
    
    <td>
      <code>
        glossary create
      </code>
      
      , <code>
        glossary get
      </code>
      
      , <code>
        glossary list
      </code>
      
      , <code>
        glossary delete
      </code>
    </td>
  </tr>
</tbody>
</table>

Global flags on every command: `--server <url>`, `--token <PAT>`, `--config <path>`, `--json`, `--fields <field list>`

> 💡 **Every command describes itself**`arx-stt --help --json` prints the whole tree, and `arx-stt transcription get --help --json` prints one command's arguments, flags and output fields. An agent can discover the surface instead of being told it.

### 1. Install the CLI

The ScribeSTT server serves the executable itself — no external package registry needed. These commands install it to `~/.local/bin`, with no sudo required:

```bash
export ARXSTT_SERVER="https://<ScribeSTT-host>"

OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m); [ "$ARCH" = "x86_64" ] && ARCH="amd64"; [ "$ARCH" = "aarch64" ] && ARCH="arm64"
mkdir -p "$HOME/.local/bin"
curl -fSL -o "$HOME/.local/bin/arx-stt" \
  "${ARXSTT_SERVER}/api/v1/cli/download?os=${OS}&arch=${ARCH}"
chmod +x "$HOME/.local/bin/arx-stt"
export PATH="$HOME/.local/bin:$PATH"
```

### 2. Sign in for access

The CLI authenticates with a PAT. Create one the same way as in Scenario 3 — see [Create a PAT for access](#1-create-a-pat-for-access).

```bash
# Store the PAT (this non-interactive form is the one to use for automation)
arx-stt auth login --server https://<ScribeSTT-host> --token <YOUR_PAT>

# Or pipe it in, to keep the token out of your command history
echo <YOUR_PAT> | arx-stt auth login --server https://<ScribeSTT-host>

# Or just set the environment variable, writing nothing to disk
export ARXSTT_TOKEN=<YOUR_PAT>
```

Then verify:

```bash
arx-stt auth status --json    # shows where the PAT came from, and checks it against the server
```

- The credential is taken in this order: `--token` flag, then `ARXSTT_TOKEN`, then the config file
- The config file lives at `~/.arx-stt/config.yaml` (mode 0600); `--config` or `ARXSTT_CONFIG` points at another path
- After a successful sign-in, later commands no longer need `--server`
- `arx-stt auth logout` only clears the locally stored PAT — it does **not** revoke it on the server

### 3. Transcribe a single file

```bash
# upload, wait for the transcription to reach a terminal state, then fetch the text
id=$(arx-stt transcription upload meeting.mp3 --json | jq -r .id)
arx-stt transcription get "$id" --json --wait --timeout 30m
arx-stt transcription output "$id" --format txt > transcript.txt

# or fused into one call, which prints the terminal-state transcription
arx-stt transcription upload meeting.mp3 --wait --json
```

`transcription upload` also takes `--language`, `--glossary` and `--formats`. `transcription output` writes to stdout by default and takes `--format txt|srt|json` or `--out FILE`.

With `--wait`, the exit code tells you the outcome: `0` completed or skipped, `5` failed or cancelled, `4` timed out. A timeout doesn't stop the server — run `transcription get --wait` again to resume watching.

Cancel, retry and delete a record with `transcription cancel <id>`, `transcription retry <id>` and `transcription delete <id>`. Retry applies to failed and cancelled records, not skipped ones, and delete needs a `destructive` PAT.

### 4. Batch transcription

Batch work is made of three objects: a **data source** (where the files are), a **task** (the static settings), and a **run** (one actual execution).

#### Pick the data sources

```bash
arx-stt datasource list --scheme smb --json
arx-stt datasource entries <datasourceId> --path recordings/ --json
```

`datasource entries` browses the folders so you can choose the source path. Local file system data sources are managed by the system and never appear in the list.

#### Create the task

```bash
arx-stt task create --name nightly \
  --source-datasource <datasourceId> --source-path recordings/ \
  --target-datasource <datasourceId> --target-path transcripts/ \
  --output-format txt --scan-depth 2 --json
```

- `--scan-depth`: how many folder levels down to scan
- `--glossary`: apply a glossary to every file in the task

#### Run and monitor

```bash
run=$(arx-stt task trigger <taskId> --json | jq -r .runId)
arx-stt run get "$run" --json --wait
arx-stt run list --task <taskId> --status failed --json
```

`task trigger` creates a run that scans the source folder and prints its `runId`. With `--wait` on `run get`, exit code `0` means completed and `5` means partial, failed or cancelled. `run cancel <id>` cancels a run and cascades to the transcriptions still active under it.

Deleting a task needs a `destructive` PAT, and is blocked while one of its runs is still active.

#### Glossaries

```bash
arx-stt glossary create --name product-names \
  --term "scribe stt=ScribeSTT" --term "ardge=ARDGE" --json
```

Each `--term` maps the form recognition tends to produce to the form you want in the transcript. Editing an existing glossary's terms is web-interface work; the CLI creates, reads and deletes.

### 5. What to watch for in automation

Errors are classified by exit code, so a script can branch on them instead of parsing error text:

<table>
<thead>
  <tr>
    <th>
      Exit code
    </th>
    
    <th>
      What it means
    </th>
    
    <th>
      What to do
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Success
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Client error — bad input, or not found
    </td>
    
    <td>
      Fix the input; don't retry
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        2
      </code>
    </td>
    
    <td>
      Authentication or authorization failure
    </td>
    
    <td>
      <strong>
        Don't retry automatically.
      </strong>
      
       The PAT is invalid or its scope was denied — rotate the PAT
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        3
      </code>
    </td>
    
    <td>
      Server error (5xx)
    </td>
    
    <td>
      Retry later
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        4
      </code>
    </td>
    
    <td>
      Network or transient error, including a poll timeout
    </td>
    
    <td>
      Retry with a backoff
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        5
      </code>
    </td>
    
    <td>
      The operation reached a terminal state of failed, cancelled or partial
    </td>
    
    <td>
      Inspect <code>
        errorCode
      </code>
      
       in the JSON output
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        130
      </code>
    </td>
    
    <td>
      Interrupted with Ctrl-C
    </td>
    
    <td>
      User abort
    </td>
  </tr>
</tbody>
</table>

JSON errors are printed to stdout in the form `{"errors":{"code":…,"message":…,"hint":…}}`.

When an agent uses the CLI, keep to the following:

- **Always pass --json**: table output is for humans, and a program can't parse it reliably
- **Narrow the output with --fields**: `--fields id,status`, for example — taking only what you need saves context
- **There is no --dry-run**: `create`, `trigger` and `delete` take effect immediately. Confirm the target with `get <id>` before you change or delete anything
- **Branch on the exit code, never on the message text**: the codes are a stable contract, the wording is not


## Sitemap

See the full [sitemap](https://docs.ardge.com/sitemap.md) for all pages.
