About ns-langgraph-agents
ns-langgraph-agents is a skill for building and maintaining LangGraph.js agent runtimes with TypeScript. It provides guidance on runtime layout, prompt and capability injection, MCP governance, and keeping graph specifications aligned with runtime changes.
Key features
- 01LangGraph.js runtime guidance
- 02Agent file placement rules
- 03Prompt and capability injection
- 04MCP access governance references
- 05Graph-spec synchronization gates
Use cases
- 01Bootstrap a new LangGraph agent runtime
- 02Recover an unclear existing runtime layout
- 03Plan prompt or tool binding changes
- 04Update graph specifications with runtime changes
MCP support
It guides agent implementations that use MCP tools as capabilities within a LangGraph graph. References cover multi-server MCP discovery, transport, lifecycle, allowlists, and tool budgets.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ nextstage-brasil/ skills --skill ns-langgraph-agentsAfter installing, type/ns-langgraph-agentsin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
evals/evals.jsonreferences/anti-patterns-extended.mdanti-patterns.mdarchitectures.mdbootstrap-agent-runtime.mdcapability-governance.mdcontext-window-and-tokens.mderror-and-reliability.mdevals-and-gates.mdevidence-and-fidelity.mdguardrail-and-adversarial.mdmcp-complex-access.mdmessage-content-blocks.mdmodel-cascade-and-cache.mdobservability.mdorphan-recovery-checklist.mdplacement-and-domains.mdprompt-and-capability-injection-plan-execute.mdprompt-and-capability-injection.mdruntime-layout.mdstreaming-and-hitl.mdturn-flow-gates.mduser-memory.mdscripts/bootstrap-agent-runtime.mjstemplates/agent-runtime/.dockerignore.env.example.gitignoredocker-compose.ymlDockerfiletemplates/agent-runtime/config/README.mdDetails
From the project READMELangGraph Agents
Production-grade LangGraph.js (Node 24+, TypeScript strict, @langchain/langgraph).
Owns runtime doctrine — placement, prompt/capability injection, graph-spec sync. Diffs via ns-coder or ns-autonomous. Framework choice: ns-agent-architecture.
Applicability
| Context | Doctrine strength |
|---|---|
| Greenfield agent-api (new LangGraph runtime) | MUST follow build workflow gates — dev-chat, budgets, normalize-before-truncate, separate skill cap. Topology: suggest plan_execute for most MCP/tool-heavy cases — lock in graph-spec.md; not a hard rule |
| Brownfield existing agent | RECOMMENDED migration toward same controls; orphan recovery does not Critical-fail missing topology |
| Intentional MCP redesign | Sync graph-spec.md + refs in same delivery — treat greenfield MUST for topology/budget/evidence sections touched |
Brownfield open ReAct valid until deliberate topology change. Greenfield MUST = new agent-api + intentional MCP redesign only.
Routing (read first)
| Signal | Action |
|---|---|
| Conceptual design unlocked (agent vs RAG vs fine-tune) | Stop → ns-agent-adaptation |
| No framework lock / CrewAI requested | Stop → ns-agent-architecture |
| Orphan / lost structure / layout unclear | Run orphan checklist before features (references/orphan-recovery-checklist.md) |
GitLab ISSUE_URL or SDD version scope | Defer to harness ../../ns-harness/references/code-skill-routing.md — do not absorb |
| Approved placement/inject plan ready for diff | Hand off to ns-coder for implementation — except greenfield bootstrap copy (references/bootstrap-agent-runtime.md) |
Boot (mandatory)
See ../../ns-harness/references/session-boot.md — complete Session boot (blocking) there, then:
- Confirm
{agent_api_root}andgraph-spec.mdwhen touching runtime - Load placement/inject refs before path decisions (
references/placement-and-domains.md,references/prompt-and-capability-injection.md) - Continue this skill
Success: placement + inject doctrine + project rules. Failure: invented folders or external frameworks.
When to use
| Situation | Action |
|---|---|
Greenfield agent-api (no {agent_api_root}/package.json) | Build workflow Phase 0 then Phase 1 bootstrap — references/bootstrap-agent-runtime.md |
| Brownfield / orphaned runtime | Run Orphan recovery (references/orphan-recovery-checklist.md) before features |
| New file / unclear folder | Placement Decision Block + references/placement-and-domains.md |
| System prompt / skill inject / bind | Prompt/Capability plan + references/prompt-and-capability-injection.md |
Topology / state / capabilities / guard_fail_mode change | Spec Sync Gate — update graph-spec.md in the same delivery |
| MCP with many servers/tools | Read references/mcp-complex-access.md + references/capability-governance.md |
| Token blow-up / slow turns | Read references/context-window-and-tokens.md |
| Agent must remember the user (preferences, terms, CRUD) | Read references/user-memory.md — per-user store, deduce-and-inform, /memories routes |
| Untrusted input / injection / claimed authority | Read references/guardrail-and-adversarial.md — scope classifier; scaffold fail-open ≠ doctrine |
| Cost-sensitive high volume / model tier / cache | Read references/model-cascade-and-cache.md |
| Provider message/reasoning quirks | Read references/message-content-blocks.md |
| HITL / streaming UX | Read references/streaming-and-hitl.md |
| JSON planner / analyst chooses tools | Operator-progress channel — templates/contracts/planner-contract.md + references/streaming-and-hitl.md |
| Prompt change / repeated hops / loop / "fixed" claim | Read references/turn-flow-gates.md — flow-first, terminal, repetition, evidence gates |
| Evals before merge | Read references/evals-and-gates.md |
Core doctrine
LangGraph = control flow. MCP/local tools = capabilities under graph. Small graph state (refs, summaries). Checkpointer = full state; context window = LLM view — separate.
System prompt: compose base_invariant (motor) + injected (product persona) per LLM invoke. Never persist composed system/persona text in graph state, checkpointer, or durable messages. Summary SystemMessage at index 0 ≠ full system — references/prompt-and-capability-injection.md, references/message-content-blocks.md.
Locale: conversation-observed turnLocale (detection-first from human messages ± intent slots); configurable.locale weak hint only; Intl formatters in code — not fixed bootstrap locale. references/evidence-and-fidelity.md, templates/snippets/conversation-locale.ts.snippet.
Operator progress (JSON planner hops): greenfield streaming_sse with a planner/analyst that emits structured executionPlan (no bindTools on that hop) MUST persist userFacingIntent (or analysis.userFacingIntent) + executionPlan on AgentState. userFacingIntent language MUST match the current user message (last HumanMessage) — not English unless that message is English; not product default locale. Machine intent stays English for audit. Emit SSE thinking from that field at node entry of the next hop — not response_streaming, not in durable messages. Hop 0 uses generic copy from conversation/presentation/ (or locale) in the same operator language. Open ReAct + ToolNode uses tool_started / tool_finished only. Details: templates/contracts/planner-contract.md, references/streaming-and-hitl.md.
Three capability kinds bind to the model:
| Kind | LLM wire name | Internal id |
|---|---|---|
| Local tool | {name} | local:{name} |
| MCP tool | mcp__{server}__{tool} | mcp:{server}:{tool} |
| Skill procedure | use_skill__{id} | skill:{id} |
Wire names must match ^[a-zA-Z0-9_-]{1,128}$ (use __ separators; colons only in internal ids). Colon in a new wire name is Critical on review.
Pre-change gates
Before new file or inject/bind change: complete all three. No code until posted.
1. Placement Decision Block
### Placement Decision Block
- Artifact: …
- Type: …
- Target path: …
- Layer: …
- Refs: placement-and-domains.md
- do_not_create_under: […]
Full matrix: references/placement-and-domains.md.
2. Prompt / Capability plan
### Prompt / Capability plan
- Compose: base_invariant + injected (rebuild per invoke; not in state/checkpointer/durable messages)
- Motor (`base_invariant`): [gather-no-Markdown / sole-writer / tool discipline / JSON planner userFacingIntent is SSE not Markdown / …]
- Product (`injected`): canonical path + persona/tone notes; mode-resolved: yes/no; modes: [...]; resolver: ...
- System layers touched: […]
- Canonical prompt path: …
- Session overlay: yes/no
- Bind list: […]
- Auto-inject skills: […] (exclusive of bind for same id)
- Truncate caps: tool vs skill body
- Bind parity: …
- Spec paths to sync: […]
Full doctrine: references/prompt-and-capability-injection.md. Plan/mode-resolve: references/prompt-and-capability-injection-plan-execute.md.
3. Spec Sync Gate
Nodes, edges, state, capabilities, recursion_limit, wire names, or guard_fail_mode change: update graph-spec.md same delivery. Stale archive ≠ SoT — sync spec to intended runtime; do not force-fit live code to stale archive.
Reference map
Load on demand — do not memorize whole files.
| Reference | Read when |
|---|---|
references/bootstrap-agent-runtime.md | Greenfield copy of templates/agent-runtime/ |
references/orphan-recovery-checklist.md | Project structure unclear or agent "lost" |
references/runtime-layout.md | Scaffolding, refactors, layer violations |
references/placement-and-domains.md | Where to put files; domain vs graph vs config |
references/prompt-and-capability-injection.md | System prompt layers, bind vs inject, bind parity |
references/prompt-and-capability-injection-plan-execute.md | Prompt/Capability plan; plan_execute gather vs composer; mode-resolved injected |
references/message-content-blocks.md | AIMessage/HumanMessage/ToolMessage across providers |
references/context-window-and-tokens.md | trim, summarize, tool vs skill body caps, context_manager |
references/mcp-complex-access.md | Multi-server MCP, discovery, transport, lifecycle |
references/capability-governance.md | Allowlist, classification, rate limits, tool budgets |
references/evidence-and-fidelity.md | State-backed evidence, fidelity gate, conversation-observed locale |
templates/snippets/conversation-locale.ts.snippet | resolveConversationLocale + Intl formatUserFacing |
templates/snippets/tool-budget.ts.snippet | Per-turn tool/MCP caps, arg fingerprint duplicate-skip |
templates/snippets/prepare-llm-messages.ts.snippet | context_manager helper |
references/error-and-reliability.md | Tool errors, circuit breaker, retries, compensation, parallel staleness, cost stop |
references/model-cascade-and-cache.md | Cheap-first cascade, both signals OR-escalate, semantic/prompt cache; not LLM intent_classify hop |
references/observability.md | Postgres audit, retention = tenant clock, query tenant+period+one decision, cost reservation |
references/architectures.md | ReAct, plan_execute (suggested start for most MCP), Reflection levels, other topologies; node id ≠ state channel |
references/guardrail-and-adversarial.md | Scope/safety classifier; fail-open scaffold ≠ graph-spec lock; not Gateway routing |
references/streaming-and-hitl.md | SSE envelopes, operator thinking, interrupted, resume SSE, interrupt(), Command resume |
references/turn-flow-gates.md | Prompt/loop incidents: flow-first, terminal, repetition, tool-error, DoD |
templates/contracts/planner-contract.md | JSON planner hops: executionPlan + userFacingIntent |
references/user-memory.md | Per-user long-term memory: kinds, deduce-and-inform write path, per-invoke read, /memories CRUD, LGPD |
references/evals-and-gates.md | Architecture, tool-selection, memory evals; golden-set promotion |
references/anti-patterns.md | Review gate before marking done — hot path |
references/anti-patterns-extended.md | Dead prompt copies, state/memory, reliability, graph, MCP, LLM, ops, process |
Templates: templates/agent-runtime/ (greenfield tree), templates/graph-spec.md, templates/contracts/, templates/snippets/ (brownfield patches).
Session inputs
| Variable | Required |
|---|---|
{agent_api_root} | Default agent-api |
{task} | What to build, fix, or review |
Orphan recovery (brownfield first)
Runtime disorganized or team blocked:
- Read
references/orphan-recovery-checklist.md; score project. - Gap report: structure, placement, context window, inject/bind parity, MCP governance, HTTP, spec sync.
- Ordered fix plan (one phase per message if large).
- Implement via
ns-coder.
No new graph nodes or MCP servers until layout + governance baselines pass.
Build workflow (greenfield or post-recovery)
Phase 0 — Spec gate
If graph-spec.md is missing, create it from templates/graph-spec.md. Minimum sections: locked header (framework, architecture, interaction_mode, guard_fail_mode), domain ownership, prompt composition, state schema, nodes table, edges, interrupts, memory, capability bind/inject table, recursion_limit, HTTP routes.
If the user has no architecture decision yet, stop and invoke ns-agent-architecture first.
Phase 1 — Skeleton
If {agent_api_root}/package.json is missing, run scripts/bootstrap-agent-runtime.mjs (references/bootstrap-agent-runtime.md). Then npm install && npm test in {agent_api_root}.
Do not assemble the tree from templates/snippets/. Do not copy another product. After copy: align src/graph/ with graph-spec.md; put domain in conversation/ + config/ via later phases / ns-coder.
Brownfield existing tree: align per references/runtime-layout.md — snippets for patches only.
Phase 2 — State and persistence
After bootstrap, verify these exist; do not re-scaffold. Fill only gaps vs graph-spec.md.
AgentStatewithmessagesreducer (Annotation.Rootor Zod +MessagesZodMeta).PostgresSaverin dev/prod;MemorySaveronly intests/setup.ts.- Long-term memory (when
graph-spec.mdlocks it): per user (tenant_id+user_id), never perthread_id—references/user-memory.md. - Every invoke/stream:
configurable.thread_idviabuildRunConfig. - JSON planner/analyst (no
bindToolson that hop): declareexecutionPlan+userFacingIntent(or nested onanalysis) ingraph-spec.mdstate schema —templates/snippets/state.ts.snippet.
Phase 3 — LLM and messages
- Provider config in
src/llm/(infra only — no domain prompts). - One env var per model role (e.g. chat, summarize, embed) — no hardcoded model id in source.
- Embedding model change invalidates vector index → full re-seed required.
- Graph/node unit tests: fake LLM in default tier. Prompt/guardrail behavior needing a real model → live tier (Test tiers in
ns-harness→docker-and-testing.md). - Prefer JSON mode + Zod parse for structured turns; avoid
withStructuredOutputon OpenAI-compatible local servers. - Normalize provider output via
contentBlocks/content_blocks— seereferences/message-content-blocks.md.
Phase 4 — Context window (mandatory)
Implement per references/context-window-and-tokens.md:
trimMessagesForLlmbefore every LLM call.normalizeMcpToolResultthentruncateToolOutputbeforeToolMessageenters state.- Separate
CONTEXT_SKILL_BODY_MAX_CHARSfor skill bodies (snippetskillBodyMaxChars). - Summarize when used tokens exceed 80% of
CONTEXT_MAX_TOKENS(raw window). Ratios are constants: trigger0.8, compact to0.5, summary cap0.1. No summarize multiplier env. summarizeOlderMessageswith persisted compaction (RemoveMessage+ rewrite) in the same agent-node return.
Never pass raw state.messages to the model.
Phase 5 — Capabilities
- Local
StructuredTools insrc/tools/. - MCP: governed client — discovery → local allowlist → wire names → singleton client lifecycle (
references/mcp-complex-access.md). - Skills:
skills/*.mdauto-discovered →use_skill__{id}or auto-inject (exclusive per id).
Apply references/capability-governance.md and references/prompt-and-capability-injection.md before bindTools. Enforce bind parity. Wire per-turn tool/MCP budgets from templates/snippets/tool-budget.ts.snippet when MCP or external tools are bound.
Phase 6 — HTTP and interaction mode
| Mode | Requirements |
|---|---|
sync_json | POST /threads, POST /threads/:id/message |
streaming_sse | SSE envelope per references/streaming-and-hitl.md; composer reply MUST model.stream() + cumulative response_streaming ticks (not invoke-dump); SSE flush leave-now; dev-chat flushSync per tick; greenfield MUST ship styled React GET /dev-chat (DEV_CHAT_ENABLED, local-only) + GET /dev-chat/app.js; JSON planner hops MUST emit operator thinking; Docker artifacts (Dockerfile, agent-only compose) shipped |
| HITL | interrupt() + POST /threads/:id/resume (SSE when Accept SSE) with Command({ resume }); always-escalate MUST send approver_id + approver_role; paused graph → terminal interrupted |
User memory enabled → caller-scoped /memories CRUD (references/user-memory.md §5); user_id from auth only.
Brownfield missing dev-chat: recommend add — not Critical. Postman synced with live routes.
Phase 7 — Observability
Wire references/observability.md: initDb, runStorage, logLlmCall, logToolExecution, persist turn_decisions. LangSmith and OTel are opt-in.
Phase 8 — Evals and review
- Add suites per
references/evals-and-gates.md. - Run
npm run build && npm testin{agent_api_root}. - Invoke
ns-revieweron the diff; ask it to verify placement, inject, wire-name, and bind-parity anti-patterns when the diff touchesagent-api.
Maintenance workflow
Ongoing work (not greenfield):
- Three Pre-change gates
graph-spec.mdmatches intended graph after change- Layer: graph node, conversation, MCP, memory, HTTP
- Read matching reference before edit
- Minimal diff via
ns-coderwith placement/inject handoff - Postman on HTTP route change
- Re-run orphan checklist items touched
MCP complex access (quick rules)
Multiple MCP servers, overlapping or large catalogs:
- Discovery filter — allowlisted
tools/listbefore bind - Local classify —
read | write | destructive | admin; never trust server read-only flags - Singleton client — one
MultiServerMCPClientper process; no per-request stdio in prod - Transport — Streamable HTTP deployed; stdio local single-user dev only
- Errors — MCP
isError: true= recoverableToolMessagestatus: "error"; protocol fail may abort with HTTP/SSEfailed - Secrets — env or
configurable/ request payload only; never state or checkpointer
Full: references/mcp-complex-access.md.
Handoff to ns-coder
When implementation is approved, delegate with:
## LangGraph implementation task
- Root: {agent_api_root}
- Spec: path/to/graph-spec.md
- Phase: [number and name from this skill]
- target_paths: […]
- layer: […]
- do_not_create_under: […]
- injection_notes: [layers / bind vs auto-inject / caps]
- spec_paths_to_sync: […]
- References to apply: [list]
- Acceptance: build + test pass; orphan checklist items [n] resolved; placement + inject + bind parity verified
- Review: ns-reviewer after tests — must check placement, inject, wire names (`:`), bind parity (load ns-langgraph-agents anti-patterns when diff touches agent-api)
Stay here for diagnosis, spec, placement, governance design, and greenfield bootstrap copy. ns-coder for feature diffs. Version-scoped features: ns-spec-driven.
Stop conditions
| Condition | Action |
|---|---|
No graph-spec.md and user wants code now | Create spec or invoke architect |
| Greenfield assembled from snippets / another product tree | Stop; run scripts/bootstrap-agent-runtime.mjs |
| Path outside placement matrix / inventing folders | Stop; propose legal path |
Domain / locale / copy landing in graph/ or llm/ | Stop; reroute to conversation/config |
| Bind without parity (dispatchable but unbound) | Stop; fix bind or document unbound + test |
: in a new wire name | Stop; use __ separators |
| Skill auto-inject + bind same id without explicit decision | Stop; choose one mode |
| CrewAI requested | Redirect to appropriate skill |
| Change spans >3 layers without plan | One-line phased plan, wait for approval |
| Critical security gap (secrets in state, ungoverned MCP) | Block feature work; fix governance first |
Related skills (ownership)
| Skill | Owns |
|---|---|
ns-langgraph-agents | Doctrine, placement, inject plan, graph-spec, greenfield scaffold |
ns-coder | Feature diffs + review loop (not initial scaffold copy) |
ns-reviewer | Verdict; when diff touches agent-api, apply placement + inject + wire-name + bind-parity anti-patterns from this skill |
ns-agent-adaptation | Conceptual adaptation design before architecture |
ns-agent-architecture | Framework choice before Phase 0 when unlocked |
ns-spec-driven | Version features after scaffold exists (or first task = bootstrap) |
ns-investigator | Runtime debug |
Forbidden
- Emitting planner
userFacingIntentasresponse_streamingor as Markdown inmessages(SSEthinkingonly; composer remains sole Markdown writer) - Composer reply via
model.invoke/ single end-of-turnresponse_streamingdump; buffered SSE; client typewriter or delayed paint of ticks (references/streaming-and-hitl.md) - Writing
userFacingIntentin a language other than the current user message (e.g. English progress when the operator wrote Portuguese) - Persisting composed system/persona prompt (
base_invariant + injected) — or secrets/API keys — in graph state, checkpointer, or durablemessages(rebuild system text per invoke) - Keying long-term user memory by
thread_id, storing company/tenant facts or secrets in user memory, persisting the rendered memory block in state/checkpointer, or asking the user to confirm each memory instead of informing (references/user-memory.md) - Treating bootstrap /
.env/configurable.localeas primary locale SoT, or persisting sticky thread locale (use conversation-observedturnLocale+ Intl) - Passing unbounded tool/MCP output into
state.messages - Applying tool/MCP truncate caps to skill bodies (use
CONTEXT_SKILL_BODY_MAX_CHARS) - Trusting MCP tool metadata for security classification
- Spawning stdio MCP subprocesses per HTTP request in production
memory/compilingStateGraph- Domain qualify/conversation prompts in
src/llm/ - Locale/presentation under
graph/ - Nudge as fake
HumanMessage - Tests under
src/ - Greenfield tree from snippets or another product — use
templates/agent-runtime/only