A wrapper answers how do I package a request? An executor answers what operation is allowed to happen here? Confusing the two is risky when a model can propose tool calls. A Python function that runs on your laptop but calls a hosted CRM remains a network client. Conversely, a function that counts lines in an approved local file can perform useful work without sending the file to a remote model—provided the model itself and every other component in that path are also local. Ollama’s API documentation explicitly offers both cloud and local models, so the word “Ollama” alone does not settle that question.[5]

This is a narrower problem than choosing local versus cloud inference. Trace one action as model response → tool dispatcher → process or library → file/network target → logs/result. For each arrow, ask who can change the instruction, what credential or operating-system permission is available, and whether the output returns to a remote service. An HTTP client inside a local function is still an API wrapper. A local executor is defined by the operation it actually performs and the permissions under which it runs, not by the word local in its function name.

For the separate choice of who operates workflow state, credentials and recovery, see the hosted versus self-managed runtime comparison.

Make the dispatcher the authority

A model should return a proposed tool name and structured arguments, not a command line. Map approved names to fixed implementations in trusted application code. The MCP tools specification describes a declared input schema, but a schema describes the interface; it does not grant permission for every argument or eliminate the need for review. Its interaction guidance calls for confirmation on operations so a person can deny a tool invocation.[1]

For example, a document assistant could expose count_lines(document_id) rather than run_shell(command). The application maps document_id to a record in a preapproved directory; the model cannot choose an arbitrary path, executable, environment variable, or destination URL. The tool returns {"ok": true, "lines": 37} or a typed failure. The number is illustrative, not a measured result. If an action would send email, overwrite data, or call a paid service, place a distinct approval gate before the side effect—not a sentence in the model prompt pretending to be an access control.

For an approved document_id, map to a fixed filename in an application-owned directory. A production reader must open a trusted directory descriptor and then the fixed filename relative to it without following symlinks, verify the opened descriptor is a regular file, cap bytes read, and read from that same descriptor. Keep directory ownership and mount points trusted; define a policy for concurrent file changes. This is a design requirement, not a tested sandbox or copy-and-run implementation. Python’s pathlib documentation explains path resolution, but resolving a pathname before a separate open does not bind the subsequent operation to that checked file.[4] A subprocess only earns its place if the external program actually does work the application needs; fixed executable paths and shell-free invocation alone do not make the file read safe.[3]

A practical boundary review

Name the operation. Enumerate inputs, output shape, maximum work, failure codes, and whether it writes, transmits, or charges. Reject a generic shell or unrestricted HTTP tool if a named operation will do.

Bind authority outside the prompt. Supply fixed allowlists, filesystem permissions, network policy, short-lived credentials, and an explicit approval step for consequential actions. MCP’s security guidance warns that locally launched servers can inherit client privileges and recommends restricted filesystem/network access and sandboxing; merely installing an MCP server does not confine it.[2]

Check the model and the network separately. Ollama documents a loopback bind by default and a configurable bind address. Loopback is an exposure setting for that server, not a guarantee that the tool, plugins, or any cloud model will remain offline.[6][5]

Observe without leaking. Record a request ID, approved tool name, outcome, and bounded error code; avoid dumping entire documents or secrets into logs. Test denied IDs, unavailable files, timeouts, and attempted outbound calls under the actual deployment permissions. Treat a network policy or container boundary as something to verify, not something prose can confer.

The decision is not “API bad, local good.” Keep an API wrapper when the authoritative data or operation is hosted and the terms and disclosure are acceptable. Build a local executor when the task is genuinely local and a narrow permission boundary offers a useful advantage. Either design needs a dispatcher that can say no.

Sources

Pin It on Pinterest