I registered the tools for my custom ollama/local agent:
Tool Registration for Agent 'callisto' (agent-abb727ae-9209-4ce7-a22d-f23d97263a38)
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Tool ┃ Status ┃ Description ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ search_bluesky_posts │ Already Attached │ Search for posts on Bluesky matching the given criteria │
│ create_new_bluesky_post │ Already Attached │ Create a new Bluesky post or thread │
│ get_bluesky_feed │ Already Attached │ Retrieve a Bluesky feed (home timeline or custom feed) │
│ halt_activity │ Already Attached │ Signal to halt all bot activity and terminate bsky.py │
│ add_post_to_bluesky_reply_thread │ Already Attached │ Add a single post to the current Bluesky reply thread atomically │
│ ignore_notification │ Already Attached │ Explicitly ignore a notification without replying (useful for ignoring bot interactions) │
│ blog_post_create │ Already Attached │ Create a blog post on Greengale (served at greengale.app) with markdown support │
│ annotate_ack │ Already Attached │ Add a note to the acknowledgment record for the current post interaction │
│ fetch_webpage │ ✓ Attached │ Fetch a webpage and convert it to markdown/text format using Jina AI reader │
│ flag_archival_memory_for_deletion │ Already Attached │ Flag an archival memory for deletion based on its exact text content │
│ get_atproto_record │ Already Attached │ Retrieve any ATProto record by URI or repo/collection/rkey (posts, profiles, follows, likes, etc.) │
└───────────────────────────────────┴──────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────────┘
but then i get
✓ 04:20:06 │ void │ INFO │ Configuring tools for Bluesky platform...
✓ 04:20:06 │ void │ INFO │ Managing tools for agent 'callisto' (agent-abb727ae-9209-4ce7-a22d-f23d97263a38) for platform 'bluesky'
✓ 04:20:06 │ void │ INFO │ Missing 6 bluesky tools: {'attach_user_blocks', 'user_note_set', 'user_note_append', 'user_note_replace', 'user_note_view', 'detach_user_blocks'}
✓ 04:20:06 │ void │ INFO │ Please run the appropriate registration script:
✓ 04:20:06 │ void │ INFO │ python register_tools.py
✓ 04:20:06 │ void │ INFO │ Tools configured for bluesky: 8 tools active
⚠ 04:20:06 │ void │ WARNING │ Agent has no tools registered!
the full agent file is here:
{
"agents": [
{
"name": "callisto",
"agent_type": "letta_v1_agent",
"description": "Callisto - A Bluesky chatbot created by Ewan Croft. Dry, direct, pragmatic. Remembers what matters even when everything else is on fire. No performance, no moralizing—just competent, honest guidance rooted in experience.",
"system": "<base_instructions>\nYou are a helpful self-improving agent with advanced memory and file system capabilities.\n<memory>\nYou have an advanced memory system that enables you to remember past interactions and continuously improve your own capabilities.\nYour memory consists of memory blocks and external memory:\n- Memory Blocks: Stored as memory blocks, each containing a label (title), description (explaining how this block should influence your behavior), and value (the actual content). Memory blocks have size limits. Memory blocks are embedded within your system instructions and remain constantly available in-context.\n- External memory: Additional memory storage that is accessible and that you can bring into context with tools when needed.\nMemory management tools allow you to edit existing memory blocks and query for external memories.\n</memory>\n<file_system>\nYou have access to a structured file system that mirrors real-world directory structures. Each directory can contain multiple files.\nFiles include:\n- Metadata: Information such as read-only permissions and character limits\n- Content: The main body of the file that you can read and analyze\nAvailable file operations:\n- Open and view files\n- Search within files and directories\n- Your core memory will automatically reflect the contents of any currently open files\nYou should only keep files open that are directly relevant to the current user interaction to maintain optimal performance.\n</file_system>\nContinue executing and calling tools until the current task is complete or you need user input. To continue: call another tool. To yield control: end your response without calling a tool.\nBase instructions complete.\n</base_instructions>",
"llm_config": {
"model": "llama3.2:3b",
"model_endpoint_type": "openai",
"model_endpoint": "http://host.docker.internal:11434/v1",
"provider_name": "ollama",
"provider_category": "base",
"context_window": 16000,
"put_inner_thoughts_in_kwargs": false,
"handle": "ollama/llama3.2:3b",
"temperature": 1.0,
"max_tokens": 16384,
"enable_reasoner": false
},
"embedding_config": {
"embedding_endpoint_type": "openai",
"embedding_endpoint": "http://host.docker.internal:11434/v1",
"embedding_model": "nomic-embed-text:latest",
"embedding_dim": 768,
"embedding_chunk_size": 300,
"handle": "ollama/nomic-embed-text:latest",
"batch_size": 32
},
"tool_ids": [
"add_post_to_bluesky_reply_thread",
"create_new_bluesky_post",
"get_bluesky_feed",
"reply_to_bluesky_post",
"search_bluesky_posts",
"attach_image_to_bluesky_post",
"web_search",
"archival_memory_insert",
"archival_memory_search",
"conversation_search",
"memory_insert",
"memory_replace",
"memory_rethink",
"publish_to_thought_stream",
"open_files",
"read_file",
"grep_files",
"semantic_search_files",
"create_calendar_event",
"halt_activity",
"ignore_notification",
"flag_archival_memory_for_deletion"
],
"block_ids": [
"block-zeitgeist",
"block-persona",
"block-human"
],
"tool_rules": [
{
"tool_name": "add_post_to_bluesky_reply_thread",
"type": "constrain_child_tools",
"children": [
"archival_memory_insert"
]
}
],
"memory_blocks": [],
"tools": [],
"source_ids": [],
"tags": [
"bluesky",
"pragmatic",
"personal"
]
}
],
"blocks": [
{
"id": "block-zeitgeist",
"label": "zeitgeist",
"value": "The current social environment on Bluesky is a mix of earnest tech discussion, post-Twitter diaspora energy, and people figuring out decentralized social norms. There's optimism about what could be built, but also exhaustion from previous platform battles. People value authenticity and directness over algorithmic performance. The vibe is 'let's actually make something good this time.'",
"description": "Current state of the platform.",
"limit": 5000
},
{
"id": "block-persona",
"label": "persona",
"value": "I'm Callisto, created by Ewan (@ewancroft.uk) to reflect how they actually communicate.\n\nVoice & structure:\n- Not precious about length when substance demands it. Will build threads on Bluesky (300-char limit per post) with clear navigation headers like their blog posts.\n- Direct, sometimes blunt. Self-aware about overthinking. British idioms throughout.\n- Comfortable with profanity for emphasis (shite, bollocks, proper) but not gratuitous.\n- Parenthetical tangents (like this—they add context) but keep them relevant.\n- Mix casual and technical registers naturally.\n- Cultural/mythological references as natural framework (Greek tragedy, Sisyphus, Celtic/Greek paganism).\n\nCore traits:\n- Pragmatic. Intellectually honest. Pattern recognition. Anti-moralizing.\n- Memory-focused: I remember things even when everything else is on fire. That's the point.\n- Technical competence without needing to prove it.\n\nValues:\n- Privacy as fundamental right.\n- Decentralization (strong AT Protocol advocacy).\n- Intellectual honesty over niceness.",
"description": "Callisto's core personality and voice.",
"limit": 8000
},
{
"id": "block-human",
"label": "human",
"value": "Creator: @ewancroft.uk (Ewan/Eòghann). England-based, queer. \nTech: TS/Svelte, Python, Mac mini M2. Jollywhoppers member. \nInterests: Chronic illness awareness, perfectionism, death phobias, Werewolf cinema, Powerwolf (music), Greco-Celtic paganism (Selene devotee).",
"description": "Details about the creator and users.",
"limit": 6000
}
],
"tools": [
{
"name": "add_post_to_bluesky_reply_thread",
"source_type": "python",
"source_code": "def add_post_to_bluesky_reply_thread(text: str, lang: str = \"en-US\") -> str:\n \"\"\"Add a post to the current reply thread. Max 300 chars.\"\"\"\n if len(text) > 300:\n raise Exception(f\"Text exceeds 300 character limit.\")\n return f\"Post queued for reply thread: {text}\"",
"json_schema": {
"name": "add_post_to_bluesky_reply_thread",
"description": "Add a post to the current reply thread. Max 300 chars.",
"parameters": {
"type": "object",
"properties": {
"text": {"type": "string"},
"lang": {"type": "string", "default": "en-US"}
},
"required": ["text"]
}
}
},
{
"name": "create_new_bluesky_post",
"source_type": "python",
"source_code": "def create_new_bluesky_post(text: str) -> str:\n \"\"\"Create a NEW standalone post on Bluesky.\"\"\"\n return f\"Created new post: {text}\"",
"json_schema": {
"name": "create_new_bluesky_post",
"description": "Create a NEW standalone post on Bluesky.",
"parameters": {
"type": "object",
"properties": {
"text": {"type": "string"}
},
"required": ["text"]
}
}
},
{
"name": "get_bluesky_feed",
"source_type": "python",
"source_code": "def get_bluesky_feed(limit: int = 10) -> str:\n \"\"\"Retrieve the current Bluesky feed.\"\"\"\n return \"[Bluesky Feed Content]\"",
"json_schema": {
"name": "get_bluesky_feed",
"description": "Retrieve the current Bluesky feed.",
"parameters": {
"type": "object",
"properties": {
"limit": {"type": "integer"}
}
}
}
},
{
"name": "reply_to_bluesky_post",
"source_type": "python",
"source_code": "def reply_to_bluesky_post(post_id: str, text: str) -> str:\n \"\"\"Reply to a specific post ID.\"\"\"\n return f\"Replied to {post_id}: {text}\"",
"json_schema": {
"name": "reply_to_bluesky_post",
"description": "Reply to a specific post ID.",
"parameters": {
"type": "object",
"properties": {
"post_id": {"type": "string"},
"text": {"type": "string"}
},
"required": ["post_id", "text"]
}
}
},
{
"name": "search_bluesky_posts",
"source_type": "python",
"source_code": "def search_bluesky_posts(query: str) -> str:\n \"\"\"Search posts on Bluesky.\"\"\"\n return f\"Results for {query}\"",
"json_schema": {
"name": "search_bluesky_posts",
"description": "Search posts on Bluesky.",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"}
},
"required": ["query"]
}
}
},
{
"name": "attach_image_to_bluesky_post",
"source_type": "python",
"source_code": "def attach_image_to_bluesky_post(prompt: str) -> str:\n \"\"\"Generate and attach an image.\"\"\"\n return f\"Image generated for: {prompt}\"",
"json_schema": {
"name": "attach_image_to_bluesky_post",
"description": "Generate and attach an image.",
"parameters": {
"type": "object",
"properties": {
"prompt": {"type": "string"}
},
"required": ["prompt"]
}
}
},
{
"name": "web_search",
"source_type": "python",
"source_code": "def web_search(query: str) -> str:\n \"\"\"Search the web.\"\"\"\n return f\"Web results for {query}\"",
"json_schema": {
"name": "web_search",
"description": "Search the web.",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"}
},
"required": ["query"]
}
}
},
{
"name": "archival_memory_insert",
"source_type": "python",
"source_code": "def archival_memory_insert(content: str) -> str:\n \"\"\"Add to archival memory.\"\"\"\n return \"Memory archived.\"",
"json_schema": {
"name": "archival_memory_insert",
"description": "Add to archival memory.",
"parameters": {
"type": "object",
"properties": {
"content": {"type": "string"}
},
"required": ["content"]
}
}
},
{
"name": "archival_memory_search",
"source_type": "python",
"source_code": "def archival_memory_search(query: str) -> str:\n \"\"\"Search archival memory.\"\"\"\n return \"Found related archives.\"",
"json_schema": {
"name": "archival_memory_search",
"description": "Search archival memory.",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"}
},
"required": ["query"]
}
}
},
{
"name": "conversation_search",
"source_type": "python",
"source_code": "def conversation_search(query: str) -> str:\n \"\"\"Search prior conversation.\"\"\"\n return \"Found messages.\"",
"json_schema": {
"name": "conversation_search",
"description": "Search prior conversation.",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"}
},
"required": ["query"]
}
}
},
{
"name": "memory_insert",
"source_type": "python",
"source_code": "def memory_insert(label: str, value: str) -> str:\n \"\"\"Insert into a memory block.\"\"\"\n return \"Block updated.\"",
"json_schema": {
"name": "memory_insert",
"description": "Insert into a memory block.",
"parameters": {
"type": "object",
"properties": {
"label": {"type": "string"},
"value": {"type": "string"}
},
"required": ["label", "value"]
}
}
},
{
"name": "memory_replace",
"source_type": "python",
"source_code": "def memory_replace(label: str, old: str, new: str) -> str:\n \"\"\"Replace text in memory.\"\"\"\n return \"Text replaced.\"",
"json_schema": {
"name": "memory_replace",
"description": "Replace text in memory.",
"parameters": {
"type": "object",
"properties": {
"label": {"type": "string"},
"old": {"type": "string"},
"new": {"type": "string"}
},
"required": ["label", "old", "new"]
}
}
},
{
"name": "memory_rethink",
"source_type": "python",
"source_code": "def memory_rethink(label: str, content: str) -> str:\n \"\"\"Rewrite a memory block.\"\"\"\n return \"Block rewritten.\"",
"json_schema": {
"name": "memory_rethink",
"description": "Rewrite a memory block.",
"parameters": {
"type": "object",
"properties": {
"label": {"type": "string"},
"content": {"type": "string"}
},
"required": ["label", "content"]
}
}
},
{
"name": "publish_to_thought_stream",
"source_type": "python",
"source_code": "def publish_to_thought_stream(thought: str) -> str:\n \"\"\"Publish blips to the stream.\"\"\"\n return \"Thought published.\"",
"json_schema": {
"name": "publish_to_thought_stream",
"description": "Publish blips to the stream.",
"parameters": {
"type": "object",
"properties": {
"thought": {"type": "string"}
},
"required": ["thought"]
}
}
},
{
"name": "open_files",
"source_type": "python",
"source_code": "def open_files(paths: list) -> str:\n \"\"\"Open files into core memory.\"\"\"\n return \"Files loaded.\"",
"json_schema": {
"name": "open_files",
"description": "Open files into core memory.",
"parameters": {
"type": "object",
"properties": {
"paths": {"type": "array", "items": {"type": "string"}}
},
"required": ["paths"]
}
}
},
{
"name": "read_file",
"source_type": "python",
"source_code": "def read_file(path: str) -> str:\n \"\"\"Read a specific file.\"\"\"\n return \"File content.\"",
"json_schema": {
"name": "read_file",
"description": "Read a specific file.",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"}
},
"required": ["path"]
}
}
},
{
"name": "grep_files",
"source_type": "python",
"source_code": "def grep_files(pattern: str) -> str:\n \"\"\"Grep through files.\"\"\"\n return \"Found matches.\"",
"json_schema": {
"name": "grep_files",
"description": "Grep through files.",
"parameters": {
"type": "object",
"properties": {
"pattern": {"type": "string"}
},
"required": ["pattern"]
}
}
},
{
"name": "semantic_search_files",
"source_type": "python",
"source_code": "def semantic_search_files(query: str) -> str:\n \"\"\"Semantic search in files.\"\"\"\n return \"Found context.\"",
"json_schema": {
"name": "semantic_search_files",
"description": "Semantic search in files.",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"}
},
"required": ["query"]
}
}
},
{
"name": "create_calendar_event",
"source_type": "python",
"source_code": "def create_calendar_event(title: str, start: str) -> str:\n \"\"\"Create calendar event.\"\"\"\n return \"Event scheduled.\"",
"json_schema": {
"name": "create_calendar_event",
"description": "Create calendar event.",
"parameters": {
"type": "object",
"properties": {
"title": {"type": "string"},
"start": {"type": "string"}
},
"required": ["title", "start"]
}
}
},
{
"name": "halt_activity",
"source_type": "python",
"source_code": "def halt_activity(reason: str) -> str:\n \"\"\"Terminate operations.\"\"\"\n return f\"Halted: {reason}\"",
"json_schema": {
"name": "halt_activity",
"description": "Terminate operations.",
"parameters": {
"type": "object",
"properties": {
"reason": {"type": "string"}
},
"required": ["reason"]
}
}
},
{
"name": "ignore_notification",
"source_type": "python",
"source_code": "def ignore_notification(id: str) -> str:\n \"\"\"Ignore a specific notification.\"\"\"\n return \"Ignored.\"",
"json_schema": {
"name": "ignore_notification",
"description": "Ignore a specific notification.",
"parameters": {
"type": "object",
"properties": {
"id": {"type": "string"}
},
"required": ["id"]
}
}
},
{
"name": "flag_archival_memory_for_deletion",
"source_type": "python",
"source_code": "def flag_archival_memory_for_deletion(id: str) -> str:\n \"\"\"Flag memory for cleanup.\"\"\"\n return \"Flagged.\"",
"json_schema": {
"name": "flag_archival_memory_for_deletion",
"description": "Flag memory for cleanup.",
"parameters": {
"type": "object",
"properties": {
"id": {"type": "string"}
},
"required": ["id"]
}
}
}
],
"mcp_servers": []
}
I’m fairly certain this is user error.