ocx2025 commited on
Commit
bd180df
Β·
1 Parent(s): 0794bda
DOCKER_VS_GRADIO.md ADDED
@@ -0,0 +1,412 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Docker vs Gradio Deployment Guide
2
+
3
+ ## Overview
4
+
5
+ This document explains the difference between Docker and Gradio deployments on Hugging Face Spaces and why we chose Docker for your MCP server.
6
+
7
+ ## Quick Comparison
8
+
9
+ ```
10
+ Gradio: Python Code β†’ Automatic UI β†’ Human Users
11
+ Docker: Dockerfile β†’ Custom Server β†’ API Clients (AI, Apps, Services)
12
+ ```
13
+
14
+ ## Your Current Setup (Docker)
15
+
16
+ ### Architecture
17
+ ```
18
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
19
+ β”‚ Hugging Face Space β”‚
20
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
21
+ β”‚ β”‚ Docker Container β”‚ β”‚
22
+ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
23
+ β”‚ β”‚ β”‚ FastAPI Server β”‚ β”‚ β”‚
24
+ β”‚ β”‚ β”‚ (app.py) β”‚ β”‚ β”‚
25
+ β”‚ β”‚ β”‚ Port: 7860 β”‚ β”‚ β”‚
26
+ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
27
+ β”‚ β”‚ β”‚ REST API Endpoints: β”‚ β”‚ β”‚
28
+ β”‚ β”‚ β”‚ - GET /health β”‚ β”‚ β”‚
29
+ β”‚ β”‚ β”‚ - GET / β”‚ β”‚ β”‚
30
+ β”‚ β”‚ β”‚ - GET /tools β”‚ β”‚ β”‚
31
+ β”‚ β”‚ β”‚ - POST /search β”‚ β”‚ β”‚
32
+ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
33
+ β”‚ β”‚ ↓ β”‚ β”‚
34
+ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
35
+ β”‚ β”‚ β”‚ MCP Server (server.py) β”‚ β”‚ β”‚
36
+ β”‚ β”‚ β”‚ YouTube Search Tool β”‚ β”‚ β”‚
37
+ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
38
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
39
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
40
+ ↓
41
+ REST API Calls
42
+ ↓
43
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
44
+ β”‚ AI Assistants β”‚
45
+ β”‚ Python Scripts β”‚
46
+ β”‚ Web Apps β”‚
47
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
48
+ ```
49
+
50
+ ### Access Methods
51
+ ```bash
52
+ # API Access (Programmatic)
53
+ curl -X POST https://your-space.hf.space/search \
54
+ -d '{"query": "Python"}'
55
+
56
+ # Python Integration
57
+ import requests
58
+ response = requests.post(
59
+ "https://your-space.hf.space/search",
60
+ json={"query": "Python", "max_results": 5}
61
+ )
62
+
63
+ # Claude Desktop Integration
64
+ # Add to MCP settings and use via chat
65
+ ```
66
+
67
+ ## Alternative: Gradio Approach
68
+
69
+ ### What It Would Look Like
70
+ ```
71
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
72
+ β”‚ Hugging Face Space β”‚
73
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
74
+ β”‚ β”‚ Gradio App β”‚ β”‚
75
+ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
76
+ β”‚ β”‚ β”‚ Auto-generated UI: β”‚ β”‚ β”‚
77
+ β”‚ β”‚ β”‚ [Search Query ] β”‚ β”‚ β”‚
78
+ β”‚ β”‚ β”‚ [Max Results: 5 ] β”‚ β”‚ β”‚
79
+ β”‚ β”‚ β”‚ [ Search Button ] β”‚ β”‚ β”‚
80
+ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
81
+ β”‚ β”‚ β”‚ Results: β”‚ β”‚ β”‚
82
+ β”‚ β”‚ β”‚ { ... json ... } β”‚ β”‚ β”‚
83
+ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
84
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
85
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
86
+ ↓
87
+ Browser Access Only
88
+ ↓
89
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
90
+ β”‚ Human Users β”‚
91
+ β”‚ (via browser) β”‚
92
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
93
+ ```
94
+
95
+ ### Gradio Code Example
96
+ ```python
97
+ # app.py for Gradio
98
+ import gradio as gr
99
+ from server import search_youtube_videos
100
+
101
+ def search(query, max_results):
102
+ return search_youtube_videos(query, max_results)
103
+
104
+ demo = gr.Interface(
105
+ fn=search,
106
+ inputs=[
107
+ gr.Textbox(label="Search Query"),
108
+ gr.Slider(1, 10, value=5, label="Max Results")
109
+ ],
110
+ outputs=gr.JSON()
111
+ )
112
+
113
+ demo.launch()
114
+ ```
115
+
116
+ ### README.md for Gradio
117
+ ```yaml
118
+ ---
119
+ title: Basicsearch
120
+ sdk: gradio
121
+ sdk_version: 5.49.1
122
+ app_file: app.py
123
+ ---
124
+ ```
125
+
126
+ ## Feature Comparison Matrix
127
+
128
+ | Feature | Docker (Your Choice) | Gradio Alternative |
129
+ |---------|---------------------|-------------------|
130
+ | **Primary Use Case** | API Service | Interactive Demo |
131
+ | **Target Users** | AI systems, apps | Humans via browser |
132
+ | **Setup Complexity** | Medium | Low |
133
+ | **Build Time** | 5-15 minutes | 2-3 minutes |
134
+ | **Deployment Method** | Dockerfile | Python script |
135
+ | | | |
136
+ | **Capabilities** | | |
137
+ | REST API | βœ… Full | ⚠️ Limited |
138
+ | Custom Server | βœ… Yes | ❌ No |
139
+ | MCP Protocol | βœ… Yes | ❌ No |
140
+ | Visual UI | ⚠️ Manual | βœ… Automatic |
141
+ | WebSocket Support | βœ… Yes | ⚠️ Limited |
142
+ | Multiple Endpoints | βœ… Yes | ⚠️ Limited |
143
+ | | | |
144
+ | **Integration** | | |
145
+ | cURL/HTTP Clients | βœ… Yes | ⚠️ Limited |
146
+ | Python requests | βœ… Yes | ⚠️ Via API |
147
+ | JavaScript fetch | βœ… Yes | ⚠️ Via API |
148
+ | Claude Desktop | βœ… Yes | ❌ No |
149
+ | Mobile Apps | βœ… Yes | ⚠️ Browser only |
150
+ | | | |
151
+ | **Control & Flexibility** | | |
152
+ | Custom Dependencies | βœ… Full control | ⚠️ Limited |
153
+ | Port Configuration | βœ… Yes | ⚠️ Fixed |
154
+ | Environment Variables | βœ… Full access | βœ… Yes |
155
+ | Multi-container | βœ… Possible | ❌ No |
156
+ | Background Tasks | βœ… Yes | ⚠️ Limited |
157
+ | | | |
158
+ | **Development** | | |
159
+ | Local Testing | βœ… Docker/Direct | βœ… Easy |
160
+ | Debugging | ⚠️ Moderate | βœ… Easy |
161
+ | Hot Reload | ⚠️ Manual | βœ… Automatic |
162
+ | Custom Middleware | βœ… Yes | ❌ No |
163
+
164
+ ## Why Docker for Your MCP Server?
165
+
166
+ ### βœ… Reasons We Chose Docker
167
+
168
+ 1. **MCP Protocol Support**
169
+ - MCP servers need custom server implementations
170
+ - Gradio doesn't support STDIO protocol
171
+ - Need full control over server lifecycle
172
+
173
+ 2. **API-First Design**
174
+ - Your service is consumed by AI assistants, not humans
175
+ - REST API is the primary interface
176
+ - Need multiple endpoints with different methods
177
+
178
+ 3. **Production Requirements**
179
+ - Needs to be reliable and production-ready
180
+ - Full control over dependencies and environment
181
+ - Better error handling and logging
182
+
183
+ 4. **Integration Flexibility**
184
+ - Can be called from any language/platform
185
+ - Works with Claude Desktop MCP settings
186
+ - Compatible with future integrations
187
+
188
+ 5. **Scalability**
189
+ - Can add more endpoints easily
190
+ - Can add authentication/rate limiting
191
+ - Can integrate with databases or other services
192
+
193
+ ### ❌ Why Gradio Wasn't Suitable
194
+
195
+ 1. **No API Focus**
196
+ - Gradio is UI-first, API-second
197
+ - Limited REST API capabilities
198
+ - Harder to integrate programmatically
199
+
200
+ 2. **Limited Server Control**
201
+ - Can't customize server behavior
202
+ - Fixed port and route structure
203
+ - Limited middleware support
204
+
205
+ 3. **Not Designed for Services**
206
+ - Designed for demos, not production services
207
+ - MCP protocol requires custom setup
208
+ - No native support for service-to-service communication
209
+
210
+ ## Hybrid Approach (Best of Both Worlds!)
211
+
212
+ You can actually use **BOTH** Docker and Gradio:
213
+
214
+ ### Option 1: Gradio as Testing UI
215
+ ```dockerfile
216
+ # Dockerfile
217
+ FROM python:3.13-slim
218
+
219
+ # Install all dependencies
220
+ RUN pip install fastapi uvicorn gradio
221
+
222
+ # Copy both apps
223
+ COPY app.py ./ # FastAPI server
224
+ COPY gradio_ui.py ./ # Gradio UI
225
+
226
+ # Run both (using a process manager)
227
+ CMD ["python", "app.py"] # Main API
228
+ # Access Gradio separately for testing
229
+ ```
230
+
231
+ ### Option 2: Separate Spaces
232
+ ```
233
+ Space 1 (Docker): API Service
234
+ β”œβ”€β”€ FastAPI REST API
235
+ β”œβ”€β”€ MCP Server
236
+ └── Production endpoints
237
+
238
+ Space 2 (Gradio): Testing UI
239
+ β”œβ”€β”€ Gradio Interface
240
+ β”œβ”€β”€ Calls Space 1 API
241
+ └── Human-friendly testing
242
+ ```
243
+
244
+ ### Option 3: Gradio + FastAPI in Same Docker
245
+ ```python
246
+ # Combined approach in app.py
247
+ from fastapi import FastAPI
248
+ import gradio as gr
249
+
250
+ # FastAPI for APIs
251
+ app = FastAPI()
252
+
253
+ @app.get("/health")
254
+ def health():
255
+ return {"status": "ok"}
256
+
257
+ @app.post("/search")
258
+ def search(query: str):
259
+ return search_youtube_videos(query)
260
+
261
+ # Gradio for UI
262
+ with gr.Blocks() as gradio_app:
263
+ # Your UI here
264
+ pass
265
+
266
+ # Mount Gradio on FastAPI
267
+ app = gr.mount_gradio_app(app, gradio_app, path="/ui")
268
+
269
+ # Now you have:
270
+ # - API: https://your-space.hf.space/search
271
+ # - UI: https://your-space.hf.space/ui
272
+ ```
273
+
274
+ ## Migration: Docker ↔ Gradio
275
+
276
+ ### From Docker to Gradio (if you want simpler demo)
277
+
278
+ **Current (Docker):**
279
+ ```yaml
280
+ # README.md
281
+ sdk: docker
282
+ ```
283
+
284
+ **Change to:**
285
+ ```yaml
286
+ # README.md
287
+ sdk: gradio
288
+ sdk_version: 5.49.1
289
+ app_file: gradio_ui.py
290
+ ```
291
+
292
+ Then push. HF will rebuild automatically.
293
+
294
+ ### From Gradio to Docker (if you need more power)
295
+
296
+ **Current:**
297
+ ```yaml
298
+ sdk: gradio
299
+ app_file: app.py
300
+ ```
301
+
302
+ **Change to:**
303
+ ```yaml
304
+ sdk: docker
305
+ app_port: 7860
306
+ ```
307
+
308
+ Add `Dockerfile`, push, and HF rebuilds.
309
+
310
+ ## Performance Comparison
311
+
312
+ | Metric | Docker | Gradio |
313
+ |--------|--------|--------|
314
+ | Cold Start | 10-30s | 5-15s |
315
+ | Build Time | 5-15 min | 2-3 min |
316
+ | Memory Usage | ~500MB | ~300MB |
317
+ | Request Latency | <100ms | <100ms |
318
+ | Concurrent Users | High | Medium |
319
+ | API Throughput | High | Medium |
320
+
321
+ ## Cost Considerations
322
+
323
+ Both are **FREE** on Hugging Face Spaces free tier:
324
+ - Docker: Same resources as Gradio
325
+ - Gradio: Same resources as Docker
326
+ - No difference in hosting cost
327
+ - Both support custom domains on Pro
328
+
329
+ ## Real-World Examples
330
+
331
+ ### Docker Deployments
332
+ ```
333
+ βœ… API Services (your case)
334
+ βœ… Database-backed apps
335
+ βœ… Multi-service architectures
336
+ βœ… WebSocket servers
337
+ βœ… Custom protocols (MCP, gRPC)
338
+ βœ… Background job processors
339
+ ```
340
+
341
+ ### Gradio Deployments
342
+ ```
343
+ βœ… ML model demos
344
+ βœ… Image classifiers
345
+ βœ… Text generators
346
+ βœ… Quick prototypes
347
+ βœ… Research showcases
348
+ βœ… Educational tools
349
+ ```
350
+
351
+ ## Testing Your Current Setup
352
+
353
+ Since you're using Docker, you can test both ways locally:
354
+
355
+ ### Test 1: API Mode (Docker)
356
+ ```bash
357
+ # Start your Docker container
358
+ docker build -t basicsearch .
359
+ docker run -p 7860:7860 basicsearch
360
+
361
+ # Test API
362
+ curl http://localhost:7860/health
363
+ ```
364
+
365
+ ### Test 2: Gradio UI (Optional)
366
+ ```bash
367
+ # Run the Gradio UI we created
368
+ uv run python gradio_ui.py
369
+
370
+ # Visit http://localhost:7860 in browser
371
+ ```
372
+
373
+ ## Recommendation for Your Project
374
+
375
+ **Stick with Docker** because:
376
+ 1. βœ… Your MCP server needs API access (primary requirement)
377
+ 2. βœ… You need integration with Claude and other tools
378
+ 3. βœ… Production-ready service is the goal
379
+ 4. βœ… You may need to add features (auth, logging, etc.)
380
+ 5. βœ… Better for long-term maintenance
381
+
382
+ **Optional:** Add Gradio UI later for:
383
+ - Human testing interface
384
+ - Demo purposes
385
+ - Visual debugging
386
+ - Client presentations
387
+
388
+ ## Summary
389
+
390
+ | Aspect | Your Choice (Docker) | Alternative (Gradio) |
391
+ |--------|---------------------|---------------------|
392
+ | **What You Built** | API Service | Would be Demo UI |
393
+ | **Who Uses It** | AI Assistants | Humans in Browser |
394
+ | **How They Use It** | HTTP Requests | Click & Type |
395
+ | **Best For** | Production Services | Quick Demos |
396
+ | **Your Needs** | βœ… Perfect Match | ❌ Wrong Tool |
397
+
398
+ ## Next Steps
399
+
400
+ 1. βœ… Continue with Docker deployment
401
+ 2. ⏳ Wait for build to complete
402
+ 3. πŸ§ͺ Test with `uv run python test_deployment.py`
403
+ 4. πŸ“ (Optional) Try Gradio UI: `uv run python gradio_ui.py`
404
+ 5. πŸš€ Integrate with your AI workflows
405
+
406
+ ---
407
+
408
+ **TL;DR:**
409
+ - **Gradio** = Easy UI for demos (humans clicking buttons)
410
+ - **Docker** = Powerful API for services (programs making requests)
411
+ - **Your choice (Docker)** = Correct for MCP server! βœ…
412
+
PRIVATE_SPACE_GUIDE.md ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Private Space Testing Guide
2
+
3
+ ## The 404 Problem: Is Your Space Private?
4
+
5
+ If you're getting 404 errors, your Space might be **PRIVATE**, which requires authentication to access.
6
+
7
+ ## Check Your Space Visibility
8
+
9
+ The settings page should now be open in your browser. Look for:
10
+
11
+ ### πŸ”’ Private Space
12
+ ```
13
+ Visibility: Private
14
+ Only you and approved users can access this Space
15
+ ```
16
+
17
+ ### 🌐 Public Space
18
+ ```
19
+ Visibility: Public
20
+ Anyone can access this Space
21
+ ```
22
+
23
+ ## Solution 1: Make Your Space Public (Recommended for APIs)
24
+
25
+ ### Steps:
26
+ 1. Go to: https://huggingface.co/spaces/ocx2025/basicsearch/settings
27
+ 2. Scroll to **"Visibility"** section
28
+ 3. Click **"Make public"**
29
+ 4. Confirm the change
30
+
31
+ ### After Making Public:
32
+ ```bash
33
+ # Wait 30 seconds, then test again
34
+ sleep 30
35
+ cd /Users/marjorie/Documents/GitHub/xctopus/mcp2/basicsearch
36
+ uv run python test_deployment.py
37
+ ```
38
+
39
+ ### βœ… Pros:
40
+ - No authentication needed
41
+ - Easy to test
42
+ - Anyone can use your API
43
+ - Better for integrations
44
+
45
+ ### ❌ Cons:
46
+ - Anyone can see your code
47
+ - Anyone can use your API
48
+ - Public usage counts
49
+
50
+ ## Solution 2: Keep Private + Use Authentication Token
51
+
52
+ If you want to keep the Space private, you'll need a Hugging Face token.
53
+
54
+ ### Get Your Token:
55
+ 1. Visit: https://huggingface.co/settings/tokens
56
+ 2. Click **"New token"**
57
+ 3. Name it: `basicsearch-testing`
58
+ 4. Select permissions: **Read**
59
+ 5. Click **"Generate"**
60
+ 6. Copy the token (starts with `hf_...`)
61
+
62
+ ### Test with Token:
63
+ ```bash
64
+ cd /Users/marjorie/Documents/GitHub/xctopus/mcp2/basicsearch
65
+
66
+ # Replace YOUR_TOKEN with actual token
67
+ python test_private_space.py https://ocx2025-basicsearch.hf.space hf_YourTokenHere
68
+ ```
69
+
70
+ ### Set as Environment Variable:
71
+ ```bash
72
+ # Add to ~/.zshrc or ~/.bashrc
73
+ export HF_TOKEN="hf_YourTokenHere"
74
+
75
+ # Then you can just run:
76
+ python test_private_space.py
77
+ ```
78
+
79
+ ### Update Test Script to Use Token:
80
+ ```python
81
+ # test_deployment.py with auth
82
+ import os
83
+ import requests
84
+
85
+ token = os.getenv("HF_TOKEN")
86
+ headers = {}
87
+ if token:
88
+ headers["Authorization"] = f"Bearer {token}"
89
+
90
+ response = requests.get(
91
+ "https://ocx2025-basicsearch.hf.space/health",
92
+ headers=headers
93
+ )
94
+ ```
95
+
96
+ ### βœ… Pros:
97
+ - Code stays private
98
+ - Control who can access
99
+ - Better security
100
+
101
+ ### ❌ Cons:
102
+ - Requires token management
103
+ - More complex testing
104
+ - Harder to share
105
+
106
+ ## Test Right Now
107
+
108
+ ### Quick Check:
109
+ ```bash
110
+ # Test without auth (works if public)
111
+ curl https://ocx2025-basicsearch.hf.space/health
112
+
113
+ # Test with auth (works if private)
114
+ curl -H "Authorization: Bearer YOUR_TOKEN" \
115
+ https://ocx2025-basicsearch.hf.space/health
116
+ ```
117
+
118
+ ### Expected Results:
119
+
120
+ #### If Space is Building:
121
+ ```
122
+ 404 - Page not found (with HF branding page)
123
+ ```
124
+ **Action:** Wait 5-10 more minutes
125
+
126
+ #### If Space is Private:
127
+ ```
128
+ 404 - Page not found
129
+ ```
130
+ **Action:** Make public OR use token
131
+
132
+ #### If Space is Public and Running:
133
+ ```json
134
+ {"status": "ok"}
135
+ ```
136
+ **Action:** Celebrate! πŸŽ‰
137
+
138
+ ## How to Use Private Space with Claude
139
+
140
+ If you keep it private, you'll need to configure Claude with your token:
141
+
142
+ ### Claude Desktop Config:
143
+ ```json
144
+ {
145
+ "mcpServers": {
146
+ "basicsearch": {
147
+ "url": "https://ocx2025-basicsearch.hf.space",
148
+ "headers": {
149
+ "Authorization": "Bearer hf_YourTokenHere"
150
+ }
151
+ }
152
+ }
153
+ }
154
+ ```
155
+
156
+ ## Docker App with Authentication
157
+
158
+ If you want your Docker app to handle authentication, update `app.py`:
159
+
160
+ ```python
161
+ from fastapi import FastAPI, Header, HTTPException
162
+
163
+ app = FastAPI()
164
+
165
+ # Simple token check
166
+ API_TOKEN = os.getenv("API_TOKEN", "")
167
+
168
+ @app.get("/health")
169
+ async def health(authorization: str = Header(None)):
170
+ if API_TOKEN and authorization != f"Bearer {API_TOKEN}":
171
+ raise HTTPException(status_code=401, detail="Unauthorized")
172
+ return {"status": "ok"}
173
+ ```
174
+
175
+ ## Comparison: Public vs Private
176
+
177
+ | Feature | Public | Private |
178
+ |---------|--------|---------|
179
+ | **Access** | Anyone | Token required |
180
+ | **Testing** | Easy | Needs token |
181
+ | **Security** | Low | High |
182
+ | **Sharing** | Easy | Controlled |
183
+ | **API Use** | Simple | Complex |
184
+ | **Best For** | Demos, Open APIs | Internal tools |
185
+
186
+ ## Recommendation for Your MCP Server
187
+
188
+ ### Choose Public If:
189
+ - βœ… You want easy testing
190
+ - βœ… You plan to share with others
191
+ - βœ… Your API is meant to be public
192
+ - βœ… No sensitive data involved
193
+ - βœ… You want Claude integration to be simple
194
+
195
+ ### Choose Private If:
196
+ - βœ… You have sensitive code
197
+ - βœ… You want to control access
198
+ - βœ… You're in development phase
199
+ - βœ… You need usage tracking
200
+ - βœ… You have private API keys (like YouTube API)
201
+
202
+ ## Current Status Check
203
+
204
+ Run this to determine what's happening:
205
+
206
+ ```bash
207
+ cd /Users/marjorie/Documents/GitHub/xctopus/mcp2/basicsearch
208
+
209
+ # Check 1: Is it accessible at all?
210
+ curl -I https://ocx2025-basicsearch.hf.space/health
211
+
212
+ # Check 2: What's the space status?
213
+ open https://huggingface.co/spaces/ocx2025/basicsearch
214
+
215
+ # Check 3: What are the settings?
216
+ open https://huggingface.co/spaces/ocx2025/basicsearch/settings
217
+ ```
218
+
219
+ Look for:
220
+ 1. **"Building"** β†’ Wait and try again
221
+ 2. **"Running" + Private** β†’ Make public or use token
222
+ 3. **"Running" + Public** β†’ Should work!
223
+ 4. **"Error"** β†’ Check logs
224
+
225
+ ## Quick Fix Checklist
226
+
227
+ - [ ] Visit: https://huggingface.co/spaces/ocx2025/basicsearch/settings
228
+ - [ ] Check visibility: Public or Private?
229
+ - [ ] If Private: Click "Make public"
230
+ - [ ] If keeping Private: Get HF token from https://huggingface.co/settings/tokens
231
+ - [ ] Wait 30 seconds after change
232
+ - [ ] Run: `uv run python test_deployment.py`
233
+ - [ ] Should see: `βœ“ PASSED` messages!
234
+
235
+ ## Troubleshooting Matrix
236
+
237
+ | Status | Visibility | Result | Action |
238
+ |--------|-----------|--------|--------|
239
+ | Building | Any | 404 | Wait |
240
+ | Running | Public | 200 OK | βœ… Success |
241
+ | Running | Public | 404 | Check logs |
242
+ | Running | Private | 404 | Use token or make public |
243
+ | Running | Private | 401 | Invalid token |
244
+ | Error | Any | 404 | Check build logs |
245
+
246
+ ## After Making Public
247
+
248
+ Once you make it public, test immediately:
249
+
250
+ ```bash
251
+ # Should work now!
252
+ curl https://ocx2025-basicsearch.hf.space/health
253
+
254
+ # Full test
255
+ uv run python test_deployment.py
256
+
257
+ # If it works, you'll see:
258
+ # βœ“ PASSED - Health Check
259
+ # βœ“ PASSED - Service Info
260
+ # ... etc
261
+ ```
262
+
263
+ ## Security Note
264
+
265
+ If you make the Space public, your code will be visible. Make sure:
266
+ - βœ… No API keys in code (use Hugging Face secrets)
267
+ - βœ… No passwords or tokens committed
268
+ - βœ… Your `.gitignore` includes `.env` files
269
+ - βœ… Secrets are set in Space settings, not code
270
+
271
+ Your `YOUTUBE_API_KEY` is safe because it's in Space secrets, not code! βœ…
272
+
273
+ ## Summary
274
+
275
+ **Most likely issue:** Your Space is **private** and needs authentication.
276
+
277
+ **Quick fix:**
278
+ 1. Open settings (should be open now)
279
+ 2. Click "Make public"
280
+ 3. Wait 30 seconds
281
+ 4. Run test again
282
+ 5. Profit! πŸš€
283
+
STATUS_CHECK.md ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # πŸ” Space Status Check
2
+
3
+ ## Current Status: Building or Configuration Needed
4
+
5
+ Your test shows **404 errors**, which means one of the following:
6
+
7
+ ### 1. Space is Still Building ⏳
8
+ Docker builds can take 5-15 minutes.
9
+
10
+ **Check build status:**
11
+ 1. Visit: https://huggingface.co/spaces/ocx2025/basicsearch
12
+ 2. Look at the top of the page for:
13
+ - ⏳ "Building..." - Wait a few more minutes
14
+ - βœ… "Running" - Space is live (404 might be a different issue)
15
+ - ❌ "Build Failed" - Check logs
16
+
17
+ ### 2. Check Build Logs πŸ“‹
18
+
19
+ Visit: https://huggingface.co/spaces/ocx2025/basicsearch/logs
20
+
21
+ Look for errors like:
22
+ - Python package installation failures
23
+ - Missing dependencies
24
+ - Docker build errors
25
+ - Port binding issues
26
+
27
+ ### 3. Common Issues & Solutions
28
+
29
+ #### Issue: Build Timeout
30
+ **Symptoms:** Build stops after 10 minutes
31
+ **Solution:**
32
+ - Optimize Dockerfile
33
+ - Use smaller base image
34
+ - Pre-build dependencies
35
+
36
+ #### Issue: Port Not Exposed
37
+ **Symptoms:** Build succeeds but 404 errors
38
+ **Solution:** Check that app.py is running on port 7860
39
+
40
+ #### Issue: Missing API Key
41
+ **Symptoms:** Build succeeds, endpoints work but search fails
42
+ **Solution:** Set `YOUTUBE_API_KEY` in Space secrets
43
+
44
+ #### Issue: Wrong Entry Point
45
+ **Symptoms:** Container starts but doesn't respond
46
+ **Solution:** Verify Dockerfile CMD is correct:
47
+ ```dockerfile
48
+ CMD ["python", "app.py"]
49
+ ```
50
+
51
+ ## Quick Diagnostic Steps
52
+
53
+ ### Step 1: Check Space Page
54
+ ```bash
55
+ open https://huggingface.co/spaces/ocx2025/basicsearch
56
+ ```
57
+
58
+ ### Step 2: Check Build Logs
59
+ ```bash
60
+ open https://huggingface.co/spaces/ocx2025/basicsearch/logs
61
+ ```
62
+
63
+ ### Step 3: Wait and Retry
64
+ ```bash
65
+ # Wait 2 minutes, then test again
66
+ sleep 120
67
+ cd /Users/marjorie/Documents/GitHub/xctopus/mcp2/basicsearch
68
+ uv run python test_deployment.py
69
+ ```
70
+
71
+ ### Step 4: Manual Test
72
+ ```bash
73
+ # Test with curl
74
+ curl -v https://ocx2025-basicsearch.hf.space/health
75
+
76
+ # Look for:
77
+ # - Connection refused = Space not started
78
+ # - 404 = Space running but wrong route
79
+ # - 200 = Success!
80
+ ```
81
+
82
+ ## What to Look for in Logs
83
+
84
+ ### βœ… Good Signs
85
+ ```
86
+ Installing dependencies...
87
+ Successfully installed...
88
+ Server running on 0.0.0.0:7860
89
+ Application startup complete
90
+ ```
91
+
92
+ ### ❌ Bad Signs
93
+ ```
94
+ Error: Could not find...
95
+ ModuleNotFoundError...
96
+ Permission denied...
97
+ Port already in use...
98
+ Build timeout...
99
+ ```
100
+
101
+ ## Next Actions
102
+
103
+ ### If Still Building:
104
+ ⏰ **Wait 5-10 more minutes**, then run:
105
+ ```bash
106
+ uv run python test_deployment.py
107
+ ```
108
+
109
+ ### If Build Failed:
110
+ 1. Check logs for specific error
111
+ 2. Fix the issue locally
112
+ 3. Commit and push:
113
+ ```bash
114
+ git add .
115
+ git commit -m "Fix: [describe fix]"
116
+ git push origin main
117
+ ```
118
+
119
+ ### If Running but 404:
120
+ Check if the Dockerfile is correct:
121
+ ```bash
122
+ cat Dockerfile
123
+ ```
124
+
125
+ Should end with:
126
+ ```dockerfile
127
+ CMD ["python", "app.py"]
128
+ ```
129
+
130
+ ### If Need Help Debugging:
131
+ 1. Copy build logs
132
+ 2. Check TESTING_GUIDE.md
133
+ 3. Verify all files are pushed:
134
+ ```bash
135
+ git status
136
+ git log -1
137
+ ```
138
+
139
+ ## Monitoring Script
140
+
141
+ Run this to continuously monitor your Space:
142
+
143
+ ```bash
144
+ #!/bin/bash
145
+ while true; do
146
+ clear
147
+ echo "Checking Space Status at $(date)"
148
+ echo "=================================="
149
+ STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://ocx2025-basicsearch.hf.space/health)
150
+
151
+ if [ "$STATUS" = "200" ]; then
152
+ echo "βœ… Space is LIVE!"
153
+ exit 0
154
+ elif [ "$STATUS" = "404" ]; then
155
+ echo "⏳ Still building or config needed (404)"
156
+ else
157
+ echo "❓ Unexpected status: $STATUS"
158
+ fi
159
+
160
+ echo "Checking again in 30 seconds..."
161
+ sleep 30
162
+ done
163
+ ```
164
+
165
+ Save as `monitor.sh`, make executable, and run:
166
+ ```bash
167
+ chmod +x monitor.sh
168
+ ./monitor.sh
169
+ ```
170
+
171
+ ## Expected Timeline
172
+
173
+ | Time | Status |
174
+ |------|--------|
175
+ | 0-2 min | Initializing build |
176
+ | 2-5 min | Installing dependencies |
177
+ | 5-8 min | Building Docker image |
178
+ | 8-10 min | Starting container |
179
+ | 10+ min | Should be live (if not, check logs) |
180
+
181
+ ## When Space is Live
182
+
183
+ You'll see:
184
+ ```bash
185
+ $ curl https://ocx2025-basicsearch.hf.space/health
186
+ {"status":"ok"}
187
+ ```
188
+
189
+ Then run full tests:
190
+ ```bash
191
+ uv run python test_deployment.py
192
+ ```
193
+
194
+ ## Resources
195
+
196
+ - **Your Space:** https://huggingface.co/spaces/ocx2025/basicsearch
197
+ - **Logs:** https://huggingface.co/spaces/ocx2025/basicsearch/logs
198
+ - **Settings:** https://huggingface.co/spaces/ocx2025/basicsearch/settings
199
+ - **HF Status:** https://status.huggingface.co/
200
+
201
+ ## TL;DR - What to Do Now
202
+
203
+ 1. Visit https://huggingface.co/spaces/ocx2025/basicsearch
204
+ 2. Check if it says "Building" at the top
205
+ 3. If yes: β˜• Take a coffee break (5-10 minutes)
206
+ 4. If no: Check logs for errors
207
+ 5. After waiting, run: `uv run python test_deployment.py`
208
+
TESTING_GUIDE.md ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Testing Guide for Deployed Space
2
+
3
+ ## Quick Status Check
4
+
5
+ Your Space is deployed at: **https://huggingface.co/spaces/ocx2025/basicsearch**
6
+
7
+ ### Check Build Status
8
+
9
+ 1. Visit https://huggingface.co/spaces/ocx2025/basicsearch
10
+ 2. Look for the build status indicator at the top
11
+ 3. If building, you'll see "Building..." with logs
12
+ 4. If running, you'll see "Running" status
13
+
14
+ ### View Build Logs
15
+
16
+ 1. Go to https://huggingface.co/spaces/ocx2025/basicsearch/logs
17
+ 2. Check for any errors during Docker build
18
+ 3. Common issues:
19
+ - Missing dependencies
20
+ - Python version mismatch
21
+ - Docker build timeout
22
+
23
+ ## Testing Methods
24
+
25
+ ### Method 1: Automated Test Scripts (Recommended)
26
+
27
+ #### Using Python Script
28
+ ```bash
29
+ cd /Users/marjorie/Documents/GitHub/xctopus/mcp2/basicsearch
30
+
31
+ # Run all tests
32
+ python test_deployment.py
33
+
34
+ # Or test a custom URL
35
+ python test_deployment.py https://your-space-url.hf.space
36
+ ```
37
+
38
+ #### Using Bash Script
39
+ ```bash
40
+ cd /Users/marjorie/Documents/GitHub/xctopus/mcp2/basicsearch
41
+
42
+ # Run all tests
43
+ ./test_deployment.sh
44
+
45
+ # Or test a custom URL
46
+ ./test_deployment.sh https://your-space-url.hf.space
47
+ ```
48
+
49
+ The scripts will test:
50
+ - βœ… Health check endpoint
51
+ - βœ… Service info endpoint
52
+ - βœ… Tools listing
53
+ - βœ… YouTube search functionality
54
+ - βœ… Error handling
55
+
56
+ ### Method 2: Manual cURL Commands
57
+
58
+ #### 1. Health Check
59
+ ```bash
60
+ curl https://ocx2025-basicsearch.hf.space/health
61
+ ```
62
+
63
+ **Expected Response:**
64
+ ```json
65
+ {"status": "ok"}
66
+ ```
67
+
68
+ #### 2. Service Information
69
+ ```bash
70
+ curl https://ocx2025-basicsearch.hf.space/
71
+ ```
72
+
73
+ **Expected Response:**
74
+ ```json
75
+ {
76
+ "status": "healthy",
77
+ "service": "basicsearch-mcp-server",
78
+ "tools": ["search_youtube_videos"]
79
+ }
80
+ ```
81
+
82
+ #### 3. List Available Tools
83
+ ```bash
84
+ curl https://ocx2025-basicsearch.hf.space/tools
85
+ ```
86
+
87
+ **Expected Response:**
88
+ ```json
89
+ {
90
+ "tools": [
91
+ {
92
+ "name": "search_youtube_videos",
93
+ "description": "Searches YouTube for videos based on a query.",
94
+ "parameters": {
95
+ "query": "string (required)",
96
+ "max_results": "integer (optional, default: 5)"
97
+ }
98
+ }
99
+ ]
100
+ }
101
+ ```
102
+
103
+ #### 4. Search YouTube Videos
104
+ ```bash
105
+ curl -X POST https://ocx2025-basicsearch.hf.space/search \
106
+ -H "Content-Type: application/json" \
107
+ -d '{
108
+ "query": "Python programming tutorial",
109
+ "max_results": 3
110
+ }'
111
+ ```
112
+
113
+ **Expected Response (if API key is set):**
114
+ ```json
115
+ {
116
+ "results": [
117
+ {
118
+ "title": "Python Tutorial for Beginners",
119
+ "video_id": "abc123xyz",
120
+ "description": "Learn Python programming..."
121
+ },
122
+ ...
123
+ ]
124
+ }
125
+ ```
126
+
127
+ **Expected Error (if API key NOT set):**
128
+ ```json
129
+ {
130
+ "error": "YOUTUBE_API_KEY environment variable is not set"
131
+ }
132
+ ```
133
+
134
+ ### Method 3: Using Python `requests` Library
135
+
136
+ ```python
137
+ import requests
138
+ import json
139
+
140
+ BASE_URL = "https://ocx2025-basicsearch.hf.space"
141
+
142
+ # Test health
143
+ response = requests.get(f"{BASE_URL}/health")
144
+ print(f"Health: {response.json()}")
145
+
146
+ # Test search
147
+ payload = {"query": "Python", "max_results": 2}
148
+ response = requests.post(f"{BASE_URL}/search", json=payload)
149
+ print(f"Search Results: {json.dumps(response.json(), indent=2)}")
150
+ ```
151
+
152
+ ### Method 4: Using Browser
153
+
154
+ Simply visit these URLs in your browser:
155
+
156
+ 1. **Health Check:** https://ocx2025-basicsearch.hf.space/health
157
+ 2. **Service Info:** https://ocx2025-basicsearch.hf.space/
158
+ 3. **Tools List:** https://ocx2025-basicsearch.hf.space/tools
159
+
160
+ For POST requests, use a browser extension like:
161
+ - [Postman](https://www.postman.com/)
162
+ - [Thunder Client](https://www.thunderclient.com/) (VS Code extension)
163
+ - [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) (VS Code extension)
164
+
165
+ ## Troubleshooting
166
+
167
+ ### Space Returns 404
168
+ **Cause:** Space is still building or failed to start
169
+ **Solution:**
170
+ 1. Check build logs at https://huggingface.co/spaces/ocx2025/basicsearch/logs
171
+ 2. Wait for build to complete (can take 5-10 minutes)
172
+ 3. Look for error messages in logs
173
+
174
+ ### API Key Error
175
+ **Cause:** `YOUTUBE_API_KEY` not set or invalid
176
+ **Solution:**
177
+ 1. Go to https://huggingface.co/spaces/ocx2025/basicsearch/settings
178
+ 2. Click "Repository secrets"
179
+ 3. Add secret: Name=`YOUTUBE_API_KEY`, Value=`your_actual_key`
180
+ 4. Restart the Space
181
+
182
+ ### Build Timeout
183
+ **Cause:** Docker build taking too long
184
+ **Solution:**
185
+ 1. Check Dockerfile for optimization opportunities
186
+ 2. Verify all dependencies are necessary
187
+ 3. Consider using a lighter base image
188
+
189
+ ### Connection Timeout
190
+ **Cause:** Space is sleeping (free tier) or overloaded
191
+ **Solution:**
192
+ 1. Visit the Space page to wake it up
193
+ 2. Wait a few seconds and retry
194
+ 3. Check Space status
195
+
196
+ ### Rate Limiting
197
+ **Cause:** Too many requests to YouTube API
198
+ **Solution:**
199
+ 1. Check your Google Cloud Console quota
200
+ 2. Default is 10,000 units/day
201
+ 3. Each search costs ~100 units
202
+
203
+ ## Monitoring
204
+
205
+ ### Real-time Logs
206
+ ```bash
207
+ # Using curl to monitor health
208
+ watch -n 5 'curl -s https://ocx2025-basicsearch.hf.space/health'
209
+ ```
210
+
211
+ ### Check Space Status
212
+ ```bash
213
+ # Simple status check
214
+ curl -s -o /dev/null -w "HTTP Status: %{http_code}\n" \
215
+ https://ocx2025-basicsearch.hf.space/health
216
+ ```
217
+
218
+ ### Continuous Testing
219
+ ```bash
220
+ # Run tests every minute
221
+ while true; do
222
+ echo "Testing at $(date)"
223
+ python test_deployment.py
224
+ sleep 60
225
+ done
226
+ ```
227
+
228
+ ## Performance Testing
229
+
230
+ ### Load Test with Apache Bench
231
+ ```bash
232
+ # Test 100 requests with 10 concurrent connections
233
+ ab -n 100 -c 10 https://ocx2025-basicsearch.hf.space/health
234
+ ```
235
+
236
+ ### Load Test with Python
237
+ ```python
238
+ import time
239
+ import requests
240
+ from concurrent.futures import ThreadPoolExecutor
241
+
242
+ def test_endpoint():
243
+ start = time.time()
244
+ response = requests.get("https://ocx2025-basicsearch.hf.space/health")
245
+ end = time.time()
246
+ return response.status_code, end - start
247
+
248
+ # Run 50 concurrent requests
249
+ with ThreadPoolExecutor(max_workers=10) as executor:
250
+ results = list(executor.map(lambda _: test_endpoint(), range(50)))
251
+
252
+ successful = sum(1 for status, _ in results if status == 200)
253
+ avg_time = sum(t for _, t in results) / len(results)
254
+
255
+ print(f"Successful: {successful}/50")
256
+ print(f"Average response time: {avg_time:.3f}s")
257
+ ```
258
+
259
+ ## Expected Response Times
260
+
261
+ | Endpoint | Expected Time |
262
+ |----------|--------------|
263
+ | `/health` | < 100ms |
264
+ | `/` | < 100ms |
265
+ | `/tools` | < 100ms |
266
+ | `/search` | 500ms - 2s (depends on YouTube API) |
267
+
268
+ ## Security Testing
269
+
270
+ ### Test Invalid Input
271
+ ```bash
272
+ # Test with missing query
273
+ curl -X POST https://ocx2025-basicsearch.hf.space/search \
274
+ -H "Content-Type: application/json" \
275
+ -d '{}'
276
+
277
+ # Test with invalid JSON
278
+ curl -X POST https://ocx2025-basicsearch.hf.space/search \
279
+ -H "Content-Type: application/json" \
280
+ -d 'invalid json'
281
+
282
+ # Test with excessive max_results
283
+ curl -X POST https://ocx2025-basicsearch.hf.space/search \
284
+ -H "Content-Type: application/json" \
285
+ -d '{"query": "test", "max_results": 1000}'
286
+ ```
287
+
288
+ ## Integration Testing
289
+
290
+ ### Test in Claude Desktop
291
+
292
+ If using with Claude Desktop, add to your MCP settings:
293
+
294
+ ```json
295
+ {
296
+ "mcpServers": {
297
+ "basicsearch": {
298
+ "url": "https://ocx2025-basicsearch.hf.space"
299
+ }
300
+ }
301
+ }
302
+ ```
303
+
304
+ ### Test with Python SDK
305
+
306
+ ```python
307
+ from mcp import ClientSession
308
+ from mcp.client.stdio import stdio_client
309
+
310
+ # Connect to your MCP server
311
+ async with stdio_client() as (read, write):
312
+ async with ClientSession(read, write) as session:
313
+ # Initialize
314
+ await session.initialize()
315
+
316
+ # Call search tool
317
+ result = await session.call_tool(
318
+ "search_youtube_videos",
319
+ arguments={"query": "Python", "max_results": 3}
320
+ )
321
+ print(result)
322
+ ```
323
+
324
+ ## Next Steps
325
+
326
+ 1. βœ… Run automated test script: `python test_deployment.py`
327
+ 2. βœ… Check build logs if tests fail
328
+ 3. βœ… Set YOUTUBE_API_KEY if not already set
329
+ 4. βœ… Monitor Space status
330
+ 5. βœ… Test all endpoints manually
331
+ 6. βœ… Perform load testing if needed
332
+ 7. βœ… Integrate with your applications
333
+
334
+ ## Useful Links
335
+
336
+ - **Your Space:** https://huggingface.co/spaces/ocx2025/basicsearch
337
+ - **Logs:** https://huggingface.co/spaces/ocx2025/basicsearch/logs
338
+ - **Settings:** https://huggingface.co/spaces/ocx2025/basicsearch/settings
339
+ - **HF Spaces Docs:** https://huggingface.co/docs/hub/spaces
340
+ - **YouTube API Console:** https://console.cloud.google.com/apis/dashboard
341
+
__pycache__/server.cpython-313.pyc CHANGED
Binary files a/__pycache__/server.cpython-313.pyc and b/__pycache__/server.cpython-313.pyc differ
 
gradio_ui.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Optional Gradio UI for the MCP server.
3
+ This can run alongside the FastAPI server for visual testing.
4
+ """
5
+ import gradio as gr
6
+ from server import search_youtube_videos
7
+ import json
8
+
9
+ def search_interface(query: str, max_results: int = 5):
10
+ """Gradio interface for YouTube search"""
11
+ if not query:
12
+ return {"error": "Please enter a search query"}
13
+
14
+ try:
15
+ results = search_youtube_videos(query, int(max_results))
16
+ return {
17
+ "success": True,
18
+ "count": len(results),
19
+ "results": results
20
+ }
21
+ except Exception as e:
22
+ return {
23
+ "success": False,
24
+ "error": str(e)
25
+ }
26
+
27
+ # Create Gradio interface
28
+ with gr.Blocks(title="YouTube Search MCP Server") as demo:
29
+ gr.Markdown("# πŸ” YouTube Search MCP Server")
30
+ gr.Markdown("Test the YouTube search functionality with a visual interface.")
31
+
32
+ with gr.Row():
33
+ with gr.Column():
34
+ query_input = gr.Textbox(
35
+ label="Search Query",
36
+ placeholder="Enter search term (e.g., 'Python tutorial')",
37
+ lines=1
38
+ )
39
+ max_results = gr.Slider(
40
+ minimum=1,
41
+ maximum=10,
42
+ value=5,
43
+ step=1,
44
+ label="Maximum Results"
45
+ )
46
+ search_btn = gr.Button("πŸ” Search", variant="primary")
47
+
48
+ with gr.Column():
49
+ output = gr.JSON(label="Search Results")
50
+
51
+ # Examples
52
+ gr.Examples(
53
+ examples=[
54
+ ["Python programming tutorial", 5],
55
+ ["Machine learning basics", 3],
56
+ ["FastAPI tutorial", 5],
57
+ ],
58
+ inputs=[query_input, max_results],
59
+ )
60
+
61
+ # Connect button
62
+ search_btn.click(
63
+ fn=search_interface,
64
+ inputs=[query_input, max_results],
65
+ outputs=output
66
+ )
67
+
68
+ if __name__ == "__main__":
69
+ demo.launch(server_name="0.0.0.0", server_port=7860)
70
+
pyproject.toml CHANGED
@@ -8,6 +8,7 @@ dependencies = [
8
  "fastapi>=0.115.0",
9
  "fastmcp>=2.12.4",
10
  "google-api-python-client>=2.184.0",
 
11
  "mcp[cli]>=1.16.0",
12
  "python-dotenv>=1.1.1",
13
  "requests>=2.32.0",
 
8
  "fastapi>=0.115.0",
9
  "fastmcp>=2.12.4",
10
  "google-api-python-client>=2.184.0",
11
+ "gradio>=5.49.1",
12
  "mcp[cli]>=1.16.0",
13
  "python-dotenv>=1.1.1",
14
  "requests>=2.32.0",
test_deployment.py ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Python test script for Hugging Face Space deployment
4
+ Usage: python test_deployment.py [space-url]
5
+ """
6
+
7
+ import sys
8
+ import requests
9
+ import json
10
+ from typing import Dict, Any
11
+
12
+ # ANSI color codes
13
+ GREEN = '\033[0;32m'
14
+ RED = '\033[0;31m'
15
+ YELLOW = '\033[1;33m'
16
+ BLUE = '\033[0;34m'
17
+ NC = '\033[0m' # No Color
18
+
19
+
20
+ def print_test(test_name: str):
21
+ """Print test header"""
22
+ print(f"\n{YELLOW}{test_name}{NC}")
23
+
24
+
25
+ def print_result(passed: bool, message: str, response: Any = None):
26
+ """Print test result"""
27
+ status = f"{GREEN}βœ“ PASSED{NC}" if passed else f"{RED}βœ— FAILED{NC}"
28
+ print(f"{status} - {message}")
29
+ if response:
30
+ try:
31
+ print(f"Response: {json.dumps(response, indent=2)}")
32
+ except:
33
+ print(f"Response: {response}")
34
+
35
+
36
+ def test_health(base_url: str) -> bool:
37
+ """Test health endpoint"""
38
+ print_test("Test 1: Health Check")
39
+ print(f"GET {base_url}/health")
40
+
41
+ try:
42
+ response = requests.get(f"{base_url}/health", timeout=10)
43
+ passed = response.status_code == 200
44
+ print_result(
45
+ passed,
46
+ f"Status: {response.status_code}",
47
+ response.json() if passed else response.text
48
+ )
49
+ return passed
50
+ except Exception as e:
51
+ print_result(False, f"Error: {str(e)}")
52
+ return False
53
+
54
+
55
+ def test_service_info(base_url: str) -> bool:
56
+ """Test root endpoint"""
57
+ print_test("Test 2: Service Info")
58
+ print(f"GET {base_url}/")
59
+
60
+ try:
61
+ response = requests.get(f"{base_url}/", timeout=10)
62
+ passed = response.status_code == 200
63
+ print_result(
64
+ passed,
65
+ f"Status: {response.status_code}",
66
+ response.json() if passed else response.text
67
+ )
68
+ return passed
69
+ except Exception as e:
70
+ print_result(False, f"Error: {str(e)}")
71
+ return False
72
+
73
+
74
+ def test_list_tools(base_url: str) -> bool:
75
+ """Test tools listing endpoint"""
76
+ print_test("Test 3: List Available Tools")
77
+ print(f"GET {base_url}/tools")
78
+
79
+ try:
80
+ response = requests.get(f"{base_url}/tools", timeout=10)
81
+ passed = response.status_code == 200
82
+ print_result(
83
+ passed,
84
+ f"Status: {response.status_code}",
85
+ response.json() if passed else response.text
86
+ )
87
+ return passed
88
+ except Exception as e:
89
+ print_result(False, f"Error: {str(e)}")
90
+ return False
91
+
92
+
93
+ def test_youtube_search(base_url: str) -> bool:
94
+ """Test YouTube search endpoint"""
95
+ print_test("Test 4: YouTube Video Search")
96
+ print(f"POST {base_url}/search")
97
+
98
+ payload = {
99
+ "query": "Python programming",
100
+ "max_results": 2
101
+ }
102
+
103
+ try:
104
+ response = requests.post(
105
+ f"{base_url}/search",
106
+ json=payload,
107
+ timeout=15
108
+ )
109
+
110
+ if response.status_code == 200:
111
+ print_result(True, f"Status: {response.status_code}", response.json())
112
+ return True
113
+ elif response.status_code == 500 and "YOUTUBE_API_KEY" in response.text:
114
+ print(f"{YELLOW}⚠ API KEY NOT SET{NC} - Status: {response.status_code}")
115
+ print("Please set YOUTUBE_API_KEY in your Space settings")
116
+ print(f"Response: {response.json()}")
117
+ return False
118
+ else:
119
+ print_result(False, f"Status: {response.status_code}", response.text)
120
+ return False
121
+ except Exception as e:
122
+ print_result(False, f"Error: {str(e)}")
123
+ return False
124
+
125
+
126
+ def test_error_handling(base_url: str) -> bool:
127
+ """Test error handling with missing query"""
128
+ print_test("Test 5: Error Handling (Missing Query)")
129
+ print(f"POST {base_url}/search (with empty query)")
130
+
131
+ payload = {}
132
+
133
+ try:
134
+ response = requests.post(
135
+ f"{base_url}/search",
136
+ json=payload,
137
+ timeout=10
138
+ )
139
+
140
+ # We expect a 400 error
141
+ passed = response.status_code == 400
142
+ print_result(
143
+ passed,
144
+ f"Status: {response.status_code} (Expected 400 error)",
145
+ response.json() if response.status_code == 400 else response.text
146
+ )
147
+ return passed
148
+ except Exception as e:
149
+ print_result(False, f"Error: {str(e)}")
150
+ return False
151
+
152
+
153
+ def main():
154
+ """Run all tests"""
155
+ # Get Space URL from command line or use default
156
+ base_url = sys.argv[1] if len(sys.argv) > 1 else "https://ocx2025-basicsearch.hf.space"
157
+
158
+ # Remove trailing slash if present
159
+ base_url = base_url.rstrip('/')
160
+
161
+ print("=" * 50)
162
+ print(f"Testing Hugging Face Space: {base_url}")
163
+ print("=" * 50)
164
+
165
+ # Run all tests
166
+ results = []
167
+ results.append(("Health Check", test_health(base_url)))
168
+ results.append(("Service Info", test_service_info(base_url)))
169
+ results.append(("List Tools", test_list_tools(base_url)))
170
+ results.append(("YouTube Search", test_youtube_search(base_url)))
171
+ results.append(("Error Handling", test_error_handling(base_url)))
172
+
173
+ # Print summary
174
+ print("\n" + "=" * 50)
175
+ print("Test Summary")
176
+ print("=" * 50)
177
+
178
+ passed = sum(1 for _, result in results if result)
179
+ total = len(results)
180
+
181
+ for test_name, result in results:
182
+ status = f"{GREEN}βœ“{NC}" if result else f"{RED}βœ—{NC}"
183
+ print(f"{status} {test_name}")
184
+
185
+ print(f"\n{BLUE}Results: {passed}/{total} tests passed{NC}")
186
+ print("=" * 50)
187
+
188
+ # Exit with appropriate code
189
+ sys.exit(0 if passed == total else 1)
190
+
191
+
192
+ if __name__ == "__main__":
193
+ main()
194
+
test_deployment.sh ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Test script for Hugging Face Space deployment
4
+ # Usage: ./test_deployment.sh [space-url]
5
+
6
+ # Color codes for output
7
+ GREEN='\033[0;32m'
8
+ RED='\033[0;31m'
9
+ YELLOW='\033[1;33m'
10
+ NC='\033[0m' # No Color
11
+
12
+ # Default URL (update with your actual Space URL)
13
+ SPACE_URL="${1:-https://ocx2025-basicsearch.hf.space}"
14
+
15
+ echo "================================================"
16
+ echo "Testing Hugging Face Space: $SPACE_URL"
17
+ echo "================================================"
18
+ echo ""
19
+
20
+ # Test 1: Health Check
21
+ echo -e "${YELLOW}Test 1: Health Check${NC}"
22
+ echo "GET $SPACE_URL/health"
23
+ RESPONSE=$(curl -s -w "\n%{http_code}" "$SPACE_URL/health")
24
+ HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
25
+ BODY=$(echo "$RESPONSE" | head -n-1)
26
+
27
+ if [ "$HTTP_CODE" = "200" ]; then
28
+ echo -e "${GREEN}βœ“ PASSED${NC} - Status: $HTTP_CODE"
29
+ echo "Response: $BODY"
30
+ else
31
+ echo -e "${RED}βœ— FAILED${NC} - Status: $HTTP_CODE"
32
+ echo "Response: $BODY"
33
+ fi
34
+ echo ""
35
+
36
+ # Test 2: Service Info
37
+ echo -e "${YELLOW}Test 2: Service Info${NC}"
38
+ echo "GET $SPACE_URL/"
39
+ RESPONSE=$(curl -s -w "\n%{http_code}" "$SPACE_URL/")
40
+ HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
41
+ BODY=$(echo "$RESPONSE" | head -n-1)
42
+
43
+ if [ "$HTTP_CODE" = "200" ]; then
44
+ echo -e "${GREEN}βœ“ PASSED${NC} - Status: $HTTP_CODE"
45
+ echo "Response: $BODY" | jq '.' 2>/dev/null || echo "Response: $BODY"
46
+ else
47
+ echo -e "${RED}βœ— FAILED${NC} - Status: $HTTP_CODE"
48
+ echo "Response: $BODY"
49
+ fi
50
+ echo ""
51
+
52
+ # Test 3: List Tools
53
+ echo -e "${YELLOW}Test 3: List Available Tools${NC}"
54
+ echo "GET $SPACE_URL/tools"
55
+ RESPONSE=$(curl -s -w "\n%{http_code}" "$SPACE_URL/tools")
56
+ HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
57
+ BODY=$(echo "$RESPONSE" | head -n-1)
58
+
59
+ if [ "$HTTP_CODE" = "200" ]; then
60
+ echo -e "${GREEN}βœ“ PASSED${NC} - Status: $HTTP_CODE"
61
+ echo "Response: $BODY" | jq '.' 2>/dev/null || echo "Response: $BODY"
62
+ else
63
+ echo -e "${RED}βœ— FAILED${NC} - Status: $HTTP_CODE"
64
+ echo "Response: $BODY"
65
+ fi
66
+ echo ""
67
+
68
+ # Test 4: YouTube Search (requires API key to be set)
69
+ echo -e "${YELLOW}Test 4: YouTube Video Search${NC}"
70
+ echo "POST $SPACE_URL/search"
71
+ RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "$SPACE_URL/search" \
72
+ -H "Content-Type: application/json" \
73
+ -d '{"query": "Python programming", "max_results": 2}')
74
+ HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
75
+ BODY=$(echo "$RESPONSE" | head -n-1)
76
+
77
+ if [ "$HTTP_CODE" = "200" ]; then
78
+ echo -e "${GREEN}βœ“ PASSED${NC} - Status: $HTTP_CODE"
79
+ echo "Response: $BODY" | jq '.' 2>/dev/null || echo "Response: $BODY"
80
+ elif [ "$HTTP_CODE" = "500" ] && echo "$BODY" | grep -q "YOUTUBE_API_KEY"; then
81
+ echo -e "${YELLOW}⚠ API KEY NOT SET${NC} - Status: $HTTP_CODE"
82
+ echo "Please set YOUTUBE_API_KEY in your Space settings"
83
+ echo "Response: $BODY"
84
+ else
85
+ echo -e "${RED}βœ— FAILED${NC} - Status: $HTTP_CODE"
86
+ echo "Response: $BODY"
87
+ fi
88
+ echo ""
89
+
90
+ # Test 5: Error handling - missing query
91
+ echo -e "${YELLOW}Test 5: Error Handling (Missing Query)${NC}"
92
+ echo "POST $SPACE_URL/search (with empty query)"
93
+ RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "$SPACE_URL/search" \
94
+ -H "Content-Type: application/json" \
95
+ -d '{}')
96
+ HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
97
+ BODY=$(echo "$RESPONSE" | head -n-1)
98
+
99
+ if [ "$HTTP_CODE" = "400" ]; then
100
+ echo -e "${GREEN}βœ“ PASSED${NC} - Status: $HTTP_CODE (Expected error)"
101
+ echo "Response: $BODY"
102
+ else
103
+ echo -e "${YELLOW}⚠ UNEXPECTED${NC} - Status: $HTTP_CODE"
104
+ echo "Response: $BODY"
105
+ fi
106
+ echo ""
107
+
108
+ echo "================================================"
109
+ echo "Testing Complete!"
110
+ echo "================================================"
111
+
test_private_space.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test script for PRIVATE Hugging Face Spaces with authentication.
4
+ Usage: uv run python test_private_space.py [space-url] [hf-token]
5
+ """
6
+
7
+ import sys
8
+ import requests
9
+ import json
10
+ import os
11
+
12
+ # ANSI color codes
13
+ GREEN = '\033[0;32m'
14
+ RED = '\033[0;31m'
15
+ YELLOW = '\033[1;33m'
16
+ BLUE = '\033[0;34m'
17
+ NC = '\033[0m' # No Color
18
+
19
+
20
+ def get_headers(token: str = None) -> dict:
21
+ """Get headers with authentication if token provided"""
22
+ headers = {"Content-Type": "application/json"}
23
+ if token:
24
+ headers["Authorization"] = f"Bearer {token}"
25
+ return headers
26
+
27
+
28
+ def test_with_auth(base_url: str, token: str = None):
29
+ """Test space with optional authentication"""
30
+ print("=" * 50)
31
+ print(f"Testing Space: {base_url}")
32
+ if token:
33
+ print(f"Using token: {token[:10]}...{token[-5:]}")
34
+ else:
35
+ print("No token provided (testing public access)")
36
+ print("=" * 50)
37
+ print()
38
+
39
+ headers = get_headers(token)
40
+
41
+ # Test health endpoint
42
+ print(f"{YELLOW}Test: Health Check{NC}")
43
+ try:
44
+ response = requests.get(f"{base_url}/health", headers=headers, timeout=10)
45
+
46
+ if response.status_code == 200:
47
+ print(f"{GREEN}βœ“ SUCCESS{NC} - Space is accessible!")
48
+ print(f"Response: {response.json()}")
49
+ return True
50
+ elif response.status_code == 404:
51
+ print(f"{RED}βœ— 404 ERROR{NC}")
52
+ print(f"\n{YELLOW}Possible reasons:{NC}")
53
+ print("1. Space is PRIVATE and needs authentication token")
54
+ print("2. Space is still building")
55
+ print("3. Space build failed")
56
+ print("\nTo fix:")
57
+ print("β€’ Make space public in settings, OR")
58
+ print("β€’ Use: uv run python test_private_space.py [url] [your-hf-token]")
59
+ return False
60
+ elif response.status_code == 401:
61
+ print(f"{RED}βœ— 401 UNAUTHORIZED{NC}")
62
+ print("Space is PRIVATE. You need a valid Hugging Face token.")
63
+ print("\nGet your token:")
64
+ print("1. Go to https://huggingface.co/settings/tokens")
65
+ print("2. Create a new token with 'read' permissions")
66
+ print("3. Run: uv run python test_private_space.py [url] [token]")
67
+ return False
68
+ else:
69
+ print(f"{RED}βœ— ERROR{NC} - Status: {response.status_code}")
70
+ print(f"Response: {response.text[:200]}")
71
+ return False
72
+ except Exception as e:
73
+ print(f"{RED}βœ— ERROR{NC} - {str(e)}")
74
+ return False
75
+
76
+
77
+ def main():
78
+ # Get space URL and token
79
+ base_url = sys.argv[1] if len(sys.argv) > 1 else "https://ocx2025-basicsearch.hf.space"
80
+ token = sys.argv[2] if len(sys.argv) > 2 else os.getenv("HF_TOKEN")
81
+
82
+ # Remove trailing slash
83
+ base_url = base_url.rstrip('/')
84
+
85
+ # Test
86
+ success = test_with_auth(base_url, token)
87
+
88
+ if success:
89
+ print(f"\n{GREEN}βœ… Space is working!{NC}")
90
+ print(f"You can now run the full test suite:")
91
+ if token:
92
+ print(f"HF_TOKEN={token} uv run python test_deployment.py")
93
+ else:
94
+ print("uv run python test_deployment.py")
95
+ else:
96
+ print(f"\n{YELLOW}πŸ“ Next Steps:{NC}")
97
+ print("\n1. Check if space is private:")
98
+ print(f" Visit: {base_url.replace('.hf.space', '').replace('https://', 'https://huggingface.co/spaces/').replace('-', '/', 1)}")
99
+ print("\n2. Make it PUBLIC:")
100
+ print(" Settings β†’ Make public")
101
+ print("\n3. OR get your HF token:")
102
+ print(" https://huggingface.co/settings/tokens")
103
+ print(f" Then run: uv run python test_private_space.py {base_url} YOUR_TOKEN")
104
+
105
+
106
+ if __name__ == "__main__":
107
+ main()
108
+
uv.lock CHANGED
The diff for this file is too large to render. See raw diff