MongoDB

MongoRunner is the built-in NoSQLRunner implementation for MongoDB. It uses pymongo and exposes only read-only operations.

Connect

from mango.integrations.mongodb import MongoRunner

db = MongoRunner()
db.connect("mongodb://localhost:27017/mydb")

The database name must be part of the URI. Use a MongoDB user limited to the read role: on connect, Mango checks the user's roles and logs a warning if it can write (or is unauthenticated).

Constructor

MongoRunner(
    max_time_ms=30_000,            # server-side time budget per query (maxTimeMS); 0 disables
    introspect_ttl_s=300.0,        # cache for describe_collection / schema inference; 0 disables
    allowed_collections=None,      # if set, only these are visible and queryable
    denied_collections=None,       # never visible; wins over allowed
)

system.* collections are always hidden. The collection policy is enforced on listing, introspection, field profiling and execution — including collections referenced by $lookup, $graphLookup and $unionWith. Per-user restrictions on top of it come from the CollectionAccess middleware.

Connection string formats

# Local
db.connect("mongodb://localhost:27017/mydb")

# With auth
db.connect("mongodb://reader:password@localhost:27017/mydb")

# MongoDB Atlas
db.connect("mongodb+srv://reader:password@cluster.mongodb.net/mydb")

# With options — extra kwargs go straight to MongoClient
db.connect("mongodb://localhost:27017/mydb", serverSelectionTimeoutMS=5000, connectTimeoutMS=10000)

Allowed operations

OperationNotes
findfilter, projection, sort, limit
aggregatepipeline; a terminal $limit is appended to enforce the row cap
countcount_documents() with optional filter
distinctemulated with $group + $sort + $limit (no 16 MB array)

Anything else raises ValidationError before reaching the server. Independently of the validator, execute_query() rejects forbidden operators at any depth of filter, pipeline, projection and sort ($out, $merge, $where, $function, $accumulator, $changeStream, admin stages), unknown pipeline stages, and inaccessible collections. See Security Model.

Literal coercion

The LLM writes JSON, MongoDB stores BSON. run_mql converts literals only when the target field's inferred type says so: ISO-8601 strings on date fields become datetime (offsets normalised to UTC), 24-hex strings on ObjectId fields become ObjectId, and Extended JSON {"$date": …} / {"$oid": …} are honoured. Fields of mixed type are left untouched.

Methods

# Execute a query → list of JSON-safe row dicts (absent fields stay absent, ints stay ints)
rows: list[dict] = db.execute_query(QueryRequest(operation="count", collection="users", filter={"active": True}))
# → [{"count": 1247}]

collections: list[str] = db.list_collections()                     # filtered by the collection policy
schema: dict[str, SchemaInfo] = db.introspect_schema()              # samples 100 docs per collection, cached
docs: list[dict] = db.get_sample_documents("orders", n=5)
indexes: list[dict] = db.get_indexes("orders")
profile: dict = db.profile_field("orders", "status", top_k=20)     # top values, cardinality, type breakdown
types: dict[str, set[str]] = db.field_types("orders")              # {dotted_path: {bson types}}
db.check_collection_access("orders")                               # raises ValidationError if hidden

BSON values in rows are made JSON-safe: datetime → ISO string, ObjectId / Decimal128 / Binary → string.

Error handling

ExceptionWhen raised
ValidationErrorforbidden operator or stage, inaccessible collection, disallowed operation, missing required argument
QueryErrorMongoDB rejected or timed out the query — the agent lets the LLM correct and retry
BackendErrorconnection failure, server unreachable — fatal for the turn, no retry