@gradio/core 0.15.1 → 0.16.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/CHANGELOG.md +6 -0
- package/dist/src/api_docs/ApiBanner.svelte +14 -10
- package/dist/src/api_docs/ApiBanner.svelte.d.ts +1 -0
- package/dist/src/api_docs/ApiDocs.svelte +448 -90
- package/dist/src/api_docs/InstallSnippet.svelte.d.ts +1 -1
- package/dist/src/api_docs/RecordingSnippet.svelte.d.ts +1 -1
- package/dist/src/api_docs/img/mcp.svg +6 -0
- package/package.json +19 -19
- package/src/api_docs/ApiBanner.svelte +15 -10
- package/src/api_docs/ApiDocs.svelte +474 -93
- package/src/api_docs/InstallSnippet.svelte +1 -1
- package/src/api_docs/RecordingSnippet.svelte +1 -1
- package/src/api_docs/img/mcp.svg +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @gradio/core
|
|
2
2
|
|
|
3
|
+
## 0.16.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- [#10984](https://github.com/gradio-app/gradio/pull/10984) [`8dab577`](https://github.com/gradio-app/gradio/commit/8dab5771c7d952c76f325681dbf364119c91b0b1) - Let Gradio apps also be MCP Servers. Thanks @abidlabs!
|
|
8
|
+
|
|
3
9
|
## 0.15.1
|
|
4
10
|
|
|
5
11
|
### Fixes
|
|
@@ -4,6 +4,7 @@ import Clear from "./img/clear.svelte";
|
|
|
4
4
|
import { BaseButton } from "@gradio/button";
|
|
5
5
|
export let root;
|
|
6
6
|
export let api_count;
|
|
7
|
+
export let current_language = "python";
|
|
7
8
|
const dispatch = createEventDispatcher();
|
|
8
9
|
</script>
|
|
9
10
|
|
|
@@ -16,17 +17,20 @@ const dispatch = createEventDispatcher();
|
|
|
16
17
|
</div>
|
|
17
18
|
</div>
|
|
18
19
|
<span class="counts">
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
{#if current_language !== "mcp"}
|
|
21
|
+
<BaseButton
|
|
22
|
+
size="sm"
|
|
23
|
+
variant="secondary"
|
|
24
|
+
elem_id="start-api-recorder"
|
|
25
|
+
on:click={() => dispatch("close", { api_recorder_visible: true })}
|
|
26
|
+
>
|
|
27
|
+
<div class="loading-dot self-baseline"></div>
|
|
28
|
+
<p class="self-baseline btn-text">API Recorder</p>
|
|
29
|
+
</BaseButton>
|
|
30
|
+
{/if}
|
|
28
31
|
<p>
|
|
29
|
-
<span class="url">{api_count}</span>
|
|
32
|
+
<span class="url">{api_count}</span>
|
|
33
|
+
{#if current_language !== "mcp"}API endpoint{:else}MCP Tool{/if}{#if api_count > 1}s{/if}<br
|
|
30
34
|
/>
|
|
31
35
|
</p>
|
|
32
36
|
</span>
|
|
@@ -6,10 +6,13 @@ import ParametersSnippet from "./ParametersSnippet.svelte";
|
|
|
6
6
|
import InstallSnippet from "./InstallSnippet.svelte";
|
|
7
7
|
import CodeSnippet from "./CodeSnippet.svelte";
|
|
8
8
|
import RecordingSnippet from "./RecordingSnippet.svelte";
|
|
9
|
+
import CopyButton from "./CopyButton.svelte";
|
|
10
|
+
import { Block } from "@gradio/atoms";
|
|
9
11
|
import python from "./img/python.svg";
|
|
10
12
|
import javascript from "./img/javascript.svg";
|
|
11
13
|
import bash from "./img/bash.svg";
|
|
12
14
|
import ResponseSnippet from "./ResponseSnippet.svelte";
|
|
15
|
+
import mcp from "./img/mcp.svg";
|
|
13
16
|
export let dependencies;
|
|
14
17
|
export let root;
|
|
15
18
|
export let app;
|
|
@@ -32,11 +35,13 @@ if (!root.endsWith("/")) {
|
|
|
32
35
|
export let api_calls = [];
|
|
33
36
|
let current_language = "python";
|
|
34
37
|
const langs = [
|
|
35
|
-
["python", python],
|
|
36
|
-
["javascript", javascript],
|
|
37
|
-
["bash", bash]
|
|
38
|
+
["python", "Python", python],
|
|
39
|
+
["javascript", "JavaScript", javascript],
|
|
40
|
+
["bash", "cURL", bash],
|
|
41
|
+
["mcp", "MCP", mcp]
|
|
38
42
|
];
|
|
39
43
|
let is_running = false;
|
|
44
|
+
let mcp_server_active = false;
|
|
40
45
|
async function get_info() {
|
|
41
46
|
let response = await fetch(
|
|
42
47
|
root.replace(/\/$/, "") + app.api_prefix + "/info"
|
|
@@ -57,11 +62,36 @@ get_js_info().then((js_api_info) => {
|
|
|
57
62
|
js_info = js_api_info;
|
|
58
63
|
});
|
|
59
64
|
const dispatch = createEventDispatcher();
|
|
65
|
+
const mcp_server_url = `${root}gradio_api/mcp/sse`;
|
|
66
|
+
let tools = [];
|
|
67
|
+
async function fetchMcpTools() {
|
|
68
|
+
try {
|
|
69
|
+
const response = await fetch(`${root}gradio_api/mcp/schema`);
|
|
70
|
+
const schema = await response.json();
|
|
71
|
+
tools = Object.entries(schema).map(([name, tool]) => ({
|
|
72
|
+
name: `${name}`,
|
|
73
|
+
description: tool.description || "",
|
|
74
|
+
parameters: tool.properties || {},
|
|
75
|
+
expanded: false
|
|
76
|
+
}));
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error("Failed to fetch MCP tools:", error);
|
|
79
|
+
tools = [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
60
82
|
onMount(() => {
|
|
61
83
|
document.body.style.overflow = "hidden";
|
|
62
84
|
if ("parentIFrame" in window) {
|
|
63
85
|
window.parentIFrame?.scrollTo(0, 0);
|
|
64
86
|
}
|
|
87
|
+
fetch(mcp_server_url).then((response) => {
|
|
88
|
+
mcp_server_active = response.ok;
|
|
89
|
+
if (mcp_server_active) {
|
|
90
|
+
fetchMcpTools();
|
|
91
|
+
}
|
|
92
|
+
}).catch(() => {
|
|
93
|
+
mcp_server_active = false;
|
|
94
|
+
});
|
|
65
95
|
return () => {
|
|
66
96
|
document.body.style.overflow = "auto";
|
|
67
97
|
};
|
|
@@ -71,26 +101,30 @@ onMount(() => {
|
|
|
71
101
|
{#if info}
|
|
72
102
|
{#if api_count}
|
|
73
103
|
<div class="banner-wrap">
|
|
74
|
-
<ApiBanner
|
|
104
|
+
<ApiBanner
|
|
105
|
+
on:close
|
|
106
|
+
root={space_id || root}
|
|
107
|
+
{api_count}
|
|
108
|
+
{current_language}
|
|
109
|
+
/>
|
|
75
110
|
</div>
|
|
76
111
|
|
|
77
112
|
<div class="docs-wrap">
|
|
78
113
|
<div class="client-doc">
|
|
79
114
|
<p style="font-size: var(--text-lg);">
|
|
80
|
-
Choose
|
|
81
|
-
API.
|
|
115
|
+
Choose one of the following ways to interact with the API.
|
|
82
116
|
</p>
|
|
83
117
|
</div>
|
|
84
118
|
<div class="endpoint">
|
|
85
119
|
<div class="snippets">
|
|
86
|
-
{#each langs as [language, img]}
|
|
120
|
+
{#each langs as [language, display_name, img]}
|
|
87
121
|
<li
|
|
88
122
|
class="snippet
|
|
89
123
|
{current_language === language ? 'current-lang' : 'inactive-lang'}"
|
|
90
124
|
on:click={() => (current_language = language)}
|
|
91
125
|
>
|
|
92
126
|
<img src={img} alt="" />
|
|
93
|
-
{
|
|
127
|
+
{display_name}
|
|
94
128
|
</li>
|
|
95
129
|
{/each}
|
|
96
130
|
</div>
|
|
@@ -139,100 +173,256 @@ onMount(() => {
|
|
|
139
173
|
href={current_language == "python" ? py_docs : js_docs}
|
|
140
174
|
target="_blank">docs</a
|
|
141
175
|
>) if you don't already have it installed.
|
|
176
|
+
{:else if current_language == "mcp"}
|
|
177
|
+
{#if mcp_server_active}
|
|
178
|
+
<Block>
|
|
179
|
+
<div class="mcp-url">
|
|
180
|
+
<label
|
|
181
|
+
><span class="status-indicator active">●</span>MCP Server
|
|
182
|
+
URL</label
|
|
183
|
+
>
|
|
184
|
+
<div class="textbox">
|
|
185
|
+
<input type="text" readonly value={mcp_server_url} />
|
|
186
|
+
<CopyButton code={mcp_server_url} />
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</Block>
|
|
190
|
+
<p> </p>
|
|
191
|
+
<strong>Available MCP Tools</strong>
|
|
192
|
+
<div class="mcp-tools">
|
|
193
|
+
{#each tools as tool}
|
|
194
|
+
<div class="tool-item">
|
|
195
|
+
<button
|
|
196
|
+
class="tool-header"
|
|
197
|
+
on:click={() => (tool.expanded = !tool.expanded)}
|
|
198
|
+
>
|
|
199
|
+
<span
|
|
200
|
+
><span class="tool-name">{tool.name}</span>
|
|
201
|
+
<span class="tool-description"
|
|
202
|
+
>{tool.description
|
|
203
|
+
? tool.description
|
|
204
|
+
: "⚠︎ No description provided in function docstring"}</span
|
|
205
|
+
></span
|
|
206
|
+
>
|
|
207
|
+
<span class="tool-arrow"
|
|
208
|
+
>{tool.expanded ? "▼" : "▶"}</span
|
|
209
|
+
>
|
|
210
|
+
</button>
|
|
211
|
+
{#if tool.expanded}
|
|
212
|
+
<div class="tool-content">
|
|
213
|
+
{#if Object.keys(tool.parameters).length > 0}
|
|
214
|
+
<div class="tool-parameters">
|
|
215
|
+
{#if Object.keys(tool.parameters).length > 0}
|
|
216
|
+
{#each Object.entries(tool.parameters) as [name, param]}
|
|
217
|
+
<div class="parameter">
|
|
218
|
+
<code>{name}</code>
|
|
219
|
+
<span class="parameter-type"
|
|
220
|
+
>({param.type})</span
|
|
221
|
+
>
|
|
222
|
+
<p class="parameter-description">
|
|
223
|
+
{param.description
|
|
224
|
+
? param.description
|
|
225
|
+
: "⚠︎ No description for this parameter in function docstring"}
|
|
226
|
+
</p>
|
|
227
|
+
</div>
|
|
228
|
+
{/each}
|
|
229
|
+
{:else}
|
|
230
|
+
<p>No parameters</p>
|
|
231
|
+
{/if}
|
|
232
|
+
</div>
|
|
233
|
+
{/if}
|
|
234
|
+
</div>
|
|
235
|
+
{/if}
|
|
236
|
+
</div>
|
|
237
|
+
{/each}
|
|
238
|
+
</div>
|
|
239
|
+
<p> </p>
|
|
240
|
+
|
|
241
|
+
<strong>Integration</strong>: To add this MCP to clients that
|
|
242
|
+
support SSE (e.g. Cursor, Windsurf, Cline), simply add the
|
|
243
|
+
following configuration to your MCP config:
|
|
244
|
+
<p> </p>
|
|
245
|
+
<Block>
|
|
246
|
+
<code>
|
|
247
|
+
<div class="copy">
|
|
248
|
+
<CopyButton
|
|
249
|
+
code={JSON.stringify(
|
|
250
|
+
{
|
|
251
|
+
mcpServers: {
|
|
252
|
+
gradio: {
|
|
253
|
+
url: mcp_server_url
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
null,
|
|
258
|
+
2
|
|
259
|
+
)}
|
|
260
|
+
/>
|
|
261
|
+
</div>
|
|
262
|
+
<div>
|
|
263
|
+
<pre>{JSON.stringify(
|
|
264
|
+
{
|
|
265
|
+
mcpServers: {
|
|
266
|
+
gradio: {
|
|
267
|
+
url: mcp_server_url
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
null,
|
|
272
|
+
2
|
|
273
|
+
)}</pre>
|
|
274
|
+
</div>
|
|
275
|
+
</code>
|
|
276
|
+
</Block>
|
|
277
|
+
<p> </p>
|
|
278
|
+
<em>Experimental stdio support</em>: For clients that only
|
|
279
|
+
support stdio, first
|
|
280
|
+
<a href="https://nodejs.org/en/download/" target="_blank"
|
|
281
|
+
>install Node.js</a
|
|
282
|
+
>. Then, you can use the following command:
|
|
283
|
+
<p> </p>
|
|
284
|
+
<Block>
|
|
285
|
+
<code>
|
|
286
|
+
<div class="copy">
|
|
287
|
+
<CopyButton
|
|
288
|
+
code={JSON.stringify(
|
|
289
|
+
{
|
|
290
|
+
mcpServers: {
|
|
291
|
+
gradio: {
|
|
292
|
+
command: "npx",
|
|
293
|
+
args: ["mcp-remote", mcp_server_url]
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
null,
|
|
298
|
+
2
|
|
299
|
+
)}
|
|
300
|
+
/>
|
|
301
|
+
</div>
|
|
302
|
+
<div>
|
|
303
|
+
<pre>{JSON.stringify(
|
|
304
|
+
{
|
|
305
|
+
mcpServers: {
|
|
306
|
+
gradio: {
|
|
307
|
+
command: "npx",
|
|
308
|
+
arguments: ["mcp-remote", mcp_server_url]
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
null,
|
|
313
|
+
2
|
|
314
|
+
)}</pre>
|
|
315
|
+
</div>
|
|
316
|
+
</code>
|
|
317
|
+
</Block>
|
|
318
|
+
<p> </p>
|
|
319
|
+
<p> </p>
|
|
320
|
+
{:else}
|
|
321
|
+
This Gradio app can also serve as an MCP server, with an MCP
|
|
322
|
+
tool corresponding to each API endpoint. To enable this, launch
|
|
323
|
+
this Gradio app with <code>.launch(mcp_server=True)</code> or
|
|
324
|
+
set the <code>GRADIO_MCP_SERVER</code> env variable to
|
|
325
|
+
<code>"True"</code>.
|
|
326
|
+
{/if}
|
|
142
327
|
{:else}
|
|
143
328
|
1. Confirm that you have cURL installed on your system.
|
|
144
329
|
{/if}
|
|
145
330
|
</p>
|
|
146
331
|
|
|
147
|
-
|
|
332
|
+
{#if current_language !== "mcp"}
|
|
333
|
+
<InstallSnippet {current_language} />
|
|
148
334
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
Or use the
|
|
165
|
-
<Button
|
|
166
|
-
size="sm"
|
|
167
|
-
variant="secondary"
|
|
168
|
-
on:click={() => dispatch("close", { api_recorder_visible: true })}
|
|
169
|
-
>
|
|
170
|
-
<div class="loading-dot"></div>
|
|
171
|
-
<p class="self-baseline">API Recorder</p>
|
|
172
|
-
</Button>
|
|
173
|
-
to automatically generate your API requests.
|
|
174
|
-
{#if current_language == "bash"}<br /> <br />Making a
|
|
175
|
-
prediction and getting a result requires
|
|
176
|
-
<strong>2 requests</strong>: a
|
|
177
|
-
<code>POST</code>
|
|
178
|
-
and a <code>GET</code> request. The <code>POST</code> request
|
|
179
|
-
returns an <code>EVENT_ID</code>, which is used in the second
|
|
180
|
-
<code>GET</code> request to fetch the results. In these snippets,
|
|
181
|
-
we've used <code>awk</code> and <code>read</code> to parse the
|
|
182
|
-
results, combining these two requests into one command for ease of
|
|
183
|
-
use. {#if username !== null}
|
|
184
|
-
Note: connecting to an authenticated app requires an additional
|
|
185
|
-
request.{/if} See
|
|
186
|
-
<a href={bash_docs} target="_blank">curl docs</a>.
|
|
187
|
-
{/if}
|
|
335
|
+
<p class="padded">
|
|
336
|
+
2. Find the API endpoint below corresponding to your desired
|
|
337
|
+
function in the app. Copy the code snippet, replacing the
|
|
338
|
+
placeholder values with your own input data.
|
|
339
|
+
{#if space_id}If this is a private Space, you may need to pass
|
|
340
|
+
your Hugging Face token as well (<a
|
|
341
|
+
href={current_language == "python"
|
|
342
|
+
? py_docs + spaces_docs_suffix
|
|
343
|
+
: current_language == "javascript"
|
|
344
|
+
? js_docs + spaces_docs_suffix
|
|
345
|
+
: bash_docs}
|
|
346
|
+
class="underline"
|
|
347
|
+
target="_blank">read more</a
|
|
348
|
+
>).{/if}
|
|
188
349
|
|
|
189
|
-
|
|
350
|
+
Or use the
|
|
351
|
+
<Button
|
|
352
|
+
size="sm"
|
|
353
|
+
variant="secondary"
|
|
354
|
+
on:click={() =>
|
|
355
|
+
dispatch("close", { api_recorder_visible: true })}
|
|
356
|
+
>
|
|
357
|
+
<div class="loading-dot"></div>
|
|
358
|
+
<p class="self-baseline">API Recorder</p>
|
|
359
|
+
</Button>
|
|
360
|
+
to automatically generate your API requests.
|
|
361
|
+
{#if current_language == "bash"}<br /> <br />Making a
|
|
362
|
+
prediction and getting a result requires
|
|
363
|
+
<strong>2 requests</strong>: a
|
|
364
|
+
<code>POST</code>
|
|
365
|
+
and a <code>GET</code> request. The <code>POST</code> request
|
|
366
|
+
returns an <code>EVENT_ID</code>, which is used in the second
|
|
367
|
+
<code>GET</code> request to fetch the results. In these
|
|
368
|
+
snippets, we've used <code>awk</code> and <code>read</code> to
|
|
369
|
+
parse the results, combining these two requests into one command
|
|
370
|
+
for ease of use. {#if username !== null}
|
|
371
|
+
Note: connecting to an authenticated app requires an
|
|
372
|
+
additional request.{/if} See
|
|
373
|
+
<a href={bash_docs} target="_blank">curl docs</a>.
|
|
374
|
+
{/if}
|
|
375
|
+
|
|
376
|
+
<!-- <span
|
|
190
377
|
id="api-recorder"
|
|
191
378
|
on:click={() => dispatch("close", { api_recorder_visible: true })}
|
|
192
379
|
>🪄 API Recorder</span
|
|
193
380
|
> to automatically generate your API requests! -->
|
|
194
|
-
|
|
381
|
+
</p>
|
|
382
|
+
{/if}
|
|
195
383
|
{/if}
|
|
196
384
|
|
|
197
|
-
{#
|
|
198
|
-
{#
|
|
199
|
-
|
|
200
|
-
<
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
.
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
385
|
+
{#if current_language !== "mcp"}
|
|
386
|
+
{#each dependencies as dependency, dependency_index}
|
|
387
|
+
{#if dependency.show_api && info.named_endpoints["/" + dependency.api_name]}
|
|
388
|
+
<div class="endpoint-container">
|
|
389
|
+
<CodeSnippet
|
|
390
|
+
named={true}
|
|
391
|
+
endpoint_parameters={info.named_endpoints[
|
|
392
|
+
"/" + dependency.api_name
|
|
393
|
+
].parameters}
|
|
394
|
+
{dependency}
|
|
395
|
+
{dependency_index}
|
|
396
|
+
{current_language}
|
|
397
|
+
{root}
|
|
398
|
+
{space_id}
|
|
399
|
+
{username}
|
|
400
|
+
api_prefix={app.api_prefix}
|
|
401
|
+
/>
|
|
402
|
+
|
|
403
|
+
<ParametersSnippet
|
|
404
|
+
endpoint_returns={info.named_endpoints[
|
|
405
|
+
"/" + dependency.api_name
|
|
406
|
+
].parameters}
|
|
407
|
+
js_returns={js_info.named_endpoints["/" + dependency.api_name]
|
|
408
|
+
.parameters}
|
|
409
|
+
{is_running}
|
|
410
|
+
{current_language}
|
|
411
|
+
/>
|
|
412
|
+
|
|
413
|
+
<ResponseSnippet
|
|
414
|
+
endpoint_returns={info.named_endpoints[
|
|
415
|
+
"/" + dependency.api_name
|
|
416
|
+
].returns}
|
|
417
|
+
js_returns={js_info.named_endpoints["/" + dependency.api_name]
|
|
418
|
+
.returns}
|
|
419
|
+
{is_running}
|
|
420
|
+
{current_language}
|
|
421
|
+
/>
|
|
422
|
+
</div>
|
|
423
|
+
{/if}
|
|
424
|
+
{/each}
|
|
425
|
+
{/if}
|
|
236
426
|
</div>
|
|
237
427
|
</div>
|
|
238
428
|
{:else}
|
|
@@ -306,7 +496,6 @@ onMount(() => {
|
|
|
306
496
|
color: var(--body-text-color);
|
|
307
497
|
line-height: 1;
|
|
308
498
|
user-select: none;
|
|
309
|
-
text-transform: capitalize;
|
|
310
499
|
}
|
|
311
500
|
|
|
312
501
|
.current-lang {
|
|
@@ -389,4 +578,173 @@ onMount(() => {
|
|
|
389
578
|
font-family: var(--font-mono);
|
|
390
579
|
font-size: var(--text-md);
|
|
391
580
|
}
|
|
581
|
+
|
|
582
|
+
code pre {
|
|
583
|
+
overflow-x: auto;
|
|
584
|
+
color: var(--body-text-color);
|
|
585
|
+
font-family: var(--font-mono);
|
|
586
|
+
tab-size: 2;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
.token.string {
|
|
590
|
+
display: contents;
|
|
591
|
+
color: var(--color-accent-base);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
.copy {
|
|
595
|
+
position: absolute;
|
|
596
|
+
top: 0;
|
|
597
|
+
right: 0;
|
|
598
|
+
margin-top: 5px;
|
|
599
|
+
margin-right: 5px;
|
|
600
|
+
z-index: 10;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.container {
|
|
604
|
+
display: flex;
|
|
605
|
+
flex-direction: column;
|
|
606
|
+
gap: var(--spacing-xxl);
|
|
607
|
+
margin-top: var(--size-3);
|
|
608
|
+
margin-bottom: var(--size-3);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.desc {
|
|
612
|
+
color: var(--body-text-color-subdued);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.api-name {
|
|
616
|
+
color: var(--color-accent);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.mcp-url {
|
|
620
|
+
padding: var(--size-2);
|
|
621
|
+
position: relative;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.mcp-url label {
|
|
625
|
+
display: block;
|
|
626
|
+
margin-bottom: var(--size-2);
|
|
627
|
+
font-weight: 600;
|
|
628
|
+
color: var(--body-text-color);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.mcp-url .textbox {
|
|
632
|
+
display: flex;
|
|
633
|
+
align-items: center;
|
|
634
|
+
gap: var(--size-2);
|
|
635
|
+
border: 1px solid var(--border-color-primary);
|
|
636
|
+
border-radius: var(--radius-sm);
|
|
637
|
+
padding: var(--size-2);
|
|
638
|
+
background: var(--background-fill-primary);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.mcp-url input {
|
|
642
|
+
flex: 1;
|
|
643
|
+
border: none;
|
|
644
|
+
background: none;
|
|
645
|
+
color: var(--body-text-color);
|
|
646
|
+
font-family: var(--font-mono);
|
|
647
|
+
font-size: var(--text-md);
|
|
648
|
+
width: 100%;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
.mcp-url input:focus {
|
|
652
|
+
outline: none;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.status-indicator {
|
|
656
|
+
display: inline-block;
|
|
657
|
+
margin-right: var(--size-1-5);
|
|
658
|
+
position: relative;
|
|
659
|
+
top: -1px;
|
|
660
|
+
font-size: 0.8em;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.status-indicator.active {
|
|
664
|
+
color: #4caf50;
|
|
665
|
+
animation: pulse 1s infinite;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
@keyframes pulse {
|
|
669
|
+
0% {
|
|
670
|
+
opacity: 1;
|
|
671
|
+
}
|
|
672
|
+
50% {
|
|
673
|
+
opacity: 0.6;
|
|
674
|
+
}
|
|
675
|
+
100% {
|
|
676
|
+
opacity: 1;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.mcp-tools {
|
|
681
|
+
margin-top: var(--size-4);
|
|
682
|
+
border: 1px solid var(--border-color-primary);
|
|
683
|
+
border-radius: var(--radius-md);
|
|
684
|
+
overflow: hidden;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
.tool-item {
|
|
688
|
+
border-bottom: 1px solid var(--border-color-primary);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
.tool-item:last-child {
|
|
692
|
+
border-bottom: none;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
.tool-header {
|
|
696
|
+
width: 100%;
|
|
697
|
+
display: flex;
|
|
698
|
+
justify-content: space-between;
|
|
699
|
+
align-items: center;
|
|
700
|
+
padding: var(--size-3);
|
|
701
|
+
background: var(--background-fill-primary);
|
|
702
|
+
border: none;
|
|
703
|
+
cursor: pointer;
|
|
704
|
+
text-align: left;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
.tool-header:hover {
|
|
708
|
+
background: var(--background-fill-secondary);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
.tool-name {
|
|
712
|
+
font-family: var(--font-mono);
|
|
713
|
+
font-weight: 600;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
.tool-arrow {
|
|
717
|
+
color: var(--body-text-color-subdued);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
.tool-content {
|
|
721
|
+
padding: var(--size-3);
|
|
722
|
+
background: var(--background-fill-secondary);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
.tool-description {
|
|
726
|
+
margin-bottom: var(--size-3);
|
|
727
|
+
color: var(--body-text-color);
|
|
728
|
+
}
|
|
729
|
+
.parameter {
|
|
730
|
+
margin-bottom: var(--size-2);
|
|
731
|
+
padding: var(--size-2);
|
|
732
|
+
background: var(--background-fill-primary);
|
|
733
|
+
border-radius: var(--radius-sm);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
.parameter code {
|
|
737
|
+
font-weight: 600;
|
|
738
|
+
color: var(--color-accent);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
.parameter-type {
|
|
742
|
+
color: var(--body-text-color-subdued);
|
|
743
|
+
margin-left: var(--size-1);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.parameter-description {
|
|
747
|
+
margin-top: var(--size-1);
|
|
748
|
+
color: var(--body-text-color);
|
|
749
|
+
}
|
|
392
750
|
</style>
|