{
  "markdown": "# opn-mcp\n\n[![opn-mcp MCP server](https://glama.ai/mcp/servers/ysalitrynskyi/opn-mcp/badges/score.svg)](https://glama.ai/mcp/servers/ysalitrynskyi/opn-mcp)\n\nAn [MCP](https://modelcontextprotocol.io) server for **[opn.onl](https://opn.onl)** — the open-source, self-hostable URL shortener. It lets AI assistants (Claude Desktop, Cursor, etc.) shorten links, read analytics, generate QR codes, and manage links in natural language.\n\nWorks against the hosted service **or your own self-hosted instance**.\n\n## Setup\n\n### 1. Get an API key\n\nOn your opn.onl instance, go to **Settings → API Keys**, create a key, and copy it (it starts with `opn_` and is shown once).\n\n### 2. Add the server to your MCP client\n\n**Claude Desktop** — edit `claude_desktop_config.json` (`~/Library/Application Support/Claude/` on macOS, `%APPDATA%\\Claude\\` on Windows):\n\n```json\n{\n  \"mcpServers\": {\n    \"opn\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"opn-mcp\"],\n      \"env\": {\n        \"OPN_API_KEY\": \"opn_your_key_here\"\n      }\n    }\n  }\n}\n```\n\nRestart your client. That's it — it talks to the hosted API (`https://l.opn.onl`) by default.\n\n> Prefer to run from source? Swap the `args` for the GitHub build — same config:\n> `\"args\": [\"-y\", \"github:ysalitrynskyi/opn-mcp\"]` (it builds on install).\n\n### Self-hosted instance\n\nPoint `OPN_BASE_URL` at your own instance's API host:\n\n```json\n{\n  \"mcpServers\": {\n    \"opn\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"opn-mcp\"],\n      \"env\": {\n        \"OPN_API_KEY\": \"opn_your_key_here\",\n        \"OPN_BASE_URL\": \"https://l.your-domain.com\"\n      }\n    }\n  }\n}\n```\n\n## Configuration\n\n| Env var | Required | Default | Description |\n|---------|----------|---------|-------------|\n| `OPN_API_KEY` | ✅ | — | Your API key (`opn_…`), from Settings → API Keys |\n| `OPN_BASE_URL` | — | `https://l.opn.onl` | API base URL — set this for a self-hosted instance |\n\n## Tools\n\n**Links**\n\n| Tool | Description |\n|------|-------------|\n| `shorten_url` | Create a short link — optional alias, title, notes, scheduling (`starts_at`/`expires_at`), `max_clicks`, password, burn-after-reading, folder and tags |\n| `shorten_urls_bulk` | Shorten many URLs at once, optionally into a folder |\n| `list_links` | List your links (limit, offset, search, folder or tag filter) |\n| `update_link` | Update destination, title, notes, scheduling, click limit, folder or protections; clear fields with `remove_*` flags |\n| `delete_link` | Delete a link |\n| `clone_link` | Duplicate a link under a fresh short code |\n| `toggle_link_pin` | Pin or unpin a link |\n| `check_alias_available` | Check whether a custom alias is free before using it |\n\n**Analytics**\n\n| Tool | Description |\n|------|-------------|\n| `get_link_stats` | Per-link analytics (clicks, unique visitors, geo, cities, devices, browsers, OS, referrers); optional `days` window |\n| `get_dashboard_stats` | Account-wide analytics across all your links |\n\n**QR & URL helpers**\n\n| Tool | Description |\n|------|-------------|\n| `get_qr_code` | Get a link's QR image — optional brand colour, centre logo, PNG/SVG |\n| `check_url_health` | Check a destination URL is reachable before shortening |\n| `build_utm_url` | Append UTM campaign parameters to a URL |\n| `preview_url_metadata` | Fetch Open Graph metadata (title, description, image) for a URL |\n\n**Tags & folders**\n\n| Tool | Description |\n|------|-------------|\n| `list_tags` / `create_tag` | List or create tags |\n| `add_tags_to_link` / `remove_tags_from_link` | Attach or detach tags on a link |\n| `list_folders` / `create_folder` | List or create folders |\n| `move_links_to_folder` | Move links into a folder |\n\n## Example prompts\n\n- \"Shorten https://example.com/very/long/url and call it launch-2026\"\n- \"Shorten these five URLs into a new folder called Q3 Campaign\"\n- \"How many clicks did link 42 get in the last 30 days, and from which countries?\"\n- \"Give me a branded SVG QR code for link 42\"\n- \"Tag my last 10 links as 'newsletter' and show my dashboard stats\"\n- \"Build a UTM link for https://example.com — source newsletter, medium email\"\n\n## Development\n\n```bash\nnpm install\nnpm run build      # tsc → dist/\nnpm test           # vitest\nOPN_API_KEY=opn_… npm run dev   # run from source (stdio)\n```\n\n## Releasing\n\nAll three registries are owned by **`ysalitrynskyi`** (npm user, GitHub user, and\nthe `io.github.ysalitrynskyi` MCP-registry namespace), so publishing must be done\nwhile signed in as that account.\n\n1. Bump the version in **three** places and keep them identical:\n   `package.json`, `server.json` (top-level **and** the `packages[0].version`),\n   and `SERVER_VERSION` in `src/server.ts`.\n2. Land it: commit to `main` and push. CI (`.github/workflows/ci.yml`) runs\n   `build` + `test` — it does **not** publish.\n3. Publish to npm (as npm user `ysalitrynskyi`):\n   ```bash\n   npm login\n   npm publish        # prepublishOnly runs the build\n   ```\n4. Publish to the MCP registry (as GitHub user `ysalitrynskyi`):\n   ```bash\n   mcp-publisher validate      # optional, offline check\n   mcp-publisher login github  # interactive browser OAuth\n   mcp-publisher publish\n   ```\n\n[Smithery](https://smithery.ai) (`smithery.yaml`) and [Glama](https://glama.ai)\n(`glama.json`) track the npm/registry release automatically — no separate step.\n\n## License\n\nMIT © ysalitrynskyi. Part of the [opn.onl](https://github.com/ysalitrynskyi/opn.onl) project.\n",
  "bytes": 5366,
  "sha": "bbeb949e6db2d11d7369c263f60095dd7aa0a8ff2dd623a38669da9528557d48",
  "repo_slug": "ysalitrynskyi/opn-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_ysalitrynskyi_opn_mcp_652b9016/readme"
}