@cloudgrid-io/mcp 0.2.0 → 0.2.2
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/package.json +1 -1
- package/src/auth.js +10 -1
- package/src/index.js +1 -1
- package/src/tools.js +9 -0
- package/src/web.js +19 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudgrid-io/mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "MCP server for CloudGrid. Two editions: a local stdio server (full toolset) and a hosted web server (light, CLI-free toolset) over MCP Streamable HTTP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/auth.js
CHANGED
|
@@ -14,8 +14,16 @@ import { mkdir, writeFile, chmod, readFile } from "node:fs/promises";
|
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
15
|
import { join } from "node:path";
|
|
16
16
|
|
|
17
|
+
// Base for server-to-API calls. On a hosted deployment this is the in-cluster
|
|
18
|
+
// service address (CLOUDGRID_API_URL), which is fast but NOT reachable by a browser.
|
|
17
19
|
const API_BASE = (process.env.CLOUDGRID_API_URL || "https://api.cloudgrid.io").replace(/\/+$/, "");
|
|
18
20
|
|
|
21
|
+
// Base for URLs handed to the user's browser (the sign-in link). Must be public,
|
|
22
|
+
// regardless of the internal API address. Defaults to the public host.
|
|
23
|
+
const PUBLIC_API_BASE = (
|
|
24
|
+
process.env.CLOUDGRID_PUBLIC_API_URL || "https://api.cloudgrid.io"
|
|
25
|
+
).replace(/\/+$/, "");
|
|
26
|
+
|
|
19
27
|
export function cloudgridHome() {
|
|
20
28
|
return process.env.CLOUDGRID_HOME || join(homedir(), ".cloudgrid");
|
|
21
29
|
}
|
|
@@ -28,8 +36,9 @@ export function newLoginCode() {
|
|
|
28
36
|
return randomUUID();
|
|
29
37
|
}
|
|
30
38
|
|
|
39
|
+
// The sign-in URL the user opens in a browser — always the public host.
|
|
31
40
|
export function buildLoginUrl(code) {
|
|
32
|
-
return `${
|
|
41
|
+
return `${PUBLIC_API_BASE}/auth/login?code=${encodeURIComponent(code)}`;
|
|
33
42
|
}
|
|
34
43
|
|
|
35
44
|
// One poll. Returns { status: 'not_started' | 'pending' | 'authenticated' | 'expired', jwt? }.
|
package/src/index.js
CHANGED
|
@@ -25,7 +25,7 @@ const ctx = {
|
|
|
25
25
|
savedLocationNote: () => `Credentials saved to ${credentialsPath()}.`,
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
const server = new McpServer({ name: "cloudgrid-mcp", version: "0.2.
|
|
28
|
+
const server = new McpServer({ name: "cloudgrid-mcp", version: "0.2.2" });
|
|
29
29
|
registerTools(server, ctx);
|
|
30
30
|
|
|
31
31
|
const transport = new StdioServerTransport();
|
package/src/tools.js
CHANGED
|
@@ -120,6 +120,15 @@ async function runDrop(ctx, { html, path: filePath, filename, anonymous, org })
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
// Anonymous path on a hosted server: present the trusted-server credential so the
|
|
124
|
+
// platform keys the anon-drop cap on the per-user id instead of the shared cluster
|
|
125
|
+
// egress IP. A missing/bad secret silently falls back to the IP cap server-side, so
|
|
126
|
+
// this is safe to send whenever configured. Only the web edition sets ctx.trustedServer.
|
|
127
|
+
if (!headers["Authorization"] && ctx.trustedServer?.secret && ctx.trustedServer?.endUserId) {
|
|
128
|
+
headers["X-CloudGrid-Trusted-Server-Auth"] = ctx.trustedServer.secret;
|
|
129
|
+
headers["X-CloudGrid-Trusted-Server-End-User"] = ctx.trustedServer.endUserId;
|
|
130
|
+
}
|
|
131
|
+
|
|
123
132
|
const form = new FormData();
|
|
124
133
|
form.append("artifact", new Blob([bytes], { type }), name);
|
|
125
134
|
if (orgSlug) form.append("org_slug", orgSlug);
|
package/src/web.js
CHANGED
|
@@ -17,8 +17,15 @@ import { registerTools, decodeJwt } from "./tools.js";
|
|
|
17
17
|
|
|
18
18
|
const PORT = Number(process.env.PORT || 8080);
|
|
19
19
|
|
|
20
|
-
//
|
|
21
|
-
|
|
20
|
+
// The trusted-server credential, if this host is provisioned as one. Sent on
|
|
21
|
+
// anonymous drops so the platform keys the anon-drop cap on the per-user id rather
|
|
22
|
+
// than the shared cluster egress IP. Missing/bad secret falls back to the IP cap
|
|
23
|
+
// server-side, so it is safe to leave unset.
|
|
24
|
+
const TRUSTED_SERVER_SECRET = process.env.MCP_TRUSTED_SERVER_SECRET || null;
|
|
25
|
+
|
|
26
|
+
// A web session: identity lives in memory for the session's lifetime only. The
|
|
27
|
+
// session id doubles as the stable, opaque end-user id for the trusted-server cap.
|
|
28
|
+
function makeWebContext(sessionId) {
|
|
22
29
|
let sessionToken = null;
|
|
23
30
|
return {
|
|
24
31
|
edition: "web",
|
|
@@ -33,6 +40,9 @@ function makeWebContext() {
|
|
|
33
40
|
return decodeJwt(jwt);
|
|
34
41
|
},
|
|
35
42
|
savedLocationNote: () => "You are signed in for this session.",
|
|
43
|
+
trustedServer: TRUSTED_SERVER_SECRET
|
|
44
|
+
? { secret: TRUSTED_SERVER_SECRET, endUserId: sessionId }
|
|
45
|
+
: null,
|
|
36
46
|
};
|
|
37
47
|
}
|
|
38
48
|
|
|
@@ -57,9 +67,12 @@ app.post("/mcp", async (req, res) => {
|
|
|
57
67
|
});
|
|
58
68
|
return;
|
|
59
69
|
}
|
|
60
|
-
// New session: fresh server + per-session identity context.
|
|
70
|
+
// New session: fresh server + per-session identity context. Generate the session
|
|
71
|
+
// id up front so it is also the trusted-server end-user id. (Distinct name from the
|
|
72
|
+
// incoming `sessionId` header above.)
|
|
73
|
+
const newSessionId = randomUUID();
|
|
61
74
|
transport = new StreamableHTTPServerTransport({
|
|
62
|
-
sessionIdGenerator: () =>
|
|
75
|
+
sessionIdGenerator: () => newSessionId,
|
|
63
76
|
onsessioninitialized: (sid) => {
|
|
64
77
|
transports[sid] = transport;
|
|
65
78
|
},
|
|
@@ -67,8 +80,8 @@ app.post("/mcp", async (req, res) => {
|
|
|
67
80
|
transport.onclose = () => {
|
|
68
81
|
if (transport.sessionId) delete transports[transport.sessionId];
|
|
69
82
|
};
|
|
70
|
-
const server = new McpServer({ name: "cloudgrid-mcp-web", version: "0.2.
|
|
71
|
-
registerTools(server, makeWebContext());
|
|
83
|
+
const server = new McpServer({ name: "cloudgrid-mcp-web", version: "0.2.2" });
|
|
84
|
+
registerTools(server, makeWebContext(newSessionId));
|
|
72
85
|
await server.connect(transport);
|
|
73
86
|
}
|
|
74
87
|
|