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/
*.swp
*.swo
keepsailing.es/

View File

@@ -6,6 +6,18 @@
"env": {},
"timeout": 30,
"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": {
"command": "node",
"args": ["mcp-server/stdio.js"],
"env": {
"ACAI_WEB_URL": "http://localhost:8080",
"ACAI_WEBSITE": "mi-sitio",
"ACAI_PROJECT_DIR": "/ruta/al/proyecto"
},
"env": {},
"timeout": 30,
"startup_timeout": 10
},
"filesystem": {
"playwright": {
"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:
"""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:
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]:
"""Resolve a namespaced tool name to (server_name, raw_tool_name)."""
# Direct lookup in index
if namespaced_name in self._tool_index:
server_name = self._tool_index[namespaced_name]
raw_name = namespaced_name
if not self._single_server_mode and "." in namespaced_name:
raw_name = namespaced_name.split(".", 1)[1]
return server_name, raw_name
# Reverse the namespace to get original tool name
if not self._single_server_mode:
safe_server = server_name.replace("-", "_")
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
if "." in namespaced_name:
server_name, raw_name = namespaced_name.split(".", 1)
if server_name in self._clients:
return server_name, raw_name
# Try splitting on '__'
if "__" in namespaced_name:
parts = namespaced_name.split("__", 1)
prefix, suffix = parts
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
for server_name, client in self._clients.items():

View File

@@ -77,7 +77,7 @@ class BaseAgent:
full_text = ""
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(
messages=ctx.to_messages(),
@@ -96,7 +96,7 @@ class BaseAgent:
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 = {
"id": chunk.tool_call_id,
"name": chunk.tool_name,
@@ -108,18 +108,23 @@ class BaseAgent:
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
if chunk.finish_reason == "tool_use" and current_tool.get("name"):
# Parse arguments
if chunk.finish_reason == "tool_use" and current_tool is not None and current_tool.get("name"):
# 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:
args = json.loads(current_tool["arguments"]) if current_tool["arguments"] else {}
args = json.loads(final_args) if final_args else {}
except json.JSONDecodeError:
logger.warning("Failed to parse tool args: %s", final_args[:200])
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)
current_tool = {}
current_tool = None
if chunk.finish_reason == "end_turn":
break
@@ -168,6 +173,8 @@ class BaseAgent:
status=ToolExecutionStatus.RUNNING,
)
logger.info("Tool call: %s(%s)", tool_name, json.dumps(arguments)[:200])
start = time.monotonic()
try:
if self.mcp.is_running and tool_name in self.mcp.tools: