Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion docs/features/langflow-assistant-mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@
> (9) **`ModelInputComponent` defensive `recoverModelOption`** — repairs doubly-encoded model values produced by the assistant's `flow_update` pipeline so the Agent node's Language Model dropdown trigger never renders literal JSON.
> (10) **Empty-state `ModelProviderModal` inline open** — the assistant's "Configure providers" CTA now opens the modal in-panel instead of navigating away.
> See ADR-023 through ADR-030 in `langflow-assistant.md` for full per-decision context; the MCP-specific impact is reflected in the glossary and behavior sections below.
>
> **2026-07-08 revision** — **Loop + conditional flow building lands on the MCP tool surface** (full breakdown in `langflow-assistant.md`'s 2026-07-08 revision):
> (1) **`add_connection` can now express the Loop feedback edge** — when the target port is an `allows_loop=True` OUTPUT (e.g. `Loop.item`), `lfx/graph/flow_builder/connect.py` emits the output-shaped targetHandle `{dataType, id, name, output_types}` the runtime's loop branch requires, with accepted types mirroring the canvas (`[selected ?? types[0], ...loop_types]`). Template inputs keep priority on name collisions; structural idempotency, `remove_connection`, and `list_connections` all recognize loop edges. Every builder tool inherits this: `connect_components`, `create_flow_from_spec`, `build_flow`, `update_flow_from_spec`.
> (2) **`describe_component` surfaces loop inputs** — `allows_loop` outputs are flagged on the output entry AND listed as inputs with `type: "loop"` and a description, so the agent can discover loop wiring instead of failing on "Input 'item' not found" (that error now also names the available loop inputs).
> (3) **"Loops and branching" prompt section** in `FLOW_BUILDER_PROMPT` — canonical one-shot loop spec (build in ONE `build_flow` call; do not re-discover the wiring) plus If-Else fan-out guidance (`ConditionalRouter.true_result`/`false_result`, `DataConditionalRouter` for Data). E2E-validated on a live instance: the assistant one-shots both a cyclic Loop flow (with a real loop-feedback edge) and a two-branch If-Else flow within the pinned 15-iteration budget.
> (4) **The lfx MCP toolkit over HTTP (consolidated, supersedes the two-server framing in ADR-MCP-007 for external access)** — `POST/GET/DELETE /api/v1/agentic/mcp` (streamable-http, stateless; `api/v1/agentic_mcp.py`) mounts the **single** Langflow MCP toolkit, `lfx.mcp.server`: the same 30+ authoring/execution tools the local `lfx-mcp` stdio bridge ships, plus `run_assistant`. Auth is the same `CurrentActiveMCPUser` dependency as the flows MCP endpoints, gated by `LANGFLOW_AGENTIC_EXPERIENCE` (404 when off). Every tool call goes through the REST API in loopback bound to the caller's own request headers, so authorization is enforced by the API on every operation and no tool touches the database directly — the `user_id`-injection machinery is gone (identity is the credential). The `login` tool is excluded over HTTP (credential-forwarding surface). The `langflow-agentic` stdio server is deprecated as an external surface, and its per-user auto-configuration (preload step `AGENTIC_MCP` + the `main.py` fallback) was **removed** — external clients connect to the HTTP mount with their own API key (`curl -X POST <server>/api/v1/agentic/mcp -H "x-api-key: <key>"`, or any streamable-http MCP client), and local-spawn clients keep using the `lfx-mcp` stdio bridge. Previously auto-registered `langflow-agentic` entries keep working (the stdio module remains for back-compat only).
> (5) **`run_assistant` in the lfx toolkit (auto-apply)** — `lfx.mcp.server.run_assistant` consumes the Assistant through the **headless** route `POST /api/v1/agentic/assist/run` with the caller's credentials (no `user_id` argument). The headless route exists because `/assist/stream` (the UI path) leaves a canvas change as a *proposal* for an approval card — an MCP caller has no card, so its edits would be silently dropped. `/assist/run` runs `run_assistant_and_persist` (`apply_edits_immediately`), so the canvas is **written**, and streams the same events, ending in `complete`. It forwards each assistant `progress` SSE event as an MCP progress notification (`send_progress_notification` with `related_request_id`, so the stateless streamable-http transport delivers it) plus a `ctx.info` log line. Forwarding failures are swallowed — progress can never break the tool. Verified live end-to-end: external MCP client → HTTP mount → assistant built a flow, with progress notifications streamed back on the same request.
> (6) **`use_template` / `list_templates` in the in-product toolkit** (`lfx/mcp/flow_builder_tools/template_tools.py`) — one-call starter-template instantiation through the SAME `set_flow` proposal gate as `build_flow` (Add/Replace card; canvas untouched without approval), emitting `tool_start`. `list_templates` returns only name + one-line description; `use_template` returns a compact summary (name, node/edge counts, node types) — the template's flow JSON never enters the LLM context (cost guard, test-enforced). Bare-lfx environments degrade gracefully ("Starter templates are not available"). Prompt: use ONLY on a clear template match. Also: `classify_intent` gains a whitespace-only/empty-input deterministic fast path (no LLM call; language-independent).
>
> **2026-07-09 revision** — **Model portability and streaming/tracing robustness across the MCP execution surface** (full breakdown in `langflow-assistant.md`'s 2026-07-09 revision). These land in the shared `lfx` model/streaming layer, so every MCP path that runs a flow (`run_flow`, `run_assistant`, and any `build_flow` + run turn) inherits them:
> (1) **Provider-agnostic model remediation** — a reactive, per-model cached layer (`lfx/base/models/model_remediation.py`) maps an error signature + provider to instantiation overrides. First entry: the OpenAI `/v1/responses` error marker → `{"use_responses_api": True}`, so gpt-5.6 (which rejects `tools` + `reasoning_effort` on chat/completions) auto-switches to the Responses API on the retry and is remembered for the session. `get_llm` pre-applies `cached_overrides(provider, model)`; the embedded `Agent` retries once with `_model_overrides` and `remember`s the fix. The assistant/MCP path bridges the same fix in `assistant_service.py`'s `FlowExecutionError` handler (before the model-swap fallback), so the shared `get_llm` cache carries the override into the Agent embedded in the assistant flow. Adding Anthropic/Ollama constraints is a new `Remediation` row, not code.
> (2) **Legacy / untagged content-block resilience** — `validate_content_blocks._parse_item` (`lfx/schema/message.py`) parses an untagged `{"text": ...}` block as `TextContent` before the discriminated-union adapter, and `BaseContent.serialize_model` (`content_types.py`) re-adds the discriminator through `exclude_unset`. Prevents a run/`run_assistant` turn from crashing while deserializing stored message history.
> (3) **Responses-API `[]` flash suppressed** — `event_consumer.py` drops a streamed token whose chunk strips to `"[]"` (the Responses API's empty-content-block-list starting state), so it never surfaces before the real answer on any streamed MCP/assistant turn.
> (4) **Langfuse callback is deep-copy safe** — with Langfuse configured, an Agent build (any `run_flow` / `run_assistant` / `build_flow`+run turn) crashed with `LangfuseResourceManager.__new__() missing 3 required keyword-only arguments`. Root cause: langflow deep-copies flow/graph state around the Agent build, and the langfuse LangChain `CallbackHandler` attached via `get_langchain_callbacks()` wraps a keyword-only-singleton client that `copy.deepcopy` (which calls `cls.__new__(cls)` with no args) cannot clone. Fix: langflow's `_RootRunReparentingCallbackHandler` (`services/tracing/langfuse.py`) implements `__deepcopy__` / `__copy__` returning `self`, so the shared observer is carried by reference. `TracingService.get_langchain_callbacks` additionally try/excepts callback creation as a belt-and-suspenders. Verified live on gpt-5.6.
> (5) **Bigger, tunable assistant iteration budget** — a compound build+run+report turn hit `Recursion limit of 35 reached`. The 35 is derived (`agent.py::_compute_recursion_limit` = `max_iterations * 2 + 5`) from the assistant Agent's pinned `max_iterations`. That pin is raised 15 → **30** in the embedded assistant flows (recursion_limit 65), and a `/iterations N` composer command overrides it per session (additive `AssistantRequest.iterations_limit`, `ge=1 le=200`, → `ITERATIONS_LIMIT` global var → `inject_iterations_into_flow` stamps the Agent's `max_iterations`, clamped `[1,200]`). Every MCP execution path that runs the assistant Agent (`run_assistant`, `build_flow`+run) inherits the larger budget. Verified live on gpt-5.6.

---

Expand Down Expand Up @@ -1337,7 +1352,7 @@ LLMs can produce flow specs where a component is declared but never wired. The r

### ADR-MCP-007: Two MCP Servers — `lfx/mcp` (canvas-aware) and `agentic/mcp` (DB-aware)

**Status**: Accepted
**Status**: Accepted — amended 2026-07-13: for EXTERNAL access the two-server split is superseded; the HTTP mount (`/api/v1/agentic/mcp`) serves only `lfx/mcp` (with `run_assistant` consuming the Assistant via REST), and `agentic/mcp` is deprecated to the internal per-user auto-configured entry. One toolkit, API-mediated, dual transport (stdio bridge + server HTTP).

#### Context

Expand Down
Loading
Loading