Build on it
MCP server
Connect Claude Code, Claude Desktop, Cursor or VS Code to your DL2App links through the hosted Model Context Protocol server.
DL2App hosts a Model Context Protocol server at /api/mcp on the dashboard’s host. It lets a coding agent or assistant list, create, change and archive links and read analytics, with the same keys, scopes, limits and errors as the REST API.
Connect
Authentication is a secret key in a header: Authorization: Bearer sk_live_…. Create the key under API keys in a project. Its scopes decide what the assistant can do: a key without links:write can only read.
Claude Code
Run in a terminal. Add --scope user to use it in every project.
claude mcp add --transport http dl2 https://portal.dl2.app/api/mcp \
--header "Authorization: Bearer sk_live_…"Claude Desktop
Settings → Developer → Edit Config (claude_desktop_config.json), then restart Claude Desktop.
{
"mcpServers": {
"dl2": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://portal.dl2.app/api/mcp",
"--header",
"Authorization:${DL2_AUTH}"
],
"env": {
"DL2_AUTH": "Bearer sk_live_…"
}
}
}
}Claude Desktop's built-in connectors sign in with OAuth, which DL2App doesn't have yet, so this uses the mcp-remote bridge (needs Node.js) to send the key as a header.
Cursor
.cursor/mcp.json in your project, or ~/.cursor/mcp.json for every project.
{
"mcpServers": {
"dl2": {
"url": "https://portal.dl2.app/api/mcp",
"headers": {
"Authorization": "Bearer sk_live_…"
}
}
}
}VS Code
.vscode/mcp.json in your workspace. VS Code asks for the key once and stores it.
{
"servers": {
"dl2": {
"type": "http",
"url": "https://portal.dl2.app/api/mcp",
"headers": {
"Authorization": "Bearer ${input:dl2-key}"
}
}
},
"inputs": [
{
"type": "promptString",
"id": "dl2-key",
"description": "DL2App secret key (sk_live_…)",
"password": true
}
]
}The command-line tool prints the same configuration with your key filled in: dl2 mcp config --client cursor.
Transport
Streamable HTTP, request and response only: POST /api/mcp with one JSON-RPC 2.0 message, or a batch, returns one JSON answer. There is no event stream (GET answers 405) and no session, so any request can stand alone. Protocol versions: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05.
Tools
There is one tool for each REST endpoint, generated from the same list as the API reference, plus a few shortcuts for assistants.
| Tool | Scope | Kind | What it does |
|---|---|---|---|
list_projects | links:read | read | Projects the key can see, newest first. |
get_project | links:read | read | One project with its link hosts, a summary of its iOS and Android apps, and how many links it has. |
list_links | links:read | read | A project's links, newest first. |
create_link | links:write | write | Creates one link. |
create_links_bulk | links:write | write | Creates up to 1,000 links in one request. |
get_link | links:read | read | One link by id. |
update_link | links:write | write, destructive | Changes only the fields you send; the rest stay as they are. |
archive_link | links:write | write, destructive | Archives the link: it stops redirecting and disappears from lists, and its click history is kept. |
get_link_stats | analytics:read | read | Totals, a daily series and breakdowns for one link. |
get_project_stats | analytics:read | read | The same figures across the whole project, plus its ten most-clicked links and the clicks on paths that aren't links (passthrough). |
get_project_health | links:read | read | The most recent result of each check on each of the project's hosts. |
create_campaign_links | links:write | write | Creates one link per channel for a campaign: the same destination each time, a different set of UTM tags. |
find_links | links:read | read | Searches a project's links with everyday filters; every filter given must match. |
explain_link | links:read | read | Says where a link sends each kind of visitor (iPhone, iPad, Android, in-app browsers, desktop, link previews), after applying the project's defaults and which apps are set up. |
qr_code | links:read | read | Returns the QR code for a link as SVG, drawn with the design saved for it in the dashboard (or the default look). |
Safety
- Dry runs. Every write tool takes
dry_run. With it, nothing is written and the result says what would change: the links that would be created, a field-by-field diff for an update, or the link and its recent clicks for an archive.create_links_bulk,archive_link,create_campaign_linksare dry runs unless you senddry_run: false. - Scopes. Write tools need
links:write, dry run or not. Stats tools needanalytics:read. - Annotations. Tools carry
readOnlyHint,destructiveHintandidempotentHint, so a client can ask before the destructive ones. - Retries. Write tools take
idempotency_key; a repeat within 24 hours returns the first result. - Small results. Lists return compact rows, 25 at a time, with
next_cursor. - Errors. A failed call has
isError: trueand the API’s error code, a message and a suggested next step.
Resources and prompts
dl2://projects: the projects the key can use.dl2://projects/{project_id}/settings: one project’s hosts, apps, default routing, deferred deep linking and link-preview defaults.- Prompt
launch_campaign: Plans and creates a set of tracked links for one campaign, with a dry run first. - Prompt
audit_broken_links: Checks a project's host health and looks for links that are disabled, expired, flagged or pointing nowhere useful. - Prompt
weekly_link_report: Summarises the last seven days of clicks, app opens and installs for a project.