# lineai-mcp-server — Architecture Diagrams
Companion to [architecture.md](architecture.md); same source-verified content
(`main` @ `e539ba9`, v1.3.2, 2026-10-05) rendered as Mermaid. Committed SVG renders
live in [rendered-diagrams/](rendered-diagrams/index.md), one per section, named by
the section's two-digit prefix.
> Written for Linear ticket **LIN-737**. **Updated for LIN-699** (PR #61 in this
> repo; server side neo4cape PR #1860): diagrams 03–05 now show the typed
> `LineaiApiError` taxonomy with `isError=True`, the honest no-match routing,
> the 401/403 auth retry with `expires_in` handling, the MV-resolution cache,
> and `viewId`-scoped impact fetches.
---
## 01 — Ecosystem: where lineai-mcp-server sits
```mermaid
flowchart LR
subgraph CLIENTS["MCP clients (stdio)"]
AGENTS["Claude agent sessions
claude-code-container / lineai-agent images:
git clone → claude mcp add-json →
uvx --directory /claude/lineai-mcp-server"]
IDES["IDE assistants
VS Code · Cursor · Claude Desktop · Windsurf
uvx lineai-mcp-server@latest (PyPI)"]
end
MCP["lineai-mcp-server v1.3.2
Python · mcp.server.Server · stdio only
8 tools · in-process caches · no own graph logic"]
subgraph LINEAI["Lineai server (neo4cape) — LINEAI_SERVER_HOST"]
AUTH["/api/authenticate"]
MV["/api/materialized-view-definition/name
/api/materialized-view/latest
/api/materialized-view/default"]
SEARCH["/api/ai-retrieval/search/*
(shortname · table · column · view)"]
IMPACT["/api/dependency/impact/full/{id}/list"]
GRAPH["/api/ai-retrieval/graph/*
404 until graph tier is deployed"]
end
REG["PyPI + official MCP registry
publish-to-pypi.yml · server.json"]
AGENTS -- "JSON-RPC over stdin/stdout" --> MCP
IDES -- "JSON-RPC over stdin/stdout" --> MCP
MCP -- "Bearer-token HTTPS
(httpx, 120 s timeout)" --> AUTH
MCP --> MV
MCP --> SEARCH
MCP --> IMPACT
MCP --> GRAPH
REG -. "distribution" .-> IDES
style MCP fill:#fef7e0,stroke:#f9ab00
style GRAPH fill:#fce8e6,stroke:#ea4335
```
---
## 02 — Module map
```mermaid
flowchart TD
subgraph ENTRY["Entry points"]
PKG["__init__.py — main()
console script lineai-mcp-server"]
DBG["src/start_server.py
dev only: + debugpy :5679"]
end
SRV["server.py (88)
Server('lineai-mcp-server') · load_dotenv
stdio_server loop · instructions string"]
subgraph HANDLERS["handlers/"]
HINIT["__init__.py (249)
@list_tools · @call_tool
dispatch via asyncio.to_thread
LineaiApiError rendering + last-resort wrapper"]
HMI["method_impact.py (459)
lineai-method-impact
Markdown impact report + UR section"]
HDI["database_impact.py (168)
lineai-database-impact
combined DB report + UR section"]
HGT["graph_tools.py (188)
6 × lineai-graph-*
camelCase bodies → fenced JSON"]
HCOM["common.py (145)
LINEAI_DEBUG_MODE · logs dir
error_result (isError=True)
render_lineai_api_error"]
end
UTILS["utils.py (1388)
LineaiApiError · _authed_request (401/403 retry)
authenticate + token cache (expires_in)
resolve_mv_id (+cache) · get_method_nodes (+cache)
get_impact (+cache, viewId) · UR extraction
DB search/report · shared httpx.Client"]
GC["graph_client.py (176)
graph_request → /api/ai-retrieval/graph/*
graph error messages"]
PKG --> SRV
DBG --> SRV
SRV -- "import side effect
registers decorators" --> HINIT
HINIT --> HMI
HINIT --> HDI
HINIT --> HGT
HMI --> HCOM
HDI --> HCOM
HGT --> HCOM
HMI --> UTILS
HDI --> UTILS
HGT --> UTILS
HGT --> GC
GC -- "reuses _authed_request()
and _client" --> UTILS
style UTILS fill:#e8f0fe,stroke:#4285f4
style HINIT fill:#fef7e0,stroke:#f9ab00
```
---
## 03 — `lineai-method-impact` call sequence (richest path, with error branches)
```mermaid
sequenceDiagram
autonumber
participant A as Agent (MCP client)
participant H as handle_method_impact
participant U as utils.py
participant L as Lineai /api
A->>H: call_tool(method, class?, workspace?, materialized_view_id?)
H->>U: resolve_mv_id(arguments)
Note over U: precedence: explicit MV id → workspace arg →
LINEAI_WORKSPACE_NAME → server default
alt explicit materialized_view_id given
U-->>H: mv_id (no resolution needed)
else MV cache hit (name or server-default slot, TTL 300s)
U-->>H: cached mv_id
else cache miss
U->>L: POST /api/authenticate (if token cache miss)
L-->>U: access_token (+ expires_in, cached §5)
alt workspace name known
U->>L: GET /api/materialized-view-definition/name?name=ws
U->>L: GET /api/materialized-view/latest?definitionId=…
else nothing specified
U->>L: GET /api/materialized-view/default (single call)
end
L-->>U: mv_id → _mv_cache (LINEAI_MV_CACHE_TTL)
Note over U,L: 404 → LineaiApiError mv_not_found / mv_no_default
→ per-kind page, isError=true
end
H->>U: get_method_nodes(mv_id, method)
alt cache hit (mv_id:method, TTL 300s)
U-->>H: cached nodes, error_kind=None
else cache miss
U->>L: POST /api/ai-retrieval/search/shortname (empty body,
via _authed_request: one retry on 401/403)
alt 200 with data
L-->>U: nodes (may include URs) → cached
U-->>H: (nodes, None)
else 200 with EMPTY data
U-->>H: ([], None) — NOT cached
else 404
U-->>H: ([], "not_found") — NOT cached
else client timeout / 504 / other error
U-->>H: ([], "timeout" / "gateway_timeout" / "http_error")
else auth failure after retry
U--xH: LineaiApiError("auth") → dispatcher page, isError=true
end
end
alt no nodes — 404 OR empty 200
H-->>A: honest no-match page, isError=true
("view {mv_id} resolved, no method matched — not infrastructure")
else no nodes — timeout / 504 / http_error
H-->>A: per-kind infrastructure-error page, isError=true
("says nothing about whether the method exists")
else nodes found
H->>H: class filter — case-insensitive substring on identity
(no match → candidates page, isError=true)
H->>U: get_impact(node.properties.id, mv_id)
alt impact cache hit ({id}:{mv_id}, TTL 300s)
U-->>H: cached stripped JSON
else miss
U->>L: GET /api/dependency/impact/full/{id}/list?viewId={mv_id}
L-->>U: impact graph (may include UR nodes)
U->>U: strip_unused_properties (URs untouched) → cache
U-->>H: JSON string
end
H->>H: metrics, owners, dependents, applications,
REST endpoints, unresolved-reference section
H-->>A: Markdown impact report
end
```
---
## 04 — Authentication and the four caches
```mermaid
flowchart TD
CALL["any tool call"] --> MVQ["resolve_mv_id(arguments)
explicit id → workspace arg →
LINEAI_WORKSPACE_NAME → server default
cached: _mv_cache, LINEAI_MV_CACHE_TTL 300s"]
MVQ --> REQ["_authed_request(method, url)
the ONLY place auth headers are attached"]
REQ --> AUTHQ{"token cached and
now < expiry?"}
AUTHQ -- yes --> USE["Bearer token"]
AUTHQ -- "no (miss / TTL lapsed)" --> POST["POST /api/authenticate
grant_type=password
LINEAI_USERNAME / LINEAI_PASSWORD"]
POST -- 200 --> STORE["cache access_token
TTL = min(LINEAI_TOKEN_CACHE_TTL 3600s,
expires_in − 30s margin)"]
POST -- failure --> RAISE["LineaiApiError('auth' / 'timeout')
→ per-kind page, isError=true"]
STORE --> USE
USE --> RESP{"response status?"}
RESP -- "401 or 403 (first time)" --> INV["invalidate_token()
retry ONCE with fresh token
server answers bad tokens with 403, not 401"]
INV --> POST
RESP -- "401/403 again" --> RAISE
RESP -- "2xx / other" --> CACHES["response → caller"]
CACHES --> C0["_mv_cache
key: workspace name or '<server-default>'
TTL 300s (LINEAI_MV_CACHE_TTL)"]
CACHES --> C1["_method_nodes_cache
key: mv_id:short_name · TTL 300s
non-empty results only;
404 and empty 200 never cached"]
CACHES --> C2["_impact_cache
key: id:mv_id · TTL 300s
value: stripped JSON string"]
CACHES --> C3["graph tools
no response cache"]
HTTP["shared httpx.Client
timeout LINEAI_REQUEST_TIMEOUT 120s
connect LINEAI_CONNECT_TIMEOUT 30s
keepalive 20 / max 30 · connect retries 3"]
C1 -.-> HTTP
C2 -.-> HTTP
C3 -.-> HTTP
style STORE fill:#e6f4ea,stroke:#34a853
style INV fill:#fef7e0,stroke:#f9ab00
style MVQ fill:#e6f4ea,stroke:#34a853
style REQ fill:#e8f0fe,stroke:#4285f4
```
---
## 05 — Error-handling taxonomy (honest since LIN-699 / PR #61)
```mermaid
flowchart TD
TC["handle_call_tool(name, args)
every error path returns CallToolResult(isError=true)"] --> MI["lineai-method-impact"]
TC --> DI["lineai-database-impact"]
TC --> GT["lineai-graph-*"]
subgraph API["LineaiApiError(kind, status, detail, endpoint) — utils.py"]
KA["auth — 401/403 after one retry,
or authenticate() failure"]
KMV["mv_not_found — unknown workspace name
mv_no_default — no server default view"]
KT["timeout · gateway_timeout (504)"]
KH["http_error — other ≥400 / transport
invalid_response — malformed payload"]
end
API --> DPAGE["render_lineai_api_error:
per-kind Markdown page with endpoint,
status, detail + recommendations"]
subgraph MIK["get_method_nodes → (nodes, error_kind)"]
KOK["404 OR empty 200 →
honest no-match page:
'view {mv_id} resolved; no method matched —
not an index or infrastructure problem'"]
KINfra["timeout / gateway_timeout / http_error →
infrastructure page: 'says nothing about
whether the method exists'"]
KCLASS["class filter miss →
candidates page (up to 20 identities)"]
end
MI --> MIK
MI -. "auth raises" .-> API
DI --> DSE["search_database_entity → (results, error_kind):
honest infra pages; no-match page names the
resolved view; per-entity impact failures →
'Impact retrieval failures' table"]
DI -. "auth / get_impact raise" .-> API
subgraph GTK["graph_request → error kind"]
G404["404 → not_deployed
'use method/database impact instead'"]
G504["504 → gateway_timeout"]
GTO["timeout → timeout"]
GJSON["2xx non-JSON → invalid_json (+ excerpt)"]
GERR["other ≥400 / transport / no host → http_error"]
end
GT --> GTK
GT -. "auth raises" .-> API
GTK --> GTPAGE["graph_error_message →
Markdown page with path + status"]
MVRES["resolve_mv_id · get_impact:
raise typed LineaiApiError"] --> API
UNTYPED["argument-validation ValueErrors only"] --> WRAP["last-resort wrapper:
# Error executing tool: {name}
raw exception text, isError=true"]
style KOK fill:#e6f4ea,stroke:#34a853
style DSE fill:#e6f4ea,stroke:#34a853
style DPAGE fill:#e8f0fe,stroke:#4285f4
style WRAP fill:#fef7e0,stroke:#f9ab00
```