Skip to content

Commit d4911e0

Browse files
ajitpratap0Ajit Pratap Singhclaude
authored
feat: remote MCP server on Render with smart rate limiting (#379)
* docs: add remote MCP server design spec Public GoSQLX MCP server on Fly.io with smart 3-layer rate limiting (tiered IP limits, adaptive load scaling, tool-aware cost weighting). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(mcp): add Handler() and Cfg() methods to expose HTTP handler chain Allows main.go to wrap the MCP handler with additional middleware (rate limiting) before starting the HTTP server. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(mcp): smart rate limiter with tiered, adaptive, tool-aware limiting Three-layer rate limiting for public MCP server deployment: - Per-IP burst limiting (10 req/sec) via token bucket - Tool-aware cost weighting (analyze_sql=5x, security_scan=2x, etc.) - Adaptive load scaling (120/60/30 weighted-req/min based on concurrency) Uses sharded map (16 shards with FNV hashing) for lock contention minimization. Returns JSON-RPC errors (not HTTP 429) per MCP protocol. Background cleanup removes stale entries every 5 minutes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(mcp): wire rate limiter and health endpoint in main Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add Dockerfile for gosqlx-mcp server Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add fly.toml and CI workflow for Fly.io MCP server deployment Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add remote MCP server connection instructions and badge Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add USER directive to Dockerfile for Trivy DS002 compliance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(mcp): cleanup race condition + use gosqlx.Version in health endpoint - Collect stale IPs first, then delete — avoids holding bucket mutex while modifying the shard map - Health endpoint now uses gosqlx.Version instead of hardcoded "1.11.1" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(lsp): increase timing margin in ResetTiming test for Windows Windows has ~15ms timer resolution so a 50ms sleep margin before the rate-limit window boundary was too tight and caused flaky failures on windows-latest runners (go 1.23). Increase pre-boundary sleep margin from 50ms→200ms and post-window buffer from 20ms→50ms so the test is robust across all platforms. * feat: switch deployment from Fly.io to Render (free tier) Fly.io no longer has a free tier (2-hour trial only). Render offers 750 hrs/month free with no credit card required. - Replace fly.toml with render.yaml blueprint - Update CI workflow to use Render deploy hooks - Update all docs/README URLs to onrender.com Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: correct Render URL to gosqlx.onrender.com The Render service was named "GoSQLX" so the URL is gosqlx.onrender.com, not gosqlx-mcp.onrender.com. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: replace all Fly.io references with Render in spec and plan Fly.io was never used — deployment went directly to Render free tier. Updated all URLs, config references, and platform-specific details. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Ajit Pratap Singh <ajitpratapsingh@Ajits-Mac-mini-2655.local> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b294d21 commit d4911e0

File tree

13 files changed

+1582
-5
lines changed

13 files changed

+1582
-5
lines changed

.dockerignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
website/
2+
.git/
3+
*.wasm
4+
wasm/playground/
5+
.superpowers/
6+
.playwright-mcp/
7+
dist/
8+
node_modules/
9+
.claude/

.github/workflows/deploy-mcp.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Deploy MCP Server
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- 'cmd/gosqlx-mcp/**'
8+
- 'pkg/mcp/**'
9+
- 'Dockerfile'
10+
- 'render.yaml'
11+
workflow_dispatch:
12+
13+
jobs:
14+
deploy:
15+
runs-on: ubuntu-latest
16+
timeout-minutes: 10
17+
if: github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main'
18+
steps:
19+
- name: Trigger Render deploy
20+
run: |
21+
if [ -n "$RENDER_DEPLOY_HOOK_URL" ]; then
22+
curl -s "$RENDER_DEPLOY_HOOK_URL"
23+
echo "Deploy triggered"
24+
else
25+
echo "RENDER_DEPLOY_HOOK_URL not configured — Render auto-deploys from GitHub"
26+
fi
27+
env:
28+
RENDER_DEPLOY_HOOK_URL: ${{ secrets.RENDER_DEPLOY_HOOK_URL }}

Dockerfile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Stage 1: Build
2+
FROM golang:1.23-alpine AS builder
3+
WORKDIR /app
4+
COPY go.mod go.sum ./
5+
RUN go mod download
6+
COPY . .
7+
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /gosqlx-mcp ./cmd/gosqlx-mcp
8+
9+
# Stage 2: Runtime
10+
FROM gcr.io/distroless/static:nonroot
11+
COPY --from=builder /gosqlx-mcp /gosqlx-mcp
12+
USER nonroot:nonroot
13+
EXPOSE 8080
14+
ENTRYPOINT ["/gosqlx-mcp"]

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
[![GitHub Marketplace](https://img.shields.io/badge/Marketplace-GoSQLX%20Lint-blue?style=for-the-badge&logo=github)](https://github.com/marketplace/actions/gosqlx-lint-action)
1414
[![VS Code Marketplace](https://img.shields.io/visual-studio-marketplace/v/ajitpratap0.gosqlx?style=for-the-badge&logo=visual-studio-code&label=VS%20Code)](https://marketplace.visualstudio.com/items?itemName=ajitpratap0.gosqlx)
1515
[![Website](https://img.shields.io/badge/Website-GoSQLX-blue?style=for-the-badge&logo=google-chrome)](https://ajitpratap0.github.io/GoSQLX/)
16+
[![MCP Server](https://img.shields.io/badge/MCP-Remote%20Server-blue?style=for-the-badge&logo=cloud)](https://gosqlx.onrender.com/health)
1617

1718
[![Tests](https://img.shields.io/github/actions/workflow/status/ajitpratap0/GoSQLX/test.yml?branch=main&label=Tests&style=flat-square)](https://github.com/ajitpratap0/GoSQLX/actions)
1819
[![Go Report Card](https://goreportcard.com/badge/github.com/ajitpratap0/GoSQLX?style=flat-square)](https://goreportcard.com/report/github.com/ajitpratap0/GoSQLX)
@@ -69,7 +70,7 @@ GoSQLX is a high-performance SQL parsing library designed for production use. It
6970
- **Zero-Copy**: Direct byte slice operations, <1μs latency
7071
- **Intelligent Errors**: Structured error codes with typo detection, context highlighting, and helpful hints
7172
- **Python Bindings**: [PyGoSQLX](python/README.md) — use GoSQLX from Python via ctypes FFI, 100x+ faster than pure Python parsers
72-
- **MCP Server** (v1.10.0): `gosqlx-mcp` exposes all 7 SQL tools as [Model Context Protocol](https://modelcontextprotocol.io) tools over streamable HTTP — integrate GoSQLX into Claude, Cursor, and any MCP-compatible AI assistant
73+
- **MCP Server** (v1.10.0): `gosqlx-mcp` exposes all 7 SQL tools as [Model Context Protocol](https://modelcontextprotocol.io) tools over streamable HTTP — integrate GoSQLX into Claude, Cursor, and any MCP-compatible AI assistant. **[Public remote server available](https://gosqlx.onrender.com/health)** — no install required
7374
- **Production Ready**: Battle-tested with 0 race conditions detected, ~85% SQL-99 compliance, Apache-2.0 licensed
7475

7576
### Performance & Quality Highlights (v1.10.0)

cmd/gosqlx-mcp/main.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,14 @@ package main
3333
import (
3434
"context"
3535
"fmt"
36+
"log"
37+
"net/http"
3638
"os"
3739
"os/signal"
3840
"syscall"
41+
"time"
3942

43+
"github.com/ajitpratap0/GoSQLX/pkg/gosqlx"
4044
gosqlxmcp "github.com/ajitpratap0/GoSQLX/pkg/mcp"
4145
)
4246

@@ -56,5 +60,34 @@ func run() error {
5660
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
5761
defer stop()
5862

59-
return gosqlxmcp.New(cfg).Start(ctx)
63+
srv := gosqlxmcp.New(cfg)
64+
65+
// Build handler chain: MCP → auth → rate limiter
66+
handler := gosqlxmcp.RateLimitMiddleware(srv.Handler())
67+
68+
mux := http.NewServeMux()
69+
mux.Handle("/mcp", handler)
70+
mux.Handle("/mcp/", handler)
71+
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
72+
w.Header().Set("Content-Type", "application/json")
73+
fmt.Fprintf(w, `{"status":"ok","version":"%s","tools":7}`, gosqlx.Version)
74+
})
75+
76+
httpSrv := &http.Server{
77+
Addr: cfg.Addr(),
78+
Handler: mux,
79+
}
80+
81+
go func() {
82+
<-ctx.Done()
83+
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
84+
defer cancel()
85+
_ = httpSrv.Shutdown(shutdownCtx)
86+
}()
87+
88+
log.Printf("gosqlx-mcp: listening on %s (auth=%v)\n", cfg.Addr(), cfg.AuthEnabled())
89+
if err := httpSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
90+
return fmt.Errorf("server error: %w", err)
91+
}
92+
return nil
6093
}

docs/MCP_GUIDE.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,77 @@ The GoSQLX MCP server (`gosqlx-mcp`) exposes all GoSQLX SQL capabilities as [Mod
4040

4141
---
4242

43+
## Remote Server (Public)
44+
45+
A public GoSQLX MCP server is available at `https://gosqlx.onrender.com/mcp` — no installation, no API key, no signup required.
46+
47+
### Claude Code
48+
49+
```bash
50+
claude mcp add --transport http gosqlx https://gosqlx.onrender.com/mcp
51+
```
52+
53+
### Claude Desktop
54+
55+
Add to your `claude_desktop_config.json`:
56+
57+
```json
58+
{
59+
"mcpServers": {
60+
"gosqlx": {
61+
"url": "https://gosqlx.onrender.com/mcp"
62+
}
63+
}
64+
}
65+
```
66+
67+
### Cursor
68+
69+
Add remote MCP server URL: `https://gosqlx.onrender.com/mcp`
70+
71+
### Available Tools
72+
73+
All 7 GoSQLX SQL tools are available instantly:
74+
- `validate_sql` — Check SQL syntax validity
75+
- `format_sql` — Format SQL with configurable options
76+
- `parse_sql` — Parse SQL into AST statement types
77+
- `extract_metadata` — Extract tables, columns, and functions
78+
- `security_scan` — Detect SQL injection patterns
79+
- `lint_sql` — Lint against 10 rules (L001–L010)
80+
- `analyze_sql` — Run all 6 tools in one concurrent request
81+
82+
### Rate Limits
83+
84+
The public server has smart rate limiting to ensure fair usage:
85+
- **Burst**: 10 requests/second per IP
86+
- **Sustained**: 120 weighted-requests/minute per IP
87+
- Heavier tools (`analyze_sql`) consume more budget than simple ones (`validate_sql`)
88+
- Limits tighten automatically under high server load
89+
90+
### Health Check
91+
92+
```bash
93+
curl https://gosqlx.onrender.com/health
94+
```
95+
96+
### Self-Hosting
97+
98+
To run your own instance, use Docker:
99+
100+
```bash
101+
docker build -t gosqlx-mcp .
102+
docker run -p 8080:8080 -e GOSQLX_MCP_HOST=0.0.0.0 gosqlx-mcp
103+
```
104+
105+
Or deploy to Render:
106+
107+
```bash
108+
# Connect repo on render.com, it auto-deploys
109+
# Or use render.yaml blueprint
110+
```
111+
112+
---
113+
43114
## Installation
44115

45116
### Install via go install (Recommended)

0 commit comments

Comments
 (0)