RagLeap Packages
Looking for the self-hosted AI employee system? This page documents the open-source pip install ragleap-integrations library. To install and run ragleap-core (AI employees, channels, Docker), see docs.ragleap.com; the project home is ragleap.com.
201 tests passing in CI on Python 3.10, 3.11 and 3.12 — including a real local TLS server for the transport, not mocks

ragleap-integrations

An MCP client that turns allowlisted tools on remote MCP servers into ragleap-tools Tool objects. Streamable HTTP only, owner-configured, with bounded network access — ragleap-integrations provides the Tool objects, not the execution loop.

0.1.0
PyPI version
201
Tests passing
3
Python versions in CI
1
Runtime dependency (ragleap-tools)

Install

Nothing to configure at install time: servers and the allowlist are built in your code, never read from the environment.

pip install ragleap-integrations
# or, with uv
uv add ragleap-integrations

What this is (and isn't)

ragleap-integrations v0.1.0 is an MCP (Model Context Protocol) client over Streamable HTTP only. The owner configures servers and an exact server.tool allowlist; the model only supplies a tool's JSON arguments, never a URL, a token or a tool outside the allowlist. It does not own a tool-calling loop — that is ragleap-agents's job.

Not supported in this version: stdio (it launches a subprocess), the deprecated HTTP+SSE transport, sampling/elicitation/roots (a server asking for client input gets a clear “unsupported” result), resources, prompts, subscriptions and OAuth. Connectors, code execution and HTTP fetch are deliberately not included: each needs its own security design pass.

Quick start

from ragleap_integrations import McpConfig, McpServerConfig, make_mcp_tools

config = McpConfig(
    servers=[McpServerConfig("deepwiki", "https://mcp.deepwiki.com/mcp")],  # token="..." if a server needs one
    allowed_tools=["deepwiki.read_wiki_structure"],
)
tools = make_mcp_tools(config)  # discovers the allowlisted tools once (tools/list)

openai_tools = [t.to_openai_schema() for t in tools]  # or t.to_gemini_schema()
result = tools[0].call(repoName="sqlite/sqlite")
print(result.success, result.result)  # True {"text": "..."}

Tools are exposed as <server>__<tool> (characters outside letters, digits, _ and - become _, at most 64 characters). To keep server-controlled text out of the model's context entirely, supply the descriptions and schemas yourself:

from ragleap_integrations import McpToolSpec

spec = McpToolSpec(
    server="deepwiki", name="read_wiki_structure",
    description="List the documentation topics of a GitHub repository.",
    parameters={"type": "object", "properties": {"repoName": {"type": "string"}}, "required": ["repoName"]},
)
tools = make_mcp_tools(config, specs=[spec])  # nothing is sent over the network at setup

Full public API

McpServerConfig(name, url, token=None)
One remote MCP server. url must be https to a public host (checked again, with DNS, on every call); token is an optional static bearer token, sent only to this URL.
McpConfig(servers, allowed_tools, ...)
Servers plus the exact server.tool allowlist and limits: max_result_chars 4000, max_description_chars 500, max_schema_chars 10000, total_timeout 30 s, op_timeout 10 s, max_response_bytes 256 KiB. Nothing is read from the environment.
McpToolSpec(server, name, description, parameters)
An owner-supplied description and schema. Skips discovery, so no server-controlled text reaches the model.
make_mcp_tools(config, specs=None, client=None)
One ragleap_tools.Tool per allowlisted tool. Discovers once (tools/list) unless specs are given. Raises McpConfigError or McpDiscoveryError at setup; tool handlers never raise.
McpClient(config, transport=None)
discover() returns a Discovery (tools plus the skipped ones with a reason); call_tool(server, tool, arguments, parameters) returns a ToolResult and never raises. The transport argument is a test seam.

Version history

VersionWhat shippedReal live test performed
v0.1.0Published to PyPI on 2026-10-06. An MCP client over Streamable HTTP that returns ragleap_tools.Tool objects. Owner-configured servers and an exact allowlist; tools discovered once (a snapshot) or supplied as specs. Modern (2026-07-28) requests with Mcp-Method, Mcp-Name and Mcp-Param-* headers and params._meta; legacy servers are detected per the specification's backward-compatibility rules and spoken to with the initialize handshake for 2025-06-18 only. A standard-library-only HTTPS transport: public-address check with the connection pinned to the resolved IP, TLS (minimum 1.2) verified against the hostname, no redirects, a response-size cap, constant error messages and a watchdog-enforced wall-clock deadline.201 tests, including a real local TLS server for the transport (hostname verification, IP pinning, size cap, truncated bodies, and the deadline against slow-drip bodies, slow-drip headers and a stalled handshake); CI green on Python 3.10, 3.11 and 3.12. After release the published wheel and sdist were checked: installed files byte-identical to the tested ones, and the sdist's tests pass against the installed wheel. Live-checked on 2026-10-05 against DeepWiki's public MCP server (no authentication): the server rejected the modern request with HTTP 400 and -32600, the client fell back to the legacy handshake, negotiated 2025-06-18, listed tools and read a repository's documentation structure. The live check found a real bug before release (a legacy server's own JSON-RPC rejection was treated as a modern server) and it was fixed. Not live-verified: the modern 2026-07-28 path, x-mcp-header mirroring, where a -32022 error carries its supported-version list, bearer-token authentication, plain JSON (non-stream) responses and pagination. Correction: the CHANGELOG shipped inside 0.1.0 is dated 2026-10-05; the files reached PyPI on 2026-10-06 (published packages cannot be edited).

Real design decisions & findings

Each of these was decided or found before release, not retrofitted.

  • ✓ shipped
    The owner decides, the model only supplies arguments. Servers, tokens and an exact server.tool allowlist come from your code; the model never picks a URL, a token or a tool outside the list.
  • ✓ shipped
    A wall-clock deadline that actually holds. A test showed a server sending one byte every 0.2 s kept a plain read blocked for 80 s against a 3 s deadline, because every byte arrived inside the per-operation timeout. A watchdog that shuts the socket down at the deadline (and sets a flag, since the read then looks like a normal end of response) fixes it, and is tested over real TLS.
  • ✓ shipped
    No server text in errors. Failures carry a constant message plus, at most, an HTTP status or a JSON-RPC error code, so a hostile server cannot put instructions into an error result.
  • ✓ shipped
    The live check found a real bug. The first detection rule treated any JSON-RPC error on a 400 as proof of a modern server; DeepWiki's own -32600 rejection broke it. Only -32020, -32021 and -32022 count as recognized modern errors now, and a failed fallback still shows the first reply's code. The new tests fail on the old code.
  • ✓ shipped
    A CodeQL alert was reproduced before it was fixed. py/insecure-protocol flagged the transport because two test contexts reached it without a TLS floor in a place the analyzer follows. It was reproduced locally with the CodeQL CLI running only that query (1 result), the test helper was fixed, and the same run returned 0. No production change.
  • not live-verified
    The modern path and several details. The modern 2026-07-28 request flow, x-mcp-header mirroring, the location of a -32022 error's supported-version list, bearer-token authentication, plain JSON responses and pagination follow the public specification and are covered by tests against fakes only. Treat them as best-effort until confirmed live.
  • deliberately deferred
    stdio, code execution, HTTP fetch and connectors. Each needs its own security design pass and is not included.

Known limitations

  • On a legacy server every tool call performs a full initialize handshake (three requests) and never closes the session.
  • A HeaderMismatch error is reported, not retried after re-reading the tool list.
  • Only text content is returned; other content types are counted and omitted.
  • Server-supplied text (descriptions, schemas, results) is untrusted and is not screened for prompt injection.
  • ragleap_tools.Tool.call in ragleap-tools 0.4.0 cannot take an argument literally named self.
  • Not safe to share one client between threads while calls are running.