ajustes de mcp

This commit is contained in:
Jordan
2026-04-02 00:14:11 +01:00
parent 264acc90b4
commit 0795b28b54
5 changed files with 70 additions and 25 deletions

1
.gitignore vendored
View File

@@ -26,3 +26,4 @@ Thumbs.db
.idea/ .idea/
*.swp *.swp
*.swo *.swo
keepsailing.es/

View File

@@ -6,6 +6,18 @@
"env": {}, "env": {},
"timeout": 30, "timeout": 30,
"startup_timeout": 10 "startup_timeout": 10
},
"playwright": {
"command": "npx",
"args": ["@playwright/mcp", "--headless"],
"timeout": 30,
"startup_timeout": 15
},
"fetch": {
"command": "uvx",
"args": ["mcp-server-fetch"],
"timeout": 30,
"startup_timeout": 15
} }
} }
} }

View File

@@ -3,17 +3,21 @@
"acai-code": { "acai-code": {
"command": "node", "command": "node",
"args": ["mcp-server/stdio.js"], "args": ["mcp-server/stdio.js"],
"env": { "env": {},
"ACAI_WEB_URL": "http://localhost:8080",
"ACAI_WEBSITE": "mi-sitio",
"ACAI_PROJECT_DIR": "/ruta/al/proyecto"
},
"timeout": 30, "timeout": 30,
"startup_timeout": 10 "startup_timeout": 10
}, },
"filesystem": { "playwright": {
"command": "npx", "command": "npx",
"args": ["@modelcontextprotocol/server-filesystem", "/tmp"] "args": ["@playwright/mcp", "--headless"],
"timeout": 30,
"startup_timeout": 15
},
"fetch": {
"command": "uvx",
"args": ["mcp-server-fetch"],
"timeout": 30,
"startup_timeout": 15
} }
} }
} }

View File

@@ -214,26 +214,47 @@ class MCPManager:
# ------------------------------------------------------------------ # ------------------------------------------------------------------
def _namespace(self, server_name: str, tool_name: str) -> str: def _namespace(self, server_name: str, tool_name: str) -> str:
"""Build namespaced tool name. Skip prefix in single-server mode.""" """Build namespaced tool name. Skip prefix in single-server mode.
Uses '__' as separator because OpenAI requires tool names to
match ^[a-zA-Z0-9_-]+$ (no dots allowed).
"""
if self._single_server_mode: if self._single_server_mode:
return tool_name return tool_name
return f"{server_name}.{tool_name}" safe_server = server_name.replace("-", "_")
safe_tool = tool_name.replace("-", "_")
return f"{safe_server}__{safe_tool}"
def _resolve_tool(self, namespaced_name: str) -> tuple[str, str]: def _resolve_tool(self, namespaced_name: str) -> tuple[str, str]:
"""Resolve a namespaced tool name to (server_name, raw_tool_name).""" """Resolve a namespaced tool name to (server_name, raw_tool_name)."""
# Direct lookup in index # Direct lookup in index
if namespaced_name in self._tool_index: if namespaced_name in self._tool_index:
server_name = self._tool_index[namespaced_name] server_name = self._tool_index[namespaced_name]
raw_name = namespaced_name # Reverse the namespace to get original tool name
if not self._single_server_mode and "." in namespaced_name: if not self._single_server_mode:
raw_name = namespaced_name.split(".", 1)[1] safe_server = server_name.replace("-", "_")
return server_name, raw_name prefix = safe_server + "__"
if namespaced_name.startswith(prefix):
# Find original tool name by matching against client's tools
suffix = namespaced_name[len(prefix):]
for original_name in self._clients[server_name].tools:
if original_name.replace("-", "_") == suffix:
return server_name, original_name
# Fallback: try suffix directly
return server_name, suffix
return server_name, namespaced_name
# Try splitting on first dot # Try splitting on '__'
if "." in namespaced_name: if "__" in namespaced_name:
server_name, raw_name = namespaced_name.split(".", 1) parts = namespaced_name.split("__", 1)
if server_name in self._clients: prefix, suffix = parts
return server_name, raw_name for server_name in self._clients:
if server_name.replace("-", "_") == prefix:
# Find original tool name
for original_name in self._clients[server_name].tools:
if original_name.replace("-", "_") == suffix:
return server_name, original_name
return server_name, suffix
# Fallback: search all servers for the bare name # Fallback: search all servers for the bare name
for server_name, client in self._clients.items(): for server_name, client in self._clients.items():

View File

@@ -77,7 +77,7 @@ class BaseAgent:
full_text = "" full_text = ""
tool_calls: list[dict[str, Any]] = [] tool_calls: list[dict[str, Any]] = []
current_tool: dict[str, Any] = {} current_tool: dict[str, Any] | None = None
async for chunk in self.model.stream( async for chunk in self.model.stream(
messages=ctx.to_messages(), messages=ctx.to_messages(),
@@ -96,7 +96,7 @@ class BaseAgent:
session_id=session.session_id, session_id=session.session_id,
) )
if chunk.tool_name and not current_tool.get("name"): if chunk.tool_name and (current_tool is None or not current_tool.get("name")):
current_tool = { current_tool = {
"id": chunk.tool_call_id, "id": chunk.tool_call_id,
"name": chunk.tool_name, "name": chunk.tool_name,
@@ -108,18 +108,23 @@ class BaseAgent:
session_id=session.session_id, session_id=session.session_id,
) )
if chunk.tool_arguments and current_tool: if chunk.tool_arguments and current_tool is not None and not chunk.finish_reason:
# Accumulate partial argument chunks (NOT the final one)
current_tool["arguments"] += chunk.tool_arguments current_tool["arguments"] += chunk.tool_arguments
if chunk.finish_reason == "tool_use" and current_tool.get("name"): if chunk.finish_reason == "tool_use" and current_tool is not None and current_tool.get("name"):
# Parse arguments # Final chunk carries complete arguments — use those if
# partial accumulation is empty, otherwise use accumulated
final_args = current_tool["arguments"] or chunk.tool_arguments or ""
try: try:
args = json.loads(current_tool["arguments"]) if current_tool["arguments"] else {} args = json.loads(final_args) if final_args else {}
except json.JSONDecodeError: except json.JSONDecodeError:
logger.warning("Failed to parse tool args: %s", final_args[:200])
args = {} args = {}
current_tool["parsed_arguments"] = args current_tool["parsed_arguments"] = args
logger.debug("Tool call finalized: %s args=%s", current_tool["name"], json.dumps(args)[:200])
tool_calls.append(current_tool) tool_calls.append(current_tool)
current_tool = {} current_tool = None
if chunk.finish_reason == "end_turn": if chunk.finish_reason == "end_turn":
break break
@@ -168,6 +173,8 @@ class BaseAgent:
status=ToolExecutionStatus.RUNNING, status=ToolExecutionStatus.RUNNING,
) )
logger.info("Tool call: %s(%s)", tool_name, json.dumps(arguments)[:200])
start = time.monotonic() start = time.monotonic()
try: try:
if self.mcp.is_running and tool_name in self.mcp.tools: if self.mcp.is_running and tool_name in self.mcp.tools: