Skip to content

LangChain / LangGraph

Vectadyne is an MCP server, so LangChain reaches it either through an MCP tool adapter or through the Python SDK directly. Prefer the SDK for the governed path, and here is why that is not just taste.

The tempting design is to expose preflight.action as a tool and let the model decide when to call it. That fails in a specific way: the model can choose not to. A model under instruction pressure — from a user, or from injected text in a document it is reading — will skip the safety check precisely when the check mattered.

Put the verdict in the graph, as a node the model does not control:

holdproceedplanassemblecontext.assembleverdictescalatesurface reasonsactingestevidence.ingestreportcontext.report

Retrieval is fine as a tool — a model choosing to look something up is normal. The verdict is not.

import os
from vectadyne import Vectadyne, interpret_verdict
vd = Vectadyne(
mcp_url=os.environ["VECTADYNE_MCP_URL"],
token=os.environ["VECTADYNE_TOKEN"],
)
def assemble_node(state):
assembled = vd.assemble(
action_type=state["action_type"],
summary=state["summary"],
changed_files=state.get("changed_files", []),
scope_key=state["scope"],
purpose=state.get("purpose", "feature"),
)
d = interpret_verdict(assembled.verdict)
return {**state, "assembled": assembled, "guidance": d}
def route(state):
return "escalate" if not state["guidance"].proceed else "act"

Note not ... .proceed. Python’s polarity is the inverse of Go’s and TypeScript’s — see Interpret a verdict.

A warn proceeds, and its advisories are the whole value:

def act_node(state):
notes = "\n".join(f"- {w}" for w in state["guidance"].warnings)
messages = state["messages"]
if notes:
messages = messages + [SystemMessage(
content=f"Governed memory notes for this action:\n{notes}"
)]
return {**state, "messages": messages}

Dropping them turns a warn into a silent allow and wastes the one thing the corpus knew that your agent did not.

def ingest_node(state):
vd.client.call_tool("evidence.ingest", evidence_args(
agent=state["agent"],
items=[{
"source_type": "run",
"summary": state["outcome"],
"scope_key": state["scope"],
}],
))
return state

Record failures too — a corpus of only successes cannot warn anyone about anything.

AsyncVectadyne mirrors the sync facade method for method, with the same parameter names and the same typed returns:

from vectadyne.aio import AsyncVectadyne # NOT vectadyne.AsyncVectadyne

Neither AsyncVectadyne nor Secret is re-exported at the top level, and the obvious guess fails.

One credential per member, and identity must survive a handoff — LangGraph state carries the conversation, and the credential is easy to drop on the hop. See Multi-agent crews.