mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-12 20:57:42 +02:00
[eric] system prompt: direct mcp__openswarm-web__WebSearch/WebFetch hint when web MCP is active (fixes 2-minute ToolSearch thrash on small Ollama
models)
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
# backend/CLAUDE.md
|
||||
|
||||
FastAPI orchestrator. Entry: `backend/main.py` (uvicorn `:8324`, REST `/api/*`, WS `/ws/*`, Swagger `/docs`). See root `CLAUDE.md` for repo-wide constraints.
|
||||
|
||||
## Run / test
|
||||
|
||||
- Dev: `bash backend/run.sh` (creates `.venv/`, installs `requirements.txt`, runs uvicorn with `--reload`).
|
||||
- Tests: `pip install -r requirements-dev.txt && pytest tests/`.
|
||||
- `requirements-dev.txt` is deliberately kept out of `requirements.txt` so `pytest` etc. don't ship in the production DMG. Sync both files when adding deps that need to exist in either place.
|
||||
|
||||
## Layout
|
||||
|
||||
- `apps/agents/` — agent orchestration, WS manager, MCP plumbing.
|
||||
- `providers/registry.py` — resolves primary + aux models across Anthropic / OpenAI / Google / OpenRouter / custom OpenAI-compatible providers. **Always go through here; never hardcode a model ID.**
|
||||
- `mcp_preflight.py` — vague-prompt classifier that surfaces the one-click MCP-connect modal.
|
||||
- `mcp_meta_server.py`, `mcp_registry.py` — MCP discovery + registry.
|
||||
- `9router_gpt5_patch.js` — patch loaded into 9router to translate OpenAI `max_tokens` semantics.
|
||||
- `apps/nine_router.py` — supervises the 9router subprocess on `:20128`.
|
||||
- `apps/subscription/router.py` — OAuth + Stripe callbacks for openswarm-pro signup.
|
||||
- `apps/outputs/` — view renderer (HTML/JS/CSS iframes, sandboxed Python execution).
|
||||
- `auth.py` — per-install bearer token. **Generated BEFORE the HTTP bind** so the Electron shell can read it from disk; don't reorder.
|
||||
|
||||
## MCP gate
|
||||
|
||||
- Dispatch flows through `_build_mcp_servers`. This is the only place MCP tools become reachable.
|
||||
- `session.active_mcps` defaults to empty; the user opts in via `MCPSearch` + `MCPActivate` (HITL).
|
||||
- New MCP-related code path? It must respect this gate. Don't add side channels.
|
||||
- Suggestions surfaced to users must come from the vetted/default set — not the full upstream registry.
|
||||
|
||||
## Providers / models
|
||||
|
||||
- Primary model: per-session user choice, resolved by `providers.registry`.
|
||||
- Aux model (preflight, classifier, summarizers): pick the **cheap tier of the user's configured provider** — Haiku for Anthropic, GPT-5-mini for OpenAI, Gemini Flash for Google, etc. Never hardcode Haiku.
|
||||
|
||||
## Common pitfalls
|
||||
|
||||
- New endpoint? Use a pydantic request/response model and `@typechecked`.
|
||||
- Pinning matters — `requirements.txt` is fully pinned for reproducibility.
|
||||
- Token middleware already scrubs bearer tokens from logs; don't re-add raw logging.
|
||||
- MCP bundles in `mcp-bundles/` are esbuild output; regenerate via the bundle script rather than editing.
|
||||
@@ -1815,6 +1815,52 @@ class AgentManager:
|
||||
if _bt not in effective_disallowed:
|
||||
effective_disallowed.append(_bt)
|
||||
|
||||
# Tell the model directly which web tools work for this session.
|
||||
# The Claude Code CLI's deferred-tool registry still advertises bare
|
||||
# `WebSearch` and `WebFetch` even when we've stripped them above —
|
||||
# frontier models (Claude/GPT-5/Gemini Pro) intuit the namespaced
|
||||
# MCP variant from context, but smaller open-source models (gpt-oss
|
||||
# via Ollama, smaller Llama/Qwen, etc.) thrash on the deferred-tool
|
||||
# handshake (saw 2+ minutes of repeated `ToolSearch(select:WebSearch)`
|
||||
# → empty matches → retry). Naming the working tool here cuts that
|
||||
# to a single direct call. Only injected when (a) we registered the
|
||||
# web MCP, AND (b) the user hasn't disabled the policy — matches
|
||||
# the same gate the MCP allowlist uses, so disabling WebSearch in
|
||||
# Settings still wins.
|
||||
_web_tools_available = _need_web_mcp and (
|
||||
"mcp__openswarm-web__WebSearch" in effective_allowed
|
||||
or "mcp__openswarm-web__WebFetch" in effective_allowed
|
||||
)
|
||||
if _web_tools_available:
|
||||
_hint_lines = ["<web_tools>"]
|
||||
_hint_lines.append(
|
||||
"This session does NOT have the built-in `WebSearch` / "
|
||||
"`WebFetch` tools (they delegate to Anthropic Haiku, which "
|
||||
"isn't reachable on this primary). Use the MCP-backed "
|
||||
"equivalents instead — call them DIRECTLY, no ToolSearch "
|
||||
"step needed:"
|
||||
)
|
||||
if "mcp__openswarm-web__WebSearch" in effective_allowed:
|
||||
_hint_lines.append(
|
||||
"- `mcp__openswarm-web__WebSearch(query: str, "
|
||||
"num_results?: int)` — DuckDuckGo search."
|
||||
)
|
||||
if "mcp__openswarm-web__WebFetch" in effective_allowed:
|
||||
_hint_lines.append(
|
||||
"- `mcp__openswarm-web__WebFetch(url: str, prompt?: "
|
||||
"str)` — fetch a URL and return readable text."
|
||||
)
|
||||
_hint_lines.append(
|
||||
"Do not call `ToolSearch(select:WebSearch)` — bare "
|
||||
"`WebSearch` is unavailable on this session and that path "
|
||||
"will return empty matches."
|
||||
)
|
||||
_hint_lines.append("</web_tools>")
|
||||
_web_hint = "\n".join(_hint_lines)
|
||||
composed_prompt = (
|
||||
f"{composed_prompt}\n\n{_web_hint}" if composed_prompt else _web_hint
|
||||
)
|
||||
|
||||
# Log effective tool lists
|
||||
google_allowed = [t for t in effective_allowed if "google-workspace" in t]
|
||||
reddit_allowed = [t for t in effective_allowed if "reddit" in t]
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# electron/CLAUDE.md
|
||||
|
||||
Electron 40.x (CastLabs DRM build) desktop shell + auto-updater via GitHub Releases. Entry: `main.js`. Version is in `package.json`. See root `CLAUDE.md` for repo-wide constraints.
|
||||
|
||||
## Build / release
|
||||
|
||||
- Local build: `npm run build` → produces `build-staging/` containing the frontend dist, backend bundle, standalone Python 3.13, and the 9router binary. Build artifacts are ephemeral; not git-tracked.
|
||||
- macOS release: requires Apple ID, app-specific password, and team ID env vars. App is signed + notarized.
|
||||
- Windows release: signed via Azure code signing in CI (`.github/workflows/release-windows.yml`); triggers on `v*` tags.
|
||||
|
||||
## Bundling
|
||||
|
||||
- Python 3.13 is bundled via python-build-standalone — users do not need a system Python.
|
||||
- 9router binary is pulled at build time by `scripts/fetch-router.sh` / `fetch-router.ps1`. The version pin (`0.3.60`) is load-bearing for cross-provider WebSearch — see root `CLAUDE.md`.
|
||||
|
||||
## Versioning
|
||||
|
||||
- Source of truth: `electron/package.json` `version`. Bump alongside any user-facing release; CI tags off it.
|
||||
- Bump only when cutting a release — coordinate with the publish flow rather than landing version bumps speculatively.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- `build-staging/` is regenerated on every build; never commit it.
|
||||
- Auto-updater reads the latest release feed from GitHub; staging/test builds should use a separate channel to avoid pushing unsigned bits to users.
|
||||
@@ -0,0 +1,28 @@
|
||||
# frontend/CLAUDE.md
|
||||
|
||||
React 18 + TypeScript + webpack 5 + Redux. Entry: `src/app/Main.tsx`. Dev server on `:3000` proxies REST and WebSocket to backend on `:8324`. See root `CLAUDE.md` for repo-wide constraints.
|
||||
|
||||
## Run
|
||||
|
||||
- Dev (full stack): `bash run.sh`.
|
||||
- Dev (frontend only): `bash frontend/run.sh` — runs `npm install` then `npm run dev`.
|
||||
- No JS/TS test runner is wired up. Changes must be manually exercised in the running app before merging.
|
||||
|
||||
## Key concepts
|
||||
|
||||
- **Spatial dashboard** — agents are draggable nodes on a canvas; layout + selection state lives in Redux.
|
||||
- **Settings draft persistence** — `AppSettings.dismissed_mcp_suggestions` is a map of MCP id → ISO timestamp; preserve this shape when modifying settings serialization.
|
||||
- **Onboarding wizard** (`src/app/pages/Onboarding/`) — 8-step agentic cursor walkthrough. Cursor offsets, fit-to-view, AC popup timing, and group-meta dedup were each delicate to land; verify visually after touching this code.
|
||||
- **Custom providers** — `AppSettings.custom_providers: CustomProvider[]` supports any OpenAI-compatible endpoint (e.g. LM Studio).
|
||||
|
||||
## Conventions
|
||||
|
||||
- TS only; no PropTypes.
|
||||
- No eslint/prettier config — match nearby files.
|
||||
- Onboarding-copy placeholders shaped like real API keys (`sk-ant-api03-…`) are already allowlisted in `.gitleaks.toml`. Reuse the existing placeholder rather than introducing new "example" tokens.
|
||||
- MCP suggestion UI must surface only the vetted/default set — never expose the full upstream registry to users.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Direct LLM calls from the frontend bypass the backend's provider routing and MCP gate. Don't add them; route through `/api/*` instead.
|
||||
- Webpack-dev-server hot reload occasionally loses WS state — full page reload after backend restarts.
|
||||
Reference in New Issue
Block a user