@ulinkly/mcp-server 0.1.2 → 0.1.4
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 +9 -10
- package/dist/auth/oauth.js +6 -8
- package/dist/auth/oauth.js.map +1 -1
- package/dist/auth/token-store.d.ts +11 -0
- package/dist/auth/token-store.js +60 -0
- package/dist/auth/token-store.js.map +1 -0
- package/dist/client/ulink-api.js +8 -1
- package/dist/client/ulink-api.js.map +1 -1
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Choose your MCP client and run the corresponding command:
|
|
|
13
13
|
**Claude Code:**
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
claude mcp add ulink -- npx -y @ulinkly/mcp-server
|
|
16
|
+
claude mcp add ulink -- npx -y @ulinkly/mcp-server@latest
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
**Cursor:**
|
|
@@ -25,7 +25,7 @@ Open **Settings > MCP > Add new MCP server**, or add to `.cursor/mcp.json`:
|
|
|
25
25
|
"mcpServers": {
|
|
26
26
|
"ulink": {
|
|
27
27
|
"command": "npx",
|
|
28
|
-
"args": ["-y", "@ulinkly/mcp-server"]
|
|
28
|
+
"args": ["-y", "@ulinkly/mcp-server@latest"]
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
}
|
|
@@ -40,7 +40,7 @@ Open **Settings > MCP > Add new MCP server**, or add to `~/.windsurf/mcp.json`:
|
|
|
40
40
|
"mcpServers": {
|
|
41
41
|
"ulink": {
|
|
42
42
|
"command": "npx",
|
|
43
|
-
"args": ["-y", "@ulinkly/mcp-server"]
|
|
43
|
+
"args": ["-y", "@ulinkly/mcp-server@latest"]
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -49,12 +49,14 @@ Open **Settings > MCP > Add new MCP server**, or add to `~/.windsurf/mcp.json`:
|
|
|
49
49
|
If you don't see your MCP client listed above, check your client's MCP documentation and use the following server command:
|
|
50
50
|
|
|
51
51
|
```
|
|
52
|
-
npx -y @ulinkly/mcp-server
|
|
52
|
+
npx -y @ulinkly/mcp-server@latest
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
### 2. Authenticate
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
The MCP server shares authentication with the [ULink CLI](https://github.com/ulinkly/ulink-cli). If you've already logged in via `ulink login`, the MCP server will use those tokens automatically — no extra login needed.
|
|
58
|
+
|
|
59
|
+
Otherwise, your MCP client will open a browser window for authentication on first use. Tokens are saved to `~/.ulink/config.json` and shared with the CLI. The session persists and tokens refresh automatically.
|
|
58
60
|
|
|
59
61
|
#### API Key (alternative)
|
|
60
62
|
|
|
@@ -63,7 +65,7 @@ For CI environments or headless servers, set the `ULINK_API_KEY` environment var
|
|
|
63
65
|
**Claude Code:**
|
|
64
66
|
|
|
65
67
|
```bash
|
|
66
|
-
claude mcp add ulink -e ULINK_API_KEY=your-api-key -- npx -y @ulinkly/mcp-server
|
|
68
|
+
claude mcp add ulink -e ULINK_API_KEY=your-api-key -- npx -y @ulinkly/mcp-server@latest
|
|
67
69
|
```
|
|
68
70
|
|
|
69
71
|
**Manual config:**
|
|
@@ -73,7 +75,7 @@ claude mcp add ulink -e ULINK_API_KEY=your-api-key -- npx -y @ulinkly/mcp-server
|
|
|
73
75
|
"mcpServers": {
|
|
74
76
|
"ulink": {
|
|
75
77
|
"command": "npx",
|
|
76
|
-
"args": ["-y", "@ulinkly/mcp-server"],
|
|
78
|
+
"args": ["-y", "@ulinkly/mcp-server@latest"],
|
|
77
79
|
"env": {
|
|
78
80
|
"ULINK_API_KEY": "your-api-key"
|
|
79
81
|
}
|
|
@@ -138,9 +140,6 @@ The following ULink tools are available to the LLM, organized by category.
|
|
|
138
140
|
| Variable | Default | Description |
|
|
139
141
|
|----------|---------|-------------|
|
|
140
142
|
| `ULINK_API_KEY` | — | API key for authentication (skips browser OAuth flow) |
|
|
141
|
-
| `ULINK_API_URL` | `https://api.ulink.ly` | Base URL for the ULink REST API |
|
|
142
|
-
| `ULINK_FRONTEND_URL` | `https://ulink.ly` | Frontend URL used for the OAuth login flow |
|
|
143
|
-
| `ULINK_SUPABASE_ANON_KEY` | — | Supabase anon key for silent token refresh |
|
|
144
143
|
|
|
145
144
|
## Requirements
|
|
146
145
|
|
package/dist/auth/oauth.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID, randomBytes, createHash } from "node:crypto";
|
|
2
2
|
import { createServer } from "node:http";
|
|
3
|
-
import {
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
4
|
import { URL } from "node:url";
|
|
5
5
|
const FRONTEND_URL = process.env.ULINK_FRONTEND_URL ?? "https://ulink.ly";
|
|
6
6
|
const SUPABASE_URL = process.env.ULINK_SUPABASE_URL ?? "https://cjgihassfsspxivjtgoi.supabase.co";
|
|
@@ -40,14 +40,14 @@ export function openBrowser(url) {
|
|
|
40
40
|
const platform = process.platform;
|
|
41
41
|
try {
|
|
42
42
|
if (platform === "darwin") {
|
|
43
|
-
|
|
43
|
+
execFileSync("open", [url]);
|
|
44
44
|
}
|
|
45
45
|
else if (platform === "win32") {
|
|
46
|
-
|
|
46
|
+
execFileSync("cmd", ["/c", "start", "", url]);
|
|
47
47
|
}
|
|
48
48
|
else {
|
|
49
49
|
// Linux / other
|
|
50
|
-
|
|
50
|
+
execFileSync("xdg-open", [url]);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
catch {
|
|
@@ -141,10 +141,8 @@ export function browserOAuthFlow() {
|
|
|
141
141
|
// Token refresh
|
|
142
142
|
// ---------------------------------------------------------------------------
|
|
143
143
|
export async function refreshAccessToken(refreshToken) {
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
throw new Error("ULINK_SUPABASE_ANON_KEY not set — cannot refresh token silently. Re-authenticating via browser.");
|
|
147
|
-
}
|
|
144
|
+
const EMBEDDED_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNqZ2loYXNzZnNzcHhpdmp0Z29pIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDUwNjE0NTUsImV4cCI6MjA2MDYzNzQ1NX0._k-iNnobMaGN1qY8BGM4mMdnGRqOn1R90i_WXUn-Gpw";
|
|
145
|
+
const anonKey = process.env.ULINK_SUPABASE_ANON_KEY ?? EMBEDDED_ANON_KEY;
|
|
148
146
|
const url = `${SUPABASE_URL}/auth/v1/token?grant_type=refresh_token`;
|
|
149
147
|
const res = await fetch(url, {
|
|
150
148
|
method: "POST",
|
package/dist/auth/oauth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,YAAY,EAA6C,MAAM,WAAW,CAAC;AACpF,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { OAuthTokens } from "./oauth.js";
|
|
2
|
+
/**
|
|
3
|
+
* Load OAuthTokens from ~/.ulink/config.json if present and valid JWT.
|
|
4
|
+
* Returns undefined if file missing, auth missing, not JWT, or expired.
|
|
5
|
+
*/
|
|
6
|
+
export declare function loadTokensFromDisk(): OAuthTokens | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* Persist OAuthTokens to ~/.ulink/config.json, merging into existing config.
|
|
9
|
+
* Preserves projects, supabaseUrl, supabaseAnonKey and all other fields.
|
|
10
|
+
*/
|
|
11
|
+
export declare function saveTokensToDisk(tokens: OAuthTokens): void;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, chmodSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
const CONFIG_DIR = join(homedir(), ".ulink");
|
|
5
|
+
const CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
6
|
+
/**
|
|
7
|
+
* Load OAuthTokens from ~/.ulink/config.json if present and valid JWT.
|
|
8
|
+
* Returns undefined if file missing, auth missing, not JWT, or expired.
|
|
9
|
+
*/
|
|
10
|
+
export function loadTokensFromDisk() {
|
|
11
|
+
try {
|
|
12
|
+
const raw = readFileSync(CONFIG_PATH, "utf-8");
|
|
13
|
+
const config = JSON.parse(raw);
|
|
14
|
+
const auth = config.auth;
|
|
15
|
+
if (!auth || auth.type !== "jwt" || !auth.token || !auth.refreshToken) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
const expiresAt = new Date(auth.expiresAt).getTime();
|
|
19
|
+
if (Number.isNaN(expiresAt) || expiresAt <= Date.now()) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
accessToken: auth.token,
|
|
24
|
+
refreshToken: auth.refreshToken,
|
|
25
|
+
expiresAt,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Persist OAuthTokens to ~/.ulink/config.json, merging into existing config.
|
|
34
|
+
* Preserves projects, supabaseUrl, supabaseAnonKey and all other fields.
|
|
35
|
+
*/
|
|
36
|
+
export function saveTokensToDisk(tokens) {
|
|
37
|
+
try {
|
|
38
|
+
let config = {};
|
|
39
|
+
try {
|
|
40
|
+
const raw = readFileSync(CONFIG_PATH, "utf-8");
|
|
41
|
+
config = JSON.parse(raw);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// File doesn't exist or is invalid — start fresh
|
|
45
|
+
}
|
|
46
|
+
config.auth = {
|
|
47
|
+
type: "jwt",
|
|
48
|
+
token: tokens.accessToken,
|
|
49
|
+
refreshToken: tokens.refreshToken,
|
|
50
|
+
expiresAt: new Date(tokens.expiresAt).toISOString(),
|
|
51
|
+
};
|
|
52
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
53
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
54
|
+
chmodSync(CONFIG_PATH, 0o600);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
console.error("Failed to save tokens to disk:", err);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=token-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAUpD;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAChC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QAC1D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAkC,CAAC;QAEvD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACtE,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;QACrD,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YACvD,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO;YACL,WAAW,EAAE,IAAI,CAAC,KAAK;YACvB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,SAAS;SACV,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAmB;IAClD,IAAI,CAAC;QACH,IAAI,MAAM,GAA4B,EAAE,CAAC;QACzC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YAC/C,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,iDAAiD;QACnD,CAAC;QAED,MAAM,CAAC,IAAI,GAAG;YACZ,IAAI,EAAE,KAAK;YACX,KAAK,EAAE,MAAM,CAAC,WAAW;YACzB,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,SAAS,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE;SACpD,CAAC;QAEF,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;QAC5E,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;IACvD,CAAC;AACH,CAAC"}
|
package/dist/client/ulink-api.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getApiKey } from "../auth/api-key.js";
|
|
2
2
|
import { browserOAuthFlow, refreshAccessToken, } from "../auth/oauth.js";
|
|
3
|
+
import { loadTokensFromDisk, saveTokensToDisk } from "../auth/token-store.js";
|
|
3
4
|
// ---------------------------------------------------------------------------
|
|
4
5
|
// Configuration
|
|
5
6
|
// ---------------------------------------------------------------------------
|
|
@@ -29,19 +30,25 @@ async function ensureAuth() {
|
|
|
29
30
|
if (apiKey) {
|
|
30
31
|
return { header: "x-app-key", value: apiKey };
|
|
31
32
|
}
|
|
32
|
-
// 2. OAuth — first
|
|
33
|
+
// 2. OAuth — try disk first, then browser
|
|
34
|
+
if (!oauthTokens) {
|
|
35
|
+
oauthTokens = loadTokensFromDisk();
|
|
36
|
+
}
|
|
33
37
|
if (!oauthTokens) {
|
|
34
38
|
oauthTokens = await browserOAuthFlow();
|
|
39
|
+
saveTokensToDisk(oauthTokens);
|
|
35
40
|
}
|
|
36
41
|
// 3. Auto-refresh if token expires within 30 s
|
|
37
42
|
if (oauthTokens.expiresAt - Date.now() < TOKEN_REFRESH_BUFFER_MS) {
|
|
38
43
|
try {
|
|
39
44
|
oauthTokens = await refreshAccessToken(oauthTokens.refreshToken);
|
|
45
|
+
saveTokensToDisk(oauthTokens);
|
|
40
46
|
}
|
|
41
47
|
catch {
|
|
42
48
|
// Refresh failed — re-authenticate via browser
|
|
43
49
|
console.error("Token refresh failed, re-authenticating...");
|
|
44
50
|
oauthTokens = await browserOAuthFlow();
|
|
51
|
+
saveTokensToDisk(oauthTokens);
|
|
45
52
|
}
|
|
46
53
|
}
|
|
47
54
|
return { header: "Authorization", value: `Bearer ${oauthTokens.accessToken}` };
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"ulink-api.js","sourceRoot":"","sources":["../../src/client/ulink-api.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ulinkly/mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "ULink MCP Server — Model Context Protocol server for the ULink REST API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
"dev": "tsc --watch",
|
|
16
16
|
"prepare": "npm run build"
|
|
17
17
|
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/FlywheelStudio/ulink-mcp-server"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
18
23
|
"engines": {
|
|
19
24
|
"node": ">=18"
|
|
20
25
|
},
|