Add multi-user auth, settings area, and unified label feed
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>
This commit is contained in:
parent
203f9b578e
commit
37cd96fa7a
33 changed files with 1614 additions and 367 deletions
145
planning/mcp_research.md
Normal file
145
planning/mcp_research.md
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# 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
|
||||
|
||||
```bash
|
||||
pip install django-mcp-server
|
||||
```
|
||||
|
||||
## 2. Configure Django settings
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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)
|
||||
|
||||
```python
|
||||
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)
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```bash
|
||||
python manage.py mcp_inspect
|
||||
```
|
||||
|
||||
## 6. Test locally
|
||||
|
||||
```python
|
||||
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](https://github.com/modelcontextprotocol/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": True` given 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-server` docs: https://github.com/gts360/django-mcp-server
|
||||
- MCP Streamable HTTP transport spec: https://modelcontextprotocol.io/specification/2025-03-26/basic/transports
|
||||
Loading…
Add table
Add a link
Reference in a new issue