/langgraph-fundamentals
INVOKE THIS SKILL when writing ANY LangGraph code. Covers StateGraph, state schemas, nodes, edges, Command, Send, invoke, streaming, and error handling.
$ npx -y skills add langchain-ai/langchain-skills --skill langgraph-fundamentals --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/langgraph-fundamentals
Context preview
The summary Claude sees to decide when to auto-load this skill.
INVOKE THIS SKILL when writing ANY LangGraph code. Covers StateGraph, state schemas, nodes, edges, Command, Send, invoke, streaming, and error handling.
SKILL.md
langgraph-fundamentals.SKILL.mdname: langgraph-fundamentals
description: "INVOKE THIS SKILL when writing ANY LangGraph code. Covers StateGraph, state schemas, nodes, edges, Command, Send, invoke, streaming, and error handling."
<overview> LangGraph models agent workflows as **directed graphs**:
- **StateGraph**: Main class for building stateful graphs
- **Nodes**: Functions that perform work and update state
- **Edges**: Define execution order (static or conditional)
- **START/END**: Special nodes marking entry and exit points
- **State with Reducers**: Control how state updates are merged
Graphs must be `compile()`d before execution. </overview>
<design-methodology>
Designing a LangGraph application
Follow these 5 steps when building a new graph:
1. **Map out discrete steps** — sketch a flowchart of your workflow. Each step becomes a node. 2. **Identify what each step does** — categorize nodes: LLM step, data step, action step, or user input step. For each, determine static context (prompt), dynamic context (from state), retry strategy, and desired outcome. 3. **Design your state** — state is shared memory for all nodes. Store raw data, format prompts on-demand inside nodes. 4. **Build your nodes** — implement each step as a function that takes state and returns partial updates. 5. **Wire it together** — connect nodes with edges, add conditional routing, compile with a checkpointer if needed.
</design-methodology>
<when-to-use-langgraph>
| Use LangGraph When | Use Alternatives When | |-------------------|----------------------| | Need fine-grained control over agent orchestration | Quick prototyping → LangChain agents | | Building complex workflows with branching/loops | Simple stateless workflows → LangChain direct | | Require human-in-the-loop, persistence | Batteries-included features → Deep Agents |
</when-to-use-langgraph>
---
State Management
<state-update-strategies>
| Need | Solution | Example | |------|----------|---------| | Overwrite value | No reducer (default) | Simple fields like counters | | Append to list | Reducer (operator.add / concat) | Message history, logs | | Custom logic | Custom reducer function | Complex merging |
</state-update-strategies>
<ex-state-with-reducer> <python> Define state schema with reducers for accumulating lists and summing integers.
from typing_extensions import TypedDict, Annotated
import operator
class State(TypedDict):
name: str # Default: overwrites on update
messages: Annotated[list, operator.add] # Appends to list
total: Annotated[int, operator.add] # Sums integers</python> <typescript> Use StateSchema with ReducedValue for accumulating arrays.
import { StateSchema, ReducedValue, MessagesValue } from "@langchain/langgraph";
import { z } from "zod";
const State = new StateSchema({
name: z.string(), // Default: overwrites
messages: MessagesValue, // Built-in for messages
items: new ReducedValue(
z.array(z.string()).default(() => []),
{ reducer: (current, update) => current.concat(update) }
),
});</typescript> </ex-state-with-reducer>
<fix-forgot-reducer-for-list> <python> Without a reducer, returning a list overwrites previous values.
# WRONG: List will be OVERWRITTEN
class State(TypedDict):
messages: list # No reducer!
# Node 1 returns: {"messages": ["A"]}
# Node 2 returns: {"messages": ["B"]}
# Final: {"messages": ["B"]} # "A" is LOST!
# CORRECT: Use Annotated with operator.add
from typing import Annotated
import operator
class State(TypedDict):
messages: Annotated[list, operator.add]
# Final: {"messages": ["A", "B"]}</python> <typescript> Without ReducedValue, arrays are overwritten not appended.
// WRONG: Array will be overwritten
const State = new StateSchema({
items: z.array(z.string()), // No reducer!
});
// Node 1: { items: ["A"] }, Node 2: { items: ["B"] }
// Final: { items: ["B"] } // A is lost!
// CORRECT: Use ReducedValue
const State = new StateSchema({
items: new ReducedValue(
z.array(z.string()).default(() => []),
{ reducer: (current, update) => current.concat(update) }
),
});
// Final: { items: ["A", "B"] }</typescript> </fix-forgot-reducer-for-list>
<fix-state-must-return-dict> <python> Nodes must return partial updates, not mutate and return full state.
# WRONG: Returning entire state object
def my_node(state: State) -> State:
state["field"] = "updated"
return state # Don't mutate and return!
# CORRECT: Return dict with only the updates
def my_node(state: State) -> dict:
return {"field": "updated"}</python> <typescript> Return partial updates only, not the full state object.
// WRONG: Returning entire state
const myNode = async (state: typeof State.State) => {
state.field = "updated";
return state; // Don't do this!
};
// CORRECT: Return partial updates
const myNode = async (state: typeof State.State) => {
return { field: "updated" };
};</typescript> </fix-state-must-return-dict>
---
Nodes
<node-function-signatures>
Node functions accept these arguments:
<python>
| Signature | When to Use | |-----------|-------------| | `def node(state: State)` | Simple nodes that only need state | | `def node(state: State, config: RunnableConfig)` | Need thread_id, tags, or configurable values | | `def node(state: State, runtime: Runtime[Context])` | Need runtime context, store, or stream_writer |
from langchain_core.runnables import RunnableConfig
from langgraph.runtime import Runtime
def plain_node(state: State):
return {"results": "done"}
def node_with_config(state: State, config: RunnableConfig):
thread_id = config["configurable"]["thread_id"]
return {"results": f"Thread: {thread_id}"}
def node_with_runtime(state: State, runtime: Runtime[Context]):
user_id = runtime.context.user_id
return {"results": f"User: {user_id}"}</python> <typescript>
| Signature | When to Use | |-----------|----------
Read more
name: langgraph-fundamentals description: "INVOKE THIS SKILL when writing ANY LangGraph code. Covers StateGraph, state schemas, nodes, edges, Command, Send, invoke, streaming, and error handling."
<overview> LangGraph models agent workflows as **directed graphs**:
- **StateGraph**: Main class for building stateful graphs
- **Nodes**: Functions that perform work and update state
- **Edges**: Define execution order (static or conditional)
- **START/END**: Special nodes marking entry and exit points
- **State with Reducers**: Control how state updates are merged
Graphs must be `compile()`d before execution. </overview>
<design-methodology>
Designing a LangGraph application
Follow these 5 steps when building a new graph:
1. **Map out discrete steps** — sketch a flowchart of your workflow. Each step becomes a node. 2. **Identify what each step does** — categorize nodes: LLM step, data step, action step, or user input step. For each, determine static context (prompt), dynamic context (from state), retry strategy, and desired outcome. 3. **Design your state** — state is shared memory for all nodes. Store raw data, format prompts on-demand inside nodes. 4. **Build your nodes** — implement each step as a function that takes state and returns partial updates. 5. **Wire it together** — connect nodes with edges, add conditional routing, compile with a checkpointer if needed.
</design-methodology>
<when-to-use-langgraph>
| Use LangGraph When | Use Alternatives When | |-------------------|----------------------| | Need fine-grained control over agent orchestration | Quick prototyping → LangChain agents | | Building complex workflows with branching/loops | Simple stateless workflows → LangChain direct | | Require human-in-the-loop, persistence | Batteries-included features → Deep Agents |
</when-to-use-langgraph>
---
State Management
<state-update-strategies>
| Need | Solution | Example | |------|----------|---------| | Overwrite value | No reducer (default) | Simple fields like counters | | Append to list | Reducer (operator.add / concat) | Message history, logs | | Custom logic | Custom reducer function | Complex merging |
</state-update-strategies>
<ex-state-with-reducer> <python> Define state schema with reducers for accumulating lists and summing integers.
from typing_extensions import TypedDict, Annotated
import operator
class State(TypedDict):
name: str # Default: overwrites on update
messages: Annotated[list, operator.add] # Appends to list
total: Annotated[int, operator.add] # Sums integers</python> <typescript> Use StateSchema with ReducedValue for accumulating arrays.
import { StateSchema, ReducedValue, MessagesValue } from "@langchain/langgraph";
import { z } from "zod";
const State = new StateSchema({
name: z.string(), // Default: overwrites
messages: MessagesValue, // Built-in for messages
items: new ReducedValue(
z.array(z.string()).default(() => []),
{ reducer: (current, update) => current.concat(update) }
),
});</typescript> </ex-state-with-reducer>
<fix-forgot-reducer-for-list> <python> Without a reducer, returning a list overwrites previous values.
# WRONG: List will be OVERWRITTEN
class State(TypedDict):
messages: list # No reducer!
# Node 1 returns: {"messages": ["A"]}
# Node 2 returns: {"messages": ["B"]}
# Final: {"messages": ["B"]} # "A" is LOST!
# CORRECT: Use Annotated with operator.add
from typing import Annotated
import operator
class State(TypedDict):
messages: Annotated[list, operator.add]
# Final: {"messages": ["A", "B"]}</python> <typescript> Without ReducedValue, arrays are overwritten not appended.
// WRONG: Array will be overwritten
const State = new StateSchema({
items: z.array(z.string()), // No reducer!
});
// Node 1: { items: ["A"] }, Node 2: { items: ["B"] }
// Final: { items: ["B"] } // A is lost!
// CORRECT: Use ReducedValue
const State = new StateSchema({
items: new ReducedValue(
z.array(z.string()).default(() => []),
{ reducer: (current, update) => current.concat(update) }
),
});
// Final: { items: ["A", "B"] }</typescript> </fix-forgot-reducer-for-list>
<fix-state-must-return-dict> <python> Nodes must return partial updates, not mutate and return full state.
# WRONG: Returning entire state object
def my_node(state: State) -> State:
state["field"] = "updated"
return state # Don't mutate and return!
# CORRECT: Return dict with only the updates
def my_node(state: State) -> dict:
return {"field": "updated"}</python> <typescript> Return partial updates only, not the full state object.
// WRONG: Returning entire state
const myNode = async (state: typeof State.State) => {
state.field = "updated";
return state; // Don't do this!
};
// CORRECT: Return partial updates
const myNode = async (state: typeof State.State) => {
return { field: "updated" };
};</typescript> </fix-state-must-return-dict>
---
Nodes
<node-function-signatures>
Node functions accept these arguments:
<python>
| Signature | When to Use | |-----------|-------------| | `def node(state: State)` | Simple nodes that only need state | | `def node(state: State, config: RunnableConfig)` | Need thread_id, tags, or configurable values | | `def node(state: State, runtime: Runtime[Context])` | Need runtime context, store, or stream_writer |
from langchain_core.runnables import RunnableConfig
from langgraph.runtime import Runtime
def plain_node(state: State):
return {"results": "done"}
def node_with_config(state: State, config: RunnableConfig):
thread_id = config["configurable"]["thread_id"]
return {"results": f"Thread: {thread_id}"}
def node_with_runtime(state: State, runtime: Runtime[Context]):
user_id = runtime.context.user_id
return {"results": f"User: {user_id}"}</python> <typescript>
| Signature | When to Use | |-----------|----------
⚠️ — This project is in early development. APIs and skill content may change. Agent skills for building agents with LangChain, LangGraph, and Deep Agents. For LangSmith-specific trace and dataset workflows, use langsmith-skills.
Repo: langchain-ai/langchain-skills
Other skills on langchain-skills.
- /deep-agents-core
INVOKE THIS SKILL when building ANY Deep Agents application. Covers create_deep_agent(), harness architecture, SKILL.md format, and configuration options.
Open skill - /deep-agents-memory
INVOKE THIS SKILL when your Deep Agent needs memory, persistence, or filesystem access. Covers StateBackend (ephemeral), StoreBackend (persistent), FilesystemMiddleware, and CompositeBackend for routing.
Open skill - /deep-agents-orchestration
INVOKE THIS SKILL when using subagents, task planning, or human approval in Deep Agents. Covers SubAgentMiddleware, TodoList for planning, and HITL interrupts.
Open skill - /deepagents-python-quickstart
Scaffold a minimal local Deep Agent in Python by following the official quickstart, using provider-native web search instead of Tavily. Use when the user wants to quickly build or try a Deep Agent locally.
Open skill - /deepagents-typescript-quickstart
Scaffold a minimal local Deep Agent in TypeScript by following the official quickstart, using provider-native web search instead of Tavily. Use when the user wants to quickly build or try a Deep Agent locally.
Open skill - /ecosystem-primer
INVOKE FIRST for any LangChain / LangGraph / Deep Agents agent building project before consulting other skills or writing any agent code. Required starting point for up to date info on framework selection (LangChain vs LangGraph vs Deep Agents vs hybrid composition), agent
Open skill

