Alibrown commited on
Commit
04948f1
Β·
verified Β·
1 Parent(s): be5ee6d

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +323 -112
README.md CHANGED
@@ -1,68 +1,251 @@
1
  ---
2
  title: Universal MCP Hub
3
- emoji: πŸ‘€
4
  colorFrom: indigo
5
  colorTo: red
6
  sdk: docker
7
  pinned: false
8
  license: apache-2.0
9
- short_description: 'Universal MCP Server(Sandboxed) built on PyFundaments '
10
  ---
11
 
12
  # Universal MCP Hub (Sandboxed)
13
- The only real (MCP) HUB you need!
14
 
15
- > running on simpleCity and **paranoidMode** β€” built on [PyFundaments](PyFundaments.md).
 
16
 
17
- ... because too many (Hype) MCP servers exist with no sandboxing, hardcoded keys, and zero security thought.
 
 
18
 
 
 
 
19
 
20
- #### This one is different.
21
 
22
- - **No key β†’ no tool β†’ no crash**
23
- - `main.py` = Guardian (controls everything, nothing bypasses it)
24
- - `app/app.py` receives only injected, validated services β€” never reads `os.environ` directly
25
- - Every tool is registered dynamically β€” only if the API key exists
26
 
27
- > *"I use AI as a tool, not as a replacement for thinking."* β€” Volkan KΓΌcΓΌkbudak
 
 
 
 
 
28
 
29
  ---
30
 
31
- ## Quick Start
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- 1. **Fork/Clone** this Repo (Space)
34
- 2. Add your API keys as **Space Secrets** (Settings β†’ Variables and secrets)
35
- 3. Space starts automatically β€” only tools with valid keys are registered
36
 
37
- That's it. No config files to edit, no code to touch.
38
 
39
- [Demo for `cloning space`](https://huggingface.co/spaces/codey-lab/Universal-MCP-Hub-DEMO) on HF
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
  ---
42
 
43
- ## Available Tools
44
 
45
- Tools are registered automatically based on which keys you configure. No key = tool doesn't exist. No crashes, no errors, no exposed secrets.
46
 
47
- | Secret | Tool | Description |
48
  | :--- | :--- | :--- |
49
  | `ANTHROPIC_API_KEY` | `llm_complete` | Claude Haiku / Sonnet / Opus |
50
- | `GEMINI_API_KEY` | `llm_complete` | Gemini Flash / Pro |
51
  | `OPENROUTER_API_KEY` | `llm_complete` | 100+ models via OpenRouter |
52
  | `HF_TOKEN` | `llm_complete` | HuggingFace Inference API |
53
- | `BRAVE_API_KEY` | `web_search` | Web Search (independent index) |
54
- | `TAVILY_API_KEY` | `web_search` | AI-optimized Search |
55
- | `DATABASE_URL` | `db_query` | Read-only DB access (SELECT only) |
56
- | *(always active)* | `list_active_tools` | Lists all currently active tools |
57
- | *(always active)* | `health_check` | System health + uptime |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- All LLM providers share a single `llm_complete` tool with automatic **fallback chain**: `anthropic β†’ gemini β†’ openrouter β†’ huggingface`
 
60
 
61
  ---
62
 
63
- ## MCP Client Configuration (SSE)
 
 
64
 
65
- Connect Claude Desktop or any MCP-compatible client:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
  ```json
68
  {
@@ -74,7 +257,7 @@ Connect Claude Desktop or any MCP-compatible client:
74
  }
75
  ```
76
 
77
- For private Spaces, add your HF token:
78
 
79
  ```json
80
  {
@@ -92,113 +275,142 @@ For private Spaces, add your HF token:
92
  ---
93
 
94
  ## Desktop Client
95
- #### Perfect for non-public spaces
96
 
 
97
 
98
- - A standalone PySide6 desktop client is included: `hub.py`, with help of ClaudeAi, was to lazy πŸ˜„
99
- - Features: Chat tab, Tools inspector, Settings (provider/model override, font size), Logs β€” all saved locally in `~/.mcp_desktop.json`. Token never leaves your machine except to your own Hub.
100
- - more about the [Desktop Client](DESKTOP_CLIENT/README.md)
 
 
 
101
 
102
- ---
 
 
 
 
 
 
 
103
 
104
- ## Architecture
105
- ```
106
- UMH
107
- β”œβ”€β”€ main.py # run main!
108
- β”œβ”€β”€ README.md
109
- β”œβ”€β”€ ESOL
110
- β”œβ”€β”€ LICENSE
111
- β”œβ”€β”€ PyFundaments.md
112
- β”œβ”€β”€ PyFundaments – Function Overview.md
113
- β”œβ”€β”€ SECURITY.md
114
- β”œβ”€β”€ requirements.txt
115
- β”œβ”€β”€ .gitignore
116
- β”œβ”€β”€ example.Dockerfile
117
- β”œβ”€β”€ example-mcp___.env
118
- β”œβ”€β”€ DESKTOP_CLIENT
119
- β”‚ └── hub.py ← light MCP Desktop client
120
- β”œβ”€β”€ app/
121
- β”‚ β”œβ”€β”€ __init__.py
122
- β”‚ β”œβ”€β”€ app.py ← sandboxed Orchestrator
123
- β”‚ β”œβ”€β”€ mcp.py ← MCP SSE server (FastMCP + Quart)
124
- β”‚ β”œβ”€β”€ tools.py ← Tool registry (from .pyfun)
125
- β”‚ β”œβ”€β”€ provider.py ← LLM + Search execution + fallback
126
- β”‚ β”œβ”€β”€ models.py ← Model limits + costs
127
- β”‚ β”œβ”€β”€ db_sync.py ← Internal SQLite state (IPC)
128
- β”‚ β”œβ”€β”€ config.py ← .pyfun parser (single source of truth)
129
- β”‚ └── .pyfun ← single source of truth
130
- β”œβ”€β”€ fundaments/ # do not touch!
131
- β”‚ β”œβ”€β”€ __init__.py
132
- β”‚ β”œβ”€β”€ access_control.py
133
- β”‚ β”œβ”€β”€ config_handler.py
134
- β”‚ β”œβ”€β”€ encryption.py
135
- β”‚ β”œβ”€β”€ postgresql.py
136
- β”‚ β”œβ”€β”€ security.py
137
- β”‚ └── user_handler.py
138
- └── docs/
139
- β”œβ”€β”€ access_control.py.md
140
- β”œβ”€β”€ encryption.py.md
141
- β”œβ”€β”€ postgresql.py.md
142
- β”œβ”€β”€ security.py.md
143
- └── user_handler.py.md
144
-
145
-
146
-
147
- ```
148
-
149
- **The Guardian pattern:** `app/*` never touches `os.environ`, `.env`, or `fundaments/` directly. Everything is injected by `main.py` as a validated `fundaments` dict. The sandbox is structural β€” not optional.
150
 
151
  ---
152
 
153
  ## Configuration (.pyfun)
154
 
155
- All app behavior is configured via `app/.pyfun` β€” a structured, human-readable config format:
 
 
 
 
 
 
 
 
156
 
157
  ```ini
158
- [LLM_PROVIDER.anthropic]
159
- active = "true"
160
- env_key = "ANTHROPIC_API_KEY"
161
- default_model = "claude-haiku-4-5-20251001"
162
- fallback_to = "gemini"
163
- [LLM_PROVIDER.anthropic_END]
 
 
 
 
164
 
165
- [TOOL.llm_complete]
166
- active = "true"
167
- provider_type = "llm"
168
- default_provider = "anthropic"
169
- timeout_sec = "60"
170
- [TOOL.llm_complete_END]
171
  ```
172
 
173
- Add a new tool/Provider/API_URL or something else just = edit `.pyfun` only. No code changes required.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
  ---
176
 
177
  ## Security Design
178
 
179
- - All API keys via e.g. HF Space Secrets β€” never hardcoded, never in `.pyfun`
180
- - `list_active_tools` returns key **names** only, never values
181
- - DB tools are `SELECT`-only, enforced at application level
182
- - Direct execution of `app/*` is blocked by design
183
- - `app/*` has zero access to `fundaments/` internals
184
- - Built on [PyFundaments](PyFundaments.md) β€” security-first Python architecture
185
 
186
- > PyFundaments is not perfect. But it's more secure than most of what runs in production today!
 
 
187
 
188
  ---
189
 
190
  ## Foundation
191
 
192
- - [PyFundaments](PyFundaments.md) β€” Security-first Python boilerplate
193
- - [PyFundaments Function Overview](Fundaments-–-Function---Overview.md)
194
- - [PROJECT_STRUCTURE.md](PROJECT_STRUCTURE.md)
195
- - [SECURITY.md](SECURITY.md)
 
 
 
 
 
 
 
 
 
196
 
197
  ---
198
 
199
  ## History
200
 
201
- [ShellMaster](https://github.com/VolkanSah/ChatGPT-ShellMaster) (2023, archived, MIT) was the precursor β€” a browser-accessible shell for ChatGPT with session memory via `/tmp/shellmaster_brain.log`, built before MCP was a word. Universal MCP Hub is its natural evolution.
202
 
203
  ---
204
 
@@ -209,12 +421,11 @@ Dual-licensed:
209
  - [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)
210
  - [Ethical Security Operations License v1.1 (ESOL)](ESOL) β€” mandatory, non-severable
211
 
212
- By using this software you agree to all ethical constraints defined in ESOL v1.1. Misuse may result in automatic license termination and legal liability.
213
 
214
  ---
215
 
216
- *Architecture, security decisions, and PyFundaments by Volkan KΓΌcΓΌkbudak. Built with Claude (Anthropic) as a typing assistant for docs & some bugs*
217
-
218
- > crafted with passion by {Volkan KΓΌcΓΌkbudak](https://github.com/volkansah/) - just want to feel how it works, mean i do not need it, have cli πŸ˜„
219
-
220
 
 
 
1
  ---
2
  title: Universal MCP Hub
3
+ emoji: πŸ›‘οΈ
4
  colorFrom: indigo
5
  colorTo: red
6
  sdk: docker
7
  pinned: false
8
  license: apache-2.0
9
+ short_description: 'Sandboxed Universal MCP Server built on PyFundaments'
10
  ---
11
 
12
  # Universal MCP Hub (Sandboxed)
 
13
 
14
+ > A production-grade MCP server that actually thinks about security.
15
+ > Built on [PyFundaments](PyFundaments.md) β€” running on **simpleCity** and **paranoidMode**.
16
 
17
+ ```
18
+ No key β†’ no tool β†’ no crash β†’ no exposed secrets
19
+ ```
20
 
21
+ Most MCP servers are prompts dressed up as servers. This one has a real architecture.
22
+
23
+ ---
24
 
25
+ ## Why this exists
26
 
27
+ The MCP ecosystem is full of servers with hardcoded keys, zero sandboxing, and `os.environ` scattered everywhere. One misconfigured fork and your API keys are gone.
 
 
 
28
 
29
+ This hub was built as the antidote:
30
+
31
+ - **Structural sandboxing** β€” `app/*` can never touch `fundaments/` or `.env`. Not by convention. By design.
32
+ - **Guardian pattern** β€” `main.py` is the only process that reads secrets. It injects validated services as a dict. `app/*` never sees the raw environment.
33
+ - **Graceful degradation** β€” No key? Tool doesn't register. Server still starts. No crash, no error, no empty `None` floating around.
34
+ - **Single source of truth** β€” All tool/provider/model config lives in `app/.pyfun`. Adding a provider = edit one file. No code changes.
35
 
36
  ---
37
 
38
+ ## Architecture
39
+
40
+ ```
41
+ main.py (Guardian)
42
+ β”‚
43
+ β”‚ reads .env / HF Secrets
44
+ β”‚ initializes fundaments/* conditionally
45
+ β”‚ injects validated services as dict
46
+ β”‚
47
+ └──► app/app.py (Orchestrator, sandboxed)
48
+ β”‚
49
+ β”‚ unpacks fundaments ONCE, at startup, never stores globally
50
+ β”‚ starts hypercorn (async ASGI)
51
+ β”‚ routes: GET / | POST /api | GET+POST /mcp
52
+ β”‚
53
+ β”œβ”€β”€ app/mcp.py ← FastMCP + SSE handler
54
+ β”œβ”€β”€ app/tools.py ← Tool registry (key-gated)
55
+ β”œβ”€β”€ app/provider.py ← LLM + Search execution + fallback chain
56
+ β”œβ”€β”€ app/models.py ← Model limits, costs, capabilities
57
+ β”œβ”€β”€ app/config.py ← .pyfun parser (single source of truth)
58
+ └── app/db_sync.py ← Internal SQLite IPC (app/* state only)
59
+ β‰  fundaments/postgresql.py (Guardian-only)
60
+ ```
61
+
62
+ **The sandbox is structural:**
63
+
64
+ ```python
65
+ # app/app.py β€” fundaments are unpacked ONCE, NEVER stored globally
66
+ async def start_application(fundaments: Dict[str, Any]) -> None:
67
+ config_service = fundaments["config"]
68
+ db_service = fundaments["db"] # None if not configured
69
+ encryption_service = fundaments["encryption"] # None if keys missing
70
+ access_control_service = fundaments["access_control"]
71
+ ...
72
+ # From here: app/* reads its own config from app/.pyfun only.
73
+ # fundaments are never passed into other app/* modules.
74
+ ```
75
+
76
+ `app/app.py` never calls `os.environ`. Never imports from `fundaments/`. Never reads `.env`.
77
+ This isn't documentation. It's enforced by the import structure.
78
+
79
+ ### Why Quart + hypercorn?
80
+
81
+ MCP over SSE needs a proper async HTTP stack. The choice here is deliberate:
82
+
83
+ **Quart** is async Flask β€” same API, same routing, but fully `async/await` native. This matters because FastMCP's SSE handler is async, and mixing sync Flask with async MCP would require thread hacks or `asyncio.run()` gymnastics. With Quart, the `/mcp` route hands off directly to `mcp.handle_sse(request)` β€” no bridging, no blocking.
84
+
85
+ **hypercorn** is an ASGI server (vs. waitress/gunicorn which are WSGI). WSGI servers handle one request per thread β€” fine for traditional web apps, wrong for SSE where a connection stays open for minutes. hypercorn handles SSE connections as long-lived async streams without tying up threads. It also runs natively on HuggingFace Spaces without extra config.
86
+
87
+ The `/mcp` route in `app.py` is also the natural interception point β€” auth checks, rate limiting, payload logging can all be added there before the request ever reaches FastMCP. That's not possible when FastMCP runs standalone.
88
+
89
+ ---
90
+
91
+ ## Two Databases β€” One Architecture
92
+
93
+ This hub runs **two completely separate databases** with distinct responsibilities. This is not redundancy β€” it's a deliberate performance and security decision.
94
+
95
+ ```
96
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€οΏ½οΏ½β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
97
+ β”‚ Guardian Layer (fundaments/*) β”‚
98
+ β”‚ β”‚
99
+ β”‚ postgresql.py β†’ Cloud DB (e.g. Neon, Supabase) β”‚
100
+ β”‚ asyncpg pool, SSL enforced β”‚
101
+ β”‚ Neon-specific quirks handled β”‚
102
+ β”‚ (statement_timeout stripped, keepalives) β”‚
103
+ β”‚ β”‚
104
+ β”‚ user_handler.py β†’ SQLite (users + sessions tables) β”‚
105
+ β”‚ PBKDF2-SHA256 password hashing β”‚
106
+ β”‚ Session validation incl. IP + UserAgent β”‚
107
+ β”‚ Account lockout after 5 failed attempts β”‚
108
+ β”‚ Path: SQLITE_PATH env var or app/ β”‚
109
+ β”‚ β”‚
110
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
111
+ β”‚ inject as fundaments dict
112
+ β–Ό
113
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
114
+ β”‚ App Layer (app/*) β”‚
115
+ β”‚ β”‚
116
+ β”‚ db_sync.py β†’ SQLite (hub_state + tool_cache tables) β”‚
117
+ β”‚ aiosqlite (async, non-blocking) β”‚
118
+ β”‚ NEVER touches users/sessions tables β”‚
119
+ β”‚ Relocated to /tmp/ on HF Spaces auto β”‚
120
+ β”‚ β”‚
121
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
122
+ ```
123
+
124
+ **Why two SQLite databases?**
125
 
126
+ `user_handler.py` (Guardian) owns `users` and `sessions` β€” authentication state that must be isolated from the app layer. `db_sync.py` (app/*) owns `hub_state` and `tool_cache` β€” fast, async IPC between tools that doesn't need to leave the process, let alone hit a cloud endpoint.
 
 
127
 
128
+ A tool caching a previous LLM response or storing intermediate state between pipeline steps should never wait on a round-trip to Neon. Local SQLite is microseconds. Cloud PostgreSQL is 50-200ms per query. For tool-to-tool communication, that difference matters.
129
 
130
+ **Table ownership β€” hard rule:**
131
+
132
+ | Table | Owner | Access |
133
+ | :--- | :--- | :--- |
134
+ | `users` | `fundaments/user_handler.py` | Guardian only |
135
+ | `sessions` | `fundaments/user_handler.py` | Guardian only |
136
+ | `hub_state` | `app/db_sync.py` | app/* only |
137
+ | `tool_cache` | `app/db_sync.py` | app/* only |
138
+
139
+ `db_sync.py` uses the same SQLite path (`SQLITE_PATH`) as `user_handler.py` β€” same file, different tables, zero overlap. The `db_query` MCP tool exposes SELECT-only access to `hub_state` and `tool_cache`. It cannot reach `users` or `sessions`.
140
+
141
+ **Cloud DB (postgresql.py):**
142
+
143
+ Handles the heavy cases β€” persistent storage, workflow tool results that need to survive restarts, anything that benefits from a real relational DB. Neon-specific quirks are handled automatically: `statement_timeout` is stripped from the DSN (Neon doesn't support it), SSL is enforced at `require` minimum, keepalives are set, and terminated connections trigger an automatic pool restart.
144
+
145
+ If no `DATABASE_URL` is set, the entire cloud DB layer is skipped cleanly. The app runs without it.
146
 
147
  ---
148
 
149
+ ## Tools
150
 
151
+ Tools register themselves at startup β€” only if the required API key exists in the environment. No key, no tool. The server always starts.
152
 
153
+ | ENV Secret | Tool | Notes |
154
  | :--- | :--- | :--- |
155
  | `ANTHROPIC_API_KEY` | `llm_complete` | Claude Haiku / Sonnet / Opus |
156
+ | `GEMINI_API_KEY` | `llm_complete` | Gemini 2.0 / 2.5 / 3.x Flash & Pro |
157
  | `OPENROUTER_API_KEY` | `llm_complete` | 100+ models via OpenRouter |
158
  | `HF_TOKEN` | `llm_complete` | HuggingFace Inference API |
159
+ | `BRAVE_API_KEY` | `web_search` | Independent web index |
160
+ | `TAVILY_API_KEY` | `web_search` | AI-optimized search with synthesized answers |
161
+ | `DATABASE_URL` | `db_query` | Read-only SELECT β€” enforced at app level |
162
+ | *(always)* | `list_active_tools` | Shows key names only β€” never values |
163
+ | *(always)* | `health_check` | Status + uptime |
164
+ | *(always)* | `get_model_info` | Limits, costs, capabilities per model |
165
+
166
+ **Configured in `.pyfun` β€” not hardcoded:**
167
+
168
+ ```ini
169
+ [TOOL.code_review]
170
+ active = "true"
171
+ description = "Review code for bugs, security issues and improvements"
172
+ provider_type = "llm"
173
+ default_provider = "anthropic"
174
+ timeout_sec = "60"
175
+ system_prompt = "You are an expert code reviewer. Analyze the given code for bugs, security issues, and improvements. Be specific and concise."
176
+ [TOOL.code_review_END]
177
+ ```
178
 
179
+ Current built-in tools: `llm_complete`, `code_review`, `summarize`, `translate`, `web_search`, `db_query`
180
+ Future hooks (commented, ready): `image_gen`, `code_exec`, `shellmaster`, Discord, GitHub webhooks
181
 
182
  ---
183
 
184
+ ## LLM Fallback Chain
185
+
186
+ All LLM providers share one `llm_complete` tool. If a provider fails, the hub automatically walks the fallback chain defined in `.pyfun`:
187
 
188
+ ```
189
+ anthropic β†’ gemini β†’ openrouter β†’ huggingface
190
+ ```
191
+
192
+ Fallbacks are configured per-provider, not hardcoded:
193
+
194
+ ```ini
195
+ [LLM_PROVIDER.anthropic]
196
+ fallback_to = "gemini"
197
+ [LLM_PROVIDER.anthropic_END]
198
+
199
+ [LLM_PROVIDER.gemini]
200
+ fallback_to = "openrouter"
201
+ [LLM_PROVIDER.gemini_END]
202
+ ```
203
+
204
+ Same pattern applies to search providers (`brave β†’ tavily`).
205
+
206
+ ---
207
+
208
+ ## Quick Start
209
+
210
+ ### HuggingFace Spaces (recommended)
211
+
212
+ 1. Fork / duplicate this Space
213
+ 2. Go to **Settings β†’ Variables and secrets**
214
+ 3. Add the API keys you have (any subset works)
215
+ 4. Space starts automatically β€” only tools with valid keys register
216
+
217
+ That's it. No config editing. No code changes.
218
+
219
+ [β†’ Live Demo Space](https://huggingface.co/spaces/codey-lab/Universal-MCP-Hub-DEMO)
220
+
221
+ ### Local / Docker
222
+
223
+ ```bash
224
+ git clone https://github.com/VolkanSah/Universal-MCP-Hub-sandboxed
225
+ cd Universal-MCP-Hub-sandboxed
226
+ cp example-mcp___.env .env
227
+ # fill in your keys
228
+ pip install -r requirements.txt
229
+ python main.py
230
+ ```
231
+
232
+ Minimum required ENV vars (everything else is optional):
233
+
234
+ ```env
235
+ PYFUNDAMENTS_DEBUG=""
236
+ LOG_LEVEL="INFO"
237
+ LOG_TO_TMP=""
238
+ ENABLE_PUBLIC_LOGS="true"
239
+ HF_TOKEN=""
240
+ HUB_SPACE_URL=""
241
+ MCP_TRANSPORT="sse"
242
+ ```
243
+
244
+ ---
245
+
246
+ ## Connect an MCP Client
247
+
248
+ ### Claude Desktop / any SSE-compatible client
249
 
250
  ```json
251
  {
 
257
  }
258
  ```
259
 
260
+ ### Private Space (with HF token)
261
 
262
  ```json
263
  {
 
275
  ---
276
 
277
  ## Desktop Client
 
278
 
279
+ A full PySide6 desktop client is included in `DESKTOP_CLIENT/hub.py` β€” ideal for private or non-public Spaces where you don't want to expose the SSE endpoint.
280
 
281
+ ```bash
282
+ pip install PySide6 httpx
283
+ # optional file handling:
284
+ pip install Pillow PyPDF2 pandas openpyxl
285
+ python DESKTOP_CLIENT/hub.py
286
+ ```
287
 
288
+ **Features:**
289
+ - Multi-chat with persistent history (`~/.mcp_desktop.json`)
290
+ - Tool/Provider/Model selector loaded live from your Hub
291
+ - File attachments: images, PDF, CSV, Excel, ZIP, source code
292
+ - Connect tab with health check + auto-load
293
+ - Settings: HF Token + Hub URL saved locally, never sent anywhere except your own Hub
294
+ - Full request/response log with timestamps
295
+ - Runs on Windows, Linux, macOS
296
 
297
+ [β†’ Desktop Client docs](DESKTOP_CLIENT/README.md)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
 
299
  ---
300
 
301
  ## Configuration (.pyfun)
302
 
303
+ `app/.pyfun` is the single source of truth for all app behavior. Three tiers β€” use what you need:
304
+
305
+ ```
306
+ LAZY: [HUB] + one [LLM_PROVIDER.*] β†’ works
307
+ NORMAL: + [SEARCH_PROVIDER.*] + [MODELS.*] β†’ works better
308
+ PRODUCTIVE: + [TOOLS] + [HUB_LIMITS] + [DB_SYNC] β†’ full power
309
+ ```
310
+
311
+ Adding a new LLM provider requires two steps β€” `.pyfun` + one line in `providers.py`:
312
 
313
  ```ini
314
+ # 1. app/.pyfun β€” add provider block
315
+ [LLM_PROVIDER.mistral]
316
+ active = "true"
317
+ base_url = "https://api.mistral.ai/v1"
318
+ env_key = "MISTRAL_API_KEY"
319
+ default_model = "mistral-large-latest"
320
+ models = "mistral-large-latest, mistral-small-latest, codestral-latest"
321
+ fallback_to = ""
322
+ [LLM_PROVIDER.mistral_END]
323
+ ```
324
 
325
+ ```python
326
+ # 2. app/providers.py β€” uncomment the dummy + register it
327
+ _PROVIDER_CLASSES = {
328
+ ...
329
+ "mistral": MistralProvider, # ← uncomment to activate
330
+ }
331
  ```
332
 
333
+ `providers.py` ships with ready-to-use commented dummy classes for OpenAI, Mistral, and xAI/Grok β€” each with the matching `.pyfun` block right above it. Most OpenAI-compatible APIs need zero changes to the class itself, just a different `base_url` and `env_key`. Search providers (Brave, Tavily) follow the same pattern and are next on the roadmap.
334
+
335
+ Model limits, costs, and capabilities are also configured here β€” `get_model_info` reads directly from `.pyfun`:
336
+
337
+ ```ini
338
+ [MODEL.claude-sonnet-4-6]
339
+ provider = "anthropic"
340
+ context_tokens = "200000"
341
+ max_output_tokens = "16000"
342
+ requests_per_min = "50"
343
+ cost_input_per_1k = "0.003"
344
+ cost_output_per_1k = "0.015"
345
+ capabilities = "text, code, analysis, vision"
346
+ [MODEL.claude-sonnet-4-6_END]
347
+ ```
348
+
349
+ ---
350
+
351
+ ## Dependencies
352
+
353
+ ```
354
+ # PyFundaments Core (always required)
355
+ asyncpg β€” async PostgreSQL pool (Guardian/cloud DB)
356
+ python-dotenv β€” .env loading
357
+ passlib β€” PBKDF2 password hashing in user_handler.py
358
+ cryptography β€” encryption layer in fundaments/
359
+
360
+ # MCP Hub
361
+ fastmcp β€” MCP protocol + tool registration
362
+ httpx β€” async HTTP for all provider API calls
363
+ quart β€” async Flask (ASGI) β€” needed for SSE + hypercorn
364
+ hypercorn β€” ASGI server β€” long-lived SSE connections, HF Spaces native
365
+ requests β€” sync HTTP for tool workers
366
+
367
+ # Optional (uncomment in requirements.txt as needed)
368
+ # aiofiles β€” async file ops (ML pipelines, file uploads)
369
+ # discord.py β€” Discord bot integration (app/discord_api.py, planned)
370
+ # PyNaCl β€” Discord signature verification
371
+ # psycopg2-binary β€” alternative PostgreSQL driver
372
+ ```
373
+
374
+ The core stack is intentionally lean. `asyncpg` + `quart` + `hypercorn` + `fastmcp` + `httpx` covers the full MCP server. Everything else is opt-in.
375
 
376
  ---
377
 
378
  ## Security Design
379
 
380
+ - API keys live in HF Secrets / `.env` β€” never in `.pyfun`, never in code
381
+ - `list_active_tools` returns key **names** only β€” never values
382
+ - `db_query` is SELECT-only, enforced at application level (not just docs)
383
+ - `app/*` has zero import access to `fundaments/` internals
384
+ - Direct execution of `app/app.py` is blocked by design β€” prints a warning and uses a null-fundaments dict
385
+ - `fundaments/` is initialized conditionally β€” missing services degrade gracefully, they don't crash
386
 
387
+ > PyFundaments is not perfect. But it's more secure than most of what runs in production today.
388
+
389
+ [β†’ Full Security Policy](SECURITY.md)
390
 
391
  ---
392
 
393
  ## Foundation
394
 
395
+ This hub is built on [PyFundaments](PyFundaments.md) β€” a security-first Python boilerplate providing:
396
+
397
+ - `config_handler.py` β€” env loading with validation
398
+ - `postgresql.py` β€” async DB pool (Guardian-only)
399
+ - `encryption.py` β€” key-based encryption layer
400
+ - `access_control.py` β€” role/permission management
401
+ - `user_handler.py` β€” user lifecycle management
402
+ - `security.py` β€” unified security manager composing the above
403
+
404
+ None of these are accessible from `app/*`. They are injected as a validated dict by `main.py`.
405
+
406
+ [β†’ PyFundaments Function Overview](PyFundaments%20–%20Function%20Overview.md)
407
+ [β†’ Module Docs](docs/app/)
408
 
409
  ---
410
 
411
  ## History
412
 
413
+ [ShellMaster](https://github.com/VolkanSah/ChatGPT-ShellMaster) (2023, MIT) was the precursor β€” browser-accessible shell for ChatGPT with session memory via `/tmp/shellmaster_brain.log`, built before MCP was even a concept. Universal MCP Hub is its natural evolution.
414
 
415
  ---
416
 
 
421
  - [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)
422
  - [Ethical Security Operations License v1.1 (ESOL)](ESOL) β€” mandatory, non-severable
423
 
424
+ By using this software you agree to all ethical constraints defined in ESOL v1.1.
425
 
426
  ---
427
 
428
+ *Architecture, security decisions, and PyFundaments by Volkan KΓΌcΓΌkbudak.*
429
+ *Built with Claude (Anthropic) as a typing assistant for docs & the occasional bug.*
 
 
430
 
431
+ > crafted with passion β€” just wanted to understand how it works, don't actually need it, have a CLI πŸ˜„