This guide walks you through deploying the UML-MCP server to Vercel and publishing it on Smithery so users can connect via Streamable HTTP without installing anything. For the full Smithery docs index, see [smithery.ai/docs/llms.txt](https://smithery.ai/docs/llms.txt.
- A Vercel account
- This repo connected to Vercel (GitHub/GitLab/Bitbucket)
- Connect the repo in the Vercel dashboard: Import your
uml-mcprepository. - Build settings (match vercel.json; clear dashboard overrides if they disagree):
- Framework Preset: FastAPI (
"framework": "fastapi"—needed so Vercel runs the custom install inside the Python build virtualenv) - Install Command:
bash scripts/vercel-install.sh(usesuv pipagainstrequirements.txtand verifiespydantic_core) - Build Command:
python scripts/generate_server_card.py(writespublic/.well-known/mcp/server-card.jsonand related files) - Serverless entrypoint:
api/app.py(seefunctionsinvercel.json); routing and output are handled byvercel.json
- Framework Preset: FastAPI (
- Deploy and wait for the build to finish.
Install step fails (e.g. exit code 2): Keep "framework": "fastapi" in vercel.json (not null / dashboard Framework Preset forced to Other without the FastAPI preset), so Vercel runs installCommand inside the Python build virtualenv. Do not override Install Command in the dashboard with a bare pip install that targets the system interpreter (PEP 668).
Your app will be available at:
- Production:
https://<your-project>.vercel.app - REST API:
https://<your-project>.vercel.app/(root,/health,/generate_diagram, etc.) - MCP endpoint:
https://<your-project>.vercel.app/mcp
Use the MCP URL when publishing to Smithery.
Smithery can list your server so users can add it with one click. You can either host the server on Smithery (Docker/stdio) or point Smithery to your Vercel URL (self-hosted HTTP.
Publishing a URL-based server is done only via the Smithery website; the @smithery/cli npm package has no deploy or publish command for this.
- Go to smithery.ai/new.
- Sign in if needed (Google or GitHub).
- Choose URL (bring your own hosting).
- Fill in:
- Namespace: your Smithery username (e.g.
antoinebou12) - Server ID: short slug (e.g.
uml) - MCP Server URL:
https://<your-project>.vercel.app/mcp
Replace<your-project>with your actual Vercel project URL. Use the root deployment URL (e.g.https://uml-xxx.vercel.app), not a path; the MCP endpoint is at/mcp.
- Namespace: your Smithery username (e.g.
- Submit. Smithery will use your server’s Streamable HTTP transport and will fetch metadata from
https://<your-project>.vercel.app/.well-known/mcp/server-card.jsonwhen automatic scanning is not possible.
Config schema (recommended): To remove the “No config schema provided” warning and let users set options (output dir, Kroki URL, etc.) in Smithery’s UI, add a session config schema after creating the server (see below). The npm package @smithery/cli (v2.x) does not provide publish or deploy for URL-based servers; use the web flow only.
After creating the server, open it on Smithery → Settings → Session configuration (or equivalent) and paste or upload the contents of smithery-config-schema.json. See Smithery Session Configuration for the JSON Schema format with x-from extension.
After publishing, your server will appear at:
https://smithery.ai/server/@<namespace>/<server-id>
Example: https://smithery.ai/server/@antoinebou12/uml
Improve your Smithery listing: Open Settings → General on your server’s Smithery page. Set Display name, Description, Homepage (e.g. your repo URL or https://umlmcp.vercel.app), and Server icon to improve discoverability and the Server Metadata score. Adding the session config schema (see above) improves the Configuration UX score.
AI and plugin compatibility: The app serves /.well-known/ai-plugin.json with a dynamic base URL (so OpenAI-style plugins work on any deployment), /openapi.json and /openapi.yaml for API docs and AI consumers, and /logo.png (ICO) for manifests. The server card at /.well-known/mcp/server-card.json uses correct JSON schema types for all tools so AI models and Smithery get accurate tool schemas.
Note: Hosted deployment is in private early access — contact Smithery if interested.
If you use Smithery-hosted deployment, the existing smithery.yaml defines the server. Connect your GitHub repo and deploy via the Deployments tab on your server's Smithery page. The smithery.yaml includes a Session Configuration schema for output directory, Kroki URL, log level, and other settings.
Once the server is on Smithery (or you have the Vercel URL), configure your MCP client with the Streamable HTTP URL:
- From Smithery: Use the “Add to Cursor” (or similar) button on the server page, or copy the URL Smithery shows.
- Direct: In Cursor (or another client), set the MCP server URL to:
https://<your-project>.vercel.app/mcp
Example Cursor config (.cursor/mcp.json or project MCP settings):
{
"mcpServers": {
"uml": {
"url": "https://<your-project>.vercel.app/mcp"
}
}
}Replace <your-project> with your real Vercel URL.
If Smithery shows a connection error like HTTP 401: Invalid OAuth error response: SyntaxError: Unexpected token '<', "<!doctype "... is not valid JSON, the gateway is hitting Vercel Deployment Protection: Vercel returns an HTML login page instead of JSON, so the MCP handshake fails.
Fix (choose one):
-
Disable Deployment Protection (recommended for public MCP)
In the Vercel project → Settings → Deployment Protection, turn off protection for Production and/or Preview so your app URL is publicly reachable. Then use the normal MCP URL in Smithery:https://<your-project>.vercel.app/mcp. -
Keep protection and use a bypass token
If you want to keep protection, use Vercel’s [Protection Bypass for automation](https://vercel.com/docs/deployment-protection/methods-to-bypass-deployment-protection/protection-bypass-automation. Get the bypass token from the project’s Deployment Protection settings, then in Smithery set MCP Server URL to:https://<your-project>.vercel.app/mcp?x-vercel-set-bypass-cookie=true&x-vercel-protection-bypass=YOUR_BYPASS_TOKENReplace
YOUR_BYPASS_TOKENwith the token from Vercel.
Smithery discovers your server by requesting /.well-known/mcp/server-card.json on the root of your deployment. Vercel reserves the /.well-known path and does not allow rewrites to serverless functions, so the FastAPI app may never receive that URL (404). If the path is served but only for certain HTTP methods, you can get 405 Method Not Allowed; serving the server card as a static asset fixes both (GET/HEAD return 200).
Fix: This repo serves the server card as a static file at public/.well-known/mcp/server-card.json (Vercel exposes it at /.well-known/mcp/server-card.json). The catch-all rewrite to /api/app must not apply to /.well-known/*, or those URLs never hit the static asset. In vercel.json, the final rewrite uses a path pattern that excludes .well-known, and buildCommand runs python scripts/generate_server_card.py so the JSON exists in public/ on each deploy. Before publishing to Smithery, verify: open https://<your-project>.vercel.app/.well-known/mcp/server-card.json in a browser; you should see valid JSON with serverInfo, tools, and resources. If you see 404/405, redeploy and ensure the latest vercel.json is deployed (and that public/.well-known/mcp/server-card.json exists after the build). To regenerate the file when tools change, run python scripts/generate_server_card.py. Then ensure public/.well-known/mcp/server-card.json is committed and deployed.
The MCP Streamable HTTP protocol requires clients to send an Accept header that includes text/event-stream (e.g. Accept: application/json, text/event-stream). If the client or proxy (e.g. Smithery) does not send this header, the MCP layer may respond with a JSON-RPC error: "Not Acceptable: Client must accept text/event-stream" (code -32600).
Server-side workaround: This app includes middleware that, for all requests to /mcp and /mcp/..., sets or normalizes the Accept header to application/json, text/event-stream when it is missing or does not include text/event-stream. So connections via Smithery or Cursor should work even if the client or proxy omits the header. No client or Smithery configuration change is required.
If you still see this error after deploying the latest version, ensure the middleware is present in app.py (_MCPAcceptHeaderMiddleware) and that requests to /mcp go through the FastAPI app (not a static rewrite).
If you see {"error":{"code":-32600,"message":"Bad Request: Missing session ID"}} when connecting to /mcp, the MCP Streamable HTTP transport is rejecting requests because session validation fails (e.g. on serverless, where sessions cannot persist across invocations).
Fix: Ensure FASTMCP_STATELESS_HTTP=true is set in your Vercel project. In stateless mode, each request gets a fresh context and does not require session IDs. This is already configured in vercel.json for this repo; if you use the Vercel Dashboard for env vars, add it there for Production (and Preview if needed).
On Vercel, the runtime filesystem may be read-only. If the MCP cannot write the generated diagram to disk, it still returns the Kroki URL and playground link in the tool result (only local_path is omitted). So diagram generation continues to work; clients can open the URL to view the image. Alternatively, use POST /kroki_encode with type, code, and output_format in the body to get a diagram URL without any file write (useful for serverless or when you only need a shareable link).
Production deployments set MCP_URL_ONLY=true (and MCP_MEMORY_ONLY=true, MCP_DIAGRAM_CACHE_SIZE=0) in vercel.json. In that mode the server builds Kroki and playground links locally and does not download rendered diagram bytes into the serverless function, does not return content_base64, and does not use the in-process render cache. PlantUML/Mermaid fallbacks return URLs only (no HTTP GET of the image inside the function). To restore fetching and base64 when not saving to disk, set MCP_URL_ONLY=false in the Vercel project environment. SVG scale is ignored in URL-only mode because scaling requires decoded SVG bytes.
If you see 405 Method Not Allowed on POST /mcp (e.g. “Reconnect failed” in Cursor via Smithery, or “Streamable HTTP error: Error POSTing to endpoint: {detail:Method Not Allowed}”), the client or a proxy is POSTing to an endpoint that does not allow POST.
What to check:
-
Correct MCP URL
The client must POST to the MCP endpoint (e.g.https://<your-project>.vercel.app/mcp), not to the server card or root. POSTing to/.well-known/mcp/server-card.jsonor to the root URL can return 405 because those are served as GET-only (static card) or other handlers. In Cursor/Smithery, ensure the configured URL is exactlyhttps://<your-project>.vercel.app/mcp(with/mcp). -
Vercel build and runtime logs
In the app startup logs, look for either “MCP HTTP app mounted at /mcp” (success) or “MCP HTTP fallback: GET/POST /mcp return 503 (MCP HTTP transport not available).” If you see the fallback message, the MCP app failed to load. Check for any exception logged in the same block (the app logs withexc_info=True). -
Dependencies
Confirm thatfastmcpand all modules used byget_mcp_server()(e.g. diagram tools, resources, prompts, Kroki, PlantUML) are installed and import without error. Production installs userequirements.txtviainstallCommandand Vercel’s Python/uvpipeline (requirements-dev.txtis for local development and CI only). Ensure the build completes and thatfastmcp>=2.3.1is present inrequirements.txt.
405 vs 503 fallback: When the MCP app is not mounted, this server registers both GET and POST for /mcp and returns 503 with {"detail": "MCP HTTP transport is not available."} (and OPTIONS for CORS). So if the request reaches the FastAPI app at /mcp, you would see 503, not 405. A 405 usually means the request is hitting a different handler (e.g. wrong URL or a proxy that only allows GET for that path).
MCP Streamable HTTP keeps a long-lived GET connection open (Server-Sent Events) for the session. Vercel terminates serverless invocations after a maximum duration, so the function that handles GET /mcp is killed when that limit is reached (default 300 seconds on Hobby and Pro).
What you see: Logs show GET 200 /mcp/ followed by Vercel Runtime Timeout Error: Task timed out after 300 seconds or Terminating session: None. The client (Cursor, Smithery, etc.) then loses the connection and may show a reconnect or timeout error.
Mitigations:
-
Increase max duration (Pro/Enterprise)
This repo setsmaxDuration: 200invercel.jsonunderfunctions["api/app.py"]. On Pro or Enterprise (with Fluid Compute), you may raise that value (for example toward 800 seconds) so the GET/mcpconnection can stay open longer than the default 300s platform cap on Hobby. On Hobby, the cap remains 300s regardless. -
Reconnect on timeout
Clients should reconnect when the stream ends. Cursor and Smithery typically retry or allow re-adding the server; after a timeout, reconnect to/mcpto start a new session. -
Long-lived or heavy use
For sessions that must stay open indefinitely (or to avoid timeouts entirely), use Smithery-hosted deployment (Docker) or self-host the server (e.g. Docker, a long-running process) instead of Vercel serverless.
Smithery does not publish a Python package on PyPI under the name smithery-cli. The official CLI is Node/npm: @smithery/cli.
Use instead:
- One-off:
npx -y @smithery/cli <command>(see Smithery docs). - Global:
npm install -g @smithery/clithen runsmithery ....
This repository’s workflow .github/workflows/deploy.yml uses npx -y @smithery/cli publish ... with Node 20+. If your workflow still runs pip install smithery-cli, remove that step, add Node 20+ (for example actions/setup-node), then call the CLI via npx or npm.
If npx -y @smithery/cli (e.g. publish or deploy) fails with ReferenceError: File is not defined (in undici), the CLI is running on Node.js 18. The File global is only available in Node.js 20+.
Fix (pick one):
- Use Node 20+ when running the CLI (e.g. install Node 20 or use nvm:
nvm use 20then run thedeploycommand again). - Use the Smithery website instead: Create the URL-based server at smithery.ai/new (choose URL, set MCP Server URL to
https://<your-project>.vercel.app/mcp). Then in Settings → Session configuration, paste the contents ofsmithery-config-schema.json. No CLI required.
- 405 Method Not Allowed on the server card URL: usually fixed by ensuring the server card is served as a static asset at
/.well-known/mcp/server-card.jsonso GET/HEAD return 200 (see the 404/405 section above). - 502 / timeout: Vercel serverless functions have a max duration (300s default on Hobby; Pro/Enterprise can set a higher
maxDurationonapi/app.pyinvercel.json). The MCP GET connection is long-lived and will hit this limit; see the timeout section above. For very long sessions or heavy use, consider Smithery-hosted Docker. - MCP at /mcp: Ensure you use the path
/mcp(e.g.https://...vercel.app/mcp), not the root URL. - CORS: The app allows all origins for API and MCP; restrict in production if needed.
- Logo or OpenAPI YAML:
/logo.pngis served by the app (image/x-icon)./openapi.yamlreturns the spec in YAML when PyYAML is installed (required inrequirements.txt); if not, it returns 501 and clients should use/openapi.json.