[{"data":1,"prerenderedAt":1820},["ShallowReactive",2],{"\u002Fdocs\u002Fadvanced\u002Fbackend-integration":3},{"id":4,"title":5,"body":6,"description":1813,"extension":1814,"meta":1815,"navigation":148,"path":1816,"seo":1817,"stem":1818,"__hash__":1819},"docs\u002Fdocs\u002F5.advanced\u002F6.backend-integration.md","Backend Integration",{"type":7,"value":8,"toc":1801},"minimark",[9,13,22,39,44,47,393,397,400,717,735,739,744,761,934,943,1235,1256,1260,1267,1320,1326,1330,1333,1410,1420,1424,1653,1657,1685,1689,1797],[10,11,5],"h1",{"id":12},"backend-integration",[14,15,16,17,21],"p",{},"This page is the end-to-end recipe for putting Mango behind your application: your users log in to ",[18,19,20],"em",{},"your"," app, ask questions, and get answers scoped to what they are allowed to see.",[23,24,26],"callout",{"type":25},"tip",[14,27,28,32,33,38],{},[29,30,31],"strong",{},"The one rule:"," Mango never authenticates. It receives a user object you already trust and enforces the permissions you attach to it. See ",[34,35,37],"a",{"href":36},"\u002Fdocs\u002Fcore-concepts\u002Faccess-control","Access Control"," for the model.",[40,41,43],"h2",{"id":42},"_1-build-the-agent-once","1. Build the agent once",[14,45,46],{},"The agent is a long-lived object: build it at application startup, not per request.",[48,49,54],"pre",{"className":50,"code":51,"language":52,"meta":53,"style":53},"language-python shiki shiki-themes github-dark","# mango_setup.py\nimport os\nfrom mango import MangoAgent\nfrom mango.tools import ToolRegistry, build_mongo_tools, SaveTextMemoryTool\nfrom mango.integrations.anthropic import AnthropicLlmService\nfrom mango.integrations.mongodb import MongoRunner\nfrom mango.integrations.chromadb import ChromaAgentMemory\n\nllm = AnthropicLlmService(model=\"claude-sonnet-4-6\", api_key=os.environ[\"ANTHROPIC_API_KEY\"])\n\ndb = MongoRunner(max_time_ms=30_000)          # server-side time budget per query\ndb.connect(os.environ[\"MONGODB_URI\"])         # use a MongoDB user with the `read` role\n\nmemory = ChromaAgentMemory(persist_dir=\"\u002Fvar\u002Flib\u002Fmango\u002Fmemory\")\n\ntools = ToolRegistry()\nfor tool in build_mongo_tools(db):\n    tools.register(tool)\ntools.register(SaveTextMemoryTool(memory))\n\nagent = MangoAgent(\n    llm_service=llm,\n    tool_registry=tools,\n    db=db,\n    agent_memory=memory,\n    introspect=True,      # schema-aware prompting; cached with a TTL\n)\nagent.setup()\n","python","",[55,56,57,66,77,91,104,117,130,143,150,189,194,220,235,240,262,267,278,293,299,305,310,321,332,343,354,365,382,387],"code",{"__ignoreMap":53},[58,59,62],"span",{"class":60,"line":61},"line",1,[58,63,65],{"class":64},"sAwPA","# mango_setup.py\n",[58,67,69,73],{"class":60,"line":68},2,[58,70,72],{"class":71},"snl16","import",[58,74,76],{"class":75},"s95oV"," os\n",[58,78,80,83,86,88],{"class":60,"line":79},3,[58,81,82],{"class":71},"from",[58,84,85],{"class":75}," mango ",[58,87,72],{"class":71},[58,89,90],{"class":75}," MangoAgent\n",[58,92,94,96,99,101],{"class":60,"line":93},4,[58,95,82],{"class":71},[58,97,98],{"class":75}," mango.tools ",[58,100,72],{"class":71},[58,102,103],{"class":75}," ToolRegistry, build_mongo_tools, SaveTextMemoryTool\n",[58,105,107,109,112,114],{"class":60,"line":106},5,[58,108,82],{"class":71},[58,110,111],{"class":75}," mango.integrations.anthropic ",[58,113,72],{"class":71},[58,115,116],{"class":75}," AnthropicLlmService\n",[58,118,120,122,125,127],{"class":60,"line":119},6,[58,121,82],{"class":71},[58,123,124],{"class":75}," mango.integrations.mongodb ",[58,126,72],{"class":71},[58,128,129],{"class":75}," MongoRunner\n",[58,131,133,135,138,140],{"class":60,"line":132},7,[58,134,82],{"class":71},[58,136,137],{"class":75}," mango.integrations.chromadb ",[58,139,72],{"class":71},[58,141,142],{"class":75}," ChromaAgentMemory\n",[58,144,146],{"class":60,"line":145},8,[58,147,149],{"emptyLinePlaceholder":148},true,"\n",[58,151,153,156,159,162,166,168,172,175,178,180,183,186],{"class":60,"line":152},9,[58,154,155],{"class":75},"llm ",[58,157,158],{"class":71},"=",[58,160,161],{"class":75}," AnthropicLlmService(",[58,163,165],{"class":164},"s9osk","model",[58,167,158],{"class":71},[58,169,171],{"class":170},"sU2Wk","\"claude-sonnet-4-6\"",[58,173,174],{"class":75},", ",[58,176,177],{"class":164},"api_key",[58,179,158],{"class":71},[58,181,182],{"class":75},"os.environ[",[58,184,185],{"class":170},"\"ANTHROPIC_API_KEY\"",[58,187,188],{"class":75},"])\n",[58,190,192],{"class":60,"line":191},10,[58,193,149],{"emptyLinePlaceholder":148},[58,195,197,200,202,205,208,210,214,217],{"class":60,"line":196},11,[58,198,199],{"class":75},"db ",[58,201,158],{"class":71},[58,203,204],{"class":75}," MongoRunner(",[58,206,207],{"class":164},"max_time_ms",[58,209,158],{"class":71},[58,211,213],{"class":212},"sDLfK","30_000",[58,215,216],{"class":75},")          ",[58,218,219],{"class":64},"# server-side time budget per query\n",[58,221,223,226,229,232],{"class":60,"line":222},12,[58,224,225],{"class":75},"db.connect(os.environ[",[58,227,228],{"class":170},"\"MONGODB_URI\"",[58,230,231],{"class":75},"])         ",[58,233,234],{"class":64},"# use a MongoDB user with the `read` role\n",[58,236,238],{"class":60,"line":237},13,[58,239,149],{"emptyLinePlaceholder":148},[58,241,243,246,248,251,254,256,259],{"class":60,"line":242},14,[58,244,245],{"class":75},"memory ",[58,247,158],{"class":71},[58,249,250],{"class":75}," ChromaAgentMemory(",[58,252,253],{"class":164},"persist_dir",[58,255,158],{"class":71},[58,257,258],{"class":170},"\"\u002Fvar\u002Flib\u002Fmango\u002Fmemory\"",[58,260,261],{"class":75},")\n",[58,263,265],{"class":60,"line":264},15,[58,266,149],{"emptyLinePlaceholder":148},[58,268,270,273,275],{"class":60,"line":269},16,[58,271,272],{"class":75},"tools ",[58,274,158],{"class":71},[58,276,277],{"class":75}," ToolRegistry()\n",[58,279,281,284,287,290],{"class":60,"line":280},17,[58,282,283],{"class":71},"for",[58,285,286],{"class":75}," tool ",[58,288,289],{"class":71},"in",[58,291,292],{"class":75}," build_mongo_tools(db):\n",[58,294,296],{"class":60,"line":295},18,[58,297,298],{"class":75},"    tools.register(tool)\n",[58,300,302],{"class":60,"line":301},19,[58,303,304],{"class":75},"tools.register(SaveTextMemoryTool(memory))\n",[58,306,308],{"class":60,"line":307},20,[58,309,149],{"emptyLinePlaceholder":148},[58,311,313,316,318],{"class":60,"line":312},21,[58,314,315],{"class":75},"agent ",[58,317,158],{"class":71},[58,319,320],{"class":75}," MangoAgent(\n",[58,322,324,327,329],{"class":60,"line":323},22,[58,325,326],{"class":164},"    llm_service",[58,328,158],{"class":71},[58,330,331],{"class":75},"llm,\n",[58,333,335,338,340],{"class":60,"line":334},23,[58,336,337],{"class":164},"    tool_registry",[58,339,158],{"class":71},[58,341,342],{"class":75},"tools,\n",[58,344,346,349,351],{"class":60,"line":345},24,[58,347,348],{"class":164},"    db",[58,350,158],{"class":71},[58,352,353],{"class":75},"db,\n",[58,355,357,360,362],{"class":60,"line":356},25,[58,358,359],{"class":164},"    agent_memory",[58,361,158],{"class":71},[58,363,364],{"class":75},"memory,\n",[58,366,368,371,373,376,379],{"class":60,"line":367},26,[58,369,370],{"class":164},"    introspect",[58,372,158],{"class":71},[58,374,375],{"class":212},"True",[58,377,378],{"class":75},",      ",[58,380,381],{"class":64},"# schema-aware prompting; cached with a TTL\n",[58,383,385],{"class":60,"line":384},27,[58,386,261],{"class":75},[58,388,390],{"class":60,"line":389},28,[58,391,392],{"class":75},"agent.setup()\n",[40,394,396],{"id":395},"_2-describe-your-users-to-mango","2. Describe your users to Mango",[14,398,399],{},"Pick the object your framework already gives you for the current user and write the role logic as functions of it:",[48,401,403],{"className":50,"code":402,"language":52,"meta":53,"style":53},"# mango_access.py\nfrom mango.middleware import CollectionAccess, RowFilter, RedactFields, DenyTools, Budget, AuditLog\n\nROLE_COLLECTIONS = {\n    \"support\": [\"users\", \"orders\", \"returns\", \"support_tickets\"],\n    \"finance\": [\"orders\", \"payments\", \"ledger_entries\", \"merchants\"],\n    \"admin\": \"*\",\n}\nPII = [\"users.email\", \"users.phone\", \"users.addresses\", \"payments.details\"]\n\ndef install_access_control(agent):\n    agent.use(CollectionAccess(lambda u: ROLE_COLLECTIONS[u.role]))\n    agent.use(RowFilter(\"users\", lambda u: {} if u.role == \"admin\" else {\"country\": u.country}))\n    agent.use(RedactFields(lambda u: [] if u.role == \"admin\" else PII))\n    agent.use(DenyTools(lambda u: [] if u.role == \"admin\" else [\n        \"save_text_memory\",                                  # tool\n        \"memory_train\", \"memory_import\", \"memory_export\",    # REST endpoints\n    ]))\n    agent.use(Budget(max_turns_per_day=300))\n    agent.use(AuditLog())\n",[55,404,405,410,422,426,437,466,492,506,511,542,546,558,574,612,638,660,671,692,697,712],{"__ignoreMap":53},[58,406,407],{"class":60,"line":61},[58,408,409],{"class":64},"# mango_access.py\n",[58,411,412,414,417,419],{"class":60,"line":68},[58,413,82],{"class":71},[58,415,416],{"class":75}," mango.middleware ",[58,418,72],{"class":71},[58,420,421],{"class":75}," CollectionAccess, RowFilter, RedactFields, DenyTools, Budget, AuditLog\n",[58,423,424],{"class":60,"line":79},[58,425,149],{"emptyLinePlaceholder":148},[58,427,428,431,434],{"class":60,"line":93},[58,429,430],{"class":212},"ROLE_COLLECTIONS",[58,432,433],{"class":71}," =",[58,435,436],{"class":75}," {\n",[58,438,439,442,445,448,450,453,455,458,460,463],{"class":60,"line":106},[58,440,441],{"class":170},"    \"support\"",[58,443,444],{"class":75},": [",[58,446,447],{"class":170},"\"users\"",[58,449,174],{"class":75},[58,451,452],{"class":170},"\"orders\"",[58,454,174],{"class":75},[58,456,457],{"class":170},"\"returns\"",[58,459,174],{"class":75},[58,461,462],{"class":170},"\"support_tickets\"",[58,464,465],{"class":75},"],\n",[58,467,468,471,473,475,477,480,482,485,487,490],{"class":60,"line":119},[58,469,470],{"class":170},"    \"finance\"",[58,472,444],{"class":75},[58,474,452],{"class":170},[58,476,174],{"class":75},[58,478,479],{"class":170},"\"payments\"",[58,481,174],{"class":75},[58,483,484],{"class":170},"\"ledger_entries\"",[58,486,174],{"class":75},[58,488,489],{"class":170},"\"merchants\"",[58,491,465],{"class":75},[58,493,494,497,500,503],{"class":60,"line":132},[58,495,496],{"class":170},"    \"admin\"",[58,498,499],{"class":75},": ",[58,501,502],{"class":170},"\"*\"",[58,504,505],{"class":75},",\n",[58,507,508],{"class":60,"line":145},[58,509,510],{"class":75},"}\n",[58,512,513,516,518,521,524,526,529,531,534,536,539],{"class":60,"line":152},[58,514,515],{"class":212},"PII",[58,517,433],{"class":71},[58,519,520],{"class":75}," [",[58,522,523],{"class":170},"\"users.email\"",[58,525,174],{"class":75},[58,527,528],{"class":170},"\"users.phone\"",[58,530,174],{"class":75},[58,532,533],{"class":170},"\"users.addresses\"",[58,535,174],{"class":75},[58,537,538],{"class":170},"\"payments.details\"",[58,540,541],{"class":75},"]\n",[58,543,544],{"class":60,"line":191},[58,545,149],{"emptyLinePlaceholder":148},[58,547,548,551,555],{"class":60,"line":196},[58,549,550],{"class":71},"def",[58,552,554],{"class":553},"svObZ"," install_access_control",[58,556,557],{"class":75},"(agent):\n",[58,559,560,563,566,569,571],{"class":60,"line":222},[58,561,562],{"class":75},"    agent.use(CollectionAccess(",[58,564,565],{"class":71},"lambda",[58,567,568],{"class":75}," u: ",[58,570,430],{"class":212},[58,572,573],{"class":75},"[u.role]))\n",[58,575,576,579,581,583,585,588,591,594,597,600,603,606,609],{"class":60,"line":237},[58,577,578],{"class":75},"    agent.use(RowFilter(",[58,580,447],{"class":170},[58,582,174],{"class":75},[58,584,565],{"class":71},[58,586,587],{"class":75}," u: {} ",[58,589,590],{"class":71},"if",[58,592,593],{"class":75}," u.role ",[58,595,596],{"class":71},"==",[58,598,599],{"class":170}," \"admin\"",[58,601,602],{"class":71}," else",[58,604,605],{"class":75}," {",[58,607,608],{"class":170},"\"country\"",[58,610,611],{"class":75},": u.country}))\n",[58,613,614,617,619,622,624,626,628,630,632,635],{"class":60,"line":242},[58,615,616],{"class":75},"    agent.use(RedactFields(",[58,618,565],{"class":71},[58,620,621],{"class":75}," u: [] ",[58,623,590],{"class":71},[58,625,593],{"class":75},[58,627,596],{"class":71},[58,629,599],{"class":170},[58,631,602],{"class":71},[58,633,634],{"class":212}," PII",[58,636,637],{"class":75},"))\n",[58,639,640,643,645,647,649,651,653,655,657],{"class":60,"line":264},[58,641,642],{"class":75},"    agent.use(DenyTools(",[58,644,565],{"class":71},[58,646,621],{"class":75},[58,648,590],{"class":71},[58,650,593],{"class":75},[58,652,596],{"class":71},[58,654,599],{"class":170},[58,656,602],{"class":71},[58,658,659],{"class":75}," [\n",[58,661,662,665,668],{"class":60,"line":269},[58,663,664],{"class":170},"        \"save_text_memory\"",[58,666,667],{"class":75},",                                  ",[58,669,670],{"class":64},"# tool\n",[58,672,673,676,678,681,683,686,689],{"class":60,"line":280},[58,674,675],{"class":170},"        \"memory_train\"",[58,677,174],{"class":75},[58,679,680],{"class":170},"\"memory_import\"",[58,682,174],{"class":75},[58,684,685],{"class":170},"\"memory_export\"",[58,687,688],{"class":75},",    ",[58,690,691],{"class":64},"# REST endpoints\n",[58,693,694],{"class":60,"line":295},[58,695,696],{"class":75},"    ]))\n",[58,698,699,702,705,707,710],{"class":60,"line":301},[58,700,701],{"class":75},"    agent.use(Budget(",[58,703,704],{"class":164},"max_turns_per_day",[58,706,158],{"class":71},[58,708,709],{"class":212},"300",[58,711,637],{"class":75},[58,713,714],{"class":60,"line":307},[58,715,716],{"class":75},"    agent.use(AuditLog())\n",[14,718,719,722,723,726,727,730,731,734],{},[55,720,721],{},"u"," here is whatever your ",[55,724,725],{},"user_for"," returns (next step). Attributes like ",[55,728,729],{},"role"," and ",[55,732,733],{},"country"," are yours.",[40,736,738],{"id":737},"_3-wire-authentication","3. Wire authentication",[740,741,743],"h3",{"id":742},"fastapi-mount-the-router-into-your-app","FastAPI — mount the router into your app",[14,745,746,749,750,753,754,756,757,760],{},[55,747,748],{},"mango_router"," returns an ",[55,751,752],{},"APIRouter",". ",[55,755,725],{}," is the only contact point with your auth: it receives the request and returns your user object, or raises ",[55,758,759],{},"HTTPException(401)",".",[48,762,764],{"className":50,"code":763,"language":52,"meta":53,"style":53},"# main.py\nfrom fastapi import FastAPI, HTTPException, Request\nfrom mango.servers.fastapi import mango_router\nfrom mango_setup import agent\nfrom mango_access import install_access_control\n\napp = FastAPI()\ninstall_access_control(agent)\n\ndef user_for(request: Request):\n    user = request.state.user            # set by your own auth middleware \u002F dependency\n    if user is None:\n        raise HTTPException(status_code=401)\n    return user\n\napp.include_router(mango_router(agent, user_for=user_for), prefix=\"\u002Fmango\")\n",[55,765,766,771,783,795,807,819,823,833,838,842,852,865,882,900,908,912],{"__ignoreMap":53},[58,767,768],{"class":60,"line":61},[58,769,770],{"class":64},"# main.py\n",[58,772,773,775,778,780],{"class":60,"line":68},[58,774,82],{"class":71},[58,776,777],{"class":75}," fastapi ",[58,779,72],{"class":71},[58,781,782],{"class":75}," FastAPI, HTTPException, Request\n",[58,784,785,787,790,792],{"class":60,"line":79},[58,786,82],{"class":71},[58,788,789],{"class":75}," mango.servers.fastapi ",[58,791,72],{"class":71},[58,793,794],{"class":75}," mango_router\n",[58,796,797,799,802,804],{"class":60,"line":93},[58,798,82],{"class":71},[58,800,801],{"class":75}," mango_setup ",[58,803,72],{"class":71},[58,805,806],{"class":75}," agent\n",[58,808,809,811,814,816],{"class":60,"line":106},[58,810,82],{"class":71},[58,812,813],{"class":75}," mango_access ",[58,815,72],{"class":71},[58,817,818],{"class":75}," install_access_control\n",[58,820,821],{"class":60,"line":119},[58,822,149],{"emptyLinePlaceholder":148},[58,824,825,828,830],{"class":60,"line":132},[58,826,827],{"class":75},"app ",[58,829,158],{"class":71},[58,831,832],{"class":75}," FastAPI()\n",[58,834,835],{"class":60,"line":145},[58,836,837],{"class":75},"install_access_control(agent)\n",[58,839,840],{"class":60,"line":152},[58,841,149],{"emptyLinePlaceholder":148},[58,843,844,846,849],{"class":60,"line":191},[58,845,550],{"class":71},[58,847,848],{"class":553}," user_for",[58,850,851],{"class":75},"(request: Request):\n",[58,853,854,857,859,862],{"class":60,"line":196},[58,855,856],{"class":75},"    user ",[58,858,158],{"class":71},[58,860,861],{"class":75}," request.state.user            ",[58,863,864],{"class":64},"# set by your own auth middleware \u002F dependency\n",[58,866,867,870,873,876,879],{"class":60,"line":222},[58,868,869],{"class":71},"    if",[58,871,872],{"class":75}," user ",[58,874,875],{"class":71},"is",[58,877,878],{"class":212}," None",[58,880,881],{"class":75},":\n",[58,883,884,887,890,893,895,898],{"class":60,"line":237},[58,885,886],{"class":71},"        raise",[58,888,889],{"class":75}," HTTPException(",[58,891,892],{"class":164},"status_code",[58,894,158],{"class":71},[58,896,897],{"class":212},"401",[58,899,261],{"class":75},[58,901,902,905],{"class":60,"line":242},[58,903,904],{"class":71},"    return",[58,906,907],{"class":75}," user\n",[58,909,910],{"class":60,"line":264},[58,911,149],{"emptyLinePlaceholder":148},[58,913,914,917,919,921,924,927,929,932],{"class":60,"line":269},[58,915,916],{"class":75},"app.include_router(mango_router(agent, ",[58,918,725],{"class":164},[58,920,158],{"class":71},[58,922,923],{"class":75},"user_for), ",[58,925,926],{"class":164},"prefix",[58,928,158],{"class":71},[58,930,931],{"class":170},"\"\u002Fmango\"",[58,933,261],{"class":75},[14,935,936,938,939,942],{},[55,937,725],{}," may be ",[55,940,941],{},"async",". Three common shapes:",[48,944,946],{"className":50,"code":945,"language":52,"meta":53,"style":53},"# JWT (any library that verifies the token for you)\nasync def user_for(request: Request):\n    token = request.headers.get(\"Authorization\", \"\").removeprefix(\"Bearer \").strip()\n    try:\n        claims = verify_jwt(token)      # your verifier: signature, issuer, audience, expiry\n    except InvalidToken:\n        raise HTTPException(status_code=401)\n    return {\"id\": claims[\"sub\"], \"role\": claims[\"role\"], \"country\": claims.get(\"country\", \"\")}\n\n# Behind an API gateway that already authenticated (Kong, Envoy, Cloudflare Access…)\ndef user_for(request: Request):\n    if request.headers.get(\"X-Gateway-Secret\") != settings.GATEWAY_SECRET:\n        raise HTTPException(status_code=401)\n    return {\"id\": request.headers[\"X-User-Id\"], \"role\": request.headers[\"X-User-Role\"]}\n\n# Service-to-service with a static API key\ndef user_for(request: Request):\n    if not secrets.compare_digest(request.headers.get(\"X-API-Key\", \"\"), settings.MANGO_KEY):\n        raise HTTPException(status_code=401)\n    return {\"id\": \"reporting-job\", \"role\": \"finance\"}\n",[55,947,948,953,964,991,998,1011,1019,1033,1074,1078,1083,1091,1114,1128,1154,1158,1163,1171,1197,1211],{"__ignoreMap":53},[58,949,950],{"class":60,"line":61},[58,951,952],{"class":64},"# JWT (any library that verifies the token for you)\n",[58,954,955,957,960,962],{"class":60,"line":68},[58,956,941],{"class":71},[58,958,959],{"class":71}," def",[58,961,848],{"class":553},[58,963,851],{"class":75},[58,965,966,969,971,974,977,979,982,985,988],{"class":60,"line":79},[58,967,968],{"class":75},"    token ",[58,970,158],{"class":71},[58,972,973],{"class":75}," request.headers.get(",[58,975,976],{"class":170},"\"Authorization\"",[58,978,174],{"class":75},[58,980,981],{"class":170},"\"\"",[58,983,984],{"class":75},").removeprefix(",[58,986,987],{"class":170},"\"Bearer \"",[58,989,990],{"class":75},").strip()\n",[58,992,993,996],{"class":60,"line":93},[58,994,995],{"class":71},"    try",[58,997,881],{"class":75},[58,999,1000,1003,1005,1008],{"class":60,"line":106},[58,1001,1002],{"class":75},"        claims ",[58,1004,158],{"class":71},[58,1006,1007],{"class":75}," verify_jwt(token)      ",[58,1009,1010],{"class":64},"# your verifier: signature, issuer, audience, expiry\n",[58,1012,1013,1016],{"class":60,"line":119},[58,1014,1015],{"class":71},"    except",[58,1017,1018],{"class":75}," InvalidToken:\n",[58,1020,1021,1023,1025,1027,1029,1031],{"class":60,"line":132},[58,1022,886],{"class":71},[58,1024,889],{"class":75},[58,1026,892],{"class":164},[58,1028,158],{"class":71},[58,1030,897],{"class":212},[58,1032,261],{"class":75},[58,1034,1035,1037,1039,1042,1045,1048,1051,1054,1056,1058,1060,1062,1065,1067,1069,1071],{"class":60,"line":145},[58,1036,904],{"class":71},[58,1038,605],{"class":75},[58,1040,1041],{"class":170},"\"id\"",[58,1043,1044],{"class":75},": claims[",[58,1046,1047],{"class":170},"\"sub\"",[58,1049,1050],{"class":75},"], ",[58,1052,1053],{"class":170},"\"role\"",[58,1055,1044],{"class":75},[58,1057,1053],{"class":170},[58,1059,1050],{"class":75},[58,1061,608],{"class":170},[58,1063,1064],{"class":75},": claims.get(",[58,1066,608],{"class":170},[58,1068,174],{"class":75},[58,1070,981],{"class":170},[58,1072,1073],{"class":75},")}\n",[58,1075,1076],{"class":60,"line":152},[58,1077,149],{"emptyLinePlaceholder":148},[58,1079,1080],{"class":60,"line":191},[58,1081,1082],{"class":64},"# Behind an API gateway that already authenticated (Kong, Envoy, Cloudflare Access…)\n",[58,1084,1085,1087,1089],{"class":60,"line":196},[58,1086,550],{"class":71},[58,1088,848],{"class":553},[58,1090,851],{"class":75},[58,1092,1093,1095,1097,1100,1103,1106,1109,1112],{"class":60,"line":222},[58,1094,869],{"class":71},[58,1096,973],{"class":75},[58,1098,1099],{"class":170},"\"X-Gateway-Secret\"",[58,1101,1102],{"class":75},") ",[58,1104,1105],{"class":71},"!=",[58,1107,1108],{"class":75}," settings.",[58,1110,1111],{"class":212},"GATEWAY_SECRET",[58,1113,881],{"class":75},[58,1115,1116,1118,1120,1122,1124,1126],{"class":60,"line":237},[58,1117,886],{"class":71},[58,1119,889],{"class":75},[58,1121,892],{"class":164},[58,1123,158],{"class":71},[58,1125,897],{"class":212},[58,1127,261],{"class":75},[58,1129,1130,1132,1134,1136,1139,1142,1144,1146,1148,1151],{"class":60,"line":242},[58,1131,904],{"class":71},[58,1133,605],{"class":75},[58,1135,1041],{"class":170},[58,1137,1138],{"class":75},": request.headers[",[58,1140,1141],{"class":170},"\"X-User-Id\"",[58,1143,1050],{"class":75},[58,1145,1053],{"class":170},[58,1147,1138],{"class":75},[58,1149,1150],{"class":170},"\"X-User-Role\"",[58,1152,1153],{"class":75},"]}\n",[58,1155,1156],{"class":60,"line":264},[58,1157,149],{"emptyLinePlaceholder":148},[58,1159,1160],{"class":60,"line":269},[58,1161,1162],{"class":64},"# Service-to-service with a static API key\n",[58,1164,1165,1167,1169],{"class":60,"line":280},[58,1166,550],{"class":71},[58,1168,848],{"class":553},[58,1170,851],{"class":75},[58,1172,1173,1175,1178,1181,1184,1186,1188,1191,1194],{"class":60,"line":295},[58,1174,869],{"class":71},[58,1176,1177],{"class":71}," not",[58,1179,1180],{"class":75}," secrets.compare_digest(request.headers.get(",[58,1182,1183],{"class":170},"\"X-API-Key\"",[58,1185,174],{"class":75},[58,1187,981],{"class":170},[58,1189,1190],{"class":75},"), settings.",[58,1192,1193],{"class":212},"MANGO_KEY",[58,1195,1196],{"class":75},"):\n",[58,1198,1199,1201,1203,1205,1207,1209],{"class":60,"line":301},[58,1200,886],{"class":71},[58,1202,889],{"class":75},[58,1204,892],{"class":164},[58,1206,158],{"class":71},[58,1208,897],{"class":212},[58,1210,261],{"class":75},[58,1212,1213,1215,1217,1219,1221,1224,1226,1228,1230,1233],{"class":60,"line":307},[58,1214,904],{"class":71},[58,1216,605],{"class":75},[58,1218,1041],{"class":170},[58,1220,499],{"class":75},[58,1222,1223],{"class":170},"\"reporting-job\"",[58,1225,174],{"class":75},[58,1227,1053],{"class":170},[58,1229,499],{"class":75},[58,1231,1232],{"class":170},"\"finance\"",[58,1234,510],{"class":75},[23,1236,1238],{"type":1237},"warning",[14,1239,1240,1241,1244,1245,1248,1249,1248,1252,1255],{},"Give your user object an ",[55,1242,1243],{},"id"," (or ",[55,1246,1247],{},"sub"," \u002F ",[55,1250,1251],{},"email",[55,1253,1254],{},"username","). Mango uses it to bind sessions to users and to key budgets and audit records.",[740,1257,1259],{"id":1258},"standalone-server","Standalone server",[14,1261,1262,1263,1266],{},"If Mango is its own service, the same callback goes to ",[55,1264,1265],{},"MangoFastAPIServer",":",[48,1268,1270],{"className":50,"code":1269,"language":52,"meta":53,"style":53},"from mango.servers.fastapi import MangoFastAPIServer\n\nMangoFastAPIServer(agent, user_for=user_for, cors_origins=\"https:\u002F\u002Fapp.example.com\").run(port=8000)\n",[55,1271,1272,1283,1287],{"__ignoreMap":53},[58,1273,1274,1276,1278,1280],{"class":60,"line":61},[58,1275,82],{"class":71},[58,1277,789],{"class":75},[58,1279,72],{"class":71},[58,1281,1282],{"class":75}," MangoFastAPIServer\n",[58,1284,1285],{"class":60,"line":68},[58,1286,149],{"emptyLinePlaceholder":148},[58,1288,1289,1292,1294,1296,1299,1302,1304,1307,1310,1313,1315,1318],{"class":60,"line":79},[58,1290,1291],{"class":75},"MangoFastAPIServer(agent, ",[58,1293,725],{"class":164},[58,1295,158],{"class":71},[58,1297,1298],{"class":75},"user_for, ",[58,1300,1301],{"class":164},"cors_origins",[58,1303,158],{"class":71},[58,1305,1306],{"class":170},"\"https:\u002F\u002Fapp.example.com\"",[58,1308,1309],{"class":75},").run(",[58,1311,1312],{"class":164},"port",[58,1314,158],{"class":71},[58,1316,1317],{"class":212},"8000",[58,1319,261],{"class":75},[14,1321,1322,1323,1325],{},"Without ",[55,1324,725],{}," the server runs open and logs a warning at startup — fine on localhost, never on a network.",[740,1327,1329],{"id":1328},"not-fastapi-call-the-agent-directly","Not FastAPI? Call the agent directly",[14,1331,1332],{},"The HTTP layer is a thin adapter. From Django, Flask, a Celery worker or a CLI you call the agent yourself and pass the user:",[48,1334,1336],{"className":50,"code":1335,"language":52,"meta":53,"style":53},"# Django (async view)\nasync def ask(request):\n    body = json.loads(request.body)\n    response = await agent.new_session().ask(body[\"question\"], user=request.user)\n    return JsonResponse({\"answer\": response.answer, \"tools\": response.tool_calls_made})\n",[55,1337,1338,1343,1355,1365,1391],{"__ignoreMap":53},[58,1339,1340],{"class":60,"line":61},[58,1341,1342],{"class":64},"# Django (async view)\n",[58,1344,1345,1347,1349,1352],{"class":60,"line":68},[58,1346,941],{"class":71},[58,1348,959],{"class":71},[58,1350,1351],{"class":553}," ask",[58,1353,1354],{"class":75},"(request):\n",[58,1356,1357,1360,1362],{"class":60,"line":79},[58,1358,1359],{"class":75},"    body ",[58,1361,158],{"class":71},[58,1363,1364],{"class":75}," json.loads(request.body)\n",[58,1366,1367,1370,1372,1375,1378,1381,1383,1386,1388],{"class":60,"line":93},[58,1368,1369],{"class":75},"    response ",[58,1371,158],{"class":71},[58,1373,1374],{"class":71}," await",[58,1376,1377],{"class":75}," agent.new_session().ask(body[",[58,1379,1380],{"class":170},"\"question\"",[58,1382,1050],{"class":75},[58,1384,1385],{"class":164},"user",[58,1387,158],{"class":71},[58,1389,1390],{"class":75},"request.user)\n",[58,1392,1393,1395,1398,1401,1404,1407],{"class":60,"line":106},[58,1394,904],{"class":71},[58,1396,1397],{"class":75}," JsonResponse({",[58,1399,1400],{"class":170},"\"answer\"",[58,1402,1403],{"class":75},": response.answer, ",[58,1405,1406],{"class":170},"\"tools\"",[58,1408,1409],{"class":75},": response.tool_calls_made})\n",[14,1411,1412,1415,1416,1419],{},[55,1413,1414],{},"new_session()"," is cheap and gives each conversation its own history; keep a map ",[55,1417,1418],{},"conversation_id → agent"," if you want follow-up questions.",[40,1421,1423],{"id":1422},"_4-talk-to-it-from-the-frontend","4. Talk to it from the frontend",[48,1425,1429],{"className":1426,"code":1427,"language":1428,"meta":53,"style":53},"language-javascript shiki shiki-themes github-dark","const res = await fetch('\u002Fmango\u002Fask\u002Fstream', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application\u002Fjson', Authorization: `Bearer ${token}` },\n  body: JSON.stringify({ question, session_id }),   \u002F\u002F session_id: null on the first turn\n})\nfor await (const event of sse(res)) {               \u002F\u002F see FastAPI Server for a reader\n  if (event.type === 'session')      session_id = event.session_id\n  if (event.type === 'tool_result' && !event.success) showBlocked(event.preview)\n  if (event.type === 'answer')       render(event.text)\n  if (event.type === 'error')        showError(event.code === 'access_denied' ? event.message : 'Something went wrong')\n}\n","javascript",[55,1430,1431,1455,1465,1493,1512,1517,1543,1565,1591,1611,1649],{"__ignoreMap":53},[58,1432,1433,1436,1439,1441,1443,1446,1449,1452],{"class":60,"line":61},[58,1434,1435],{"class":71},"const",[58,1437,1438],{"class":212}," res",[58,1440,433],{"class":71},[58,1442,1374],{"class":71},[58,1444,1445],{"class":553}," fetch",[58,1447,1448],{"class":75},"(",[58,1450,1451],{"class":170},"'\u002Fmango\u002Fask\u002Fstream'",[58,1453,1454],{"class":75},", {\n",[58,1456,1457,1460,1463],{"class":60,"line":68},[58,1458,1459],{"class":75},"  method: ",[58,1461,1462],{"class":170},"'POST'",[58,1464,505],{"class":75},[58,1466,1467,1470,1473,1475,1478,1481,1484,1487,1490],{"class":60,"line":79},[58,1468,1469],{"class":75},"  headers: { ",[58,1471,1472],{"class":170},"'Content-Type'",[58,1474,499],{"class":75},[58,1476,1477],{"class":170},"'application\u002Fjson'",[58,1479,1480],{"class":75},", Authorization: ",[58,1482,1483],{"class":170},"`Bearer ${",[58,1485,1486],{"class":75},"token",[58,1488,1489],{"class":170},"}`",[58,1491,1492],{"class":75}," },\n",[58,1494,1495,1498,1501,1503,1506,1509],{"class":60,"line":93},[58,1496,1497],{"class":75},"  body: ",[58,1499,1500],{"class":212},"JSON",[58,1502,760],{"class":75},[58,1504,1505],{"class":553},"stringify",[58,1507,1508],{"class":75},"({ question, session_id }),   ",[58,1510,1511],{"class":64},"\u002F\u002F session_id: null on the first turn\n",[58,1513,1514],{"class":60,"line":106},[58,1515,1516],{"class":75},"})\n",[58,1518,1519,1521,1523,1526,1528,1531,1534,1537,1540],{"class":60,"line":119},[58,1520,283],{"class":71},[58,1522,1374],{"class":71},[58,1524,1525],{"class":75}," (",[58,1527,1435],{"class":71},[58,1529,1530],{"class":212}," event",[58,1532,1533],{"class":71}," of",[58,1535,1536],{"class":553}," sse",[58,1538,1539],{"class":75},"(res)) {               ",[58,1541,1542],{"class":64},"\u002F\u002F see FastAPI Server for a reader\n",[58,1544,1545,1548,1551,1554,1557,1560,1562],{"class":60,"line":132},[58,1546,1547],{"class":71},"  if",[58,1549,1550],{"class":75}," (event.type ",[58,1552,1553],{"class":71},"===",[58,1555,1556],{"class":170}," 'session'",[58,1558,1559],{"class":75},")      session_id ",[58,1561,158],{"class":71},[58,1563,1564],{"class":75}," event.session_id\n",[58,1566,1567,1569,1571,1573,1576,1579,1582,1585,1588],{"class":60,"line":145},[58,1568,1547],{"class":71},[58,1570,1550],{"class":75},[58,1572,1553],{"class":71},[58,1574,1575],{"class":170}," 'tool_result'",[58,1577,1578],{"class":71}," &&",[58,1580,1581],{"class":71}," !",[58,1583,1584],{"class":75},"event.success) ",[58,1586,1587],{"class":553},"showBlocked",[58,1589,1590],{"class":75},"(event.preview)\n",[58,1592,1593,1595,1597,1599,1602,1605,1608],{"class":60,"line":152},[58,1594,1547],{"class":71},[58,1596,1550],{"class":75},[58,1598,1553],{"class":71},[58,1600,1601],{"class":170}," 'answer'",[58,1603,1604],{"class":75},")       ",[58,1606,1607],{"class":553},"render",[58,1609,1610],{"class":75},"(event.text)\n",[58,1612,1613,1615,1617,1619,1622,1625,1628,1631,1633,1636,1639,1642,1644,1647],{"class":60,"line":191},[58,1614,1547],{"class":71},[58,1616,1550],{"class":75},[58,1618,1553],{"class":71},[58,1620,1621],{"class":170}," 'error'",[58,1623,1624],{"class":75},")        ",[58,1626,1627],{"class":553},"showError",[58,1629,1630],{"class":75},"(event.code ",[58,1632,1553],{"class":71},[58,1634,1635],{"class":170}," 'access_denied'",[58,1637,1638],{"class":71}," ?",[58,1640,1641],{"class":75}," event.message ",[58,1643,1266],{"class":71},[58,1645,1646],{"class":170}," 'Something went wrong'",[58,1648,261],{"class":75},[58,1650,1651],{"class":60,"line":196},[58,1652,510],{"class":75},[40,1654,1656],{"id":1655},"_5-memory-in-a-multi-user-deployment","5. Memory in a multi-user deployment",[1658,1659,1660,1677,1680],"ul",{},[1661,1662,1663,174,1666,1669,1670,1673,1674,760],"li",{},[55,1664,1665],{},"save_text_memory",[55,1667,1668],{},"delete_last_memory_entry"," and the ",[55,1671,1672],{},"\u002Fmemory\u002F*"," endpoints write to a store shared by everyone. Restrict them to trusted roles with ",[55,1675,1676],{},"DenyTools",[1661,1678,1679],{},"Memory examples are filtered by the caller's visible collections before they enter the prompt, so a support agent never sees a finance query as an example.",[1661,1681,1682,1683,760],{},"One agent = one database = one memory directory. Do not point two databases at the same ",[55,1684,253],{},[40,1686,1688],{"id":1687},"_6-production-checklist","6. Production checklist",[1690,1691,1692,1709,1717,1735,1751,1757,1770,1784],"ol",{},[1661,1693,1694,1701,1702,174,1705,1708],{},[29,1695,1696,1697,1700],{},"MongoDB user with the ",[55,1698,1699],{},"read"," role."," Mango blocks writes at the query level (",[55,1703,1704],{},"$out",[55,1706,1707],{},"$merge",", server-side JavaScript, admin stages, at any depth) and logs a warning if the connected user can write — but the database role is the first ring, Mango is the second.",[1661,1710,1711,1716],{},[29,1712,1713,1715],{},[55,1714,725],{}," on every deployment"," that is reachable from a network.",[1661,1718,1719,1722,1723,1726,1727,1730,1731,1734],{},[29,1720,1721],{},"Scope collections"," with ",[55,1724,1725],{},"CollectionAccess"," or ",[55,1728,1729],{},"MongoRunner(allowed_collections=[…])",", and mask PII with ",[55,1732,1733],{},"RedactFields",". Sampled documents and query results are sent to the LLM provider.",[1661,1736,1737,1740,1741,1248,1743,1746,1747,1750],{},[29,1738,1739],{},"CORS",": set ",[55,1742,1301],{},[55,1744,1745],{},"MANGO_CORS_ORIGINS"," to your frontend origin(s); the default ",[55,1748,1749],{},"*"," disables credentials as the CORS spec requires.",[1661,1752,1753,1756],{},[29,1754,1755],{},"Reverse proxy"," with TLS and rate limiting in front. Mango caps question length (4 000 chars), session id format and import size (1 000 entries) but does not rate-limit.",[1661,1758,1759,499,1762,1765,1766,1769],{},[29,1760,1761],{},"Time and size budgets",[55,1763,1764],{},"MongoRunner(max_time_ms=…)"," bounds every query server-side; ",[55,1767,1768],{},"run_mql"," caps rows at 100 by default.",[1661,1771,1772,1775,1776,1779,1780,1783],{},[29,1773,1774],{},"Audit",": keep ",[55,1777,1778],{},"AuditLog()"," on and ship the ",[55,1781,1782],{},"mango.audit"," logger to your log pipeline.",[1661,1785,1786,1789,1790,1248,1793,1796],{},[29,1787,1788],{},"Sessions",": tune ",[55,1791,1792],{},"session_ttl_seconds",[55,1794,1795],{},"max_sessions"," on the router to your traffic; sessions live in process memory.",[1798,1799,1800],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}",{"title":53,"searchDepth":68,"depth":68,"links":1802},[1803,1804,1805,1810,1811,1812],{"id":42,"depth":68,"text":43},{"id":395,"depth":68,"text":396},{"id":737,"depth":68,"text":738,"children":1806},[1807,1808,1809],{"id":742,"depth":79,"text":743},{"id":1258,"depth":79,"text":1259},{"id":1328,"depth":79,"text":1329},{"id":1422,"depth":68,"text":1423},{"id":1655,"depth":68,"text":1656},{"id":1687,"depth":68,"text":1688},"The complete guide to running Mango inside your own backend — auth, permissions, sessions, memory, production checklist.","md",{},"\u002Fdocs\u002Fadvanced\u002Fbackend-integration",{"title":5,"description":1813},"docs\u002F5.advanced\u002F6.backend-integration","f2Czhlop7vhE893HzIhdAlQ2OLUdkyEujEbXsKuUSWw",1789553695272]