Munin med OpenAI Agents SDK.
Hver eneste anden integrationsside her handler om en klient, en anden har skrevet. Denne handler om det tilfælde, hvor du er klienten — én MCPServerStreamableHttp, et værktøjsfilter, en godkendelsespolitik, du styrer, og en gennemgangskø nedenunder, der holder uanset hvad din kode beslutter.

Der kommer et øjeblik i ethvert agentprojekt, hvor de hostede klienter holder op med at være nok. Du vil have, at sløjfen kører klokken 06:00, uden at nogen åbner en laptop. Du vil have, at den læser fra din egen kø, skriver til dine egne logge og fejler på en måde, din vagtplan forstår. På det punkt leder du ikke efter et chatvindue. Du skriver et program, og programmet har brug for værktøjer.
Hvordan forbinder du OpenAI Agents SDK til et CRM?
Peg en MCPServerStreamableHttp mod en MCP-server, der allerede har kundedataene i sig. Munin serverer CRM, samtaler, videnbase, CMS, udgående kontakt og analyse som MCP-værktøjer på ét HTTPS-endpoint, så et enkelt serverobjekt i din Python-proces giver en Agent hele kundeplatformen — ingen SDK per modul, ingen REST-klient, intet eget skema at vedligeholde.
import asyncio
import os
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp
async def main() -> None:
async with MCPServerStreamableHttp(
name="Munin",
params={
"url": "https://mcp.getmunin.com",
"headers": {"Authorization": f"Bearer {os.environ['MUNIN_API_KEY']}"},
"timeout": 30,
},
cache_tools_list=True,
max_retry_attempts=3,
) as server:
agent = Agent(
name="Desk",
instructions="Brug Munin-værktøjerne. Citér kundens egne ord.",
mcp_servers=[server],
)
result = await Runner.run(
agent,
"Hvilke samtaler fra de sidste syv dage fik aldrig svar?",
)
print(result.final_output)
asyncio.run(main())Det er hele integrationen. pip install openai-agents på Python 3.10 eller nyere, en adminnøgle lavet under Indstillinger → API-nøgler, og agenten kan nå crm_search_contacts, conv_list_conversations, kb_search, cms_create_entry og resten af kataloget ved navn.
To konstruktørargumenter er værd at sætte bevidst frem for at kopiere. cache_tools_list=True forhindrer SDK'en i at liste værktøjer igen ved hver kørsel — værktøjsdefinitionerne ændrer sig ikke mellem dine 06:00- og 06:05-kørsler, og at liste et stort katalog over netværket er det langsomste i sløjfen. Kald invalidate_tools_cache() på serveren, hvis du har brug for en frisk liste midt i processen. max_retry_attempts tilføjer automatiske gentagne forsøg omkring list_tools() og call_tool(), hvilket betyder mere for et ubemandet cron-job end for et menneske, der sidder foran et chatvindue og bare kan prøve igen.
Driver du selv i stedet for Cloud? Det samme objekt, peget mod http://localhost:3001/mcp. Din proces laver forbindelsen, så et privat endpoint er fint her på en måde, det ikke er for enhver rute — mere om det nedenfor.
Hvilken af SDK'ens fire MCP-integrationer bør du bruge?
Python-SDK'en understøtter fire, og valget er i virkeligheden et spørgsmål om, hvor værktøjskaldet udføres. For en kundeplatform, der holder rigtige poster, er Streamable HTTP inde i din egen proces den rigtige standard: din kode holder legitimationen, din kode ser hvert kald, og endpointet skal aldrig kunne nås fra andet end din infrastruktur.
- MCPServerStreamableHttpDin proces åbner forbindelsen og foretager kaldene. Legitimationen bliver i dit miljø, trafikken bliver på din netværksvej, og et privat eller selvdrevet endpoint fungerer fint. Det er den, du skal gribe efter.
- HostedMCPToolDu giver OpenAI's Responses API et
server_labelog enserver_url, og OpenAI's infrastruktur kalder serveren på modellens vegne. Mindre kode og ingen returtur til Python — men dokumentationen er eksplicit om, at serveren skal være offentligt tilgængelig, og den fungerer i dag med OpenAI-modeller, der understøtter Responses API'ets hostede MCP-integration. - MCPServerSseDen ældre HTTP-med-SSE-transport. MCP-projektet har udfaset den; SDK-dokumentationen siger behold den til ældre servere og foretræk Streamable HTTP til alt nyt.
- MCPServerStdioSDK'en starter en lokal underproces og taler over stdin og stdout. Rigtigt til en filsystemserver på din egen maskine, forkert til en multi-tenant-platform, der bor bag et HTTPS-endpoint.
Den hostede vej har en netværksform, ikke en kvalitetsforskel
HostedMCPTool er reelt mindre kode. Det betyder også, at OpenAI's servere, ikke dine, skal kunne åbne en forbindelse til dit MCP-endpoint — så en laptopinstallation eller en kun-VPC-udrulning er udelukket.
Munin Cloud er offentligt tilgængelig og fungerer begge veje. Vælg den hostede vej, når du vil have Responses API til at eje returturen; vælg Streamable HTTP, når du vil have legitimationen og kaldloggen inde i din egen proces.
Hvordan forhindrer jeg agenten i at se hvert eneste værktøj i kataloget?
Brug tool_filter. En fuld kundeplatform eksponerer en stor værktøjsflade på tværs af seks moduler, og en agent, hvis eneste job er at triagere indbakken, har intet at gøre med et værktøj, der slår kontakter sammen. create_static_tool_filter tager en tilladelsesliste, en blokeringsliste eller begge — SDK'en anvender tilladelseslisten først og fjerner derefter alt blokeret fra det, der er tilbage.
Det er de ti mest værdifulde linjer i filen. At afgrænse værktøjer er ikke kun et argument om ventetid og tokens; det er forskellen mellem en agent, der ikke kan gøre det forkerte, og en agent, du stoler på ikke gør det.
from agents import Agent
from agents.mcp import MCPServerStreamableHttp, create_static_tool_filter
triage = MCPServerStreamableHttp(
name="Munin (triage)",
params={"url": "https://mcp.getmunin.com", "headers": headers},
tool_filter=create_static_tool_filter(
allowed_tool_names=[
"conv_list_conversations",
"conv_search_messages",
"conv_set_topic",
"conv_set_subject",
"crm_lookup_contact",
"kb_search",
],
),
)
agent = Agent(
name="Triage",
mcp_servers=[triage],
mcp_config={
"convert_schemas_to_strict": True,
"include_server_in_tool_names": True,
},
)mcp_config ligger på agentniveau frem for serverniveau, og begge nøgler fortjener deres plads. convert_schemas_to_strict er bedste indsats — hvor et værktøjsskema kan konverteres til strikt JSON-skema, bliver det det, og hvor det ikke kan, bruges originalen, så det koster intet at lade den stå til. include_server_in_tool_names sætter servernavnet foran lokale MCP-værktøjsnavne, hvilket stopper kollisioner i det øjeblik, du tilføjer en server nummer to; kører du Munin ved siden af en filsystem- eller søgeserver, så slå den til før det første navnesammenstød frem for efter.
Til logik, en statisk liste ikke kan udtrykke, sender du en callable ind i stedet. Den får en ToolFilterContext, der bærer den aktive run_context, den agent, der beder om værktøjerne, og server_name, og returnerer True, når værktøjet skal eksponeres — så ét serverobjekt kan præsentere en skrivebeskyttet flade for en opsummerende agent og en bredere for den agent, der faktisk arkiverer ting.
Hvordan kræver jeg godkendelse, før et værktøj kører?
Send require_approval til serveren. MCPServerStdio, MCPServerSse og MCPServerStreamableHttp accepterer det alle, i fire former: "always" eller "never" for alt, booleanerne True og False, der betyder det samme, et opslag per værktøj som {"conv_send_message": "always", "kb_search": "never"}, eller et grupperet objekt, der navngiver værktøjer efter politik.
Den grupperede form er den, du skal skrive, fordi den læses som et politikdokument, og en gennemgang kan tjekke den i en kodegennemgang.
async with MCPServerStreamableHttp(
name="Munin",
params={"url": "https://mcp.getmunin.com", "headers": headers},
require_approval={
"always": {"tool_names": ["conv_send_message", "crm_update_contact"]},
"never": {"tool_names": ["conv_search_messages", "kb_search"]},
},
) as server:
...Testen
Hvis nogen slettede din godkendelsespolitik i nat, hvad kunne agenten så nå at gøre ved en kunde inden morgenen?
Hvad sker der, hvis min kører bare godkender alt?
Sammenlægningerne og den udgående kontakt affyres stadig ikke. Munin deler skrivninger efter omkostning frem for efter rettighed: billige, reversible som at tagge en samtale eller sætte dens emne udføres direkte, mens dem, du ikke kan tage tilbage — at slå to kontaktposter sammen, at sende udgående kontakt — kun findes som foreslå-værktøjer, der arkiverer ind i en gennemgangskø, som et indlogget menneske rydder; skrivninger til videnbasen og samtaler er direkte, og det er, hvad require_approval er til for.
Den skelnen er tilsigtet, og den er grunden til, at en egen kører er en rimelig ting at give en produktionsorganisation. Godkendelse på klientsiden er en egenskab ved din kode: den er lige så god som din seneste udrulning, og require_approval="never" er én skødesløs commit væk. Gennemgangskøen er en egenskab ved platformen, så den overlever, at din kode tager fejl. outreach_propose_first_touch skriver en mail og arkiverer den; der findes intet værktøj i kataloget, der sender den. kb_propose_curation_candidate skriver et udkast, kun administratorer ser; at løfte det er en menneskelig handling. Den udgående kø virker ens fra enhver klient, hvilket er pointen — sikkerheden ligger ikke i klienten.
Skriv godkendelsespolitikken alligevel. To lag, der er enige, er det rigtige antal, og det på klientsiden er det, der stopper et spildt modelkald, før det sker, frem for bagefter.
Hvordan ser en rigtig ubemandet kørsel ud?
Nattekørslen på supportafdelingen, som er det job, de fleste skriver en kører til i første omgang. Fire trin, alle med rigtige værktøjsnavne, ingen af dem kræver, at et menneske er vågent:
- 01Find hvad der gik ubesvaret.
conv_list_conversationsfiltreret til åbne tråde, derefterconv_search_messagesfor at læse, hvad der faktisk blev spurgt om. Kundens formulering, ikke et resumé af den. - 02Find ud af, hvem der spørger.
crm_lookup_contactkobler afsenderen til en kontaktpost;analytics_get_contact_journeysiger, hvilke sider de læste, før de skrev ind. Begge sidder på den samme Postgres-række, så det her er en kobling, ikke en integration. - 03Tjek, om du allerede har besvaret det.
kb_searchkører vektor- og fuldtekstssøgning over videnbasen. Findes svaret, skriver svaret sig selv; findes det ikke, er det fravær det mere nyttige fund. - 04Arkivér, send ikke.
conv_set_topicogconv_set_subjectskriver direkte, fordi de er trivielt reversible. Alt udgående er styret af dinrequire_approval-politik; udgående kontakt kan kun foreslås.
Din kører læser tekst, kunder har skrevet
En supportbesked er ikke-betroet input, og den lander i modellens kontekst. Behandl en sag som en plausibel vektor for prompt-injektion frem for som data.
Modtrækket er afgrænsning, ikke årvågenhed: en tool_filter-tilladelsesliste, require_approval på alt udgående, og en nøgle afgrænset til de moduler, jobbet kræver. En Munin-adminnøgle er fuld adgang til organisationen — hold den i miljøet, aldrig i repoet.
Hvem bør ikke skrive sin egen kører?
De fleste, ærligt talt, og det er værd at være tydelig om, hvad Munin leverer og ikke leverer. Munin er en kundeplatform, ikke et agentframework: der er ingen kører, ingen prompthåndtering, ingen eval-rig, intet sporings-UI i det. Det er Agents SDK'ens område, og SDK'en er velbygget til det — sporing fanger MCP-værktøjslistning og værktøjskald automatisk, Runner.run_streamed giver dig inkrementelt output, og MCPServerManager håndterer at forbinde flere servere med drop_failed_servers- og reconnect-semantik, der allerede er gennemtænkt. Tag det maskineri fra SDK'en, og lad Munin være det, den kalder.
Det, du får ud af selv at skrive sløjfen, er de to ting, en hostet klient ikke kan give dig: en tidsplan og et sted at lægge outputtet. En cron-post, et Slack-opslag, en række i din egen tabel, en test der fejler, når kørslen intet producerer. Er dit agentarbejde samtalepræget — nogen der stiller spørgsmål og læser svar — er en klient mindre arbejde, og gennemgangene for Claude Code og Cursor dækker den opsætning fra ende til anden. Skriv en kører, når det, du har brug for, er et job frem for en samtale, og når du vil have, at sløjfen, gentagelserne og fejltilstanden er dine. Værktøjerne er identiske uanset hvad, fordi det er ét endpoint og ét skema nedenunder.
Findes der en TypeScript-version?
Ja. JavaScript- og TypeScript-udgaven af Agents SDK kommer fra @openai/agents og eksponerer de samme to former under de samme navne: MCPServerStreamableHttp, når din proces ejer forbindelsen, og hostedMcpTool, når Responses API skal eje den. Begreberne er én til én — et serverobjekt med en URL, værktøjsfiltrering, en godkendelsespolitik.
Stavemåden på optionerne afviger fra Python nogle steder, og i stedet for at gætte på dem her, så læs dem fra TypeScript-guiden til MCP, før du skriver konfigurationen. En konfigurationsnøgle opfundet i et blogindlæg koster en eftermiddag.
Ofte stillede spørgsmål
Understøtter OpenAI Agents SDK eksterne MCP-servere?
Ja. MCPServerStreamableHttp forbinder til en Streamable HTTP-server hvor som helst, din proces kan nå, hvor params tager url, headers og timeout. HostedMCPTool er alternativet, hvor OpenAI's Responses API kalder en offentligt tilgængelig server på modellens vegne.
Hvordan autentificerer jeg en MCP-server i Agents SDK?
Læg en Authorization-header i params-ordbogen: {"url": ..., "headers": {"Authorization": f"Bearer {token}"}}. Læs token fra miljøet. For Munin laver du nøglen under Indstillinger → API-nøgler og afgrænser den til de moduler, jobbet kræver.
Kan jeg begrænse, hvilke MCP-værktøjer en agent kan kalde?
Ja, med tool_filter. Brug create_static_tool_filter(allowed_tool_names=[...], blocked_tool_names=[...]) til en fast liste — tilladelseslisten anvendes først, derefter fjernes blokerede navne — eller send en callable ind, der får en ToolFilterContext til logik per kørsel.
Hvordan tilføjer jeg menneskelig godkendelse til MCP-værktøjskald?
Send require_approval til serverobjektet som "always", "never", et opslag per værktøj eller et grupperet {"always": {"tool_names": [...]}}-objekt. Til hostede værktøjer sætter du require_approval i tool_config og leverer en on_approval_request-callback til at afgøre i Python.
Virker det her med en selvdrevet Munin?
Ja, over Streamable HTTP. docker compose up serverer MCP på /mcp på backend-porten, så peg params["url"] mod http://localhost:3001/mcp. Den hostede værktøjsvej kræver en offentligt tilgængelig URL, så brug Streamable HTTP-serveren til en privat udrulning.
Skal jeg bruge OpenAI-modeller?
Til de lokale transporter — Streamable HTTP, SSE, stdio — konverteres MCP-værktøjerne til almindelige funktionsværktøjer, så det her er et helt almindeligt modelvalg i Agents SDK. HostedMCPTool er undtagelsen: SDK'en dokumenterer det som fungerende med OpenAI-modeller, der understøtter Responses API'ets hostede MCP-integration.
Hvad koster Munin at køre det her mod? Cloud Free koster 0 € om måneden med 5.000 MCP-kald, 250 kontakter og 100 MB lagring — nok til at køre en natlig kørsel, mens du beslutter dig. Egen drift er gratis for altid under MIT, uden en enterprise-mappe holdt tilbage.
Den korte version
MCPServerStreamableHttpmed enurlog enAuthorization-header giver en Agents SDK-agent hele Munin-kataloget — CRM, samtaler, videnbase, CMS, udgående kontakt, analyse — på omkring femogtyve linjer Python.- Der findes fire integrationsveje; Streamable HTTP er standarden til rigtige kundedata, fordi din proces holder legitimationen, og endpointet aldrig behøver at være offentligt.
create_static_tool_filterafgrænser agenten til de værktøjer, jobbet kræver. Sætinclude_server_in_tool_names, før du tilføjer en server nummer to, ikke bagefter.require_approvali grupperet form læses som en politik og gennemgås som en. Nedenunder lader Munin kun agenter foreslå sammenlægninger og udgående kontakt — så gennemgangskøen holder, uanset hvad din kode beslutter.- Skriv en kører, når du har brug for en tidsplan og et sted at lægge outputtet. Til samtalepræget arbejde er en eksisterende MCP-klient mindre arbejde og når de samme værktøjer.
Peg en MCPServerStreamableHttp mod din organisation og spørg den, hvilke samtaler fra sidste uge der aldrig fik svar — MCP-værktøjsreferencen har navnene, og Munin Cloud er gratis at starte på.
Skriv sløjfen. Alt, den skal kalde, er der allerede.