Institutional research is assembly work: pull the filings, the market data and the news, reconcile what disagrees, then write the note. At Munshot I built the agent that does the assembly: ten-plus sources behind one tool protocol, a fifteen-node graph deciding what to call and when, and an API that streams the work back while the run is still going.
- 15Decision graph nodes
- 10+Tools behind one protocol
The problem
Ten data sources are ten failure modes. One research question touches a vector index, a web search, a general-purpose model and five proprietary financial data APIs, each with its own schema, its own limits and its own idea of a valid response. Chained naively, the whole run stops at the first payload that will not parse.
A long agent run looks exactly like a broken one if the backend answers only at the end. The service also has to hold many of those runs open at once without one blocking call taking a worker down.
The system I built
One tool protocol, one state graph, one streaming surface.
What it is made of
- An MCP tool server putting more than ten tools behind one contract: a vector store, a general model, a search API and five proprietary financial data sources. The agent never sees a vendor client library.
- A LangGraph reasoning engine: fifteen nodes carrying more than twenty state keys, sequencing tool calls, holding memory across steps and deciding when the question is answered.
- Recovery inside the graph: retry paths for transient tool failures and fallback parsing when a provider returns something the schema rejects, so a bad payload degrades one branch instead of ending the run.
- A FastAPI backend on non-blocking asyncio with JWT-secured routes and streaming JSON, so the client renders agent progress as it happens.
The agent was my surface across the internship: the tool server, the reasoning engine on top of it, the service that exposes both, and the validation layer holding them together. The hardest part was the boundary work. Pydantic models with custom validators sit on every API boundary and every agent state transition, so a provider's schema drift surfaces as a typed error at the edge instead of an unexplained failure deep in the graph.
Key decisions
One protocol in front of ten tools. Every capability sits behind a custom MCP tool server, so the graph calls one uniform interface. Calling each provider's client from the graph nodes is quicker for the first two tools and worse for every one after. Adding or swapping a data source became a tool-server change instead of a graph change, at the cost of one validated hop per call.
Recovery belongs inside the graph, not around it. An outer retry around the whole run throws away work already paid for and re-spends every tool call that had succeeded. Pushing failure handling into the graph lets a run continue on a degraded branch and keep everything it already knows.
The outcome
By the end of the internship the agent was serving production endpoints under concurrent load, with failures absorbed inside the graph instead of ending the run.
