MPATO is designed to allow an AI agent to use tools, without demanding that the owner of said tool develop their own MCP server. The genesis of this is a product of this LinkedIn post, with some also-included comments, that I made:
Hot take: MCP is stupid and does nothing. Service discovery, streaming, i/o redirection… these are solved problems and implementing it all again opens one more, unnecessary, attack surface. Most companies choke trying to own and maintain one API attack surface. Asking them to open another one, for every service you want an LLM to talk to, is bad engineering. Refusing to conform to existing standards limits your tool access, all while materially enshittifying the API ecosystem. Stop it. Maybe you need a new client? But the world does not need a new server.
APIs are implemented to act in the way humans need to interact. Services that require streaming have streaming APIs. Services that require auth take auth in a way that mirrors how humans go through the interaction flow. An agent is designed to act the way a human acts. The end result is that API endpoints should work perfectly well with an agent as long as the agent is provided a semantically-appropriate description of the existing API endpoint. I’m not arguing against having shims that make APIs semantic for agents; I’m arguing that those shims should live at the client side and, preferably, not require dedicated services or endpoints.
Further, I would want most of the business logic to describe interaction patterns; general REST, WSS, OAuth, OpenAPI, etc.; with one module of business logic to handle it. Then a tools interaction service includes the modules, which in turn consume static files describing the respective individual services. All of that can, and I’d argue should, be client-side to avoid server-side accommodation of nonhuman interaction.
Build a client-side Python library that allows an AI agent (or any caller) to invoke external APIs — REST and WebSocket — using static declarative definition files (YAML or JSON), with no requirement that the service provider implement any agent-specific protocol. The library should be importable as a module and callable programmatically.
The guiding principle: every capability MCP provides (service discovery, auth, streaming, tool invocation) already exists in the broader ecosystem. MPATO composes those existing capabilities rather than reimplementing them.
- Zero server-side requirements. Service definitions are static files that can be version-controlled, shared, and served from a CDN. No running process is needed on the service side beyond the API itself.
- Any API is fair game. If a service exposes an HTTP or WebSocket API, it can be wrapped with a definition file. The service provider does not need to be involved.
- Community-extensible. Anyone can write a definition file for any third-party service and publish it. Consuming a new service is a matter of dropping in a definition file, not standing up infrastructure.
- Graceful degradation. The library should handle missing capabilities cleanly — a service definition that omits auth, for example, should still work for services that don't require it.
- Sync and async first-class. Both calling patterns should be fully supported without one being an afterthought.
Each service is described in a YAML or JSON file. The library loads these at runtime.
A definition file describes:
- Service metadata: name, base URL, protocol (
restorwss) - Auth configuration: type (none, API key, Bearer, OAuth2), where credentials are injected (header, query param, body), and how to resolve the credential value (env var, file, static)
- Endpoints / channels: each callable unit with its path, method (for REST), parameter schema, and response handling hints
- Timeout and retry policy: per-service or per-endpoint overrides
- Streaming behavior (WSS only): end-of-message delimiter, idle timeout in ms after which a buffered response is considered complete
The schema for definition files should be documented and validated on load. Invalid definitions should fail loudly at load time, not silently at call time.
mpato/
__init__.py # Public API surface
loader.py # Parses and validates service definition files
protocols/
rest.py # Handles REST service calls via aiohttp
wss.py # Handles WebSocket services; runs listener in thread
auth/
resolver.py # Resolves credential values from env, file, or static config
injector.py # Injects resolved credentials into requests
dispatcher.py # Routes calls to the correct protocol handler
result.py # Unified result/response type
shims/
mcp.py # Client-side MCP compatibility layer over the registry
from mpato import ServiceRegistry
registry = ServiceRegistry()
registry.load("definitions/github.yaml")
result = registry.call("github", "get_issue", {"owner": "acme", "repo": "widget", "issue_number": 42})
print(result.data)For REST: blocks until response received, returns Result object.
For WSS: blocks until end-of-message delimiter received or idle timeout exceeded, returns buffered Result.
def on_result(result):
print(result.data)
registry.call_async("github", "get_issue", {"owner": "acme", "repo": "widget", "issue_number": 42}, callback=on_result)pipe = registry.call_stream("my_wss_service", "subscribe", {"channel": "trades"})
for chunk in pipe:
print(chunk)Returns a pipe/iterator object. Does not block. Caller is responsible for consuming or closing the pipe.
- On first call to a WSS service (or on explicit
.connect()), the library opens a connection in a background thread and holds it open. - Subsequent calls reuse the open connection.
- The background thread buffers inbound messages.
- Sync callers block on the buffer; async callers register a callback or receive a pipe.
- Connection teardown happens on explicit
.disconnect()or when the registry is garbage collected.
Credentials are never hardcoded in definition files. The definition file specifies how to find the credential; the credential itself lives in the environment or a secrets file.
Supported resolution strategies:
env: read from a named environment variablefile: read from a file path (first line, stripped)static: value provided directly in the definition (for non-secret tokens only)
Supported injection strategies:
header: inject as a named HTTP headerquery: inject as a query parameterbody: inject into the request body
OAuth2 support: the definition file specifies the token endpoint and credential resolution for client ID/secret. The library handles the token exchange and caches the token, refreshing on expiry.
All calls return a Result object with:
success: booldata: Any— parsed response body on successstatus_code: int— HTTP status or None for WSSerror: str— human-readable error description on failureraw: bytes— raw response for inspection
Exceptions are not raised by default. Callers can opt into exception-raising mode via a flag on the registry or per-call.
name: github
base_url: https://api.github.com
protocol: rest
auth:
type: bearer
resolve:
strategy: env
key: GITHUB_TOKEN
inject:
strategy: header
name: Authorization
prefix: "Bearer "
endpoints:
get_issue:
path: /repos/{owner}/{repo}/issues/{issue_number}
method: GET
params:
owner:
type: string
required: true
repo:
type: string
required: true
issue_number:
type: integer
required: true
timeout_ms: 5000- MPATO does not provide a server. There is no daemon, no port, no process to manage.
- MPATO does not implement a new protocol. It speaks existing protocols (HTTP, WebSocket, OAuth2) and nothing else.
- MPATO does not require modifications to target services.
- MPATO does not implement tool-use framing for any specific LLM in the core library. The MCP shim (
shims/mcp.py) provides MCP-compatible tool schemas and dispatch as an optional layer; other LLM tool-use formats (OpenAI function calling, etc.) are out of scope for the core library but are natural consumers of it.
MPATO optionally exposes a client-side MCP-compatible interface over any loaded service definitions. This requires no MCP server. The shim translates MPATO's registry into the MCP tool schema format and routes inbound tool_use requests back through the dispatcher.
from mpato import ServiceRegistry
from mpato.shims.mcp import MCPShim
registry = ServiceRegistry()
registry.load("definitions/github.yaml")
shim = MCPShim(registry)
# Get tool definitions to pass to an LLM
tools = shim.tools() # returns MCP-compatible tool schema list
# Handle a tool_use block returned by the LLM
result = shim.dispatch(tool_use_block) # returns MCP-compatible tool_resulttools()iterates loaded service definitions and emits a list of tool descriptors in MCP schema format — name, description, and input schema derived from the endpoint parameter definitions.dispatch()accepts an MCPtool_useblock, resolves the target service and endpoint, and routes the call throughregistry.call(), returning the result in MCPtool_resultformat.- Auth, retries, and protocol handling are unchanged — the shim is purely a translation layer over the existing dispatcher.
- It does not run a server or open a port.
- It does not speak the MCP wire protocol over a socket — it operates in-process.
- It does not require or contact any external MCP server.
Any agent framework with MCP tool-use support gets MPATO compatibility for free, without the framework needing to know MPATO exists. From the framework's perspective it receives tool schemas and dispatches tool calls through a standard interface. The shim handles all translation entirely on the client side. shims/mcp.py is MCP, implemented client-side, in one file, with no server.
MCP provides service discovery, auth, streaming, and tool invocation under one protocol. MPATO provides the same capabilities by composing existing infrastructure, and provides a client-side MCP compatibility shim for frameworks that expect MCP tool schemas:
| MCP capability | MPATO equivalent |
|---|---|
| Service discovery | Static definition files, hostable on CDN |
| Auth | Auth resolver + injector modules consuming existing OAuth2 / API key patterns |
| Streaming | Native WebSocket support in the WSS protocol handler |
| Tool invocation | registry.call() / registry.call_async() / registry.call_stream() |
| MCP tool schema | shims/mcp.py — MCPShim.tools() and MCPShim.dispatch() |
| Server-side adoption | Not required |
loader.py— definition file parsing and schema validationauth/resolver.pyandauth/injector.py— credential resolution and injectionprotocols/rest.py— synchronous REST calls viaaiohttpresult.py— unified result typedispatcher.py— route calls through loader + protocol handlerprotocols/wss.py— WebSocket listener thread, sync blocking, pipe interface- Async callback support across both protocol handlers
- OAuth2 token exchange and caching in auth module
shims/mcp.py—MCPShim.tools()andMCPShim.dispatch()over the registry- Example definition files for at least two real public APIs
- README examples validated against working code