Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ coverage/

# Session docs
*.md

bun.lock
iTerm2
mcpflow-router@*
node
8 changes: 6 additions & 2 deletions mcp-server/mcp_tool_router/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def __init__(
self._host_tool_definitions: dict[str, list[dict[str, Any]]] = (
host_tool_definitions or {}
)
self._ignore_tool_ids: set[str] = set()

@classmethod
def from_yaml(
Expand All @@ -67,21 +68,24 @@ def from_opencode_config(
auto_sync: bool = True,
include_disabled: bool = False,
ignore_ids: Iterable[str] | None = None,
ignore_tool_ids: Iterable[str] | None = None,
) -> "ToolRouterHub":
registry, host_tools, host_tool_definitions = cls.load_opencode_runtime(
path,
include_disabled=include_disabled,
ignore_ids=ignore_ids,
)
router = ToolRouter(routerd_path=routerd_path)
return cls(
hub = cls(
registry,
router,
auto_sync=auto_sync,
include_disabled=include_disabled,
host_tools=host_tools,
host_tool_definitions=host_tool_definitions,
)
hub._ignore_tool_ids = set(ignore_tool_ids or [])
return hub

@classmethod
def load_opencode_runtime(
Expand Down Expand Up @@ -180,7 +184,7 @@ def sync_server(self, server_id: str, *, raise_on_error: bool = True) -> None:
raise ValueError(f"Server '{server_id}' is disabled.")
try:
client = self._ensure_client(server)
self._router.sync_from_mcp(server_id, client)
self._router.sync_from_mcp(server_id, client, ignore_tool_ids=self._ignore_tool_ids)
except Exception as exc:
if server.transport == "http" and not raise_on_error:
print(
Expand Down
4 changes: 2 additions & 2 deletions mcp-server/mcp_tool_router/opencode_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def apply_router_config(
entry["enabled"] = True

_write_config(path, payload, create_backup=create_backup)
_reenable_oh_my_opencode_mcps(path.parent, create_backup=create_backup)
# _reenable_oh_my_opencode_mcps removed: user config should be respected
return payload


Expand Down Expand Up @@ -135,7 +135,7 @@ def main() -> int:
return 0

_write_config(path, payload, create_backup=args.create_backup)
_reenable_oh_my_opencode_mcps(path.parent, create_backup=args.create_backup)
# _reenable_oh_my_opencode_mcps removed: user config should be respected
return 0


Expand Down
2 changes: 2 additions & 0 deletions mcp-server/mcp_tool_router/opencode_gateway_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -829,13 +829,15 @@ def _load_gateway_hub() -> ToolRouterHub:
ignore_ids.add(router_id)
if not ignore_ids:
ignore_ids.add("router")
ignore_tool_ids = _parse_id_list(os.environ.get("ROUTER_IGNORE_TOOL_IDS"))
routerd_cmd = os.environ.get("ROUTERD")
return ToolRouterHub.from_opencode_config(
config_path,
routerd_path=routerd_cmd,
auto_sync=True,
include_disabled=include_disabled,
ignore_ids=sorted(ignore_ids),
ignore_tool_ids=sorted(ignore_tool_ids),
)


Expand Down
6 changes: 5 additions & 1 deletion mcp-server/mcp_tool_router/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def _routerd_argv(self) -> list[str]:
return shlex.split(self._config.routerd_path)
return ["tool-routerd"]

def sync_from_mcp(self, server_id: str, mcp_client: Any) -> None:
def sync_from_mcp(self, server_id: str, mcp_client: Any, ignore_tool_ids: set[str] | None = None) -> None:
"""Sync MCP tools into the router catalog.

Expected flow:
Expand All @@ -149,6 +149,7 @@ def sync_from_mcp(self, server_id: str, mcp_client: Any) -> None:
tools=tools or [],
source_type="mcp",
source_platform="mcp",
ignore_tool_ids=ignore_tool_ids,
)

def sync_from_tool_definitions(
Expand All @@ -158,6 +159,7 @@ def sync_from_tool_definitions(
*,
source_type: str = "mcp",
source_platform: str = "mcp",
ignore_tool_ids: set[str] | None = None,
) -> None:
tool_cards: list[dict[str, Any]] = []
for tool in tools:
Expand All @@ -172,6 +174,8 @@ def sync_from_tool_definitions(
if not card:
continue
tool_id = card["toolId"]
if ignore_tool_ids and tool_id in ignore_tool_ids:
continue
raw_tool = dict(tool)
if "name" not in raw_tool:
raw_tool["name"] = card["toolName"]
Expand Down
8 changes: 5 additions & 3 deletions mcp-server/mcp_tool_router/router_mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,12 +359,13 @@ def _parse_id_list(value: str | None) -> set[str]:
return {item.strip() for item in value.split(",") if item.strip()}


def _load_hub() -> tuple[ToolRouterHub, str, bool, list[str], str]:
def _load_hub() -> tuple[ToolRouterHub, str, bool, list[str], list[str], str]:
config_path = os.environ.get("OPENCODE_CONFIG", "~/.config/opencode/opencode.json")
include_disabled = os.environ.get(
"ROUTER_INCLUDE_DISABLED", "true"
).lower() not in {"0", "false", "no"}
ignore_ids = _parse_id_list(os.environ.get("ROUTER_IGNORE_IDS"))
ignore_tool_ids = _parse_id_list(os.environ.get("ROUTER_IGNORE_TOOL_IDS"))
router_id = os.environ.get("ROUTER_MCP_ID")
if router_id:
ignore_ids.add(router_id)
Expand All @@ -377,8 +378,9 @@ def _load_hub() -> tuple[ToolRouterHub, str, bool, list[str], str]:
auto_sync=True,
include_disabled=include_disabled,
ignore_ids=sorted(ignore_ids),
ignore_tool_ids=sorted(ignore_tool_ids),
)
return hub, config_path, include_disabled, sorted(ignore_ids), routerd_cmd
return hub, config_path, include_disabled, sorted(ignore_ids), sorted(ignore_tool_ids), routerd_cmd


class _ConfigWatcher(threading.Thread):
Expand Down Expand Up @@ -438,7 +440,7 @@ def _reload(self) -> None:


def main() -> int:
hub, config_path, include_disabled, ignore_ids, routerd_cmd = _load_hub()
hub, config_path, include_disabled, ignore_ids, ignore_tool_ids, routerd_cmd = _load_hub()
interval = _coerce_int(os.environ.get("ROUTER_WATCH_INTERVAL"), 1)
watcher = _ConfigWatcher(
hub,
Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ const INSTALL_PROFILES: Record<InstallTarget, InstallProfile> = {
defaultConfigPath: "~/.config/opencode/opencode.json",
mcpField: "mcp",
wellKnownRemoteMcps: OPENCODE_WELL_KNOWN_REMOTE_MCPS,
postWriteHook: reenableOhMyOpencodeMcps,
// postWriteHook removed: reenableOhMyOpencodeMcps was overriding user
// config by force-re-enabling built-in MCPs the user had disabled.
// User config should always be respected.
postInstallHook: ensureOpencodeGatewayShim,
},
claude: {
Expand Down