Skip to main content

Knowledge class

The Knowledge class holds pages and notes, declares which scopes exist and who gets which role on them, and gives attached agents the Knowledge tools. See Knowledge for what it does and a quickstart.

alpha

Knowledge is in alpha. Its API may change between releases.

Usage example
Direct link to Usage example

src/mastra/knowledge.ts
import { Knowledge, knowledgeMemoryScopes } from '@mastra/core/knowledge'
import { LibSQLStore } from '@mastra/libsql'

export const knowledge = new Knowledge({
id: 'company',
storage: new LibSQLStore({ id: 'knowledge', url: 'file:./knowledge.db' }),
tools: true,
scopes: {
...knowledgeMemoryScopes(),
'team:$orgId:$teamId': {
access: [
{ principal: 'self', role: 'editor' },
{ principal: 'org:$orgId', role: 'viewer' },
],
},
},
})

Constructor options
Direct link to Constructor options

scopes:

KnowledgeScopeDeclarations
Scope types and concrete scopes this Knowledge knows, keyed by pattern (team:$orgId:$teamId) or address (org:acme). Required. Every run scope of an attached agent must match a declaration. Spread knowledgeMemoryScopes() for Memory's scopes, or pass {} to declare none. See Scope declarations for the fields of each declaration.

id?:

string
Identifier. Random if omitted.

name?:

string
Name used in logs. Defaults to id.

description?:

string
Stored as knowledge.description. Agents and Studio don't use it yet.

storage?:

MastraCompositeStore
Storage adapter. Without it, Knowledge uses the storage of the Mastra instance it is registered on.

tools?:

boolean | { read?: boolean | { maxTokens?: number }; write?: boolean }
= false
Tools an attached agent gets. true gives all five: knowledge_list, knowledge_search, knowledge_read, knowledge_page and knowledge_note. { read: true } gives the first three. { write: true } gives the last two. { read: { maxTokens } } turns on read and caps each knowledge_read body or match result, and each knowledge_list result, at maxTokens: 1,000 by default, at least 100. See Read pages.

pinnedContext?:

boolean | { maxPins?: number; maxTokens?: number }
= false
Pinned pages delivered to an attached agent every turn, plus the knowledge_pin tool. Needs Memory. maxPins defaults to 20 and maxTokens to 500, with no maximum. See Pinned context.

vector?:

MastraVector
Vector store for semantic search. Pass with embedder; passing only one throws. Without both, search is keyword only.

embedder?:

MastraEmbeddingModel<string>
Embedding model for semantic search. Pass with vector.

embedderOptions?:

MastraEmbeddingOptions
Options passed to the embedder on every call: maxRetries, headers, providerOptions, telemetry.

instructions?:

false | string | ((parts: { read: string; write: string; pins: string }) => string)
Replaces the built-in usage instructions added when Knowledge attaches tools. false adds none. A function receives the built-in parts, with an empty string for each disabled group, and must return the same text for the same input so the prompt stays cacheable. See Tool instructions.

The constructor throws a KnowledgeDeclarationError when scopes is missing or a declaration is invalid, for example two patterns of one type that overlap or a rule principal that doesn't bind. Without scopes the message is:

Knowledge needs `scopes`: declare the scopes its runs hold, e.g. `scopes: { ...knowledgeMemoryScopes() }`, or `{}` for none.

Methods
Direct link to Methods

Tools
Direct link to Tools

createTools()
Direct link to createtools

createTools(options?: { read?: boolean; write?: boolean; pinnedContext?: boolean }): Record<string, Tool>

Returns the Knowledge tools for explicit wiring, such as an agent that isn't attached with knowledge. Each option overrides the matching group set on the instance, and an omitted option keeps it. On an instance with pinnedContext on, createTools({ write: false }) also returns knowledge_pin. See Knowledge tools.

const tools = knowledge.createTools({ write: false })
// knowledge_list, knowledge_search, knowledge_read

getInstructions()
Direct link to getinstructions

getInstructions(options?: { read?: boolean; write?: boolean; pinnedContext?: boolean }): string

Returns the usage instructions that go with createTools(options), built the same way as for an attached agent: the built-in parts for the enabled groups, or what the instructions option makes of them.

Access
Direct link to Access

explainAccess()
Direct link to explainaccess

explainAccess(
runScopes: readonly string[],
target: { scope: string } | { page: string },
): Promise<KnowledgeAccessExplanation>

Explains why a run holding runScopes can or can't reach a scope or page. runScopes are raw run scopes, as you'd pass to an agent. target.page takes a page ID or a page path such as org:acme#policies/refund.

await knowledge.explainAccess(['org:acme'], { page: 'team:acme:support#refunds' })
// {
// role: 'viewer',
// reasons: [{ principal: 'org:acme', role: 'viewer', rule: 'team:$orgId:$teamId', via: 'rule' }],
// page: { id: '…', scope: 'team:acme:support' },
// }

await knowledge.explainAccess(['org:globex'], { page: 'team:acme:support#refunds' })
// { role: null, reasons: [], page: { id: '…', scope: 'team:acme:support' }, reason: 'hidden' }

role:

'viewer' | 'contributor' | 'editor' | 'owner' | null
The strongest role the run has on the target scope. null when it can't read it.

reasons:

Array<{ principal: string; role: string; rule: string; via: "self" | "rule" | "belongsTo" }>
Every rule that gives one of the run's scopes a role there: the held scope, the role, the declaration key or concrete address, and how the rule bound.

page?:

{ id: string; scope: string }
The resolved page and its home scope, for page targets that exist.

reason?:

'missing' | 'hidden' | 'no-rule'
missing: no such page. hidden: the page exists but the run can't read its scope. no-rule: no rule gives the run a role on the scope.

A run scope with no declaration throws, as it does at run start:

Knowledge run scope "project:x" does not match a declared scope type. Declare it in Knowledge `scopes` (for Memory's resource:/thread: scopes, spread `...knowledgeMemoryScopes()`).

A target.scope with no declaration throws Knowledge scope "project:x" does not match a declared scope type.

warning

explainAccess() is host-only. It tells a missing page from a hidden one, which runs must never learn. Don't expose it through an agent tool, or through a server route without admin-level auth.

readScopeFor()
Direct link to readscopefor

readScopeFor(principals: readonly string[]): Promise<KnowledgeReadScope>

Returns what a holder of principals can read: the principals themselves plus every scope their rules give them a role on. Roles don't carry over to further scopes. Pass the result as read to the store's read methods when host code reads Knowledge for a user. Undeclared principals throw Knowledge run scope "project:x" does not match a declared scope type. An empty list reads nothing.

scopeGrants()
Direct link to scopegrants

scopeGrants(scope: { address: string; parentAddress?: string | null }): KnowledgeScopeGrant[]

The roles the declarations give other scopes on this one, as { principal, role } with the strongest role per principal and self rules left out. It reads no storage. parentAddress supplies the tree parent that rules binding through belongsTo need. Use it for a browsing UI that draws access between scopes. Use explainAccess() for one run's view.

scopeDeclaration()
Direct link to scopedeclaration

scopeDeclaration(key: string): KnowledgeScopeDeclarationInfo | undefined

Returns the declaration a scope was created from, by its declaration key, for display: { key, description?, displayLabel?, belongsTo?, children }. Child keys work too, such as org:$orgId/private. Access rules are left out. Use explainAccess() for those. Returns undefined for an unknown key.

scopeLabel()
Direct link to scopelabel

scopeLabel(address: string): string | undefined

Returns the displayLabel for a scope address with its $params filled in: '$orgId organization' on org:$orgId gives acme organization for org:acme. A concrete address's own displayLabel wins. Returns undefined when the address matches no declaration or has no label.

Host writes
Direct link to Host writes

upsertPage(), removePage(), addNote(), updateNote() and removeNote() are host-only: they check no run scopes or access rules. Don't expose them through an agent tool, or through a server route without admin-level auth.

upsertPage()
Direct link to upsertpage

upsertPage(input: {
scope: string
path: string
name: string
description?: string
body?: string
author?: string
}): Promise<{ page: KnowledgePage; status: 'created' | 'updated' | 'unchanged' }>

Creates or updates the page at path (keys joined by /, such as guides/install) in scope, creating the scope and any missing pages along the path. Writing the same path again updates that page. description and body keep their stored values when left out. An unchanged result means nothing was written. The page keeps its version, emits no change event and isn't re-embedded. author is recorded as the writer and defaults to host. A path with no keys throws Knowledge page path "<path>" needs at least one key. See Load your content.

removePage()
Direct link to removepage

removePage(input: { scope: string; path: string; author?: string }): Promise<KnowledgePage | null>

Removes the page at path and resolves to it, or to null when no page is there. Throws "<scope>#<path>" has child pages; remove them first. for a page with children, and "<scope>#<path>" is declared in config; change the scope's pages to remove it. for a template page. Throws without retrying when another write changed the page first.

addNote()
Direct link to addnote

addNote(input: {
scope: string
path: string
text: string
when?: Date
stamps?: string[]
author?: string
}): Promise<KnowledgeNote>

Adds a note to the page at path. text is at most 250 tokens. [[scope#path]] mentions of existing pages link the note to them. Other [[...]] text stays plain. stamps are the scope addresses whose readers see the note and default to the page's scope. Throws KnowledgeNotFoundError when the page doesn't exist.

updateNote()
Direct link to updatenote

updateNote(input: { id: string; text: string; when?: Date; stamps?: string[]; author?: string }): Promise<KnowledgeNote>

Replaces a note: adds the new note, then removes the old one, and resolves to the new note with a new ID. when and stamps keep their old values when left out or empty. If removing the old note fails after the add, both notes exist and the call throws, so remove the one you don't want. When the old note was already removed by someone else, the new note is removed too and nothing is left behind.

removeNote()
Direct link to removenote

removeNote(input: { id: string; author?: string }): Promise<KnowledgeNote | null>

Removes a note and resolves to it, or to null when it's already gone.

Storage and maintenance
Direct link to Storage and maintenance

getStorage()
Direct link to getstorage

getStorage(): Promise<KnowledgeStorage>

Returns the Knowledge store, for host code such as admin pages, deletes and your own import scripts. Its read methods take read: 'all' skips the role checks, so pass the value from readScopeFor() whenever a read should be limited to what a user can see.

const store = await knowledge.getStorage()
const read = await knowledge.readScopeFor(['org:acme'])
const [scope] = await store.getScopesByAddress(['team:acme:support'])
const page = await store.getPageByPath({ scopeId: scope.id, path: ['handbook', 'refunds'], read })
reindexLinks(): Promise<number>

Rebuilds the page link index from every page body, and resolves to how many pages it indexed. Writes keep the index current, so this is for pages that predate it or were written straight to the store. It needs no vector store.

renameScopeDeclaration()
Direct link to renamescopedeclaration

renameScopeDeclaration(from: string, to: string): Promise<number>

Moves stored scopes created from declaration key from, and its child scopes (from/<name>), to key to, in one step. Resolves to how many scopes moved. Refuses, and changes nothing, when from is still in scopes, to isn't, or a stored address doesn't match to's pattern. Until stored keys match scopes, the instance refuses to run.

On MongoDB, the rename runs in a transaction only on a replica set. On a standalone server it isn't atomic, and a failure partway can leave some scopes renamed.

See rename or retire a scope type.

Events
Direct link to Events

onChange()
Direct link to onchange

onChange(listener: (event: KnowledgeChangeEvent) => void): () => void

Calls listener after each committed write made through this instance's store: tool writes, host writes through getStorage(), and new scopes. Returns a function that removes the listener. Use it to refresh a live UI. Writes made on a store obtained another way aren't seen.

Events carry IDs, not content or access, so anything that relays them to people must re-read each page under that person's read scope from readScopeFor():

kindopFields
scopecreatepageId, scopeId (the new scope page's ID)
pagecreate, update, move, merge, deletepageId, scopeId; fromScopeId for move and merge; sourceId (the page merged away) for merge
noteadd, restamp, remove, restorepageId, noteId
pinadd, removepageId

Every event also has knowledgeId and at. Creating a scope emits one scope event, not one per child scope or template page, so re-list the scope when it arrives. A listener that throws is logged and doesn't fail the write.

const unsubscribe = knowledge.onChange(event => {
if (event.kind === 'page') refreshPage(event.pageId)
})

Properties
Direct link to Properties

pinnedContextEnabled
Direct link to pinnedcontextenabled

boolean. Whether pinnedContext is on, so attached agents get pinned pages every turn.