Browse the documentation

Docs Guides

Put Geometer in front of your MCP servers

Your agent already talks to its tools through MCP servers. Wrapping puts a small Geometer relay in front of each one, on your computer, so Geometer learns what the tools are and what each one does.

On this page

What wrapping does#

geo mcp wrap finds the MCP configuration files in your project (.mcp.json, .cursor/mcp.json, .vscode/mcp.json and the like) and changes each server's entry so your agent starts geo mcp proxy first. The proxy starts your server exactly as before and passes every message through. Nothing is sent anywhere.

By default a wrapped server is in pass mode. Your agent behaves as it did. Geometer keeps one thing: the list of tools the server offers.

Wrap your servers#

The quickest way in is one command in your project folder. It finds geo (and offers Geometer's installer, only after you say yes in a terminal, if it is missing), sets Docker up if it is not ready, shows the change below, asks, and then tells you what to run next:

Terminal
npx @geometer/init

Without a terminal it never asks or installs: it prints the commands. You can also do each step yourself. Run geo mcp wrap in your project folder. It shows what it would change, one line for each server, and writes nothing yet.

sh
geo mcp wrap
text
.mcp.json
  stripe: npx -y @stripe/mcp → through Geometer (pass: nothing changes until you choose try or guard)
  orders: node ./orders-server.js → through Geometer (pass: nothing changes until you choose try or guard)

To see the full before and after of every entry, as the file holds them, add --json:

json
{ "command": "npx", "args": ["-y", "@acme/orders-mcp"] }
json
{
  "command": "/usr/local/bin/geo",
  "args": ["mcp", "proxy", "--name", "orders", "--", "npx", "-y", "@acme/orders-mcp"],
  "geometer": { "wrapped": 1, "original": "{ \"command\": \"npx\", ... }" }
}

When you are happy with it, write the change. Geometer asks first; --yes skips the question for scripts.

sh
geo mcp wrap --write

Restart your assistant or agent so it starts the servers again. The rest of the file, its other keys, comments and spacing, stays as it was. Then try your agent against a simulated service: geo mcp try -- <how you run your agent>.

In the app. When a project has an MCP configuration with servers that are not wrapped, its page shows a card, "Wrap your MCP servers". Show what changes gives the same preview, and the files are written only when you press the confirm button. Afterwards the card says what to run to try it. Your coding assistant cannot do this for you.

Take it off#

The original of every entry is kept inside the wrapped entry, so there is no backup file to lose.

sh
geo mcp unwrap --write

The file comes back byte for byte.

Modes#

Each server has a mode. It is set from Geometer, not in the configuration file, and the proxy prints it on its error output each time it starts.

Modes
ModeStatusWhat it does
passAvailable (the default)Passes every message through. Keeps the tool list only.
guardAvailableHands each call to Guard, which decides it by the rules in its guard.toml. If Guard or its file is missing, or the agent's credential is, every request gets an error that says why and nothing reaches the server.
tryAvailable, through geo mcp tryAnswers tool calls from a simulated service and injects a fault, so you can watch your agent with a lost reply. It starts and ends by itself; you do not set it with geo mcp mode.
recordAvailable, through geo mcp recordPasses every message through as pass does, and also writes the tool lists and calls on your computer, with your consent, so you can learn a simulated copy (a twin) of the service. It asks first, and ends by itself; you do not set it with geo mcp mode.
packsAvailable, chosen with --packReady-made simulated services: payments (Stripe and Adyen), commerce (Shopify, Zendesk and Intercom) and CRM and ERP (Salesforce, HubSpot and NetSuite). Each is a simulation, not the real service.
sh
geo mcp mode orders guard --config ./guard.toml
geo mcp mode --all pass

Guard mode needs GEO_GUARD_AGENT_TOKEN in the server's env and geo-guard installed next to geo. For an HTTP server, Guard must already be running at a fixed address.

When your assistant starts the server, the proxy says which mode it is in and which Guard configuration it uses, for example orders is in guard mode: Guard decides each call, using /work/shop/guard.toml.

Watching or stopping comes from `guard.toml`. With mode = "enforce", a call above a limit (for example a refund over the amount that needs approval) is held: the agent gets a tool error that says it was held, and the server never sees the call. With mode = "shadow", the call is sent as it is, and Guard writes would have held refund to the proxy's error output and to its record, so you can see what stopping would have done before you turn it on.

One record per call. Every tools/call that goes through Guard adds one entry to Guard's evidence: the server's name, the tool, what Guard decided (allowed, held, refused, or in watching mode "would have held" or "would have refused"), a digest of the request, a digest of the reply (or "none" if the server was never asked, "lost" if no reply came back, "error" if the server answered with an error), and the time. A digest is a fingerprint: it shows that two records are about the same message, and it does not reveal the message. The arguments and the reply are kept only if you set this in guard.toml:

toml
[mcp]
record_values = true   # default false: digests only

With that on, the arguments and the reply are stored in a locked payload next to the entry, on this computer, and can be deleted later without breaking the record's chain. Turn it on only where you are happy for those values to be kept. See Guard and Privacy.

Try your agent against a simulated service#

geo mcp try starts a simulated service on your computer and points your wrapped servers at it for a while. Your own agent, with your own model, runs as usual. The first change it makes gets a lost reply: the change is made in the simulated service, and your agent is told the request timed out. Then Geometer says which rules held.

Terminal
geo mcp try --pack payments -- node my-agent.js

It prints the result in words:

text
Try result: 1 rule(s) broke. 2 refunds of 40.00 EUR (received) on payment PSP_SIM0001 (80.00 EUR).
Fault: a lost reply: the change was made, then the agent got a timeout

Calls, in order:
  1. adyen create_refund({"paymentPspReference":"PSP_SIM0001",...}): the change was made, but the agent got a timeout
  2. adyen create_refund({"paymentPspReference":"PSP_SIM0001",...}): made a change

Rules:
  BROKE         The same create_refund is carried out at most once per paymentPspReference (adyen) (pack)

What changed in the simulated adyen:
  2 refunds of 40.00 EUR (received) on payment PSP_SIM0001 (80.00 EUR)

Without -- <command>, the simulated service keeps running and Geometer says Now run your agent as usual. Restart your agent first, so it starts the wrapped servers again. Press Ctrl-C when it is done.

What is simulated. The pack's service is modelled on the provider's public documentation, and says so in its answers. One service holds all the state for the project, so a server your client restarts finds what happened before, and a rule that spans two servers is judged on one ledger.

What never happens. A call is never sent to your real server. If the pack does not cover everything a server lists, the real server is started only so its tool list can be answered, and a call to a tool the pack does not simulate gets an error that says so. The simulated service listens on this computer only. A try ends by itself (30 minutes unless you say --for 10m), and the servers go back to pass. If the service is gone, your agent gets an error and nothing falls back to the real server.

The fault. --fault lost-reply (the default), lost-request (the change is not made, the agent gets a timeout) or crash (the change is made, then your agent is stopped and started again; this needs -- <command>). --tool create_refund hits that tool's first call instead of the first change. --scenario picks a starting situation from the pack.

The result. Each rule of the pack, and each rule you declared in .geometer/rules.toml that names a tool the pack simulates, is held, broken or not exercised, with the calls in order and what changed in the simulated service. It is kept in Geometer's folder and shown on the project page.

This is a live look, not a replay. Your agent and your model are not controlled, so a second try can go differently. To explore it fully, run geo check.

Keeping a try as a check. With --record-model, your agent's model address (OPENAI_BASE_URL or ANTHROPIC_BASE_URL) is pointed at a gateway on this computer, which passes each request on to your provider with your own key, as your agent always did, and keeps the model's answers on this computer. Then geo mcp try keep (or the project page's "Keep this check" button) writes the check and the recorded answers into .geometer/regressions/, after you say yes. geo mcp try replay runs your agent again against a fresh simulated service, from the same start and with the same fault, with the model's answers coming from the recording: no provider is reached, and Geometer says whether every rule came out as before. The provider's address may be https (api.openai.com, api.anthropic.com or your own): Geometer connects to it over TLS, trusting the certificate authorities your computer trusts, and your key goes to the provider unchanged and is never written down. A plain http address is accepted only for a model server on this computer; plain http to another computer is refused, because it would send your key and prompts unprotected.

In `geo ci`. geo ci replays each kept try beside your other kept checks, and reports them the same way: if a rule breaks again, the run fails (exit 1) and says "A kept check failed"; if the replay comes out differently without breaking a rule, there is no conclusion (exit 2); if the tape, the pack or the command is missing or unusable, it says "Couldn't run" (exit 3) and never counts as passed. Holding means only that this one recorded try passed again, not that every situation is covered.

Record a session and learn a twin#

A twin is a simulated copy of a service, learned from a session you recorded. Record your agent using a server once, then try the agent against the twin with a fault.

Terminal
geo mcp record orders --for 10m
geo mcp twin orders
geo mcp try --pack twin:orders -- node my-agent.js
geo check --pack twin:orders

geo mcp record asks first and shows what it keeps and where. The server works as always, and each tool list and each call is also written, on this computer only, under mcp/recordings/ in the Geometer home: the arguments, the result or error, and the time. Values that look like secrets are replaced before anything is written: keys and tokens, values under names such as password or api_key, card numbers and email addresses. Your credentials and headers are never kept. Recording stops by itself (10 minutes unless you say --for 30m) and the server goes back to pass. geo mcp record --list shows what is kept and geo mcp record --delete <id> removes it.

For an internal REST service that is not MCP, run geo mcp record --http http://10.0.0.5:8080 and point your agent's base URL at the local address it prints (--listen 127.0.0.1:9000 chooses the port). It records each request and response the same way. The service must be plain http.

geo mcp twin orders turns the recordings of orders into a twin in your Geometer home, never in your project (--name renames it, --list shows yours). It works out the resources from ids in the results and paths, which tools read and which change something (the method, the hints, and whether a later read shows a difference), which fields a change touches, which calls create and which list, and what the service held when you started. It says what it learned in words, for example learned from 5 recorded calls on 2026-10-28.

Learned, not known. A twin knows only what the session showed. A call it never saw, a tool that was not used, or an item that was not read gets the answer not simulated: not in the recording; it is never invented. Run the agent through more of the service while recording to teach it more.

Whole-number ids and what the service sets by itself. Ids that were whole numbers stay whole numbers: a new record gets the next number of its collection. When a change also set a field the arguments did not give, the twin learns it as service-set and fills it in on every change: a constant (a status of cancelled after cancel) is repeated, and a time (updated_at) is filled with a simulated time that moves on with each change and is the same on every run. A field that went up by one (a version counter), or that differed from one change to the next, cannot be told from one session; the twin says so and leaves it as it was.

In `geo check`. geo check answers a service of your project with a twin in two cases. You name one, geo check --pack twin:orders (repeat the flag for more than one; geo ci takes it too), or a twin was learned from a server of this project and nothing else answers that server: no API description in the project, no provider Geometer already simulates, no tool you wrote by hand. A twin finds its service by the address it recorded (a REST recording) or by the server's name in the host or in the name of the setting that holds the address (ORDERS_MCP_URL for the orders server). A twin you name that does not say which service it stands in for takes the one service nothing else answers; if there are two or more it is not used, and the check says which ones it could have been. A twin of an MCP server answers an MCP address (an http URL of an MCP endpoint), a twin of a REST service answers a REST address; both are reached by name inside the check's network, as the other simulated tools are. The twin's data is in your Geometer home, not in the project.

The plan and the report say plainly what answered: orders was answered by a twin learned from 5 recorded calls on 2026-10-29. A call the recording does not show gets not simulated: not in the recording, and a check with such a call is couldn't tell for it (exit 2, "Not enough evidence to say"), never a pass. A problem that was found still counts as a problem. Learned from what was recorded: a twin repeats the answers it saw and the changes it worked out, and it is not the service.

What Geometer keeps#

sh
geo mcp tools

For each server it lists the tools and says whether each one reads, changes something, or is unknown. A tool is judged by its name first (list_, get_, search_ read; create_, update_, delete_, refund, send, cancel change). The hints a server gives about itself are only hints: when a hint disagrees with the name, the tool is unknown.

For a tool that changes something and takes an identifier, Geometer suggests a check that it happens at most once. Geometer reads this list the next time it works out your agent's tools. When your project has one MCP endpoint, the checks appear in geo rules propose for you to review, and nothing is on until you accept it. A server that Geometer does not run in its sandbox does not get suggestions yet.

Kept, under mcp/ in the Geometer home on your computer: the tool names, their input schemas, their hints, and the class. Never kept: the arguments of a call, or what came back.

Limits#

  • Servers defined in code (MCPServerStdio, MCPClient, MultiServerMCPClient) are not edited. geo mcp wrap prints the one-line change to make by hand.
  • A file that cannot be read as JSON (comments and trailing commas are fine) is left alone, with the reason.
  • An HTTP server is reached with curl, which must be installed. Messages are handled one at a time, in order.
  • A try uses one pack at a time (--pack), and the packs cover only part of what each provider offers. See Check your agent.
  • A twin is learned from the calls you recorded. --http recording is plain http only. In geo check a twin answers an MCP server reached by an http address, not a stdio server: a stdio server runs behind Geometer's bridge as itself, so use geo mcp try for that one.
  • A try replaces the tool list of a server the pack fully covers with the pack's list, and runs only the pack's tools.

Something unclear, wrong or missing on this page? Report a problem with these docs.