# Shorty for AI agents — MCP, llms.txt & Markdown docs

> How AI assistants and agents connect to Shorty: the remote MCP server, machine-readable llms.txt / llms-full.txt, Markdown twins of every docs page, the OpenAPI spec, and agent interaction guidelines.

Shorty is built to be read and used by agents. This page routes you to the machine-readable surfaces.

## Connect over MCP

Shorty runs a remote Model Context Protocol server behind OAuth 2.1. Add it to Claude Code with one command:

```bash
claude mcp add --transport http shorty https://aishorty.com/api/mcp
```

The endpoint is `https://aishorty.com/api/mcp`. It offers 12 scoped operations (each is reachable only when your grant includes its scope). The server uses Code Mode: it lists two tools, `search_tools` and `execute_typescript`, and the assistant finds the operations below with `search_tools` and runs them with `execute_typescript`. See [Connecting AI assistants](/docs/connecting-ai-assistants) for scopes and consent details.

| Operation | Access | Description |
| --- | --- | --- |
| `search_articles` | Read | Search the current user's Shorty library by free-text query (title, description, URL). Returns matching article cards with id, title, description, articleType and createdAt — pass the id to get_article for the full summary. |
| `list_recent_articles` | Read | List the current user's most recently summarized articles. Use when the user asks about their recent summaries, library overview, or 'what have I saved lately'. |
| `get_article` | Read | Fetch a single article (summary, highlights, body) so you can answer questions about it. Pass a real article UUID as articleId. |
| `list_transcriptions` | Read | List the current user's audio/video transcriptions (newest first) with status and progress. Optional free-text query matches the transcript text. |
| `get_transcription` | Read | Fetch a single transcription (status, language, full transcript text) by its UUID so you can answer questions about it. |
| `get_usage_quota` | Read | Read the current user's Shorty plan (Free / Premium / Pro), tier limits (upload sizes, realtime minutes, conversion caps), and current cloud-conversion usage against the daily quota. |
| `get_job_status` | Read | Check the status of a job you started (summary, transcription, or subtitles) by its jobId. Returns { found, status, jobType, outputId? }: status is QUEUED \| PROCESSING \| SUCCESS \| ERROR \| CANCELLED. Poll until terminal (SUCCESS/ERROR/CANCELLED). |
| `search_docs` | Read | Search Shorty's developer documentation (API reference, error codes, webhooks, MCP guide) by free-text query. Returns matching pages with a snippet, the page URL, and a raw-Markdown twin URL you can fetch directly. |
| `create_youtube_summary` | Write | Start an AI summary job for a YouTube URL. Returns { jobId, articleId? }. If articleId is present the summary already exists — read it with get_article; get_job_status on that jobId also reports SUCCESS with outputId. Otherwise the job started — poll it with get_job_status. Pass a stable idempotencyKey to make retries safe (a repeat returns the original job). |
| `create_content_summary` | Write | Start an AI summary job for a webpage URL, pasted text, or a file already uploaded to Shorty. Supply exactly ONE source field, and it must be the one the sourceType reads: 'webpage'/'article' → url; 'text' → text; 'pdf'/'word'/'audio' → fileKey. Those last three read an already-uploaded file and NEVER accept a url — a link to a PDF cannot be summarized here, the file has to be uploaded first. Returns { jobId, articleId? }. If articleId is present the summary already exists — read it with get_article; get_job_status on that jobId also reports SUCCESS with outputId. Otherwise poll get_job_status. Pass a stable idempotencyKey to make retries safe. |
| `create_transcription` | Write | Transcribe a publicly accessible audio/video URL via the GPU service. Returns { jobId, trackingUrl } to follow, or an { error } if it did not start (relay that reason and suggest uploading the file instead). Poll progress with get_job_status. Pass a stable idempotencyKey to make retries safe. |
| `create_subtitles` | Write | Generate subtitles for a public video/audio URL. Style is CLEAN \| TIKTOK \| PODCAST \| MINIMAL; not every style is included in every plan, and CLEAN always is. Returns { jobId, trackingUrl } — poll get_job_status; the user can download SRT/VTT, and a captioned MP4 where their plan includes it. If the account has reached a plan limit the call is denied with a message naming the limit — report that message as-is. Call get_usage_quota for the account's actual limits and remaining usage. Pass a stable idempotencyKey to make retries safe. |

## Machine-readable surfaces

- [llms.txt](/llms.txt) — Site + API index for LLMs.
- [llms-full.txt](/llms-full.txt) — Full API docs + operation summary in one file.
- [OpenAPI 3.1 spec](/api/openapi) — The machine-readable contract.
- [API reference](/docs/api) — Human docs (each has a .md twin).

## Markdown twins

Every documentation page under `/docs` serves a raw Markdown version at the same URL with a `.md` suffix — e.g. `/docs/api.md`, `/docs/api-reference/createSummary.md`. Fetch the `.md` variant for clean, token-efficient context.

## Agent guidelines

- Authenticate every request with `Authorization: Bearer <key>` (a `shk_live_` key or OAuth token).
- Respect rate limits: 60 req/min free, 300/min paid — counted per API key for `shk_live_` callers and per user for OAuth/MCP callers; read the `RateLimit` headers and back off on 429.
- Usage quotas are shared across the app, MCP, and API — a 429 `quota_exhausted` means the plan allowance is spent, not a transient limit.
- Send an `Idempotency-Key` on writes so retries never create duplicate jobs.
- Writes are async: expect `202` + a `job_id`, then poll `GET /v1/jobs/{id}`. Never assume synchronous completion.
- On any error, read the RFC 9457 `code` and branch on it — do not parse human message text.
