Knowledge Bases & No-Code
Notion, Confluence, and Airtable toolsets.
#Knowledge bases & no-code
Wiki connectors (Notion, Confluence) and the Airtable no-code database share enough of a workflow shape — read pages / records, write pages / records, attach metadata — that they're grouped together here. Every connector follows the agent-ready toolset pattern (compact summaries by default, opt-in raw IDs, tolerant write inputs).
#NotionToolSet
NotionToolSet wraps the Notion API. It uses an integration secret or OAuth bearer token plus a Notion-Version header.
from maivn_tools import NotionToolSet, register_connector
connector = NotionToolSet(token=secrets["NOTION_TOKEN"])
register_connector(agent, connector)#Tools
#Agent-ready behavior
searchandquery_databasereturn compact summaries by default. Each summary carries a stable ref —page_ref(page_1, ...) for page objects,result_reffor non-page hits — plus theobjecttype (page/database),title,url,last_edited_time,archived, andparent_type(when present).- Default
page_sizeis 25 on bothsearchandquery_database(max 100).list_usersandlist_block_childrenretain their Notion-native 100 default. - Notion
idGUIDs are hidden by default. Passinclude_ids=Trueto addpage_idto each summary — needed before callingget_page,update_page,archive_page, orget_page_property. - Pass
include_metadata=Falseonsearchorquery_databaseto receive the raw Notion response, includinghas_moreandnext_cursorpagination cursors. get_page,get_page_property,update_page, andarchive_pageaccept either a raw Notion ID string, a summary dict fromsearchorquery_database, or a list of such dicts. The connector resolves the ID frompage_idorid.
#Destructive tools
archive_page and delete_block carry destructive=True. Both move content to Notion's trash. Confirm with the user, or filter them out with exclude_tags=["destructive"].
#Example: summary-mode search
agent.add_toolset(NotionToolSet(token=secrets["NOTION_TOKEN"]))
response = connector.search("runbook", filter_type="page", page_size=10)
# response == {
# "results": [
# {"page_ref": "page_1", "object": "page",
# "title": "On-call runbook", "url": "https://www.notion.so/...",
# "last_edited_time": "...", "archived": False,
# "parent_type": "database_id"},
# ...
# ],
# "has_more": False, "next_cursor": None,
# }To act on a result, request raw IDs:
hits = connector.search(
"Q3 plan", filter_type="page", include_ids=True
)["results"]
connector.update_page(hits[0], archived=True)#ConfluenceToolSet
ConfluenceToolSet wraps Atlassian Confluence Cloud REST API v1.
from maivn_tools import ConfluenceToolSet
connector = ConfluenceToolSet(
base_url="https://acme.atlassian.net",
email="ops@acme.com",
api_token=secrets["CONFLUENCE_API_TOKEN"],
)#Tools
#Agent-ready behavior
list_content,search, andsearch_contentreturn compact summaries by default. Each summary carries a stablepage_ref(page_1,page_2, ...) plustitle,type,status,space_key,version, and theurl(webui link). Search results additionally include theexcerptandresultGlobalContainerfields when present.- Default
limitis 25 onlist_content,search, andsearch_content.list_spacesdefaults to 25 (max 250). - Confluence numeric content IDs are hidden by default. Pass
include_ids=Trueto addcontent_idto each summary — needed before callingget_content,update_page,delete_content,list_children,list_versions,list_labels,add_label,remove_label,list_comments, orlist_attachments. - Pass
include_metadata=Falseonlist_content,search, orsearch_contentto receive the raw Confluence response, includingstart,limit,size,totalSize, and_linkscursors. - All of the per-content tools above accept either a raw numeric ID string, a summary dict from
list_content/search/search_content/get_content, or a list of such dicts. The connector resolves the ID fromcontent_id,page_id, orid. - Space tools use the human-readable
key(e.g.ENG) — this is the canonical handle and is safe to surface in final answers.
#Destructive tools
delete_space, delete_content, and remove_label carry destructive=True. delete_space schedules a long-running deletion of the space and all its content; delete_content trashes a page (call again to purge). Confirm with the user, or filter them out with exclude_tags=["destructive"].
#Example: summary-mode search
agent.add_toolset(
ConfluenceToolSet(base_url="https://acme.atlassian.net",
email="ops@acme.com",
api_token=secrets["CONFLUENCE_API_TOKEN"])
)
response = connector.search(
"space = ENG AND text ~ 'runbook' ORDER BY lastmodified DESC",
limit=10,
)
# response == {
# "results": [
# {"page_ref": "page_1", "title": "On-call runbook",
# "type": "page", "status": "current", "space_key": "ENG",
# "version": 12, "url": "/spaces/ENG/pages/...",
# "excerpt": "...", "resultGlobalContainer": "Engineering"},
# ...
# ],
# "start": 0, "limit": 10, "size": 4, "totalSize": 4,
# }To follow up with an update, request raw IDs:
hits = connector.search(
"title = 'On-call runbook'", include_ids=True
)["results"]
page = connector.get_content(hits[0], expand="body.storage,version")
connector.update_page(
hits[0],
title=page["title"],
body=page["body"]["storage"]["value"] + "<p>Updated.</p>",
version=page["version"]["number"] + 1,
)#AirtableToolSet
AirtableToolSet wraps the Airtable Web + Meta APIs. It accepts a personal access token (pat...) or an OAuth bearer.
from maivn_tools import AirtableToolSet
connector = AirtableToolSet(access_token=secrets["AIRTABLE_PAT"])#Tools
#Agent-ready behavior
list_recordsreturns compact summaries by default. Each summary carries a response-localrecord_ref(record_1,record_2, ...), the original Airtablefieldsdict for the row, and thecreated_time.- Default
page_sizeis 25 onlist_records.list_commentsand webhook helpers retain their Airtable-native 100 default. - Raw Airtable record IDs (
rec...) are hidden by default. Passinclude_ids=Trueto addrecord_idto each summary — needed before callingupdate_records,delete_records,list_comments,create_comment, ordelete_comment. - Pass
include_metadata=Falseto receive the raw Airtable payload (records with rawidplusoffsetpagination cursor). delete_recordsaccepts a list whose items can be raw Airtable IDs ("rec..."), full record dicts fromlist_records(include_ids=True) orget_record, or a mixed list. The connector resolves each entry fromrecord_idorid.
#Destructive tools
delete_records, delete_comment, and delete_webhook carry destructive=True. delete_records cannot be undone; the others remove the underlying resource. Confirm with the user, or filter them out with exclude_tags=["destructive"].
#Example: summary-mode list
agent.add_toolset(AirtableToolSet(access_token=secrets["AIRTABLE_PAT"]))
response = connector.list_records(
base_id="appXXXXXXXXXXXXXX",
table_id_or_name="Customers",
filter_by_formula="{Status} = 'Active'",
page_size=10,
)
# response == {
# "records": [
# {"record_ref": "record_1",
# "fields": {"Name": "ACME", "Status": "Active", "MRR": 4500},
# "created_time": "..."},
# ...
# ],
# "offset": "...", # only when more results exist
# }To delete matching rows, request raw IDs and pipe them straight through:
hits = connector.list_records(
base_id="appXXXXXXXXXXXXXX",
table_id_or_name="Customers",
filter_by_formula="{Status} = 'Cancelled'",
include_ids=True,
)["records"]
connector.delete_records(
base_id="appXXXXXXXXXXXXXX",
table_id_or_name="Customers",
record_ids=hits[:10], # dicts accepted; the connector resolves IDs
)Airtable enforces a max of 10 records per write call; the connector raises ValueError before the API does. `table_id_or_name accepts either the stable tbl...` ID or the human-readable table name (the connector URL-encodes either form).
See Permissions and dry-run for the permission model shared by all toolsets.