Prefer the hosted server
Option A keeps your credentials out of config files entirely and gives you live, revocable, scope-limited access. Reach for the bridge only when you specifically need stdio.
Give a self-hosted OpenClaw AI agent access to iSearchFrom localized search, SERP, and ad data — over the hosted iSearchFrom MCP server, or a local bridge for stdio-only setups.
OpenClaw is an open-source, self-hosted AI agent with native Model Context Protocol (MCP) support. Because iSearchFrom runs a hosted MCP server, the simplest path is to point OpenClaw straight at it — no bridge, no API key in a config file. A local bridge is still available if you specifically want stdio transport or a hand-picked tool subset.
This is the least setup and gives OpenClaw the full, permission-scoped tool catalog with OAuth — your credentials never sit in a config file.
OpenClaw reads MCP servers from its config (~/.openclaw/openclaw.json). Add iSearchFrom as an HTTP
MCP server:
{ "mcp": { "servers": { "isearchfrom": { "url": "https://api.isearchfrom.com/mcp" } } }}On first use OpenClaw opens a browser to sign in to iSearchFrom, pick the organization the agent acts in, and approve permissions. See Permissions & security to scope this tightly.
openclaw mcp listopenclaw mcp status --verboseYou should see isearchfrom connected with its tools available.
That’s it — your agent now has the tools in the tool catalog, bounded by the permissions you approved. The rest of this page is only needed if you want the local-bridge approach.
Use this only if you need stdio transport, want to expose a deliberately small tool subset, or run in an environment where the OAuth browser flow isn’t practical. The bridge is a small local server you own that turns iSearchFrom REST calls into MCP tools, authenticated with an API key.
openclaw --version).fetch).mkdir isearchfrom-mcp && cd isearchfrom-mcpnpm init -ynpm pkg set type=modulenpm install @modelcontextprotocol/sdk zodCreate server.js:
#!/usr/bin/env nodeimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';import { z } from 'zod';
const BASE_URL = process.env.ISEARCHFROM_BASE_URL ?? 'https://api.isearchfrom.com/v1';const API_KEY = process.env.ISEARCHFROM_API_KEY;
if (!API_KEY) { console.error('ISEARCHFROM_API_KEY is not set'); process.exit(1);}
async function api(path, init = {}) { const res = await fetch(`${BASE_URL}${path}`, { ...init, headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY, ...(init.headers ?? {}) } }); const body = await res.json().catch(() => ({})); if (!res.ok) { throw new Error(`iSearchFrom API ${res.status}: ${JSON.stringify(body)}`); } return body;}
const server = new McpServer({ name: 'isearchfrom', version: '1.0.0' });
server.tool( 'serp_proxy_search', 'Run a localized Google or Bing search from a chosen country, city, and device, then return the parsed organic and ad results.', { query: z.string().describe('Search query, worded as a real user would type it.'), country: z.string().describe('ISO country code, e.g. "US", "GB", "BR".'), language: z.string().describe('Language code, e.g. "en", "pt".'), engine: z.string().default('google').describe('Search engine: "google" or "bing".'), device: z.string().default('desktop').describe('Device: "desktop" or "mobile".'), city: z.string().optional().describe('Optional city-level location.'), pages: z .array(z.number().int().min(1).max(10)) .default([1]) .describe('Result pages to capture (1-10).') }, async (args) => { // 1. Create the search. It runs asynchronously server-side. const search = await api('/tools/serp/proxy-search', { method: 'POST', body: JSON.stringify(args) });
// 2. Poll until the search finishes, fails, or times out. const deadline = Date.now() + 60_000; let current = search; while (current.status === 'pending' || current.status === 'running') { if (Date.now() > deadline) break; await new Promise((resolve) => setTimeout(resolve, 1500)); current = await api(`/tools/serp/proxy-search/${search.id}`); }
return { content: [{ type: 'text', text: JSON.stringify(current, null, 2) }], isError: current.status === 'failed' }; });
await server.connect(new StdioServerTransport());{ "mcp": { "servers": { "isearchfrom": { "command": "node", "args": ["/absolute/path/to/isearchfrom-mcp/server.js"], "env": { "ISEARCHFROM_API_KEY": "isk_your_key_here" } } } }}Prefer the hosted server
Option A keeps your credentials out of config files entirely and gives you live, revocable, scope-limited access. Reach for the bridge only when you specifically need stdio.
Least-privilege keys (bridge)
If you use the bridge, give the key only the search permission it needs, and use a separate key per integration so you can rotate or revoke it independently.
Expose only read tools
Keep an autonomous agent’s tools search-and-read only; don’t wrap create, update, or delete for unattended loops.
Mind tokens and rate limits
Proxy searches consume tokens; the API’s default limit is 600 requests per minute. Bound agent loops so a runaway plan can’t drain the balance. See Errors, pagination & rate limits.
| Symptom | Likely cause | Fix |
|---|---|---|
Tool not listed in mcp status | Server failed to start / connect | For Option A, recheck the URL and re-authorize. For the bridge, run node server.js directly. |
| Browser sign-in never opens (A) | Client didn’t complete OAuth | Confirm the URL is https://api.isearchfrom.com/mcp and your OpenClaw version supports HTTP MCP. |
401 / API key invalid (B) | Wrong, disabled, or expired key | Recreate the key in API Keys. |
403 / forbidden | Missing permission or feature | Grant the search permission; confirm the org has the Search Engine Proxy feature. |
429 / too many requests | Rate limit exceeded | Slow the agent; the default limit is 600 requests per minute. |
| Empty or failed results | No token balance or unsupported options | Check the balance and review Search Parameters. |