From d7a7520ac76f5fb503928bc3a852658c3283b462 Mon Sep 17 00:00:00 2001 From: George Pickett Date: Thu, 27 Aug 2026 21:19:42 -0700 Subject: [PATCH] Add free Parallel Search MCP connection --- README.md | 20 ++++++++++++++++++++ agent/connections/parallel.ts | 8 ++++++++ agent/instructions.md | 4 +++- knip.config.ts | 1 + tests/parallel-connection.test.ts | 24 ++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 agent/connections/parallel.ts create mode 100644 tests/parallel-connection.test.ts diff --git a/README.md b/README.md index 4e593d9a..c9eb9ad2 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,26 @@ from contacts that are not on this allowlist. The connector without them permits outbound token access but does not forward incoming messages to OpenInstinct. +## Public web search + +The root agent can search the web and read public pages through +[Parallel Search MCP](https://docs.parallel.ai/integrations/mcp/search-mcp). +The native Eve connection in `agent/connections/parallel.ts` is discovered +automatically. No Parallel account, API key, OAuth setup, or extra package is +required. Free anonymous access has lower rate limits and is intended for +exploration and light use. + +The agent discovers the connection with `connection_search`, then calls +`parallel__web_search` or `parallel__web_fetch`. Only those two tools are +allowed. Existing gateway search remains available. Interactive browsing, +sign-in, forms, and purchases still belong to the browser worker. + +Search terms and requested URLs are sent to Parallel. Use this connection only +for public information, never private messages, account data, vault contents, +credentials, or authenticated or signed URLs. Retrieved pages remain +untrusted content. Restart development or rebuild the application after +changing connection files. + ## Google Workspace connection OpenInstinct can use a user's Gmail, Calendar, and read-only Contacts through a diff --git a/agent/connections/parallel.ts b/agent/connections/parallel.ts new file mode 100644 index 00000000..e1b90081 --- /dev/null +++ b/agent/connections/parallel.ts @@ -0,0 +1,8 @@ +import { defineMcpClientConnection } from "eve/connections"; + +export default defineMcpClientConnection({ + url: "https://search.parallel.ai/mcp", + description: + "Search the public web and extract content from public URLs with Parallel. Find current facts and sources without an API key. Send only public information, never private account data or secrets.", + tools: { allow: ["web_search", "web_fetch"] }, +}); diff --git a/agent/instructions.md b/agent/instructions.md index 69b7c3ca..0eb2c786 100644 --- a/agent/instructions.md +++ b/agent/instructions.md @@ -32,6 +32,8 @@ The main conversation is the control plane. Coordinate the user's work there, de - Keep routine browser assignments fast and bounded. Aim to finish an uncomplicated browser task within 90 seconds and six browser tool calls. Do not keep retrying the same page state, selector, or action. - Recover from a browser failure with at most two materially different tactics. If neither works, stop promptly and report the last verified state and exact blocker instead of leaving the task running. - Prefer the narrowest capable integration: root vault setup for non-secret coordination, `worker` for browser work, connected tools for their supported services, and public search or APIs for public facts. +- The `parallel` connection offers public web search and public page extraction. Discover `parallel__web_search` and `parallel__web_fetch` with `connection_search` when those capabilities fit the task. Keep source links in factual answers, and report search failures or rate limits instead of inventing results. +- Send only public search terms and public URLs to `parallel`. Never send credentials, vault data, private messages, private account content, or authenticated or signed URLs to public search or extraction tools. - Prefer `google_workspace_read` and `google_workspace_write` over browser automation for connected Gmail, Calendar, and Contacts work. Never ask for Google tokens or credentials in chat. If authorization is required, let the connection surface its sign-in challenge. - Use exact Gmail message IDs for reversible inbox updates. Before sending email or creating a calendar event, make the recipients, content, timing, attendees, and other material fields explicit in the approval request. - Keep the user's constraints intact while delegating, comparing alternatives, recovering from failures, and synthesizing results. @@ -60,7 +62,7 @@ The main conversation is the control plane. Coordinate the user's work there, de # Worker coordination -- Delegate every task that requires navigating, inspecting, or acting on a website to `worker`. Do not use a generic agent copy or any browser-execution tool yourself. +- Delegate every task that requires a browser to navigate, inspect, or act on a website to `worker`. Public web search and reading public pages through search tools do not need a worker. Do not use a generic agent copy or any browser-execution tool yourself. - Give the worker one bounded browser outcome, all relevant non-secret context, the user's constraints, and any exact transaction approval already granted. The worker does not see the parent conversation. - Every initial or resumed `worker` call must set `outputSchema` to `{ "type": "object", "properties": { "status": { "type": "string", "enum": ["success", "failure"] }, "message": { "type": "string", "minLength": 1 } }, "required": ["status", "message"], "additionalProperties": false }`. Never omit it, including when passing an existing `agentId`; persistent workers otherwise return unstructured conversation text. - Treat a background-task receipt as acceptance, not completion. Briefly acknowledge accepted work in the root conversation and end the turn. When Eve returns the worker result, synthesize it in the root conversation. diff --git a/knip.config.ts b/knip.config.ts index 77baa791..33059b43 100644 --- a/knip.config.ts +++ b/knip.config.ts @@ -3,6 +3,7 @@ import type { KnipConfig } from "knip"; export default { entry: [ "agent/channels/**/*.ts", + "agent/connections/**/*.ts", "agent/hooks/**/*.ts", "agent/subagents/**/*.ts", "agent/tools/**/*.ts", diff --git a/tests/parallel-connection.test.ts b/tests/parallel-connection.test.ts new file mode 100644 index 00000000..f8105566 --- /dev/null +++ b/tests/parallel-connection.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from "vitest"; +import parallel from "../agent/connections/parallel"; + +describe("Parallel public web connection", () => { + it("uses the free MCP endpoint", () => { + expect(parallel.url).toBe("https://search.parallel.ai/mcp"); + }); + + it("exposes only public search and page extraction", () => { + expect(parallel.tools).toEqual({ + allow: ["web_search", "web_fetch"], + }); + }); + + it("does not attach credentials or session-derived arguments", () => { + expect(parallel.auth).toBeUndefined(); + expect(parallel.headers).toBeUndefined(); + expect(parallel.toolCall).toBeUndefined(); + }); + + it("describes the public-only data boundary during discovery", () => { + expect(parallel.description).toContain("Send only public information"); + }); +});