# 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 ```