sulus-web-mcp
This server provides stateless browser-based web research via three Playwright MCP tools, each opening a URL, extracting content, and closing the browser context.
browse_page: Fetch a public page's title, final URL, and cleaned markdown (or links/title only) with configurable wait and extraction modes.
extract_links: Get a markdown list of unique outbound links (href + text), optionally limited and restricted to the same origin.
search_page: Search a page's text for a case-insensitive query and return up to N snippets with surrounding context.
Each call is self-contained: navigate, extract, close; no cookies or sessions shared between calls.
No click, type, login, or file-upload capabilities.
Enforces safety by blocking localhost, private networks, and other risky hosts; HTTPS only in production.
Usable over HTTP MCP with bearer auth or stdio for local Cursor, and ready for ai-phone-system agents.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@sulus-web-mcpBrowse https://example.com and summarize the content"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
sulus-web-mcp
Stateless Playwright MCP for live-web research. Agents get three tools — browse_page, extract_links, search_page — each of which opens a URL, extracts text, and closes the browser context. No click/type/login tools.
Built for Sulus Agents (ai-phone-system MCP connectors) and the rest of the ecosystem (Cursor, MCPConnect). Laravel cannot run Chromium in-process; this service is the HTTP MCP those agents call.
Tools
Tool | When to use | What it returns |
| Need the content of a public page | Title, final URL, cleaned markdown (or links/title only) |
| Need outbound links | Markdown list of href + text |
| Need excerpts matching a query | Up to N snippets around the query |
Each call is self-contained (navigate + extract). That matches ai-phone-system's connector client, which POSTs tools/list / tools/call with no MCP session.
Related MCP server: mcp-playwright
Requirements
Node.js 20+
Playwright Chromium (
npx playwright install chromiumafternpm install)
Setup
cd /Users/macbook/Workshop/Sulus.ai/sulus-web-mcp
npm install
npx playwright install chromium
cp .env.example .env
# set MCP_SHARED_SECRET
npm run buildHTTP (Agents, team, production)
npm run start:http
# or: npm run dev:httpHealth: GET /health
MCP: POST /mcp (JSON-RPC, Streamable HTTP)
Variable | Description |
| Bearer token required on |
| Bind (default |
| Allowed |
| Set |
| Optional host lists |
| Default 25000 (stay under Agents' 60s tool timeout) |
| Default 3 |
| Per bearer token, default 30 |
| Default 18000 (Agents truncate at 20k) |
Logs are one JSON line per browse (url origin+path, status, ms). Page bodies are never logged.
Production (browse.sulus.ai, same droplet as MiniERP)
Native Node + systemd + nginx — no Docker. Runbook, unit file, nginx, and ./deploy.sh:
# On the droplet, as deploy:
cp deploy/vps/env.production.example .env.production # set MCP_SHARED_SECRET
./deploy.shstdio (local Cursor)
npm run start.cursor/mcp.json:
{
"mcpServers": {
"sulus-web": {
"command": "node",
"args": ["dist/index.js"],
"cwd": "/Users/macbook/Workshop/Sulus.ai/sulus-web-mcp"
}
}
}HTTP from Cursor (after deploy):
{
"mcpServers": {
"sulus-web": {
"url": "https://browse.sulus.ai/mcp",
"headers": {
"Authorization": "Bearer YOUR_SHARED_SECRET"
}
}
}
}Connect from ai-phone-system Agents
Deploy this service behind HTTPS.
In MCP Connectors, add a custom server:
URL:
https://browse.sulus.ai/mcpAuth: Bearer, token =
MCP_SHARED_SECRET
Enable the connector on the agent.
The HTTP handler answers tools/list and tools/call without initialize / Mcp-Session-Id, which is what ExternalMcpClientService sends. Sessionful Streamable HTTP still works for Cursor.
Safety
HTTPS only (unless
ALLOW_INSECURE_HTTP=true)Blocks localhost, RFC1918, link-local, CGNAT, cloud metadata hosts
Resolves DNS and rejects private answers
Aborts in-page requests to blocked URLs
New browser context per call — no cookie sharing
No form-fill / click / file-upload tools
Scripts
npm run typecheck
npm test
npm run buildAvailable Tools
3 toolsbrowse_pageA
Use this when you need the content of a live public web page. It opens the URL in a headless browser, waits for the document, and returns the title, final URL, and cleaned markdown (or links/title only). Returns extracted text, not a screenshot. Does not log in or fill forms.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Absolute HTTPS URL to open. | |
| wait | No | Playwright waitUntil. Default: domcontentloaded. | |
| extract | No | What to return. Default: markdown. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It thoroughly explains that it opens a headless browser, waits for the document, returns text (not screenshots), includes the final URL and title, and explicitly states it does not log in or fill forms. This is highly transparent about side effects and limitations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the primary use case. Every sentence adds value: the first establishes what and when, the second clarifies output format and exclusions. No wasted words and well-structured for quick parsing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that all parameters are documented in the schema, the output format is explained in the description, and there is no explicit output schema, the description is quite complete. It covers return values, limitations, and waiting behavior. It could mention error handling or rate limits, but these are not critical for this straightforward tool. Minor gaps in edge-case coverage keep it from a 5.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds minimal parameter-specific detail beyond the schema: it mentions 'cleaned markdown (or links/title only)' which clarifies what extract does, and implies that wait is for waiting. However, it does not provide new syntax or format details beyond the schema's own descriptions, so it stays at the baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: retrieving content of a live public web page. It specifies the action (opens in headless browser), the resource, and the output (title, final URL, cleaned markdown). It also distinguishes itself from siblings by noting it returns text not screenshots and doesn't log in or fill forms, making it easy for an agent to differentiate.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description opens with a clear 'when to use' condition: 'when you need the content of a live public web page.' It also implicitly states when not to use it by saying 'Does not log in or fill forms.' However, it does not explicitly name alternatives like extract_links or search_page, so it lacks explicit exclusion or alternative guidance. Still, the context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
extract_linksA
Use this when you need outbound links from a public page. It opens the URL and returns unique hrefs with link text. Returns a markdown list. Optionally restrict to the same origin.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Absolute HTTPS URL to open. | |
| limit | No | Max links to return (1–100). Default: 50. | |
| same_origin | No | If true, only keep links on the page origin. Default: false. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It states that the tool 'opens the URL', returns unique hrefs with link text, produces a markdown list, and offers same-origin restriction. These details convey the key behavioral traits without revealing any hidden side effects, though it doesn't mention potential failures or authentication needs, which is acceptable for a public-page tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences long, front-loaded with the primary use case, and each sentence earns its place. It covers the main action, the return format, and an optional parameter without any redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple extraction tool with three parameters and no output schema, the description is sufficiently complete. It explains the core behavior, the output format, and the optional filtering. The limit parameter is not mentioned, but it's fully covered by the schema, so no critical information is missing. The lack of negative guidance on siblings is a minor gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds some value by mentioning 'same origin' which maps to the same_origin parameter, and 'unique hrefs' which relates to the limit/deduplication behavior, but it doesn't introduce additional semantics beyond what the schema already documents.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: extracting outbound links from a public page. It specifies the verb ('extract'), the resource ('outbound links from a public page'), and the outcome ('returns unique hrefs with link text'). This distinguishes it from siblings like browse_page and search_page, which serve different purposes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The opening phrase 'Use this when you need outbound links from a public page' provides clear context for when to invoke the tool. However, it does not explicitly mention when not to use it or name alternative siblings for comparison, leaving the exclusion logic implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_pageA
Use this when you need excerpts matching a query on a public page. It opens the URL, extracts text, and returns up to N case-insensitive snippets around the query. Returns markdown snippets.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Absolute HTTPS URL to open. | |
| query | Yes | Case-insensitive text to find. | |
| max_matches | No | Max snippets (1–20). Default: 8. | |
| context_chars | No | Characters of context on each side. Default: 160. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations present, the description carries the full behavioral burden. It discloses the core mechanics (opens URL, extracts text, returns up to N case-insensitive snippets, returns markdown). However, it doesn't disclose failure modes—such as paywalls, JavaScript-rendered pages that yield no text, or redirects—nor timeouts or how empty results are reported. This is adequate but leaves meaningful behavioral gaps.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tightly-written sentences, no filler. The usage trigger is front-loaded in the first clause, and the behavioral detail follows efficiently. Every phrase earns its place and the result format is included.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a search tool with four fully-documented parameters and no output schema, the description covers the essential call contract: purpose, behavior, and return format (markdown snippets). The main omissions—failure behaviors, authentication assumptions, and page-format limits—are secondary for a straightforward public search operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so per the rubric the baseline is 3. The description adds marginal value beyond the schema: 'case-insensitive' reinforces the query property and 'up to N' maps to max_matches, but these merely echo schema semantics rather than adding new detail. No compensatory burden since coverage is complete.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific, distinct purpose: retrieving query-matching excerpts from a page. It names the resource (public page) and result type (markdown snippets), which clearly separates it from browse_page (full page reading) and extract_links (link harvesting). It earns a 4 but not a 5 because it never explicitly names its siblings to draw the distinction.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Use this when you need excerpts matching a query on a public page' is an explicit when-to-use statement with a clear triggering condition. The phrase 'public page' implicitly scopes the tool to public content. It doesn't name alternatives or exclusions, but the context is clear enough for an agent to select it correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
browse_page - First observed
extract_links - First observed
search_page
TDQS
Scored across 3 tools
Each tool has a clearly distinct purpose: browse_page returns page content, extract_links retrieves outbound links, and search_page provides matching snippets. No ambiguity exists between them despite all operating on URLs.
All tool names follow a consistent verb_noun pattern (browse_page, extract_links, search_page) using snake_case. The pattern is predictable and uniform across the three tools.
Three tools is a well-scoped count for a web browsing server. Each tool handles a distinct aspect (content, links, search) and none are redundant or missing for the core functionality.
The tool surface covers the primary browsing capabilities (content extraction, link discovery, and targeted searching). Minor gaps like screenshot capture or form interaction exist, but they are beyond the stated scope and the tools together handle the typical use cases effectively.
Maintenance
Related MCP Connectors
Agent-native search engine with live web research optimized for AI agents.
Web research for agents: quality-scored Google search, webpage extraction, and deep research.
Live web access for agents: scrape, SERP search, crawl/map, 74 collectors, datasets, proxies.
Web search, browser automation, scraping, crawling and CAPTCHA solving for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceEnables web search across Bing, DuckDuckGo, and Google using Playwright automation, with tools for retrieving search results and extracting page text content.7 npmMIT
- AlicenseBqualityDmaintenanceProvides browser automation capabilities for LLM applications, enabling web page interaction and data extraction via Playwright.1230 PyPI10MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to control a web browser using Playwright, supporting navigation, interaction, and data extraction through natural language.MIT
- FlicenseAqualityDmaintenanceEnables web search, content fetching, dynamic page scraping, and URL preview via DuckDuckGo, Playwright, and Cheerio, without requiring API keys.4-