NAMS AI provider API reference
The shape of the public surface of @neo4j-labs/nams-ai-provider: every export,
grouped by the mode that uses it, plus every option’s type, default, and
constraint.
|
This package has no generated TypeDoc reference — it publishes on its own
|
See choosing a mode for a task-oriented comparison, and how retrieval works for why the defaults below are shaped the way they are.
Exports by mode
Provider mode
| Export | Signature |
|---|---|
|
Returns a |
NamsProviderOptions extends NamsConfig with:
| Field | Type | Default | Notes |
|---|---|---|---|
|
|
(required) |
Any |
|
|
(required) |
Fixed for the life of the provider. Create one provider instance per user session; see |
|
|
|
Capped at |
|
|
|
Save each turn (user input and assistant response) back to NAMS. |
Middleware mode
| Export | Signature |
|---|---|
|
Returns |
|
Lower-level factory |
NamsMemoryConfig extends NamsConfig with the same maxMemories (default
6, capped at 12) and persistInteractions (default true) fields as
NamsProviderOptions, without baseProvider/scope.
Tools mode
| Export | Signature |
|---|---|
|
Returns |
|
NAMS tools, optionally merged with an MCP server’s tools. |
|
A |
|
Returns an |
|
|
|
Thrown by |
|
Tools-mode-only entity/relationship extraction from a stored memory. Passed as |
NamsToolsOptions extends NamsConfig and NamsScope with:
| Field | Type | Default | Notes |
|---|---|---|---|
|
|
— |
Tools mode only; one extra model call per |
|
|
— |
|
McpConfig (the second argument to toolsWithMcp, or NamsToolsWithMcpOptions.mcp):
| Field | Type | Default | Notes |
|---|---|---|---|
|
|
(required) |
MCP server URL. |
|
|
— |
Sent on every request. |
|
|
— |
Prefixes every MCP tool name (e.g. |
|
|
|
When |
NamsToolsResult is { tools: ToolSet; close: () => Promise<void>; mcp?: McpConnectionStatus }. tools holds the NAMS and MCP tools together;
close() closes the MCP connection and is a no-op without one.
McpConnectionStatus is { connected: boolean; toolNames: string[]; error?:
NamsMcpConnectionError } — toolNames are the MCP tools' names as the
model sees them, with toolPrefix applied, and error is set when
optional: true hid a connection failure. mcp is absent when no MCP
config was passed. ensureMemoryStored accepts the merged tools object.
EnforceQueryMemoryOptions.graceSteps: number, default 3 — steps that
get toolChoice: 'required' before query_memory is forced; step
graceSteps itself is the forced one (toolChoice: { type: 'tool', toolName:
'query_memory' }), so { graceSteps: 0 } forces it as the literal first
step. While toolChoice is 'required' the model can call any tool, in any
order, but cannot finish with a text-only answer. Keep graceSteps at least
two below your stopWhen budget so the forced query and the final answer
both still fit.
EnsureMemoryStoredOptions.fallback: (turn: UnstoredTurn) => StoreInput | null,
default stores the final assistant text as an interaction (never a
fact), or nothing when the text is empty; UnstoredTurn is { text:
string; toolNames: string[] } and StoreInput is { content: string; type:
MemoryType; confidence?: number; tags?: string[] }.
EnsureMemoryStoredResult is { stored: true; input } | { stored: false;
reason: 'already-stored' | 'nothing-to-store' | 'failed' }. failed means
the write threw; the error is logged.
The query_memory tool’s input schema (QueryInput): { query: string;
limit?: number (1–20, default 5) }. At most 12 hits are returned, whatever
limit asks for. It returns QueryOutput: { found: boolean; count?:
number; message?: string; memories: MemoryHit[] } — count is set when
something was found, and message is "No relevant memories found." or
"Memory lookup failed." otherwise.
The store_memory tool’s input schema (ToolStoreInput): { content:
string (1–2000 chars); type: 'fact' | 'interaction' | 'pattern' |
'user_preference'; confidence?: number (0–1, default 0.7); tags?: string[]
(max 10 entries, each ≤40 chars, default []) }. An interaction is saved as
an assistant message in the user’s conversation; every other type creates (or
reuses) a flat entity, unless extractionModel is set. It returns
StoreOutput: { stored: boolean; type: string; preview: string; message:
string } — preview is the first 80 characters of content, and a failed
write returns stored: false with message: "Failed to store memory.".
Hooks mode
| Export | Signature |
|---|---|
|
Returns |
|
Compiles a |
NamsHooksOptions extends NamsConfig with:
| Field | Type | Default | Notes |
|---|---|---|---|
|
|
— |
Default scope; every method call can override it. |
|
|
|
Max prior turns |
|
|
— |
Lifecycle hooks by event — see Hook events. |
|
|
logs via the configured logger |
Where a hook’s |
NamsHooks methods:
| Method | Behavior |
|---|---|
|
Restores prior |
|
Runs |
|
Wraps a tool set so |
|
Runs |
|
Runs |
OnFinishScope: { userId?, conversationId?, prompt?: string | ModelMessage[], persistUserPrompt?: boolean (default true) }.
LoadSessionOptions is { userId?, conversationId?, limit? }, and
PrepareOptions adds prompt: string (required) to it. NamsToolBlocked —
what a denied tool returns to the model instead of its result — is {
blocked: true; toolName: string; reason: string }.
Low-level utilities
makeClient, getLogger, resolveConversation, findExistingConversation,
retrieveMemories, and storeMemory (all from the shared client module) are
exported for advanced use — building a custom mode on the same retrieval and
persistence logic the four built-in modes use. Ordinary usage does not need
them.
Types
NamsConfig
| Field | Type | Default | Notes |
|---|---|---|---|
|
|
(required) |
No built-in environment-variable fallback. |
|
|
The NAMS REST endpoint. |
|
|
|
— |
NAMS workspace id. |
|
|
|
Sink for non-fatal errors: |
|
|
|
Other recent conversations each lookup also searches, 2 requests each. |
|
|
|
Matched entities whose relationships are read back, 1 request each. |
NamsScope
{ userId: string; conversationId?: string }.
The conversation a scope reads and writes is, in order: the conversationId
you pass; otherwise the one this client already resolved for the same user;
otherwise the user’s most recent NAMS conversation; and only if the user has
none, a new conversation. Leaving out conversationId therefore continues
the user’s latest conversation — it does not start a new one. To start a
separate conversation, create it with the SDK
(shortTerm.createConversation({ userId })) and pass its id. Hooks mode’s
loadSession follows the same order but never creates a conversation.
Memory hit types
MemoryHit: { content: string; source: MemorySource; type: string; score?:
number }. score is the entity’s stored extraction confidence where one
exists — it is not used to rank hits (see
why).
MemorySource: 'long-term' | 'graph' | 'conversation' | 'cross-session' |
'reasoning'.
MemoryType: 'fact' | 'interaction' | 'pattern' | 'user_preference'.
Other exported types
Every other exported name is a type that appears in the signatures above:
-
Configuration and results:
NamsMode('provider' | 'middleware' | 'tools' | 'hooks'),NamsFactoryConfig,NamsLogger,GraphExtractor,GraphExtractorOptions,FinishedTurn(the{ text?, toolCalls?, steps? }that theensureMemoryStoredcallback reads),UnstoredTurn,StoreInput,ToolStoreInput,QueryInput,QueryOutput,StoreOutput,McpConnectionStatus. -
Hooks:
NamsHookEvent(the eight event names),NamsHookConfig,NamsHookGroup({ matcher?, hooks }),NamsHookEntry({ handler, timeout?, name? }),NamsHookHandler,NamsHookBase,NamsHookInputsandNamsHookOutputs(maps from event name to input and output type),NamsHookResult,NamsHookRegistry,NamsSystemMessageSink,NamsOnFinishEvent,NamsOnFinishCallback,LoadSessionOptions,PrepareOptions,PrepareResult,OnFinishScope,SessionTurn, and the per-event<Event>Input/<Event>Outputtypes (for examplePreToolUseInputandPreToolUseOutput).
Hook events
Every input extends a common base: { event, userId: string, conversationId?:
string, timestamp: Date }. Every output can also carry additionalContext?:
string (text returned in the instructions field of the prepare() result
for you to hand to the model: in the current call for SessionStart and
UserPromptSubmit, on the next call for PreToolUse, PostToolUse, PostToolUseFailure, PreMemoryWrite, and
Stop, capped at 20 queued entries per scope; discarded for SessionEnd) and
systemMessage?: string (routed to onSystemMessage), in addition to the
fields below. additionalContext never becomes a message, because the AI SDK
rejects system messages inside messages; append instructions to your own
instructions instead.
| Event | Fires | Extra input fields | Extra output fields |
|---|---|---|---|
|
The first |
|
— (context/system-message only) |
|
Inside |
|
|
|
Before a |
|
|
|
After a wrapped tool returns |
|
|
|
After a wrapped tool throws |
|
|
|
Inside |
|
|
|
Inside |
|
— (context/system-message only; the write has already happened) |
|
|
|
— (system-message only; |
SessionTurn is { role: 'user' | 'assistant'; content: string; metadata?:
Record<string, unknown> }.
Rules:
-
A
matcherscopes a hook group to specific tool names or session reasons. It is omitted or*(everything), a plain name, a|- or,-separated list of plain names, or otherwise a regular expression tested against the subject. An invalid regular expression is logged once at startup and never matches. A bare handler is shorthand for a group that matches everything. -
SessionStartandSessionEndmatch onreason,PreToolUse/PostToolUse/PostToolUseFailureontoolName.UserPromptSubmit,PreMemoryWrite, andStophave nothing to match on, so a matcher set on them is ignored with a startup warning. -
Rewrites chain — each handler sees the previous handler’s replacement, and the final value is what the tool, the model, or the write gets.
-
The first
deny/blockwins and skips the handlers after it. A denied tool does not throw: it returnsNamsToolBlockedto the model, so the loop continues and the model can explain the refusal. -
A hook that throws is logged as an error and skipped. One that outruns its
timeout(default30000ms, per-entry override viaNamsHookEntry.timeout) is abandoned and logged as a warning — a hook can never break a generation.
Errors
| Class | When it is thrown |
|---|---|
|
From |
The package does not define its own transport error hierarchy. Failures from
the underlying @neo4j-labs/agent-memory calls (see the SDK’s error classes
on the TypeScript SDK API reference) are
mostly caught and logged through the configured NamsLogger rather than
thrown — a lookup or persistence failure degrades that turn’s memory instead
of failing the model call.
Environment variables
| Variable | Required | Used for |
|---|---|---|
|
yes |
NAMS API key — pass it as |
No environment variable selects a mode; provider, middleware, tools, and hooks are chosen entirely in code.
Peers, runtime, and versioning
| Peer | Range |
|---|---|
|
|
|
|
|
|
|
|
Requires Node.js 22 or newer (engines.node: ">=22"). ESM-only ("type":
"module") with a single entry point — package.json declares no subpath
exports, unlike @neo4j-labs/agent-memory.
Releases are tagged nams-ai-provider-v<X.Y.Z> and published via
.github/workflows/publish-nams-ai-provider.yml, independently of the SDK’s
typescript-v* tags. The current release is 0.3.0 (2026-09-22).