@ulinkly/mcp-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -0
- package/dist/auth/api-key.d.ts +1 -0
- package/dist/auth/api-key.js +4 -0
- package/dist/auth/api-key.js.map +1 -0
- package/dist/auth/oauth.d.ts +8 -0
- package/dist/auth/oauth.js +168 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/client/ulink-api.d.ts +5 -0
- package/dist/client/ulink-api.js +84 -0
- package/dist/client/ulink-api.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/account.d.ts +2 -0
- package/dist/tools/account.js +67 -0
- package/dist/tools/account.js.map +1 -0
- package/dist/tools/api-keys.d.ts +2 -0
- package/dist/tools/api-keys.js +82 -0
- package/dist/tools/api-keys.js.map +1 -0
- package/dist/tools/domains.d.ts +2 -0
- package/dist/tools/domains.js +105 -0
- package/dist/tools/domains.js.map +1 -0
- package/dist/tools/links.d.ts +2 -0
- package/dist/tools/links.js +263 -0
- package/dist/tools/links.js.map +1 -0
- package/dist/tools/projects.d.ts +2 -0
- package/dist/tools/projects.js +164 -0
- package/dist/tools/projects.js.map +1 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# @ulinkly/mcp-server
|
|
2
|
+
|
|
3
|
+
Model Context Protocol (MCP) server for [ULink](https://ulink.ly) -- manage deep links, domains, and projects from AI coding tools like Claude Code and Cursor.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
Add the following to your MCP client configuration:
|
|
8
|
+
|
|
9
|
+
**Claude Code** (`~/.claude/claude_desktop_config.json`):
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"ulink": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": ["-y", "@ulinkly/mcp-server"]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Cursor** (`.cursor/mcp.json`):
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"mcpServers": {
|
|
27
|
+
"ulink": {
|
|
28
|
+
"command": "npx",
|
|
29
|
+
"args": ["-y", "@ulinkly/mcp-server"]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
On first use the server opens your browser for authentication. After you log in, the session persists and tokens refresh automatically.
|
|
36
|
+
|
|
37
|
+
## Authentication
|
|
38
|
+
|
|
39
|
+
### OAuth (default)
|
|
40
|
+
|
|
41
|
+
No configuration needed. The server launches a browser-based login flow using Supabase Auth with PKCE. Access tokens refresh automatically before they expire. If a refresh fails, the browser flow is re-triggered.
|
|
42
|
+
|
|
43
|
+
### API Key
|
|
44
|
+
|
|
45
|
+
Set the `ULINK_API_KEY` environment variable to skip the browser flow entirely. This is useful for CI environments or headless servers.
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"mcpServers": {
|
|
50
|
+
"ulink": {
|
|
51
|
+
"command": "npx",
|
|
52
|
+
"args": ["-y", "@ulinkly/mcp-server"],
|
|
53
|
+
"env": {
|
|
54
|
+
"ULINK_API_KEY": "your-api-key-here"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
You can generate an API key from the ULink dashboard under **Project Settings > API Keys**, or by using the `create_api_key` tool.
|
|
62
|
+
|
|
63
|
+
## Tools
|
|
64
|
+
|
|
65
|
+
The server exposes 21 tools organized into five categories.
|
|
66
|
+
|
|
67
|
+
### Projects (5 tools)
|
|
68
|
+
|
|
69
|
+
| Tool | Description |
|
|
70
|
+
|------|-------------|
|
|
71
|
+
| `list_projects` | List all projects owned by or shared with the authenticated user |
|
|
72
|
+
| `get_project` | Get detailed information about a specific project |
|
|
73
|
+
| `create_project` | Create a new project with a name and default fallback URL |
|
|
74
|
+
| `update_project` | Update the name or default URL of an existing project |
|
|
75
|
+
| `configure_project` | Set platform-specific config (iOS bundle ID, Android package, deeplink schemas, SHA-256 fingerprints) |
|
|
76
|
+
|
|
77
|
+
### Links (6 tools)
|
|
78
|
+
|
|
79
|
+
| Tool | Description |
|
|
80
|
+
|------|-------------|
|
|
81
|
+
| `create_link` | Create a unified or dynamic smart link with platform-specific URLs and parameters |
|
|
82
|
+
| `list_links` | List all links in a project with pagination |
|
|
83
|
+
| `get_link` | Get detailed information about a specific link |
|
|
84
|
+
| `update_link` | Update a link's URLs, parameters, or metadata |
|
|
85
|
+
| `delete_link` | Permanently delete a link |
|
|
86
|
+
| `get_link_analytics` | Get click analytics for a link (total clicks, platform/country/referrer breakdowns) |
|
|
87
|
+
|
|
88
|
+
### Domains (4 tools)
|
|
89
|
+
|
|
90
|
+
| Tool | Description |
|
|
91
|
+
|------|-------------|
|
|
92
|
+
| `list_domains` | List all domains (shared and custom) associated with a project |
|
|
93
|
+
| `add_domain` | Add a custom domain to a project |
|
|
94
|
+
| `verify_domain` | Trigger DNS verification for a custom domain |
|
|
95
|
+
| `delete_domain` | Remove a custom domain from a project |
|
|
96
|
+
|
|
97
|
+
### API Keys (3 tools)
|
|
98
|
+
|
|
99
|
+
| Tool | Description |
|
|
100
|
+
|------|-------------|
|
|
101
|
+
| `list_api_keys` | List all API keys for a project (metadata only, not the key value) |
|
|
102
|
+
| `create_api_key` | Create a new API key (the full key is only shown once) |
|
|
103
|
+
| `revoke_api_key` | Permanently revoke an API key |
|
|
104
|
+
|
|
105
|
+
### Account (3 tools)
|
|
106
|
+
|
|
107
|
+
| Tool | Description |
|
|
108
|
+
|------|-------------|
|
|
109
|
+
| `get_subscription` | Get the current subscription plan, status, and renewal date |
|
|
110
|
+
| `list_plans` | List all available subscription plans with pricing and limits |
|
|
111
|
+
| `get_usage` | Get usage statistics for the current billing period (clicks, links, API calls) |
|
|
112
|
+
|
|
113
|
+
## Environment Variables
|
|
114
|
+
|
|
115
|
+
| Variable | Default | Description |
|
|
116
|
+
|----------|---------|-------------|
|
|
117
|
+
| `ULINK_API_KEY` | -- | API key for authentication (skips browser OAuth flow) |
|
|
118
|
+
| `ULINK_API_URL` | `https://api.ulink.ly` | Base URL for the ULink REST API |
|
|
119
|
+
| `ULINK_FRONTEND_URL` | `https://ulink.ly` | Frontend URL used for the OAuth login flow |
|
|
120
|
+
| `ULINK_SUPABASE_ANON_KEY` | -- | Supabase anon key for silent token refresh (without it, expired tokens trigger re-authentication via browser) |
|
|
121
|
+
|
|
122
|
+
## Requirements
|
|
123
|
+
|
|
124
|
+
- Node.js 18 or later
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getApiKey(): string | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-key.js","sourceRoot":"","sources":["../../src/auth/api-key.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,SAAS;IACvB,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface OAuthTokens {
|
|
2
|
+
accessToken: string;
|
|
3
|
+
refreshToken: string;
|
|
4
|
+
expiresAt: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function openBrowser(url: string): void;
|
|
7
|
+
export declare function browserOAuthFlow(): Promise<OAuthTokens>;
|
|
8
|
+
export declare function refreshAccessToken(refreshToken: string): Promise<OAuthTokens>;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { randomUUID, randomBytes, createHash } from "node:crypto";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { URL } from "node:url";
|
|
5
|
+
const FRONTEND_URL = process.env.ULINK_FRONTEND_URL ?? "https://ulink.ly";
|
|
6
|
+
const SUPABASE_URL = process.env.ULINK_SUPABASE_URL ?? "https://cjgihassfsspxivjtgoi.supabase.co";
|
|
7
|
+
const TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Helpers
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
function base64url(buf) {
|
|
12
|
+
return buf.toString("base64url");
|
|
13
|
+
}
|
|
14
|
+
function generateCodeVerifier() {
|
|
15
|
+
return base64url(randomBytes(32));
|
|
16
|
+
}
|
|
17
|
+
function generateCodeChallenge(verifier) {
|
|
18
|
+
return base64url(createHash("sha256").update(verifier).digest());
|
|
19
|
+
}
|
|
20
|
+
function successHtml() {
|
|
21
|
+
return `<!DOCTYPE html>
|
|
22
|
+
<html><head><meta charset="utf-8"><title>ULink CLI</title>
|
|
23
|
+
<style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#fafafa}
|
|
24
|
+
.card{text-align:center;padding:2rem;border-radius:12px;background:#fff;box-shadow:0 2px 8px rgba(0,0,0,.08)}
|
|
25
|
+
h1{color:#22c55e;margin:0 0 .5rem}p{color:#555}</style></head>
|
|
26
|
+
<body><div class="card"><h1>Authenticated</h1><p>You can close this window and return to your terminal.</p></div></body></html>`;
|
|
27
|
+
}
|
|
28
|
+
function errorHtml(message) {
|
|
29
|
+
return `<!DOCTYPE html>
|
|
30
|
+
<html><head><meta charset="utf-8"><title>ULink CLI — Error</title>
|
|
31
|
+
<style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#fafafa}
|
|
32
|
+
.card{text-align:center;padding:2rem;border-radius:12px;background:#fff;box-shadow:0 2px 8px rgba(0,0,0,.08)}
|
|
33
|
+
h1{color:#ef4444;margin:0 0 .5rem}p{color:#555}</style></head>
|
|
34
|
+
<body><div class="card"><h1>Authentication Error</h1><p>${message}</p></div></body></html>`;
|
|
35
|
+
}
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Browser opener
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
export function openBrowser(url) {
|
|
40
|
+
const platform = process.platform;
|
|
41
|
+
try {
|
|
42
|
+
if (platform === "darwin") {
|
|
43
|
+
execSync(`open "${url}"`);
|
|
44
|
+
}
|
|
45
|
+
else if (platform === "win32") {
|
|
46
|
+
execSync(`start "" "${url}"`);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Linux / other
|
|
50
|
+
execSync(`xdg-open "${url}"`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
console.error(`Could not open browser. Please visit:\n${url}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Browser OAuth PKCE flow
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
export function browserOAuthFlow() {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
const sessionId = randomUUID();
|
|
63
|
+
const codeVerifier = generateCodeVerifier();
|
|
64
|
+
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
65
|
+
let settled = false;
|
|
66
|
+
const server = createServer((req, res) => {
|
|
67
|
+
if (!req.url) {
|
|
68
|
+
res.writeHead(400);
|
|
69
|
+
res.end();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const parsed = new URL(req.url, `http://127.0.0.1`);
|
|
73
|
+
if (parsed.pathname !== "/callback") {
|
|
74
|
+
res.writeHead(404);
|
|
75
|
+
res.end("Not found");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const error = parsed.searchParams.get("error");
|
|
79
|
+
if (error) {
|
|
80
|
+
const desc = parsed.searchParams.get("error_description") ?? "Unknown error";
|
|
81
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
82
|
+
res.end(errorHtml(desc));
|
|
83
|
+
settled = true;
|
|
84
|
+
server.close();
|
|
85
|
+
reject(new Error(`OAuth error: ${error} — ${desc}`));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const accessToken = parsed.searchParams.get("access_token");
|
|
89
|
+
const refreshToken = parsed.searchParams.get("refresh_token");
|
|
90
|
+
const expiresIn = parsed.searchParams.get("expires_in");
|
|
91
|
+
if (!accessToken || !refreshToken || !expiresIn) {
|
|
92
|
+
const msg = "Missing token parameters in callback";
|
|
93
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
94
|
+
res.end(errorHtml(msg));
|
|
95
|
+
settled = true;
|
|
96
|
+
server.close();
|
|
97
|
+
reject(new Error(msg));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
101
|
+
res.end(successHtml());
|
|
102
|
+
const tokens = {
|
|
103
|
+
accessToken,
|
|
104
|
+
refreshToken,
|
|
105
|
+
expiresAt: Date.now() + parseInt(expiresIn, 10) * 1000,
|
|
106
|
+
};
|
|
107
|
+
settled = true;
|
|
108
|
+
server.close();
|
|
109
|
+
resolve(tokens);
|
|
110
|
+
});
|
|
111
|
+
// Listen on a random available port on loopback
|
|
112
|
+
server.listen(0, "127.0.0.1", () => {
|
|
113
|
+
const addr = server.address();
|
|
114
|
+
if (!addr || typeof addr === "string") {
|
|
115
|
+
reject(new Error("Failed to bind local server"));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const port = addr.port;
|
|
119
|
+
const authUrl = `${FRONTEND_URL}/auth/cli` +
|
|
120
|
+
`?session=${encodeURIComponent(sessionId)}` +
|
|
121
|
+
`&code_challenge=${encodeURIComponent(codeChallenge)}` +
|
|
122
|
+
`&code_challenge_method=S256` +
|
|
123
|
+
`&callback_port=${port}` +
|
|
124
|
+
`&source=mcp`;
|
|
125
|
+
console.error(`Opening browser for authentication...\n${authUrl}`);
|
|
126
|
+
openBrowser(authUrl);
|
|
127
|
+
});
|
|
128
|
+
// 5-minute timeout
|
|
129
|
+
const timer = setTimeout(() => {
|
|
130
|
+
if (!settled) {
|
|
131
|
+
settled = true;
|
|
132
|
+
server.close();
|
|
133
|
+
reject(new Error("OAuth flow timed out after 5 minutes"));
|
|
134
|
+
}
|
|
135
|
+
}, TIMEOUT_MS);
|
|
136
|
+
// Don't let the timer keep the process alive if the server closes first
|
|
137
|
+
timer.unref();
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Token refresh
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
export async function refreshAccessToken(refreshToken) {
|
|
144
|
+
const anonKey = process.env.ULINK_SUPABASE_ANON_KEY;
|
|
145
|
+
if (!anonKey) {
|
|
146
|
+
throw new Error("ULINK_SUPABASE_ANON_KEY not set — cannot refresh token silently. Re-authenticating via browser.");
|
|
147
|
+
}
|
|
148
|
+
const url = `${SUPABASE_URL}/auth/v1/token?grant_type=refresh_token`;
|
|
149
|
+
const res = await fetch(url, {
|
|
150
|
+
method: "POST",
|
|
151
|
+
headers: {
|
|
152
|
+
"Content-Type": "application/json",
|
|
153
|
+
apikey: anonKey,
|
|
154
|
+
},
|
|
155
|
+
body: JSON.stringify({ refresh_token: refreshToken }),
|
|
156
|
+
});
|
|
157
|
+
if (!res.ok) {
|
|
158
|
+
const body = await res.text();
|
|
159
|
+
throw new Error(`Token refresh failed (${res.status}): ${body}`);
|
|
160
|
+
}
|
|
161
|
+
const data = (await res.json());
|
|
162
|
+
return {
|
|
163
|
+
accessToken: data.access_token,
|
|
164
|
+
refreshToken: data.refresh_token,
|
|
165
|
+
expiresAt: Date.now() + data.expires_in * 1000,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { getApiKey } from "../auth/api-key.js";
|
|
2
|
+
import { browserOAuthFlow, refreshAccessToken, } from "../auth/oauth.js";
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Configuration
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
const API_BASE = process.env.ULINK_API_URL ?? "https://api.ulink.ly";
|
|
7
|
+
const TOKEN_REFRESH_BUFFER_MS = 30 * 1000; // refresh 30 s before expiry
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Error type
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
export class ApiError extends Error {
|
|
12
|
+
status;
|
|
13
|
+
constructor(status, message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.status = status;
|
|
16
|
+
this.name = "ApiError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Token state
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
let oauthTokens;
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Auth helper
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
async function ensureAuth() {
|
|
27
|
+
// 1. API-key takes precedence
|
|
28
|
+
const apiKey = getApiKey();
|
|
29
|
+
if (apiKey) {
|
|
30
|
+
return { header: "x-app-key", value: apiKey };
|
|
31
|
+
}
|
|
32
|
+
// 2. OAuth — first call triggers browser flow
|
|
33
|
+
if (!oauthTokens) {
|
|
34
|
+
oauthTokens = await browserOAuthFlow();
|
|
35
|
+
}
|
|
36
|
+
// 3. Auto-refresh if token expires within 30 s
|
|
37
|
+
if (oauthTokens.expiresAt - Date.now() < TOKEN_REFRESH_BUFFER_MS) {
|
|
38
|
+
try {
|
|
39
|
+
oauthTokens = await refreshAccessToken(oauthTokens.refreshToken);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Refresh failed — re-authenticate via browser
|
|
43
|
+
console.error("Token refresh failed, re-authenticating...");
|
|
44
|
+
oauthTokens = await browserOAuthFlow();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { header: "Authorization", value: `Bearer ${oauthTokens.accessToken}` };
|
|
48
|
+
}
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Generic API request
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
export async function apiRequest(method, path, body) {
|
|
53
|
+
const auth = await ensureAuth();
|
|
54
|
+
const headers = {
|
|
55
|
+
[auth.header]: auth.value,
|
|
56
|
+
};
|
|
57
|
+
if (body !== undefined) {
|
|
58
|
+
headers["Content-Type"] = "application/json";
|
|
59
|
+
}
|
|
60
|
+
const res = await fetch(`${API_BASE}${path}`, {
|
|
61
|
+
method,
|
|
62
|
+
headers,
|
|
63
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
64
|
+
});
|
|
65
|
+
// 204 No Content
|
|
66
|
+
if (res.status === 204) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
if (!res.ok) {
|
|
70
|
+
let message = `API request failed: ${res.status}`;
|
|
71
|
+
try {
|
|
72
|
+
const errorBody = (await res.json());
|
|
73
|
+
if (errorBody.message) {
|
|
74
|
+
message = errorBody.message;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// ignore JSON parse failure — use default message
|
|
79
|
+
}
|
|
80
|
+
throw new ApiError(res.status, message);
|
|
81
|
+
}
|
|
82
|
+
return (await res.json());
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=ulink-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ulink-api.js","sourceRoot":"","sources":["../../src/client/ulink-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EACL,gBAAgB,EAChB,kBAAkB,GAEnB,MAAM,kBAAkB,CAAC;AAE1B,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,sBAAsB,CAAC;AAErE,MAAM,uBAAuB,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,6BAA6B;AAExE,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEf;IADlB,YACkB,MAAc,EAC9B,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,WAAM,GAAN,MAAM,CAAQ;QAI9B,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,IAAI,WAAoC,CAAC;AAEzC,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,KAAK,UAAU,UAAU;IACvB,8BAA8B;IAC9B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChD,CAAC;IAED,8CAA8C;IAC9C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,WAAW,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACzC,CAAC;IAED,+CAA+C;IAC/C,IAAI,WAAW,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,uBAAuB,EAAE,CAAC;QACjE,IAAI,CAAC;YACH,WAAW,GAAG,MAAM,kBAAkB,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;QACnE,CAAC;QAAC,MAAM,CAAC;YACP,+CAA+C;YAC/C,OAAO,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAC5D,WAAW,GAAG,MAAM,gBAAgB,EAAE,CAAC;QACzC,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,WAAW,CAAC,WAAW,EAAE,EAAE,CAAC;AACjF,CAAC;AAED,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAc,EACd,IAAY,EACZ,IAAc;IAEd,MAAM,IAAI,GAAG,MAAM,UAAU,EAAE,CAAC;IAEhC,MAAM,OAAO,GAA2B;QACtC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,KAAK;KAC1B,CAAC;IAEF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;IAC/C,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,GAAG,IAAI,EAAE,EAAE;QAC5C,MAAM;QACN,OAAO;QACP,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;KAC5D,CAAC,CAAC;IAEH,iBAAiB;IACjB,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACvB,OAAO,SAAc,CAAC;IACxB,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,IAAI,OAAO,GAAG,uBAAuB,GAAG,CAAC,MAAM,EAAE,CAAC;QAClD,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAyB,CAAC;YAC7D,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;gBACtB,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;YAC9B,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,kDAAkD;QACpD,CAAC;QACD,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAM,CAAC;AACjC,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { registerProjectTools } from "./tools/projects.js";
|
|
5
|
+
import { registerLinkTools } from "./tools/links.js";
|
|
6
|
+
import { registerDomainTools } from "./tools/domains.js";
|
|
7
|
+
import { registerApiKeyTools } from "./tools/api-keys.js";
|
|
8
|
+
import { registerAccountTools } from "./tools/account.js";
|
|
9
|
+
const server = new McpServer({
|
|
10
|
+
name: "ulink",
|
|
11
|
+
version: "0.1.0",
|
|
12
|
+
});
|
|
13
|
+
registerProjectTools(server);
|
|
14
|
+
registerLinkTools(server);
|
|
15
|
+
registerDomainTools(server);
|
|
16
|
+
registerApiKeyTools(server);
|
|
17
|
+
registerAccountTools(server);
|
|
18
|
+
async function main() {
|
|
19
|
+
const transport = new StdioServerTransport();
|
|
20
|
+
await server.connect(transport);
|
|
21
|
+
console.error("ULink MCP Server running on stdio (21 tools registered)");
|
|
22
|
+
}
|
|
23
|
+
main().catch((error) => {
|
|
24
|
+
console.error("Fatal error:", error);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
});
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,OAAO;IACb,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAC7B,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAC1B,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAC5B,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAC5B,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAE7B,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,yDAAyD,CAAC,CAAC;AAC3E,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { apiRequest } from "../client/ulink-api.js";
|
|
2
|
+
export function registerAccountTools(server) {
|
|
3
|
+
// -----------------------------------------------------------------------
|
|
4
|
+
// get_subscription
|
|
5
|
+
// -----------------------------------------------------------------------
|
|
6
|
+
server.registerTool("get_subscription", {
|
|
7
|
+
title: "Get Subscription",
|
|
8
|
+
description: "Retrieve the current user's active subscription details, including plan name, billing period, status, and renewal date.",
|
|
9
|
+
annotations: { readOnlyHint: true },
|
|
10
|
+
}, async () => {
|
|
11
|
+
try {
|
|
12
|
+
const data = await apiRequest("GET", "/subscriptions/current");
|
|
13
|
+
return {
|
|
14
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
return {
|
|
19
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
20
|
+
isError: true,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
// -----------------------------------------------------------------------
|
|
25
|
+
// list_plans
|
|
26
|
+
// -----------------------------------------------------------------------
|
|
27
|
+
server.registerTool("list_plans", {
|
|
28
|
+
title: "List Plans",
|
|
29
|
+
description: "List all available ULink subscription plans with their features, limits, and pricing. Useful for comparing plans or determining upgrade options.",
|
|
30
|
+
annotations: { readOnlyHint: true },
|
|
31
|
+
}, async () => {
|
|
32
|
+
try {
|
|
33
|
+
const data = await apiRequest("GET", "/subscriptions/plans");
|
|
34
|
+
return {
|
|
35
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
// -----------------------------------------------------------------------
|
|
46
|
+
// get_usage
|
|
47
|
+
// -----------------------------------------------------------------------
|
|
48
|
+
server.registerTool("get_usage", {
|
|
49
|
+
title: "Get Usage",
|
|
50
|
+
description: "Retrieve the current user's usage statistics for the active billing period, including link clicks, links created, and API calls against plan limits.",
|
|
51
|
+
annotations: { readOnlyHint: true },
|
|
52
|
+
}, async () => {
|
|
53
|
+
try {
|
|
54
|
+
const data = await apiRequest("GET", "/subscriptions/me/usage");
|
|
55
|
+
return {
|
|
56
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
return {
|
|
61
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
62
|
+
isError: true,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=account.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account.js","sourceRoot":"","sources":["../../src/tools/account.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,MAAM,UAAU,oBAAoB,CAAC,MAAiB;IACpD,0EAA0E;IAC1E,mBAAmB;IACnB,0EAA0E;IAC1E,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EACT,yHAAyH;QAC3H,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;KACpC,EACD,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,wBAAwB,CAAC,CAAC;YAC/D,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aACjE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAW,GAAW,CAAC,OAAO,EAAE,EAAE,CAAC;gBACnE,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;IAEF,0EAA0E;IAC1E,aAAa;IACb,0EAA0E;IAC1E,MAAM,CAAC,YAAY,CACjB,YAAY,EACZ;QACE,KAAK,EAAE,YAAY;QACnB,WAAW,EACT,kJAAkJ;QACpJ,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;KACpC,EACD,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,sBAAsB,CAAC,CAAC;YAC7D,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aACjE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAW,GAAW,CAAC,OAAO,EAAE,EAAE,CAAC;gBACnE,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;IAEF,0EAA0E;IAC1E,YAAY;IACZ,0EAA0E;IAC1E,MAAM,CAAC,YAAY,CACjB,WAAW,EACX;QACE,KAAK,EAAE,WAAW;QAClB,WAAW,EACT,sJAAsJ;QACxJ,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;KACpC,EACD,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,yBAAyB,CAAC,CAAC;YAChE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aACjE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAW,GAAW,CAAC,OAAO,EAAE,EAAE,CAAC;gBACnE,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { apiRequest } from "../client/ulink-api.js";
|
|
3
|
+
export function registerApiKeyTools(server) {
|
|
4
|
+
// -----------------------------------------------------------------------
|
|
5
|
+
// list_api_keys
|
|
6
|
+
// -----------------------------------------------------------------------
|
|
7
|
+
server.registerTool("list_api_keys", {
|
|
8
|
+
title: "List API Keys",
|
|
9
|
+
description: "List all API keys for a ULink project. API keys are used for server-side and SDK authentication. Returns key metadata (name, prefix, creation date) but never the full key value.",
|
|
10
|
+
annotations: { readOnlyHint: true },
|
|
11
|
+
inputSchema: {
|
|
12
|
+
projectId: z.string().uuid().describe("The project whose API keys to list"),
|
|
13
|
+
},
|
|
14
|
+
}, async ({ projectId }) => {
|
|
15
|
+
try {
|
|
16
|
+
const data = await apiRequest("GET", `/api-keys?projectId=${projectId}`);
|
|
17
|
+
return {
|
|
18
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
24
|
+
isError: true,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
// -----------------------------------------------------------------------
|
|
29
|
+
// create_api_key
|
|
30
|
+
// -----------------------------------------------------------------------
|
|
31
|
+
server.registerTool("create_api_key", {
|
|
32
|
+
title: "Create API Key",
|
|
33
|
+
description: "Create a new API key for a ULink project. The full key value is only returned once in the response and cannot be retrieved again. Store it securely.",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
projectId: z.string().uuid().describe("The project to create the API key for"),
|
|
36
|
+
name: z.string().describe("A descriptive name for the API key (e.g. 'Production Server')"),
|
|
37
|
+
},
|
|
38
|
+
}, async ({ projectId, name }) => {
|
|
39
|
+
try {
|
|
40
|
+
const data = await apiRequest("POST", `/api-keys?projectId=${encodeURIComponent(projectId)}`, { name });
|
|
41
|
+
return {
|
|
42
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
48
|
+
isError: true,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
// -----------------------------------------------------------------------
|
|
53
|
+
// revoke_api_key
|
|
54
|
+
// -----------------------------------------------------------------------
|
|
55
|
+
server.registerTool("revoke_api_key", {
|
|
56
|
+
title: "Revoke API Key",
|
|
57
|
+
description: "Permanently revoke an API key. Any applications using this key will immediately lose access. This action cannot be undone.",
|
|
58
|
+
annotations: { destructiveHint: true },
|
|
59
|
+
inputSchema: {
|
|
60
|
+
apiKeyId: z.string().uuid().describe("The unique identifier of the API key to revoke"),
|
|
61
|
+
},
|
|
62
|
+
}, async ({ apiKeyId }) => {
|
|
63
|
+
try {
|
|
64
|
+
await apiRequest("DELETE", `/api-keys/${apiKeyId}`);
|
|
65
|
+
return {
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: "text",
|
|
69
|
+
text: `Successfully revoked API key ${apiKeyId}`,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
return {
|
|
76
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
77
|
+
isError: true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=api-keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-keys.js","sourceRoot":"","sources":["../../src/tools/api-keys.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { apiRequest } from "../client/ulink-api.js";
|
|
3
|
+
export function registerDomainTools(server) {
|
|
4
|
+
// -----------------------------------------------------------------------
|
|
5
|
+
// list_domains
|
|
6
|
+
// -----------------------------------------------------------------------
|
|
7
|
+
server.registerTool("list_domains", {
|
|
8
|
+
title: "List Domains",
|
|
9
|
+
description: "List all domains associated with a ULink project, including shared domains and any custom domains that have been added. Shows verification status for each domain.",
|
|
10
|
+
annotations: { readOnlyHint: true },
|
|
11
|
+
inputSchema: {
|
|
12
|
+
projectId: z.string().uuid().describe("The project whose domains to list"),
|
|
13
|
+
},
|
|
14
|
+
}, async ({ projectId }) => {
|
|
15
|
+
try {
|
|
16
|
+
const data = await apiRequest("GET", `/domains/projects/${projectId}`);
|
|
17
|
+
return {
|
|
18
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
24
|
+
isError: true,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
// -----------------------------------------------------------------------
|
|
29
|
+
// add_domain
|
|
30
|
+
// -----------------------------------------------------------------------
|
|
31
|
+
server.registerTool("add_domain", {
|
|
32
|
+
title: "Add Domain",
|
|
33
|
+
description: "Add a custom domain to a ULink project. After adding, you must configure DNS records and verify the domain before it can be used for links.",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
projectId: z.string().uuid().describe("The project to add the domain to"),
|
|
36
|
+
host: z.string().describe("The domain hostname to add (e.g. links.example.com)"),
|
|
37
|
+
},
|
|
38
|
+
}, async ({ projectId, host }) => {
|
|
39
|
+
try {
|
|
40
|
+
const data = await apiRequest("POST", "/domains", { projectId, host });
|
|
41
|
+
return {
|
|
42
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
48
|
+
isError: true,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
// -----------------------------------------------------------------------
|
|
53
|
+
// verify_domain
|
|
54
|
+
// -----------------------------------------------------------------------
|
|
55
|
+
server.registerTool("verify_domain", {
|
|
56
|
+
title: "Verify Domain",
|
|
57
|
+
description: "Trigger DNS verification for a custom domain. The domain's DNS records must be correctly configured before verification will succeed. Returns the current verification status.",
|
|
58
|
+
inputSchema: {
|
|
59
|
+
domainId: z.string().uuid().describe("The unique identifier of the domain to verify"),
|
|
60
|
+
},
|
|
61
|
+
}, async ({ domainId }) => {
|
|
62
|
+
try {
|
|
63
|
+
const data = await apiRequest("POST", `/domains/${domainId}/verify`);
|
|
64
|
+
return {
|
|
65
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
return {
|
|
70
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
71
|
+
isError: true,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
// -----------------------------------------------------------------------
|
|
76
|
+
// delete_domain
|
|
77
|
+
// -----------------------------------------------------------------------
|
|
78
|
+
server.registerTool("delete_domain", {
|
|
79
|
+
title: "Delete Domain",
|
|
80
|
+
description: "Remove a custom domain from a ULink project. Any links using this domain will stop working. This action is irreversible.",
|
|
81
|
+
annotations: { destructiveHint: true },
|
|
82
|
+
inputSchema: {
|
|
83
|
+
domainId: z.string().uuid().describe("The unique identifier of the domain to delete"),
|
|
84
|
+
},
|
|
85
|
+
}, async ({ domainId }) => {
|
|
86
|
+
try {
|
|
87
|
+
await apiRequest("DELETE", `/domains/${domainId}`);
|
|
88
|
+
return {
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: "text",
|
|
92
|
+
text: `Successfully deleted domain ${domainId}`,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
return {
|
|
99
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
100
|
+
isError: true,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=domains.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domains.js","sourceRoot":"","sources":["../../src/tools/domains.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { apiRequest } from "../client/ulink-api.js";
|
|
3
|
+
export function registerLinkTools(server) {
|
|
4
|
+
// -----------------------------------------------------------------------
|
|
5
|
+
// create_link
|
|
6
|
+
// -----------------------------------------------------------------------
|
|
7
|
+
server.registerTool("create_link", {
|
|
8
|
+
title: "Create Link",
|
|
9
|
+
description: "Create a new smart link in a ULink project. Supports unified links (single URL that routes by platform) and dynamic links (parameterised deep links). You must specify the project, domain, and link type. Optionally set platform-specific URLs, fallback URLs, custom slug, metadata, and parameters.",
|
|
10
|
+
inputSchema: {
|
|
11
|
+
projectId: z.string().uuid().describe("The project to create the link in"),
|
|
12
|
+
domainId: z.string().uuid().describe("The domain to host the link on"),
|
|
13
|
+
type: z
|
|
14
|
+
.enum(["unified", "dynamic"])
|
|
15
|
+
.describe("Link type: 'unified' for smart routing or 'dynamic' for parameterised deep links"),
|
|
16
|
+
slug: z.string().optional().describe("Custom slug for the short URL (auto-generated if omitted)"),
|
|
17
|
+
name: z.string().optional().describe("Human-readable name for the link"),
|
|
18
|
+
iosUrl: z.string().optional().describe("URL to open on iOS devices"),
|
|
19
|
+
androidUrl: z.string().optional().describe("URL to open on Android devices"),
|
|
20
|
+
fallbackUrl: z.string().optional().describe("Fallback URL for unsupported platforms"),
|
|
21
|
+
iosFallbackUrl: z
|
|
22
|
+
.string()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe("iOS-specific fallback URL (e.g. App Store link)"),
|
|
25
|
+
androidFallbackUrl: z
|
|
26
|
+
.string()
|
|
27
|
+
.optional()
|
|
28
|
+
.describe("Android-specific fallback URL (e.g. Play Store link)"),
|
|
29
|
+
parameters: z
|
|
30
|
+
.record(z.string())
|
|
31
|
+
.optional()
|
|
32
|
+
.describe("Key-value parameters passed through the deep link"),
|
|
33
|
+
metadata: z
|
|
34
|
+
.record(z.unknown())
|
|
35
|
+
.optional()
|
|
36
|
+
.describe("Arbitrary metadata attached to the link"),
|
|
37
|
+
},
|
|
38
|
+
}, async ({ projectId, domainId, type, slug, name, iosUrl, androidUrl, fallbackUrl, iosFallbackUrl, androidFallbackUrl, parameters, metadata }) => {
|
|
39
|
+
try {
|
|
40
|
+
const body = { type };
|
|
41
|
+
if (slug !== undefined)
|
|
42
|
+
body.slug = slug;
|
|
43
|
+
if (name !== undefined)
|
|
44
|
+
body.name = name;
|
|
45
|
+
if (iosUrl !== undefined)
|
|
46
|
+
body.iosUrl = iosUrl;
|
|
47
|
+
if (androidUrl !== undefined)
|
|
48
|
+
body.androidUrl = androidUrl;
|
|
49
|
+
if (fallbackUrl !== undefined)
|
|
50
|
+
body.fallbackUrl = fallbackUrl;
|
|
51
|
+
if (iosFallbackUrl !== undefined)
|
|
52
|
+
body.iosFallbackUrl = iosFallbackUrl;
|
|
53
|
+
if (androidFallbackUrl !== undefined)
|
|
54
|
+
body.androidFallbackUrl = androidFallbackUrl;
|
|
55
|
+
if (parameters !== undefined)
|
|
56
|
+
body.parameters = parameters;
|
|
57
|
+
if (metadata !== undefined)
|
|
58
|
+
body.metadata = metadata;
|
|
59
|
+
const qs = domainId ? `?domainId=${encodeURIComponent(domainId)}` : "";
|
|
60
|
+
const data = await apiRequest("POST", `/api/v1/projects/${projectId}/links${qs}`, body);
|
|
61
|
+
return {
|
|
62
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
return {
|
|
67
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
68
|
+
isError: true,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
// -----------------------------------------------------------------------
|
|
73
|
+
// list_links
|
|
74
|
+
// -----------------------------------------------------------------------
|
|
75
|
+
server.registerTool("list_links", {
|
|
76
|
+
title: "List Links",
|
|
77
|
+
description: "List all links in a ULink project with optional pagination. Returns an array of link objects with their configuration, URLs, and metadata.",
|
|
78
|
+
annotations: { readOnlyHint: true },
|
|
79
|
+
inputSchema: {
|
|
80
|
+
projectId: z.string().uuid().describe("The project whose links to list"),
|
|
81
|
+
offset: z
|
|
82
|
+
.number()
|
|
83
|
+
.int()
|
|
84
|
+
.min(0)
|
|
85
|
+
.optional()
|
|
86
|
+
.describe("Number of links to skip for pagination (starts at 0)"),
|
|
87
|
+
limit: z
|
|
88
|
+
.number()
|
|
89
|
+
.int()
|
|
90
|
+
.positive()
|
|
91
|
+
.max(100)
|
|
92
|
+
.optional()
|
|
93
|
+
.describe("Number of links to return (max 100)"),
|
|
94
|
+
},
|
|
95
|
+
}, async ({ projectId, offset, limit }) => {
|
|
96
|
+
try {
|
|
97
|
+
const params = new URLSearchParams();
|
|
98
|
+
if (offset !== undefined)
|
|
99
|
+
params.set("offset", String(offset));
|
|
100
|
+
if (limit !== undefined)
|
|
101
|
+
params.set("limit", String(limit));
|
|
102
|
+
const qs = params.toString();
|
|
103
|
+
const path = `/api/v1/projects/${projectId}/links${qs ? `?${qs}` : ""}`;
|
|
104
|
+
const data = await apiRequest("GET", path);
|
|
105
|
+
return {
|
|
106
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
return {
|
|
111
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
112
|
+
isError: true,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
// -----------------------------------------------------------------------
|
|
117
|
+
// get_link
|
|
118
|
+
// -----------------------------------------------------------------------
|
|
119
|
+
server.registerTool("get_link", {
|
|
120
|
+
title: "Get Link",
|
|
121
|
+
description: "Retrieve detailed information about a specific link by its ID, including all platform URLs, parameters, metadata, and current configuration.",
|
|
122
|
+
annotations: { readOnlyHint: true },
|
|
123
|
+
inputSchema: {
|
|
124
|
+
linkId: z.string().uuid().describe("The unique identifier of the link"),
|
|
125
|
+
},
|
|
126
|
+
}, async ({ linkId }) => {
|
|
127
|
+
try {
|
|
128
|
+
const data = await apiRequest("GET", `/api/v1/links/${linkId}`);
|
|
129
|
+
return {
|
|
130
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
return {
|
|
135
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
136
|
+
isError: true,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
// -----------------------------------------------------------------------
|
|
141
|
+
// update_link
|
|
142
|
+
// -----------------------------------------------------------------------
|
|
143
|
+
server.registerTool("update_link", {
|
|
144
|
+
title: "Update Link",
|
|
145
|
+
description: "Update an existing link's properties. You can change the name, platform-specific URLs, fallback URLs, parameters, and metadata. Only the fields you provide will be modified.",
|
|
146
|
+
inputSchema: {
|
|
147
|
+
linkId: z.string().uuid().describe("The unique identifier of the link to update"),
|
|
148
|
+
name: z.string().optional().describe("New human-readable name for the link"),
|
|
149
|
+
iosUrl: z.string().optional().describe("New URL to open on iOS devices"),
|
|
150
|
+
androidUrl: z.string().optional().describe("New URL to open on Android devices"),
|
|
151
|
+
fallbackUrl: z.string().optional().describe("New fallback URL for unsupported platforms"),
|
|
152
|
+
iosFallbackUrl: z
|
|
153
|
+
.string()
|
|
154
|
+
.optional()
|
|
155
|
+
.describe("New iOS-specific fallback URL"),
|
|
156
|
+
androidFallbackUrl: z
|
|
157
|
+
.string()
|
|
158
|
+
.optional()
|
|
159
|
+
.describe("New Android-specific fallback URL"),
|
|
160
|
+
parameters: z
|
|
161
|
+
.record(z.string())
|
|
162
|
+
.optional()
|
|
163
|
+
.describe("New key-value parameters passed through the deep link"),
|
|
164
|
+
metadata: z
|
|
165
|
+
.record(z.unknown())
|
|
166
|
+
.optional()
|
|
167
|
+
.describe("New arbitrary metadata attached to the link"),
|
|
168
|
+
},
|
|
169
|
+
}, async ({ linkId, name, iosUrl, androidUrl, fallbackUrl, iosFallbackUrl, androidFallbackUrl, parameters, metadata }) => {
|
|
170
|
+
try {
|
|
171
|
+
const body = {};
|
|
172
|
+
if (name !== undefined)
|
|
173
|
+
body.name = name;
|
|
174
|
+
if (iosUrl !== undefined)
|
|
175
|
+
body.iosUrl = iosUrl;
|
|
176
|
+
if (androidUrl !== undefined)
|
|
177
|
+
body.androidUrl = androidUrl;
|
|
178
|
+
if (fallbackUrl !== undefined)
|
|
179
|
+
body.fallbackUrl = fallbackUrl;
|
|
180
|
+
if (iosFallbackUrl !== undefined)
|
|
181
|
+
body.iosFallbackUrl = iosFallbackUrl;
|
|
182
|
+
if (androidFallbackUrl !== undefined)
|
|
183
|
+
body.androidFallbackUrl = androidFallbackUrl;
|
|
184
|
+
if (parameters !== undefined)
|
|
185
|
+
body.parameters = parameters;
|
|
186
|
+
if (metadata !== undefined)
|
|
187
|
+
body.metadata = metadata;
|
|
188
|
+
const data = await apiRequest("PUT", `/api/v1/links/${linkId}`, body);
|
|
189
|
+
return {
|
|
190
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
return {
|
|
195
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
196
|
+
isError: true,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
// -----------------------------------------------------------------------
|
|
201
|
+
// delete_link
|
|
202
|
+
// -----------------------------------------------------------------------
|
|
203
|
+
server.registerTool("delete_link", {
|
|
204
|
+
title: "Delete Link",
|
|
205
|
+
description: "Permanently delete a link. This is irreversible and the short URL will stop working immediately. Use with caution.",
|
|
206
|
+
annotations: { destructiveHint: true },
|
|
207
|
+
inputSchema: {
|
|
208
|
+
linkId: z.string().uuid().describe("The unique identifier of the link to delete"),
|
|
209
|
+
},
|
|
210
|
+
}, async ({ linkId }) => {
|
|
211
|
+
try {
|
|
212
|
+
await apiRequest("DELETE", `/api/v1/links/${linkId}`);
|
|
213
|
+
return {
|
|
214
|
+
content: [
|
|
215
|
+
{
|
|
216
|
+
type: "text",
|
|
217
|
+
text: `Successfully deleted link ${linkId}`,
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
return {
|
|
224
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
225
|
+
isError: true,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
// -----------------------------------------------------------------------
|
|
230
|
+
// get_link_analytics
|
|
231
|
+
// -----------------------------------------------------------------------
|
|
232
|
+
server.registerTool("get_link_analytics", {
|
|
233
|
+
title: "Get Link Analytics",
|
|
234
|
+
description: "Retrieve click analytics for a specific link. Returns aggregated data such as total clicks, unique clicks, and breakdowns by platform, country, and referrer for the requested time period.",
|
|
235
|
+
annotations: { readOnlyHint: true },
|
|
236
|
+
inputSchema: {
|
|
237
|
+
linkId: z.string().uuid().describe("The unique identifier of the link"),
|
|
238
|
+
period: z
|
|
239
|
+
.enum(["24h", "7d", "30d", "90d"])
|
|
240
|
+
.optional()
|
|
241
|
+
.describe("Time period for analytics data (defaults to 7d)"),
|
|
242
|
+
},
|
|
243
|
+
}, async ({ linkId, period }) => {
|
|
244
|
+
try {
|
|
245
|
+
const params = new URLSearchParams();
|
|
246
|
+
if (period !== undefined)
|
|
247
|
+
params.set("period", period);
|
|
248
|
+
const qs = params.toString();
|
|
249
|
+
const path = `/api/v1/links/${linkId}/analytics${qs ? `?${qs}` : ""}`;
|
|
250
|
+
const data = await apiRequest("GET", path);
|
|
251
|
+
return {
|
|
252
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
return {
|
|
257
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
258
|
+
isError: true,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"links.js","sourceRoot":"","sources":["../../src/tools/links.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { apiRequest } from "../client/ulink-api.js";
|
|
3
|
+
export function registerProjectTools(server) {
|
|
4
|
+
// -----------------------------------------------------------------------
|
|
5
|
+
// list_projects
|
|
6
|
+
// -----------------------------------------------------------------------
|
|
7
|
+
server.registerTool("list_projects", {
|
|
8
|
+
title: "List Projects",
|
|
9
|
+
description: "List all ULink projects owned by or shared with the authenticated user. Returns an array of project objects including id, name, slug, and default URL.",
|
|
10
|
+
annotations: { readOnlyHint: true },
|
|
11
|
+
}, async () => {
|
|
12
|
+
try {
|
|
13
|
+
const data = await apiRequest("GET", "/projects");
|
|
14
|
+
return {
|
|
15
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
return {
|
|
20
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
21
|
+
isError: true,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
// -----------------------------------------------------------------------
|
|
26
|
+
// get_project
|
|
27
|
+
// -----------------------------------------------------------------------
|
|
28
|
+
server.registerTool("get_project", {
|
|
29
|
+
title: "Get Project",
|
|
30
|
+
description: "Retrieve detailed information about a specific ULink project by its ID, including configuration, domains, and membership details.",
|
|
31
|
+
annotations: { readOnlyHint: true },
|
|
32
|
+
inputSchema: {
|
|
33
|
+
projectId: z.string().uuid().describe("The unique identifier of the project"),
|
|
34
|
+
},
|
|
35
|
+
}, async ({ projectId }) => {
|
|
36
|
+
try {
|
|
37
|
+
const data = await apiRequest("GET", `/projects/${projectId}`);
|
|
38
|
+
return {
|
|
39
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
return {
|
|
44
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
45
|
+
isError: true,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
// -----------------------------------------------------------------------
|
|
50
|
+
// create_project
|
|
51
|
+
// -----------------------------------------------------------------------
|
|
52
|
+
server.registerTool("create_project", {
|
|
53
|
+
title: "Create Project",
|
|
54
|
+
description: "Create a new ULink project. A project is the top-level container for links, domains, and API keys. Requires a name and a default fallback URL.",
|
|
55
|
+
inputSchema: {
|
|
56
|
+
name: z.string().describe("Human-readable name for the project"),
|
|
57
|
+
defaultUrl: z
|
|
58
|
+
.string()
|
|
59
|
+
.url()
|
|
60
|
+
.describe("Default fallback URL used when no platform-specific URL matches"),
|
|
61
|
+
},
|
|
62
|
+
}, async ({ name, defaultUrl }) => {
|
|
63
|
+
try {
|
|
64
|
+
const data = await apiRequest("POST", "/projects", { name, default_url: defaultUrl });
|
|
65
|
+
return {
|
|
66
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
return {
|
|
71
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
72
|
+
isError: true,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
// -----------------------------------------------------------------------
|
|
77
|
+
// update_project
|
|
78
|
+
// -----------------------------------------------------------------------
|
|
79
|
+
server.registerTool("update_project", {
|
|
80
|
+
title: "Update Project",
|
|
81
|
+
description: "Update the name and/or default URL of an existing ULink project. Only the fields you provide will be changed.",
|
|
82
|
+
inputSchema: {
|
|
83
|
+
projectId: z.string().uuid().describe("The unique identifier of the project to update"),
|
|
84
|
+
name: z.string().optional().describe("New name for the project"),
|
|
85
|
+
defaultUrl: z.string().url().optional().describe("New default fallback URL"),
|
|
86
|
+
},
|
|
87
|
+
}, async ({ projectId, name, defaultUrl }) => {
|
|
88
|
+
try {
|
|
89
|
+
const body = {};
|
|
90
|
+
if (name !== undefined)
|
|
91
|
+
body.name = name;
|
|
92
|
+
if (defaultUrl !== undefined)
|
|
93
|
+
body.default_url = defaultUrl;
|
|
94
|
+
const data = await apiRequest("PATCH", `/projects/${projectId}`, body);
|
|
95
|
+
return {
|
|
96
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
return {
|
|
101
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
102
|
+
isError: true,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// -----------------------------------------------------------------------
|
|
107
|
+
// configure_project
|
|
108
|
+
// -----------------------------------------------------------------------
|
|
109
|
+
server.registerTool("configure_project", {
|
|
110
|
+
title: "Configure Project",
|
|
111
|
+
description: "Update platform-specific configuration for a ULink project, such as iOS bundle identifier, Android package name, deeplink schemas, and SHA-256 fingerprints. These settings are used for deep link resolution on mobile platforms.",
|
|
112
|
+
inputSchema: {
|
|
113
|
+
projectId: z.string().uuid().describe("The unique identifier of the project to configure"),
|
|
114
|
+
androidPackageName: z
|
|
115
|
+
.string()
|
|
116
|
+
.optional()
|
|
117
|
+
.describe("Android package name (e.g. com.example.app)"),
|
|
118
|
+
iosBundleIdentifier: z
|
|
119
|
+
.string()
|
|
120
|
+
.optional()
|
|
121
|
+
.describe("iOS bundle identifier (e.g. com.example.app)"),
|
|
122
|
+
iosTeamId: z.string().optional().describe("Apple Developer Team ID"),
|
|
123
|
+
iosDeeplinkSchema: z
|
|
124
|
+
.string()
|
|
125
|
+
.optional()
|
|
126
|
+
.describe("iOS deeplink URI scheme (e.g. myapp://)"),
|
|
127
|
+
androidDeeplinkSchema: z
|
|
128
|
+
.string()
|
|
129
|
+
.optional()
|
|
130
|
+
.describe("Android deeplink URI scheme (e.g. myapp://)"),
|
|
131
|
+
androidSha256Fingerprints: z
|
|
132
|
+
.array(z.string())
|
|
133
|
+
.optional()
|
|
134
|
+
.describe("SHA-256 certificate fingerprints for Android App Links verification"),
|
|
135
|
+
},
|
|
136
|
+
}, async ({ projectId, androidPackageName, iosBundleIdentifier, iosTeamId, iosDeeplinkSchema, androidDeeplinkSchema, androidSha256Fingerprints, }) => {
|
|
137
|
+
try {
|
|
138
|
+
const body = {};
|
|
139
|
+
if (androidPackageName !== undefined)
|
|
140
|
+
body.android_package_name = androidPackageName;
|
|
141
|
+
if (iosBundleIdentifier !== undefined)
|
|
142
|
+
body.ios_bundle_identifier = iosBundleIdentifier;
|
|
143
|
+
if (iosTeamId !== undefined)
|
|
144
|
+
body.ios_team_id = iosTeamId;
|
|
145
|
+
if (iosDeeplinkSchema !== undefined)
|
|
146
|
+
body.ios_deeplink_schema = iosDeeplinkSchema;
|
|
147
|
+
if (androidDeeplinkSchema !== undefined)
|
|
148
|
+
body.android_deeplink_schema = androidDeeplinkSchema;
|
|
149
|
+
if (androidSha256Fingerprints !== undefined)
|
|
150
|
+
body.android_sha256_fingerprints = androidSha256Fingerprints;
|
|
151
|
+
const data = await apiRequest("PATCH", `/projects/${projectId}/configuration`, body);
|
|
152
|
+
return {
|
|
153
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
return {
|
|
158
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
159
|
+
isError: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=projects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/tools/projects.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,MAAM,UAAU,oBAAoB,CAAC,MAAiB;IACpD,0EAA0E;IAC1E,gBAAgB;IAChB,0EAA0E;IAC1E,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;QACE,KAAK,EAAE,eAAe;QACtB,WAAW,EACT,wJAAwJ;QAC1J,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;KACpC,EACD,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;YAClD,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aACjE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAW,GAAW,CAAC,OAAO,EAAE,EAAE,CAAC;gBACnE,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;IAEF,0EAA0E;IAC1E,cAAc;IACd,0EAA0E;IAC1E,MAAM,CAAC,YAAY,CACjB,aAAa,EACb;QACE,KAAK,EAAE,aAAa;QACpB,WAAW,EACT,mIAAmI;QACrI,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;QACnC,WAAW,EAAE;YACX,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;SAC9E;KACF,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;QACtB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,aAAa,SAAS,EAAE,CAAC,CAAC;YAC/D,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aACjE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAW,GAAW,CAAC,OAAO,EAAE,EAAE,CAAC;gBACnE,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;IAEF,0EAA0E;IAC1E,iBAAiB;IACjB,0EAA0E;IAC1E,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;QACE,KAAK,EAAE,gBAAgB;QACvB,WAAW,EACT,gJAAgJ;QAClJ,WAAW,EAAE;YACX,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;YAChE,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,GAAG,EAAE;iBACL,QAAQ,CAAC,iEAAiE,CAAC;SAC/E;KACF,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE;QAC7B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;YACtF,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aACjE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAW,GAAW,CAAC,OAAO,EAAE,EAAE,CAAC;gBACnE,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;IAEF,0EAA0E;IAC1E,iBAAiB;IACjB,0EAA0E;IAC1E,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;QACE,KAAK,EAAE,gBAAgB;QACvB,WAAW,EACT,+GAA+G;QACjH,WAAW,EAAE;YACX,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,gDAAgD,CAAC;YACvF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;YAChE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;SAC7E;KACF,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE;QACxC,IAAI,CAAC;YACH,MAAM,IAAI,GAA4B,EAAE,CAAC;YACzC,IAAI,IAAI,KAAK,SAAS;gBAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;YACzC,IAAI,UAAU,KAAK,SAAS;gBAAE,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;YAE5D,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,aAAa,SAAS,EAAE,EAAE,IAAI,CAAC,CAAC;YACvE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aACjE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAW,GAAW,CAAC,OAAO,EAAE,EAAE,CAAC;gBACnE,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;IAEF,0EAA0E;IAC1E,oBAAoB;IACpB,0EAA0E;IAC1E,MAAM,CAAC,YAAY,CACjB,mBAAmB,EACnB;QACE,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EACT,oOAAoO;QACtO,WAAW,EAAE;YACX,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,mDAAmD,CAAC;YAC1F,kBAAkB,EAAE,CAAC;iBAClB,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,6CAA6C,CAAC;YAC1D,mBAAmB,EAAE,CAAC;iBACnB,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,8CAA8C,CAAC;YAC3D,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;YACpE,iBAAiB,EAAE,CAAC;iBACjB,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,yCAAyC,CAAC;YACtD,qBAAqB,EAAE,CAAC;iBACrB,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,6CAA6C,CAAC;YAC1D,yBAAyB,EAAE,CAAC;iBACzB,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;iBACjB,QAAQ,EAAE;iBACV,QAAQ,CAAC,qEAAqE,CAAC;SACnF;KACF,EACD,KAAK,EAAE,EACL,SAAS,EACT,kBAAkB,EAClB,mBAAmB,EACnB,SAAS,EACT,iBAAiB,EACjB,qBAAqB,EACrB,yBAAyB,GAC1B,EAAE,EAAE;QACH,IAAI,CAAC;YACH,MAAM,IAAI,GAA4B,EAAE,CAAC;YACzC,IAAI,kBAAkB,KAAK,SAAS;gBAAE,IAAI,CAAC,oBAAoB,GAAG,kBAAkB,CAAC;YACrF,IAAI,mBAAmB,KAAK,SAAS;gBAAE,IAAI,CAAC,qBAAqB,GAAG,mBAAmB,CAAC;YACxF,IAAI,SAAS,KAAK,SAAS;gBAAE,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;YAC1D,IAAI,iBAAiB,KAAK,SAAS;gBAAE,IAAI,CAAC,mBAAmB,GAAG,iBAAiB,CAAC;YAClF,IAAI,qBAAqB,KAAK,SAAS;gBAAE,IAAI,CAAC,uBAAuB,GAAG,qBAAqB,CAAC;YAC9F,IAAI,yBAAyB,KAAK,SAAS;gBACzC,IAAI,CAAC,2BAA2B,GAAG,yBAAyB,CAAC;YAE/D,MAAM,IAAI,GAAG,MAAM,UAAU,CAC3B,OAAO,EACP,aAAa,SAAS,gBAAgB,EACtC,IAAI,CACL,CAAC;YACF,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aACjE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAW,GAAW,CAAC,OAAO,EAAE,EAAE,CAAC;gBACnE,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ulinkly/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "ULink MCP Server — Model Context Protocol server for the ULink REST API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ulink-mcp-server": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc && node -e \"const fs=require('fs');const f='dist/index.js';const c=fs.readFileSync(f,'utf8');if(!c.startsWith('#!')){fs.writeFileSync(f,'#!/usr/bin/env node\\n'+c);}\" && chmod 755 dist/index.js",
|
|
15
|
+
"dev": "tsc --watch",
|
|
16
|
+
"prepare": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
23
|
+
"zod": "^3.22.4"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22",
|
|
27
|
+
"typescript": "^5.5.4"
|
|
28
|
+
}
|
|
29
|
+
}
|