# Updation_MCP — Project Memory (MCP + Context-Aware Agent)
## 0) Goal (What we’re building)
We are building a “screen-aware” agent (Rufus-style) for Updation that:
- Answers questions based on **what the user is currently viewing**
- Switches context automatically when the user navigates (table → detail → PDF viewer)
- Prevents hallucinations by forcing all answers to come from:
- tools
- validated page context
- permissioned data
We are NOT training our own LLM. We will use GPT/Claude as the reasoning engine,
and MCP tools + context router as the “truth” layer.
---
## 1) Current stack
- Frontend: React (react-router-dom v6) + Redux state for filters/tabs/lists
- Backend app: Laravel (auth, core business APIs)
- MCP layer: Python (FastAPI web_chat + FastMCP server/tools)
- DB: Postgres (stores AI extracted content in a column; no embeddings yet, but planned)
---
## 2) The key missing capability (before improvements)
Originally the system enforced RBAC only.
That meant:
- model could try to answer without the right screen context
- tools could be available even on the wrong page (or missing page linkage)
- no deterministic “page-aware” tool selection
We decided to add a “Context Router” system like Rufus:
- tools become available only if both:
(A) RBAC allows
(B) PageContext policy allows
This reduces hallucinations and makes the assistant behave like it “sees the page”.
---
## 3) The new architecture (Rufus-style)
### 3.1 PageContext: UI tells the truth
UI sends structured page context with every chat turn:
- pageType: which page user is on (invoice_table, invoice_detail, vendor_table, etc.)
- entity: { type, id } for detail pages only
- pageState: visible IDs, filters, tab, counts (only what UI knows)
NO DOM scraping. NO model guessing.
### 3.2 Dual-gate tool enforcement
Gate A: Tool Visibility
- The orchestrator returns only tools allowed by:
- RBAC
- page policy
Gate B: Tool Execution
- Even if LLM tries to call a tool, runtime checks enforce:
- policy matches current context
- entity binding rules
Fail-closed design:
- missing policy => tool hidden + cannot be called
---
## 4) Tool policy system (the “truth contract”)
Each tool has metadata:
ToolContextPolicy:
- allowed_page_types: set[PageType]
- requires_entity: bool
- entity_type: optional EntityType
- read_only: bool
- entity_param: (optional) which argument gets overridden with context entity_id
Core rules:
- Tools MUST have a policy (missing policy => tool does not exist)
- Table pages cannot have entity_id/entity_type
- Detail pages require matching entity_type/entity_id
- Entity ID comes from PageContext, not user args (prevents spoofing)
---
## 5) The integration gap we discovered (critical)
We built context-aware components, but they were NOT integrated into the request flow.
Symptoms:
- tools discovered: ✅
- role detected: ✅ ORG_ADMIN
- allowed_tools = 0 ❌
- logs showed: tool_missing_policy for every tool
Root cause:
- tools were being discovered dynamically
- policies were defined statically
- but NOT attached at registration time
- filter stage removed all tools as “missing policy”
Fix direction:
- register tools ONLY through ContextAwareToolRegistry that requires policy
- attach policy during tool registration, not via lookup later
---
## 6) The bug we saw (and why it happened)
Payload:
{
"message": "what vendors do i have",
"pageContext": { "pageType": "vendor_table", "entity": null }
}
Logs:
- tools discovered: 8
- tool_missing_policy for each tool
- allowed_tools = 0
Explanation:
- policy check ran before RBAC mattered
- since every tool had no policy attached, all were dropped
- LLM responded with “I will access…” but did not call tools
Required fix:
- attach ToolContextPolicy to every tool at registration
- ensure “system tools” also have policies (e.g., PageType.ANY)
---
## 7) What “perfect” looks like (desired behavior)
On vendor_table page:
- “what vendors do I have?” -> tool `list_vendors` is available and called
On invoice_detail page:
- “summarize this invoice” -> tool fetches invoice + extracted text (and/or PDF text)
- model summarises ONLY from returned tool data
If user navigates:
- next chat turn sends new pageType/entity and tools update immediately
---
## 8) What we still need to implement (next steps)
1) Fully integrate ValidatedPageContext into web_chat request pipeline
2) Ensure context is stored per-request and cleared (thread-safe)
3) Enforce policies at tool execution (hard failures)
4) Ensure tool registration uses ContextAwareToolRegistry
5) Add SYSTEM_POLICIES for echo/get_user_profile/etc.
6) Add integration tests:
- tool missing policy => hidden
- wrong page => blocked
- entity spoof => overridden by context
7) Add “LLM prompt contract”:
- model must use tools when available
- refuse if insufficient data
- never invent IDs or facts
---
## 9) UI contract summary (what UI must always send)
- pageType (enum)
- optional entity {type,id} for detail pages
- optional pageState { visibleIds, filters, tab, counts }
- the page context is part of every /chat request
---
## 10) Notes
- We will add embeddings later for semantic search across PDF text chunks.
- For now, deterministic “page” tasks should use relational data + extracted text column.