diff options
| author | tanaka takashi <isazaka@gmail.com> | 2026-06-27 01:09:00 +0900 |
|---|---|---|
| committer | tanaka takashi <isazaka@gmail.com> | 2026-06-27 01:09:00 +0900 |
| commit | fce9dc1b2940ee6699d2acf0c9e56456d9899581 (patch) | |
| tree | 6bb0cfd1b220b7a37f77d55348803dd46ea30e45 /src/ollama_web/llm.py | |
| parent | 3a9e8e18df4c596360da54000f65e1bebc6039c2 (diff) | |
MCP server機能をセキュア化
Diffstat (limited to 'src/ollama_web/llm.py')
| -rw-r--r-- | src/ollama_web/llm.py | 34 |
1 files changed, 26 insertions, 8 deletions
diff --git a/src/ollama_web/llm.py b/src/ollama_web/llm.py index 0ab00a3..11e93d2 100644 --- a/src/ollama_web/llm.py +++ b/src/ollama_web/llm.py @@ -11,6 +11,7 @@ import ollama from ollama import Message from .config import settings +from .mcp import is_dangerous_mcp_tool, redact_secrets from .tools.registry import ToolRegistry, default_registry # Maximum successive tool-call rounds before we force a final answer. @@ -28,6 +29,25 @@ MAX_TOOL_CONTEXT_CHARS = 24000 ToolCallable = Callable[..., Any] +def _safe_tool_arguments(arguments: Any) -> Any: + return redact_secrets(arguments) + + +def _safe_tool_result(result: str) -> str: + redacted = redact_secrets(result) + return str(redacted) + + +def _execute_tool_with_policy( + reg: ToolRegistry, + name: str, + args: Any, +) -> str: + if is_dangerous_mcp_tool(name): + return json.dumps({"error": "MCP tool requires explicit approval"}, ensure_ascii=False) + return reg.execute(name, args if isinstance(args, dict) else json.dumps(args)) + + def get_client(host: str | None = None) -> ollama.Client: """Return an ``ollama.Client`` configured for the configured host.""" import httpx @@ -245,9 +265,9 @@ def chat_with_tools( for call in calls: name = call.get("name", "") args = call.get("arguments", {}) - yield {"type": "tool_start", "name": name, "arguments": args} - result = reg.execute(name, args if isinstance(args, dict) else json.dumps(args)) - result = _trim_tool_result(result, settings.max_tool_result_chars) + yield {"type": "tool_start", "name": name, "arguments": _safe_tool_arguments(args)} + result = _execute_tool_with_policy(reg, name, args) + result = _safe_tool_result(_trim_tool_result(result, settings.max_tool_result_chars)) yield {"type": "tool_end", "name": name, "result": result} tool_results.append((name, result)) @@ -423,18 +443,16 @@ def stream_chat_with_tools( for call in tool_calls: name = call.get("name", "") args = call.get("arguments", {}) - yield {"type": "tool_start", "name": name, "arguments": args} + yield {"type": "tool_start", "name": name, "arguments": _safe_tool_arguments(args)} t1 = time.time() try: - result = reg.execute( - name, args if isinstance(args, dict) else json.dumps(args) - ) + result = _execute_tool_with_policy(reg, name, args) except Exception as exc: # noqa: BLE001 logger.warning("tool %s execution failed in %.2fs: %s", name, time.time() - t1, exc) result = f"Tool execution failed: {exc}" # Always truncate per-tool result immediately so it does not bloat # the context even before we reach the cumulative limit. - result = _trim_tool_result(result, settings.max_tool_result_chars) + result = _safe_tool_result(_trim_tool_result(result, settings.max_tool_result_chars)) logger.info( "tool %s executed len=%d elapsed=%.2fs", name, |
