text2image-mcp 2.3.0 → 2.4.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/dist/auth.d.ts +31 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +210 -0
- package/dist/auth.js.map +1 -0
- package/dist/remote.js +101 -16
- package/dist/remote.js.map +1 -1
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +15 -1
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Response } from "express";
|
|
2
|
+
import { OAuthRegisteredClientsStore } from "@modelcontextprotocol/sdk/server/auth/clients.js";
|
|
3
|
+
import { OAuthServerProvider, AuthorizationParams } from "@modelcontextprotocol/sdk/server/auth/provider.js";
|
|
4
|
+
import { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js";
|
|
5
|
+
import { OAuthClientInformationFull, OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
6
|
+
export declare class InMemoryClientsStore implements OAuthRegisteredClientsStore {
|
|
7
|
+
private clients;
|
|
8
|
+
getClient(clientId: string): OAuthClientInformationFull | undefined;
|
|
9
|
+
registerClient(client: Omit<OAuthClientInformationFull, "client_id" | "client_id_issued_at">): OAuthClientInformationFull;
|
|
10
|
+
}
|
|
11
|
+
export declare class GeminiKeyOAuthProvider implements OAuthServerProvider {
|
|
12
|
+
readonly clientsStore: InMemoryClientsStore;
|
|
13
|
+
private codes;
|
|
14
|
+
private tokens;
|
|
15
|
+
constructor(clientsStore?: InMemoryClientsStore);
|
|
16
|
+
/**
|
|
17
|
+
* Look up the Gemini API key associated with a verified access token.
|
|
18
|
+
*/
|
|
19
|
+
getGeminiApiKey(accessToken: string): string | undefined;
|
|
20
|
+
authorize(client: OAuthClientInformationFull, params: AuthorizationParams, res: Response): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Called from the custom /oauth/authorize/submit POST handler.
|
|
23
|
+
* Generates an auth code, stores the Gemini key, and redirects to the client.
|
|
24
|
+
*/
|
|
25
|
+
completeAuthorization(client: OAuthClientInformationFull, params: AuthorizationParams, geminiApiKey: string, res: Response): void;
|
|
26
|
+
challengeForAuthorizationCode(_client: OAuthClientInformationFull, authorizationCode: string): Promise<string>;
|
|
27
|
+
exchangeAuthorizationCode(_client: OAuthClientInformationFull, authorizationCode: string): Promise<OAuthTokens>;
|
|
28
|
+
exchangeRefreshToken(): Promise<OAuthTokens>;
|
|
29
|
+
verifyAccessToken(token: string): Promise<AuthInfo>;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EACL,2BAA2B,EAC5B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,gDAAgD,CAAC;AAC1E,OAAO,EACL,0BAA0B,EAC1B,WAAW,EACZ,MAAM,0CAA0C,CAAC;AAIlD,qBAAa,oBAAqB,YAAW,2BAA2B;IACtE,OAAO,CAAC,OAAO,CAAiD;IAEhE,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,0BAA0B,GAAG,SAAS;IAInE,cAAc,CACZ,MAAM,EAAE,IAAI,CAAC,0BAA0B,EAAE,WAAW,GAAG,qBAAqB,CAAC,GAC5E,0BAA0B;CAS9B;AAqBD,qBAAa,sBAAuB,YAAW,mBAAmB;IAChE,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAAC;IAG5C,OAAO,CAAC,KAAK,CAAkC;IAG/C,OAAO,CAAC,MAAM,CAAkC;gBAEpC,YAAY,CAAC,EAAE,oBAAoB;IAI/C;;OAEG;IACH,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAYlD,SAAS,CACb,MAAM,EAAE,0BAA0B,EAClC,MAAM,EAAE,mBAAmB,EAC3B,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC,IAAI,CAAC;IAOhB;;;OAGG;IACH,qBAAqB,CACnB,MAAM,EAAE,0BAA0B,EAClC,MAAM,EAAE,mBAAmB,EAC3B,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,QAAQ,GACZ,IAAI;IAcD,6BAA6B,CACjC,OAAO,EAAE,0BAA0B,EACnC,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,MAAM,CAAC;IAMZ,yBAAyB,CAC7B,OAAO,EAAE,0BAA0B,EACnC,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,WAAW,CAAC;IAsBjB,oBAAoB,IAAI,OAAO,CAAC,WAAW,CAAC;IAI5C,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;CAgB1D"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// src/auth.ts — OAuth provider for multi-tenant Gemini API key flow
|
|
2
|
+
import { randomUUID, randomBytes } from "crypto";
|
|
3
|
+
// --- Clients Store (dynamic registration) ---
|
|
4
|
+
export class InMemoryClientsStore {
|
|
5
|
+
clients = new Map();
|
|
6
|
+
getClient(clientId) {
|
|
7
|
+
return this.clients.get(clientId);
|
|
8
|
+
}
|
|
9
|
+
registerClient(client) {
|
|
10
|
+
const full = {
|
|
11
|
+
...client,
|
|
12
|
+
client_id: randomUUID(),
|
|
13
|
+
client_id_issued_at: Math.floor(Date.now() / 1000),
|
|
14
|
+
};
|
|
15
|
+
this.clients.set(full.client_id, full);
|
|
16
|
+
return full;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// --- OAuth Provider ---
|
|
20
|
+
const TOKEN_TTL_SECONDS = 3600; // 1 hour
|
|
21
|
+
export class GeminiKeyOAuthProvider {
|
|
22
|
+
clientsStore;
|
|
23
|
+
// auth code -> pending code data
|
|
24
|
+
codes = new Map();
|
|
25
|
+
// access token -> stored token data
|
|
26
|
+
tokens = new Map();
|
|
27
|
+
constructor(clientsStore) {
|
|
28
|
+
this.clientsStore = clientsStore ?? new InMemoryClientsStore();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Look up the Gemini API key associated with a verified access token.
|
|
32
|
+
*/
|
|
33
|
+
getGeminiApiKey(accessToken) {
|
|
34
|
+
const stored = this.tokens.get(accessToken);
|
|
35
|
+
if (!stored)
|
|
36
|
+
return undefined;
|
|
37
|
+
if (Date.now() / 1000 > stored.expiresAt) {
|
|
38
|
+
this.tokens.delete(accessToken);
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
return stored.geminiApiKey;
|
|
42
|
+
}
|
|
43
|
+
// --- OAuthServerProvider implementation ---
|
|
44
|
+
async authorize(client, params, res) {
|
|
45
|
+
// Serve an HTML form where the user enters their Gemini API key.
|
|
46
|
+
// The form POSTs to /oauth/authorize/submit with the data we need.
|
|
47
|
+
const html = buildAuthorizePage(client, params);
|
|
48
|
+
res.type("html").send(html);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Called from the custom /oauth/authorize/submit POST handler.
|
|
52
|
+
* Generates an auth code, stores the Gemini key, and redirects to the client.
|
|
53
|
+
*/
|
|
54
|
+
completeAuthorization(client, params, geminiApiKey, res) {
|
|
55
|
+
const code = randomBytes(32).toString("hex");
|
|
56
|
+
this.codes.set(code, { client, params, geminiApiKey });
|
|
57
|
+
// Build redirect URL with code (and state if provided)
|
|
58
|
+
const redirectUrl = new URL(params.redirectUri);
|
|
59
|
+
redirectUrl.searchParams.set("code", code);
|
|
60
|
+
if (params.state) {
|
|
61
|
+
redirectUrl.searchParams.set("state", params.state);
|
|
62
|
+
}
|
|
63
|
+
res.redirect(redirectUrl.toString());
|
|
64
|
+
}
|
|
65
|
+
async challengeForAuthorizationCode(_client, authorizationCode) {
|
|
66
|
+
const pending = this.codes.get(authorizationCode);
|
|
67
|
+
if (!pending)
|
|
68
|
+
throw new Error("Unknown authorization code");
|
|
69
|
+
return pending.params.codeChallenge;
|
|
70
|
+
}
|
|
71
|
+
async exchangeAuthorizationCode(_client, authorizationCode) {
|
|
72
|
+
const pending = this.codes.get(authorizationCode);
|
|
73
|
+
if (!pending)
|
|
74
|
+
throw new Error("Unknown authorization code");
|
|
75
|
+
this.codes.delete(authorizationCode);
|
|
76
|
+
const accessToken = randomBytes(48).toString("hex");
|
|
77
|
+
const expiresAt = Math.floor(Date.now() / 1000) + TOKEN_TTL_SECONDS;
|
|
78
|
+
this.tokens.set(accessToken, {
|
|
79
|
+
clientId: pending.client.client_id,
|
|
80
|
+
scopes: pending.params.scopes ?? [],
|
|
81
|
+
expiresAt,
|
|
82
|
+
geminiApiKey: pending.geminiApiKey,
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
access_token: accessToken,
|
|
86
|
+
token_type: "bearer",
|
|
87
|
+
expires_in: TOKEN_TTL_SECONDS,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async exchangeRefreshToken() {
|
|
91
|
+
throw new Error("Refresh tokens not supported");
|
|
92
|
+
}
|
|
93
|
+
async verifyAccessToken(token) {
|
|
94
|
+
const stored = this.tokens.get(token);
|
|
95
|
+
if (!stored)
|
|
96
|
+
throw new Error("Invalid access token");
|
|
97
|
+
if (Date.now() / 1000 > stored.expiresAt) {
|
|
98
|
+
this.tokens.delete(token);
|
|
99
|
+
throw new Error("Access token expired");
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
token,
|
|
103
|
+
clientId: stored.clientId,
|
|
104
|
+
scopes: stored.scopes,
|
|
105
|
+
expiresAt: stored.expiresAt,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// --- HTML form for the authorize page ---
|
|
110
|
+
function buildAuthorizePage(client, params) {
|
|
111
|
+
const clientName = client.client_name || client.client_id;
|
|
112
|
+
return `<!DOCTYPE html>
|
|
113
|
+
<html lang="en">
|
|
114
|
+
<head>
|
|
115
|
+
<meta charset="utf-8">
|
|
116
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
117
|
+
<title>Authorize - text2image MCP</title>
|
|
118
|
+
<style>
|
|
119
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
120
|
+
body {
|
|
121
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
122
|
+
background: #0f0f0f;
|
|
123
|
+
color: #e0e0e0;
|
|
124
|
+
display: flex;
|
|
125
|
+
justify-content: center;
|
|
126
|
+
align-items: center;
|
|
127
|
+
min-height: 100vh;
|
|
128
|
+
padding: 1rem;
|
|
129
|
+
}
|
|
130
|
+
.card {
|
|
131
|
+
background: #1a1a1a;
|
|
132
|
+
border: 1px solid #333;
|
|
133
|
+
border-radius: 12px;
|
|
134
|
+
padding: 2rem;
|
|
135
|
+
max-width: 420px;
|
|
136
|
+
width: 100%;
|
|
137
|
+
}
|
|
138
|
+
h1 { font-size: 1.4rem; margin-bottom: 0.25rem; color: #fff; }
|
|
139
|
+
.subtitle { color: #888; font-size: 0.9rem; margin-bottom: 1.5rem; }
|
|
140
|
+
.client-name { color: #60a5fa; font-weight: 600; }
|
|
141
|
+
label { display: block; font-size: 0.85rem; color: #aaa; margin-bottom: 0.4rem; }
|
|
142
|
+
input[type="password"] {
|
|
143
|
+
width: 100%;
|
|
144
|
+
padding: 0.7rem 0.8rem;
|
|
145
|
+
background: #111;
|
|
146
|
+
border: 1px solid #444;
|
|
147
|
+
border-radius: 8px;
|
|
148
|
+
color: #fff;
|
|
149
|
+
font-size: 0.95rem;
|
|
150
|
+
outline: none;
|
|
151
|
+
transition: border-color 0.2s;
|
|
152
|
+
}
|
|
153
|
+
input[type="password"]:focus { border-color: #60a5fa; }
|
|
154
|
+
.help { font-size: 0.75rem; color: #666; margin-top: 0.4rem; }
|
|
155
|
+
.help a { color: #60a5fa; text-decoration: none; }
|
|
156
|
+
.help a:hover { text-decoration: underline; }
|
|
157
|
+
button {
|
|
158
|
+
width: 100%;
|
|
159
|
+
margin-top: 1.5rem;
|
|
160
|
+
padding: 0.75rem;
|
|
161
|
+
background: #2563eb;
|
|
162
|
+
color: #fff;
|
|
163
|
+
border: none;
|
|
164
|
+
border-radius: 8px;
|
|
165
|
+
font-size: 1rem;
|
|
166
|
+
font-weight: 600;
|
|
167
|
+
cursor: pointer;
|
|
168
|
+
transition: background 0.2s;
|
|
169
|
+
}
|
|
170
|
+
button:hover { background: #1d4ed8; }
|
|
171
|
+
.field { margin-bottom: 1rem; }
|
|
172
|
+
</style>
|
|
173
|
+
</head>
|
|
174
|
+
<body>
|
|
175
|
+
<div class="card">
|
|
176
|
+
<h1>text2image MCP</h1>
|
|
177
|
+
<p class="subtitle">
|
|
178
|
+
<span class="client-name">${escapeHtml(clientName)}</span> wants to connect.
|
|
179
|
+
Enter your Gemini API key to authorize.
|
|
180
|
+
</p>
|
|
181
|
+
<form method="POST" action="/oauth/authorize/submit">
|
|
182
|
+
<input type="hidden" name="client_id" value="${escapeHtml(client.client_id)}">
|
|
183
|
+
<input type="hidden" name="redirect_uri" value="${escapeHtml(params.redirectUri)}">
|
|
184
|
+
<input type="hidden" name="code_challenge" value="${escapeHtml(params.codeChallenge)}">
|
|
185
|
+
${params.state ? `<input type="hidden" name="state" value="${escapeHtml(params.state)}">` : ""}
|
|
186
|
+
${params.scopes ? `<input type="hidden" name="scopes" value="${escapeHtml(params.scopes.join(" "))}">` : ""}
|
|
187
|
+
<div class="field">
|
|
188
|
+
<label for="gemini_api_key">Gemini API Key</label>
|
|
189
|
+
<input type="password" id="gemini_api_key" name="gemini_api_key"
|
|
190
|
+
placeholder="AIza..." required autocomplete="off">
|
|
191
|
+
<p class="help">
|
|
192
|
+
Get a free key at
|
|
193
|
+
<a href="https://aistudio.google.com/apikey" target="_blank" rel="noopener">aistudio.google.com/apikey</a>
|
|
194
|
+
</p>
|
|
195
|
+
</div>
|
|
196
|
+
<button type="submit">Authorize</button>
|
|
197
|
+
</form>
|
|
198
|
+
</div>
|
|
199
|
+
</body>
|
|
200
|
+
</html>`;
|
|
201
|
+
}
|
|
202
|
+
function escapeHtml(str) {
|
|
203
|
+
return str
|
|
204
|
+
.replace(/&/g, "&")
|
|
205
|
+
.replace(/</g, "<")
|
|
206
|
+
.replace(/>/g, ">")
|
|
207
|
+
.replace(/"/g, """)
|
|
208
|
+
.replace(/'/g, "'");
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/remote.js
CHANGED
|
@@ -4,7 +4,10 @@ import express from "express";
|
|
|
4
4
|
import cors from "cors";
|
|
5
5
|
import { randomUUID } from "crypto";
|
|
6
6
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
7
|
+
import { mcpAuthRouter } from "@modelcontextprotocol/sdk/server/auth/router.js";
|
|
8
|
+
import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
|
|
7
9
|
import { NanoBananaMCP } from "./server.js";
|
|
10
|
+
import { GeminiKeyOAuthProvider } from "./auth.js";
|
|
8
11
|
const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
|
|
9
12
|
const sessions = new Map();
|
|
10
13
|
// --- Cleanup stale sessions ---
|
|
@@ -17,37 +20,110 @@ const cleanupInterval = setInterval(() => {
|
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
22
|
}, 60_000);
|
|
23
|
+
// --- OAuth setup ---
|
|
24
|
+
const SERVER_API_KEY = process.env.GEMINI_API_KEY?.trim() || null;
|
|
25
|
+
const PUBLIC_URL = process.env.PUBLIC_URL?.trim() || null;
|
|
26
|
+
// OAuth is enabled when PUBLIC_URL is set (HTTPS URL of the deployment)
|
|
27
|
+
const oauthEnabled = PUBLIC_URL !== null;
|
|
28
|
+
const oauthProvider = oauthEnabled ? new GeminiKeyOAuthProvider() : null;
|
|
29
|
+
// Compute stable widget domain from PUBLIC_URL (host-independent identifier)
|
|
30
|
+
function computeAppDomain() {
|
|
31
|
+
if (!PUBLIC_URL)
|
|
32
|
+
return undefined;
|
|
33
|
+
try {
|
|
34
|
+
const hostname = new URL(PUBLIC_URL).hostname;
|
|
35
|
+
// Replace dots with dashes for a flat subdomain-style identifier
|
|
36
|
+
return hostname.replace(/\./g, "-");
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const appDomain = computeAppDomain();
|
|
20
43
|
// --- Express app ---
|
|
21
44
|
const app = express();
|
|
22
45
|
app.use(cors());
|
|
46
|
+
// Mount OAuth router BEFORE body parsing (it has its own parsers)
|
|
47
|
+
if (oauthEnabled && oauthProvider) {
|
|
48
|
+
const issuerUrl = new URL(PUBLIC_URL);
|
|
49
|
+
app.use(mcpAuthRouter({
|
|
50
|
+
provider: oauthProvider,
|
|
51
|
+
issuerUrl,
|
|
52
|
+
serviceDocumentationUrl: new URL("https://github.com/kishorkukreja/Nano-Banana-MCP"),
|
|
53
|
+
}));
|
|
54
|
+
// Custom form submission endpoint for the authorize page
|
|
55
|
+
app.post("/oauth/authorize/submit", express.urlencoded({ extended: false }), (req, res) => {
|
|
56
|
+
const { client_id, redirect_uri, code_challenge, state, scopes, gemini_api_key } = req.body;
|
|
57
|
+
if (!client_id || !redirect_uri || !code_challenge || !gemini_api_key) {
|
|
58
|
+
res.status(400).send("Missing required fields.");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const client = oauthProvider.clientsStore.getClient(client_id);
|
|
62
|
+
if (!client) {
|
|
63
|
+
res.status(400).send("Unknown client.");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const params = {
|
|
67
|
+
redirectUri: redirect_uri,
|
|
68
|
+
codeChallenge: code_challenge,
|
|
69
|
+
state: state || undefined,
|
|
70
|
+
scopes: scopes ? scopes.split(" ") : undefined,
|
|
71
|
+
};
|
|
72
|
+
oauthProvider.completeAuthorization(client, params, gemini_api_key, res);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
23
75
|
app.use(express.json());
|
|
24
|
-
// --- Health endpoint ---
|
|
76
|
+
// --- Health endpoint (no auth) ---
|
|
25
77
|
app.get("/health", (_req, res) => {
|
|
26
|
-
res.json({ status: "ok", sessions: sessions.size });
|
|
78
|
+
res.json({ status: "ok", sessions: sessions.size, oauth: oauthEnabled });
|
|
27
79
|
});
|
|
28
|
-
// ---
|
|
29
|
-
// API key resolution: env var takes priority, then Bearer token from client
|
|
30
|
-
const SERVER_API_KEY = process.env.GEMINI_API_KEY?.trim() || null;
|
|
80
|
+
// --- API key resolution ---
|
|
31
81
|
function resolveApiKey(req) {
|
|
32
|
-
// Server-side env var —
|
|
82
|
+
// 1. Server-side env var — self-hosted single-tenant
|
|
33
83
|
if (SERVER_API_KEY)
|
|
34
84
|
return SERVER_API_KEY;
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
85
|
+
// 2. OAuth: look up Gemini key from verified access token
|
|
86
|
+
if (oauthProvider && req.auth) {
|
|
87
|
+
const key = oauthProvider.getGeminiApiKey(req.auth.token);
|
|
88
|
+
if (key)
|
|
89
|
+
return key;
|
|
90
|
+
}
|
|
91
|
+
// 3. Direct Bearer token fallback (non-OAuth, e.g. curl testing)
|
|
92
|
+
if (!oauthEnabled) {
|
|
93
|
+
const auth = req.headers.authorization;
|
|
94
|
+
if (auth?.startsWith("Bearer ")) {
|
|
95
|
+
return auth.slice(7).trim() || null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
40
99
|
}
|
|
100
|
+
// --- Bearer auth middleware (conditional) ---
|
|
101
|
+
const bearerAuth = oauthEnabled && oauthProvider
|
|
102
|
+
? requireBearerAuth({ verifier: oauthProvider })
|
|
103
|
+
: undefined;
|
|
104
|
+
function authMiddleware(req, res, next) {
|
|
105
|
+
// If OAuth is enabled, require Bearer auth on MCP endpoints
|
|
106
|
+
if (bearerAuth) {
|
|
107
|
+
bearerAuth(req, res, next);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// Otherwise, skip (resolveApiKey handles direct Bearer / env var)
|
|
111
|
+
next();
|
|
112
|
+
}
|
|
113
|
+
// --- MCP helpers ---
|
|
41
114
|
function isInitializeRequest(body) {
|
|
42
115
|
if (Array.isArray(body)) {
|
|
43
116
|
return body.some((msg) => typeof msg === "object" && msg !== null && msg.method === "initialize");
|
|
44
117
|
}
|
|
45
118
|
return typeof body === "object" && body !== null && body.method === "initialize";
|
|
46
119
|
}
|
|
47
|
-
|
|
120
|
+
// --- MCP endpoint ---
|
|
121
|
+
app.post("/mcp", authMiddleware, async (req, res) => {
|
|
48
122
|
const apiKey = resolveApiKey(req);
|
|
49
123
|
if (!apiKey) {
|
|
50
|
-
res.status(401).json({
|
|
124
|
+
res.status(401).json({
|
|
125
|
+
error: "Missing API key. Authorize via OAuth or set GEMINI_API_KEY env var on server.",
|
|
126
|
+
});
|
|
51
127
|
return;
|
|
52
128
|
}
|
|
53
129
|
// Check for existing session
|
|
@@ -68,7 +144,7 @@ app.post("/mcp", async (req, res) => {
|
|
|
68
144
|
return;
|
|
69
145
|
}
|
|
70
146
|
// Create new session
|
|
71
|
-
const mcp = new NanoBananaMCP({ apiKey, isRemote: true });
|
|
147
|
+
const mcp = new NanoBananaMCP({ apiKey, isRemote: true, appDomain });
|
|
72
148
|
const transport = new StreamableHTTPServerTransport({
|
|
73
149
|
sessionIdGenerator: () => randomUUID(),
|
|
74
150
|
onsessioninitialized: (newSessionId) => {
|
|
@@ -81,7 +157,7 @@ app.post("/mcp", async (req, res) => {
|
|
|
81
157
|
await mcp.connectTransport(transport);
|
|
82
158
|
await transport.handleRequest(req, res, req.body);
|
|
83
159
|
});
|
|
84
|
-
app.get("/mcp", async (req, res) => {
|
|
160
|
+
app.get("/mcp", authMiddleware, async (req, res) => {
|
|
85
161
|
const sessionId = req.headers["mcp-session-id"];
|
|
86
162
|
if (!sessionId) {
|
|
87
163
|
res.status(400).json({ error: "Missing mcp-session-id header." });
|
|
@@ -95,7 +171,7 @@ app.get("/mcp", async (req, res) => {
|
|
|
95
171
|
session.lastActivity = Date.now();
|
|
96
172
|
await session.transport.handleRequest(req, res);
|
|
97
173
|
});
|
|
98
|
-
app.delete("/mcp", async (req, res) => {
|
|
174
|
+
app.delete("/mcp", authMiddleware, async (req, res) => {
|
|
99
175
|
const sessionId = req.headers["mcp-session-id"];
|
|
100
176
|
if (!sessionId) {
|
|
101
177
|
res.status(400).json({ error: "Missing mcp-session-id header." });
|
|
@@ -115,6 +191,15 @@ const server = app.listen(PORT, () => {
|
|
|
115
191
|
console.log(`text2image-mcp remote server listening on port ${PORT}`);
|
|
116
192
|
console.log(`Health: http://localhost:${PORT}/health`);
|
|
117
193
|
console.log(`MCP endpoint: http://localhost:${PORT}/mcp`);
|
|
194
|
+
if (oauthEnabled) {
|
|
195
|
+
console.log(`OAuth enabled (issuer: ${PUBLIC_URL})`);
|
|
196
|
+
}
|
|
197
|
+
else if (SERVER_API_KEY) {
|
|
198
|
+
console.log("Using server-side GEMINI_API_KEY (no OAuth)");
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
console.log("No GEMINI_API_KEY or PUBLIC_URL set — clients must send Bearer token directly");
|
|
202
|
+
}
|
|
118
203
|
});
|
|
119
204
|
// --- Graceful shutdown ---
|
|
120
205
|
function shutdown() {
|
package/dist/remote.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.js","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":";AACA,gBAAgB;AAChB,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"remote.js","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
|
2
2
|
export interface NanoBananaMCPOptions {
|
|
3
3
|
apiKey?: string;
|
|
4
4
|
isRemote?: boolean;
|
|
5
|
+
/** Stable domain for the MCP App widget sandbox (derived from PUBLIC_URL). */
|
|
6
|
+
appDomain?: string;
|
|
5
7
|
}
|
|
6
8
|
export declare class NanoBananaMCP {
|
|
7
9
|
private server;
|
|
@@ -10,8 +12,10 @@ export declare class NanoBananaMCP {
|
|
|
10
12
|
private lastImagePath;
|
|
11
13
|
private isRemote;
|
|
12
14
|
private injectedApiKey;
|
|
15
|
+
private appDomain;
|
|
13
16
|
constructor(options?: NanoBananaMCPOptions);
|
|
14
17
|
private setupHandlers;
|
|
18
|
+
private buildUiMeta;
|
|
15
19
|
private resolveViewerPath;
|
|
16
20
|
private routeToolCall;
|
|
17
21
|
connectTransport(transport: Transport): Promise<void>;
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAe1E,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAKD,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,cAAc,CAAqB;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAe1E,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,SAAS,CAAqB;gBAE1B,OAAO,CAAC,EAAE,oBAAoB;IAY1C,OAAO,CAAC,aAAa;IAoErB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,iBAAiB;YAMX,aAAa;IAyDrB,gBAAgB,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IASrD,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAI3B"}
|
package/dist/server.js
CHANGED
|
@@ -22,11 +22,13 @@ export class NanoBananaMCP {
|
|
|
22
22
|
lastImagePath = null;
|
|
23
23
|
isRemote;
|
|
24
24
|
injectedApiKey;
|
|
25
|
+
appDomain;
|
|
25
26
|
constructor(options) {
|
|
26
27
|
this.isRemote = options?.isRemote ?? false;
|
|
27
28
|
this.injectedApiKey = options?.apiKey;
|
|
29
|
+
this.appDomain = options?.appDomain;
|
|
28
30
|
this.configManager = new ConfigManager();
|
|
29
|
-
this.server = new Server({ name: "text2image-mcp", version: "2.
|
|
31
|
+
this.server = new Server({ name: "text2image-mcp", version: "2.4.0" }, { capabilities: { tools: {}, resources: {} } });
|
|
30
32
|
this.setupHandlers();
|
|
31
33
|
}
|
|
32
34
|
setupHandlers() {
|
|
@@ -63,6 +65,7 @@ export class NanoBananaMCP {
|
|
|
63
65
|
name: "Image Viewer",
|
|
64
66
|
description: "Interactive image viewer with zoom, pan, and metadata display",
|
|
65
67
|
mimeType: MCP_APP_MIME_TYPE,
|
|
68
|
+
_meta: { ui: this.buildUiMeta() },
|
|
66
69
|
},
|
|
67
70
|
],
|
|
68
71
|
};
|
|
@@ -81,11 +84,22 @@ export class NanoBananaMCP {
|
|
|
81
84
|
uri: IMAGE_VIEWER_RESOURCE_URI,
|
|
82
85
|
mimeType: MCP_APP_MIME_TYPE,
|
|
83
86
|
text: html,
|
|
87
|
+
_meta: { ui: this.buildUiMeta() },
|
|
84
88
|
},
|
|
85
89
|
],
|
|
86
90
|
};
|
|
87
91
|
});
|
|
88
92
|
}
|
|
93
|
+
buildUiMeta() {
|
|
94
|
+
const meta = {
|
|
95
|
+
// Self-contained HTML — no external network access needed
|
|
96
|
+
csp: {},
|
|
97
|
+
};
|
|
98
|
+
if (this.appDomain) {
|
|
99
|
+
meta.domain = this.appDomain;
|
|
100
|
+
}
|
|
101
|
+
return meta;
|
|
102
|
+
}
|
|
89
103
|
resolveViewerPath() {
|
|
90
104
|
// Resolve relative to the compiled JS location (dist/server.js → dist/ui/image-viewer.html)
|
|
91
105
|
const thisDir = path.dirname(fileURLToPath(import.meta.url));
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,0BAA0B,EAC1B,yBAAyB,EAGzB,SAAS,EACT,QAAQ,GACT,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,8BAA8B;AAC9B,OAAO,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC5I,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|