TypeScript
npm install @vectadyne/sdkBrowser or Node. Same seven beats as Go and Python.
What this SDK has, and what it does not
Section titled “What this SDK has, and what it does not”Read this before you start, so nothing is a surprise three files in:
| Go | Python | TypeScript | |
|---|---|---|---|
| Typed verbs, governance interpretation | ✅ | ✅ | ✅ |
Retries, Retry-After, typed errors | ✅ | ✅ | ✅ |
| Compile-time request validation | partial | partial | ✅ best here |
| Context meter | ✅ | ✅ | ❌ use callTool |
| Testing fake control plane | ✅ | ✅ | ❌ |
| Auth module / credential exchange | ✅ | ✅ | ❌ wire it yourself |
| Environment facade | FromEnv() | from_env() | ❌ read process.env |
The gaps are real and are listed rather than discovered. ingest and report go through
client.callTool with the same Zod-validated shapes the typed verbs use, so you lose a convenience
method, not a capability.
The win is the first row. Request shapes are z.strictObject, so a misplaced key is caught
locally, at compile time, with the path — instead of becoming a validation fault one round trip
away:
await vd.recall("reconcile failures", { top_k: 5 });// ~~~~~ error: 'top_k' does not exist in type// SearchOptions. Did you mean 'ranking.top_k'?That mistake is a runtime round trip in the other two languages.
1. Point at the endpoint
Section titled “1. Point at the endpoint”There is no environment facade, so this is the wiring you write:
import { Vectadyne, VectadyneMCPClient } from "@vectadyne/sdk";
const mcpUrl = process.env.VECTADYNE_MCP_URL;const token = process.env.VECTADYNE_TOKEN;if (!mcpUrl || !token) throw new Error("VECTADYNE_MCP_URL and VECTADYNE_TOKEN are required");
const client = new VectadyneMCPClient({ mcpUrl, token, app: "release-bot" });const vd = new Vectadyne(client);
await client.discover(); // no credential needed — proves reachabilitydiscover() takes no token, so a failure there is a network problem and nothing else.
2. Remember and recall
Section titled “2. Remember and recall”await vd.remember( "The nightly reconcile job fails when billing/reconcile.py is edited without regenerating fixtures.", { kind: "lesson", scope_key: "repo:payments" },);
const hits = await vd.recall("reconcile job failures");for (const h of hits) console.log(h.content, h.memory_id);Exactly one of content or messages. Enforced by the type, so the compiler catches it.
An empty recall is a legitimate answer — admission filtering happens at read time.
3. Assemble
Section titled “3. Assemble”const assembled = await vd.assemble({ action: { action_type: "code.apply_patch", summary: "Rewrite the reconcile loop", changed_files: ["billing/reconcile.py"], scope_key: "repo:payments", }, purpose: "refactor",});action_type and purpose are union types, so an invalid value is a compile error rather than a
validation fault. Every closed vocabulary in the reference is a union
here.
4. Branch on the verdict
Section titled “4. Branch on the verdict”The polarity is inverted between our SDKs. TypeScript matches Go, not Python:
| SDK | Field | true means |
|---|---|---|
| Go | Guidance.Hold | stop |
| TypeScript | guidance.hold | stop |
| Python | GovernanceDecision.proceed | go |
import { interpretContext } from "@vectadyne/sdk";
const g = interpretContext(assembled);if (g.hold) { console.warn("holding:", g.reason); return;}for (const a of g.advisories) console.info("advisory:", a.message);- A
warnis never a hold. - An unknown decision sets
unknownand holds. Fails closed, like the other two.
5. Split errors four ways
Section titled “5. Split errors four ways”import { AuthError, ValidationError, RateLimitedError, TransportError } from "@vectadyne/sdk";
try { await vd.assemble({ /* … */ });} catch (e) { if (e instanceof AuthError) { /* refresh the credential; do not loop */ } else if (e instanceof ValidationError) { /* fix the call site */ } else if (e instanceof RateLimitedError) { /* e.retryAfter */ } else if (e instanceof TransportError) { /* the platform did NOT answer */ } else throw e;}“The platform said no” is not “the platform did not answer.” The SDK never synthesizes a verdict. See Handle errors.
6. Close the loop
Section titled “6. Close the loop”No convenience wrappers here, so use callTool — same validated shapes:
await client.callTool("evidence.ingest", { agent: "release-bot", items: [{ source_type: "pr", summary: "Reconcile loop rewritten; fixtures regenerated", scope_key: "repo:payments", }],});
await client.callTool("context.report", { agent: "release-bot", window_total: 128_000, segments: { system_prompt: 1_200, retrieved_memory: 4_800 },});There is no meter to accumulate segments for you, so build the segments object yourself. Omit a
label you did not measure — an absent label means “not reported”, and sending 0 claims you measured
zero. That distinction is the difference between a gap in your instrumentation and a finding.
7. Test that you asked
Section titled “7. Test that you asked”There is no bundled fake, so stub fetch or point at any HTTP mock and assert on requests:
const calls: string[] = [];globalThis.fetch = async (_url, init) => { const body = JSON.parse(String(init?.body)); calls.push(body.params?.name ?? body.method); return new Response(JSON.stringify({ /* your canned verdict */ }), { status: 200 });};
// … run your agent …expect(calls).toContain("preflight.action"); // it askedexpect(calls.length).toBe(1); // and did not retry a refusalAssert the count. A block must produce exactly one request.
Reference
Section titled “Reference”- MCP tools · Vocabularies · SDK parity
- Package: npmjs.com/package/@vectadyne/sdk