# Render MCP Server The Render MCP Server is a Model Context Protocol (MCP) implementation that provides programmatic access to Render cloud platform resources through LLM integrations. Built in Go, it exposes Render's infrastructure management capabilities including web services, static sites, PostgreSQL databases, Redis key-value stores, logs, metrics, and deployments through a standardized MCP interface. This server enables AI assistants to create and manage cloud infrastructure, query databases, monitor application performance, troubleshoot issues through log analysis, and track deployment history. It supports both stdio transport for desktop AI applications and HTTP transport with session management for web-based integrations, making it suitable for interactive development workflows and automated infrastructure operations. ## APIs and Key Functions ### Workspace Management List and select workspaces to scope all subsequent operations to the correct organizational context. ```go // List all accessible workspaces // Returns JSON array of workspace objects with id and name fields // Automatically selects workspace if only one exists import ( "context" "github.com/render-oss/render-mcp-server/pkg/owner" "github.com/render-oss/render-mcp-server/pkg/client" ) func listWorkspaces(ctx context.Context, c *client.ClientWithResponses) { ownerRepo := owner.NewRepo(c) workspaces, err := ownerRepo.ListOwners(ctx, owner.ListInput{}) if err != nil { panic(err) } // Response: [{"id":"own-123abc","name":"My Team","email":"team@example.com"}] } // Select a specific workspace for all operations // Required before creating resources func selectWorkspace(ctx context.Context, ownerID string) { sess := session.FromContext(ctx) err := sess.SetWorkspace(ctx, ownerID) if err != nil { panic(err) } // All subsequent operations will use this workspace } ``` ### Web Service Management Create and manage web services with automatic deployments from Git repositories. ```go // Create a new Node.js web service // Automatically deploys on branch updates import ( "context" "github.com/render-oss/render-mcp-server/pkg/service" "github.com/render-oss/render-mcp-server/pkg/client" ) func createWebService(ctx context.Context, c *client.ClientWithResponses) { serviceRepo := service.NewRepo(c) // Build request with required parameters envVars := []client.EnvVarInput{} envVar := client.EnvVarInput{} envVar.FromEnvVarKeyValue(client.EnvVarKeyValue{ Key: "NODE_ENV", Value: "production", }) envVars = append(envVars, envVar) repo := "https://github.com/username/my-app.git" branch := "main" region := client.Oregon plan := client.PaidPlanStarter nativeEnv := client.NativeEnvironmentDetailsPOST{ BuildCommand: "npm install && npm run build", StartCommand: "npm start", } envDetails := client.EnvSpecificDetailsPOST{} envDetails.FromNativeEnvironmentDetailsPOST(nativeEnv) webServiceDetails := client.WebServiceDetailsPOST{ Runtime: client.ServiceRuntimeNode, EnvSpecificDetails: &envDetails, Plan: &plan, Region: ®ion, } serviceDetails := client.ServicePOST_ServiceDetails{} serviceDetails.FromWebServiceDetailsPOST(webServiceDetails) request := client.CreateServiceJSONRequestBody{ Name: "my-api-service", OwnerId: "own-123abc", Type: client.WebService, ServiceDetails: &serviceDetails, Repo: &repo, Branch: &branch, EnvVars: &envVars, } response, err := serviceRepo.CreateService(ctx, request) if err != nil { panic(err) } // Response: {"service":{"id":"srv-xyz789","name":"my-api-service","createdAt":"2025-11-01T10:00:00Z"}} } // List all services in workspace func listServices(ctx context.Context, serviceRepo *service.Repo) { params := &client.ListServicesParams{} includePreviews := false params.IncludePreviews = &includePreviews services, err := serviceRepo.ListServices(ctx, params) if err != nil { panic(err) } // Response: [{"id":"srv-xyz789","name":"my-api-service","type":"web_service"}] } // Get specific service details func getService(ctx context.Context, serviceRepo *service.Repo, serviceId string) { service, err := serviceRepo.GetService(ctx, serviceId) if err != nil { panic(err) } // Response includes full service configuration, status, and URLs } ``` ### Static Site Management Deploy static sites with CDN distribution for frontend applications. ```go // Create a React static site func createStaticSite(ctx context.Context, serviceRepo *service.Repo) { repo := "https://github.com/username/my-react-app.git" branch := "main" buildCommand := "yarn install && yarn build" publishPath := "build" staticDetails := client.StaticSiteDetailsPOST{ BuildCommand: &buildCommand, PublishPath: &publishPath, } serviceDetails := client.ServicePOST_ServiceDetails{} serviceDetails.FromStaticSiteDetailsPOST(staticDetails) envVars := []client.EnvVarInput{} envVar := client.EnvVarInput{} envVar.FromEnvVarKeyValue(client.EnvVarKeyValue{ Key: "REACT_APP_API_URL", Value: "https://my-api-service.onrender.com", }) envVars = append(envVars, envVar) request := client.CreateServiceJSONRequestBody{ Name: "my-react-frontend", OwnerId: "own-123abc", Type: client.StaticSite, ServiceDetails: &serviceDetails, Repo: &repo, Branch: &branch, EnvVars: &envVars, } response, err := serviceRepo.CreateService(ctx, request) if err != nil { panic(err) } // Response: {"service":{"id":"srv-abc123","url":"https://my-react-frontend.onrender.com"}} } ``` ### Environment Variable Management Update service configuration without redeployment triggers, with automatic merge support. ```go // Update environment variables with merge strategy func updateEnvVars(ctx context.Context, serviceRepo *service.Repo, serviceId string) { // New variables to set newVars := []client.EnvVarInput{} var1 := client.EnvVarInput{} var1.FromEnvVarKeyValue(client.EnvVarKeyValue{ Key: "DATABASE_URL", Value: "postgresql://user:pass@host:5432/db", }) newVars = append(newVars, var1) var2 := client.EnvVarInput{} var2.FromEnvVarKeyValue(client.EnvVarKeyValue{ Key: "API_KEY", Value: "sk_live_abc123", }) newVars = append(newVars, var2) // Merge with existing variables (replace=false by default) oldVars, err := serviceRepo.ListEnvVars(ctx, serviceId, &client.GetEnvVarsForServiceParams{}) if err != nil { panic(err) } // Merge logic envVarMap := make(map[string]string) for _, envVar := range oldVars { envVarMap[envVar.Key] = envVar.Value } for _, envVarInput := range newVars { envVar, _ := envVarInput.AsEnvVarKeyValue() envVarMap[envVar.Key] = envVar.Value } // Update and trigger deploy mergedVars := []client.EnvVarInput{} for k, v := range envVarMap { var envVarInput client.EnvVarInput envVarInput.FromEnvVarKeyValue(client.EnvVarKeyValue{Key: k, Value: v}) mergedVars = append(mergedVars, envVarInput) } _, err = serviceRepo.UpdateEnvVars(ctx, serviceId, mergedVars) if err != nil { panic(err) } // Automatically triggers deployment deployResponse, err := serviceRepo.DeployService(ctx, serviceId) if err != nil { panic(err) } // Response: {"deploy":{"id":"dep-xyz123","status":"pending"}} } ``` ### PostgreSQL Database Management Create and query PostgreSQL databases with read-only transaction safety. ```go // Create a PostgreSQL database import ( "github.com/render-oss/render-mcp-server/pkg/postgres" ) func createPostgres(ctx context.Context, c *client.ClientWithResponses) { postgresRepo := postgres.NewRepo(c) region := "oregon" diskSize := 10 version := client.PostgresVersion("16") createParams := client.PostgresPOSTInput{ Name: "my-production-db", OwnerId: "own-123abc", Plan: "pro_4gb", Region: ®ion, Version: version, DiskSizeGB: &diskSize, } pg, err := postgresRepo.CreatePostgres(ctx, createParams) if err != nil { panic(err) } // Response: {"id":"pgdb-xyz789","name":"my-production-db","status":"creating"} } // Query database with read-only transactions func queryPostgres(ctx context.Context, postgresRepo *postgres.Repo, postgresId string) { sqlQuery := "SELECT id, email, created_at FROM users WHERE active = true ORDER BY created_at DESC LIMIT 10" connInfo, err := postgresRepo.GetPostgresConnectionInfo(ctx, postgresId) if err != nil { panic(err) } config, _ := pgx.ParseConfig(connInfo.ExternalConnectionString) conn, _ := pgx.ConnectConfig(ctx, config) defer conn.Close(ctx) // Automatically wrapped in READ ONLY transaction tx, _ := conn.BeginTx(ctx, pgx.TxOptions{AccessMode: pgx.ReadOnly}) defer tx.Rollback(ctx) rows, err := tx.Query(ctx, sqlQuery) if err != nil { panic(err) } defer rows.Close() results := []map[string]interface{}{} fieldDescs := rows.FieldDescriptions() columnNames := make([]string, len(fieldDescs)) for i, fd := range fieldDescs { columnNames[i] = string(fd.Name) } for rows.Next() { values, _ := rows.Values() rowMap := make(map[string]interface{}) for i, col := range columnNames { rowMap[col] = values[i] } results = append(results, rowMap) } // Response: [{"id":1,"email":"user@example.com","created_at":"2025-01-15T10:00:00Z"}] } ``` ### Redis Key-Value Store Management Deploy Redis instances with configurable eviction policies. ```go // Create a Redis key-value store import ( "github.com/render-oss/render-mcp-server/pkg/keyvalue" ) func createKeyValue(ctx context.Context, c *client.ClientWithResponses) { keyValueRepo := keyvalue.NewRepo(c) region := "oregon" policy := client.AllkeysLru createParams := client.KeyValuePOSTInput{ Name: "session-cache", OwnerId: "own-123abc", Plan: client.KeyValuePlanPro, Region: ®ion, MaxmemoryPolicy: &policy, } kv, err := keyValueRepo.CreateKeyValue(ctx, createParams) if err != nil { panic(err) } // Response: {"id":"kv-abc123","name":"session-cache","maxmemoryPolicy":"allkeys-lru"} } // List all key-value stores func listKeyValue(ctx context.Context, keyValueRepo *keyvalue.Repo) { kvs, err := keyValueRepo.ListKeyValue(ctx, &client.ListKeyValueParams{}) if err != nil { panic(err) } // Response: [{"id":"kv-abc123","name":"session-cache","plan":"pro","region":"oregon"}] } ``` ### Log Querying and Analysis Search and filter application logs with regex support and pagination. ```go // Query logs with filters import ( "time" "github.com/render-oss/render-mcp-server/pkg/logs" logsclient "github.com/render-oss/render-mcp-server/pkg/client/logs" ) func listLogs(ctx context.Context, c *client.ClientWithResponses, ownerId string) { logRepo := logs.NewLogRepo(c) resources := []string{"srv-xyz789"} statusCodes := []string{"500", "502", "503"} textFilters := []string{"error", "exception"} limit := 50 direction := logsclient.Backward startTime := time.Now().Add(-1 * time.Hour) endTime := time.Now() startTimeParam := client.StartTimeParam(startTime) endTimeParam := client.EndTimeParam(endTime) params := &client.ListLogsParams{ OwnerId: ownerId, Resource: resources, StatusCode: &statusCodes, Text: &textFilters, StartTime: &startTimeParam, EndTime: &endTimeParam, Direction: &direction, Limit: &limit, } response, err := logRepo.ListLogs(ctx, params) if err != nil { panic(err) } // Response: {"logs":[{"timestamp":"2025-11-01T10:30:00Z","text":"Error: Connection timeout","level":"error"}],"hasMore":true,"nextStartTime":"2025-11-01T10:25:00Z"} } // Discover available log labels func listLogLabelValues(ctx context.Context, logRepo *logs.LogRepo, ownerId string) { resources := []string{"srv-xyz789"} params := &client.ListLogsValuesParams{ OwnerId: ownerId, Label: client.ListLogsValuesParamsLabel("statusCode"), Resource: resources, } values, err := logRepo.ListLogLabelValues(ctx, params) if err != nil { panic(err) } // Response: ["200","404","500","502"] } ``` ### Performance Metrics Retrieval Monitor resource utilization, HTTP performance, and database connections with time-series data. ```go // Get comprehensive metrics import ( "github.com/render-oss/render-mcp-server/pkg/metrics" metricstypes "github.com/render-oss/render-mcp-server/pkg/client/metrics" ) func getMetrics(ctx context.Context, c *client.ClientWithResponses, resourceId string) { metricsRepo := metrics.NewRepo(c) startTime := time.Now().Add(-24 * time.Hour) endTime := time.Now() startTimeParam := client.StartTimeParam(startTime) endTimeParam := client.EndTimeParam(endTime) resolution := float32(300) // 5-minute intervals quantile := metricstypes.Quantile(0.95) aggMethod := metricstypes.AVG metricsRequest := metrics.MetricsRequest{ ResourceID: resourceId, MetricTypes: []metrics.MetricType{ metrics.MetricTypeCPUUsage, metrics.MetricTypeMemoryUsage, metrics.MetricTypeHTTPRequestCount, metrics.MetricTypeHTTPLatency, metrics.MetricTypeBandwidthUsage, }, StartTime: &startTimeParam, EndTime: &endTimeParam, Resolution: &resolution, CpuUsageAggregationMethod: &aggMethod, HttpLatencyQuantile: &quantile, } response, err := metricsRepo.GetMetrics(ctx, metricsRequest) if err != nil { panic(err) } // Response: {"cpuUsage":{"datapoints":[{"timestamp":"2025-11-01T10:00:00Z","value":45.2}]},"httpLatency":{"datapoints":[{"timestamp":"2025-11-01T10:00:00Z","value":123.5}]}} } // Get metrics with HTTP filtering func getHTTPMetrics(ctx context.Context, metricsRepo *metrics.Repo, serviceId string) { startTime := time.Now().Add(-1 * time.Hour) endTime := time.Now() startTimeParam := client.StartTimeParam(startTime) endTimeParam := client.EndTimeParam(endTime) host := metricstypes.HostQueryParam("api.example.com") path := metricstypes.PathQueryParam("/api/users") aggregateBy := metricstypes.HttpAggregateByStatusCode metricsRequest := metrics.MetricsRequest{ ResourceID: serviceId, MetricTypes: []metrics.MetricType{metrics.MetricTypeHTTPRequestCount}, StartTime: &startTimeParam, EndTime: &endTimeParam, HttpHost: &host, HttpPath: &path, AggregateHttpRequestCountBy: &aggregateBy, } response, err := metricsRepo.GetMetrics(ctx, metricsRequest) if err != nil { panic(err) } // Response: {"httpRequestCount":{"datapoints":[{"timestamp":"2025-11-01T10:00:00Z","value":150,"statusCode":"200"}]}} } ``` ### Deployment Management Track deployment history and status for continuous delivery workflows. ```go // List deployment history import ( "github.com/render-oss/render-mcp-server/pkg/deploy" ) func listDeploys(ctx context.Context, c *client.ClientWithResponses, serviceId string) { deployRepo := deploy.NewRepo(c) limit := 10 cursor := "" params := &client.ListDeploysParams{ Limit: &limit, Cursor: &cursor, } deploys, nextCursor, err := deployRepo.ListDeploys(ctx, serviceId, params) if err != nil { panic(err) } // Response: [{"id":"dep-123","status":"live","createdAt":"2025-11-01T09:00:00Z","commit":"abc123"}] // nextCursor: "dep-122" for pagination } // Get specific deployment details func getDeploy(ctx context.Context, deployRepo *deploy.Repo, serviceId string, deployId string) { deployment, err := deployRepo.GetDeploy(ctx, serviceId, deployId) if err != nil { panic(err) } // Response: {"id":"dep-123","status":"live","createdAt":"2025-11-01T09:00:00Z","finishedAt":"2025-11-01T09:05:23Z"} } ``` ### MCP Server Initialization Bootstrap the MCP server with all tools and configure transport mechanisms. ```go // Start MCP server with stdio transport import ( "github.com/mark3labs/mcp-go/server" "github.com/render-oss/render-mcp-server/cmd" "github.com/render-oss/render-mcp-server/pkg/cfg" ) func main() { // Initialize MCP server s := server.NewMCPServer("render-mcp-server", cfg.Version) // Create authenticated client c, err := client.NewDefaultClient() if err != nil { panic(err) } // Register all tool categories s.AddTools(owner.Tools(c)...) s.AddTools(service.Tools(c)...) s.AddTools(deploy.Tools(c)...) s.AddTools(postgres.Tools(c)...) s.AddTools(keyvalue.Tools(c)...) s.AddTools(logs.Tools(c)...) s.AddTools(metrics.Tools(c)...) // Start with stdio transport for CLI integration err = server.ServeStdio(s, server.WithStdioContextFunc( multicontext.MultiStdioContextFunc( session.ContextWithStdioSession, authn.ContextWithAPITokenFromConfig, ), )) if err != nil { panic(err) } } // Start with HTTP transport for web integration func startHTTPServer(s *server.MCPServer) { sessionStore := session.NewInMemoryStore() // Use Redis for distributed sessions if redisURL := os.Getenv("REDIS_URL"); redisURL != "" { sessionStore, _ = session.NewRedisStore(redisURL) } err := server.NewStreamableHTTPServer(s, server.WithHTTPContextFunc(multicontext.MultiHTTPContextFunc( session.ContextWithHTTPSession(sessionStore), authn.ContextWithAPITokenFromHeader, )), ).Start(":10000") if err != nil { panic(err) } } ``` ## Integration and Use Cases The Render MCP Server enables AI-assisted infrastructure management through natural language interactions. Primary use cases include automated service provisioning where developers describe requirements and the LLM creates appropriate web services, databases, and caches with proper configuration; production troubleshooting where AI analyzes logs and metrics to identify error patterns, performance bottlenecks, and resource constraints; database operations allowing direct SQL queries for data analysis within conversational workflows; and deployment monitoring to track release history and status. Integration patterns support desktop AI assistants using stdio transport for interactive terminal-based workflows, web applications using HTTP transport with session management for multi-user environments, and CI/CD pipelines for automated infrastructure operations. The server implements comprehensive error handling, read-only transaction safety for database queries, automatic workspace selection, environment variable merging to prevent accidental overwrites, and paginated responses for large datasets. Authentication uses Render API tokens configured through environment variables or HTTP headers, with context propagation ensuring workspace isolation across all operations.