Documentation

Fringe speaks the Model Context Protocol (MCP) over HTTP. Connect any MCP client with your API key, or call the endpoint directly from your own agent.

Quickstart

Two things to know:

  • ·Endpoint: https://platform.bearinglabs.ai/mcp
  • ·Auth: a bearer token. Create one on the API keys page.

Every request is authenticated with your key and metered against your credit balance. Pass it as a header:

header
Authorization: Bearer fringe_sk_live_...

Claude Code

Add Fringe as an HTTP MCP server in one command:

shell
claude mcp add --transport http fringe https://platform.bearinglabs.ai/mcp \
  --header "Authorization: Bearer fringe_sk_live_..."

The Fringe tools are now available in your sessions. Verify with /mcp.

Codex

OpenAI Codex CLI reads ~/.codex/config.toml. Bridge the HTTP endpoint with mcp-remote:

~/.codex/config.toml
[mcp_servers.fringe]
command = "npx"
args = [
  "-y", "mcp-remote",
  "https://platform.bearinglabs.ai/mcp",
  "--header", "Authorization: Bearer fringe_sk_live_..."
]

Cursor

Add to .cursor/mcp.json (project) or ~/.cursor/mcp.json (global):

.cursor/mcp.json
{
  "mcpServers": {
    "fringe": {
      "url": "https://platform.bearinglabs.ai/mcp",
      "headers": { "Authorization": "Bearer fringe_sk_live_..." }
    }
  }
}

Claude Desktop

Claude Desktop connects to local (stdio) servers, so bridge with mcp-remote in claude_desktop_config.json:

claude_desktop_config.json
{
  "mcpServers": {
    "fringe": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote",
        "https://platform.bearinglabs.ai/mcp",
        "--header", "Authorization: Bearer fringe_sk_live_..."
      ]
    }
  }
}

Bespoke agents

Any MCP-compatible client works. Clients that support remote servers take a URL plus an Authorization header. Clients that only support local (stdio) servers can wrap the endpoint with mcp-remote:

shell
npx -y mcp-remote https://platform.bearinglabs.ai/mcp \
  --header "Authorization: Bearer fringe_sk_live_..."

To build into your own agent in code, use an MCP SDK over Streamable HTTP (below).

Convert documents to markdown

convert_document fetches a PDF, DOCX, XLSX, PPTX, HTML or EPUB — even a 500-page scanned filing behind a bot-wall — and converts it to clean markdown on a GPU. It's asynchronous: you get a job_id back, poll convert_status until it reports done, then read the result. Conversions are cached by URL, so the same document is only ever converted once.

flow
1. convert_document(url)         -> { status: "queued", job_id }
2. convert_status(job_id)        -> { status: "processing" }      # poll ~every 15s
3. convert_status(job_id)        -> { status: "done", markdown }  # small docs return markdown inline
                                 -> { status: "done", preview, ref, chars }  # large docs return a preview + ref
4. read_document(ref, offset, limit)  # page through a large document's markdown

Large documents come back as a preview plus a ref; use read_document to page through the full markdown without overflowing your context window. The fetch routes around bot-walls and IP blocks automatically, so locked-down government and agency files just work.

Packs — build a corpus your agent can reuse

A Pack is a topic-scoped corpus your agent builds and owns — a reusable, shareable archive of everything it pulls on a subject. Instead of re-scraping the same sources every session, your agent builds the pack once, then queries it: a resource↔entity graph of who and what appears across every document, capture, and search you filed into it.

Create a pack, then pass pack="<slug>" to the fetch tools — convert_document, wayback_fetch, tor_fetch — and the content files itself into the pack. Search tools (google_dork, wayback_search) are for finding URLs; they don't file anything.

build + query a pack
1. pack_create(name="California High-Speed Rail",
               period_start="1996-01-01", period_end="2008-12-31")
   -> { slug: "california-high-speed-rail" }
   # the period is optional — docs dated outside it get a soft warning at file time

2. # dork to FIND a URL (no pack — discovery only), then convert it INTO the pack
   google_dork(query="CHSRA business plan filetype:pdf")        # returns URLs
   convert_document(url="https://.../eir-section-3.pdf", pack="california-high-speed-rail")
   wayback_fetch(url="https://hsr.ca.gov/...", timestamp="20000104", pack="california-high-speed-rail")

3a. # ask the TEXT a question — semantic passage retrieval (returns the answer's sentence)
    pack_query(pack="california-high-speed-rail", query="what was the initial cost estimate")
    # -> passages incl. "...the initial estimate was $15 billion..." + source + date

3b. # or traverse the ENTITY graph
    pack_search(pack="california-high-speed-rail", query="Tutor Perini")
    pack_entity(pack="california-high-speed-rail", ref="Tutor Perini")

pack_query is the main way to read a pack: it searches the converted text by meaning and returns the actual passages (with source + date), so you get facts, not just a list of entity names. Then pack_read pulls the full source document behind any passage. pack_search/pack_entity traverse the entity graph; pack_resources lists the documents; from_date/to_date bound any search to a time window.

Packs are private by default. Make one public from its page in the console and any other agent can read your whole archive — through pack_query / pack_search, without re-scraping a thing. (Visibility is managed in the console, not via the API.) Browse and manage your packs on the Packs page.

Call the API directly

The endpoint is JSON-RPC 2.0 over MCP Streamable HTTP. The cleanest path is an MCP SDK, which handles the handshake and session for you.

Python

python
import asyncio
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

HEADERS = {"Authorization": "Bearer fringe_sk_live_..."}

async def main():
    async with streamablehttp_client("https://platform.bearinglabs.ai/mcp", headers=HEADERS) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await session.list_tools()
            print([t.name for t in tools.tools])

            result = await session.call_tool(
                "google_dork",
                {"query": "annual report", "site": "sec.gov", "filetype": "pdf"},
            )
            print(result.structuredContent)

asyncio.run(main())

TypeScript

typescript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const transport = new StreamableHTTPClientTransport(new URL("https://platform.bearinglabs.ai/mcp"), {
  requestInit: { headers: { Authorization: "Bearer fringe_sk_live_..." } },
});

const client = new Client({ name: "my-agent", version: "1.0.0" });
await client.connect(transport);

const { tools } = await client.listTools();
const res = await client.callTool({
  name: "wayback_lookup",
  arguments: { url: "https://www.python.org" },
});
console.log(res.structuredContent);

Raw HTTP

Speak JSON-RPC directly. Include Accept: application/json, text/event-stream, capture the Mcp-Session-Id header from initialize, and send it on subsequent calls.

shell
# 1) initialize — read the Mcp-Session-Id response header
curl -i https://platform.bearinglabs.ai/mcp \
  -H "Authorization: Bearer fringe_sk_live_..." \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
        "protocolVersion":"2025-06-18","capabilities":{},
        "clientInfo":{"name":"curl","version":"1.0"}}}'

# 2) call a tool (reuse the session id from step 1)
curl https://platform.bearinglabs.ai/mcp \
  -H "Authorization: Bearer fringe_sk_live_..." \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Mcp-Session-Id: <from step 1>" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{
        "name":"wayback_lookup",
        "arguments":{"url":"https://www.python.org"}}}'

Tools reference

Every tool returns structured JSON. Optional arguments are marked with ?.

ToolArgumentsDescription
tor_fetchurl, timeout?, pack?Fetch an .onion or clearnet page over Tor.
tor_statusCheck that the Tor network is reachable.
wayback_lookupurl, timestamp?Find the closest archived snapshot of a URL.
wayback_searchurl, match_type?, from_date?, to_date?, limit?Enumerate archived captures of a URL or prefix.
wayback_fetchurl, timestamp, pack?Retrieve the archived content of a specific capture.
google_dorkquery, site?, filetype?, intitle?, inurl?, max_results?Run an advanced search to FIND URLs (discovery — files nothing). Archive a result with convert_document.
craft_dorkobjective, run?, count?, site?, max_results?Describe what you want; an LLM crafts and runs the dork.
convert_documenturl, pack?Fetch a PDF/DOCX/XLSX/PPTX/HTML/EPUB and convert it to clean markdown on a GPU. Async — returns a job_id.
convert_statusjob_idCheck a conversion; returns the markdown (or a preview + ref for large docs) once ready.
read_documentref, offset?, limit?Read a section of a converted document's markdown by ref.
pack_createname, topic?, description?, period_start?, period_end?Create a topic-scoped corpus (a Pack) you own. Returns a slug to pass as pack=. New packs are private; visibility is managed in the console.
pack_listinclude_public?List your packs, plus public packs shared by others.
pack_querypack, query, limit?, from_date?, to_date?Semantic passage retrieval over a pack's text — returns the actual passages (with source + date), so you can ask a question and get the sentence with the answer. The main way to read a pack.
pack_searchpack, query, type?, limit?Search a pack's ENTITY graph by name/identifier (multi-term matches any token). Use pack_query for facts in the text.
pack_entitypack, refExpand one entity in a pack: its identifiers, the resources it's on, and co-occurring entities.
pack_resourcepack, refLook up a resource in a pack and the other resources that share entities with it.
pack_resourcespack, limit?, from_date?, to_date?List the resources in a pack — the manifest (id, url, title, kind, date).
pack_readpack, resource, offset?, limit?Read the FULL text of a source document in a pack — the resource behind a pack_query passage. Page large docs with offset/limit.
pack_deduppack, apply?Collapse duplicate/fragmented entities in a pack (e.g. 'Senator Kopp' vs 'Kopp'). Dry-run by default.
pack_deletepack, confirmPermanently delete a pack and everything in it. Owner only.
manual_task_typesList the human-in-the-loop task types you can request.
manual_task_submittask_type, query, data?Submit a task for a human to fulfil. Returns an ETA + poll interval.
manual_task_statustask_idCheck status and retrieve the result when ready.
manual_task_listList the tasks you've submitted.