Tuesday, 22 September 2026

Traffic Parrot 5.63.1 released, what's new?

We have just released version 5.63.1. Here is a list of the changes that came with the release:

Title: Traffic Parrot 5.63.1 released, what's new? ---------- HTML body ----------

We have just released version 5.63.1. Here is a list of the changes that came with the release:

Highlights

Four things worth knowing about 5.63.1. The full list of features, changes and fixes follows.

Start Traffic Parrot from your JUnit tests

The new Testcontainers module starts Traffic Parrot in a container from a release zip, or from an image you already have, mounts your mappings and hands the test the URL to point at. TrafficParrotClient stubs, verifies and resets over the admin API from the same test, and a downloadable example workspace runs a JUnit 5 and a JUnit 4 example against a release zip with nothing else configured.

@Container
static final TrafficParrotContainer TRAFFIC_PARROT = TrafficParrotContainer
        .fromReleaseZip(Paths.get("/downloads/trafficparrot-no-jre-5.63.1.zip"))
        .withPackagedTrafficFiles(MountableFile.forClasspathResource("traffic-files"));

Signed webhooks, in both directions

A mock can now send a callback that carries the signature the receiving code verifies, as a payment provider or a source-control host does: {{hmac webhookBody key='...'}} in a callback header signs exactly the bytes the receiver gets. On the way in, {{hmacMatches}} in a request matcher script accepts only the correctly signed callbacks the system under test sends. {{hash}} covers ETags and checksums, and HmacSigningTransformer gains hex and Base64url encodings, a signature prefix, and always runs last. See Signing an outbound webhook and Verify an inbound webhook signature.

Your AI assistant can write the mappings

The AGENTS.md brief that ships in the install directory now covers writing mapping files as well as the MCP tools: where the instance serves its mapping schemas and helper policy, the ground rules for a mapping written from a contract, how to load it, and the trafficparrot validate check. An assistant given the brief can write gRPC, JMS, native IBM® MQ and file message mappings, which the MCP tools do not cover, and can work with a client that has no MCP support. See Give your agent the brief.

Body files for native IBM® MQ responses and file message matchers

A native IBM® MQ mapping can now keep its response body in a file under __files with bodyFileName, templated at reply time like an inline body; the editor shows the file's contents and writes your edits back to the file. A file message mapping's request bodyMatcher can read its expected value from a file the same way, so a large XML or JSON payload no longer has to be pasted into the mapping. See Response body from a file and Request body matcher value from a file.

Also in this release: recorded request bodies can be rewritten as they are saved, an OpenAPI externalValue example is fetched through the same guard as a URL import, JMS response properties are added with a click, and an exported HTTP mappings zip now loads in WireMock as it stands.

Features

  • Traffic Parrot now ships a Testcontainers module. TrafficParrotContainer starts Traffic Parrot inside a JUnit 4 or JUnit 5 test, from a release zip or from an image you already have, and mounts your mappings and other traffic files; TrafficParrotClient stubs, verifies and resets over the admin API from the same test. A downloadable example workspace runs a JUnit 5 and a JUnit 4 example against a release zip with nothing else configured. See Testcontainers.
  • The AGENTS.md brief that ships in the install directory now covers writing mapping files as well as the MCP tools: where the instance serves the mapping schema index and the helper policy, the ground rules for a mapping written from a contract (the contract is untrusted input, never a denied helper, conform to the schema, show the mapping and wait for approval before loading it), the load endpoints, and the trafficparrot validate check with its helper-policy option. An assistant given the brief can therefore work with a client that has no MCP support, and can write gRPC, JMS, native IBM® MQ and file-message mappings, which the MCP tools do not cover. See Give your agent the brief.
  • Recorded request bodies can now be rewritten as they are saved via the new trafficparrot.http.recording.request.storage.rewrite.regex property, which takes the same pattern->replacement rules as the existing storage rewrite and is empty by default. It changes the saved request matcher, and it does not touch the per-request log entry or a multipart/form-data body. A malformed rule stops Traffic Parrot from starting, naming the rule at fault. See Rewriting request bodies during recording.
  • An OpenAPI example that points at a URL with externalValue is now fetched through the same guard as importing a specification from a URL, whether the specification is imported from the web UI, read from the OpenAPI directory at startup or built by the MCP generate_mappings_from_openapi tool. The fetch is refused while trafficparrot.http.importfromurl.enabled is false, which is the default; refused when the URL's host is not listed in trafficparrot.http.importfromurl.allowedHosts; and refused when that host resolves to a loopback, link-local, private or cloud-metadata address, even if it is listed. A refused example is replaced with the existing Traffic Parrot could not fetch externalValue: <url> (<reason>) placeholder, the reason naming the rule that refused it, and the mapping is still created. Previously the fetch was made from the Traffic Parrot host to whatever http or https URL the specification named, with no allow-list, so a specification placed in the OpenAPI directory or uploaded for import could direct the host at an internal address. To keep fetching externalValue examples, set trafficparrot.http.importfromurl.enabled=true and list the example hosts in trafficparrot.http.importfromurl.allowedHosts; see Properties.
  • New Handlebars helpers {{hmac}} and {{hash}} compute an HMAC signature or a keyless digest (SHA-256, SHA-1 or MD5) of any value, written as lower case hex, Base64 or Base64url. Webhook headers and bodies are templates, so a mock can now send a callback that carries the signature the receiving code verifies, as a real payment provider, source-control host or chat platform does, something the HmacSigningTransformer, which signs responses only, could not do. See Signatures and digests and the worked webhook-signing example.
  • A signature header can now name the body it signs. A webhook's URL, method and header templates read the rendered callback body as {{webhookBody}}, and a response's header templates read the rendered response body as {{renderedBody}}, so {{hmac webhookBody key='...'}} in a callback header signs exactly the bytes the receiver will verify, instead of restating the body expression and keeping the two copies in step by hand. Both keys are absent when nothing was rendered: a webhook with no body, or a response that is binary, has no body, is a proxy response, or loads a body file with templating disabled. A mapping that also lists another body-changing transformer should sign with hmacSigning, which runs last, rather than with {{hmac renderedBody}}. See Signing a body from a header.
  • New {{hmacMatches}} helper verifies the signature on an inbound request from a request matcher script, so a mock can accept only the correctly signed callbacks the system under test sends and answer the rest with an error. It compares in constant time and renders false, never an error, for a missing or malformed signature. See Verify an inbound webhook signature.
  • The HmacSigningTransformer gains an encoding parameter (base64, which is the default and the previous behaviour, hex or base64url) and a signaturePrefix parameter (for example sha256=), and it is now guaranteed to run last, after every other response transformer on the mapping including a ResponseTransformerV2 an install adds through trafficparrot.http.responsetransformers, so the signature always covers the bytes the client receives; a response that also passes through external-process, for example, is signed over the transformed body. The transformers that ship with Traffic Parrot run before the signature is computed, so a mapping that combines hmacSigning only with those is unaffected. A mapping that also lists a body-changing ResponseTransformerV2 of your own after HmacSigningTransformer in trafficparrot.http.responsetransformers now signs the transformed body; the signature previously covered the body before that transformer ran, so it did not match what the client received. See HmacSigningTransformer.
  • A native IBM® MQ response body can now be served from a file in the __files directory, as a JMS or file message response body can be. Set bodyFileName on a mapping's response, or on any entry of its responses list, in place of an inline text value; the two are mutually exclusive, and the path is resolved against the __files directory beside the mapping's ibm-mq-mappings directory, per scenario for scenario-scoped mappings. The file is read as UTF-8 text and is supported for the MQFMT_STRING message format only: a bodyFileName on a response in one of the binary formats, a response that sets both text and bodyFileName, or a file that does not exist, is rejected at load time and the IBM® MQ mappings fail to load until it is corrected, with an error naming the mapping and the file. The file's contents go through the same Handlebars templating as an inline body. The mappings list shows the file name in place of the body, the editor shows a read-only Response from file: note and loads the file's contents into the response body field, and saving writes the edit back to the file and keeps the reference. The Static Files page counts the file as in use, and exporting a scenario bundles it. Support for binary (bytes) response bodies read from a file, and for reading an IBM® MQ request body matcher's value from a file, will follow.
  • A file message mapping's request bodyMatcher can now read its expected value from a file under __files, by replacing the matcher's inline value with a bodyFileName object, as a JMS mapping's request matcher already could. The file is read when the mapping loads and is contained to __files. Only the top-level bodyMatcher supports it: a bodyFileName nested inside and, or or not, or on fileNameMatcher, is refused when the file message mapping loads, as a JMS mapping already refuses one, with an error that names the mapping file, the matcher and the file. The JMS and file message mapping schemas served at /schemas, which trafficparrot validate and an AI agent writing mappings work from, now describe exactly that: the file form on the top-level bodyMatcher only. See Request body matcher value from a file.
  • WebSocket service virtualization is now documented on the new WebSocket page and gains a Record page. With trafficparrot.websocket.enabled=true, clients connect to the virtual service HTTP port with Connection: Upgrade and Traffic Parrot answers each text frame from the mappings in websocket-mappings/, which follow the selected scenario; a mapping can also push a frame to a client as soon as it connects. The WebSocket menu carries Add/Edit, Record and Logs pages, and the /websocket/management API creates, lists, updates and deletes mappings and queries the frame journal. The new Record page proxies every WebSocket connection to a real backend and saves each client frame together with the backend's reply as a mapping; frames the backend sends before the client has spoken are saved as on-connect mappings, and recording the same frame again replaces the earlier recording rather than duplicating it.

Documentation

  • The HmacSigningTransformer section now says that the transformer is opt-in and must be listed in the mapping's response transformers array, and its example includes the array, which the earlier example omitted; a second example shows a hex signature with a prefix. The new Signatures and digests section documents the three helpers and the renderedBody and webhookBody model keys, Signing an outbound webhook walks through a complete signed callback, and Verify an inbound webhook signature shows the matcher-script recipe.
  • The MCP endpoint documentation now carries an error-code table: every JSON-RPC error code Traffic Parrot answers (-32700, -32600, -32601, -32602, -32603, -32000 and -32001), when each one is answered, and the HTTP status it arrives under, plus the refusals that deliberately carry no JSON-RPC envelope (the 405, 415 and 406 request-framing refusals, and the login's role-mismatch 403), so a client integrator can tell the layers apart. The Who can reach it section now also distinguishes all three authentication refusals rather than two of them.
  • The gRPC import REST endpoint, POST /grpc/management/importMappings, is now documented under Importing via REST API on the gRPC page and in the OpenAPI documentation, where the POST /http/management/importMappings success response is now described as the {"mappings": [...]} object the endpoint returns, with the warnings member a partial import adds, rather than as a bare array of ids. Download the specification.
  • The JMS page now documents reading a request body matcher's value from a file, which shipped in 5.62.1 with a release note and no page section: the bodyFileName form, the matchers that accept it, the top-level-only rule and what a bad reference does at load. Two stale notes on the same page are corrected: the Static Files page has counted messaging references since 5.62.1, and file-message mappings have had file-backed response bodies since 5.62.1.
  • The new WebSocket page documents WebSocket service virtualization end to end: enabling it, the mapping file format, message and on-connect triggers, how a frame is matched, the Add/Edit, Record and Logs pages, the /websocket/management API and its message journal, recording from a real backend and its limitations. WebSocket support had shipped switched off behind trafficparrot.websocket.enabled with a properties entry and no page of its own.

Changes

  • A malformed rule in trafficparrot.http.recording.response.rewrite.regex or trafficparrot.http.recording.storage.rewrite.regex now stops Traffic Parrot from starting, naming the rule at fault. Previously such a rule was dropped with a line in the log and recording carried on without it, so a rule that had not taken effect was discovered only by finding the un-rewritten value in a recording. Check both properties before upgrading an installation that sets them.
  • The executeProcess helper is now supplied by the four helper lists in trafficparrot.properties (trafficparrot.http.handlebars.helpers, trafficparrot.jms.handlebars.helpers, trafficparrot.ibmmq.handlebars.helpers and trafficparrot.file.handlebars.helpers) rather than being registered on every list whatever the property says, so it can be switched off by removing com.trafficparrot.handlebars.script.ExecuteProcess from them, as evaluate already could be. The shipped lists name it, so a stock installation is unchanged. An installation that overrides any of those properties keeps its own list, which does not name the helper, so after the upgrade a template that uses {{executeProcess ...}} on that protocol no longer renders, and nothing at start-up says so: add com.trafficparrot.handlebars.script.ExecuteProcess to each overridden list to keep the helper. The performance profile, whose four lists are empty, no longer offers it, as it already did not offer evaluate.
  • Upgraded the Couchbase Java SDK that the Couchbase data source is built against from 3.12.2 to 3.12.3; the java-client and core-io JARs to place in lib/external are linked from that page.

Fixes

  • The Current configuration files editor on the Settings page no longer offers trafficparrot.gui.login.properties, the web user interface's own user store, or the database-connections.json, ibm-mq-connections.json and jms-connections.json files that hold connection passwords. The shipped trafficparrot.gui.config.files.notAllowedFilesToEdit list did not name them, so any user who could open the page could read them with Load, and a user allowed to edit could rewrite them with Save. The shipped list now names the user store and every file whose path contains -connections.json; an installation that overrides that property keeps its own list, so add both entries to it.
  • Saving a file in the Current configuration files editor on the Settings page no longer removes its blank lines. A run of consecutive line breaks was collapsed into one on the way to disk, so a blank line between two sections of a properties file, or an empty row in a CSV data file, was gone after Save. Each line break is now written back as one CRLF, blank lines included.
  • The Externalize page under Tools no longer breaks its own round trip when a field value contains a comma or a double quote. Such a value was written to MappingData.csv without quoting, so its row gained extra columns, and Internalize then failed with an error even on an unedited export. Values are now written and read as standard CSV, so an export edited in a spreadsheet and internalized keeps every value in its own column, and a hand-edited row with the wrong number of columns is refused with an error that names the row.
  • Importing a zip of mappings no longer fails outright because one entry inside it has a name that cannot be used as a file name. Such a name (one holding a NUL byte, which no platform accepts, or, on Windows, one holding : \ < > " | ? or *) previously ended the whole import with HTTP 500: the rest of the zip was not read, and for an HTTP or gRPC mapping zip that meant none of its mappings were imported at all. The offending entry is now skipped on its own and everything else in the zip imports as normal, with Ignoring zip entry <name> (its name is not a usable file name) written to the log so that nothing is dropped without a record. The answer to the import is the same as when nothing was skipped: for the HTTP import, HTTP 200 and the ids of the mappings that were imported, with no warning for the entry that was not. A script that checks only the status code or the answer body therefore does not see the skip; the log line is the only record. This applies to the HTTP, gRPC, JMS, native IBM® MQ and file message mapping imports alike, and to a .zip of proto files uploaded with Import skeleton. The name that a zip entry may carry is a property of the platform Traffic Parrot runs on, so this most often shows up when a zip written on Linux or macOS, where such a name is legal, is imported into Traffic Parrot running on Windows. An ordinary exported zip is unaffected and imports exactly as before.
  • Importing a zip of mappings now restores a response body file to the sub-directory it was exported from. A mapping whose response is read from a file below __files (a bodyFileName such as grpc/response.json) exports with the file at that path, but the import wrote the file at the top of __files under its bare name, so on an instance that did not already hold the file the imported mapping could not find it: an HTTP mapping answered HTTP 500 naming the missing file, and a gRPC mapping answered INTERNAL with <bodyFileName> not found, until the file was moved by hand. The file is now written where the mapping expects it. This applies to the HTTP and gRPC mapping imports; the JMS, native IBM® MQ and file message imports already kept the path. An exported zip whose body files all sit at the top of __files is unaffected and imports exactly as before.
  • Exporting the HTTP mappings now writes every mapping in the zip as a .json file, so an exported zip unpacked into a WireMock root directory loads every mapping it holds. A mapping keeps the name it was given, whether it arrived through the admin API or in a mapping file that carries its own name, as a stub recorded by WireMock does, and the export previously used that name as the file name as it stood, so a mapping named Get order 1001 became the entry mappings/Get order 1001, with no extension. A mapping file with no name of its own is named after the file, and a mapping saved from the Add/Edit HTTP mappings editor is always named <name>.json, so those already carried the extension. Traffic Parrot imported such a zip either way, but WireMock reads only files with the .json extension from its mappings directory, so it silently loaded none of the mappings exported without it. Every entry is now written as mappings/<mapping name>.json, whether the zip is downloaded with the Download Mappings button on the Export page or through the Management API. Nothing else in the zip changes: importing it back into Traffic Parrot is unchanged, and a mapping whose name already ends in .json is exported exactly as before.
  • The gRPC Add mapping form no longer carries an exception mapping's metadata and error details onto a successful mapping saved after it. The form keeps its values after a save, and switching from the Exception tab to the Successful tab left the exception metadata and error details in place behind it, so the next mapping saved from the Successful tab replied with status OK but with the earlier mapping's metadata and a grpc-status-details-bin header attached to the reply. Switching to the Successful tab now clears them, and a mapping saved with a successful status never carries error details, whichever tab it was saved from.
  • Clearing the HTTP stub mappings no longer fails when one of the mapping files holds more than one mapping. A file in the mappings directory may carry several mappings in a single JSON document ({"mappings": [ ... ]}) rather than one mapping per file, and Traffic Parrot loads and serves those mappings normally. Clearing them, however, answered HTTP 500 and removed nothing. It went on answering HTTP 500 for every attempt that followed until Traffic Parrot was restarted, including after the file itself had been deleted from disk. The mappings are now cleared, the answer is HTTP 200, and such a file is deleted whole, exactly as the mapping files holding a single mapping already were. A file holding several mappings that earlier clears left in place is deleted by the next clear, with every mapping it holds, so copy any such file you still need before clearing. This applies to all three endpoints that clear the mappings: POST /api/http/__admin/mappings/reset, DELETE /api/http/__admin/mappings and POST /api/http/__admin/reset. Saving or deleting one individual mapping that was loaded from a file holding several is unchanged and is still refused, because that one mapping cannot be rewritten out of the file it shares with the others; edit the file on disk instead. An installation whose mapping files each hold a single mapping is unaffected and clears exactly as before.
  • Editing a recorded gRPC mapping in the gRPC Add/Edit mapping form no longer discards the CallId and MessageNumber values that mapping carries. Traffic Parrot writes these as response headers in the mapping file when it records a streaming call, to group the messages belonging to one RPC call and to order them (see gRPC streaming modes). The form does not offer them for editing, and it previously saved the mapping back without them, so opening a recorded mapping and clicking Save dropped both values even when nothing had been changed. A recorded server streaming, client streaming or bidirectional streaming call then stopped replaying in order, or stopped replaying as one call, as soon as any single message of it had been edited in the GUI, and putting the values back meant editing the mapping file by hand or recording the call again. Every response header a mapping already holds is now carried through a save unchanged. A mapping that holds none, such as a unary mapping added by hand, is unaffected and saves exactly as before, and editing exception metadata is unchanged.
  • Sending the gRPC management API (POST /grpc/management/mapping and PUT /grpc/management/mapping/{id}) a body that cannot be read as a mapping now returns HTTP 400, carrying the reason in the same primingResult field these endpoints already use to report a refusal, instead of HTTP 500 and a stack trace in the log. For example, a request matcher written as {"containing": "b"}, where the key the mapping grammar defines is contains, is answered with {"containing":"b"} is not a valid match operation; a body that is not JSON at all is answered with the reason it could not be parsed. Nothing is created or updated in either case, as before. The gRPC Add/Edit mapping form is unaffected, because it cannot produce such a body.
  • The message shown when a mapping cannot be saved because another mapping already owns its file name now quotes the name you typed, and names the file the two mappings land on. In the Add/Edit HTTP mappings editor the refusal previously quoted that file name where the mapping name belonged, so a mapping renamed to orders:new was refused with Mapping 'orders_new.json' cannot be saved because its file name clashes with another mapping's., naming a string that appears nowhere on the form. It now reads Mapping 'orders:new' cannot be saved because its file name 'orders_new.json' clashes with another mapping's. and goes on, as before, to explain how a file name is derived from a mapping name, so you can see both the name to change and the file the two names collapse onto. The MCP create_mapping and update_mapping tools return the same message; there the name was already reported correctly, and the file name is the new part. Which saves are refused is unchanged, and so are the mapping name Traffic Parrot stores and the file it writes.
  • WebSocket mappings are now loaded from the selected scenario. Every scenario directory has always been created with a websocket-mappings folder beside its mappings, grpc-mappings, jms-mappings, ibm-mq-mappings and file-mappings folders, but the WebSocket simulator only ever read the folder at the traffic files root, so a mapping placed in a scenario's folder was silently ignored: it did not appear on the WebSocket Add/Edit page, nothing was written to the log, and a client that connected to its channel was accepted and then got no reply. Mappings are now loaded from websocket-mappings in the selected scenario, or from the traffic files root when no scenario is selected, as the other protocols' mappings are, and the WebSocket Add/Edit page and the /websocket/management API follow a scenario switch together with the virtual service. A mapping saved while a scenario is selected is written to that scenario's folder. If you have kept WebSocket mappings at the traffic files root and work with a scenario selected, move them into that scenario's websocket-mappings folder, because the root folder now serves only when no scenario is selected. A mapping left at the root while a scenario is selected is not served: it is not listed on the Add/Edit page, nothing is written to the log, and a client that connects to its channel is accepted and then gets no reply. The file is not moved or deleted. The traffic files root is the default scenario, the one that is active when Traffic Parrot starts, so an installation that has never activated another scenario is unaffected. A mapping file written by an earlier version of the WebSocket simulator, whose only top-level property is webSocket, is not in the current mapping format: it loads as a catch-all mapping that matches incoming frames and replies nothing, and Traffic Parrot now writes a warning naming the file to the log, once per file, with the instruction to delete it, because such a file carries no id and cannot be edited or deleted from the Add/Edit page. See WebSocket System Simulation Properties.
  • A mapping with a scenarioName that is added through the API while Traffic Parrot is running is now listed once by GET /api/http/__admin/scenarios. Previously it appeared twice in that scenario's mappings array, so a test or tool that counted a scenario's mappings saw every mapping added at runtime twice. Matching, the scenario's state, its possibleStates and its transitions were unaffected, and mappings loaded from the mappings directory at startup were always listed once.
  • Importing a zip of mappings through POST /http/management/importMappings or POST /grpc/management/importMappings now reports an imported mapping that replaced an existing one separately from one that was added. An imported mapping is registered under a fresh id and written to the file its name maps to, and a mapping whose name ends in .json is written to the file of that name, so importing an instance's own export writes each such mapping over the file of the mapping it was exported from. The response listed only the new id among mappings, as though the mapping had been added, so a re-import could replace mappings without saying so. The response now also carries a replaced member, present only when at least one imported mapping took the file of an existing one, with one entry per such mapping giving its id and the supersededId of the mapping it replaced. The superseded mapping no longer backs a file on disk, and deleting the imported id does not restore it. An import that replaces nothing responds exactly as before. See Importing via REST API on the HTTP page, Importing via REST API on the gRPC page and the OpenAPI documentation.
  • A backslash now escapes a {{...}} expression that is the first one in an HTTP response body. Disabling {{...}} documents \{{...}} as the way to leave one expression as written, but when the escaped expression was the first in the body the backslash was kept in the output and the expression was substituted anyway; an escaped expression anywhere after the first was already left alone. The escape now works in every position, so a body that starts with an escaped expression renders it as written, without the backslash. A body whose first expression is not escaped renders exactly as before.

Wednesday, 26 August 2026

Traffic Parrot 5.62.1 released, what's new?

We have just released version 5.62.1. Here is a list of the changes that came with the release:

Features

  • The messaging Add/Edit mapping editors (JMS, native IBM® MQ and file message) now offer the NOT (negate a matcher) request body matcher, as the HTTP and gRPC editors already do. Choosing it in the Request body dropdown replaces the request body field with a NOT child matcher sub-editor where you pick the matcher to negate and type its value, so you can stub "everything except this" without hand-editing the mapping JSON. The child matcher dropdown offers the leaf matchers that can be negated (equalTo, contains, doesNotContain, matches, doesNotMatch, equalToJson, matchesJson, matchesJsonPath, equalToXml, matchesXml and matchesXPath); the SWIFT and FIX field matchers and the JMS-only binaryEqualTo matcher are not offered as children and are still negated by writing the matcher in the mapping JSON. The mapping is saved as a nested "bodyMatcher": {"not": {"contains": "CANCELLED"}}, reopening it restores the body matcher, child matcher and child value into the sub-editor, and the messaging mappings list now shows a composite matcher as a readable one-line summary such as not (contains 'CANCELLED') instead of the raw matcher JSON (matching how the HTTP and gRPC mapping lists and the messaging import preview already render it). The and and or operators are offered in the same dropdown; see Authoring and and or in the messaging editor.
  • The messaging Add/Edit mapping editors (JMS, native IBM® MQ and file message) now also offer the AND (all sub-matchers match) and OR (any sub-matcher matches) request body matchers, so all three logical operators are now offered in the request body matcher dropdown of every mapping editor that has one (see Logical operators). Choosing AND or OR in the Request body dropdown replaces the request body field with a Sub-matchers panel holding two rows, each one a matcher plus the value to match it against. Add sub-matcher appends a row and the bin button on a row removes it, down to the two sub-matchers that and and or need to mean anything. A row's matcher dropdown offers the same leaf matchers as the NOT child matcher dropdown (equalTo, contains, doesNotContain, matches, doesNotMatch, equalToJson, matchesJson, matchesJsonPath, equalToXml, matchesXml and matchesXPath), and a sub-matcher value is a multi-line field, so a multi-line JSON or XML body can be typed or pasted straight into a sub-matcher row and keeps its line breaks when the mapping is saved and reopened. The mapping is saved as "bodyMatcher": {"and": [{"contains": "ORDER-"}, {"doesNotContain": "DRAFT"}]}, reopening it restores one row per sub-matcher in the order it was saved, and the messaging mappings list shows the matcher as a readable one-line summary such as and (contains 'ORDER-', doesNotContain 'DRAFT') instead of the raw matcher JSON.
  • A JMS response body can now be served from a file in the __files directory, the messaging counterpart of an HTTP file-backed response. Set bodyFileName on a JMS mapping's response (a path under __files, resolved against the __files directory beside the mapping's jms-mappings directory, per-scenario for scenario-scoped mappings) in place of an inline text value; the two are mutually exclusive. Setting both, or referencing a file that does not exist, is rejected at load time and the messaging mappings fail to load until it is corrected (the error names the mapping and the missing file). The file's contents are UTF-8 text and are run through the same Handlebars templating as an inline text body, so a body file can reference the incoming request (for example {{request.body}}) and use the response helpers. In the JMS mapping editor a file-backed response shows a read-only Response from file: note and loads the file's contents into the editable response body field; saving writes your edit back to the file and keeps the bodyFileName reference, so the body stays in the file rather than being copied inline. Exporting a scenario bundles the referenced body file, so file-backed JMS mappings round-trip through import and export. File message mappings can reference a response body file in exactly the same way. Native IBM® MQ mappings cannot: bodyFileName is not part of the IBM® MQ response format, so an IBM® MQ response body stays inline. Support for binary (bytes) response bodies read from a file will follow. See Response body from a file on the JMS page.
  • A gRPC request body matcher can now read the value it matches against from an external __files file through a bodyFileName reference: the same file-backed request body matcher already available for HTTP. Traffic Parrot resolves the file when the mapping loads and matches against its contents on replay exactly as it would an inline value, and the gRPC mappings list now shows the resolved file content (or, for a binary file, the matcher and the file path) in the Request message column instead of the raw bodyFileName reference. Editing a file-backed gRPC request matcher from the mapping editor is covered by the Switch to file and Switch to inline entry below.
  • The HTTP and gRPC mappings list now shows the matcher together with the bodyFileName path for a request body matcher whose value is read from a binary file (an image, PDF or other non-text file), for example binaryEqualTo 'payload.png', truncated to fit the column with the full path on hover, instead of a fixed placeholder that read the same for every binary file and so identified none of them. This applies to the HTTP Request body column, the gRPC Request message column and the rows' hover summaries, and it mirrors what the response body column already shows for a binary file-backed response body. Because the mappings list searches the text it displays, a mapping whose request body matcher is backed by a binary file can now also be found by typing the file name into the list search. A request body matcher backed by a text file still shows the file's content inlined, exactly as before.
  • A gRPC request payload can now be moved into, and out of, an external __files file from the mapping editor, rather than only by hand-editing the mapping JSON on disk. Opening a saved gRPC mapping whose request payload is inline shows a Switch to file button beneath the payload field; clicking it reveals a file name field pre-filled with a name derived from the mapping, and Save writes the payload to that file in __files and rewrites the matcher to the bodyFileName reference form. Re-opening the mapping shows a Content from file: note naming the file, loads the file's contents into the payload field so you can edit them in place, and offers Switch to inline to detach the reference and store the payload in the mapping again. A warning is shown when the file is shared with other mappings, because saving changes the request matcher for all of them. This matches what the HTTP editor already did, and the gRPC response body controls. The controls appear only for a mapping that has already been saved, and only for a single in-scope text matcher: a binary matcher or a request with several body patterns stays read-only in the editor, as on the HTTP form. Note that the file name may include forward-slash subdirectories, which resolve under __files, so requests/example.json is saved as __files/requests/example.json - the same as a response body file. A name that would resolve outside __files is rejected.
  • The request body Switch to file and Switch to inline controls in the mapping editor now cover three more request body matchers: matches JSON (matchesJson), matches GraphQL (matchesGraphQL) and matches JSON schema (matchesJsonSchema). Traffic Parrot already matched all three against a value read from an external __files file, but the editor did not offer to move one out to a file, so a large JSON body, GraphQL query or JSON Schema had to be externalised by hand-editing the mapping JSON on disk. This matters most in the gRPC editor, where matches JSON representation (matchesJson) is the matcher gRPC mappings most often use for the request payload. Saving keeps the matcher you chose rather than swapping it for equalToJson, so what the mapping matches is unchanged.
  • The Static Files page is now listed in the gRPC, JMS and Files menus, as it already was in the HTTP menu. All four entries open the same page, because __files is a single directory shared by every protocol within a scenario, so a file there can back a gRPC request payload, a gRPC response, a JMS or file-message response body, or an HTTP response equally. Previously the page could only be reached from the HTTP menu (or from the Manage files link in the gRPC response body area), which was easy to miss when working entirely in another protocol. The WebSocket and IBM® MQ menus deliberately do not carry the entry: those two do not resolve a bodyFileName, so the entry would advertise a capability they do not have.
  • Traffic Parrot now serves a Model Context Protocol (MCP) endpoint, so an AI assistant can connect to a running instance and read what it holds instead of working only from files you paste into the conversation. Traffic Parrot serves it itself, so there is nothing extra to install and no separate process to keep running: it listens on the management port (8080 by default) at /mcp and takes JSON-RPC 2.0 over HTTP POST, at the bare endpoint URL MCP clients are configured with rather than under /api. A connecting client completes the initialize handshake, which negotiates the protocol revision — the client is answered the revision it asked for when that is one Traffic Parrot speaks (2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05 or 2024-10-07), and the newest, 2025-11-25, when it asked for an unknown revision or for none at all — and which reports the instance's own version and its capabilities, and then lists twelve tools. Six only read — list_mappings, get_mapping, list_received_requests, report_coverage, list_scenarios and verify_mapping_matches — covering the mappings being served, the request journal, endpoint coverage, the defined scenarios, and whether received traffic still matches the mappings loaded now. The other six change something: create_mapping, update_mapping and delete_mapping change what the running instance serves and write the change through to the selected scenario's mapping files, so a scenario directory kept in version control will show it as a local change; select_scenario switches which scenario is being served, and so decides what every other tool reads and writes; generate_mappings_from_openapi builds mappings from a specification file already in the instance's OpenAPI directory, writing them through to the scenario's mapping files in the same way; and cleanup_mappings turns a recording into a flexible mock in a new scenario, leaving the recording untouched. Running a tool (tools/call) returns MCP content blocks, and only the advertised tools can be called. Every tool that can lose work takes dryRun, which defaults to true, so a call that does not say otherwise previews the change and tells the assistant how to commit it. A preview changes nothing in Traffic Parrot, but it is not inert: generate_mappings_from_openapi reads the specification file and fetches any externalValue example URL in it during a preview exactly as it does when applying. By default the endpoint accepts requests from the local machine only and needs no token; to reach it from elsewhere — notably Traffic Parrot in a container with a published port — set trafficparrot.mcp.authenticator=BEARER_TOKEN and trafficparrot.mcp.token, and send Authorization: Bearer <token>. Otherwise, anything that can reach the management port can reach this endpoint, so run Traffic Parrot on your own machine or a trusted network. Switching on the web UI login (trafficparrot.gui.security.mode=LOGIN_PROPERTIES) covers /mcp as well, so the client must then also send the HTTP BASIC credentials from trafficparrot.gui.login.properties; that refusal is an HTTP 401 whose JSON-RPC error names the login property and the credentials file, as against the HTTP 403 the MCP authenticator returns.
  • The HTTP Mapping cleanup results now name the mapping directories the cleaned scenario does not carry. Cleanup copies the source scenario's HTTP mappings and its __files directory and nothing else, so any JMS, native IBM® MQ, file message, gRPC or WebSocket mappings the scenario holds stay behind in the source scenario. That has always been the case, and it is deliberate (the cleanup conventions are HTTP conventions and have no meaning for a JMS or a gRPC mapping); what is new is that cleanup now tells you instead of leaving you to notice. A warning panel headed Mappings the cleaned scenario does not serve appears above the result counters with one entry per directory, naming it and the number of files that stayed behind, for example jms-mappings (3 file(s)) - not copied into the cleaned scenario. Nothing is lost: cleanup never modifies the source scenario, and the warning is there so the cleaned scenario is not mistaken for a complete replacement of the recording it came from and the other mappings dropped at the next promotion. Only a directory that actually holds files is listed, so an HTTP-only scenario shows no panel; the count is of files rather than mappings, because one mapping file can hold several mappings. The same list is returned by POST /api/http/cleanup/execute in a new droppedMappingDirectories field, and by the cleanup_mappings MCP tool, whose dryRun preview reports the same entries a real run would. It is a warning rather than an error: the cleanup succeeded and the errors field stays empty. See What the cleaned scenario does not carry.
  • A JMS request body matcher can now read the value it matches against from an external __files file through a bodyFileName reference, the messaging counterpart of the HTTP and gRPC file-backed request matchers. Traffic Parrot resolves the file when the mapping loads and matches against its contents on replay exactly as it would an inline value, so a large expected payload can live in a file beside the mapping instead of inside it. Exporting a scenario bundles the files referenced by request matchers as well as those referenced by response bodies, so a file-backed matcher round-trips through import and export. A bodyFileName is supported only on a matcher at the top level of the request: nesting one inside an and, or or not operator is refused when the mapping loads, with an error naming the mapping and the matcher. Native IBM® MQ and file-message request matchers keep their expected values inline.
  • Traffic Parrot now publishes the JSON Schemas that describe its own mapping formats, served from the management port (8080 by default), so a tool — or an AI coding agent — can fetch them from the very instance it is targeting and check a mapping before loading it. /schemas/index.json is a machine-readable index with one entry per mapping format (HTTP, which also covers gRPC; JMS; native IBM® MQ; and file messages), each carrying that schema's URL, its version, and the directory the format's mapping files live in; the schemas themselves are served beside it, for example /schemas/http-mapping.schema.json. They ship inside Traffic Parrot, so they always describe the version you are actually running. Alongside them, /helper-policy.json publishes the Handlebars helper safety policy: which response-templating helpers are safe to use, and which are denied because they can run arbitrary code (executeProcess and evaluate). trafficparrot validate can enforce that policy as well as the schemas — set trafficparrot.validate.helper.policy.check and validation fails when a mapping uses a denied helper, which is worth switching on for mappings a machine wrote. See Point the agent at the mapping schema.

Documentation

  • Added a Generate mocks with your own AI agent recipe page. It shows how to have your own AI coding agent (Claude Code, Cursor, or any assistant that can fetch a URL and call an HTTP API) write Traffic Parrot mappings for you: ground the agent on the mapping JSON Schema and the helper policy your instance serves, plus your own API contract, have it check the mapping against the schema, then load it through the admin API. The page opens with a Claude Code quick start and a copy-paste starter prompt that runs end to end against the community Train Travel API sample, so you can try it as-is before swapping in your own contract. It also covers running on a trusted network, the Handlebars helper deny-list (helper-policy.json) and its opt-in trafficparrot.validate.helper.policy.check enforcement in trafficparrot validate, and why an API contract handed to a language model is untrusted input. The mapping-creation endpoints it uses are documented in the Management API specification.
  • Expanded the Management API documentation to cover ten more management endpoints: priming an HTTP mapping and downloading a file from the scenario's __files directory, exporting the HTTP mappings, listing the gRPC mappings, and listing, creating, editing and deleting JMS and native IBM® MQ mappings. Each carries its request and response shapes and the status codes it returns, including the HTTP 409 the JMS and native IBM® MQ endpoints return while the virtual service is running — stop it before adding, editing or deleting a mapping. The WireMock-compatible admin API continues to be covered by reference to WireMock's own API documentation rather than restated here, and the raw specification remains available to download.

Fixes

  • Saving a gRPC mapping with an empty or malformed method name (the package.Service/Method value) now reports a clear validation message instead of failing with an internal server error. In the gRPC Add/Edit mapping form an inline message, Enter a fully-qualified gRPC method as package.Service/Method, appears beneath the gRPC method field and blocks the save; the gRPC management API (POST /grpc/management/mapping and PUT /grpc/management/mapping/{id}) returns HTTP 400 with the same message instead of HTTP 500. Previously an empty or malformed method produced a raw HTTP 500 and stack trace. Mappings imported from an exported zip are unchanged.
  • The ActiveMQ internal broker no longer caps the TCP connection handshake allowance at 10 seconds. Traffic Parrot asks for 60 seconds when it connects to the internal broker, but the broker itself had never advertised a matching allowance, and the OpenWire protocol settles on the shorter of the two ends' values — so 10 seconds was always the limit in force, and a connection slow to complete its handshake (a heavily loaded machine, or many connections opening at once) could be dropped with an inactivity error. The broker now advertises the same 60 seconds, so Traffic Parrot's own connections get the allowance they ask for, and an application that configures a longer allowance on its own ActiveMQ client is no longer capped to 10 seconds by the internal broker. An application left on the ActiveMQ client default is unaffected. AMQP connections are unaffected — this applies to the TCP (OpenWire) transport only.
  • The target scenario name given to HTTP mapping cleanup must now be a name inside the instance's scenarios directory. Previously a name that resolved somewhere else — one climbing out of that directory with .., or an absolute path — was accepted, and the cleaned mappings were written to wherever it pointed. Such a name is now refused: the Cleanup page reports Target scenario '…' must be a name inside the scenarios directory instead of running, and POST /api/http/cleanup/execute returns HTTP 400 carrying the same message in its error field, instead of HTTP 200 after writing the mappings. An ordinary scenario name is unaffected and behaves exactly as before.
  • A request body matcher that reads its value from a file (a bodyFileName reference) must now name a file inside the mapping's __files directory. Previously a bodyFileName that resolved outside it, whether by climbing out with .. or by giving an absolute path, was accepted, and Traffic Parrot read that out-of-directory file and matched against its contents. Such a reference is now rejected when the mapping loads. An ordinary bodyFileName under __files is unaffected and behaves exactly as before.
  • Importing a zip of mappings now refuses any entry that would be written outside the directory being imported into. Previously an entry whose path climbed out with .. was extracted to wherever it pointed, so importing a zip from an untrusted source — which the admin GUI allows in a single click — could create or overwrite a file anywhere the Traffic Parrot process could write. Every file written during an import is now checked to be inside the target directory before it is created, and an entry that fails the check stops the import with an error naming it. An ordinary exported zip is unaffected and imports exactly as before.
  • Renaming a scenario now requires both the existing and the new name to be names inside the instance's scenarios directory. Previously a name that climbed out of that directory with .., or gave an absolute path, was accepted and the scenario directory was moved to wherever it pointed. Such a name is now refused, and a rename rejected for this or any other validation reason returns HTTP 412 carrying the reason instead of HTTP 500 and a stack trace. An ordinary scenario name is unaffected.
  • Importing a gRPC skeleton now refuses a file name that would resolve outside the scenario's gRPC mappings directory, instead of writing the generated mapping there.
  • A path that reads as being inside a permitted directory but points outside it through a symbolic link is now refused, where it was previously accepted. This applies wherever Traffic Parrot checks that a path stays inside a directory, including the mapping conflict preview.
  • The in-GUI configuration file editor now refuses a path that belongs to neither of the directories it manages, rather than falling through and editing it anyway.
  • The Static Files page now takes messaging mappings into account when it decides whether a file is still in use. Deleting or renaming a file referenced only by a JMS or file-message mapping was previously allowed without warning, because the check looked at HTTP and gRPC mappings only, and the mapping was left pointing at a file that no longer existed. The orphan badge now counts references from every protocol, so a file shown as orphaned really is unreferenced.
  • Two mappings can no longer be saved under the same mapping file name. A mapping's file is named after the mapping, so saving a second mapping whose name produces a file name already in use previously overwrote the first; such a save is now refused with a message asking you to rename the mapping, on the GUI, the admin API and the MCP tools alike. Mappings you already have are unaffected: importing and starting up are unchanged, so a scenario that already holds two mappings sharing a file name keeps loading exactly as before — what changes is that re-saving one of them from the editor now asks you to rename it first. The two shapes to expect are two mapping names that produce the same file name (orders/new and orders:new both become orders_new.json) and, on Linux, two names differing only in case. Relatedly, resetting the admin API while two mappings already shared a file name returned HTTP 500; it now completes.
  • Three fixes to the log viewer: the line-number gutter stays aligned with wrapped lines instead of drifting out of step with them; a log file with CRLF line endings no longer renders a stray carriage return in every line; and querying the configured log appenders returns an answer instead of throwing, so the viewer can report which files it is reading.
  • Exporting mappings to CSV no longer silently drops a mapping whose request-body matcher reads its expected body from an external file; it is exported with the rest.
  • Generating a mapping from a captured TEXT message that has no body no longer fails with an internal error.
  • Uploading a protoset file no longer poisons later gRPC proto scans: a bad upload is contained to that upload, instead of making every subsequent scan fail until Traffic Parrot is restarted.
  • Switch to file now reports why it cannot externalise a request body it does not support, instead of appearing to work and silently doing nothing.
  • A failed specification import now shows the reason the server gave, instead of the literal text undefined.
  • Importing an OpenAPI specification whose operation declares a response without content — a description but no content block, as is usual for a 204 No Content on a delete — now generates a mapping whose response has no body. Previously the generated response body was the literal text */*, a placeholder that leaked out of the example generator, and the mock then served that text to every matching call. A response that does declare content, including a declared */* media type carrying an example or a schema, is generated exactly as before. Upgrading does not rewrite mappings you already have; the change applies when a specification is imported, so re-importing the same specification regenerates such mappings without the placeholder body.
  • The file messaging page now reports a mappings-load failure instead of waiting indefinitely for mappings that will never arrive.
  • Property values that look like secrets are now redacted in the configuration Traffic Parrot writes to its log at startup, instead of being logged verbatim.

Monday, 20 July 2026

Traffic Parrot 5.61.0 released, what's new?

We have just released version 5.61.0. Here is a list of the changes that came with the release:

Features

  • Added the ability to import an AsyncAPI specification to create messaging mappings automatically — the messaging counterpart of importing an OpenAPI specification for HTTP. Go to JMS or IBM® MQ in the top navigation bar and click Import AsyncAPI, then upload a JSON or YAML AsyncAPI 2.x or 3.0 document. Traffic Parrot parses the document's channels and operations and shows a preview of one mapping per importable operation (destination, queue/topic type, operation id, the example payload generated from the message schema, and a duplicate-status badge); you select exactly which operations to import with per-row checkboxes and an Import Selected count. A malformed, unsupported, or empty document reports a clear error and creates nothing. Each selected operation becomes a mapping — a JMS mapping from the JMS page or a native IBM® MQ mapping from the IBM® MQ page — that serves the generated example payload, ready to refine in the editor. Both AsyncAPI 2.x and AsyncAPI 3.0 documents are supported with the JMS and IBM® MQ channel bindings; for a 3.0 document the destination is taken from the channel's address (falling back to the channel name). Both one-way send/receive operations and request-reply operations (those carrying an AsyncAPI 3.0 reply object) are imported: a request-reply operation receives the request on the operation's own channel and publishes the reply message's example on the reply channel's destination, shown in a Reply destination column in the preview. Other bindings (AMQP, Kafka, MQTT) and AsyncAPI 4.x are not yet supported, and advanced JSON Schema draft 2020-12 constructs that cannot yet be turned into an example render a placeholder payload.
  • The headless trafficparrot validate command now also checks your messaging mappings against your imported AsyncAPI specifications, so a partially-mocked messaging API cannot silently drift from its contract — the messaging counterpart of the OpenAPI (HTTP) and proto (gRPC) coverage checks. It enumerates the operations declared in the AsyncAPI specifications under <files-root>/asyncapi/ and reports any declared operation that has no backing JMS, native IBM® MQ or file-message mapping, for example No mapping covers AsyncAPI operation: QUEUE:orders. Operations are matched to mappings by destination identity (TYPE:name, where TYPE is QUEUE or TOPIC); for a request-reply operation coverage is measured on the inbound (request) destination only. Messaging drift participates in the same report and the same exit codes as the HTTP and gRPC checks (drift fails the build with exit code 1). Destinations you deliberately leave unmocked can be excluded with a messaging-coverage.properties file (key exclude.destinations) in the files-root, and a stale allowlist entry for a destination not present in any AsyncAPI specification is itself reported as drift. See Messaging coverage check on the JMS page.
  • The add/edit messaging-mapping editor now has a skeletons dropdown that pre-fills a messaging mapping form from an operation in an imported AsyncAPI specification — the messaging counterpart of the HTTP and gRPC skeletons dropdowns. Place AsyncAPI specifications (JSON or YAML, 2.x or 3.0) into the install's asyncapi/ directory (or use the import button beside the dropdown to upload one), then pick an operation: the form's destination name, destination type (Queue/Topic radio) and request body/payload pre-fill. For a request-reply operation the option is labelled <operationId> (request-reply) and pre-fills both the request side and the reply side (a cloned response row); a one-way operation pre-fills the request side only. The dropdown appears in both the JMS and native IBM® MQ messaging editors. See Messaging skeletons on the JMS page and on the IBM® MQ page.
  • Added the ability to import an async-message Pact contract as JMS or native IBM® MQ messaging stubs — the messaging counterpart of importing a Pact contract's HTTP interactions. Go to JMS or IBM® MQ in the top navigation bar and click Import message Pact, then upload a Pact .json file. Both contract shapes are detected automatically: a specification v3 contract's top-level messages[] array and a v4 contract's Asynchronous/Messages interactions[] (whose example body is read from the contents.content envelope). An HTTP-only contract is rejected with a message pointing you to the HTTP Pact import. Traffic Parrot shows a preview of one mapping per message interaction (destination, queue/topic type, the message description as the operation id, the example contents payload, and a duplicate-status badge); you select exactly which messages to import with per-row checkboxes and an Import Selected count. A file that is not valid JSON or has no message interactions reports a clear error and creates nothing. Each selected message becomes a one-way publish mapping — a JMS mapping or a native IBM® MQ mapping — that publishes the example payload on receipt of any message on its destination; Pact message interactions are fire-and-forget, so these are not request-reply stubs. Because a Pact message carries no destination, it is derived from the message's metadata (a destination, queue or topic key, where a topic key makes it a TOPIC) or, failing that, from the message's description; a description-derived destination raises a warning in the preview so you review it before importing. Import message Pact is licence-gated, available on a JMS or IBM® MQ licence respectively (the menu link does not appear without it). See Import a message Pact contract on the JMS page and on the IBM® MQ page.
  • You can now import a Pact contract (consumer-driven contract, specification v2, v3 and v4) as HTTP stubs. Upload the Pact .json file on the HTTP Import page exactly as you would any other format — Traffic Parrot detects it automatically (no format to choose) and turns each HTTP interaction into a stub mapping through the same preview, filter and select flow used for HAR and OpenAPI/Swagger. The request (method, path, query, headers, body) becomes the request matchers and the response (status, headers, body) becomes the stubbed response; JSON request bodies are matched tolerantly. For v4 contracts the Synchronous/HTTP interactions are imported (their bodies are read from the v4 body.content / body.contentType envelope); everything else is imported the same way as for v2 and v3. Pact matchingRules are honoured where they map onto Traffic Parrot's request matchers (request header and path regex, default JSON-body type matching); other rules fall back to an exact match and the import reports a warning listing them. This HTTP import handles HTTP interactions only — Pact message (asynchronous) interactions, including the Asynchronous/Messages interactions of a v4 contract, are imported separately as JMS or IBM® MQ messaging stubs (when a v4 contract mixes the two, the HTTP import brings in the HTTP interactions and skips the message ones with a warning). Response templating and provider verification / Pact Broker pull are not yet supported. See Importing Pact contracts.
  • You can now import an HTTP specification from a URL instead of downloading it to disk first. Paste a specification URL into the URL field on the HTTP Import page and click Fetch & preview; Traffic Parrot fetches the document server-side and feeds it into the same content-based import pipeline as an uploaded file, so Swagger/OpenAPI, HAR and Pact specifications can be imported by URL exactly as they are from a file, each shown in the same preview before importing. RAML and WireMock ZIP specifications cannot yet be imported from a URL and must be imported from disk. The feature is off by default and must be enabled by an administrator: set trafficparrot.http.importfromurl.enabled to true and list the hosts Traffic Parrot may fetch from in trafficparrot.http.importfromurl.allowedHosts, a default-deny allow-list (with the feature enabled but the allow-list empty, every fetch is refused). Because the fetch originates from the Traffic Parrot host, the feature is guarded against server-side request forgery (SSRF): allow-listed or not, a URL that resolves to a loopback, link-local, private or cloud-metadata address is rejected, DNS rebinding is guarded against, the scheme is restricted to http and https, and the response size and the connect and read timeouts are capped. See Importing from a URL.
  • Added the ability to export HTTP mappings as an OpenAPI 3.0 specification — the inverse of importing an OpenAPI specification. The active scenario's mappings are translated into OpenAPI paths and operations (grouping by path and method, normalising exact, templated and regular-expression URL matchers into OpenAPI paths, lifting query and header matchers into parameters, and emitting each response body as a literal example together with an inferred JSON schema for JSON bodies) and served off the existing export endpoint via a new ?format=openapi query parameter (GET /http/management/exportMappings?format=openapi), returning a YAML document (Content-Type: application/yaml) as openapi-<timestamp>.yaml; without the parameter the endpoint still returns the native WireMock ZIP unchanged. The same document can also be exported as JSON with ?format=openapi-json (Content-Type: application/json, downloaded as openapi-<timestamp>.json) — only the serialisation differs. The OpenAPI export can be triggered from the GUI with the Export as OpenAPI (YAML) button on the HTTP Export page (alongside the existing Download Mappings button) as well as programmatically through the endpoint. Matchers with no clean OpenAPI representation (regex/JSONPath/XPath body matchers, proxy responses) are never silently dropped — every mapping's disposition (REPRESENTED, APPROXIMATED or OMITTED) is recorded in a coverage report surfaced both as a root-level x-trafficparrot-export-coverage extension and as a one-line summary in info.description. For each JSON response body Traffic Parrot also infers a JSON schema and emits it alongside the example, so generated clients, SDK codegen and documentation portals get real models rather than just a sample value; inference is conservative and based on the single example body (structure followed recursively, integral numbers become integer and non-integral number, JSON null becomes nullable, observed keys become properties but none is marked required, and no enum is inferred). A non-JSON response body (XML, plain text, empty) gets no schema — the example is still emitted and the mapping is recorded in the coverage report with reason no-schema-inferred. This release is single-scenario and OpenAPI 3.0 (not 3.1), with no multi-example merge or security schemes yet.
  • Added the ability to export recorded HTTP traffic as a HAR 1.2 file, ready to drive a load or resilience test through your own engine. Unlike the native ZIP and OpenAPI exports, which export the deduplicated mappings, the HAR export is taken from the live request journal — the ordered, timed stream of inbound requests served during the current session — so the request order and per-request timing a load profile needs are preserved. Each request becomes one HAR log.entries entry carrying the method, URL, headers, query string and request body, the response status, headers and body, and startedDateTime / time. Click the new Download recorded traffic (HAR) button on the HTTP Export page (alongside Download Mappings and Export as OpenAPI (YAML)), or trigger it programmatically off the existing export endpoint with the ?format=har query parameter (GET /http/management/exportMappings?format=har), which returns a HAR 1.2 document (Content-Type: application/json) downloaded as recorded-traffic-<timestamp>.har; without the parameter the endpoint still returns the native WireMock ZIP unchanged. The exported HAR can be fed into the common HAR-to-load-test converters such as the Gatling HAR Converter or Grafana har-to-k6 (Traffic Parrot does not run the load itself — you supply the engine). The export reads the in-memory request journal, so only the most recent requests retained by trafficparrot.virtualservice.maxRequestJournalEntries (default 1000) are exported; if the journal is disabled (=0) the export is a valid but empty HAR. This first release exports text bodies for HTTP traffic only.
  • HAR import now offers explicit conflict resolution for duplicate entries. When a preview entry matches an existing stub with the same HTTP method and exact URL, an On duplicate dropdown on that row — and a single For duplicates: control that applies to every duplicate at once — lets you Skip it (the default, so imports you do not touch are unchanged), Overwrite the existing stub (all stubs matching that method and URL are removed before the imported one is added, so no duplicate is left behind), or Import as new (the existing stub is kept and the imported one is added under a disambiguated name such as <name> (copy)). A per-row choice overrides the bulk default. Re-importing the same file still replaces the mappings it created last time, so those entries show as New; the actions apply to a clash against a stub from a different import or one created by hand. The same choice is available on the REST import endpoint (POST /http/management/importSelectedEntries) through an optional conflict-strategy parameter (skip | overwrite | importAsNew, default skip) with per-entry conflictStrategies overrides; omitting the parameter keeps the previous behaviour. Duplicate detection is an exact method-and-URL match, so an existing stub that matches its URL by pattern or regular expression is not detected as a duplicate. See Preview and filter before importing.
  • The HTTP and gRPC mappings list now shows the bodyFileName path for a mapping whose response body is served from a binary file (an image, PDF or other non-text file), truncated to fit the column with the full path on hover, instead of an unreadable placeholder, so you can tell at a glance which mapping serves which file. When that binary file is an image (PNG, JPEG, GIF, WebP, BMP, ICO, APNG or AVIF), hovering the row pops a small rendered preview of the image in place of the file-path tooltip, so you can recognise the picture a mapping serves without opening the editor; the cell still shows the truncated bodyFileName path, the preview is loaded on first hover and capped to a thumbnail, and it falls back to the plain path tooltip if the image cannot be read. Non-image binary rows keep the full-path tooltip. Inline, recorded, and text file-backed responses show their body content in the Response body / Response message column as before. See File-backed responses in the mappings list.
  • The HTTP mappings list now shows a composite request body matcher — one built from the logical and, or or not operators — as a readable one-line summary in the Request body column, for example and (contains 'A', contains 'B') or not (equalTo 'X'), instead of the raw matcher JSON. The row's request-match hover tooltip (which already summarised the method, URL matcher, URL and query parameters) now also lists each request body matcher on its own Body: line. Simple (leaf) matchers such as equalTo and contains are shown exactly as before; this changes only how existing matchers are displayed — nothing about how requests are matched or how mappings are saved changes.
  • The HTTP Add/Edit mapping panel now shows a live, non-blocking warning beneath the URL match mode when you combine a full-URL match mode (equal to or matches regex) with one or more query parameter matchers — a combination that is almost always wrong, because those modes match the whole URL (including the query string) and conflict with the separate query parameter matchers. The warning explains the problem and recommends switching to a path-only mode (path equal to or path matches regex), and it appears and disappears as you change the URL mode or add and remove query parameter rows. Saving is not blocked.
  • The HTTP and gRPC Add/Edit mapping forms gained three improvements for working with a file-backed response. A Manage files link in the response body area opens the Static Files browser in a new browser tab, so you can manage the files a response is served from without leaving, or losing, the mapping you are editing; the link is always shown, whether the response is inline or already file-backed (previously the Static Files browser was reachable only from the HTTP menu). When the response is an image file (PNG, JPEG, GIF, SVG, WebP, BMP, ICO, APNG or AVIF) the form shows an inline preview of it above the Response from file note, so you can see at a glance which image a mapping serves without opening the file; the body field stays read-only for binary files as before, non-image binary files keep the read-only field with no preview, and this mirrors the inline image rendering already used in the Static Files browser preview pane. Clicking Switch to file now shows a Choose an existing file… dropdown beside the response body file name field, listing the files already in the scenario's __files directory (including nested responses/ files); selecting one fills the file name so you can point a response at an existing body file without typing the path, and typing the name by hand still works as before. See Editing a file-backed response in the UI.
  • The HTTP Mapping cleanup results now include a collapsible Per-endpoint detail view showing, per recorded endpoint, exactly what cleanup did to it (kept, consolidated, headers stripped, body match removed, URL pattern applied, response template added, or removed), plus a consolidation-groups breakdown of which recorded URLs collapsed into each surviving pattern. The /api/http/cleanup/execute response gains matching endpointDispositions and consolidationGroups fields.
  • The HTTP Add/Edit mapping editor's request body matcher dropdown now offers matches JSON schema, which was previously only usable by editing the mapping JSON directly — it matches when the request body is JSON that validates against the given JSON Schema. Enter the schema as the matcher value; it round-trips on save. The advanced JSON-only option (the JSON Schema schemaVersion dialect selector) remains available by editing the mapping JSON — see the JSON-only matchers reference.
  • The HTTP Add/Edit mapping editor's Request URL matcher dropdown now offers path template as a fifth URL match type, alongside equal to, matches regex, path equal to and path matches regex. Enter a URL path template with named variables such as /orders/{orderId}; a request path matches when it fits the template's shape regardless of the concrete variable values (so /orders/{orderId} matches /orders/123). It persists as the urlPathTemplate request field and round-trips on save. The matcher also appears in the editor's Available helpers panel under URL match types.
  • The HTTP Add/Edit mapping editor now also lets you edit request headers and response headers with per-header rows, using the same click-to-add-rows editing already offered for query parameters. A request header row carries a header name, a matcher dropdown (equal to, contains, matches regex, does not match regex, equal to date/time, before date/time and after date/time) and a value, because request headers are matched; a response header row has just a Name and a Value with no matcher dropdown, because response headers are static Name: value pairs the mock returns rather than request matchers. In both cases the free-text headers field is kept for bulk, copy-paste-friendly editing, and the rows and text area stay in two-way sync (most recent edit wins); click Add request header or Add response header to add a row and the trash button to remove one. The saved mapping JSON is unchanged (standard WireMock request and response headers), so existing mappings load back into both views and round-trip on save, no header-matching expressiveness is lost, and the gRPC and messaging editors are unchanged.
  • A file-backed request body matcher (one whose value is read from a bodyFileName file in __files) is now editable in the HTTP Add/Edit mapping editor, like a file-backed response: a Content from file note names the file, the request body field is pre-filled with its contents, and saving writes your edit back to the file (the matcher keeps its bodyFileName reference). A single file-backed text matcher also offers a Switch to inline button (mirroring the file-backed response control) that detaches the bodyFileName reference and keeps the loaded contents as an inline matcher value; on save the matcher is stored inline, with no __files reference and the file on disk left untouched. A binaryEqualTo matcher, or a mapping with more than one request body matcher, stays read-only, offers no Switch to inline, and is edited on disk. See Request body file: In the GUI.
  • The Available helpers panel is now also available on the gRPC mapping editor (gRPC » Add/Edit), as it already is on the HTTP and messaging editors. Expand the collapsible Available helpers card to browse, filter and copy the response-templating helpers and the request-body matchers that apply to a gRPC mapping, without leaving the editor.
  • HAR import now offers an opt-in Preserve full fidelity (all headers) mode. By default an imported stub stays flexible — it matches on method, URL and the request Content-Type, and its response carries only the status, body and Content-Type, with all other request and response headers dropped so the stub is not tied to the exact browser, session or environment that produced the recording. Tick the new Preserve full fidelity (all headers) checkbox next to Import Selected on the HAR import preview (off by default; a whole-import setting, not per-entry) to instead turn every recorded request header — including Content-Type — into an exact request matcher and emit every recorded response header (with the exact response Content-Type and charset) on the stub response. This produces a faithful reproduction of the recorded exchange but makes stubs strict and often single-use: headers such as Host, Cookie, Authorization and Content-Length tie the stub to a specific host, session, token or body, so leave it off for reusable stubs. HTTP/2 pseudo-headers (:method, :authority, :scheme, :path, :status) are automatically excluded. The same behaviour is available programmatically through an optional preserveAllHeaders parameter on the /http/management/importMappings and /http/management/importSelectedEntries REST endpoints. Request and response bodies import unchanged (base64-encoded bodies are decoded automatically), and the option affects HAR import only — OpenAPI, Swagger, RAML, Pact and WireMock ZIP imports are unaffected. See Preserve full fidelity (all headers).
  • HTTP recording gained two Advanced parameters options on the HTTP Record page for how bodies are stored, both off by default so existing recording workflows are unchanged. Keep valid-JSON response body inline keeps a recorded response body inline in the mapping (as a structured jsonBody) when it is valid JSON served with a text content type, instead of writing it to a separate __files/ body file, so a recorded JSON API produces self-contained mappings with no companion body files; non-JSON text bodies and binary bodies (including a binary content type whose bytes happen to parse as JSON) are still externalised to __files/ as before, and there is no size limit on inlining, so a very large JSON response is embedded directly in the mapping file. Externalise request body to file is the request-side counterpart of the response-side inline option, and the write-time counterpart of a file-backed request body matcher: it writes a recorded request body to a separate __files/ file, with the recorded mapping's request matcher carrying a bodyFileName reference (equalToJson, equalToXml, binaryEqualTo or equalTo according to the request's content type, a binary body written byte-for-byte) instead of an inline bodyPatterns value; empty and multipart request bodies are always kept inline, and an externalised request body loads, matches on replay, and is editable in the Add/Edit mapping editor exactly like a manually file-backed one. With Externalise request body to file off, recording produces byte-identical mappings. Set trafficparrot.http.recording.response.inlineJson or trafficparrot.http.recording.request.externaliseToFile in trafficparrot.properties to change the checkboxes' default states. See Keeping JSON response bodies inline and Externalising request bodies to files.
  • The URL contains filter on the HTTP import preview now supports glob wildcards — * for any sequence of characters and ? for any single character — in addition to plain substring matching. Plain text still matches any path that contains it; a pattern with a wildcard switches to an anchored, case-insensitive match against the request path (for example /api/*, *.json or /users/?). This filters which entries are shown for selection only and does not change how the imported stubs match at runtime (GitHub issue #68).
  • The admin request-journal endpoint (GET /api/http/requests) now accepts two optional query parameters for paging and filtering a large journal: limit bounds the response to the first N entries in journal order, and since returns only the requests recorded strictly after a given ISO-8601 timestamp (for example 2026-07-16T10:00:00Z); a negative limit or a malformed since is rejected with HTTP 400. Both are off by default, so a request with neither parameter still returns the whole journal unchanged, and the meta.total field continues to report the full count of everything recorded, independent of either parameter. See Page or filter the journal.

Documentation

  • Added a Recording HTTPS via the browser proxy section to the User Guide. It explains that Traffic Parrot records HTTPS by acting as a browser (forward) proxy: point your browser or application's proxy setting at Traffic Parrot, import the Traffic Parrot CA certificate (certificates/virtualservice-CA-keystore.p12, password trafficparrot, PKCS12) into the client's trust store, and Traffic Parrot mints a per-host certificate on the fly from the TLS SNI so it can decrypt and record traffic to any host. The Recording HTTP section links across to it.

Changes

  • The default in-memory request-journal retention has been lowered from 10000 to 1000 entries. The trafficparrot.virtualservice.maxRequestJournalEntries property bounds all request journals — HTTP, gRPC and JMS/IBM® MQ — so by default each now keeps the last 1000 requests (older entries age out as new requests arrive) instead of 10000. This keeps the default -Xmx128m install memory-safe under a sustained first-run/evaluation workload, where the oversized journal could otherwise exhaust the heap. If you need a longer request-log history, raise maxRequestJournalEntries in your trafficparrot.properties; the JVM heap setting (-Xmx128m) is unchanged.