Skip to content
This repository was archived by the owner on May 13, 2026. It is now read-only.
This repository was archived by the owner on May 13, 2026. It is now read-only.

@claude there should be an mcp server for the app so it can be controlled by the ai agent within the app's own ai chat #14

Description

@steezeburger

⏺ MCP Server Implementation Prompt for Brainspread Django App

Overview

Build a Model Context Protocol (MCP) server as a Django app within the Brainspread
project. The server will enable full agentic control of the hierarchical block-based
knowledge management system, allowing AI chat to create, move, edit, and organize
blocks and pages programmatically while leveraging existing Django infrastructure.

Architecture Understanding

Brainspread uses:

  • Pages: Containers for blocks (daily notes, regular pages, templates)
  • Blocks: Hierarchical content units with parent/child relationships
  • Tags: Hashtag-based organization (#tag)
  • Links: Page references ([[Page Title]]) and block references (((block-uuid)))
  • Properties: Key-value metadata (key:: value)
  • Commands Pattern: All business logic in command classes

Django App Structure

Create new Django app at packages/django-app/app/mcp_server/:

mcp_server/
├── init.py
├── apps.py
├── server.py # Main MCP server implementation
├── tools/ # MCP tool implementations
│ ├── init.py
│ ├── page_tools.py # Page management tools
│ ├── block_tools.py # Block manipulation tools
│ ├── content_tools.py # Search, tags, backlinks
│ └── batch_tools.py # Bulk operations
├── utils/ # MCP-specific utilities
│ ├── init.py
│ ├── auth.py # User context handling
│ ├── serializers.py # Data formatting
│ └── validators.py # Input validation
├── management/ # Django commands
│ └── commands/
│ └── run_mcp_server.py
└── tests/ # MCP server tests
├── init.py
├── test_page_tools.py
├── test_block_tools.py
└── test_integration.py

Command Integration

The MCP server will directly use existing Commands with their updated signatures:

Command Signature Reference:

Updated command signatures (from system reminders)

CreateBlockCommand(user, page, content="", content_type="text", block_type="bullet",
order=0, parent=None, media_url="", media_metadata=None, properties=None)
CreatePageCommand(form, user)
UpdateBlockCommand(user, block_id, **updates)
ToggleBlockTodoCommand(user, block_id)
GetTagContentCommand(user, tag_name)
GetHistoricalDataCommand(form) # Still uses form

Required MCP Tools

  1. Page Management Tools

brainspread_create_page

  • Purpose: Create new pages or daily notes

  • Parameters:

    • title (string): Page title
    • page_type (enum): "page", "daily", "template"
    • date (string, optional): For daily notes (YYYY-MM-DD)
    • content (string, optional): Initial page content
  • Implementation:
    @server.call_tool()
    async def brainspread_create_page(title: str, page_type: str = "page", date: str =
    None, content: str = "") -> dict:
    try:
    # Create form data
    form_data = {"title": title, "content": content, "page_type": page_type}
    if date:
    form_data["date"] = date

      form = CreatePageForm(form_data)
      command = CreatePageCommand(form, current_user)
      page = command.execute()
      return {"success": True, "page_uuid": str(page.uuid), "slug": page.slug}
    

    except Exception as e:
    return {"success": False, "error": str(e)}

brainspread_get_page

  • Purpose: Retrieve page with all nested blocks
  • Parameters:
    • page_slug (string, optional): Page slug
    • page_uuid (string, optional): Page UUID
    • date (string, optional): For daily notes
  • Returns: Full page structure with nested blocks
  • Implementation: Use PageRepository.get_by_slug() or daily note methods

brainspread_update_page

  • Purpose: Update page metadata and content
  • Parameters:
    • page_uuid (string): Page identifier
    • title (string, optional): New title
    • content (string, optional): New content
  • Implementation: Use UpdatePageCommand with form

brainspread_list_pages

  • Purpose: List user's pages with filtering
  • Parameters:
    • page_type (enum, optional): Filter by type
    • limit (integer, default 50): Max results
    • search (string, optional): Search term
  • Returns: List of pages with metadata
  • Implementation: Use GetUserPagesCommand with form
  1. Block Management Tools

brainspread_create_block

  • Purpose: Create new blocks with hierarchy support

  • Parameters:

    • page_uuid (string): Target page
    • content (string): Block content
    • parent_uuid (string, optional): Parent block for nesting
    • block_type (enum, optional): "bullet", "todo", "done", "heading", "quote", "code",
      "divider"
    • content_type (enum, optional): "text", "markdown", "code", etc.
    • order (integer, optional): Position within parent/page
  • Implementation:
    @server.call_tool()
    async def brainspread_create_block(page_uuid: str, content: str, parent_uuid: str =
    None, block_type: str = "bullet", content_type: str = "text", order: int = 0) -> dict:
    try:
    # Get page object
    page = Page.objects.get(uuid=page_uuid, user=current_user)

      # Get parent if specified
      parent = None
      if parent_uuid:
          parent = Block.objects.get(uuid=parent_uuid, user=current_user, page=page)
    
      # Use command directly with new signature
      command = CreateBlockCommand(
          user=current_user,
          page=page,
          content=content,
          content_type=content_type,
          block_type=block_type,
          order=order,
          parent=parent
      )
      block = command.execute()
      return {"success": True, "block_uuid": str(block.uuid)}
    

    except Exception as e:
    return {"success": False, "error": str(e)}

brainspread_update_block

  • Purpose: Edit block content and properties
  • Parameters:
    • block_uuid (string): Block identifier
    • content (string, optional): New content
    • block_type (enum, optional): New block type
    • properties (object, optional): Key-value properties
  • Implementation:
    @server.call_tool()
    async def brainspread_update_block(block_uuid: str, **updates) -> dict:
    try:
    command = UpdateBlockCommand(current_user, block_uuid, **updates)
    block = command.execute()
    return {"success": True, "block_uuid": str(block.uuid)}
    except Exception as e:
    return {"success": False, "error": str(e)}

brainspread_move_block

  • Purpose: Move blocks between pages or change hierarchy
  • Parameters:
    • block_uuid (string): Block to move
    • target_page_uuid (string, optional): New page
    • new_parent_uuid (string, optional): New parent (null for top-level)
    • new_order (integer, optional): Position in new location
  • Implementation: Use UpdateBlockCommand with page and parent_id updates

brainspread_delete_block

  • Purpose: Delete blocks and their children
  • Parameters:
    • block_uuid (string): Block to delete
  • Implementation: Use DeleteBlockCommand(user, block_id)

brainspread_toggle_todo

  • Purpose: Toggle block between todo/done states
  • Parameters:
    • block_uuid (string): Block identifier
  • Implementation:
    @server.call_tool()
    async def brainspread_toggle_todo(block_uuid: str) -> dict:
    try:
    command = ToggleBlockTodoCommand(current_user, block_uuid)
    block = command.execute()
    return {"success": True, "block_uuid": str(block.uuid), "new_type":
    block.block_type}
    except Exception as e:
    return {"success": False, "error": str(e)}
  1. Content Organization Tools

brainspread_search_content

  • Purpose: Search across pages and blocks
  • Parameters:
    • query (string): Search term
    • content_type (enum, optional): Filter by content type
    • tags (array, optional): Filter by tags
    • date_range (object, optional): {start: "YYYY-MM-DD", end: "YYYY-MM-DD"}
  • Returns: Matching pages and blocks with context
  • Implementation: Use repository search methods and queryset filtering

brainspread_get_tag_content

  • Purpose: Retrieve all content with specific tags
  • Parameters:
    • tag_name (string): Tag to search for
    • limit (integer, default 50): Max results
  • Returns: Tagged pages and blocks
  • Implementation:
    @server.call_tool()
    async def brainspread_get_tag_content(tag_name: str, limit: int = 50) -> dict:
    try:
    command = GetTagContentCommand(current_user, tag_name)
    result = command.execute()
    return {"success": True, "data": result}
    except Exception as e:
    return {"success": False, "error": str(e)}

brainspread_get_backlinks

  • Purpose: Find all references to a page or block
  • Parameters:
    • target_uuid (string): Page or block UUID
    • type (enum): "page" or "block"
  • Returns: List of linking blocks and pages
  • Implementation: Use model get_backlinks() methods
  1. Historical and Date-based Tools

brainspread_get_daily_note

  • Purpose: Get or create daily note for specific date
  • Parameters:
    • date (string): Date in YYYY-MM-DD format
    • create_if_missing (boolean, default true): Auto-create if not exists
  • Returns: Daily note page with blocks
  • Implementation: Use PageRepository daily note methods

brainspread_get_historical_data

  • Purpose: Retrieve content from specific time periods
  • Parameters:
    • days_back (integer, default 30): Days to look back
    • limit (integer, default 50): Max results
  • Returns: Historical pages and blocks
  • Implementation:
    @server.call_tool()
    async def brainspread_get_historical_data(days_back: int = 30, limit: int = 50) ->
    dict:
    try:
    form_data = {"user": current_user.pk, "days_back": days_back, "limit": limit}
    form = GetHistoricalDataForm(form_data)
    command = GetHistoricalDataCommand(form)
    result = command.execute()
    return {"success": True, "data": result}
    except Exception as e:
    return {"success": False, "error": str(e)}

brainspread_move_blocks_by_date

  • Purpose: Move blocks from one date to another (key use case)
  • Parameters:
    • source_date (string): Source date (YYYY-MM-DD)
    • target_date (string): Target date (YYYY-MM-DD)
    • block_uuids (array): Specific blocks to move
    • preserve_hierarchy (boolean, default true): Keep parent/child relationships
  • Implementation: Complex operation using multiple UpdateBlockCommand calls
  1. Batch Operations Tools

brainspread_bulk_create_blocks

  • Purpose: Create multiple blocks efficiently (for TODO lists)
  • Parameters:
    • page_uuid (string): Target page
    • blocks (array): Array of block definitions with content, type, hierarchy
  • Implementation: Multiple CreateBlockCommand calls in transaction

brainspread_bulk_move_blocks

  • Purpose: Move multiple blocks while preserving relationships
  • Parameters:
    • operations (array): Array of move operations
    • preserve_hierarchy (boolean, default true): Maintain parent/child links
  • Implementation: Coordinated UpdateBlockCommand calls

Django App Implementation

  1. App Configuration (apps.py)

from django.apps import AppConfig

class McpServerConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'mcp_server'
verbose_name = 'MCP Server'

  1. Main Server (server.py)

import asyncio
from mcp import Server
from mcp.server.models import InitializationOptions
from mcp.server.stdio import stdio_server
from mcp.types import TextContent, Tool

from django.contrib.auth import get_user_model
from django.core.management import setup_environ
from django.conf import settings

from .tools.page_tools import PageTools
from .tools.block_tools import BlockTools
from .tools.content_tools import ContentTools
from .tools.batch_tools import BatchTools
from .utils.auth import get_current_user

User = get_user_model()

class BrainspreadMCPServer:
def init(self):
self.server = Server("brainspread")
self.setup_tools()

  def setup_tools(self):
      # Register all tool categories
      page_tools = PageTools(self.server)
      block_tools = BlockTools(self.server)
      content_tools = ContentTools(self.server)
      batch_tools = BatchTools(self.server)

      # Register tools with server
      page_tools.register_tools()
      block_tools.register_tools()
      content_tools.register_tools()
      batch_tools.register_tools()

  async def run(self):
      async with stdio_server() as (read_stream, write_stream):
          await self.server.run(
              read_stream,
              write_stream,
              InitializationOptions(
                  server_name="brainspread",
                  server_version="1.0.0",
                  capabilities=self.server.get_capabilities(
                      notification_options=None,
                      experimental_capabilities=None,
                  ),
              ),
          )
  1. Management Command (management/commands/run_mcp_server.py)

from django.core.management.base import BaseCommand
from mcp_server.server import BrainspreadMCPServer
import asyncio

class Command(BaseCommand):
help = 'Run the Brainspread MCP server'

  def handle(self, *args, **options):
      server = BrainspreadMCPServer()
      asyncio.run(server.run())
  1. User Authentication (utils/auth.py)

from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist

User = get_user_model()

For now, use a simple approach - in production you'd want proper auth

def get_current_user():
"""Get the current user for MCP operations"""
# This could be enhanced to support multiple users via session/token
try:
return User.objects.get(email="admin@email.com") # Default admin user
except ObjectDoesNotExist:
raise Exception("Default user not found. Please create admin user first.")

Future enhancement: support user switching

def set_current_user(user_id: str):
"""Switch to different user context"""
pass

Integration with Django Project

  1. Add to INSTALLED_APPS in settings.py:

INSTALLED_APPS = [
# ... existing apps
'mcp_server',
]

  1. Run MCP Server:

just django-admin run_mcp_server

  1. Connect from Claude Code:

Add MCP server configuration to connect to the running Django MCP server.

Key Use Cases to Support

  1. Daily TODO Management

AI: "Move all incomplete TODOs from yesterday to today"
Tools: brainspread_get_daily_note(yesterday) → brainspread_bulk_move_blocks →
brainspread_get_daily_note(today)

  1. Content Reorganization

AI: "Create a 'Project Planning' page and move all blocks tagged #project there"
Tools: brainspread_create_page → brainspread_get_tag_content →
brainspread_bulk_move_blocks

  1. Nested Structure Creation

AI: "Create a nested outline for the meeting notes with action items"
Tools: brainspread_bulk_create_blocks with hierarchical structure

Implementation Requirements

  1. Error Handling
  • Validate UUIDs and object existence
  • Handle circular reference prevention
  • Provide clear error messages
  • Handle concurrent modification conflicts
  1. Transaction Management
  • Wrap complex operations in database transactions
  • Ensure consistency for multi-block operations
  • Rollback on validation failures
  1. Data Validation
  • Validate block hierarchy (no circular references)
  • Respect content type constraints
  • Enforce user permissions and data isolation
  1. Performance Considerations
  • Use select_related/prefetch_related for nested queries
  • Implement pagination for large result sets
  • Cache frequently accessed data
  1. Testing Strategy
  • Unit tests for each MCP tool
  • Integration tests for complex workflows
  • Test user isolation and permissions
  • Test concurrent access scenarios

This Django app approach provides seamless integration with existing Brainspread
infrastructure while enabling powerful agentic control through the MCP protocol.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions