Jofthomas commited on
Commit
30f4f7f
·
1 Parent(s): c103f98
Files changed (2) hide show
  1. server.py +12 -33
  2. static/index.html +104 -0
server.py CHANGED
@@ -1,6 +1,7 @@
1
  import contextlib
2
  from fastapi import FastAPI
3
- from fastapi.responses import HTMLResponse
 
4
  from echo_server import mcp as echo_mcp
5
  from math_server import mcp as math_mcp
6
  import os
@@ -15,46 +16,24 @@ async def lifespan(app: FastAPI):
15
  yield
16
 
17
 
 
 
 
18
  app = FastAPI(lifespan=lifespan)
19
 
 
 
 
 
20
  @app.get("/", response_class=HTMLResponse)
21
  async def index():
22
- return """
23
- <!doctype html>
24
- <html lang="en">
25
- <head>
26
- <meta charset="utf-8" />
27
- <meta name="viewport" content="width=device-width, initial-scale=1" />
28
- <title>Multiple MCP Servers Template</title>
29
- <style>
30
- body { font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif; margin: 2rem; line-height: 1.5; }
31
- code, pre { background: #f6f8fa; padding: 0.2rem 0.4rem; border-radius: 4px; }
32
- a { color: #2563eb; text-decoration: none; }
33
- a:hover { text-decoration: underline; }
34
- .container { max-width: 800px; }
35
- h1 { margin-bottom: 0.5rem; }
36
- .routes { margin-top: 1rem; }
37
- </style>
38
- </head>
39
- <body>
40
- <div class="container">
41
- <h1>Multiple MCP Servers Template</h1>
42
- <p>This FastAPI app demonstrates how to host multiple Model Context Protocol (MCP) servers on a single server instance.</p>
43
- <p>The following MCP servers are mounted under this app:</p>
44
- <ul class="routes">
45
- <li><a href="/echo">/echo</a> — Echo MCP server</li>
46
- <li><a href="/math">/math</a> — Math MCP server</li>
47
- </ul>
48
- <p>Use these routes as sub-apps or as examples for adding more MCP servers.</p>
49
- </div>
50
- </body>
51
- </html>
52
- """
53
 
54
  app.mount("/echo", echo_mcp.streamable_http_app())
55
  app.mount("/math", math_mcp.streamable_http_app())
56
 
57
- PORT = os.environ.get("PORT", 10000)
58
 
59
  if __name__ == "__main__":
60
  import uvicorn
 
1
  import contextlib
2
  from fastapi import FastAPI
3
+ from fastapi.responses import HTMLResponse, FileResponse
4
+ from fastapi.staticfiles import StaticFiles
5
  from echo_server import mcp as echo_mcp
6
  from math_server import mcp as math_mcp
7
  import os
 
16
  yield
17
 
18
 
19
+ BASE_DIR = os.path.dirname(__file__)
20
+ STATIC_DIR = os.path.join(BASE_DIR, "static")
21
+
22
  app = FastAPI(lifespan=lifespan)
23
 
24
+ # Serve static assets (screenshot, styles) and the root HTML page
25
+ app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
26
+
27
+
28
  @app.get("/", response_class=HTMLResponse)
29
  async def index():
30
+ return FileResponse(os.path.join(STATIC_DIR, "index.html"), media_type="text/html")
31
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  app.mount("/echo", echo_mcp.streamable_http_app())
34
  app.mount("/math", math_mcp.streamable_http_app())
35
 
36
+ PORT = int(os.environ.get("PORT", "10000"))
37
 
38
  if __name__ == "__main__":
39
  import uvicorn
static/index.html ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Multiple MCP Servers — FastAPI Template</title>
7
+ <style>
8
+ :root {
9
+ --bg: #0b1220;
10
+ --card: #0f172a;
11
+ --muted: #93a2b8;
12
+ --primary: #60a5fa;
13
+ --accent: #a78bfa;
14
+ --ring: rgba(99, 102, 241, 0.35);
15
+ }
16
+ * { box-sizing: border-box; }
17
+ body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif; color: #e5e7eb; background: radial-gradient(1200px 600px at 10% -10%, rgba(99,102,241,0.18), transparent), radial-gradient(1200px 600px at 90% 110%, rgba(56,189,248,0.12), transparent), var(--bg); }
18
+ a { color: var(--primary); text-decoration: none; }
19
+ a:hover { text-decoration: underline; }
20
+
21
+ .container { max-width: 980px; margin: 0 auto; padding: 32px 20px 64px; }
22
+ .hero { display: grid; grid-template-columns: 1.2fr 1fr; gap: 24px; align-items: center; }
23
+ .card { background: linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.02)); border: 1px solid rgba(148,163,184,0.15); border-radius: 16px; box-shadow: 0 10px 40px rgba(0,0,0,0.3); padding: 28px; }
24
+ h1 { font-size: 32px; margin: 0 0 8px; letter-spacing: -0.02em; }
25
+ .subtitle { color: var(--muted); margin: 0 0 16px; }
26
+ .badge { display: inline-block; background: rgba(96,165,250,0.12); color: #c7d2fe; border: 1px solid rgba(165,180,252,0.35); padding: 6px 10px; border-radius: 999px; font-size: 12px; margin-bottom: 12px; }
27
+
28
+ .grid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 16px; margin-top: 12px; }
29
+ .tile { background: rgba(15,23,42,0.6); border: 1px solid rgba(148,163,184,0.12); border-radius: 12px; padding: 16px; }
30
+ .tile h3 { margin: 0 0 8px; font-size: 16px; }
31
+ .muted { color: var(--muted); }
32
+
33
+ pre { background: #0b1328; border: 1px solid rgba(148,163,184,0.15); border-radius: 10px; padding: 12px 14px; overflow: auto; box-shadow: inset 0 0 0 1px rgba(99,102,241,0.1); }
34
+ code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; font-size: 12.5px; }
35
+
36
+ .screenshot { width: 100%; border-radius: 12px; border: 1px solid rgba(148,163,184,0.18); background: #0b1328; box-shadow: 0 8px 28px rgba(0,0,0,0.35); }
37
+
38
+ .footer { margin-top: 28px; color: var(--muted); font-size: 13px; text-align: center; }
39
+
40
+ @media (max-width: 900px) { .hero { grid-template-columns: 1fr; } }
41
+ </style>
42
+ </head>
43
+ <body>
44
+ <div class="container">
45
+ <div class="hero">
46
+ <div class="card">
47
+ <span class="badge">FastAPI • MCP</span>
48
+ <h1>Host multiple MCP servers on a single app</h1>
49
+ <p class="subtitle">This template mounts multiple Model Context Protocol (MCP) servers under one FastAPI instance.</p>
50
+
51
+ <div class="grid">
52
+ <div class="tile">
53
+ <h3>Available servers</h3>
54
+ <ul class="muted">
55
+ <li><a href="/echo">/echo</a> — Echo MCP server</li>
56
+ <li><a href="/math">/math</a> — Math MCP server</li>
57
+ </ul>
58
+ </div>
59
+ <div class="tile">
60
+ <h3>Base URL</h3>
61
+ <p class="muted">Each server lives at <code>{base_URL}/&lt;server-name&gt;</code>. Replace <code>{base_URL}</code> with your Space's origin.</p>
62
+ </div>
63
+ </div>
64
+
65
+ <h3 style="margin:16px 0 8px">How to get your {base_URL}</h3>
66
+ <ol class="muted" style="margin:0 0 12px 18px">
67
+ <li>Open your Space and click <strong>“Embed this Space”</strong>.</li>
68
+ <li>Copy the <strong>iframe</strong> code and take the value of the <strong>src</strong> attribute.</li>
69
+ <li>That origin (e.g. <code>https://your-space.hf.space</code>) is your <code>{base_URL}</code>.</li>
70
+ </ol>
71
+
72
+ <pre><code>Example
73
+
74
+ base_URL = https://your-space-name.hf.space
75
+
76
+ Echo MCP = {base_URL}/echo
77
+ Math MCP = {base_URL}/math
78
+ </code></pre>
79
+
80
+ <p class="muted" style="margin:10px 0 8px">Illustration of the “Embed this Space” dialog:</p>
81
+ <img class="screenshot" src="/static/embed.png" alt="Embed this Space dialog showing iframe src base URL" />
82
+ <p class="muted" style="font-size:12px;margin-top:6px">If the image doesn’t load yet, upload your screenshot to <code>Multiple_mcp_fastapi_template/static/embed.png</code>.</p>
83
+ </div>
84
+
85
+ <div class="card">
86
+ <h3 style="margin-top:0">Quick links</h3>
87
+ <ul>
88
+ <li><a href="/echo">Open /echo</a></li>
89
+ <li><a href="/math">Open /math</a></li>
90
+ </ul>
91
+ <h3>Use in clients</h3>
92
+ <p class="muted">Point your MCP client to the endpoints below:</p>
93
+ <pre><code>HTTP streaming endpoints
94
+
95
+ Echo: GET {base_URL}/echo
96
+ Math: GET {base_URL}/math
97
+ </code></pre>
98
+ </div>
99
+ </div>
100
+
101
+ <p class="footer">Built with FastAPI. This page is just a static HTML file served from <code>/static/index.html</code>. Credit: discovered via <a href="https://youtu.be/wXAqv8uvY0M" target="_blank" rel="noopener noreferrer">this video</a>.</p>
102
+ </div>
103
+ </body>
104
+ </html>