MCP Server
Reference for Flyn's hosted Model Context Protocol server: the endpoint and transport, how clients authenticate, every tool with its input parameters and annotations, and the errors and limits it returns.
Endpoint
https://www.flyn.to/mcpUse this exact URL, with the www. The address without www redirects to it, and clients drop the Authorization header on that redirect, so every tool call fails.
8
Tools
Streamable HTTP
Transport
2025-06-18
Protocol
OAuth or API key
Auth
This page is the parameter-level reference. To connect a specific assistant step by step, follow How do I connect an AI assistant to Flyn with MCP?; for an overview of what an assistant can do with Flyn, see Flyn for AI assistants.
Transport
- Streamable HTTP, stateless. Every message is a JSON-RPC 2.0 request sent as an HTTP
POSTto the endpoint, and every reply is a single JSON response. The server never opens an event stream and keeps no session, soGETandDELETEanswer 405. - Protocol versions:
2025-06-18and2025-03-26.initializeechoes the version you request when it is one of these and otherwise answers with 2025-06-18. Responses with a JSON-RPC body carry anMCP-Protocol-Versionheader. - Batches (a 2025-03-26 feature) are accepted, up to 20 messages per request, and get a batch response.
- CORS is open (
Access-Control-Allow-Origin: *, preflight answers 204), so browser-based clients can call it. Credentials are only ever a Bearer token, never a cookie. - Capabilities: tools only, with
listChanged: false.
Methods
| Method | Credentials | Returns |
|---|---|---|
| initialize | No | Negotiated protocol version, the tools capability, server info and usage instructions. |
| tools/list | No | All eight tools with name, title, description, inputSchema and annotations. |
| ping | No | An empty result. |
| notifications/initialized, notifications/cancelled | No | Nothing (HTTP 202). Sent with an id, they get an empty result. |
| tools/call | Yes | The tool result: a content array, with isError set when the call failed. |
| Any other method | Yes | JSON-RPC error -32601, Method not found. |
Discovery needs no credentials, so a client can list the tools before it signs in. A batch that contains any method needing credentials is checked as a whole.
curl -X POST https://www.flyn.to/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "my-client", "version": "1.0.0" }
}
}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"capabilities": { "tools": { "listChanged": false } },
"serverInfo": { "name": "flyn", "title": "Flyn Link Shortener", "version": "1.0.0" },
"instructions": "Flyn creates and manages short links (flyn.to) with click analytics. ..."
}
}Authentication
tools/call needs one of two credentials, sent as Authorization: Bearer ... on every request.
- OAuth sign-in, on every plan including Free. A client that supports OAuth adds the endpoint with no key. Its first unauthenticated tool call gets a 401 whose
WWW-Authenticateheader points to the protected-resource metadata, and from there the client opens the Flyn sign-in and consent page and receives an access token. There is no plan check anywhere in this flow. - API key, on Pro, Lifetime or Team. Send
Bearer flyn_sk_live_..., the same key the REST API takes. Keys are created in Settings, API Keys, and creating one needs a paid plan; an account can hold up to 5 active keys. See Authentication.
OAuth details
| Protected resource metadata | https://www.flyn.to/.well-known/oauth-protected-resource/mcp (RFC 9728; also served without the /mcp suffix) |
| Authorization server metadata | https://www.flyn.to/.well-known/oauth-authorization-server (RFC 8414; also served with a /mcp suffix) |
| Client registration | https://www.flyn.to/oauth/register (dynamic client registration, RFC 7591) |
| Authorization | https://www.flyn.to/oauth/authorize (the Flyn sign-in and consent page) |
| Token | https://www.flyn.to/oauth/token (authorization_code and refresh_token grants) |
| PKCE | Required, S256 only |
| Scope | mcp, the only scope: full link management on the signed-in account |
| Access token | Prefix flyn_mcp_at_, valid for 1 hour |
| Refresh token | Prefix flyn_mcp_rt_, valid for 90 days, replaced on every refresh |
The token endpoint accepts public clients and client credentials in the body or in an HTTP Basic header. Every authorization response carries an iss parameter (RFC 9207). A user can revoke a client at any time under Settings, Connected Apps; its next call is rejected.
Which calls an OAuth token reaches
An OAuth access token belongs to the MCP connection, not to the account at large. Outside /mcp it is accepted only by the few REST operations listed on the OpenAPI page, and it cannot create API keys or reach webhooks, domains, billing or account settings.
Plan limits
Every tool calls the same route as the REST API, so the account's plan applies exactly as it does in the dashboard. The MCP layer adds no gate of its own.
- Free accounts can create 25 links per calendar month (UTC), counted per account, or per workspace inside a team. The next create returns an error result naming the limit.
- Where a description below says Pro, the gate is any paid plan: Pro, Lifetime or Team. On Free, a call that uses such an input returns an error result naming the feature, with a link to pricing, and nothing is created or changed.
- On Free,
get_link_analyticsreturns timestamps and counts, with the location, device and referrer fields set to null, for the last 30 days. - Team role permissions apply as they do in the dashboard, for example a viewer cannot edit links.
Tools
The 8 tools tools/list returns. Descriptions are the ones the server sends to the model, word for word; parameter rows are condensed and add the schema constraints. Each tool also carries the four MCP annotation hints shown under its name; they are advisory, for clients that ask before running a destructive tool.
| Tool | Title | Kind |
|---|---|---|
| create_short_link | Create short link | Write |
| list_links | List links | Read-only |
| get_link | Get link details | Read-only |
| update_link | Update link | Destructive |
| delete_link | Delete link | Destructive |
| get_link_analytics | Get link analytics | Read-only |
| get_link_qr_code | Get QR code for a link | Read-only |
| create_qr_code | Create QR code | Read-only |
create_short_link
Create short link
readOnlyHint: falsedestructiveHint: falseidempotentHint: falseopenWorldHint: trueCreate a new Flyn short link that redirects to a destination URL. Returns the link id, slug, and full short URL. The destination is scanned with Google Safe Browsing before creation. Free accounts can create 25 links per calendar month; password protection, click limits, fallback URLs, weighted rotation, and conversion tracking require Flyn Pro.
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | The destination URL to shorten (https:// is assumed if no protocol is given). |
| slug | string | No | Optional custom slug (letters, numbers, hyphens, underscores; max 100 chars). A random slug is generated when omitted. |
| title | string | No | Optional human-readable title for the link. |
| tags | string[] | No | Optional tags for organizing links (Pro plan only). |
| expiresAt | string | No | Optional ISO 8601 datetime after which the link stops redirecting. |
| forwardParams | boolean | No | Whether query parameters appended to the short link are forwarded to the destination. Defaults to true. |
| password | string | No | Optional password required to open the link (Pro plan only). |
| clickLimit | integer | No | Optional maximum number of human clicks before the link stops redirecting (Pro plan only). Minimum 1. |
| fallbackUrl | string | No | Optional URL visitors are sent to after the click limit is reached (Pro plan only). |
| conversionTracking | boolean | No | Enable per-link conversion tracking (Pro plan only). The redirect then appends an unguessable click id to the destination URL, and the link reports conversions and total conversion value next to its clicks. Nothing is reported until the Flyn tag (or a server-to-server postback) is installed on the destination site, so tell the user that setup step is still theirs. |
| domain | string | No | Optional custom domain to mint the link on (must be a verified domain you own; Pro plan only). Omit to use your account default domain. |
| rotation | object[] | No | Optional weighted rotation of destinations, one short link splitting traffic between several URLs (Pro plan only). Every destination is Safe Browsing scanned before the link is created. At most 12 items; item shape and behaviour under Rotation destinations below. |
Runs the REST operation below, limited to 10 per minute per client IP. See Links.
list_links
List links
readOnlyHint: truedestructiveHint: falseidempotentHint: trueopenWorldHint: falseList the account's short links with pagination, search, and filtering. Returns each link's id, slug, short URL, destination, click count, and settings, plus its conversion counters: conversionTracking (whether the link carries a click id), conversions, and conversionValueCents (total value reported for those conversions, in cents, not currency converted).
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
| search | string | No | Optional text search across slug, destination URL, and title. |
| tag | string | No | Optional: only return links carrying this tag. |
| status | string | No | Optional status filter. One of: active, expired, archived. |
| page | integer | No | Page number, starting at 1. Minimum 1, default 1. |
| limit | integer | No | Results per page (max 100). Minimum 1, maximum 100, default 20. |
| sort | string | No | Sort column. One of: created_at, clicks, slug, title. Default created_at. |
| order | string | No | Sort direction. One of: asc, desc. Default desc. |
Runs the REST operation below, limited to 30 per minute per client IP. See Links.
get_link
Get link details
readOnlyHint: truedestructiveHint: falseidempotentHint: trueopenWorldHint: falseFetch one short link by its id, including destination, settings, total click count, and its conversion counters (conversionTracking, conversions, conversionValueCents).
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The link id (from create_short_link or list_links). |
Runs the REST operation below, limited to 60 per minute per client IP. See Links.
update_link
Update link
readOnlyHint: falsedestructiveHint: trueidempotentHint: falseopenWorldHint: trueUpdate an existing short link. Can re-point the destination URL without changing the printed/shared short link, plus edit title, slug, tags, expiration, rotation destinations, and Pro settings. Only the provided fields are changed.
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The link id to update. |
| url | string | No | New destination URL. |
| title | string | No | New title. |
| slug | string | No | New slug (changes the short URL; the old slug stops working). |
| tags | string[] | No | Replacement tag list. Pro plan only: the tool description does not say so, but the API rejects a non-empty tag list on the Free plan. |
| expiresAt | string | null | No | New ISO 8601 expiration, or null to remove. |
| status | string | No | Link status. One of: active, archived. |
| forwardParams | boolean | No | Forward appended query params to the destination. |
| password | string | No | Set a password (Pro plan only). |
| clickLimit | integer | No | Maximum human clicks before the link stops (Pro plan only). Minimum 1. |
| fallbackUrl | string | No | URL served after the click limit is reached (Pro plan only). |
| conversionTracking | boolean | No | Enable or disable per-link conversion tracking (Pro plan only). Enabling only makes the redirect append a click id; conversions arrive from the Flyn tag or the server postback on the destination site. |
| rotation | object[] | No | Replacement list of weighted rotation destinations (Pro plan only). This REPLACES the whole list, so read the link first and send back every destination you are keeping, each with the id it already has. Pass an empty array to stop rotating and serve the single url again. At most 12 items; item shape and behaviour under Rotation destinations below. |
Runs the REST operation below, limited to 10 per minute per client IP. See Links.
delete_link
Delete link
readOnlyHint: falsedestructiveHint: trueidempotentHint: trueopenWorldHint: truePermanently delete a short link. The short URL immediately stops redirecting and this cannot be undone.
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The link id to delete. |
Runs the REST operation below, limited to 60 per minute per client IP. See Links.
get_link_analytics
Get link analytics
readOnlyHint: truedestructiveHint: falseidempotentHint: trueopenWorldHint: falseFetch click events for a short link, newest first. Every plan sees timestamps and total counts; country, city, device, OS, browser, and referrer detail requires Flyn Pro (fields are null on the free plan). Free plans see the last 30 days of history. Click events carry no conversion data: for conversions and total conversion value, read the link itself with get_link or list_links.
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The link id. |
| page | integer | No | Page number, starting at 1. Minimum 1, default 1. |
| limit | integer | No | Click events per page (max 200 via MCP). Minimum 1, maximum 200, default 50. |
Runs the REST operation below, limited to 30 per minute per client IP. See Click Analytics.
get_link_qr_code
Get QR code for a link
readOnlyHint: truedestructiveHint: falseidempotentHint: trueopenWorldHint: falseRender a scannable QR code for an existing Flyn short link. Because the code encodes the short link rather than the destination, you can repoint the link later and every printed code follows, and each scan is counted as a click. The image is returned as a PNG you can look at. Custom colours and SVG output require Flyn Pro.
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The id of the link to render a QR code for. |
| format | string | No | Output format: 'png' (default) or 'svg'. SVG requires Flyn Pro. One of: png, svg. |
| size | integer | No | Maximum width in pixels, 64 to 2048, default 512. The image is the largest whole-module square that fits, so the real width is usually slightly smaller and is reported back. Minimum 64, maximum 2048. |
| margin | integer | No | Quiet zone in modules. Minimum and default is 4, as required by ISO/IEC 18004; smaller values are raised to 4 because a short quiet zone is the most common cause of a printed code failing to scan. Minimum 4, maximum 32. |
| level | string | No | Error correction level: 'L', 'M' (default), 'Q' or 'H'. Higher levels survive more damage but make the symbol denser. One of: L, M, Q, H. |
| fg | string | No | Foreground colour as 6-digit hex, default Flyn green #14532D. Requires Flyn Pro. |
| bg | string | No | Background colour as 6-digit hex, default #FFFFFF. Requires Flyn Pro. |
Runs the REST operation below, limited to 60 per minute per client IP. See QR Codes.
create_qr_code
Create QR code
readOnlyHint: truedestructiveHint: falseidempotentHint: trueopenWorldHint: falseRender a QR code for any of seven content types: a link, plain text, WiFi credentials, a v-card contact, a pre-filled email, a phone number, or a pre-filled SMS. The image is returned as a PNG you can look at. Nothing is stored, so this code is STATIC: it encodes the content directly and cannot be changed after it is printed. If the user wants a code they can repoint later, or wants to count scans, create a short link with create_short_link first and then call get_link_qr_code instead. Link, Text and WiFi work on every plan; V-card, E-mail, Call and SMS require Flyn Pro, as do custom colours and SVG output.
Input
| Parameter | Type | Required | Description |
|---|---|---|---|
| type | string | Yes | What to encode. 'url', 'text' and 'wifi' work on any plan. 'vcard', 'email', 'phone' and 'sms' require Flyn Pro. One of: url, text, wifi, vcard, email, phone, sms. |
| url | string | No | For type 'url': the URL to encode. |
| text | string | No | For type 'text': the plain text to show when scanned. |
| wifiSsid | string | No | For type 'wifi': the network name. |
| wifiPass | string | No | For type 'wifi': the password. Omit for an open network. |
| wifiEnc | string | No | For type 'wifi': security type. Defaults to WPA. One of: WPA, WEP, nopass. |
| wifiHidden | boolean | No | For type 'wifi': true if the network does not broadcast its SSID. |
| vcName | string | No | For type 'vcard': the person's full name. Required for a v-card. |
| vcOrg | string | No | For type 'vcard': organisation or company. |
| vcPhone | string | No | For type 'vcard': phone number. Include the country code, for example +15551234567. |
| vcEmail | string | No | For type 'vcard': email address. |
| vcUrl | string | No | For type 'vcard': website URL. |
| string | No | For type 'email': the recipient address. | |
| emailSubject | string | No | For type 'email': pre-filled subject line. |
| emailBody | string | No | For type 'email': pre-filled message body. |
| phone | string | No | For type 'phone': the number to dial. Include the country code, for example +15551234567, or the code only works for people dialling from the same country. |
| smsPhone | string | No | For type 'sms': the number to text. Include the country code. |
| smsBody | string | No | For type 'sms': the pre-filled message. The sender still has to press send. |
| format | string | No | Output format: 'png' (default) or 'svg'. SVG requires Flyn Pro. One of: png, svg. |
| size | integer | No | Maximum width in pixels, 64 to 2048, default 512. The image is the largest whole-module square that fits, so the real width is usually slightly smaller and is reported back. Minimum 64, maximum 2048. |
| margin | integer | No | Quiet zone in modules. Minimum and default is 4, as required by ISO/IEC 18004; smaller values are raised to 4 because a short quiet zone is the most common cause of a printed code failing to scan. Minimum 4, maximum 32. |
| level | string | No | Error correction level: 'L', 'M' (default), 'Q' or 'H'. Higher levels survive more damage but make the symbol denser. One of: L, M, Q, H. |
| fg | string | No | Foreground colour as 6-digit hex, default Flyn green #14532D. Requires Flyn Pro. |
| bg | string | No | Background colour as 6-digit hex, default #FFFFFF. Requires Flyn Pro. |
Runs the REST operation below, limited to 60 per minute per client IP. See QR Codes.
Rotation destinations
The item shape of rotation in create_short_link and update_link. The schema allows no other properties; the server ignores extra ones rather than rejecting them.
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | No | Stable identifier for this destination, recorded as the click variant on each click. Omit it on a brand new destination and Flyn mints one. When editing, send every existing destination back with the id it already has: click history is attributed by id, so a changed id detaches that destination's past clicks and a reused id merges two destinations' stats. Never derive ids from list position. |
| url | string | Yes | Destination URL for this entry. Scanned with Google Safe Browsing like the primary destination. |
| weight | number | Yes | Relative share of traffic, NOT a percentage. Weights are normalised across the list, so [3, 1] sends roughly three quarters of clicks to the first entry and equal weights give an even split. A weight of 0 keeps the destination in the list without ever serving it. Minimum 0. |
| label | string | No | Optional human-readable name for this destination. Cosmetic, it does not affect routing. |
update_link's rotation description ends with this text, and create_short_link's has it just before its closing Safe Browsing sentence:
Up to 12 destinations; sending more is rejected rather than truncated, so nothing is dropped without telling you. Each human visitor is sent to one destination drawn at random in proportion to its weight, so the split settles over many clicks rather than following a strict order. Bots and crawlers always get the primary url, which stays required. Each click records the id of the destination that served it, and that id, not the url and not the list position, is what per-destination stats are keyed on. The Flyn dashboard does report them: the link's own analytics page has a "Destination Performance" panel listing every destination with its click count, its actual share of clicks, and the share its weight intended, and it keeps showing a destination you later remove from the rotation. No API or MCP call returns that breakdown, or the per-click destination id, so send the user to that page rather than inventing a tool call for it. The panel counts clicks only, across the clicks that carry a destination id: no conversion or revenue split, and no winner declared.
Tool results
- On success,
contentholds one text block with the REST response body as pretty-printed JSON. - The two QR tools, in PNG format (the default), return an
imageblock (base64) plus a text block summarising the code; the base64 is not repeated in the text. Withformat: "svg"there is no image block and the markup is in the text block, unless it is over 8,000 characters, in which case a note says to request PNG or call the REST route. - A failed call is still a JSON-RPC result, with
isError: trueand the API's error message as text, so the model can read it and correct itself. Plan errors add a link to pricing, 401s add where to create an API key, and 429s say the limit is temporary and to wait about a minute.
curl -X POST https://www.flyn.to/mcp \
-H "Authorization: Bearer flyn_sk_live_..." \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2025-06-18" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "create_short_link",
"arguments": { "url": "https://example.com/spring-sale", "slug": "spring-sale" }
}
}'{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [
{ "type": "text", "text": "<the POST /api/links response body, as pretty-printed JSON>" }
]
}
}{
"jsonrpc": "2.0",
"id": 3,
"result": {
"isError": true,
"content": [
{
"type": "text",
"text": "Password protection is only available on Pro or Lifetime. Upgrade at /pricing. This is a Flyn Pro feature: https://www.flyn.to/pricing"
}
]
}
}Errors and limits
What the endpoint itself returns, before any tool runs. Oversized bodies are refused before authentication, so an anonymous request cannot make the server parse more than 256 KB declared, or 262,144 characters on a chunked body.
| HTTP | JSON-RPC | When |
|---|---|---|
| 400 | -32600 | The MCP-Protocol-Version header names a version other than 2025-06-18 or 2025-03-26. |
| 400 | -32700 | The body could not be read, or is not valid JSON. |
| 400 | -32600 | An empty batch array. |
| 401 | none | A method that needs credentials was called without a valid API key or OAuth token. The body names both ways to authenticate, and the WWW-Authenticate header points to the protected-resource metadata. |
| 405 | none | GET or DELETE. The server is stateless and opens no event streams. (A GET that asks for text/html gets a short description page instead.) |
| 413 | -32600 | Body larger than 256 KB (262,144 bytes declared, or 262,144 characters on a chunked body), or a batch of more than 20 messages. |
| 429 | none | More than 120 requests a minute from one IP address. A Retry-After header gives the seconds to wait. |
| 200 | -32600 | An authenticated message (or one naming a public method) that is not a JSON object, or lacks jsonrpc "2.0" or a string method. The same message without an id is treated as a notification and gets 202 with no body. |
| 200 | -32601 | Unknown method. |
| 200 | -32602 | tools/call without a string name, or with a tool name that does not exist. |
Each tool is also limited by its own REST route, per client IP, as listed under each tool above. See Rate Limits and Errors for those routes.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://www.flyn.to/.well-known/oauth-protected-resource/mcp"
{
"error": "unauthorized",
"message": "The Flyn MCP server needs credentials, and there are two ways to give them. Easiest: if your client supports OAuth, add https://www.flyn.to/mcp with no key and it will open a Flyn sign-in page. That works on any plan, the free one included. Otherwise send \"Authorization: Bearer flyn_sk_live_...\" with every request; API keys are created at https://www.flyn.to/settings?tab=api and require a paid plan (Pro, Lifetime or Team). Setup guide: https://www.flyn.to/ai"
}Related
- Connect an AI assistant with MCP: the step-by-step setup for each client.
- Flyn for AI assistants: what an assistant can do with Flyn.
- OpenAPI Specification: the REST API the tools run on.
Was this page helpful? Spotted something wrong?