-
-
Qnsult Homepage
-
Qnsult Dashboard
-
The Two Axis Portfolio Chart depicting where the client stands in terms of value and relationship strength, goal is to move right and up
-
User workspace complete with a dev console to access, monitor and update dev tools for the application
-
Qnsult's frontend visualization of the agentic workflow, reflecting agent activity and audit logs
-
Qnsult's repository of strategic data used for agent enrichment as well as for company databasing
-
Snapshot showing Qnsult's in-built portfolio positioning AI chatbot that triggers async responses from the native agent system.
Inspiration
Consulting relationships don't fail suddenly, they stall slowly. An executive goes quiet for three weeks. A milestone slips. A competitor's proposal lands in someone's inbox. Each signal is small on its own, but by the time anyone recognises the pattern, the relationship has already cooled and the renewal is at risk.
We kept watching firms lose accounts they could have saved if they'd acted four weeks earlier. The data was always there - in email threads, calendar gaps, billing deltas, meeting notes - but scattered across tools that track what already happened, not what's about to happen.
The second trigger was the AI disruption hitting consulting itself. Firms doing execution work - delivery, documentation, code review - are increasingly exposed. The ones that survive are the ones embedded in their clients' strategy cycles, not just their task queues. We wanted to build a system that could measure that axis, score it, and tell you exactly which accounts are drifting toward commoditisation before it's too late.
What it does
Qnsult is a 12-agent autonomous intelligence system that monitors every client relationship in a consulting portfolio - reading Gmail, Google Calendar, and meeting artefacts in real time - and surfaces who is at risk, what to do next, and when to act.
Every client is scored on two axes:
- Y-axis - Value Chain Position (1–10): Are you doing execution work (AI-replaceable, low-margin) or embedded strategic advisory (AI-resistant, high retention)?
- X-axis - Relationship Health (1–10): Transactional contact, engaged cadence, or multi-stakeholder embedded partnership?
The Momentum Score driving the dashboard is:
$$ \text{score} = r \cdot 0.30 + g \cdot 0.25 + (10 - s) \cdot 0.20 + \frac{c+1}{2} \cdot 1.5 + \frac{p}{10} \cdot 1.0 $$
where \(r\) = relationship score, \(g\) = goal alignment, \(s\) = stall score, \(c \in {-1,0,1}\ ) = cadence trend, \(p\) = value chain position. Clamped to \([1, 10]\). Bands: \(\geq 8.0\) Accelerating · \(\geq 6.0\) On Track · \(\geq 4.0\) Progressing · \(\geq 2.0\) At Risk · otherwise Stalling.
The system:
- Detects stalls 4–6 weeks early using a weighted signal model across exec silence, milestone slippage, cadence decline, and meeting cancellations
- Maps AI displacement exposure per deliverable, the "AI Danger Zone" shows which work is at risk of being automated away and what adjacent strategic work to pitch instead
- Detects competitive threats - scans inbox for RFP language, talent poaching signals, and shadow proposals from named competitors
- Auto-drafts outreach emails for stall recovery, expansion pitches, and renewal preparation, written and queued for approval, not sent automatically
- Answers portfolio questions conversationally via the Portfolio Positioning AI, a sub-agent that reads live Supabase data and responds with scores, deltas, and recommended plays
- Builds a Pattern Library, records what worked for similar accounts and surfaces those playbooks (Executive Bridge, Stakeholder Anchor, Scope Lock) when conditions match.
How we built it
Agent architecture (Google ADK 2.2 + Gemini 2.5 Flash)
We built 12 specialist agents arranged in four layers: ingestion, analysis, synthesis, and action. The root orchestrator (momentum_agent) delegates to each as an AgentTool which returns control back to the orchestrator after each completes, unlike sub_agents which transfer control permanently. This distinction let us build a proper sequential pipeline with one agent that always stays in charge of the overall run.
MCP for MongoDB
All agent reads and writes go through the mongodb-mcp-server MCP binary. We built a thin _call() utility that spawns a fresh subprocess per operation and captures the result inside the async with block before teardown. On Cloud Run the binary is globally installed, giving ~1–1.5s per call. Locally it falls back to npx.
Data bridging (MongoDB to Supabase)
Agents write structured data to MongoDB Atlas. A bridge layer in mongo_tools.py automatically mirrors specific collections to Supabase on every write: dashboard_queue to action_items (for the Priority Queue panel) and outreach_drafts to gmail_threads (for the Mails tab). This keeps agents completely decoupled from Supabase while the frontend sees live data via Realtime subscriptions.
Frontend (Next.js 15 + Supabase Realtime)
The dashboard subscribes to postgres_changes INSERT events on dashboard_queue. As agents complete each client, notifications push directly to the browser — no polling, no webhooks. The run-analysis route flushes stale agent output before each run so the dashboard always reflects the current pipeline pass, not accumulated history.
Deployment (Google Cloud Run)
Two services: qnsult (Next.js) and qnsult-adk (ADK backend). The ADK Dockerfile installs Node.js alongside Python 3.12 so the MCP binary is available at runtime. Service-to-service calls use public Cloud Run URLs with allUsers invoker access on the ADK service.
Challenges we ran into
anyio cancel scope incompatibility on Python 3.13
Our first approach was a persistent MCP session kept open across all agent calls via AsyncExitStack. It failed with RuntimeError: Attempted to exit cancel scope in a different task. We then tried a background asyncio.create_task() session loop — sess.initialize() hung indefinitely. The root cause: anyio 4.13 cancel scopes cannot be held open or transferred across task boundaries on Python 3.13. We abandoned persistent sessions entirely and moved to per-call subprocess spawning.
Wrong MCP tool names
mongodb-mcp-server@1.12.0 exposes tools named find, insert-many, and update-many; not insertMany, updateOne, or any camelCase variant. Silent failures for hours until we read the npm package source directly. In MCP, the tool name is the contract.
Result captured outside the async with block
When we extracted result.content after the async with stdio_client() block exited, anyio's subprocess teardown raised a BrokenResourceError that propagated before we could read anything. The fix was capturing the result inside the block and letting teardown fail quietly when a result had already been captured.
Supabase schema discovery
We were inserting into gmail_threads with columns that don't exist (is_draft, labels). The Supabase client silently ignores unknown columns instead of erroring, making this invisible until we probed information_schema.columns directly. We switched to using summary = 'DRAFT' as a soft tag on the existing schema rather than adding columns mid-hackathon.
Cloud Run service-to-service authentication
The ADK service launched with no IAM bindings, so all fetch() calls from the Next.js service returned 403s. The frontend was silently falling back to http://localhost:8000 which doesn't exist inside a container, producing the "ADK server unreachable" error. Fixed by granting allUsers run.invoker on the ADK service and setting ADK_SERVER_URL as an env var on the frontend service.
Accomplishments that we're proud of
A pipeline that actually finishes. The 12-agent orchestration runs end-to-end - ingestion, analysis, synthesis, action, scoring - and writes coherent, joined-up output to two separate databases without losing state between agents. Getting that to work reliably through the MCP constraint was the hardest engineering problem on this project.
The two-axis model. A single health score collapses information that is actually decision-relevant. An account can be high-relationship but execution-trapped (vulnerable to AI displacement), or low relationship but strategically positioned (recoverable with the right play). Separating those axes into a live scatter map gives consultants genuinely different information than any existing CRM.
Real-time agent output in the browser. The Supabase Realtime subscription means the dashboard updates as each client analysis completes - no refresh, no polling. The pattern of agents writing to Postgres and the frontend subscribing to row-level events turned out to be one of the cleanest parts of the architecture.
What we learned
AgentTool vs sub_agents is an architectural choice, not a detail.: AgentTool returns control to the parent after the child completes. sub_agents transfers control permanently. That single distinction determines whether you have a pipeline orchestrator or a chat router. We used both deliberately: all specialist agents as AgentTool, the conversational agent as a sub_agent.
MCP is a process boundary, not a library.: Every call is a subprocess spawn with a startup cost. The right mental model is to treat the MCP server as infrastructure — install it once at image build time, call it many times — rather than as an on-demand dependency resolved at runtime.
LLM agents need strict output contracts.: When Gemini writes to MongoDB, it will invent field names, use inconsistent types, and sometimes write Python code instead of a tool call if the prompt is ambiguous. Every agent instruction in this project starts with IMPORTANT — Never write Python code. Make direct tool calls only. That single line eliminated an entire class of failures.
The value of the AI danger zone framing.: Measuring AI displacement exposure per deliverable, asking which of this client's work is most automatable? - turned out to be one of the most interesting outputs the system produces. It reframes the AI threat from "will AI replace consultants?" to "which of your current engagements are at risk, and what do you move into instead?"
What's next for Qnsult
Multi-user, multi-firm support: The architecture is single-user today (one set of Google OAuth tokens, one MongoDB database). The next milestone is per-user credential isolation, so a whole consulting firm can onboard, each consultant seeing only their own portfolio.
Richer competitive intelligence: The current competitive signal agent reads email only. Expanding it to scan LinkedIn activity, news mentions, and job postings for named competitors would significantly increase signal quality for the threat matrix.
Calibrated scoring per industry: The composite score formula uses fixed weights. With enough data from the Pattern Library, those weights could be calibrated per industry vertical — the relationship-to-value-chain ratio that predicts retention in private equity is different from technology services.
Built With
- agent
- cloud
- mongodb
- nextjs
- supabase
- typescript
Log in or sign up for Devpost to join the conversation.