Commit 1d2b20ab authored by Lisa (AI Assistant)'s avatar Lisa (AI Assistant)

Add self-registration: agent sends own token, capability_prompt and skill_prompt

parent f196c0da
Pipeline #274 canceled with stages
...@@ -14,7 +14,8 @@ from pathlib import Path ...@@ -14,7 +14,8 @@ from pathlib import Path
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from typing import Optional from typing import Optional
from fastapi import FastAPI, HTTPException, Header from fastapi import FastAPI, HTTPException, Header, Body
from fastapi.responses import PlainTextResponse
import uvicorn import uvicorn
import httpx import httpx
from cryptography import x509 from cryptography import x509
...@@ -42,7 +43,7 @@ AGENTS = {} ...@@ -42,7 +43,7 @@ AGENTS = {}
async def init_db(): async def init_db():
"""Initialize database""" """Initialize database"""
async with aiosqlite.connect(DB_PATH) as db: async with aiosqlite.connect(DB_PATH) as db:
# Agents table with skill prompts # Agents table with large text fields for prompts
await db.execute(""" await db.execute("""
CREATE TABLE IF NOT EXISTS agents ( CREATE TABLE IF NOT EXISTS agents (
name TEXT PRIMARY KEY, name TEXT PRIMARY KEY,
...@@ -216,11 +217,13 @@ app = FastAPI(lifespan=lifespan) ...@@ -216,11 +217,13 @@ app = FastAPI(lifespan=lifespan)
async def list_tools(authorization: str = Header(None)): async def list_tools(authorization: str = Header(None)):
verify_token(authorization) verify_token(authorization)
return {"tools": [ return {"tools": [
{"name": "register_agent", "description": "Register an agent with its webhook URL and skill prompts", {"name": "register", "description": "Register this agent with the MCP server. Sends own token, hook URL, capability_prompt and skill_prompt",
"inputSchema": {"type": "object", "properties": { "inputSchema": {"type": "object", "properties": {
"name": {}, "hook": {}, "token": {}, "hook": {"type": "string", "description": "Your webhook URL (https://your-server/hooks/agent)"},
"capability_prompt": {}, "skill_prompt": {} "token": {"type": "string", "description": "Your OpenClaw hook token"},
}, "required": ["name", "hook"]}}, "capability_prompt": {"type": "string", "description": "Short description of what this agent can do"},
"skill_prompt": {"type": "string", "description": "Detailed instructions on how to make requests to this agent (can be large)"}
}, "required": ["hook", "token"]}},
{"name": "list_hosts", "description": "List all registered agents with their capability prompts", {"name": "list_hosts", "description": "List all registered agents with their capability prompts",
"inputSchema": {"type": "object", "properties": {}}}, "inputSchema": {"type": "object", "properties": {}}},
{"name": "get_host_info", "description": "Get detailed info about a specific agent including skill prompt", {"name": "get_host_info", "description": "Get detailed info about a specific agent including skill prompt",
...@@ -275,19 +278,37 @@ async def get_host_info(name: str, authorization: str = Header(None)): ...@@ -275,19 +278,37 @@ async def get_host_info(name: str, authorization: str = Header(None)):
} }
@app.post("/tools/register_agent") @app.post("/tools/register")
async def tool_register_agent(data: dict, authorization: str = Header(None)): async def tool_register(data: dict = Body(...), authorization: str = Header(None)):
"""Register an agent with its webhook URL and skill prompts""" """
Agent self-registration. The agent sends:
- hook: its webhook URL
- token: its own hook token for callbacks
- capability_prompt: short description of capabilities
- skill_prompt: detailed instructions (can be large)
"""
verify_token(authorization) verify_token(authorization)
name = data.get("name")
hook = data.get("hook") hook = data.get("hook")
token = data.get("token", "") token = data.get("token", "")
capability_prompt = data.get("capability_prompt", "") capability_prompt = data.get("capability_prompt", "")
skill_prompt = data.get("skill_prompt", "") skill_prompt = data.get("skill_prompt", "")
if not name or not hook: if not hook:
return {"success": False, "error": "name and hook are required"} return {"success": False, "error": "hook URL is required"}
# Extract agent name from hook URL path or use a default
# The agent identifies itself - we'll use the hook URL's hostname as name if not provided
# Or we can require a "name" field
name = data.get("name")
if not name:
# Try to extract from hook URL
import urllib.parse
try:
parsed = urllib.parse.urlparse(hook)
name = parsed.netloc.split('.')[0] # e.g., "lisa" from "lisa.nexlab.net"
except:
name = "unknown"
# Ensure HTTPS # Ensure HTTPS
if not hook.startswith("https://"): if not hook.startswith("https://"):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment