OpenCode's terminal-first, model-agnostic design makes it a natural fit for API testing work that's mostly file-and-terminal already — you're editing a spec, running newman, and reading JSON responses, none of which needs a GUI. This topic covers wiring Postman MCP into OpenCode's config format, the day-to-day commands for managing collections and environments from inside a session, a full smoke-test build-out, and the rough edges you'll hit that don't show up in the Claude Code or Cursor integrations.
Installing and Connecting Postman MCP to OpenCode
OpenCode reads MCP server definitions from opencode.json (project-local) or ~/.config/opencode/opencode.json (global). Servers are declared under the mcp key, with type: "local" for a stdio-launched process:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"postman": {
"type": "local",
"command": ["npx", "-y", "@postman/postman-mcp-server", "--full"],
"environment": {
"POSTMAN_API_KEY": "{env:POSTMAN_API_KEY}"
},
"enabled": true
}
}
}
The {env:POSTMAN_API_KEY} interpolation pulls from your shell environment at launch time rather than storing the key in the file — export it in your shell profile or a project-local .env you load before starting opencode:
POSTMAN_API_KEY=PMAK-xxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
source .env && opencode
Verify the connection from inside a session with a direct ask rather than trusting the config silently — "list my Postman workspaces" is the cheapest smoke test, since a bad key or a typo'd command array shows up immediately as a tool-call error rather than a vague "not connected" state.
Project-local opencode.json is the right place for this over the global config if the Postman workspace ID is specific to one repo — it keeps the server definition next to the API it's testing and lets a teammate onboard by cloning the repo and setting one env var, rather than hunting through a home-directory config for context that isn't there.
Tips
- PutPOSTMAN_API_KEYin a project-local, gitignored.envsourced before launch —opencode.jsonitself is often checked in, and the{env:...}interpolation keeps the actual secret out of it.
- Scopeopencode.json'smcp.postmanblock per-project when the workspace ID is project-specific; use the global config only for a Postman workspace you genuinely use across every repo.
-enabled: trueis worth stating explicitly even though it may default to true — config files get copy-pasted between projects, and an explicit flag makes intent visible on review.
Managing Collections, Environments, and Runs from OpenCode
Day-to-day, three patterns cover most sessions. First, resolving names to IDs once and reusing them — OpenCode sessions tend to be shorter-lived than a full IDE session, so re-establishing context efficiently matters more here than in an editor you leave open all day:
Find the collection named "Orders API — v2" in my default workspace
and remember its collection ID for the rest of this session.
Second, environment variable updates as an API's contract shifts — a common OpenCode use case is running this right after pulling a branch that changed a config value:
The staging base URL changed to https://api-staging-v2.internal.example.com.
Update the "staging" environment's baseUrl variable to match, and confirm
the change by reading it back.
Third, triggering a run and getting a terminal-readable summary rather than raw JSON — worth asking for explicitly, since the raw MCP tool response is a nested result object that's harder to scan than a formatted table:
Run the "Orders API — v2" collection against the staging environment.
Summarize results as a table: request name, status, pass/fail count,
and response time. List full assertion failure details only for
failing requests.
| Request | Status | Tests | Time |
|---------------------|--------|-------|-------|
| Create order | 201 | 4/4 | 210ms |
| Get order by id | 200 | 3/3 | 95ms |
| List orders (paged) | 200 | 2/3 | 340ms |
Failure — List orders (paged):
"Response has nextCursor when hasMore is true"
Expected body.nextCursor to be a string, got undefined
That failure detail is exactly what you want piped into the next prompt: "check the paginated list controller for why nextCursor is missing" turns a test failure into a code fix in the same session, without leaving the terminal.
Tips
- Resolve collection/environment names to IDs at the start of a session and keep them in scope — OpenCode's shorter session lifecycle makes repeated name-to-ID lookups a bigger tax than in a long-lived IDE session.
- Ask for tabular run summaries with failure detail only on failing requests — the default MCP tool output is a full JSON result object that's expensive to read in a terminal.
- Chain a failing assertion straight into a "check the implementation" prompt in the same session — that's the actual time savings over running newman separately and switching tools to investigate.
Practical Example: Building a Smoke Test Suite for a REST API
A smoke suite's job is narrow: catch "the service is fundamentally broken" fast, not "every edge case is covered." Ask for exactly that scope, or you'll get a full regression suite mixed in with the smoke checks and lose the speed that makes a smoke suite useful in the first place.
Build a smoke-test collection for the Orders API covering only:
- GET /health returns 200
- POST /orders with a minimal valid body returns 201 and a Location header
- GET /orders/{id} for the id just created returns 200 with matching data
- GET /orders (list, no filters) returns 200 and an array
Keep it to 4 requests total, chained so each depends on the previous
one's response (use pm.collectionVariables to pass the created order id
forward). No exhaustive field checks — just "did this endpoint respond
sanely."
// In "Create order" request's Tests tab — capture the id for the next request
pm.test("Order created", function () {
pm.response.to.have.status(201);
const body = pm.response.json();
pm.expect(body.id).to.be.a("string");
pm.collectionVariables.set("smokeOrderId", body.id);
});
// In "Get order by id" request — uses the captured id
pm.test("GET /health returned 200 earlier in the run", function () {
// sanity check that pm.collectionVariables carried state correctly
pm.expect(pm.collectionVariables.get("smokeOrderId")).to.not.be.undefined;
});
pm.test("Fetched order matches created id", function () {
pm.response.to.have.status(200);
const body = pm.response.json();
pm.expect(body.id).to.eql(pm.collectionVariables.get("smokeOrderId"));
});
Wire this into a lightweight local check with newman, run before every push rather than only in CI — a four-request smoke suite runs in under two seconds and catches a broken local dev server before you've written a single line of the feature you meant to build:
newman run smoke.postman_collection.json -e local.postman_environment.json --reporters cli
Tips
- State the request count limit explicitly ("keep it to 4 requests") — without a stated scope, "smoke test" and "full regression suite" blur together and you get more collection than you asked for.
- Usepm.collectionVariables(not environment variables) to pass state between chained requests within one run — it's scoped to the run and won't leak stale IDs into the next person's session.
- Run the smoke suite locally before every push, not just in CI — at four requests and under two seconds, the friction of adding it to a local pre-push habit is close to zero.
Known Limitations for Postman MCP in OpenCode
A few things worth knowing before you rely on this combination for anything time-sensitive:
No native visual diffing of collection changes. Unlike an IDE with a GUI diff view, OpenCode's terminal interface means reviewing what an agent changed in a collection — a modified assertion, a renamed variable — means asking the agent to describe the diff in text, or exporting the collection and diffing the JSON yourself. For collections under version control (see File 6), a plain git diff on the exported collection JSON is more reliable than trusting a natural-language summary of "what changed."
Model-dependent tool-call reliability. OpenCode's model-agnostic design is a strength generally, but Postman MCP's full configuration exposes 100+ tools, and smaller or less tool-call-tuned models pick the wrong one more often than Claude or GPT-4-class models do. If you're running OpenCode against a lighter local model, stick to the minimal tool set — the accuracy difference is more noticeable here than with a smaller, well-scoped tool list.
No built-in run history browsing. The MCP run tools return the result of the run you just triggered; they don't give you a convenient way to browse past runs the way the Postman web app's run history view does. If you need trend data — "are our p95 response times creeping up over the last ten runs" — that's still a job for exporting newman JSON reporter output over time and analyzing it yourself, not something the MCP session tracks for you.
Session-scoped context, not persistent memory of your API. Each OpenCode session starts fresh on what it knows about your collection structure unless you re-establish it (see the ID-resolution tip above). This isn't unique to OpenCode, but it's more noticeable here than in an IDE session you leave open for a full day.
Tips
- Keep collections under version control and diff the exported JSON directly for anything you need to audit precisely — don't rely on a natural-language "here's what changed" summary as your only record.
- Default to theminimaltool configuration when running OpenCode against smaller or local models;full's 100+ tools measurably hurt tool-selection accuracy on weaker models.
- For run-history trends (latency creep, flakiness rate over time), export newman's JSON reporter output to a file per run and aggregate it yourself — the MCP tools don't retain history across sessions.
Tips
Tips
- WirePOSTMAN_API_KEYthrough a gitignored.envand OpenCode's{env:...}interpolation — never a literal value inopencode.json.
- Ask for tabular, failure-focused run summaries in every session; the raw JSON tool output is not built for terminal reading.
- Scope smoke suites explicitly by request count and purpose, and run them locally pre-push — the payoff is speed, and speed only holds if the suite stays small.
- Default tominimaltools unless you specifically need mock servers or monitors, especially when running smaller models through OpenCode.