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.
Knowledge is in alpha. Its API may change between releases.
Usage exampleDirect link to Usage example
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 optionsDirect link to Constructor options
scopes:
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?:
name?:
id.description?:
knowledge.description. Agents and Studio don't use it yet.storage?:
tools?:
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?:
knowledge_pin tool. Needs Memory. maxPins defaults to 20 and maxTokens to 500, with no maximum. See Pinned context.vector?:
embedder; passing only one throws. Without both, search is keyword only.embedder?:
vector.embedderOptions?:
maxRetries, headers, providerOptions, telemetry.instructions?:
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.
MethodsDirect link to Methods
ToolsDirect 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.
AccessDirect 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:
null when it can't read it.reasons:
page?:
reason?:
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.
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 writesDirect 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 maintenanceDirect 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()Direct link to reindexlinks
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.
EventsDirect 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():
kind | op | Fields |
|---|---|---|
scope | create | pageId, scopeId (the new scope page's ID) |
page | create, update, move, merge, delete | pageId, scopeId; fromScopeId for move and merge; sourceId (the page merged away) for merge |
note | add, restamp, remove, restore | pageId, noteId |
pin | add, remove | pageId |
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)
})
PropertiesDirect link to Properties
pinnedContextEnabledDirect link to pinnedcontextenabled
boolean. Whether pinnedContext is on, so attached agents get pinned pages every turn.