Skip to main content

Using STRONGLY_SERVICES in Your Code

All connected services are automatically available through the STRONGLY_SERVICES environment variable. This provides a unified interface for accessing databases, AI models, workflows, ML models, and more. Apps do not select MCP servers: agents and workflows use them.

Overview​

When you connect services during app deployment, the platform injects a JSON object containing all connection details:

const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');

Complete Structure​

The STRONGLY_SERVICES JSON has the following top-level structure:

{
"version": "1.0.0",
"environment": "production",
"generated_at": "2025-01-15T10:30:00.000Z",
"services": {
"addons": { ... },
"datasources": { ... },
"aigateway": { ... },
"mlmodels": [ ... ],
"workflows": { ... },
"agents": { ... },
"platform": { ... }
},
"circuit_breaker": {
"enabled": true,
"failure_threshold": 5,
"timeout_seconds": 30,
"reset_timeout_seconds": 60
},
"monitoring": {
"metrics_endpoint": "https://app.strongly.ai/api/metrics",
"logs_endpoint": "https://app.strongly.ai/api/logs",
"tracing_endpoint": "https://app.strongly.ai/api/traces"
}
}

Service Categories​

Add-ons (Managed Databases)​

Platform-managed database instances organized by type. Each type contains an array of service instances.

Structure:

{
"services": {
"addons": {
"mongodb": [
{
"id": "addon-xyz123",
"name": "My MongoDB",
"type": "mongodb",
"category": "add-on",
"status": "active",
"version": "7.0",
"internal": false,
"connection": {
"connection_string": "mongodb://user:pass@addon-xyz123.addons.svc.cluster.local:27017/mydb",
"uri": "mongodb://user:pass@addon-xyz123.addons.svc.cluster.local:27017/mydb",
"host": "addon-xyz123.addons.svc.cluster.local",
"port": 27017,
"database": "mydb"
},
"auth": {
"method": "username_password",
"credentials": {
"username": "admin",
"password": "decrypted-password"
}
},
"limits": {
"max_connections": 100,
"storage_gb": 10
},
"metadata": {
"cpu": "500m",
"memory": "1Gi",
"disk": "10Gi",
"backup_enabled": false
}
}
],
"postgres": [
{
"id": "addon-abc456",
"name": "My PostgreSQL",
"type": "postgres",
"category": "add-on",
"status": "active",
"connection": {
"connection_string": "postgresql://user:pass@addon-abc456.addons.svc.cluster.local:5432/mydb",
"uri": "postgresql://user:pass@addon-abc456.addons.svc.cluster.local:5432/mydb",
"host": "addon-abc456.addons.svc.cluster.local",
"port": 5432,
"database": "mydb"
},
"auth": {
"method": "username_password",
"credentials": {
"username": "admin",
"password": "decrypted-password"
}
}
}
],
"redis": [
{
"id": "addon-red789",
"name": "My Redis",
"type": "redis",
"category": "add-on",
"status": "active",
"connection": {
"connection_string": "redis://default:pass@addon-red789.addons.svc.cluster.local:6379",
"uri": "redis://default:pass@addon-red789.addons.svc.cluster.local:6379",
"host": "addon-red789.addons.svc.cluster.local",
"port": 6379
},
"auth": {
"method": "username_password",
"credentials": {
"username": "default",
"password": "decrypted-password"
}
}
}
]
}
}
}

Supported addon types: mongodb, postgres, redis, rabbitmq, kafka, neo4j, milvus, greenplum, surrealdb

Addon entry fields: every addon entry has id, name, type, category ("add-on"), status, version, internal (true for app-internal metadata databases), and configId. The connection object provides connection_string plus a uri alias, the add-on's host (its address inside the platform), port, and database. limits contains max_connections and storage_gb; metadata contains cpu, memory, disk, and backup_enabled.

Data Sources (External Connections)​

External data connections organized by type. Credentials are decrypted and provided directly.

Structure:

{
"services": {
"datasources": {
"s3": [
{
"id": "ds-s3-001",
"name": "Production Files",
"type": "s3",
"category": "data-source",
"status": "connected",
"connection": {
"bucket": "my-app-files",
"region": "us-east-1"
},
"auth": {
"method": "aws_access_key",
"credentials": {
"access_key_id": "AKIA...",
"secret_access_key": "..."
}
},
"metadata": {
"owner": "user123",
"created_at": "2025-01-10T08:00:00.000Z",
"tags": ["production"]
}
}
],
"mysql": [
{
"id": "ds-mysql-001",
"name": "Analytics DB",
"type": "mysql",
"category": "data-source",
"status": "connected",
"connection": {
"host": "analytics.example.com",
"port": 3306,
"database": "analytics"
},
"auth": {
"method": "username_password",
"credentials": {
"username": "app_user",
"password": "decrypted-password"
}
}
}
],
"snowflake": [
{
"id": "ds-sf-001",
"name": "Data Warehouse",
"type": "snowflake",
"category": "data-source",
"status": "connected",
"connection": {
"connection_string": "snowflake://app_user@xy12345.us-east-1/ANALYTICS",
"account": "xy12345.us-east-1",
"warehouse": "COMPUTE_WH",
"database": "ANALYTICS",
"region": "us-east-1"
},
"auth": {
"method": "username_password",
"credentials": {
"username": "app_user",
"password": "decrypted-password"
}
}
}
]
}
}
}

Supported datasource types: s3, mysql, mariadb, postgres, mongodb, bigquery, snowflake, redshift, dynamodb, elasticsearch, pinecone, gcs, minio, azure-blob, and many more.

AI Gateway​

AI model endpoints with provider information and available models.

Structure:

{
"services": {
"aigateway": {
"id": "ai-gateway-001",
"status": "active",
"base_url": "http://ai-gateway.strongly.svc.cluster.local",
"providers": {
"openai": {
"enabled": true,
"base_url": "http://ai-gateway.strongly.svc.cluster.local",
"type": "third-party",
"models": [
{
"id": "model-abc123",
"name": "GPT-4o",
"display_name": "GPT-4o",
"type": "third-party",
"modelType": "multimodal",
"context_window": 128000,
"max_output_tokens": 4096
}
]
},
"self_hosted": {
"enabled": true,
"base_url": "http://llama.strongly.svc.cluster.local",
"type": "self-hosted",
"models": [
{
"id": "model-xyz789",
"name": "Llama 3.1 70B",
"display_name": "Llama 3.1 70B",
"type": "self-hosted",
"modelType": "chat",
"endpoint": "http://llama.strongly.svc.cluster.local"
}
]
}
},
"available_models": [
{
"_id": "model-abc123",
"vendor_model_id": "gpt-4o",
"vendor": "OpenAI",
"display_name": "GPT-4o",
"provider": "OpenAI",
"type": "third-party",
"modelType": "multimodal",
"context_window": 128000,
"max_output_tokens": 4096,
"capabilities": ["streaming"],
"status": "active",
"api_endpoint": "https://api.openai.com/v1"
},
{
"_id": "model-xyz789",
"vendor_model_id": "llama-3.1-70b",
"vendor": "Meta",
"display_name": "Llama 3.1 70B",
"provider": "Self-Hosted",
"type": "self-hosted",
"modelType": "chat",
"endpoint": "http://llama.strongly.svc.cluster.local",
"deployment_id": "deploy-xyz",
"instance_type": "g5.2xlarge"
}
],
"retry_policy": {
"max_retries": 3,
"backoff_multiplier": 2,
"initial_delay_ms": 1000
}
}
}
}

Model entry fields: each available_models entry carries _id (the Strongly model ID), vendor_model_id, vendor, display_name, provider, type (third-party or self-hosted), modelType (chat, embedding, multimodal, text_to_speech, speech_to_text, image_generation, etc.), model_name, context_window, max_output_tokens, capabilities, status, owner, and shared_with_me. Self-hosted models add endpoint, deployment_id, and instance_type; third-party models add api_endpoint. Note the field is modelType (camelCase), not model_type.

ML Models (Model Registry)​

Traditional and AutoML models from the model registry.

Structure:

{
"services": {
"mlmodels": [
{
"id": "ml-model-001",
"name": "sales-predictor",
"display_name": "Sales Predictor",
"type": "traditional",
"framework": "XGBoost",
"problem_type": "Regression",
"status": "deployed",
"version": "1.0.0",
"accuracy": 0.95,
"endpoint": "http://ml-model-001.models.svc.cluster.local",
"deployment_id": "deploy-ml-001",
"instance_type": "m5.large",
"inference_time_ms": 15,
"size_mb": 120
}
]
}
}

Workflows​

Workflow engine configuration and the workflows connected to the app. Only workflows the app was deployed with appear here. Each entry has a trigger_type of rest_api, webhook, or streaming and a mode field (production by default). Webhook workflows carry a webhook_url in endpoints; streaming workflows expose a session-mint proxy_url (/api/v1/streaming-sessions) plus a session_status_url instead of a request/response endpoint.

Every address here is the platform's internal one, reachable from inside your app, workspace or job, and calls to it are signed in as you automatically: send no API key and no Authorization header.

Structure:

{
"services": {
"workflows": {
"engine": {
"id": "workflow-controller-001",
"status": "active",
"version": "2.0.0",
"api_endpoint": "http://workflow-controller.strongly.svc.cluster.local/api/v1",
"webhook_endpoint": "http://workflow-controller.strongly.svc.cluster.local/api/webhooks"
},
"available_workflows": [
{
"id": "wf-data-processor",
"name": "Data Processor",
"description": "Processes incoming data files",
"trigger_type": "rest_api",
"mode": "production",
"input_schema": {
"file_url": {
"type": "string",
"required": true,
"description": "URL of the file to process"
}
},
"endpoints": {
"proxy_url": "http://dashboard.strongly.svc.cluster.local/api/v1/workflows/wf-data-processor/execute",
"method": "POST"
},
"deployment": {
"pod_name": "wf-data-processor-abc123",
"environment": "production",
"namespace": "user-workflows"
}
}
],
"limits": {
"max_concurrent_executions": 10,
"max_execution_time_seconds": 3600
}
}
}
}

Agents​

Persistent agents the application selected. available_agents holds exactly the selected agents and selected_agent_ids their ids. An app that selected no agent has no agents block.

Structure:

{
"services": {
"agents": {
"id": "agents-001",
"status": "ready",
"selected_agent_ids": ["wf-support-brain"],
"available_agents": [
{
"id": "wf-support-brain",
"name": "Support Agent",
"description": "Handles inbound support requests",
"workflow_id": "wf-support-brain",
"agent_type": "streaming-workflow",
"status": "running",
"endpoints": {
"base_url": "http://dashboard.strongly.svc.cluster.local/api/v1/streaming-sessions"
}
}
]
}
}
}

Talk to an agent by creating a session: POST {"workflowId": "<workflow_id>"} to endpoints.base_url. The response carries sessionId, wsUrl (the platform's internal WebSocket address, which the app connects to directly) and publicWsUrl (the public address, for when the app hands the session to its own users' browsers). The session runs on the agent's production deployment. See Streaming Workflows.

Platform API​

Internal REST API access for apps. api_url is the in-cluster platform API base; public_url is the platform's public URL, needed when constructing URLs that external callers (webhooks, telephony providers) must reach.

Structure:

{
"services": {
"platform": {
"api_url": "http://dashboard.strongly.svc.cluster.local/api/v1",
"api_version": "v1",
"public_url": "https://app.strongly.ai"
}
}
}

Usage Examples​

Node.js / Express​

MongoDB Add-on​

const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');
const { MongoClient } = require('mongodb');

// Get first MongoDB addon
const mongoAddons = services.services?.addons?.mongodb || [];
if (mongoAddons.length === 0) {
throw new Error('No MongoDB addon configured');
}

const mongoConfig = mongoAddons[0];

// Connect using connection string
const client = await MongoClient.connect(
mongoConfig.connection.connection_string
);
const db = client.db(mongoConfig.connection.database);

// Use the database
const users = await db.collection('users').find({ active: true }).toArray();

PostgreSQL Data Source​

const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');
const { Pool } = require('pg');

// Get first PostgreSQL datasource
const pgSources = services.services?.datasources?.postgres || [];
if (pgSources.length === 0) {
throw new Error('No PostgreSQL datasource configured');
}

const pgConfig = pgSources[0];

// Connect using connection details
const pool = new Pool({
host: pgConfig.connection.host,
port: pgConfig.connection.port,
database: pgConfig.connection.database,
user: pgConfig.auth?.credentials?.username,
password: pgConfig.auth?.credentials?.password,
max: 20
});

// Query data
const result = await pool.query('SELECT * FROM users WHERE active = $1', [true]);

AI Model (via AI Gateway)​

const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');

// Get AI Gateway config
const aiGateway = services.services?.aigateway;
if (!aiGateway) {
throw new Error('No AI Gateway configured');
}

// Get the first available model
const models = aiGateway.available_models || [];
const model = models[0];

// Call via the AI Gateway base URL (OpenAI-compatible API)
const response = await fetch(`${aiGateway.base_url}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Model-Id': model._id // Use Strongly model ID
},
body: JSON.stringify({
model: model.vendor_model_id, // e.g., 'gpt-4o'
messages: [
{ role: 'user', content: 'Hello, how are you?' }
]
})
});

const data = await response.json();
console.log(data.choices[0].message.content);

Workflow Trigger​

const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');

// Get available workflows
const workflows = services.services?.workflows?.available_workflows || [];
const workflow = workflows.find(w => w.name === 'Data Processor');

if (!workflow) {
throw new Error('Workflow not found');
}

// Trigger workflow via proxy URL
const result = await fetch(workflow.endpoints.proxy_url, {
method: workflow.endpoints.method || 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
file_url: 'https://example.com/data.csv',
timestamp: new Date().toISOString()
})
});

const response = await result.json();
console.log('Workflow result:', response);

S3 Data Source​

const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');
const { S3Client, ListObjectsV2Command } = require('@aws-sdk/client-s3');

// Get first S3 datasource
const s3Sources = services.services?.datasources?.s3 || [];
if (s3Sources.length === 0) {
throw new Error('No S3 datasource configured');
}

const s3Config = s3Sources[0];

// Create S3 client
const s3Client = new S3Client({
region: s3Config.connection.region || 'us-east-1',
credentials: {
accessKeyId: s3Config.auth.credentials.access_key_id,
secretAccessKey: s3Config.auth.credentials.secret_access_key
}
});

// List objects
const response = await s3Client.send(new ListObjectsV2Command({
Bucket: s3Config.connection.bucket
}));

for (const obj of response.Contents || []) {
console.log(obj.Key);
}

Python / Flask​

MongoDB Add-on​

import os
import json
from pymongo import MongoClient

# Parse STRONGLY_SERVICES
services = json.loads(os.environ.get('STRONGLY_SERVICES', '{}'))

# Get first MongoDB addon
mongo_addons = services.get('services', {}).get('addons', {}).get('mongodb', [])
if not mongo_addons:
raise ValueError('No MongoDB addon configured')

mongo_config = mongo_addons[0]

# Connect using connection string
client = MongoClient(mongo_config['connection']['connection_string'])
db = client[mongo_config['connection']['database']]

# Use the database
users = list(db.users.find({'active': True}))

AI Model (via AI Gateway)​

import os
import json
import requests

# Parse STRONGLY_SERVICES
services = json.loads(os.environ.get('STRONGLY_SERVICES', '{}'))

# Get AI Gateway config
ai_gateway = services.get('services', {}).get('aigateway', {})
if not ai_gateway:
raise ValueError('No AI Gateway configured')

# Get the first available model
models = ai_gateway.get('available_models', [])
model = models[0]

# Call via AI Gateway (OpenAI-compatible API)
response = requests.post(
f"{ai_gateway['base_url']}/v1/chat/completions",
headers={
'Content-Type': 'application/json',
'X-Model-Id': model['_id']
},
json={
'model': model['vendor_model_id'],
'max_tokens': 1024,
'messages': [
{'role': 'user', 'content': 'Hello, Claude!'}
]
}
)

data = response.json()
print(data['choices'][0]['message']['content'])

S3 Data Source​

import os
import json
import boto3

# Parse STRONGLY_SERVICES
services = json.loads(os.environ.get('STRONGLY_SERVICES', '{}'))

# Get first S3 datasource
s3_sources = services.get('services', {}).get('datasources', {}).get('s3', [])
if not s3_sources:
raise ValueError('No S3 datasource configured')

s3_config = s3_sources[0]

# Create S3 client
s3_client = boto3.client(
's3',
region_name=s3_config['connection'].get('region', 'us-east-1'),
aws_access_key_id=s3_config['auth']['credentials']['access_key_id'],
aws_secret_access_key=s3_config['auth']['credentials']['secret_access_key']
)

# List objects
response = s3_client.list_objects_v2(Bucket=s3_config['connection']['bucket'])
for obj in response.get('Contents', []):
print(obj['Key'])

ML Model Inference​

import os
import json
import requests

# Parse STRONGLY_SERVICES
services = json.loads(os.environ.get('STRONGLY_SERVICES', '{}'))

# Get ML models
ml_models = services.get('services', {}).get('mlmodels', [])
model = next((m for m in ml_models if m['name'] == 'sales-predictor'), None)

if model and model.get('endpoint'):
response = requests.post(
f"{model['endpoint']}/predict",
json={'features': [100, 200, 300]}
)
prediction = response.json()
print(f"Prediction: {prediction}")

React (Frontend)​

For React apps, STRONGLY_SERVICES should be accessed via backend API, not directly in frontend code. However, you can inject specific values at build time:

// Backend API endpoint
const API_URL = process.env.REACT_APP_API_URL;

// Call backend which has access to STRONGLY_SERVICES
async function fetchData() {
const response = await fetch(`${API_URL}/api/data`);
return response.json();
}

Helper Functions​

Service Lookup Helper​

// services.js
class ServicesHelper {
constructor() {
this.data = JSON.parse(process.env.STRONGLY_SERVICES || '{}');
this.services = this.data.services || {};
}

// Get all addons of a specific type
getAddonsByType(type) {
return this.services.addons?.[type] || [];
}

// Get first addon of a specific type
getFirstAddon(type) {
const addons = this.getAddonsByType(type);
return addons.length > 0 ? addons[0] : null;
}

// Get all datasources of a specific type
getDataSourcesByType(type) {
return this.services.datasources?.[type] || [];
}

// Get AI Gateway config
getAIGateway() {
return this.services.aigateway || null;
}

// Get available AI models
getAvailableModels() {
return this.services.aigateway?.available_models || [];
}

// Get ML models
getMLModels() {
return this.services.mlmodels || [];
}

// Get available workflows
getWorkflows() {
return this.services.workflows?.available_workflows || [];
}
}

module.exports = new ServicesHelper();

Usage:

const services = require('./services');

// Get first MongoDB addon
const mongo = services.getFirstAddon('mongodb');
if (mongo) {
console.log('MongoDB connection:', mongo.connection.connection_string);
}

// Get all AI models
const models = services.getAvailableModels();
console.log(`${models.length} AI models available`);

Error Handling​

Always handle missing or invalid service configurations:

const data = JSON.parse(process.env.STRONGLY_SERVICES || '{}');
const services = data.services || {};

function getRequiredAddon(type) {
const addons = services.addons?.[type] || [];

if (addons.length === 0) {
throw new Error(`No ${type} addon found in STRONGLY_SERVICES`);
}

const addon = addons[0];
if (!addon.connection) {
throw new Error(`${type} addon missing connection details`);
}

return addon;
}

// Usage with error handling
try {
const mongoAddon = getRequiredAddon('mongodb');
const client = await MongoClient.connect(
mongoAddon.connection.connection_string
);
} catch (error) {
console.error('Failed to connect to addon:', error.message);
// Fallback or retry logic
}

Debugging​

Log Service Configuration​

const data = JSON.parse(process.env.STRONGLY_SERVICES || '{}');
const services = data.services || {};

// Log available services (NEVER log in production - contains secrets!)
if (process.env.NODE_ENV === 'development') {
console.log('Version:', data.version);
console.log('Environment:', data.environment);
console.log('Addon types:', Object.keys(services.addons || {}));
console.log('Datasource types:', Object.keys(services.datasources || {}));
console.log('AI models:', (services.aigateway?.available_models || []).length);
console.log('ML models:', (services.mlmodels || []).length);
console.log('Workflows:', (services.workflows?.available_workflows || []).length);
}

Validate Services on Startup​

const data = JSON.parse(process.env.STRONGLY_SERVICES || '{}');
const services = data.services || {};

function validateServices() {
const issues = [];

// Check for required addons
if (!services.addons?.mongodb?.length) {
issues.push('No MongoDB addon configured');
}

// Check for AI Gateway
if (!services.aigateway?.base_url) {
issues.push('No AI Gateway configured');
}

if (issues.length > 0) {
console.warn('Service validation warnings:', issues);
}
}

// Run on startup
validateServices();

Best Practices​

  1. Parse Once: Parse STRONGLY_SERVICES once at startup, not on every request
  2. Use Connection Pools: Reuse database connections across requests
  3. Handle Missing Services: Always check if services exist before using them
  4. Never Log Secrets: Don't log STRONGLY_SERVICES in production - it contains decrypted credentials
  5. Validate on Startup: Check required services are available at app start
  6. Use Helpers: Create helper functions for common service access patterns
  7. Access by Type: Services are organized by type (e.g., addons.mongodb, datasources.s3) - iterate arrays to find what you need

Next Steps​