feat: write API + session protocol
This commit is contained in:
Binary file not shown.
+26
-1
@@ -2,7 +2,7 @@ import json
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from app.auth import get_current_agent
|
||||
from app.scopes import has_scope_access, ALL_SCOPES
|
||||
from app.models import get_rule, get_all_rules, get_all_ports, add_audit_log
|
||||
from app.models import get_rule, get_all_rules, get_all_ports, add_audit_log, set_rule
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
@@ -48,3 +48,28 @@ async def search_rules(q: str, request: Request, agent: str = Depends(get_curren
|
||||
results[scope] = content
|
||||
|
||||
return results
|
||||
|
||||
@router.patch("/rules/{scope}")
|
||||
async def patch_scope_rules(scope: str, request: Request, agent: str = Depends(get_current_agent)):
|
||||
if not has_scope_access(agent, scope):
|
||||
await log_audit(request, agent, f"{scope} (WRITE FORBIDDEN)")
|
||||
raise HTTPException(status_code=403, detail="Forbidden")
|
||||
body = await request.json()
|
||||
if not isinstance(body, dict):
|
||||
raise HTTPException(status_code=400, detail="Body must be a JSON object")
|
||||
existing = await get_rule(scope) or {}
|
||||
existing.update(body)
|
||||
await set_rule(scope, existing)
|
||||
await log_audit(request, agent, f"{scope} (WRITE)")
|
||||
return {"status": "ok", "scope": scope, "updated_keys": list(body.keys())}
|
||||
|
||||
@router.get("/context")
|
||||
async def get_agent_context(request: Request, agent: str = Depends(get_current_agent)):
|
||||
await log_audit(request, agent, "context (READ ALL)")
|
||||
all_rules = await get_all_rules()
|
||||
result = {}
|
||||
for scope in ALL_SCOPES:
|
||||
if has_scope_access(agent, scope):
|
||||
result[scope] = all_rules.get(scope, {})
|
||||
ports = await get_all_ports()
|
||||
return {"agent": agent, "scopes": result, "ports": ports}
|
||||
|
||||
+41
-1
@@ -7,7 +7,7 @@ from mcp.server.sse import SseServerTransport
|
||||
from mcp.types import Tool, TextContent
|
||||
from app.auth import get_current_agent
|
||||
from app.scopes import has_scope_access, ALL_SCOPES
|
||||
from app.models import get_rule, get_all_rules, get_all_ports, get_port
|
||||
from app.models import get_rule, get_all_rules, get_all_ports, get_port, set_rule
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -68,6 +68,24 @@ async def handle_list_tools() -> list[Tool]:
|
||||
},
|
||||
"required": ["port"]
|
||||
}
|
||||
),
|
||||
Tool(
|
||||
name="get_my_context",
|
||||
description="Get all scopes authorized for this agent — call at START of session",
|
||||
inputSchema={"type": "object", "properties": {"agent": {"type": "string"}}, "required": ["agent"]}
|
||||
),
|
||||
Tool(
|
||||
name="update_rule",
|
||||
description="Merge-update a scope with new key-value data — call at END of session",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scope": {"type": "string", "description": "Scope to update (infra/llm/nyora/perso/tt)"},
|
||||
"agent": {"type": "string", "description": "Agent name for scope access check"},
|
||||
"data": {"type": "object", "description": "Key-value pairs to merge into the scope"}
|
||||
},
|
||||
"required": ["scope", "agent", "data"]
|
||||
}
|
||||
)
|
||||
]
|
||||
|
||||
@@ -124,6 +142,28 @@ async def handle_call_tool(name: str, arguments: dict) -> list[TextContent]:
|
||||
else:
|
||||
return [TextContent(type="text", text=json.dumps({"status": "free", "port": port}))]
|
||||
|
||||
elif name == "get_my_context":
|
||||
agent_name = arguments.get("agent", "")
|
||||
all_rules = await get_all_rules()
|
||||
result = {}
|
||||
for scope in ALL_SCOPES:
|
||||
if has_scope_access(agent_name, scope):
|
||||
result[scope] = all_rules.get(scope, {})
|
||||
return [TextContent(type="text", text=json.dumps({"agent": agent_name, "scopes": result}))]
|
||||
|
||||
elif name == "update_rule":
|
||||
scope = arguments.get("scope")
|
||||
agent_name = arguments.get("agent", "")
|
||||
data = arguments.get("data", {})
|
||||
if not has_scope_access(agent_name, scope):
|
||||
return [TextContent(type="text", text=json.dumps({"error": f"Agent {agent_name} forbidden on scope {scope}"}))]
|
||||
if not isinstance(data, dict):
|
||||
return [TextContent(type="text", text=json.dumps({"error": "data must be a JSON object"}))]
|
||||
existing = await get_rule(scope) or {}
|
||||
existing.update(data)
|
||||
await set_rule(scope, existing)
|
||||
return [TextContent(type="text", text=json.dumps({"status": "ok", "scope": scope, "updated_keys": list(data.keys())}))]
|
||||
|
||||
return [TextContent(type="text", text=json.dumps({"error": "Unknown tool"}))]
|
||||
|
||||
# FastMCP / SSE Integration
|
||||
|
||||
Reference in New Issue
Block a user