summaryrefslogtreecommitdiff
path: root/src/ollama_web/llm.py
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-06-27 01:09:00 +0900
committertanaka takashi <isazaka@gmail.com>2026-06-27 01:09:00 +0900
commitfce9dc1b2940ee6699d2acf0c9e56456d9899581 (patch)
tree6bb0cfd1b220b7a37f77d55348803dd46ea30e45 /src/ollama_web/llm.py
parent3a9e8e18df4c596360da54000f65e1bebc6039c2 (diff)
MCP server機能をセキュア化
Diffstat (limited to 'src/ollama_web/llm.py')
-rw-r--r--src/ollama_web/llm.py34
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,