openclaw-mcp-router 0.2.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@ on:
5
5
  types: [published]
6
6
 
7
7
  permissions:
8
- contents: write
8
+ contents: read
9
9
  id-token: write # npm provenance (links published package back to this repo+commit)
10
10
 
11
11
  jobs:
@@ -13,14 +13,10 @@ jobs:
13
13
  runs-on: ubuntu-latest
14
14
  steps:
15
15
  - uses: actions/checkout@v4
16
- with:
17
- # Use a PAT so the version-bump commit can trigger other workflows if needed.
18
- # Falls back to GITHUB_TOKEN (works fine, but commits won't trigger further CI).
19
- token: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }}
20
16
 
21
17
  - uses: actions/setup-node@v4
22
18
  with:
23
- node-version: 22
19
+ node-version: 24
24
20
  registry-url: https://registry.npmjs.org
25
21
 
26
22
  - name: Extract version from release tag
@@ -39,19 +35,11 @@ jobs:
39
35
  echo "version=$VERSION" >> "$GITHUB_OUTPUT"
40
36
  echo "Resolved version: $VERSION"
41
37
 
42
- - name: Bump package.json version
43
- run: npm version "${{ steps.version.outputs.version }}" --no-git-tag-version
44
-
45
- - name: Commit version bump
46
- run: |
47
- git config user.name "github-actions[bot]"
48
- git config user.email "github-actions[bot]@users.noreply.github.com"
49
- git add package.json package-lock.json 2>/dev/null || true
50
- git commit -m "chore: bump version to ${{ steps.version.outputs.version }}" || echo "No changes to commit"
51
- git push origin HEAD:${{ github.event.release.target_commitish }}
52
-
53
38
  - run: npm ci
54
39
 
40
+ - name: Set package version from tag (publish only, no commit)
41
+ run: npm version "${{ steps.version.outputs.version }}" --no-git-tag-version
42
+
55
43
  - name: Publish to npm
56
44
  run: npm publish --provenance --access public
57
45
  env:
package/README.md CHANGED
@@ -1,124 +1,129 @@
1
- # openclaw-mcp-router
1
+ # OpenClaw MCP Router 🚀
2
2
 
3
- Dynamic MCP tool router for [OpenClaw](https://openclaw.ai) semantic search over large MCP tool catalogs to eliminate context bloat.
3
+ **OpenClaw MCP Router** is a dynamic tool discovery layer for [OpenClaw](https://openclaw.ai). It uses semantic vector search to eliminate **Context Bloat** by routing only the necessary Model Context Protocol (MCP) tool schemas to your agent on-demand.
4
4
 
5
- ## The problem
5
+ ## The Problem: Context Window Exhaustion
6
6
 
7
- Loading all MCP tool schemas upfront burns 55k–134k tokens before your agent processes a single message. With 58 tools across 5 MCP servers, that's ~77k tokens wasted on schemas the agent may never use.
7
+ Modern MCP catalogs are growing. Loading every tool schema upfront is expensive and inefficient:
8
8
 
9
- ## The solution
9
+ * **Token Waste:** 5 MCP servers with 50+ tools can burn **55k–134k tokens** before your agent even says "Hello."
10
+ * **Performance Hit:** Massive system prompts degrade reasoning accuracy (the "lost in the middle" phenomenon).
11
+ * **Cost:** High token usage leads to higher API costs for every turn of the conversation.
10
12
 
11
- Two tiny tools replace the full schema dump:
13
+ ## 🛠️ The Solution: Semantic Tool Routing
12
14
 
13
- - **`mcp_search(query)`** — embed the query via Ollama, search a local LanceDB index, return only matching tool definitions (~8.7k tokens, 95% reduction)
14
- - **`mcp_call(tool_name, params_json)`** — look up the owning MCP server, execute the call, return the result
15
+ Instead of a full schema dump, this plugin registers two lightweight "Meta-Tools":
15
16
 
16
- The agent asks for tools it needs instead of receiving every schema upfront.
17
+ 1. **`mcp_search(query)`**: Uses **Ollama** and **LanceDB** to perform a semantic search. It returns only the top-N most relevant tool definitions (reducing overhead by ~95%).
18
+ 2. **`mcp_call(tool_name, params)`**: Dynamically resolves the owning MCP server and executes the call.
17
19
 
18
- ## Install
20
+ > **Result:** Your agent "asks" for the tools it needs, keeping the context window clean and the reasoning sharp.
19
21
 
20
- ```sh
21
- openclaw plugins install openclaw-mcp-router
22
- ```
22
+ ---
23
+
24
+ ## 🚀 Quick Start
25
+
26
+ ### 1. Prerequisites
27
+
28
+ Ensure you have **Ollama** running locally with an embedding model:
23
29
 
24
- Requires [Ollama](https://ollama.ai) running locally with an embedding model:
30
+ ```bash
31
+ ollama pull embeddinggemma
25
32
 
26
- ```sh
27
- ollama pull nomic-embed-text
28
- ollama serve
29
33
  ```
30
34
 
31
- ## Configuration
32
-
33
- Add to `~/.openclaw/openclaw.yml`:
34
-
35
- ```yaml
36
- tools:
37
- alsoAllow:
38
- - mcp_search
39
- - mcp_call
40
-
41
- plugins:
42
- mcp-router:
43
- enabled: true
44
- config:
45
- servers:
46
- - name: filesystem
47
- transport: stdio
48
- command: npx
49
- args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
50
- - name: github
51
- transport: sse
52
- url: https://api.githubcopilot.com/mcp/
53
- embedding:
54
- provider: ollama
55
- model: nomic-embed-text # or mxbai-embed-large, all-minilm
56
- url: http://localhost:11434
57
- search:
58
- topK: 5 # tools returned per search (1–20)
59
- minScore: 0.3 # minimum similarity threshold (0–1)
35
+ ### 2. Installation
36
+
37
+ ```bash
38
+ openclaw plugins install openclaw-mcp-router
39
+
60
40
  ```
61
41
 
62
- ## Configuration reference
42
+ ### 3. Setup & Indexing
63
43
 
64
- ### `servers[]`
44
+ Run the interactive wizard to configure your servers and automatically update your `alsoAllow` permissions:
65
45
 
66
- | Field | Required | Description |
67
- |-------|----------|-------------|
68
- | `name` | yes | Unique server identifier |
69
- | `transport` | yes | `stdio`, `sse`, or `http` |
70
- | `command` | stdio only | Executable to run |
71
- | `args` | stdio only | Arguments array |
72
- | `env` | no | Extra env vars merged over process.env; supports `${VAR}` expansion |
73
- | `url` | sse/http only | Server endpoint URL |
46
+ ```bash
47
+ openclaw openclaw-mcp-router setup
48
+ openclaw openclaw-mcp-router reindex
74
49
 
75
- ### `embedding`
50
+ ```
76
51
 
77
- | Field | Default | Description |
78
- |-------|---------|-------------|
79
- | `provider` | `ollama` | Only Ollama is supported |
80
- | `model` | `nomic-embed-text` | Embedding model name |
81
- | `url` | `http://localhost:11434` | Ollama base URL (must be localhost) |
52
+ ---
82
53
 
83
- ### `vectorDb`
54
+ ## ⚙️ Configuration
84
55
 
85
- | Field | Default | Description |
86
- |-------|---------|-------------|
87
- | `path` | `~/.openclaw/mcp-router/lancedb` | LanceDB database directory |
56
+ The plugin is highly configurable via `~/.openclaw/openclaw.json`.
88
57
 
89
- ### `search`
58
+ ### Server Management
90
59
 
91
- | Field | Default | Description |
92
- |-------|---------|-------------|
93
- | `topK` | `5` | Max tools returned per search |
94
- | `minScore` | `0.3` | Minimum similarity score (0–1) |
60
+ You can manage servers via the **Interactive TUI**:
95
61
 
96
- ## CLI commands
62
+ ```bash
63
+ openclaw openclaw-mcp-router control
97
64
 
98
- ```sh
99
- # Re-index all configured MCP servers
100
- openclaw mcp-router reindex
65
+ ```
66
+
67
+ ### Manual Schema Example
68
+
69
+ For power users, add servers directly to your `plugins.entries`:
70
+
71
+ | Key | Description | Default |
72
+ | --- | --- | --- |
73
+ | `topK` | Number of tools returned per search | `5` |
74
+ | `minScore` | Similarity threshold (0.0 - 1.0) | `0.3` |
75
+ | `maxRetries` | Connection attempts for slow servers | `3` |
76
+
77
+ ```json5
78
+ // ~/.openclaw/openclaw.json
79
+ {
80
+ "plugins": {
81
+ "entries": {
82
+ "openclaw-mcp-router": {
83
+ "enabled": true,
84
+ "config": {
85
+ "servers": [{ "name": "filesystem", "transport": "stdio", "command": "npx", "args": ["..."] }],
86
+ "embedding": { "provider": "ollama", "model": "embeddinggemma" }
87
+ }
88
+ }
89
+ }
90
+ }
91
+ }
101
92
 
102
- # Show indexed tool count
103
- openclaw mcp-router stats
104
93
  ```
105
94
 
106
- ## How it works
95
+ ---
96
+
97
+ ## 🧠 How It Works: Under the Hood
98
+
99
+ 1. **Indexing:** During `reindex`, the router connects to all configured MCP servers, fetches their manifests, and generates vector embeddings for every tool description.
100
+ 2. **Storage:** These embeddings are stored in a local **LanceDB** instance for sub-millisecond retrieval.
101
+ 3. **Runtime Discovery:** * Agent detects a task (e.g., "Analyze this CSV").
102
+ * Agent calls `mcp_search("read or analyze csv files")`.
103
+ * Router returns the `filesystem` tool schema.
104
+ * Agent executes the tool via `mcp_call`.
105
+
106
+
107
+
108
+ ---
109
+
110
+ ## 📈 Performance & Benchmarks
111
+
112
+ Based on the [Anthropic Tool Search](https://www.anthropic.com/engineering/advanced-tool-use) pattern, dynamic routing can improve tool selection accuracy significantly:
113
+
114
+ * **Standard Loading:** ~49% Accuracy (Large catalogs)
115
+ * **Dynamic Routing:** **~88% Accuracy** (Opus 4.5 benchmarks)
107
116
 
108
- 1. At gateway startup, the plugin connects to each MCP server, lists its tools, embeds each description via Ollama, and stores them in a local LanceDB index.
109
- 2. When the agent needs to use an MCP capability, it calls `mcp_search("what I want to do")` to find relevant tools.
110
- 3. The agent then calls `mcp_call("tool_name", '{"param": "value"}')` to execute the chosen tool.
117
+ ---
111
118
 
112
- ## Supported embedding models
119
+ ## 🤝 Contributing
113
120
 
114
- | Model | Dims | Notes |
115
- |-------|------|-------|
116
- | `nomic-embed-text` | 768 | Good balance, recommended default |
117
- | `mxbai-embed-large` | 1024 | Higher quality, larger footprint |
118
- | `all-minilm` | 384 | Fast and lightweight |
121
+ We are looking to implement **Hybrid Search (BM25)** and **LLM-based Reranking**. If you're interested in improving LLM orchestration efficiency, we'd love your help!
119
122
 
120
- Any Ollama embedding model works — dimensions are detected automatically for unknown models.
123
+ 1. Fork the repo.
124
+ 2. Create your feature branch.
125
+ 3. Submit a PR.
121
126
 
122
- ## License
127
+ ## 📄 License
123
128
 
124
- MIT
129
+ Released under the [MIT License](https://www.google.com/search?q=LICENSE).
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "mcp-router",
2
+ "id": "openclaw-mcp-router",
3
3
  "name": "MCP Router",
4
4
  "description": "Semantic search across MCP tool catalogs. Reduces context bloat from ~77k→8.7k tokens by dynamically surfacing only relevant tools.",
5
5
  "configSchema": {
@@ -18,13 +18,14 @@
18
18
  "url": { "type": "string" },
19
19
  "serverUrl": { "type": "string" },
20
20
  "type": { "type": "string", "enum": ["stdio", "sse", "http"] },
21
- "headers": { "type": "object", "additionalProperties": { "type": "string" } }
21
+ "headers": { "type": "object", "additionalProperties": { "type": "string" } },
22
+ "timeout": { "type": "number", "description": "Per-server connect timeout in ms; overrides indexer.connectTimeout" }
22
23
  }
23
24
  }
24
25
  },
25
26
  "mcpServersFile": {
26
27
  "type": "string",
27
- "description": "Path to a .mcp.json file. Default: ~/.openclaw/.mcp.json"
28
+ "description": "Path to a .mcp.json file. Default: ~/.openclaw/openclaw-mcp-router/.mcp.json"
28
29
  },
29
30
  "servers": {
30
31
  "type": "array",
@@ -39,10 +40,23 @@
39
40
  "args": { "type": "array", "items": { "type": "string" } },
40
41
  "env": { "type": "object", "additionalProperties": { "type": "string" } },
41
42
  "url": { "type": "string" },
42
- "headers": { "type": "object", "additionalProperties": { "type": "string" } }
43
+ "headers": { "type": "object", "additionalProperties": { "type": "string" } },
44
+ "timeout": { "type": "number", "description": "Per-server connect timeout in ms; overrides indexer.connectTimeout" }
43
45
  }
44
46
  }
45
47
  },
48
+ "indexer": {
49
+ "type": "object",
50
+ "description": "Indexer retry and timeout settings",
51
+ "properties": {
52
+ "connectTimeout": { "type": "number", "description": "Per-server default connect timeout in ms (default: 60000)" },
53
+ "maxRetries": { "type": "number", "minimum": 0, "description": "Retry attempts per server, 0 = no retry (default: 3)" },
54
+ "initialRetryDelay": { "type": "number", "description": "Initial backoff delay in ms (default: 2000)" },
55
+ "maxRetryDelay": { "type": "number", "description": "Max backoff cap in ms (default: 30000)" },
56
+ "maxChunkChars": { "type": "number", "minimum": 0, "description": "Max chars per chunk for long tool descriptions. 0 = disable chunking (default: 500)" },
57
+ "overlapChars": { "type": "number", "minimum": 0, "description": "Overlap chars between adjacent chunks (default: 100)" }
58
+ }
59
+ },
46
60
  "embedding": {
47
61
  "type": "object",
48
62
  "properties": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-mcp-router",
3
- "version": "0.2.1",
3
+ "version": "1.0.1",
4
4
  "private": false,
5
5
  "description": "Dynamic MCP tool router for OpenClaw — semantic search over large MCP catalogs to eliminate context bloat",
6
6
  "type": "module",
@@ -18,15 +18,18 @@
18
18
  "ollama"
19
19
  ],
20
20
  "dependencies": {
21
+ "@clack/prompts": "^1.0.1",
21
22
  "@lancedb/lancedb": "^0.26.2",
22
23
  "@modelcontextprotocol/sdk": "^1.27.1",
23
- "@sinclair/typebox": "^0.34.48"
24
+ "@sinclair/typebox": "^0.34.48",
25
+ "json5": "^2.2.3",
26
+ "open": "^11.0.0"
24
27
  },
25
28
  "peerDependencies": {
26
- "openclaw": "^2026.2.24"
29
+ "openclaw": "^2026.2.23"
27
30
  },
28
31
  "devDependencies": {
29
- "openclaw": "^2026.2.19",
32
+ "openclaw": "^2026.2.23",
30
33
  "typescript": "^5.7.0",
31
34
  "vitest": "^3.0.0"
32
35
  },
@@ -37,6 +40,9 @@
37
40
  "openclaw": {
38
41
  "extensions": [
39
42
  "./src/index.ts"
43
+ ],
44
+ "skills": [
45
+ "./skills"
40
46
  ]
41
47
  }
42
48
  }
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: mcp-router
3
+ description: Discover and route tasks to configured MCP tools using mcp_search + mcp_call. Use when a task needs external capabilities (APIs, SaaS, databases, web, files, messaging, infra) and the best tool is not already known in-session, especially before falling back to manual shell/curl/web workflows.
4
+ ---
5
+
6
+ # MCP Router
7
+
8
+ Use MCP as the first integration layer for external capabilities.
9
+
10
+ ## Core Rule
11
+
12
+ Before manual API calls, curl scripts, or ad-hoc web work, run:
13
+
14
+ - `mcp_search("<action-oriented intent>")`
15
+
16
+ If a relevant tool exists, use it with `mcp_call`.
17
+
18
+ ## Fast Workflow
19
+
20
+ 1. **Search capability**
21
+ - Use an action-oriented query: `"create github pull request"`, `"query postgres"`, `"send slack message"`.
22
+ 2. **Select tool**
23
+ - Prefer best intent match + feasible required params.
24
+ 3. **Read schema**
25
+ - Identify required fields, types, enums, nested structure.
26
+ 4. **Call tool**
27
+ - `mcp_call("exact_tool_name", "{...valid JSON...}")`
28
+ 5. **Recover on failure**
29
+ - Fix schema/type mismatch or re-search with rewritten query.
30
+
31
+ ## Query Rewrite Ladder (Deterministic)
32
+
33
+ If search quality is poor, retry in this order:
34
+
35
+ 1. **Action only**: `"read file"`
36
+ 2. **Action + system**: `"read file from s3"`, `"github create issue"`
37
+ 3. **Verb swap**: create/open, read/fetch/get, list/enumerate
38
+ 4. **Scope adjust**: broaden then narrow
39
+
40
+ Stop once you have a high-confidence tool.
41
+
42
+ ## Tool Selection Rules
43
+
44
+ When multiple tools match, rank by:
45
+
46
+ 1. Intent fit (description matches requested outcome)
47
+ 2. Required-input fit (you can provide required params now)
48
+ 3. Simplicity (fewer fragile/optional parameters)
49
+ 4. Score/order from search results (tie-breaker)
50
+
51
+ ## `mcp_call` Parameter Checklist
52
+
53
+ `params_json` must be a **JSON string**.
54
+
55
+ - Include all required fields.
56
+ - Match exact types (`42` vs `"42"`, `true` vs `"true"`).
57
+ - Respect enums and nested object shapes.
58
+ - Do not add unsupported keys unless schema allows them.
59
+
60
+ Examples:
61
+
62
+ ```text
63
+ ✅ mcp_call("filesystem::read_file", '{"path":"/tmp/a.txt"}')
64
+ ❌ mcp_call("filesystem::read_file", {"path":"/tmp/a.txt"})
65
+ ```
66
+
67
+ ```text
68
+ ✅ mcp_call("db::query", '{"sql":"select * from t where id=$1","params":[123]}')
69
+ ```
70
+
71
+ ## Error Handling
72
+
73
+ - **Missing required / invalid type**: re-read schema and correct `params_json`.
74
+ - **Unknown tool name**: re-run `mcp_search` and use exact returned name.
75
+ - **Server-side error**: report clearly; retry only with changed inputs.
76
+ - **No relevant tools**: use native fallback tools/workflow.
77
+
78
+ ## Practical Boundaries
79
+
80
+ - Reuse known tool names in the same task; avoid unnecessary re-search.
81
+ - Re-search when intent changes materially.
82
+ - Do not loop retries blindly; each retry must change query or params.
83
+
84
+ For full examples, see [references/workflows.md](references/workflows.md).
@@ -0,0 +1,184 @@
1
+ # Complete Workflow Examples
2
+
3
+ ---
4
+
5
+ ## Example 0: Check MCP before web search
6
+
7
+ **Goal:** Research a topic — but before using a built-in web search, check if a configured MCP server already handles this.
8
+
9
+ ```
10
+ mcp_search("search the web")
11
+ ```
12
+
13
+ **Tool card returned:**
14
+ ```
15
+ Tool: brave::web_search
16
+ Description: Search the web using the Brave Search API.
17
+ Input Schema:
18
+ - query (string, required): The search query
19
+ - count (number, optional): Number of results (default: 10)
20
+ ```
21
+
22
+ **Call:**
23
+ ```
24
+ mcp_call("brave::web_search", '{"query": "MCP server authentication patterns", "count": 5}')
25
+ ```
26
+
27
+ If no web-search tool appears, fall back to whichever search capability is natively available.
28
+
29
+ **The pattern applies everywhere:**
30
+ - Need to fetch a URL? → `mcp_search("fetch a webpage")` before using curl
31
+ - Need to query a DB? → `mcp_search("run a SQL query")` before writing a connection script
32
+ - Need to post to Slack? → `mcp_search("send a Slack message")` before building an API request
33
+ - Need to read a file? → `mcp_search("read a local file")` before opening a shell
34
+
35
+ The configured MCP catalog is the toolbox. Exhaust it before doing things the hard way.
36
+
37
+ These end-to-end examples show the full search → read tool card → call sequence with realistic inputs.
38
+
39
+ ---
40
+
41
+ ## Example 1: Read a local file
42
+
43
+ **Goal:** Read the contents of `/tmp/report.txt`.
44
+
45
+ ```
46
+ mcp_search("read a local file")
47
+ ```
48
+
49
+ **Tool card returned:**
50
+ ```
51
+ Tool: filesystem::read_file
52
+ Description: Read the complete contents of a file from the local filesystem.
53
+ Input Schema:
54
+ - path (string, required): Absolute path to the file
55
+ - encoding (string, optional): File encoding (default: "utf-8")
56
+ ```
57
+
58
+ **Call:**
59
+ ```
60
+ mcp_call("filesystem::read_file", '{"path": "/tmp/report.txt"}')
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Example 2: Create a GitHub pull request
66
+
67
+ **Goal:** Open a PR from branch `feature/add-auth` to `main`.
68
+
69
+ ```
70
+ mcp_search("create a GitHub pull request")
71
+ ```
72
+
73
+ **Tool card returned:**
74
+ ```
75
+ Tool: github::create_pull_request
76
+ Description: Creates a new pull request in a GitHub repository.
77
+ Input Schema:
78
+ - owner (string, required): Repository owner (user or org)
79
+ - repo (string, required): Repository name
80
+ - title (string, required): PR title
81
+ - head (string, required): Branch containing the changes
82
+ - base (string, required): Branch to merge into
83
+ - body (string, optional): PR description in Markdown
84
+ ```
85
+
86
+ **Call:**
87
+ ```
88
+ mcp_call("github::create_pull_request", '{
89
+ "owner": "acme-corp",
90
+ "repo": "backend",
91
+ "title": "Add JWT authentication",
92
+ "head": "feature/add-auth",
93
+ "base": "main",
94
+ "body": "Implements JWT-based auth as described in issue #42."
95
+ }')
96
+ ```
97
+
98
+ ---
99
+
100
+ ## Example 3: Query a database
101
+
102
+ **Goal:** Count active users in a Postgres database.
103
+
104
+ ```
105
+ mcp_search("execute a SQL query against a Postgres database")
106
+ ```
107
+
108
+ **Tool card returned:**
109
+ ```
110
+ Tool: postgres::query
111
+ Description: Execute a read-only SQL query against the configured Postgres database.
112
+ Input Schema:
113
+ - sql (string, required): SQL statement to execute
114
+ - params (array, optional): Positional parameters for parameterized queries
115
+ ```
116
+
117
+ **Call:**
118
+ ```
119
+ mcp_call("postgres::query", '{"sql": "SELECT COUNT(*) FROM users WHERE active = true"}')
120
+ ```
121
+
122
+ ---
123
+
124
+ ## Example 4: Multi-step workflow (search once, call multiple times)
125
+
126
+ **Goal:** List files in a directory, then read one of them.
127
+
128
+ ```
129
+ mcp_search("list files in a directory")
130
+ ```
131
+
132
+ **Tool card returned:**
133
+ ```
134
+ Tool: filesystem::list_directory
135
+ Description: List the files and subdirectories in a given directory.
136
+ Input Schema:
137
+ - path (string, required): Absolute path to the directory
138
+ - recursive (boolean, optional): Include subdirectories (default: false)
139
+ ```
140
+
141
+ **Call 1 — list:**
142
+ ```
143
+ mcp_call("filesystem::list_directory", '{"path": "/tmp/project"}')
144
+ ```
145
+
146
+ **Result:** `["README.md", "config.json", "src/"]`
147
+
148
+ **Call 2 — read (reuse known tool name `filesystem::read_file` from Example 1):**
149
+ ```
150
+ mcp_call("filesystem::read_file", '{"path": "/tmp/project/config.json"}')
151
+ ```
152
+
153
+ No second `mcp_search` needed — once a tool name is known, reuse it directly.
154
+
155
+ ---
156
+
157
+ ## Example 5: Handling a failed search
158
+
159
+ **Goal:** Get the current weather for a city, but the first query returns nothing.
160
+
161
+ ```
162
+ mcp_search("weather")
163
+ ```
164
+
165
+ **Result:** No matches.
166
+
167
+ **Rephrase with action verb + domain:**
168
+ ```
169
+ mcp_search("get current weather conditions for a city")
170
+ ```
171
+
172
+ **Tool card returned:**
173
+ ```
174
+ Tool: weather-api::current
175
+ Description: Fetches current weather conditions for a given city or coordinates.
176
+ Input Schema:
177
+ - city (string, required): City name or "lat,lon" coordinates
178
+ - units (string, optional): "metric" or "imperial" (default: "metric")
179
+ ```
180
+
181
+ **Call:**
182
+ ```
183
+ mcp_call("weather-api::current", '{"city": "San Francisco", "units": "imperial"}')
184
+ ```