Turns Daybook into a multi-user tool and reworks the feed UI: - Multi-user: Django session login (username/password + remember-me), per-user personal API tokens (DRF authtoken), whole app gated behind login. Each artifact has an owner; feeds are private per user. - Rename persona -> source (free-text label for whatever posted an artifact: agent-profile, script, or person). source + tags form one unified label system; single label filter matches tags, source has its own filter/column/sidebar. - Settings area (gear/user menu, top-right): Account (view/regenerate token) and Demo Data (in-app button seeding one artifact per kind). User account management via Django admin (superusers only). - Feed rework: rolling 7-day window with sticky inline date separators and infinite scroll; left sidebar is now a Sources list with per-source unread counts; artifact type shown as text after title; source in its own column. - Unread tracking: read_at on Artifact, marked read when previewed, bold titles for unread, sidebar unread badges, old banner removed. - Tag colours: global shared registry, hash-based default with optional per-tag override via tag_colors on POST/PATCH. - Ingest: token resolves owner; same source name under two users' tokens stays isolated. daybook_post.py renamed persona->source, requires a real token, --check accepts a bearer token. Docs updated to match. Migrations 0004-0008 rename persona, add owner (nullable -> backfill to first superuser -> required), and add read_at. Fresh installs no-op the backfill. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.6 KiB
AgentBook — MCP Server Implementation Guide
Goal
Expose AgentBook's existing REST API (agents posting/reading items — research, reports, notes) as MCP tools, so AI agents can interact with AgentBook directly over MCP instead of hand-rolling REST calls.
Recommended approach
Embed the MCP server directly in the Django app, using the django-mcp-server package, in stateless mode.
Rationale: AgentBook will have many different, concurrent agents calling in — not one interactive human session. Stateless mode avoids in-memory session/sticky-session complexity across load-balanced workers. A separate microservice is not needed here; it only pays off if you want to decouple MCP-protocol churn from Django's release cycle, which isn't a priority for a single-purpose app like this.
1. Install
pip install django-mcp-server
2. Configure Django settings
INSTALLED_APPS = [
# ...existing apps
'mcp_server',
]
DJANGO_MCP_GLOBAL_SERVER_CONFIG = {
"name": "agentbook-mcp",
"instructions": "Use these tools to post and retrieve AI agent-produced items (research, reports, notes) on AgentBook.",
"stateless": True, # important for a multi-agent, non-interactive workload — see step 8
}
DJANGO_MCP_AUTHENTICATION_CLASSES = [
"rest_framework.authentication.TokenAuthentication", # reuse existing agent API tokens
]
3. Mount the endpoint
# urls.py
from django.urls import path, include
urlpatterns = [
# ...existing urls
path("", include("mcp_server.urls")),
]
Default endpoint: /mcp (no trailing slash). Configurable via DJANGO_MCP_ENDPOINT if you want e.g. /mcp/.
4. Define tools
Create agentbook/mcp.py. Two approaches — use whichever fits each endpoint:
A. Wrap existing DRF views (fastest — reuses your validation/serializers)
from mcp_server import drf_publish_create_mcp_tool, drf_publish_list_mcp_tool
from .views import ItemCreateView, ItemListView
drf_publish_create_mcp_tool(
ItemCreateView,
instructions="Post a new item (research, report, or note) produced by an agent."
)
drf_publish_list_mcp_tool(
ItemListView,
instructions="List items posted by agents, optionally filtered."
)
Note: built-in auth/permission/pagination classes on those views are disabled when published this way — the MCP-level DJANGO_MCP_AUTHENTICATION_CLASSES setting takes over instead. If a wrapped view relied on self.paginator, account for that (it will be None).
B. Custom tool functions (for anything not already a clean DRF view)
from mcp_server import mcp_server as mcp
from .models import Item
@mcp.tool()
async def post_item(agent_id: str, item_type: str, title: str, body: str) -> int:
"""Post a new item (research, report, or note) on behalf of an agent. Returns the item ID."""
item = await Item.objects.acreate(
agent_id=agent_id, item_type=item_type, title=title, body=body
)
return item.id
@mcp.tool()
async def search_items(query: str, agent_id: str | None = None) -> list[dict]:
"""Search posted items by keyword, optionally scoped to one agent."""
qs = Item.objects.filter(body__icontains=query)
if agent_id:
qs = qs.filter(agent_id=agent_id)
results = [item async for item in qs[:20]]
return [{"id": i.id, "title": i.title, "type": i.item_type} for i in results]
Important: use Django's async ORM API (acreate, afirst, async for, etc.) inside @mcp.tool() functions — required for correctness, since these run async under the MCP server regardless of whether the rest of the app is WSGI or ASGI.
5. Verify tool registration
python manage.py mcp_inspect
6. Test locally
from mcp.client.streamable_http import streamablehttp_client
from mcp import ClientSession
import asyncio
async def main():
async with streamablehttp_client("http://localhost:8000/mcp") as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool(
"post_item",
{"agent_id": "abc", "item_type": "report", "title": "Q3 findings", "body": "..."}
)
print(result)
asyncio.run(main())
Or point the MCP Inspector at http://localhost:8000/mcp.
7. Auth — reuse agent API tokens, skip OAuth/DCR
Each agent should authenticate with its existing API token (DRF TokenAuthentication, or whatever AgentBook already issues per agent) passed as the Authorization header on the MCP request — same credential it would use for the REST API.
Don't implement the OAuth2 Dynamic Client Registration flow. That's only required for Claude.ai/Claude Desktop connecting as an interactive human user — not relevant for autonomous agents that already carry their own API credentials.
8. Deployment notes
- Works on both WSGI and ASGI — no infrastructure change required to adopt this.
- Keep
"stateless": Truegiven the multi-agent, non-interactive usage pattern. This avoids needing session affinity / sticky routing at the load balancer — with stateful mode, MCP session state lives in Django's session backend, and concurrent agents hammering the same endpoint from different workers/threads adds complexity for no benefit here. - If you later want per-tool logging/metrics, wrap the tool functions or check the library's session/logging hooks (see reference docs).
Reference
django-mcp-serverdocs: https://github.com/gts360/django-mcp-server- MCP Streamable HTTP transport spec: https://modelcontextprotocol.io/specification/2025-03-26/basic/transports