{
  "markdown": "# qencode-mcp\n\nModel Context Protocol (MCP) server for the [Qencode Transcoding API](https://docs.qencode.com/api-reference/transcoding).\n\nConnect any MCP-compatible AI client — Claude, Cursor, ChatGPT, Grok, Gemini — to your Qencode account and let it submit, monitor, and reason about transcoding jobs on your behalf.\n\n\n## Quick example\n\nOnce your client is connected (see [Connect a client](#connect-a-client)), ask your agent in plain English:\n\n> Transcode `https://example.com/input.mp4` as an HLS ladder at 1080/720/540/360. Put it in my R2 bucket `videos/demo/`.\n\nThe agent picks the `hls_abr` recipe, fills in the per-rendition encoding params, submits via `start_encode2_raw`, and polls until the job is done.\n\n## Prerequisites\n\n- A **Qencode portal account** with at least one project — sign in at the portal for your environment: <https://portal.qencode.com> (production) or <https://portal-qa.qencode.com> (QA). You select the project during the OAuth consent step.\n- An **MCP-compatible client** (Claude, Cursor, ChatGPT, Grok, Gemini, or any custom client).\n\nThere are no API keys to copy into client config — authentication is browser-based OAuth.\n\n## How it works\n\nThe connector uses standard OAuth 2.1 — no API keys in client config. On first use, your client opens a browser, you sign in to your Qencode portal account, pick a project, and approve the requested scopes. The client stores the token; subsequent calls are silent until the token expires.\n\n**Scopes the client should request at authorize time** (published via Protected Resource Metadata):\n\n| Scope | Purpose |\n| ----- | ------- |\n| `openid` | OIDC identity |\n| `profile` | Display name |\n| `email` | Account email |\n| `offline_access` | Refresh token |\n| `transcoding:read` | `get_job_status`, `wait_for_job`, docs tools |\n| `transcoding:write` | `transcode_video`, `start_encode2_raw` |\n\nThe RS enforces `transcoding:read` and `transcoding:write` on access tokens at the transport layer.\n\nYour Qencode API keys never leave the portal. The MCP server derives a short-lived session token per request via an internal portal endpoint.\n\n## Environments\n\nThe same connector is deployed in two environments. Each has its own domains, accounts, projects, and credentials — sign in to the portal that matches the endpoint you connect to.\n\n| Role | Production | QA (testing) |\n| --- | --- | --- |\n| MCP endpoint (connect here) | `https://mcp.qencode.com/mcp` | `https://mcp-qa.qencode.com/mcp` |\n| Portal (sign in / projects) | `https://portal.qencode.com` | `https://portal-qa.qencode.com` |\n| Authorization server | `https://auth.qencode.com` | `https://auth-qa.qencode.com` |\n| Qencode API | `https://api.qencode.com` | `https://api-qa.qencode.com` |\n\nThe instructions below use the **production** endpoint. To test against QA, swap in the QA URL and sign in at the QA portal.\n\n## Connect a client\n\n**Endpoint:** `https://mcp.qencode.com/mcp` — same for every client below. Sign in to your Qencode account when the browser opens and approve access.\n\n> **QA (internal testing):** use `https://mcp-qa.qencode.com/mcp` and sign in at the QA portal instead.\n\n| Client | Where to add it | MCP URL / config |\n| --- | --- | --- |\n| **Claude** (chat) | Message box → **+** → Connectors → Add connector | `https://mcp.qencode.com/mcp` |\n| **Claude Code** | Terminal | `claude mcp add --transport http qencode https://mcp.qencode.com/mcp` |\n| **ChatGPT** | Apps → search **Qencode** → Connect; or Developer Mode → Build app | Connector URL: `https://mcp.qencode.com/mcp` |\n| **Gemini** | `~/.gemini/settings.json` → `mcpServers` | `\"httpUrl\": \"https://mcp.qencode.com/mcp\"` — then `/mcp auth qencode` in the CLI |\n| **Cursor** | Settings → Tools & MCP → New MCP Server (or `~/.cursor/mcp.json`) | `\"url\": \"https://mcp.qencode.com/mcp\"` — restart Cursor after saving |\n\n**Cursor** (`mcp.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"qencode\": { \"url\": \"https://mcp.qencode.com/mcp\" }\n  }\n}\n```\n\n**Gemini** (`settings.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"qencode\": {\n      \"httpUrl\": \"https://mcp.qencode.com/mcp\",\n      \"timeout\": 30000,\n      \"trust\": false\n    }\n  }\n}\n```\n\nTip: sign in to [portal.qencode.com](https://portal.qencode.com) in your browser before connecting — OAuth goes smoother.\n\n## What the connector exposes\n\n### Tools\n\n**Transcoding & jobs**\n\n| Tool | Description |\n| ---- | ----------- |\n| `transcode_video` | Submit a job from a source URL to one or more outputs. Convenience wrapper — auto-injects `encoder_version: 2` (or `1` for VMAF) when omitted. |\n| `start_encode2_raw` | Escape hatch — submit a job with the full `query` JSON exactly as the [Qencode API](https://docs.qencode.com/api-reference/transcoding/#start_encode2___query__attributes--format__attributes) expects. |\n| `get_job_status` | One-shot status snapshot by `task_token`. |\n| `get_job_status_detailed` | Full, authoritative job status, including per-rendition progress and output details. |\n| `wait_for_job` | Poll until terminal state, timeout, or internal poll cap. Do not call in parallel with other tools in the same client batch. |\n| `search_qencode_docs` | Search the built-in knowledge base of recipes and reference docs. |\n| `fetch_qencode_doc` | Fetch the full content of a knowledge-base resource by `qencode://` URI (tool-based counterpart to `resources/read`). |\n\n**Media Storage**\n\nBucket management and ingest for Qencode Media Storage. These ride the same OAuth grant as the transcoding tools — no extra scope and no re-consent.\n\n| Tool | Description |\n| ---- | ----------- |\n| `list_buckets` | List the Media Storage buckets available to the account. |\n| `create_bucket` | Create a new bucket. Called only on an explicit request — not to satisfy a missing `destination`. |\n| `list_objects` | Browse the contents of a bucket. |\n| `get_download_url` | Return a time-limited download URL for an existing object. |\n| `download_url_to_bucket` | Server-side copy of a public URL into a bucket (ingest, no transcoding). |\n\n### Resources\n\nThe server ships a knowledge base of recipes and reference docs, exposed as MCP resources so the agent can fetch only what it needs. Notable URIs:\n\n- `qencode://docs/best-practices` — composition defaults the agent applies automatically\n- `qencode://docs/storage` — destination compatibility matrix (Qencode S3, R2, AWS S3, Azure, B2, FTP/SFTP)\n- `qencode://docs/error-codes` — error code → cause → fix\n- `qencode://docs/gotchas` — non-obvious API quirks\n- `qencode://schema/digest` — full attribute reference for `start_encode2`\n- `qencode://recipe/<slug>` — one per feature flow: `hls_abr`, `mp4_ladder`, `audio_outputs`, `thumbnails`, `speech_to_text`, `subtitles`, `stitching`, `drm_widevine_ezdrm`, `drm_fairplay_ezdrm`, `drm_playready_ezdrm`, `drm_aes128`, `drm_buydrm`, `drm_expressplay`, `codec_av1`, `per_title_encoding`, `incremental_abr`, `refresh_abr_playlist`, `callbacks`, `reliability`, `video_metadata`\n\nUse `search_qencode_docs` to discover the right recipe URI for a goal.\n\n### Prompts (slash commands)\n\nIn clients that surface MCP prompts, **21** one-shot templates are available. Each tells the agent to read the matching `qencode://recipe/...` resource and submit via `start_encode2_raw`.\n\n**ABR / packaging:** `encode_hls_abr`, `encode_dash_abr`, `encode_mp4_ladder`, `encode_incremental_rung`, `encode_refreshing_playlist`\n\n**Codecs / quality:** `encode_av1`, `tune_per_title`\n\n**Audio / images / text:** `extract_audio`, `generate_thumbnails`, `transcribe`, `add_subtitles`\n\n**Probe / stitch:** `get_video_metadata`, `stitch_videos`\n\n**Production hooks:** `enable_callbacks`, `enable_reliability`\n\n**DRM:** `encode_aes128_hls`, `encode_widevine_ezdrm`, `encode_playready_ezdrm`, `encode_fairplay_ezdrm`, `encode_drm_buydrm`, `encode_drm_expressplay`\n\n### Source URL rules\n\n`transcode_video` and `start_encode2_raw` accept `source` values with schemes `https://`, `http://`, `s3://`, or `tus:`. FTP/SFTP and private/metadata URLs are rejected at the tool boundary (SSRF defence). See `docs/security/THREAT_MODEL.md` for limitations.\n\n## Security\n\nAuthentication is OAuth 2.1 only — there is no static-API-key mode. Your Qencode API keys never leave the portal; the server derives a fresh, short-lived session token per request via an internal portal endpoint. Source URLs are validated at the tool boundary (SSRF defence — see [Source URL rules](#source-url-rules)).\n\nFull threat model and adversarial test coverage: [`docs/security/THREAT_MODEL.md`](docs/security/THREAT_MODEL.md).\n\n## Development\n\n```bash\nuv venv && uv pip install -e \".[dev]\"\nNO_NETWORK=1 pytest -q              # offline L1 + L2 + L5 (~540 tests)\npytest -m protocol                # MCP wire conformance only\npytest -m unit                    # per-tool logic (FakeQencode)\npytest -m security                # OWASP MCP Top 10 adversarial suite\n```\n\nProtocol tests run fully offline (mocked Authorization Server, portal, and Qencode API). **CI** is the Jenkins job `mcp_automated_tests` (`Jenkinsfile.manual`): manual checkboxes for any layer, nightly L3 cron, weekly L4 cron.\n\n### Supported MCP protocol versions\n\nClients negotiate a version at `initialize`. This server targets **[MCP 2025-11-25](https://modelcontextprotocol.io/specification/2025-11-25)** as the primary version. CI also runs conformance tests against **2025-06-18** because JSON-RPC batching behavior differs between earlier revisions. We do not claim support for **2025-03-26** or older wire semantics beyond what the underlying SDK negotiates.\n\n| Version     | Support        | Notes                                      |\n| ----------- | -------------- | ------------------------------------------ |\n| 2025-11-25  | Primary        | Streamable HTTP, resumable SSE where used  |\n| 2025-06-18  | CI matrix      | Regression guard for mid-2025 clients      |\n| 2025-03-26  | Not targeted   | Batching semantics differ from 2025-06-18  |\n\n### More docs\n\n- Local server / env vars: [`docs/local-development.md`](docs/local-development.md)\n- Test layers (L1–L5): [`docs/testing.md`](docs/testing.md) and [`tests/README.md`](tests/README.md)\n- L3 against live QA / PROD: [`tests/integration/README.md`](tests/integration/README.md)\n- L4 agent evals: [`evals/README.md`](evals/README.md)\n- Pre-release gate: [`docs/release-checklist.md`](docs/release-checklist.md)\n\n## Versioning policy\n\nThe connector follows [SemVer](https://semver.org/) applied to the **MCP surface** — tools, prompts, resources, OAuth scopes, and supported protocol versions. Qencode HTTP API changes are out of scope (they are the API's own concern, not the connector's).\n\n- **MAJOR** — a breaking surface change: a tool/prompt/resource is removed or renamed, a previously optional argument becomes required, an OAuth scope is added or tightened in a way that forces re-consent, or a supported MCP protocol version is dropped.\n- **MINOR** — a backward-compatible addition: a new tool/prompt/resource, a new optional argument, or a newly supported protocol version.\n- **PATCH** — no change to the surface shape: tool/prompt description rewordings, knowledge-base/doc updates, and bug fixes.\n\nSurface changes are guarded by snapshot tests under [`tests/protocol/`](tests/protocol). When you change the surface, regenerate the snapshots (`python scripts/regen_tools_snapshot.py`) and bump the version in the **same** PR: `pyproject.toml`, `src/qencode_mcp/__init__.py`, `server.json`, and a new `CHANGELOG.md` entry must all agree.\n\n## Links\n\n- Changelog: [`CHANGELOG.md`](CHANGELOG.md)\n- OAuth 2.1 authorization-server spec: [`docs/oauth-spec.md`](docs/oauth-spec.md)\n- Qencode portal: <https://portal.qencode.com> (production) · <https://portal-qa.qencode.com> (QA)\n- Qencode API reference: <https://docs.qencode.com/api-reference/transcoding>",
  "bytes": 11782,
  "sha": "fc100ffd6821f81a94db3a632eb44ce078d82b260cd98aa3ae3400dcd9c69fef",
  "repo_slug": "qencode-corp/mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_com_qencode_qencode_8ad58c05/readme"
}