Appearance
Manage Resources โ
Three tools follow the same action-discriminated "manage" pattern โ one tool, one action parameter, multiple operations (create, list, get, update, delete, assign, remove):
gnosari_manage_knowledgeโ knowledge sources (this page's primary subject)gnosari_manage_traitsโ personality traits (briefly covered below)gnosari_manage_data_collectionโ data collection templates (briefly covered below)
See Tool Patterns for the general manage-tool action pattern shared by all three.
gnosari_manage_knowledge โ
Create, list, get, update, delete, assign, or remove knowledge sources. Knowledge sources are documents or websites that give agents domain expertise via RAG.
Annotations: readOnlyHint: false ยท destructiveHint: false ยท idempotentHint: false ยท openWorldHint: true
This is the only manage_*-family tool with openWorldHint: true. Every other manage_* tool (gnosari_manage_traits, gnosari_manage_data_collection) is openWorldHint: false because they only touch Gnosari's own data. gnosari_manage_knowledge breaks that pattern specifically for action="create" with type omitted: the auto-resolve path makes outbound HTTP requests to an arbitrary caller-supplied site (probing for a sitemap) before any database write happens. That reaches beyond the server's own data boundary, so the annotation reflects it honestly.
Parameters โ
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
action | Literal["create", "list", "get", "update", "delete", "assign", "remove"] | Yes | - | Action to perform on knowledge sources |
name | string | null | Conditional | null | Source name โ required for create (even in auto-resolve mode; unlike the REST API, this tool never derives it from the hostname) |
url | string | null | Conditional | null | Source URL โ required for create |
type | Literal["website", "sitemap", "discovery"] | null | No | null | Content type for create. Omit it OR pass "website" for whole-site auto-resolve โ see below. "sitemap" / "discovery" select that loader explicitly and are used as-is with no probing |
single_page | boolean | No | false | create only. Opt out of whole-site ingestion: load ONLY the given URL as a single page (data_type="website"), skipping the probe. Applies to omitted/"website" type; combining it with "sitemap"/"discovery" raises a conflict error |
search | string | null | No | null | Search query (list) |
status | Literal["unloaded", "loading", "loaded", "failed"] | null | No | null | Filter by loading status (list) |
source_id | integer | null | Conditional | null | Source ID โ required for get / update / delete |
source_ids | list[integer] | null | Conditional | null | Source IDs โ required for assign / remove |
gnosari_id | integer | null | Conditional | null | Agent ID โ required for assign / remove; optional on create to create-and-assign in one call |
Returns โ
KnowledgeResult โ one of:
| Action | Return type |
|---|---|
create, get, update | KnowledgeSourceRead |
list | PaginatedKnowledgeSourcesResponse |
delete | dict[str, str] โ {"message": "Knowledge source {id} deleted"} |
assign, remove | dict[str, str | list[int]] โ {"message": str, "knowledge_source_ids": list[int]} |
The auto-resolve contract โ
When action="create" and type is omitted (or explicitly None or "website") and single_page is not set, the server calls resolve_website_source(url) โ a standalone resolver that does no database work โ before opening the transaction, since it makes outbound HTTP requests. It probes, in order, stopping at the first candidate that yields at least one page:
{url}/sitemap.xml{url}/sitemap_index.xml- Each
Sitemap:directive found inrobots.txt, in the order they appear
A candidate "yields" when its <loc> count is greater than zero (namespace-agnostic parsing, gzip-decompression supported, one level of <sitemapindex> nesting followed). The first sitemap with >0 locations wins and the source is created with data_type="sitemap", paths=[thatSitemapUrl].
If nothing yields a usable sitemap, the source falls back to data_type="discovery", paths=[normalizedUrl] โ NeoReader's Discovery API crawls the whole site starting from that URL when loading runs.
Passing type="sitemap" or type="discovery" skips the probe entirely: the URL you passed is used as-is as paths=[url]. type="website" (like omitting type) auto-resolves the whole site. To load a single page only, pass single_page=true โ that stores data_type="website" with paths=[url] and no probe.
type omitted / "website" โ resolve_website_source(url) โ sitemap (first working candidate) OR discovery (fallback)
type "sitemap" / "discovery" โ used as-is, no HTTP probe, no resolver call
single_page=true โ single-page website load, no probe
single_page=true + sitemap/disc โ conflict error (single_page is a whole-site opt-out)Invalid or blocked URLs โ
Before probing, the resolver normalizes the URL (prepends https:// if no scheme is present) and validates it isn't SSRF-blocked โ internal, loopback, link-local, or cloud-metadata hosts are rejected, as is any scheme other than http/https.
If validation fails, gnosari_manage_knowledge raises a fixed, non-enumerating error:
URL not permitted โ provide a public http(s) site URLThis message is deliberately generic. It does not say why the URL was rejected (blocked scheme vs. internal IP vs. metadata host) because a caller who could distinguish those reasons could use this tool as a blind SSRF oracle โ probing for which internal hosts exist by reading the difference in error text. The real reason is logged server-side only, via structlog under the event knowledge_source_url_rejected, for operators debugging a legitimate rejected URL. This is a deliberate anti-enumeration design decision, not a gap to "improve" by making the error more specific.
Examples โ
Auto-resolve (recommended default) โ omit type entirely:
python
result = gnosari_manage_knowledge(
action="create",
name="Product Documentation",
url="https://docs.acme.com",
gnosari_id=gnosari_id # creates AND assigns in one call
)
print(result.data_type) # "sitemap" if one was found, else "discovery"Single page only โ opt out of whole-site ingestion with single_page:
python
result = gnosari_manage_knowledge(
action="create",
name="Pricing Page",
url="https://acme.com/pricing",
single_page=True, # skip the probe, load exactly this one page
gnosari_id=gnosari_id
)
print(result.data_type) # "website"List sources filtered by loading status:
python
failed = gnosari_manage_knowledge(action="list", status="failed")
for source in failed.data:
print(f"{source.name}: {source.data_type} โ {source.paths}")Create standalone, then assign to multiple agents:
python
src = gnosari_manage_knowledge(action="create", name="Shared KB", url="https://kb.acme.com")
gnosari_manage_knowledge(action="assign", source_ids=[src.id], gnosari_id=support_bot_id)
gnosari_manage_knowledge(action="assign", source_ids=[src.id], gnosari_id=sales_bot_id)Remove and delete:
python
gnosari_manage_knowledge(action="remove", source_ids=[src.id], gnosari_id=support_bot_id)
gnosari_manage_knowledge(action="delete", source_id=src.id)Common Errors โ
| Error | Cause | Solution |
|---|---|---|
ValueError: "name is required for create" | name omitted on action="create" | name is always required, even in auto-resolve mode |
ValueError: "url is required for create when auto-resolving the whole site (omit type or use type='website'; pass single_page=true for a single page)" | url omitted while auto-resolving (type omitted or type="website", single_page not set) | Pass url, or pass type="sitemap"/"discovery" with url, or set single_page=true with url |
ValueError: "URL not permitted โ provide a public http(s) site URL" | URL is SSRF-blocked (internal/loopback/link-local/metadata host) or uses a non-http(s) scheme | Use a public, internet-reachable http(s):// URL. Check server logs (knowledge_source_url_rejected) for the specific reason if you operate the server |
ValueError: "source_id is required for get/update/delete" | Missing source_id on those actions | Pass source_id |
ValueError: "source_ids and gnosari_id are required for assign/remove" | Missing either param on assign/remove | Pass both source_ids (list) and gnosari_id |
gnosari_manage_traits (brief) โ
Create, list, get, update, delete, assign, or remove personality traits โ the behavioral shaping layer for how an agent communicates.
Annotations: readOnlyHint: false ยท destructiveHint: false ยท idempotentHint: false ยท openWorldHint: false
| Name | Type | Required | Description |
|---|---|---|---|
action | Literal["create", "list", "get", "update", "delete", "assign", "remove"] | Yes | Action to perform |
name, description, instructions | string | null | Required together for create | Trait identity and behavioral instructions |
weight | float | null | No | Influence weight 0.0โ10.0 (default 1.0) |
is_default | bool | null | No | Auto-assign to newly created agents in this account |
search | string | null | No | Search query (list) |
trait_id | integer | null | Conditional | Required for get / update / delete |
trait_ids | list[integer] | null | Conditional | Required for assign / remove |
gnosari_id | integer | null | Conditional | Required for assign / remove, or optional on create to assign immediately |
python
trait = gnosari_manage_traits(
action="create",
name="Empathetic",
description="Warm, understanding tone",
instructions="Acknowledge the user's feelings before offering solutions.",
weight=2.0,
gnosari_id=gnosari_id # creates AND assigns
)gnosari_manage_data_collection (brief) โ
Create, list, get, update, delete, assign, or remove data collection templates โ what structured data agents extract from conversations. Also toggles per-agent file-upload capability via enable_attachments.
Annotations: readOnlyHint: false ยท destructiveHint: false ยท idempotentHint: false ยท openWorldHint: false
| Name | Type | Required | Description |
|---|---|---|---|
action | Literal["create", "list", "get", "update", "delete", "assign", "remove"] | Yes | Action to perform |
name, description | string | null | Conditional | Template identity (create/update) |
fields | list[FieldInput] | null | Conditional | Field definitions (create/update) |
identity_field | string | null | No | Name of the field to use as the person's identity/display name (create/update). Must name one of this template's fields. On update, omit to leave unchanged โ see clear_fields to remove the designation |
clear_fields | list["identity_field"] | null | No | Fields to reset to null on update. Currently only "identity_field" is supported. Passing a field here AND as a value in the same call is rejected |
search | string | null | No | Search query (list) |
include_system | bool | No | Include system templates in list (default true) |
template_id | integer | null | Conditional | Required for get / update / delete / assign / remove |
gnosari_id | integer | null | Conditional | Required for assign / remove, create+assign, or enable_attachments |
enable_attachments | bool | null | No | Turn file uploads on/off for the agent's chat (requires gnosari_id); null leaves unchanged |
collection_mode | string | null | No | passive, opportunistic, active, or guided |
custom_prompt | string | null | No | Required when collection_mode="guided" (max 5000 chars) |
python
template = gnosari_manage_data_collection(
action="create",
name="Lead Info",
fields=[{"name": "email", "field_type": "email", "required": True}],
identity_field="email",
gnosari_id=gnosari_id,
collection_mode="active"
)Clear the identity designation โ revert to server heuristic resolution without touching any other field:
python
gnosari_manage_data_collection(
action="update",
template_id=template.id,
clear_fields=["identity_field"]
)Next Steps โ
- Agent Management Tools - Agent CRUD and configuration tools
- Agent Data Model -
knowledge_source_typeenum and full data model - Knowledge System - Source types, loading states, RAG concepts
- Knowledge Loading Flow - What happens after a source is created
- Discovery Tools -
listaction equivalents viagnosari_search