String docs
MCP server

MCP /v3 endpoint

Connect to the /v3 MCP endpoint and see each tool's inputs, outputs, read or write classification, billing, errors and limits.

https://mcp.usestring.ai/v3/mcp serves six tools, each of which does one kind of thing: a search, a page read, a request with a body, or one step of a site crawl. Every tool's MCP annotations describe exactly what a call can do, so your client can let reads run and ask you before a call that spends your balance or sends data to another site. If you need browser actions, site integrations, or to cancel or list crawl jobs, use /v2/mcp instead.

Connect

Add this URL to your MCP client as a remote server. It speaks Streamable HTTP, and there is nothing to install.

https://mcp.usestring.ai/v3/mcp

The client steps on Remote (hosted) MCP work here too, with this URL in place of the /v2 one.

Authenticate

Sign in with String, or send an API key. Either way, each call spends one Web Access key.

Sign in with String (OAuth)

A request with no credential gets 401 and a WWW-Authenticate header that points the client at the protected resource metadata:

https://mcp.usestring.ai/.well-known/oauth-protected-resource/v3/mcp

The client finds the authorization server there and runs the authorization code flow:

SettingValue
Scopeweb_access. It is the only scope, and it grants all six tools.
PKCERequired, with S256.
Client registrationA client ID metadata document: the client ID is an HTTPS URL that serves the client's metadata. There is no dynamic client registration endpoint.
Token endpoint authnone for public clients, client_secret_basic or client_secret_post.

On String's consent screen you choose which of your organization's Web Access keys the app may use, or create one for it. The app never sees the key. Sign in with String shows the screens, and Managing connected apps shows how to disconnect an app.

API key

Send the key as a bearer token on every request:

Authorization: Bearer YOUR_API_KEY

Create a key under Settings in the portal; see Getting a key.

Permissions

The key decides what a call may do. Calls are billed to the key's organization and follow that organization's access rules, and solveCaptcha: true only has an effect when the key is allowed to solve captchas. The web_access scope has no read-only variant, so read-only access is something your client enforces, using the classifications below.

Smallest working example

Every request is a JSON-RPC message, and the server keeps no session, so one tools/call is a complete request. This fetches example.com as Markdown:

curl https://mcp.usestring.ai/v3/mcp \
  -H "Authorization: Bearer $STRING_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"web_access_fetch","arguments":{"url":"https://example.com"}}}'

The answer is one server-sent event. Its data is the JSON-RPC result, trimmed here to the headers shown:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      { "type": "text", "text": "HTTP 200, 174 bytes" },
      { "type": "text", "text": "{\"statusCode\":200,\"headers\":{…},\"body\":\"# Example Domain\\n\\n…\"}" }
    ],
    "structuredContent": {
      "statusCode": 200,
      "headers": {
        "Content-Type": ["text/markdown; charset=utf-8"],
        "X-Billed-Request-Type": ["request_standard"],
        "X-Final-Url": ["https://example.com/"],
        "X-Status-Code": ["200"]
      },
      "body": "# Example Domain\n\nThis domain is for use in documentation examples without needing permission. This is not a service; avoid relying on it for testing and monitoring purposes."
    }
  }
}

Every tool answers in this shape: a one-line summary, then the same JSON as text and as structuredContent, which matches the tool's output schema.

Tools

ToolClassificationreadOnlyHintdestructiveHintidempotentHintopenWorldHint
web_access_searchReadtruefalsefalsetrue
web_access_fetchReadtruefalsefalsetrue
web_access_sitemap_quoteWritefalsefalsefalsetrue
web_access_sitemap_startSensitive writefalsefalsefalsetrue
web_access_sitemap_statusReadtruefalsetruefalse
web_access_requestSensitive writefalsetruefalsetrue
ClassificationWhat a call does
ReadRetrieves information and changes nothing, on String or on any other site.
WriteCreates data on String, here a crawl quote, and spends nothing.
Sensitive writeSpends your balance or sends data to another site. A client should ask before every call.

Each call is billed as described under its tool, at the rates on Pricing.

Searches the web for a query and returns ranked results, along with the other parts of the Google results page, such as the knowledge panel, AI overview and local listings. It reads search results only and changes nothing.

FieldTypeDefaultDescription
querystring—Required. The search query. Quoted phrases and site: work.
searchCountintegernoneOrganic results wanted, 1 to 300. Pages are collected until that many are in hand. Not with format: "raw" or aiMode.
pageinteger1Results page to start from, 1 to 30. shopping has page 1 only, images 3 pages and places 15. page and searchCount together stay within the first 300 results.
countrystringUSISO 3166-1 alpha-2 code the search runs from.
languagestringnoneResults language, such as en or pt-br.
locationstringnoneA place name such as London, 1 to 200 characters. Results are biased toward it.
coordinatesobjectnone{ latitude, longitude, radius }: latitude -90 to 90, longitude -180 to 180, radius 1 to 1,000,000 meters (default 5000). Wins over location.
searchTypestringwebweb, images, videos, shopping, books, places or forums.
dateRangestring or objectnonehour, day, week, month or year, or { "from": "2026-01-01", "to": "2026-06-30" }. With searchType web, videos or forums only.
sortBystringrelevancerelevance or date. With searchType web, videos or forums only.
safeSearchbooleanfalsetrue removes explicit results.
includeOmittedResultsbooleanfalsetrue includes results Google hides as very similar.
autocorrectbooleantruefalse searches the query as typed.
restrictCountrystringnoneTwo-letter code; keeps only pages from that country.
verbatimbooleanfalsetrue matches the query's words exactly. With searchType web, videos or forums only.
formatstringstructuredstructured for parsed results, or raw for the results page as HTML, at most 60,000 bytes per call. raw works with searchType: "web" only.
aiModebooleanfalsetrue returns Google AI Mode's answer and its sources instead of results. Not with searchCount, page, format, searchType, dateRange, sortBy or the filters.
aiOverviewbooleanfalsetrue fills an AI overview that loads after the results page. With searchType: "web" only, and not with aiMode.

Any other field is rejected before anything is sent. web_access_search on MCP tools explains each field and combination in full.

Returns: results, each with position, rank, title, url, snippet, displayUrl, displayText, and source and timestamp when Google shows them; zeroResults, true when nothing matched; each other part of the page Google showed, such as entity, places, overviews and peopleAlsoAsk; aiMode when you asked for it; paging when you sent searchCount; and html, htmlBytes and htmlTruncated with format: "raw". The fields are those of POST /search.

Example call
{ "query": "model context protocol specification" }
Response, trimmed to the first result and the fields shown
{
  "results": [
    {
      "position": 1,
      "rank": 1,
      "title": "Specification",
      "url": "https://modelcontextprotocol.io/specification/2025-06-18",
      "snippet": "Model Context Protocol (MCP) is an open protocol that enables seamless integration between LLM applications and external data sources and tools.",
      "displayUrl": "https://modelcontextprotocol.io › specification",
      "displayText": "https://modelcontextprotocol.io › specification",
      "source": "Model Context Protocol",
      "timestamp": "Jun 18, 2025"
    }
  ],
  "relatedSearches": ["…"],
  "peopleAlsoAsk": [{ "question": "…" }],
  "overviews": [{ "text": "…", "sources": [{ "title": "…", "url": "…" }] }]
}

Side effects and billing: none outside String. Each results page fetched is billed as one search, so a searchCount call is billed once per page in paging.pages. aiOverview: true is billed at the AI Overview rate on Pricing.

Errors: a value out of range or a combination that doesn't apply fails before anything is sent, with a message naming the field. If an AI Mode answer isn't available, the call fails; retry, or search without aiMode.

web_access_fetch

Fetches a URL with a GET and returns the page, as Markdown by default. It reads the page and changes nothing on the destination. To send a body, use web_access_request.

FieldTypeDefaultDescription
urlstring—Required. The full http or https URL.
formatstringmarkdownmarkdown, raw for the destination's body as it was sent, or json for the destination's own status and headers.
executeJSbooleanfalsetrue renders JavaScript first, for pages that build their content in the browser. Not with headers.
headersobjectnoneRequest headers to send, up to 50, in the order given. A name given twice is an error. Not with executeJS.
countryCodestringnoneISO 3166-1 alpha-2 code; the page is requested from that country.
solveCaptchabooleanfalseWith false, a challenge page fails the call with an error. true has the request attempt the challenge; see Captcha solving.

Returns: statusCode, the destination's HTTP status; headers; and body, the page in the requested format. With markdown or raw, headers includes X-Status-Code and X-Billed-Request-Type; with json, headers are the destination's own. The smallest working example shows a full response.

Side effects and billing: one GET to the destination, which changes nothing there. The request is billed at the rate X-Billed-Request-Type names (see Billed request type), including when the destination answers with an error status such as 404.

Errors: a destination error such as 404 is a successful call carrying that statusCode. A page that can't be returned, including a challenge page with solveCaptcha: false, fails the call; see Errors.

web_access_sitemap_quote

Quotes a crawl of one site, starting from a URL and following same-domain links breadth-first. It creates a job waiting for approval and crawls nothing.

FieldTypeDefaultDescription
urlstring—Required. The URL to start from. The crawl stays on its domain.
maxPagesinteger10Most pages to fetch, 1 to 10,000.
maxDepthinteger2How many links deep to follow from the start URL, 1 to 100.
pathPrefixstringnoneOnly crawl URLs whose path starts with this, such as /docs.
budgetUsdnumberquoteSpend cap in USD, at least 0.0001. The crawl stops as token_cap_exceeded before going over it.
useSitemapbooleanfalseAlso start from the site's /sitemap.xml, which is one more billed page.

A value out of range is rejected; leave a field out to get its default.

Returns: jobId, status (awaiting_approval), estimatedPages and estimatedCostUsd, the most the crawl will cost.

Example call
{ "url": "https://example.com", "maxPages": 500, "maxDepth": 3 }
Response, with jobId shortened
{
  "jobId": "6f1c9f0a-…",
  "status": "awaiting_approval",
  "estimatedCostUsd": "0.0500000000",
  "estimatedPages": 500
}

Side effects and billing: creates one job record. Quoting is free, and each call creates a new job. A quote that is not started within one hour expires: the job becomes failed with errorMessage: "quote_expired".

Errors: 403 when the site is not enabled for your organization; see Access control.

web_access_sitemap_start

Starts a quoted crawl. This is the step that spends money, so call it only after the user has approved the quote's estimatedCostUsd.

FieldTypeDefaultDescription
jobIdstring—Required. The jobId that web_access_sitemap_quote returned.

Returns: jobId and its new status, running.

Side effects and billing: holds estimatedCostUsd, or budgetUsd when that is lower, against your balance, then crawls the site with GET requests, up to maxPages pages. Pages are billed as they are fetched, and the hold is settled against the pages actually crawled when the job ends. Calling it again with the same jobId never starts a second crawl or holds the cost twice: a running job answers running, and a finished one answers with its final status, such as failed for an expired quote.

Errors: 402 when your balance can't cover the hold; the job becomes failed, so quote again after adding funds. 409 with partial_state means the start was interrupted; call this tool again with the same jobId. 404 means the job doesn't exist or belongs to another organization. A canceled or token_cap_exceeded job can't be started; quote a new crawl.

web_access_sitemap_status

Reports a crawl job's status and, once the job has ended, returns a page of the URLs it found. It reads String's job record and changes nothing, so calling it again is always safe.

FieldTypeDefaultDescription
jobIdstring—Required. The jobId that web_access_sitemap_quote returned.
limitinteger1000URLs per page once the job has ended, 1 to 5,000.
offsetinteger0URLs to skip, for paging.

Returns: jobId and status, plus whichever of estimatedCostUsd, pending, processed, pagesProcessed, errorMessage and finishedAt the job reports. Once the job has ended it also returns total and urls. Each URL has url, statusCode (0 if it was found but not fetched), depth, parentUrl, isSitemap, sourceType, and error when fetching it failed. See Get discovered URLs.

A running job, with jobId shortened
{ "jobId": "6f1c9f0a-…", "status": "running", "pending": 42, "processed": 158 }

Side effects and billing: none, and it is free.

Errors: 404 means the job doesn't exist or belongs to another organization. If a job has ended but its URLs can't be read, the call fails instead of returning the status alone; call it again.

web_access_request

Sends a POST, PUT or PATCH with a body to a URL you give it, and returns the destination's response. Some of these calls only read, such as a GraphQL query or a search form, but a call can create, change or delete data on the destination, so treat every call as one that can.

FieldTypeDefaultDescription
urlstring—Required. The full http or https URL.
methodstring—Required. POST, PUT or PATCH. GET is refused; read a page with web_access_fetch.
bodystring or objectnoneA string is sent as it is; an object is sent as the JSON you wrote.
formatstringjsonjson for the destination's status and headers, markdown, or raw.
headersobjectnoneRequest headers to send, up to 50, in the order given. A name given twice is an error.
countryCodestringnoneISO 3166-1 alpha-2 code; the request is sent from that country.
solveCaptchabooleanfalseWith false, a challenge page fails the call with an error. true has the request attempt the challenge.

Returns: statusCode, headers and body, as for web_access_fetch.

Example call
{ "url": "https://httpbin.org/post", "method": "POST", "body": { "name": "widget" } }
Response, with headers and body trimmed
{
  "statusCode": 200,
  "headers": { "Content-Type": ["application/json"] },
  "body": "{\"json\":{\"name\":\"widget\"},\"url\":\"https://httpbin.org/post\"}"
}

Side effects and billing: sends the request and its body to the destination, which may act on it. A call is not idempotent: sending it twice can act twice. A call that fails may still have reached the destination, so check the destination's state before you send the same request again. Billed like web_access_fetch.

Errors: 403 with a reason when the method needs approval for that path, such as a sign-in path; see Access control & KYC.

Crawl status

A crawl moves through these statuses, read with web_access_sitemap_status:

statusMeaningNext step
awaiting_approvalQuoted. Nothing is crawled or held.Show the user estimatedCostUsd, then start it.
partial_stateA start was interrupted.Call web_access_sitemap_start again.
runningCrawling. pending and processed count the URLs.Poll again.
completedFinished.Read urls, paging with offset to total.
token_cap_exceededStopped at the spend cap before maxPages. The URLs found so far are readable.Read urls.
failedEnded without finishing; errorMessage says why, such as quote_expired.Read urls, or quote again.
canceledCanceled. The pages already fetched stay billed and readable.Read urls.

So a crawl takes three tools in order:

  1. web_access_sitemap_quote returns a jobId and estimatedCostUsd.
  2. After the user approves that cost, web_access_sitemap_start starts the job.
  3. web_access_sitemap_status reports progress. Poll every few seconds for a small crawl, and every few tens of seconds for a crawl of hundreds of pages. Once the job has ended, the same tool returns its URLs.

Errors

A request the endpoint refuses gets an HTTP error before any tool runs:

StatusMeaningWhat to do
401No credential, or an OAuth token that is invalid or expired. WWW-Authenticate names the metadata and scope="web_access".Sign in again.
403The token lacks the web_access scope, or no key is connected for this app.Sign in again and choose a key.
413The request body is over 4 MiB.Send a smaller body.

A tool call that fails returns a result with isError: true and a text message. A message about your input names the field and the rule, such as maxPages 0 is out of range 1-10000, and nothing was sent. A refusal from the Web Access API names its HTTP status, such as status=402, with the API's error text:

StatusMeaningWhat to do
400The API rejected a field.Fix the field the message names.
401The API key is invalid or revoked.Use an active key.
402Your organization's balance can't cover the call.Add funds under Usage & Plans, then retry.
403The destination isn't enabled for your organization, or the method needs approval for the path.See Access control & KYC.
404The crawl job doesn't exist or belongs to another organization.Check the jobId.
409partial_state: a crawl start was interrupted.Call web_access_sitemap_start again.
429Over the rate limit.Wait, then retry with backoff.
502The destination returned an error, or the request could not be completed.Retry later; read errorType in the message when present.
504The destination did not respond in time.Retry later.

A failed call is not billed, apart from the cases on Pricing. The general meaning of each status and errorType is on Errors.

Rate limits

Tool calls are Web Access API requests made with your key, so they count toward your organization's rate limit; a call over it fails with status=429. Each tool's own limits are in its field table above.

See also