{"_id":"@airi.live/mcp","name":"@airi.live/mcp","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@airi.live/mcp","version":"1.0.0","description":"MCP server for Airi — upload, transcode, DRM-encrypt and publish video from your AI assistant","type":"module","bin":{"airi-mcp":"dist/index.js"},"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/raestrada95/airi-kms.git","directory":"mcp"},"homepage":"https://airi.live","bugs":{"url":"https://github.com/raestrada95/airi-kms/issues"},"keywords":["mcp","model-context-protocol","airi","video","drm","widevine","playready","fairplay","transcoding","hls","dash","claude"],"scripts":{"build":"tsc -p tsconfig.build.json","dev":"tsx src/index.ts","typecheck":"tsc","smoke":"tsx test/smoke.ts","test:upload":"tsx test/upload.ts","prepublishOnly":"npm run typecheck && npm run build && npm run test:upload && npm run smoke"},"engines":{"node":">=20"},"dependencies":{"@modelcontextprotocol/server":"^2.0.0","zod":"^4.4.3"},"devDependencies":{"@types/node":"^24.10.1","tsx":"^4.23.1","typescript":"^5.9.3"},"license":"ISC","_id":"@airi.live/mcp@1.0.0","gitHead":"efab7db27490d7ebc204d53dfbef8e4e1a49fac1","_nodeVersion":"24.6.0","_npmVersion":"11.5.1","dist":{"integrity":"sha512-ikDpEIoGqyN5Dj3iJr2QkcFfpWkfRh0YSdJEe20vTIind9TQvhixb/UHpOgyIJHLI6/w/ogl8/foxSFpfvQoAw==","shasum":"6dc2d0c09df1900fd4ed509b465af774aed17be1","tarball":"https://registry.npmjs.org/@airi.live/mcp/-/mcp-1.0.0.tgz","fileCount":29,"unpackedSize":151977,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCBRpfmSMp30UgzHWFBTeUaiJgf9oruHVzR1yKVucP1WwIgVurHU4vQVK2Df9ykCGNVPpeGoykvblO0+H/5deEhcdk="}]},"_npmUser":{"name":"airilive","email":"raestrada@airi.live"},"directories":{},"maintainers":[{"name":"airilive","email":"raestrada@airi.live"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp_1.0.0_1785886817728_0.9797715477335933"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-04T23:40:17.532Z","1.0.0":"2026-08-04T23:40:17.874Z","modified":"2026-08-04T23:40:18.111Z"},"maintainers":[{"name":"airilive","email":"raestrada@airi.live"}],"description":"MCP server for Airi — upload, transcode, DRM-encrypt and publish video from your AI assistant","homepage":"https://airi.live","keywords":["mcp","model-context-protocol","airi","video","drm","widevine","playready","fairplay","transcoding","hls","dash","claude"],"repository":{"type":"git","url":"git+https://github.com/raestrada95/airi-kms.git","directory":"mcp"},"bugs":{"url":"https://github.com/raestrada95/airi-kms/issues"},"license":"ISC","readme":"# @airi.live/mcp\n\nAiri video infrastructure as [MCP](https://modelcontextprotocol.io) tools: upload a\nfile, transcode it into a quality ladder, encrypt it with Widevine, PlayReady and\nFairPlay, and get back an embeddable player — from your AI assistant, in one\nconversation.\n\nRuns locally over stdio. Your token never leaves your machine, and an upload's\nbytes go from disk straight to the bucket.\n\n## Add it\n\nGet a token at [airi.live/app/developers](https://airi.live/app/developers), then:\n\n**Claude Code**\n\n```bash\nclaude mcp add airi --env AIRI_KMS_TOKEN=kms_live_... -- npx -y @airi.live/mcp\n```\n\n**Claude Desktop, Cursor, Windsurf, and anything else with a JSON config**\n\n```json\n{\n  \"mcpServers\": {\n    \"airi\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@airi.live/mcp\"],\n      \"env\": { \"AIRI_KMS_TOKEN\": \"kms_live_...\" }\n    }\n  }\n}\n```\n\nNode 20 or newer. Nothing to install — `npx` fetches it on first run.\n\n## Try it\n\n> Upload ~/Videos/keynote.mp4, encode it at 1080p and 720p with DRM, and give me\n> an embed I can paste into our site.\n\nThe assistant will run roughly this:\n\n```\nairi_storage_upload   path=~/Videos/keynote.mp4        → file id\nairi_encoding_quote   heights=[1080,720] duration=600  → $0.16\nairi_encoding_start   package=hls+dash drm=true        → job id\nairi_encoding_job     wait=true                        → completed\nairi_player_create    name=\"Site player\"               → player id\nairi_player_embed     file=<id>                        → signed URL + <iframe>\n```\n\n## The tools\n\n| | |\n|---|---|\n| **Storage** | upload, list, get, sign_url, delete, usage |\n| **Encoding** | quote, renditions, start, job, list, cancel, packaging_job |\n| **Players** | create, list, update, delete, embed |\n| **DRM keys** | create, get, list, rotate, periods |\n| **Policies** | set, list, resolve, delete |\n| **Usage** | balance, licenses, stats |\n| **Anything else** | api_endpoints, api_call |\n\nThirty-two in all, prefixed `airi_`.\n\nThree of them do more than one HTTP call, because the shape of the API is built\nfor browsers and an assistant is not one. `airi_storage_upload` takes a path and\nreturns a sealed file — declare, multipart upload, ETag collection and complete,\nall inside the tool. `airi_encoding_job` with `wait: true` blocks until the job\nfinishes, so an encode is one step rather than a poll loop. `airi_player_embed`\nreturns the `<iframe>` and `player.js` snippets alongside the resolved player.\n\nThe last two are an escape hatch. `airi_api_endpoints` reads the deployed API's\nown OpenAPI document — generated from the schemas that validate requests, so it\nis never stale — and `airi_api_call` runs whatever it finds. New endpoints are\nreachable the day they ship, without waiting for this package to catch up.\n\n## Two things it will not do\n\n**It will not hand your content keys to the model.** `key` and `iv` are the AES\nsecret that decrypts your video, and a tool result lands verbatim in the\nassistant's transcript — which is logged, replayed, and often sent to a third\nparty. The key tools return the key id, the PSSH boxes and the licence URLs, and\nredact the rest. `includeKeyMaterial: true` returns it when you genuinely need to\nencrypt locally with shaka-packager. Encoding with `drm: true` never needs it.\n\n**It will not touch billing or your account.** `/v1/admin` (credits balances,\nmints credentials), `/v1/auth` (issues session tokens) and `/cas` (the DRM\nprovider's callback) are refused no matter what your token is allowed to do.\nWhat an agent may do and what a credential may do are different questions, and\nthe answer to the first should not depend on how generously the second was\nminted. `--allow-admin` unlocks them if you decide otherwise.\n\nEverything else is reachable, because it is the job. Deletes and encodes carry\nMCP's `destructiveHint` so your client can ask before running them, and the tool\ndescriptions say plainly what cannot be undone: a contentId is permanent at the\nDRM provider, a deleted file has no undo, and cancelling an encode stops the\nbilling rather than the encoder.\n\n## Scope it down\n\nThe token is the real boundary — this server cannot grant what your credential\ndoes not have. Mint one for the assistant rather than reusing your own, with only\nthe scopes it needs:\n\n| scope | |\n|---|---|\n| `storage:read` `storage:write` | files and signed URLs |\n| `encoding:read` `encoding:write` | transcoding and packaging |\n| `player:read` `player:write` | players and embeds |\n| `keys:read` `keys:write` | DRM keys, policies, balance and usage |\n\nA read-only assistant is a credential with only the `:read` scopes. Revoke it\nfrom [airi.live/app/developers](https://airi.live/app/developers) and every tool\nstops working on the next call — Hyperdrive query caching is off for exactly\nthis reason.\n\n## Options\n\nFlags and environment variables both work; flags win. An env block is the better\nplace for the token, since a flag shows up in the process list.\n\n| flag | env | default |\n|---|---|---|\n| `--token` | `AIRI_KMS_TOKEN` | — (required) |\n| `--namespace` | `AIRI_KMS_NAMESPACE` | — |\n| `--url` | `AIRI_KMS_URL` | `https://kms.airi.live` |\n| `--allow-admin` | `AIRI_MCP_ALLOW_ADMIN=true` | off |\n| `--timeout` | `AIRI_KMS_TIMEOUT_MS` | `60000` |\n| | `AIRI_DASHBOARD_URL` | `https://airi.live` |\n\n`--namespace` is only needed when your token is not already confined to one.\nStorage, encoding and players refuse to run without a concrete namespace: the\nobject key is `${namespace}/${id}`, so a wildcard would write to `/uuid`.\n\n`airi-mcp --help` prints all of it.\n\n## Development\n\n```bash\nnpm install\nnpm run dev          # run from source\nnpm run typecheck\nnpm run test:upload  # multipart correctness against a stub bucket\nnpm run smoke        # CLI, JSON-RPC handshake, tool registration, the admin guard\nSMOKE_LIVE=true npm run smoke   # also calls read-only tools against the API\n```\n\n`test/upload.ts` is the one that matters. The multipart path is the only part of\nthis server that can corrupt data rather than merely fail — an off-by-one in the\noffset arithmetic produces an object of exactly the right size whose bytes are\nwrong, and nothing downstream notices until a viewer sees a broken video. The\nlive part size is 100 MB, so testing that boundary against the real service would\nmean pushing a gigabyte; the stub uses 64 KB parts and reassembles what the\nbucket received, including the ragged-last-part case.\n\nNothing may be written to stdout that is not a JSON-RPC message — stdout *is* the\ntransport, and one stray `console.log` corrupts the stream in a way users see as\n\"the MCP server failed to start\" with no clue why. Diagnostics go to stderr.\n\n## Links\n\n- [MCP guide](https://airi.live/docs/mcp) — this package, in prose\n- [Docs](https://airi.live/docs) — how the pieces fit together\n- [API reference](https://kms.airi.live/docs) — every endpoint and parameter\n- [Issues](https://github.com/raestrada95/airi-kms/issues)\n\nISC\n","readmeFilename":"README.md","_rev":"1-799f9a3453e57e6af73e036f01d44dbc"}