freddyaboulton HF Staff commited on
Commit
7e2bf9a
·
verified ·
1 Parent(s): 981fa65

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +8 -8
  2. run.ipynb +1 -0
  3. run.py +134 -0
README.md CHANGED
@@ -1,12 +1,12 @@
 
1
  ---
2
- title: Mcp Image App
3
- emoji: 🏢
4
- colorFrom: gray
5
- colorTo: gray
6
  sdk: gradio
7
- sdk_version: 5.49.1
8
- app_file: app.py
9
  pinned: false
 
10
  ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+
2
  ---
3
+ title: mcp_image_app
4
+ emoji: 🔥
5
+ colorFrom: indigo
6
+ colorTo: indigo
7
  sdk: gradio
8
+ sdk_version: 6.0.0
9
+ app_file: run.py
10
  pinned: false
11
+ hf_oauth: true
12
  ---
 
 
run.ipynb ADDED
@@ -0,0 +1 @@
 
 
1
+ {"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: mcp_image_app"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "import tempfile\n", "from PIL import Image\n", "import numpy as np\n", "\n", "\n", "@gr.mcp.tool(\n", " _meta={\n", " \"openai/outputTemplate\": \"ui://widget/app.html\",\n", " \"openai/resultCanProduceWidget\": True,\n", " \"openai/widgetAccessible\": True,\n", " }\n", ")\n", "\n", "def power_law_image(input_path: str, gamma: float = 0.5) -> str:\n", " \"\"\"\n", " Applies a power-law (gamma) transformation to an image file and saves\n", " the result to a temporary file.\n", "\n", " Args:\n", " input_path (str): Path to the input image.\n", " gamma (float): Power-law exponent. <1 brightens, >1 darkens.\n", "\n", " Returns:\n", " str: Path to the saved temporary output image.\n", " \"\"\"\n", " img = Image.open(input_path).convert(\"RGB\")\n", " arr = np.array(img, dtype=np.float32) / 255.0\n", " arr = np.power(arr, gamma)\n", " arr = np.clip(arr * 255, 0, 255).astype(np.uint8)\n", " out_img = Image.fromarray(arr)\n", "\n", " tmp_file = tempfile.NamedTemporaryFile(delete=False, suffix=\".png\")\n", " out_img.save(tmp_file.name)\n", " tmp_file.close()\n", "\n", " return tmp_file.name\n", "\n", "\n", "@gr.mcp.resource(\"ui://widget/app.html\", mime_type=\"text/html+skybridge\")\n", "def app_html():\n", " visual = \"\"\"\n", " <style>\n", " #image-container {\n", " position: relative;\n", " display: inline-block;\n", " max-width: 100%;\n", " }\n", " #image-display {\n", " max-width: 100%;\n", " height: auto;\n", " display: block;\n", " border-radius: 8px;\n", " }\n", " #brighten-btn {\n", " position: absolute;\n", " bottom: 16px;\n", " right: 26px;\n", " padding: 12px 24px;\n", " background: #1a1a1a;\n", " color: white;\n", " border: none;\n", " border-radius: 8px;\n", " font-weight: 600;\n", " cursor: pointer;\n", " box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n", " }\n", " #brighten-btn:hover {\n", " background: #000000;\n", " }\n", " </style>\n", " <div id=\"image-container\">\n", " <img id=\"image-display\" alt=\"Processed image\" />\n", " <button id=\"brighten-btn\">Brighten</button>\n", " </div>\n", " <script>\n", " const imageEl = document.getElementById('image-display');\n", " const btnEl = document.getElementById('brighten-btn');\n", "\n", " function extractImageUrl(data) {\n", " if (data?.text?.startsWith('Image URL: ')) {\n", " return data.text.substring('Image URL: '.length).trim();\n", " }\n", " if (data?.content) {\n", " for (const item of data.content) {\n", " if (item.type === 'text' && item.text?.startsWith('Image URL: ')) {\n", " return item.text.substring('Image URL: '.length).trim();\n", " }\n", " }\n", " }\n", " }\n", "\n", " function render() {\n", " const url = extractImageUrl(window.openai?.toolOutput);\n", " if (url) imageEl.src = url;\n", " }\n", "\n", " async function brightenImage() {\n", " btnEl.disabled = true;\n", " btnEl.textContent = 'Brightening...';\n", " const result = await window.openai.callTool('power_law_image', {\n", " input_path: imageEl.src\n", " });\n", " const newUrl = extractImageUrl(result);\n", " if (newUrl) imageEl.src = newUrl;\n", " btnEl.disabled = false;\n", " btnEl.textContent = 'Brighten';\n", " }\n", "\n", " btnEl.addEventListener('click', brightenImage);\n", " window.addEventListener(\"openai:set_globals\", (event) => {\n", " if (event.detail?.globals?.toolOutput) render();\n", " }, { passive: true });\n", "\n", " render();\n", " </script>\n", " \"\"\"\n", " return visual\n", "\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " with gr.Column():\n", " original_image = gr.Image(label=\"Original Image\", type=\"filepath\")\n", " btn = gr.Button(\"Brighten Image\")\n", " with gr.Column():\n", " output_image = gr.Image(label=\"Output Image\", type=\"filepath\")\n", " html = gr.Code(language=\"html\", max_lines=20)\n", "\n", " btn.click(power_law_image, inputs=original_image, outputs=original_image)\n", " btn.click(app_html, outputs=html)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch(mcp_server=True, share=True)\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
run.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import tempfile
3
+ from PIL import Image
4
+ import numpy as np
5
+
6
+
7
+ @gr.mcp.tool(
8
+ _meta={
9
+ "openai/outputTemplate": "ui://widget/app.html",
10
+ "openai/resultCanProduceWidget": True,
11
+ "openai/widgetAccessible": True,
12
+ }
13
+ )
14
+
15
+ def power_law_image(input_path: str, gamma: float = 0.5) -> str:
16
+ """
17
+ Applies a power-law (gamma) transformation to an image file and saves
18
+ the result to a temporary file.
19
+
20
+ Args:
21
+ input_path (str): Path to the input image.
22
+ gamma (float): Power-law exponent. <1 brightens, >1 darkens.
23
+
24
+ Returns:
25
+ str: Path to the saved temporary output image.
26
+ """
27
+ img = Image.open(input_path).convert("RGB")
28
+ arr = np.array(img, dtype=np.float32) / 255.0
29
+ arr = np.power(arr, gamma)
30
+ arr = np.clip(arr * 255, 0, 255).astype(np.uint8)
31
+ out_img = Image.fromarray(arr)
32
+
33
+ tmp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
34
+ out_img.save(tmp_file.name)
35
+ tmp_file.close()
36
+
37
+ return tmp_file.name
38
+
39
+
40
+ @gr.mcp.resource("ui://widget/app.html", mime_type="text/html+skybridge")
41
+ def app_html():
42
+ visual = """
43
+ <style>
44
+ #image-container {
45
+ position: relative;
46
+ display: inline-block;
47
+ max-width: 100%;
48
+ }
49
+ #image-display {
50
+ max-width: 100%;
51
+ height: auto;
52
+ display: block;
53
+ border-radius: 8px;
54
+ }
55
+ #brighten-btn {
56
+ position: absolute;
57
+ bottom: 16px;
58
+ right: 26px;
59
+ padding: 12px 24px;
60
+ background: #1a1a1a;
61
+ color: white;
62
+ border: none;
63
+ border-radius: 8px;
64
+ font-weight: 600;
65
+ cursor: pointer;
66
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
67
+ }
68
+ #brighten-btn:hover {
69
+ background: #000000;
70
+ }
71
+ </style>
72
+ <div id="image-container">
73
+ <img id="image-display" alt="Processed image" />
74
+ <button id="brighten-btn">Brighten</button>
75
+ </div>
76
+ <script>
77
+ const imageEl = document.getElementById('image-display');
78
+ const btnEl = document.getElementById('brighten-btn');
79
+
80
+ function extractImageUrl(data) {
81
+ if (data?.text?.startsWith('Image URL: ')) {
82
+ return data.text.substring('Image URL: '.length).trim();
83
+ }
84
+ if (data?.content) {
85
+ for (const item of data.content) {
86
+ if (item.type === 'text' && item.text?.startsWith('Image URL: ')) {
87
+ return item.text.substring('Image URL: '.length).trim();
88
+ }
89
+ }
90
+ }
91
+ }
92
+
93
+ function render() {
94
+ const url = extractImageUrl(window.openai?.toolOutput);
95
+ if (url) imageEl.src = url;
96
+ }
97
+
98
+ async function brightenImage() {
99
+ btnEl.disabled = true;
100
+ btnEl.textContent = 'Brightening...';
101
+ const result = await window.openai.callTool('power_law_image', {
102
+ input_path: imageEl.src
103
+ });
104
+ const newUrl = extractImageUrl(result);
105
+ if (newUrl) imageEl.src = newUrl;
106
+ btnEl.disabled = false;
107
+ btnEl.textContent = 'Brighten';
108
+ }
109
+
110
+ btnEl.addEventListener('click', brightenImage);
111
+ window.addEventListener("openai:set_globals", (event) => {
112
+ if (event.detail?.globals?.toolOutput) render();
113
+ }, { passive: true });
114
+
115
+ render();
116
+ </script>
117
+ """
118
+ return visual
119
+
120
+
121
+ with gr.Blocks() as demo:
122
+ with gr.Row():
123
+ with gr.Column():
124
+ original_image = gr.Image(label="Original Image", type="filepath")
125
+ btn = gr.Button("Brighten Image")
126
+ with gr.Column():
127
+ output_image = gr.Image(label="Output Image", type="filepath")
128
+ html = gr.Code(language="html", max_lines=20)
129
+
130
+ btn.click(power_law_image, inputs=original_image, outputs=original_image)
131
+ btn.click(app_html, outputs=html)
132
+
133
+ if __name__ == "__main__":
134
+ demo.launch(mcp_server=True, share=True)