Skip to content

Commit 22662b3

Browse files
committed
feat: add VoyageAI embeddings/rerank integration and MongoDB Atlas connection string support
- Add VoyageAI tools: embeddings (voyage-3, voyage-3-large, etc.) and rerank (rerank-2, rerank-2-lite) - Add VoyageAI block with operation dropdown (Generate Embeddings / Rerank) - Add VoyageAI icon and register in tool/block registries - Enhance MongoDB with connection string mode for Atlas (mongodb+srv://) support - Add connection mode toggle to MongoDB block (Host & Port / Connection String) - Update all 6 MongoDB API routes to accept optional connectionString - Add 48 unit tests (VoyageAI tools, block config, MongoDB utils)
1 parent 19442f1 commit 22662b3

File tree

25 files changed

+939
-28
lines changed

25 files changed

+939
-28
lines changed

apps/sim/app/api/tools/mongodb/delete/route.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import { createMongoDBConnection, sanitizeCollectionName, validateFilter } from
88
const logger = createLogger('MongoDBDeleteAPI')
99

1010
const DeleteSchema = z.object({
11-
host: z.string().min(1, 'Host is required'),
12-
port: z.coerce.number().int().positive('Port must be a positive integer'),
11+
connectionString: z.string().optional(),
12+
host: z.string().default(''),
13+
port: z.coerce.number().int().nonnegative().default(27017),
1314
database: z.string().min(1, 'Database name is required'),
14-
username: z.string().min(1, 'Username is required'),
15-
password: z.string().min(1, 'Password is required'),
15+
username: z.string().default(''),
16+
password: z.string().default(''),
1617
authSource: z.string().optional(),
1718
ssl: z.enum(['disabled', 'required', 'preferred']).default('preferred'),
1819
collection: z.string().min(1, 'Collection name is required'),
@@ -75,6 +76,7 @@ export async function POST(request: NextRequest) {
7576
}
7677

7778
client = await createMongoDBConnection({
79+
connectionString: params.connectionString,
7880
host: params.host,
7981
port: params.port,
8082
database: params.database,

apps/sim/app/api/tools/mongodb/execute/route.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import { createMongoDBConnection, sanitizeCollectionName, validatePipeline } fro
88
const logger = createLogger('MongoDBExecuteAPI')
99

1010
const ExecuteSchema = z.object({
11-
host: z.string().min(1, 'Host is required'),
12-
port: z.coerce.number().int().positive('Port must be a positive integer'),
11+
connectionString: z.string().optional(),
12+
host: z.string().default(''),
13+
port: z.coerce.number().int().nonnegative().default(27017),
1314
database: z.string().min(1, 'Database name is required'),
14-
username: z.string().min(1, 'Username is required'),
15-
password: z.string().min(1, 'Password is required'),
15+
username: z.string().default(''),
16+
password: z.string().default(''),
1617
authSource: z.string().optional(),
1718
ssl: z.enum(['disabled', 'required', 'preferred']).default('preferred'),
1819
collection: z.string().min(1, 'Collection name is required'),
@@ -61,6 +62,7 @@ export async function POST(request: NextRequest) {
6162
const pipelineDoc = JSON.parse(params.pipeline)
6263

6364
client = await createMongoDBConnection({
65+
connectionString: params.connectionString,
6466
host: params.host,
6567
port: params.port,
6668
database: params.database,

apps/sim/app/api/tools/mongodb/insert/route.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import { createMongoDBConnection, sanitizeCollectionName } from '../utils'
88
const logger = createLogger('MongoDBInsertAPI')
99

1010
const InsertSchema = z.object({
11-
host: z.string().min(1, 'Host is required'),
12-
port: z.coerce.number().int().positive('Port must be a positive integer'),
11+
connectionString: z.string().optional(),
12+
host: z.string().default(''),
13+
port: z.coerce.number().int().nonnegative().default(27017),
1314
database: z.string().min(1, 'Database name is required'),
14-
username: z.string().min(1, 'Username is required'),
15-
password: z.string().min(1, 'Password is required'),
15+
username: z.string().default(''),
16+
password: z.string().default(''),
1617
authSource: z.string().optional(),
1718
ssl: z.enum(['disabled', 'required', 'preferred']).default('preferred'),
1819
collection: z.string().min(1, 'Collection name is required'),
@@ -54,6 +55,7 @@ export async function POST(request: NextRequest) {
5455

5556
const sanitizedCollection = sanitizeCollectionName(params.collection)
5657
client = await createMongoDBConnection({
58+
connectionString: params.connectionString,
5759
host: params.host,
5860
port: params.port,
5961
database: params.database,

apps/sim/app/api/tools/mongodb/introspect/route.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import { createMongoDBConnection, executeIntrospect } from '../utils'
88
const logger = createLogger('MongoDBIntrospectAPI')
99

1010
const IntrospectSchema = z.object({
11-
host: z.string().min(1, 'Host is required'),
12-
port: z.coerce.number().int().positive('Port must be a positive integer'),
11+
connectionString: z.string().optional(),
12+
host: z.string().default(''),
13+
port: z.coerce.number().int().nonnegative().default(27017),
1314
database: z.string().optional(),
1415
username: z.string().optional(),
1516
password: z.string().optional(),
@@ -36,6 +37,7 @@ export async function POST(request: NextRequest) {
3637
)
3738

3839
client = await createMongoDBConnection({
40+
connectionString: params.connectionString,
3941
host: params.host,
4042
port: params.port,
4143
database: params.database || 'admin',

apps/sim/app/api/tools/mongodb/query/route.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import { createMongoDBConnection, sanitizeCollectionName, validateFilter } from
88
const logger = createLogger('MongoDBQueryAPI')
99

1010
const QuerySchema = z.object({
11-
host: z.string().min(1, 'Host is required'),
12-
port: z.coerce.number().int().positive('Port must be a positive integer'),
11+
connectionString: z.string().optional(),
12+
host: z.string().default(''),
13+
port: z.coerce.number().int().nonnegative().default(27017),
1314
database: z.string().min(1, 'Database name is required'),
14-
username: z.string().min(1, 'Username is required'),
15-
password: z.string().min(1, 'Password is required'),
15+
username: z.string().default(''),
16+
password: z.string().default(''),
1617
authSource: z.string().optional(),
1718
ssl: z.enum(['disabled', 'required', 'preferred']).default('preferred'),
1819
collection: z.string().min(1, 'Collection name is required'),
@@ -90,6 +91,7 @@ export async function POST(request: NextRequest) {
9091
}
9192

9293
client = await createMongoDBConnection({
94+
connectionString: params.connectionString,
9395
host: params.host,
9496
port: params.port,
9597
database: params.database,

apps/sim/app/api/tools/mongodb/update/route.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import { createMongoDBConnection, sanitizeCollectionName, validateFilter } from
88
const logger = createLogger('MongoDBUpdateAPI')
99

1010
const UpdateSchema = z.object({
11-
host: z.string().min(1, 'Host is required'),
12-
port: z.coerce.number().int().positive('Port must be a positive integer'),
11+
connectionString: z.string().optional(),
12+
host: z.string().default(''),
13+
port: z.coerce.number().int().nonnegative().default(27017),
1314
database: z.string().min(1, 'Database name is required'),
14-
username: z.string().min(1, 'Username is required'),
15-
password: z.string().min(1, 'Password is required'),
15+
username: z.string().default(''),
16+
password: z.string().default(''),
1617
authSource: z.string().optional(),
1718
ssl: z.enum(['disabled', 'required', 'preferred']).default('preferred'),
1819
collection: z.string().min(1, 'Collection name is required'),
@@ -99,6 +100,7 @@ export async function POST(request: NextRequest) {
99100
}
100101

101102
client = await createMongoDBConnection({
103+
connectionString: params.connectionString,
102104
host: params.host,
103105
port: params.port,
104106
database: params.database,

apps/sim/app/api/tools/mongodb/utils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ import { validateDatabaseHost } from '@/lib/core/security/input-validation.serve
33
import type { MongoDBCollectionInfo, MongoDBConnectionConfig } from '@/tools/mongodb/types'
44

55
export async function createMongoDBConnection(config: MongoDBConnectionConfig) {
6+
if (config.connectionString) {
7+
const client = new MongoClient(config.connectionString, {
8+
connectTimeoutMS: 10000,
9+
socketTimeoutMS: 10000,
10+
maxPoolSize: 1,
11+
})
12+
await client.connect()
13+
return client
14+
}
15+
616
const hostValidation = await validateDatabaseHost(config.host, 'host')
717
if (!hostValidation.isValid) {
818
throw new Error(hostValidation.error)

apps/sim/blocks/blocks/mongodb.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,41 @@ export const MongoDBBlock: BlockConfig<MongoDBResponse | MongoDBIntrospectRespon
3030
],
3131
value: () => 'query',
3232
},
33+
{
34+
id: 'connectionMode',
35+
title: 'Connection Mode',
36+
type: 'dropdown',
37+
options: [
38+
{ label: 'Host & Port', id: 'host_port' },
39+
{ label: 'Connection String (Atlas)', id: 'connection_string' },
40+
],
41+
value: () => 'host_port',
42+
},
43+
{
44+
id: 'connectionString',
45+
title: 'Connection String',
46+
type: 'short-input',
47+
placeholder: 'mongodb+srv://user:password@cluster.mongodb.net/mydb',
48+
password: true,
49+
condition: { field: 'connectionMode', value: 'connection_string' },
50+
required: { field: 'connectionMode', value: 'connection_string' },
51+
},
3352
{
3453
id: 'host',
3554
title: 'Host',
3655
type: 'short-input',
3756
placeholder: 'localhost or your.mongodb.host',
38-
required: true,
57+
condition: { field: 'connectionMode', value: 'connection_string', not: true },
58+
required: { field: 'connectionMode', value: 'connection_string', not: true },
3959
},
4060
{
4161
id: 'port',
4262
title: 'Port',
4363
type: 'short-input',
4464
placeholder: '27017',
4565
value: () => '27017',
46-
required: true,
66+
condition: { field: 'connectionMode', value: 'connection_string', not: true },
67+
required: { field: 'connectionMode', value: 'connection_string', not: true },
4768
},
4869
{
4970
id: 'database',
@@ -57,15 +78,17 @@ export const MongoDBBlock: BlockConfig<MongoDBResponse | MongoDBIntrospectRespon
5778
title: 'Username',
5879
type: 'short-input',
5980
placeholder: 'mongodb_user',
60-
required: true,
81+
condition: { field: 'connectionMode', value: 'connection_string', not: true },
82+
required: { field: 'connectionMode', value: 'connection_string', not: true },
6183
},
6284
{
6385
id: 'password',
6486
title: 'Password',
6587
type: 'short-input',
6688
password: true,
6789
placeholder: 'Your database password',
68-
required: true,
90+
condition: { field: 'connectionMode', value: 'connection_string', not: true },
91+
required: { field: 'connectionMode', value: 'connection_string', not: true },
6992
},
7093
{
7194
id: 'authSource',
@@ -837,7 +860,7 @@ Return ONLY the MongoDB query filter as valid JSON - no explanations, no markdow
837860
}
838861
},
839862
params: (params) => {
840-
const { operation, documents, ...rest } = params
863+
const { operation, documents, connectionMode, ...rest } = params
841864

842865
let parsedDocuments
843866
if (documents && typeof documents === 'string' && documents.trim()) {
@@ -853,7 +876,7 @@ Return ONLY the MongoDB query filter as valid JSON - no explanations, no markdow
853876
parsedDocuments = documents
854877
}
855878

856-
const connectionConfig = {
879+
const connectionConfig: Record<string, unknown> = {
857880
host: rest.host,
858881
port: typeof rest.port === 'string' ? Number.parseInt(rest.port, 10) : rest.port || 27017,
859882
database: rest.database,
@@ -863,6 +886,10 @@ Return ONLY the MongoDB query filter as valid JSON - no explanations, no markdow
863886
ssl: rest.ssl || 'preferred',
864887
}
865888

889+
if (rest.connectionString) {
890+
connectionConfig.connectionString = rest.connectionString
891+
}
892+
866893
const result: any = { ...connectionConfig }
867894

868895
if (rest.collection) result.collection = rest.collection
@@ -900,6 +927,8 @@ Return ONLY the MongoDB query filter as valid JSON - no explanations, no markdow
900927
},
901928
inputs: {
902929
operation: { type: 'string', description: 'Database operation to perform' },
930+
connectionMode: { type: 'string', description: 'Connection mode (host_port or connection_string)' },
931+
connectionString: { type: 'string', description: 'Full MongoDB connection string' },
903932
host: { type: 'string', description: 'MongoDB host' },
904933
port: { type: 'string', description: 'MongoDB port' },
905934
database: { type: 'string', description: 'Database name' },

0 commit comments

Comments
 (0)