feat: intégration TikTak #197-200 + scoped tokens, webhooks filtrés, listes mtime/size ; fix e2e-server BUG-112
CI / lint (push) Successful in 3m17s
CI / security (push) Failing after 1m51s
CI / test (push) Successful in 5m14s
CI / build (push) Successful in 1m50s
CI / e2e (push) Successful in 18m18s

This commit is contained in:
2026-10-14 11:42:53 -04:00
parent 2be0b15bcf
commit f783394f09
59 changed files with 3233 additions and 77 deletions
+20
View File
@@ -43,6 +43,7 @@ from backend.auth.middleware import get_current_user, is_auth_enabled
from backend.services.files import read_file_text
from backend.services.vaults import list_accessible_vaults
from backend.tools.api import (
FILES_SCOPE_TOOLS,
ToolContext,
ToolError,
ToolMode,
@@ -191,6 +192,14 @@ def _tool_definitions() -> list[types.Tool]:
return definitions
def _mcp_base_name(mcp_name: str) -> str:
"""Strip the ``propose_``/``apply_`` prefix back to the registry tool name."""
for prefix in ("propose_", "apply_"):
if mcp_name.startswith(prefix):
return mcp_name[len(prefix):]
return mcp_name
def _parse_vault_uri(uri: str) -> tuple[str, str]:
"""Split ``vault://name/path`` into ``(vault, path)``."""
parsed = urlparse(uri)
@@ -208,6 +217,10 @@ def build_server() -> Server:
# ── Tools ──────────────────────────────────────────────────────────
@server.list_tools()
async def _list_tools() -> list[types.Tool]:
# #199 : un token à portée "files" ne voit que les outils fichiers.
user = _user_from_context(server)
if (user.get("_token_scope") or "full") == "files":
return [t for t in _tool_definitions() if _mcp_base_name(t.name) in FILES_SCOPE_TOOLS]
return _tool_definitions()
@server.call_tool()
@@ -262,6 +275,7 @@ def build_server() -> Server:
user = _user_from_context(server)
vault, path = _parse_vault_uri(str(uri))
ctx = ToolContext(user=user, mode=ToolMode.MCP)
ctx.require_tool_allowed("read_file") # #199 : portée inconnue → refus
ctx.require_vault_access(vault)
data = read_file_text(vault, path, redact=True, max_bytes=MAX_RESOURCE_BYTES)
mime = "text/markdown" if Path(path).suffix.lower() == ".md" else "text/plain"
@@ -352,6 +366,12 @@ def _propose(user: dict[str, Any], tool: str, arguments: dict[str, Any]) -> list
params = spec.input_model.model_validate(arguments)
vault = getattr(params, "vault", None)
ctx = ToolContext(user=user, mode=ToolMode.MCP)
# #199 : les tokens à portée limitée ne peuvent proposer que les outils
# fichiers (même garde que call_tool, qui couvre apply_).
try:
ctx.require_tool_allowed(tool)
except ToolError as e:
return _text(e.to_dict())
if vault and vault != "all":
ctx.require_vault_access(vault)
if spec.risk == ToolRisk.DANGEROUS: