world-model-mcp
v0.1.0
Published
TypeScript SDK for world-model-mcp — a temporal knowledge graph MCP server for AI coding agents. Provides typed access to all 28 tools exposed by the Python server.
Downloads
324
Maintainers
Readme
world-model-mcp — TypeScript SDK
Typed TypeScript client for world-model-mcp, a temporal knowledge graph MCP server for AI coding agents.
The SDK gives your agent three verbs against your project's memory:
- Capture what the agent, the user, and CI actually did — file edits, corrections, decisions, test outcomes.
- Enforce learned constraints before a write lands, backed by past violation counts and external linters.
- Verify that an answer citing memory is actually grounded in the facts it names, using an independent adversarial Coach pass.
v0.2 status. 11 of the 30 server tools have first-class typed methods — the ones you actually reach for in hook code — plus a
preToolUseconvenience wrapper. The rest go throughclient.callTool<T>(name, args)as a typed escape hatch. Two transports supported:stdio(local subprocess) andhttp(remote server via StreamableHTTP per the MCP 2025-11-05 spec). Bearer-token auth for the HTTP path lands with the SaaS hosted layer. Open an issue if you find yourself repeating a specificcallTooland we will surface it.
Install
npm install world-model-mcpYou will also need a running world-model-mcp Python server:
pipx install world-model-mcpQuickstart
import { WorldModelClient } from "world-model-mcp";
const client = new WorldModelClient({
transport: {
type: "stdio",
pythonPath: "python3",
args: ["-m", "world_model_server.server"],
env: { WORLD_MODEL_DB_PATH: ".claude/world-model" },
},
});
await client.connect();
const result = await client.queryFact({
query: "Which linter do we use for TypeScript",
entity_type: "constraint",
});
console.log(result.confidence, result.facts);
await client.close();HTTP transport
Connect to a remote world-model-mcp server over HTTP. Uses the StreamableHTTP transport per MCP 2025-11-05. On the server side, start with WORLD_MODEL_TRANSPORT=http (and install the HTTP extras: pipx install 'world-model-mcp[http]'). Default endpoint is /mcp, override via WORLD_MODEL_HTTP_PATH on the server and mcpPath on the client.
const client = new WorldModelClient({
transport: {
type: "http",
url: "https://your-tenant.world-model-mcp.dev", // origin, /mcp appended
authToken: process.env.WORLD_MODEL_API_KEY, // Bearer <token>
headers: { "X-Trace-Id": crypto.randomUUID() },
},
});
await client.connect();Same typed method surface as stdio — the transport swap is transparent to callers. ConnectionError fires the same way when the server is unreachable, dies mid-session, or returns a non-2xx during the initial handshake.
The three verbs
Capture
| Method | When to call it |
|---|---|
| recordEvent(input) | Every file edit, test run, or tool invocation |
| recordCorrection(input) | Whenever a user overrides the agent's output — high-priority signal |
| recordDecision(input) | Agent-proposal vs. human-correction traces for the governance loop |
| getDecisionLog(input) | Read decisions back for a session, file, or type |
Enforce
| Method | When to call it |
|---|---|
| getConstraints(input) | Constraints that apply to a specific file |
| getContextForAction(input) | Layer 1 bundle — constraints + decisions + bugs + co-edits + facts + regression risk in one round trip |
| validateChange(input) | Layer 2 pre-write gate — returns deny / defer / warn / proceed plus violations and suggestions |
| simulateChange(input) | Dry-run projection of blast radius + historical outcomes for a proposed change |
| preToolUse(input) | Convenience: getContextForAction + validateChange in parallel, guaranteed non-null decision band |
| getInjectionContext(input) | Compact context bundle for PostCompact / UserPromptSubmit / SessionStart hooks |
Verify
| Method | When to call it |
|---|---|
| queryFact(input) | Look up facts about entities to ground an answer |
| verifyRetrieval(input) | Layer 3 — adversarial Coach pass over the answer + supplied fact ids |
| findContradictions(input) | Discover contradicting-fact pairs |
| resolveContradiction(input) | Pick a winner between two facts using a confidence-weighted strategy |
Realistic hook: Layer 1 → Layer 2 → capture
// PreToolUse — one call runs get_context_for_action + validate_change in
// parallel and returns a non-null decision band.
const pre = await client.preToolUse({
change_type: "edit",
file_path: "src/api/auth.ts",
proposed_content: newContent,
});
if (pre.decision === "deny") {
throw new Error(`Blocked by constraint: ${pre.validation.violations[0].rule}`);
}
if (pre.context.risk_level !== "low") {
console.warn(`High-risk change (${pre.context.risk_score}):`, pre.context.factors);
}
// PostToolUse — record what actually happened
await client.recordEvent({
event_type: "file_edit",
session_id: sessionId,
entities: ["src/api/auth.ts"],
description: "Rotated JWT secret handling to env var",
success: true,
});Wiring into Claude Code
A ready-to-run version of the hook script above lives at examples/pre-tool-use.mjs. Wire it into ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "node ./examples/pre-tool-use.mjs" }
]
}
]
}
}The script reads Claude Code's PreToolUse payload from stdin, calls preToolUse, exits 2 with a { decision: "block", reason } payload on deny, logs a stderr warning on defer or elevated risk, and fails open on transport errors so a broken world-model server never blocks your edits. See docs/HOOKS.md for pre-commit git hook and custom-MCP-wrapper patterns.
Verifying a memory-grounded answer
const q = await client.queryFact({
query: "Which auth library",
entity_type: "package",
});
const answer = synthesizeAnswer(q.facts); // your Player call
const v = await client.verifyRetrieval({
query: "Which auth library",
answer,
fact_ids: q.facts.map((f) => f.id),
});
if (v.confidence === "LOW") {
console.warn("Answer not grounded:", v.unverified_claims);
}Untyped tools
The remaining 17 server tools reach the wire through the escape hatch:
const health = await client.callTool<Record<string, unknown>>(
"get_health_report",
{}
);callTool is generic over the return type, so you can pass a shape when you know it:
type CoEdits = { file_path: string; suggestions: string[]; message: string };
const co = await client.callTool<CoEdits>("get_co_edit_suggestions", {
file_path: "src/api/auth.ts",
limit: 5,
});Errors
All errors extend WorldModelError:
ConnectionError— server did not start, connection dropped, or subprocess died mid-session. When this fires mid-session, the client tears down its internal handle; construct a fresh client andconnect()again (do not reuse the dead one for stateful session tracking).ToolCallError— the server processed the request but the tool returned an error, or the response failed schema validation.err.causecontains the underlying Zod error (or the raw server error object) for inspection.
Server-side errors surface as ToolCallError with a Server error: prefix — the SDK checks isError on the MCP response envelope, so they don't get misreported as schema-validation failures.
License
MIT
