Maya Workflow Kit
The whole Maya workflow in Blender, in one install. Free and open source, Blender 4.5 – 5.2.
翻译准备中
🚀 Feather Wand - JMeter Agent for performance engineers w/ Codex and Claude Code integration
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗AI-powered assistant for Apache JMeter
Features · Install · Configure · Commands · Changelog
🪄 Why "Feather Wand"? My kids named it after a Bluey episode, where a simple feather becomes a magical wand that turns the ordinary into something special. That's exactly what this plugin does for your JMeter workflow.
🤩 Show off your project in this Feather Wand showcase.
🔒 Anonymous usage telemetry: starting with v3.8.6, Feather Wand sends one anonymous ping per day so we can see how many people use it and which features matter. No prompts, keys, or test plan content ever leave your machine. See what is collected and how to turn it off.
Agent Mode builds a 20-user load test with an assertion and a Summary Report from a single prompt (sped up 3x).
| 🤖 Multi-Model Chat | Talk to API-backed Claude, OpenAI, Google Gemini, DeepSeek, Ollama, Grok (xAI), OpenRouter, Meta Muse, or AWS Bedrock models, or use a CLI-managed ChatGPT/Codex, Claude Code, or OpenCode account. |
| ⚡ Real-Time Streaming | Watch supported API responses appear token-by-token with a Stop button to cancel anytime; CLI-backed requests return one completed answer and are cancellable too. |
| 🖥️ AI CLI Terminal | Run Claude Code, OpenAI Codex, OpenCode, Antigravity, or Grok CLI directly in JMeter. This interactive terminal is separate from CLI-backed chat providers. |
| 🎯 Extract this | Highlight a value in a View Results Tree response, right-click and pick Extract this (Feather Wand). Feather Wand builds JSON, CSS selector, regex and boundary extractors, runs each one through JMeter's own extractor code against the real response, and offers only the ones that return exactly that value. Pick one, name the variable and it lands under the sampler that produced the response, optionally replacing later hard-coded copies with ${var}. Works without an API key. |
| 🧹 Smart Refactoring | Right-click in the JSR223 editor to refactor, format, or inject functions with AI. |
| 🔍 Context-Aware Commands | @this, @testplan, @optimize, @lint, @wrap, @load, @fix, @code, @usage, each tailored to your test plan. |
| 📈 Workload Designer | Describe sample or message rates, iterations, users and generator hosts. Preview the sampler mix and closed-model load shape, apply JMeter properties and copy a ready CLI command without an AI call. |
| 🔔 Audio Chime | Optional sound notification when AI finishes responding. |
| 🐾 |
Requirements: Apache JMeter 5.6.3+ running on Java 17 or newer. Feather Wand is compiled for Java 17 (class version 61), although JMeter itself runs on Java 8+. On Java 8 or 11, JMeter cannot load the plugin and its toolbar button will not appear.
Check with java -version or in JMeter's jmeter.log.
1. Install JMeter Plugins Manager → https://jmeter-plugins.org/
2. Restart JMeter
3. Open Plugins Manager → Available Plugins
4. Search for "feather wand"
5. Select it → Apply Changes and Restart JMeter
1. Download the latest JAR from Releases
2. Drop it into JMeter's lib/ext directory
3. Paste jmeter-ai-quickstart.properties into JMeter's bin/user.properties
4. Uncomment one provider (Claude, OpenAI, Gemini, Ollama or Apple on-device), add its key (Ollama and Apple on-device need none), then restart JMeter
See Releases for the latest JAR.
First time? Paste jmeter-ai-quickstart.properties into JMeter's bin/user.properties, uncomment one provider and add its key (Ollama and Apple on-device need none):
You can connect a provider from the Feather Wand panel's ⋯ menu > Connect a provider and use it immediately in Chat and Agent Mode, without restarting JMeter. Smart routing and Apple on-device can also be configured in this dialog. The manual properties below remain documented. The JSR223 refactor menu picks up a new provider after a JMeter restart.
jmeter.ai.service.type=anthropic
anthropic.api.key=YOUR_ANTHROPIC_API_KEY
That is all Feather Wand needs; every other setting has a default. jmeter-ai-sample.properties is the full reference: copy only the settings you want to change from it into user.properties (or jmeter.properties). The tables below list each setting and its default.
| Property | Description | Default |
|---|---|---|
jmeter.ai.streaming.enabled | Stream AI responses token-by-token | true |
jmeter.ai.response.chime | Play a chime when AI finishes | false |
jmeter.ai.refactoring.enabled | Enable JSR223 editor AI refactoring | true |
jmeter.ai.extract.enabled | Add Extract this to the right-click menu of View Results Tree responses | true |
jmeter.ai.service.type | Default AI service for refactoring (anthropic, openai, ollama, deepseek, apple, and others) | anthropic |
jmeter.ai.cli.max.history.size | Conversation entries replayed with each one-shot Codex/Claude Code/OpenCode chat request | 10 |
jmeter.ai.dev.menu | Show the AI Dev tool items under the Run menu | false |
| Property | Description | Default |
|---|---|---|
anthropic.api.key | Claude API key | Required (optional behind a gateway that authenticates via headers) |
anthropic.base.url | Endpoint speaking the Anthropic /v1/messages format; point this at a corporate gateway | https://api.anthropic.com |
anthropic.extra.headers | Extra request headers, ;-separated name=value pairs | (empty) |
anthropic.max.retries | Automatic SDK retries on 429/5xx; set 0 behind a quota-limited gateway | 2 |
anthropic.models | Explicit model list (comma-separated) for gateways that don't expose model listing | (empty) |
claude.default.model | Default model | claude-sonnet-4-6 |
claude.temperature | Temperature (0.0-1.0) | 0.5 |
claude.max.tokens | Max response tokens | 1024 |
claude.max.history.size | Conversation history size | 10 |
claude.system.prompt | System prompt | See sample file |
anthropic.log.level | Logging (info/debug) | (empty) |
| Property | Description | Default |
|---|---|---|
openai.api.key | OpenAI API key | Required (optional behind a gateway that authenticates via headers) |
openai.base.url | Endpoint speaking the OpenAI /v1/chat/completions format; point this at a corporate gateway | https://api.openai.com/v1 |
openai.extra.headers | Extra request headers, ;-separated name=value pairs | (empty) |
openai.models | Explicit model list (comma-separated) for gateways that don't expose GET /v1/models | (empty) |
openai.max.retries | Automatic SDK retries on 429/5xx; set 0 behind a quota-limited gateway | 2 |
openai.default.model | Default model | gpt-4o |
openai.temperature | Temperature (0.0-1.0) | 0.5 |
openai.max.tokens | Max response tokens | 1024 |
openai.max.history.size | Conversation history size | 10 |
openai.system.prompt | System prompt | See sample file |
openai.log.level | Logging (INFO/DEBUG) | (empty) |
Apple on-device is available in the chat model picker and for JSR223 Refactor. The Agent option is disabled while Apple on-device is selected, and Apple on-device is not available in Record Mode. It uses Apple's FoundationModels framework locally, so no API key or network inference service is required. The model has an approximately 4K-token context window.
Requirements: macOS 26 or later, Apple silicon, and Apple Intelligence enabled in System Settings. The model appears in the picker only when the helper reports it is available.
| Property | Description | Default |
|---|---|---|
apple.enabled | Enable Apple on-device model discovery | true |
apple.fm.helper.path | Override the bundled native helper executable path | (bundled helper) |
apple.max.history.size | Conversation entries replayed with each request | 6 |
apple.system.prompt | Instructions sent separately from the conversation | JMeter default |
apple.temperature | Optional generation temperature | (system default) |
apple.max.tokens | Optional maximum response tokens | (system default) |
jmeter.ai.service.type | Select Apple on-device for JSR223 Refactor | Set to apple |
The bundled helper is for macOS arm64. See native/apple-fm/README.md to rebuild it.
Uses the authentication managed by your local Codex CLI. It normally uses ChatGPT, but an API key configured inside Codex is also recognized. Feather Wand does not require or inspect openai.api.key for this provider. See Using Feather Wand with ChatGPT / Codex.
| Property | Description | Default |
|---|---|---|
jmeter.ai.codex.enabled | Show the ChatGPT/Codex status and models in the picker after restart | false |
jmeter.ai.codex.executable | Full path to the codex binary | (PATH auto-detect) |
jmeter.ai.codex.timeout.seconds | Hard timeout for one codex exec request | 120 |
jmeter.ai.codex.login.timeout.seconds | Timeout for the CLI-owned browser login flow | 300 |
jmeter.ai.codex.models | Comma-separated raw model ids added beside codex:default | (empty = CLI default only) |
jmeter.ai.codex.sandbox | Sandbox passed to codex exec (read-only, workspace-write, or danger-full-access) | read-only |
Uses the authentication managed by your local Claude Code CLI, including a Claude subscription, Anthropic Console key, or supported cloud credentials. Feather Wand does not require or inspect anthropic.api.key for this provider. See Using Feather Wand with Claude Code.
| Property | Description | Default |
|---|---|---|
jmeter.ai.claudecode.provider.enabled | Show the Claude Code status and models in the picker after restart | false |
jmeter.ai.claudecode.executable | Full path to the claude binary | (PATH auto-detect) |
jmeter.ai.claudecode.timeout.seconds | Hard timeout for one claude -p request | 120 |
jmeter.ai.claudecode.login.timeout.seconds | Timeout for the CLI-owned browser login flow | 300 |
jmeter.ai.claudecode.models | Comma-separated raw model ids added beside claude-code:default | (empty = CLI default only) |
OpenCode's free models work without signing in. Other providers use credentials managed by OpenCode. Feather Wand does not read or store those credentials.
| Property | Description | Default |
|---|---|---|
jmeter.ai.opencode.enabled | Enable the OpenCode Chat and Agent Mode provider | false |
jmeter.ai.opencode.executable | Full path to the opencode binary | (PATH auto-detect) |
jmeter.ai.opencode.timeout.seconds | Hard timeout for one opencode run request | 180 |
jmeter.ai.opencode.models | Comma-separated raw provider/model ids added beside opencode:default | (empty = discovered models plus CLI default) |
Feather Wand runs OpenCode in an empty temporary directory. File writes, shell, and web tools are denied, as are reads outside that directory. Feather Wand's own JMeter tools do the plan editing. To enable OpenCode without editing properties, open Connect a provider, choose OpenCode, click Test connection, then Save and connect. It takes effect immediately without restarting JMeter. As a manual alternative, set jmeter.ai.opencode.enabled=true and optionally jmeter.ai.opencode.executable=/path/to/opencode in user.properties.
This Chat and Agent Mode provider is separate from
jmeter.ai.terminal.opencode.*, which controls the interactive terminal tab.
These Chat and Agent Mode providers are not the
jmeter.ai.terminal.*integration, which controls the separate interactive terminal tab.
| Property | Description | Default |
|---|---|---|
google.api.key | Google AI API key | Required |
google.default.model | Default model | gemini-3.5-flash |
google.temperature | Temperature (0.0-1.0) | 0.7 |
google.max.tokens | Max response tokens | 4096 |
google.max.history.size | Conversation history size | 10 |
google.system.prompt | System prompt | See sample file |
| Property | Description | Default |
|---|---|---|
ollama.host | Server host | http://localhost |
ollama.port | Server port | 11434 |
ollama.default.model | Default model | deepseek-r1:1.5b |
ollama.temperature | Temperature (0.0-1.0) | 0.5 |
ollama.max.history.size | Conversation history size | 10 |
ollama.thinking.mode | Extended thinking (ENABLED/DISABLED) | DISABLED |
ollama.thinking.level | Thinking depth (LOW/MEDIUM/HIGH) | MEDIUM |
ollama.request.timeout.seconds | Request timeout | 120 |
ollama.system.prompt | System prompt | See sample file |
⚠️ If
ollama.thinking.mode=ENABLED, raiseollama.request.timeout.secondsto at least300.
| Property | Description | Default |
|---|---|---|
grok.api.key | xAI API key | Required |
grok.base.url | Base URL endpoint | https://api.x.ai/v1 |
grok.default.model | Default model | grok-4.5 |
grok.temperature | Temperature (0.0-1.0) | 0.7 |
grok.max.tokens | Max response tokens | 4096 |
grok.max.history.size | Conversation history size | 10 |
grok.system.prompt | System prompt | See sample file |
OpenRouter uses its OpenAI-compatible Chat Completions API for Chat and Agent Mode. The key is shared with OpenRouter Decisions. The model picker requests only models advertising tool support, so every listed model can be used with Agent Mode.
| Property | Description | Default |
|---|---|---|
openrouter.api.key | OpenRouter API key, shared with OpenRouter Decisions | Required |
openrouter.base.url | API base URL | https://openrouter.ai/api/v1 |
openrouter.default.model | Default model | anthropic/claude-sonnet-5.5 |
openrouter.temperature | Temperature | 0.7 |
openrouter.max.tokens | Maximum response tokens | 4096 |
openrouter.max.history.size | Conversation history size | 10 |
openrouter.system.prompt | System prompt | JMeter default |
openrouter.models | Comma-separated featured model IDs, shown first when available | See sample file |
The model list is fetched from OpenRouter with supported_parameters=tools. Featured IDs missing from the live tool-capable catalog are skipped. OpenRouter attribution headers identify requests as JMeter and Feather Wand.
| Property | Description | Default |
|---|---|---|
deepseek.api.key | DeepSeek API key | Required |
deepseek.api.format | Wire format: openai or anthropic | openai |
deepseek.base.url | Base URL endpoint; use https://api.deepseek.com/anthropic with the anthropic format | https://api.deepseek.com |
deepseek.default.model | Default model (deepseek-chat or deepseek-reasoner) | deepseek-chat |
deepseek.temperature | Temperature (0.0-1.0) | 0.7 |
deepseek.max.tokens | Max response tokens | 4096 |
deepseek.max.history.size | Conversation history size | 10 |
deepseek.system.prompt | System prompt | See sample file |
| Property | Description | Default |
|---|---|---|
meta.api.key | Meta AI API key | Required |
meta.base.url | Base URL endpoint | https://api.meta.ai/v1 |
meta.default.model | Default model | muse-spark-1.1 |
meta.temperature | Temperature (0.0-1.0) | 0.7 |
meta.max.tokens | Max response tokens | 4096 |
meta.max.history.size | Conversation history size | 10 |
meta.system.prompt | System prompt | See sample file |
Feather Wand uses Bedrock's standardized Converse and ConverseStream APIs for chat requests. This provides one request and streaming format across compatible Anthropic, Meta, Mistral, MiniMax, Amazon, and other Bedrock text-generation models instead of requiring a provider-specific payload formatter.
| Property | Description | Default |
|---|---|---|
bedrock.api.key | Bedrock API key (bearer token) | (empty) |
bedrock.aws.access.key | IAM access key; used when no Bedrock API key is set | (empty) |
bedrock.aws.secret.key | IAM secret key | (empty) |
bedrock.aws.region | AWS Region used for discovery and inference | us-east-1 |
bedrock.default.model | Default Bedrock model ID | anthropic.claude-3-5-sonnet-20241022-v2:0 |
bedrock.model.providers | Comma-separated provider filter | Anthropic |
bedrock.temperature | Temperature | 0.5 |
bedrock.max.tokens | Maximum response tokens | 4096 |
bedrock.max.history.size | Conversation history size | 10 |
bedrock.system.prompt | System prompt | See sample file |
Authentication priority: Bedrock API key, IAM access key and secret key, then the AWS default credential chain. Do not commit credentials to a properties file or source repository.
Model discovery: The selector includes active, text-capable foundation models and active system-defined inference profiles matching bedrock.model.providers. Inference profiles are checked for agreement, authorization, entitlement, and regional availability before they are shown. Models such as image, embedding, reranking, and speech models are excluded from the chat selector.
Anthropic access: Anthropic models may require the Bedrock First-Time Use form, AWS Marketplace permissions, a valid payment method, and an active model agreement. If get-foundation-model-availability reports agreementAvailability=NOT_AVAILABLE, the model can be visible in AWS discovery but cannot be invoked by the account yet.
Compatibility: Chat models must support Bedrock Converse/ConverseStream. Models that only provide embeddings, image generation, audio, video, or other non-chat capabilities require a separate feature flow and cannot be used in this chat panel.
Restart JMeter after changing Bedrock properties or installing a new plugin JAR.
See the Bedrock Converse API documentation and model access documentation for account setup and model availability details.
If your organization fronts OpenAI or Anthropic with an internal gateway (LiteLLM, Azure API Management, Apigee, Kong, Portkey, Bedrock Access Gateway, ...), point Feather Wand at it instead of the vendor host. Any gateway that speaks the OpenAI /v1/chat/completions or Anthropic /v1/messages wire format works, with no gateway-specific setup:
# OpenAI-compatible gateway
openai.base.url=https://llm-gateway.corp.example.com/v1
openai.extra.headers=X-Gateway-Key=<your-gateway-key>;X-Team=perf
openai.models=corp-gpt-4o,corp-gpt-4o-mini
# Anthropic-compatible gateway
anthropic.base.url=https://llm-gateway.corp.example.com
anthropic.extra.headers=X-Gateway-Key=<your-gateway-key>;X-Team=perf
anthropic.models=corp-claude-sonnet,corp-claude-haiku
*.api.key when the gateway expects the usual vendor auth header (many issue a "virtual key"). When it authenticates purely through its own header, set *.extra.headers and leave *.api.key unset: the gateway configuration alone is enough for the chat panel and the JSR223 refactoring menu. Values may contain = (only the first = separates name from value). Header names are gateway-specific, so take them from your gateway's own documentation.*.models to list them explicitly and Feather Wand skips the discovery call entirely. With a custom base URL the gpt prefix requirement is also lifted, so ids like azure/gpt-4o survive.https://. A plaintext http:// base URL still works (useful for a loopback or in-cluster endpoint) but logs a warning, since keys and prompts would travel unencrypted.-Djavax.net.ssl.trustStore=...).First confirm the gateway works outside JMeter, using the same URL, header and model id you put in the properties:
curl -sS https://llm-gateway.corp.example.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "X-Gateway-Key: $GATEWAY_KEY" \
-d '{"model":"corp-gpt-4o","messages":[{"role":"user","content":"ping"}]}'
| Symptom | Likely cause |
|---|---|
Requests still reach api.openai.com / api.anthropic.com | Properties aren't loaded. They must be in jmeter.properties or user.properties, and JMeter must be restarted. jmeter.log logs the effective endpoint: Initialized OpenAI service with baseUrl: ... |
| No gateway models in the picker | The gateway doesn't serve a model-listing endpoint, or returns ids the filter drops. Set openai.models / anthropic.models explicitly |
Error adding OpenAI models / Error loading Anthropic models in jmeter.log | The discovery call failed: wrong base URL path, untrusted certificate, or missing auth header |
| 401 / 403 on chat | The gateway wants its own header (*.extra.headers), or expects a virtual key in *.api.key rather than your vendor key |
| 404 on chat | Base URL path is off. OpenAI-compatible URLs normally end in /v1, Anthropic-compatible ones do not |
PKIX path building failed | The gateway's certificate chains to an internal CA; start JMeter with -Djavax.net.ssl.trustStore=/path/to/corp-truststore.jks |
uses plaintext HTTP warning | The base URL is http://; switch to https:// unless it's a loopback endpoint |
429 / RateLimitException / "token quota exceeded" | Usually the gateway's token budget is smaller than one Agent Mode request (a 429 can also be a request-rate or concurrency throttle; the panel shows the gateway's own message); see Token quotas and 429 errors |
Agent Mode is token-hungry by design: every reason/act turn re-sends the system prompt (including the JMeter element hierarchy), all 21 tool definitions and the full conversation so far, and even a trivial request takes 2-4 turns (the agent reads the tree first). That fixed overhead is several thousand tokens per turn, so a gateway tier with a small budget (for example 10K tokens per 5 minutes) is exhausted on the first or second turn, whatever the prompt says. Some gateways also count max_tokens against the quota before the request runs.
What helps, roughly in order of impact:
openai.max.retries=0 (or anthropic.max.retries=0). The SDK otherwise retries a 429 twice with backoff, tripling the tokens charged for one click. The chat panel now shows the gateway's own 429 message instead of retrying as plain chat.jmeter.ai.agent.max.tokens (e.g. 1024) if your gateway reserves max_tokens up front.Agent token usage [...] prompt=... completion=... | run total: ... in jmeter.log, so you can compare a run against your quota before asking for a larger tier.If the quota is fixed at a few thousand tokens per window, Agent Mode will not fit; ask the gateway team for a larger allocation for the Feather Wand client.
If that doesn't settle it, paste the following into your own LLM (redact secrets first):
I'm using the Feather Wand plugin for Apache JMeter and routing it through my
company's LLM gateway instead of the vendor endpoint. It isn't working.
My Feather Wand properties (secrets redacted, header NAMES kept):
openai.base.url=...
openai.extra.headers=...
openai.models=...
openai.default.model=...
openai.api.key=<set | not set>
(or the anthropic.* equivalents)
What JMeter shows me: <error text from the Feather Wand chat panel>
Relevant jmeter.log lines: <lines mentioning baseUrl, models, or an exception>
How my gateway works: it speaks the <OpenAI /v1/chat/completions | Anthropic
/v1/messages> wire format, authenticates via <bearer API key | custom header
named X-...>, and <does | does not> expose a model-listing endpoint. Its
documentation says: <paste the relevant part>
Tell me which of these is wrong and give me the corrected property lines:
the base URL (including whether a /v1 suffix belongs there), the auth header
name and whether the key should go in openai.api.key instead, the model ids,
or the TLS trust configuration.
Restart JMeter after changing gateway properties.
| Property | Description | Default |
|---|---|---|
jmeter.ai.terminal.claudecode.enabled | Enable the embedded terminal | true |
jmeter.ai.terminal.claudecode.path | Full path to claude binary | (auto-detect) |
jmeter.ai.terminal.copilot.enabled | Enable GitHub Copilot CLI | false |
jmeter.ai.terminal.copilot.path | Full path to copilot binary | (auto-detect) |
jmeter.ai.terminal.antigravity.enabled | Enable Antigravity CLI | false |
jmeter.ai.terminal.grok.enabled | Enable Grok CLI | false |
jmeter.ai.terminal.codex.enabled | Enable OpenAI Codex CLI | false |
jmeter.ai.terminal.opencode.enabled | Enable OpenCode CLI | false |
jmeter.ai.terminal.<cli>.prompt | Starter prompt for that CLI (claudecode, copilot, antigravity, grok, codex, opencode) | See sample file |
jmeter.ai.terminal.font.family | Terminal font family (e.g. Consolas, Noto Sans Mono CJK SC) | (auto-detect) |
jmeter.ai.terminal.font.size | Terminal font size | 16.0 |
jmeter.ai.terminal.font.cjk.fallback | Fall back to a CJK-capable font when the selected font cannot display CJK | true |
The terminal uses the font family you configure. If jmeter.ai.terminal.font.cjk.fallback=true and the selected font cannot display CJK characters, the plugin automatically picks the best CJK-capable font installed on your system (NSimSun, SimSun, MS Gothic, Microsoft YaHei, Malgun Gothic, etc.).
| Use case | Recommended configuration |
|---|---|
| English / Latin only; keep your Western monospaced font | jmeter.ai.terminal.font.family=Consolasjmeter.ai.terminal.font.size=16.0jmeter.ai.terminal.font.cjk.fallback=false |
| CJK support; let the plugin pick the best available font | jmeter.ai.terminal.font.size=16.0jmeter.ai.terminal.font.cjk.fallback=true |
| CJK support with a specific installed font | jmeter.ai.terminal.font.family=Noto Sans Mono CJK SCjmeter.ai.terminal.font.size=16.0jmeter.ai.terminal.font.cjk.fallback=false |
⚠️ When
cjk.fallback=truewith a non-CJK font likeConsolas, the configured family is overridden becauseConsolashas no CJK glyphs. If you want to forceConsolas, setcjk.fallback=false; CJK will then render as boxes.
Prerequisite CLIs
| CLI | Binary | Install Guide |
|---|---|---|
| Claude Code | claude | Docs |
| OpenAI Codex | codex | Repo |
| GitHub Copilot | copilot | Docs |
| OpenCode | opencode | Repo |
| Antigravity | agy | Site |
| Grok CLI | grok | Console |
Each service supports its own *.system.prompt property; tweak them in your properties file to focus the AI on specific JMeter topics or team conventions.
Used by find_correlation_candidates / apply_correlation, which replay the plan with 1 thread and 1 iteration and look for dynamic values.
| Property | Description | Default |
|---|---|---|
jmeter.ai.correlation.known_tokens | Token names to detect in cookies, JSON keys, form fields, and URL parameters (comma-separated, case-insensitive) | JSESSIONID,jsessionid,sessionId,_sourcePage,__fp,_token,csrf,access_token,id_token,code,state,nonce |
jmeter.ai.correlation.custom.<n> | Custom pattern as name::regex, numbered 1 to 50 | uuid, jwt, hex in the sample file |
jmeter.ai.correlation.exclude_values | Static values that are never correlated (comma-separated) | true,false,null,undefined,application/json,text/html,utf-8,en-us |
jmeter.ai.correlation.min_value_length | Minimum length for a value to count as dynamic | 3 |
jmeter.ai.correlation.min_entropy | Minimum entropy (0.0-8.0) for auto-detected values; 0 disables entropy detection | 2.8 |
These are read as Java system properties, not from jmeter.properties / user.properties. Set them in JMeter's system.properties or pass them with -D (for example JVM_ARGS="-Djmeter.ai.session.dir=/data/fw-sessions" ./jmeter).
| Property | Description | Default |
|---|---|---|
jmeter.ai.session.dir | Folder for saved conversations | ~/.jmeter-ai/sessions |
jmeter.ai.preferences.file | Model picker pins, recents, and custom model ids | ~/.jmeter-ai/model-selector.json |
jmeter.ai.record.mcp.command | Command that starts the Playwright MCP server for Browser Recording | npx -y @playwright/mcp@latest |
Feather Wand can use the account already managed by your local Codex CLI. It normally uses a ChatGPT subscription, but a Codex CLI API-key session is recognized too. This is a separate provider from the OpenAI API integration and does not require openai.api.key in Feather Wand.
IMPORTANT: Check your provider's current terms before using a subscription.
OpenAI, Anthropic, and other LLM or CLI providers may change their terms of service, acceptable-use policies, subscription eligibility, rate limits, features, or billing at any time. Before enabling a subscription-backed provider in Feather Wand, review the current terms for your account and confirm that your plan permits this type of use.
You are responsible for complying with the provider's terms and for monitoring usage, limits, and charges. Feather Wand is an independent integration and is not affiliated with or endorsed by these providers. It does not guarantee continued access or compatibility. To the extent permitted by law, the project and its maintainers are not responsible for provider-side changes, account restrictions or suspension, unexpected charges, or other consequences resulting from your use. Double-check the current terms before proceeding.
npm install -g @openai/codex (see the Codex repo).jmeter.ai.codex.enabled=true to user.properties and restart JMeter.codex login) and complete the browser flow owned by Codex.default to let Codex choose its configured model, select a model from jmeter.ai.codex.models, or use Custom model….Statuses include ✓ Signed in, Signed in using API key, Not signed in, Codex CLI not installed, and Unable to determine status. Refresh clears cached executable discovery, re-runs codex login status, and adds configured models after a CLI is installed without reopening JMeter.
The same flow works with the authentication managed by Claude Code: a Claude Pro/Max subscription, an Anthropic Console key, or supported cloud credentials. This provider is separate from the Anthropic API integration and does not require anthropic.api.key in Feather Wand.
npm install -g @anthropic-ai/claude-code (see the Claude Code docs).jmeter.ai.claudecode.provider.enabled=true to user.properties and restart JMeter.claude auth login --claudeai) and complete the CLI-owned browser flow.default, a model from jmeter.ai.claudecode.models, or a model entered through Custom model….The footer offers the same Sign in, Sign out, Refresh, and Custom model… actions and reports signed-in, API-key, signed-out, missing-CLI, or unknown states.
OpenCode's free models work without signing in. Other models use providers added to the local OpenCode CLI. Feather Wand does not read or store provider credentials, and adding a provider is done by you in the AI CLI terminal tab.
opencode auth login yourself in the AI CLI terminal tab. Feather Wand never runs the interactive login command.default, discovered provider/model ids, configured ids from jmeter.ai.opencode.models, and Custom model….As a manual alternative, set jmeter.ai.opencode.enabled=true and optionally jmeter.ai.opencode.executable=/path/to/opencode in user.properties. The provider status row offers Refresh and Custom model…, but no Sign in or Sign out buttons. Free models can be used without adding a provider.
Each request runs in a new empty temporary directory with --pure and a permission configuration that allows read-only discovery tools while denying file writes, shell, web tools, and reads outside that directory. The temporary directory is removed after the request. In Agent Mode, Feather Wand's own JMeter tools perform plan edits; OpenCode itself cannot write to the plan files.
| Provider | Authentication commands | Prompt command |
|---|---|---|
| ChatGPT / Codex | codex login status, codex login, codex logout | codex exec with the prompt on UTF-8 stdin, the configured sandbox, optional --model, and a clean last-message output file |
| Claude Code | claude auth status --json, claude auth login --claudeai, claude auth logout | claude -p --output-format text with the prompt on UTF-8 stdin and optional --model |
| OpenCode | opencode --version; provider sign-in is done manually in the AI CLI terminal | opencode run --pure --format json --dir <empty temp dir> with the prompt on UTF-8 stdin, OPENCODE_CONFIG_CONTENT, and optional -m provider/model |
Commands are launched directly with ProcessBuilder, never through a shell, and prompt text is never interpolated into the command line. Standard output and error are drained concurrently off the Swing event thread. Each request has a hard timeout; timeout or Stop terminates the CLI process and its descendants.
CLI-backed chat is one-shot: the most recent jmeter.ai.cli.max.history.size conversation entries are flattened with the JMeter system prompt and replayed on every request. The completed answer arrives as one chunk rather than token-by-token, even when jmeter.ai.streaming.enabled=true.
Feather Wand checks CLI availability and provides the Codex and Claude Code login/logout actions, but never reads, copies, logs, or deletes CLI credential files or tokens. OpenCode provider sign-in is performed manually in its terminal tab.
| Mode | Credential source | Billing/account |
|---|---|---|
OpenAI API (openai:*) | openai.api.key read by Feather Wand | OpenAI API usage |
ChatGPT / Codex (codex:*) | Session managed entirely by the Codex CLI | Whatever account Codex reports (ChatGPT or API key) |
| Anthropic API (unprefixed Claude models) | anthropic.api.key read by Feather Wand | Anthropic API usage |
Claude Code (claude-code:*) | Session managed entirely by the Claude Code CLI | Whatever account Claude Code reports (subscription, API key, or cloud) |
OpenCode (opencode:*) | Free models or provider credentials managed by the OpenCode CLI | OpenCode's free tier or the account configured in OpenCode |
Switching providers never rewrites or removes the other providers' settings.
Codex and Claude Code publish no model list, so the picker offers codex:default / claude-code:default (let the CLI decide) plus raw ids from jmeter.ai.codex.models / jmeter.ai.claudecode.models. OpenCode discovery runs opencode models, and the picker merges those ids with opencode:default and any ids in jmeter.ai.opencode.models. For an id that is not listed, click Custom model…, type the raw id, and it is selected immediately, with no properties edit or restart. Custom ids are remembered in ~/.jmeter-ai/model-selector.json and passed to the selected CLI; an unsupported id therefore returns a CLI error instead of silently falling back. API-backed providers continue to list models from their provider APIs.
All three CLI providers participate in the same 18-tool Agent Mode loop. Because the CLIs expose no native tool-calling API to Feather Wand, each prompt contains the available tool schemas and requests one JSON object with either tool_calls or a final answer. Tool results are replayed into the next one-shot CLI request; non-JSON output safely becomes the final plain-text answer. Existing iteration limits and destructive-tool confirmations still apply.
The Codex child process uses jmeter.ai.codex.sandbox (read-only by default); Claude Code follows its own CLI permissions and configuration; OpenCode runs with the restricted temporary-directory configuration above. Feather Wand's Thinking and effort controls do not override the CLIs' reasoning budgets; each CLI owns that behavior.
The redesigned chat panel uses shared spacing, typography, button, and theme tokens so it follows JMeter's active light/dark look-and-feel, live theme changes, and UI scaling. No additional property is required.
Enter sends and Shift+Enter inserts a newline. IME composition is tracked so Enter can confirm Chinese, Japanese, or Korean input before sending.~/.jmeter-ai/model-selector.json and stay synchronized across both star controls.Type any of these directly in the chat box. All commands are context-aware and work with the currently selected test-plan element.
| Command | What it does | Example |
|---|---|---|
@this | Describe the selected element and suggest best practices. | How do I configure @this? |
@testplan | Send the entire test plan tree context to the AI (e.g. to find all target URLs). | @testplan which URL is under test? |
@optimize | Analyze the selected element and suggest performance tweaks. | @optimize or optimize this sampler |
@lint | Auto-rename elements for consistency. Undo/redo supported. | @lint rename elements in PascalCase |
@wrap | Group HTTP samplers under Transaction Controllers. | @wrap (select a Thread Group first) |
@code | Extract the last AI code block into the JSR223 editor. | @code |
@usage | Show token-usage stats and recent conversation history. | @usage |
@sla | Add SLA assertions (Duration Assertion and response-code Response Assertion) under the selected element. No AI call. | @sla p95 under 800 ms and no 5xx |
@load | Design sample rate, threads, ramp-up and generator setup from a target. No AI call. | @load 200 rps, 10 min ramp-up, 1 hour soak |
@fix | Run the plan with 1 user and fix failures until it passes. | @fix |
@prompts | Browse and insert prompts from the prompt library. | @prompts analyze |
Type @prompts in the chat input to open the prompt picker. It ships with six built-in starters (Analyze results, Review plan vs best practices, Explain errors in jmeter.log, Suggest assertions & timers, Find correlation candidates, Recording brief) and grows with your own prompts.
Enter puts the prompt text into the input box so you can edit it (fill in the [bracketed] placeholders, attach files) before sending.[file name] references).Del deletes one of your prompts (confirmed), F2 edits/renames it. Built-ins are read-only. Pressing F2 on one offers Save as copy so you can adapt it.User prompts live in ~/.jmeter-ai/prompts.json (unencrypted, so do not save credentials; override the path with jmeter.ai.prompts.file).
@lint Tips@lint rename based on the URL.@sla Details@sla reads the SLA from plain English, without calling the AI, and adds the matching assertions under the selected sampler, controller, Thread Group or Test Plan, so they apply to every sampler in that scope.
p95, p99, avg), the reply explains that JMeter assertions check each sample, so any single sample over the limit fails. That is stricter than a p95 target; read the percentile itself from the HTML dashboard or Aggregate Report.@load Details@load opens a local workload designer. It parses the command without an AI call or server connection, previews the thread ramp and steady hold, and applies reviewed values to an enabled Thread Group.
Open it from the toolbar button or Run > Workload Designer..., or type @load in chat.
rps, tps, qps, requests per second, queries per second, transactions per second, msg/s and messages per second; iterations or journeys per second, minute or hour; concurrent users or threads; response time, think time, ramp-up, hold duration, generator hosts and maximum threads per generator. Thousands separators and k/m suffixes are supported. Message targets also ask for messages per sample.threads ≈ samples/s × (response time + think time) × 1.5. Iteration rates are multiplied by the number of enabled samplers in one iteration. The iteration estimate counts each enabled sampler once; If, Loop, Throughput and Transaction (parent sample) controllers change the actual number. A message target is converted to samples per second by dividing by messages per sample. User targets keep the requested thread count and can optionally be capped at a sample rate.-R. With no hosts, the command uses agent1..N placeholders and the dialog explains how to replace them or configure remote_hosts. The per-generator capacity warning compares the required generator count with the selected count. Distributed notes cover CSV file paths, RMI SSL, consistent JMeter/Java/plugin versions and the per-generator scope of -G properties.remote_hosts: when remote_hosts in jmeter.properties contains a valid non-local host list, the dialog prefills Generator hosts if its count matches the command. Invalid hosts, local-only lists and count mismatches are ignored.@wrap Details@wrap uses pattern matching (not AI) to group related HTTP samplers under Transaction Controllers, preserving child elements and hierarchy. Great for imported or recorded plans.
Agent Mode lets the AI autonomously edit your live JMeter test plan through a tool-calling loop. Instead of just chatting about what you should do, the agent reads the tree, reasons about needed changes, calls tools to mutate elements, verifies the results, and iterates until the task is done, all inside the existing chat panel.
⚠️ Supported Agent Mode backends: API-backed Anthropic Claude, OpenAI, Google Gemini, DeepSeek, Grok, OpenRouter, and Meta Muse, plus the ChatGPT / Codex CLI, Claude Code CLI, and OpenCode CLI providers. Ollama and Bedrock currently fall back to plain chat.
Agent Mode is on by default: with a supported model selected, Agent is preselected under the message box, and you can switch to Chat at any time for questions and snippets (plain chat costs far fewer tokens, see token quotas). By default, delete, move, open plan and apply correlation ask for confirmation in JMeter; set jmeter.ai.agent.confirm.destructive=false to disable these prompts. If a cloud provider key is set and you have switched Agent Mode off, the welcome message shows a one-time tip explaining how to turn it back on (it never changes the setting for you; the marker lives at ~/.jmeter-ai/agent-mode-tip-shown). To switch it off for everyone on the machine:
# In user.properties or jmeter.properties
jmeter.ai.agent.enabled=false
Select a Claude, OpenAI, Google Gemini, DeepSeek, Grok, OpenRouter, or Meta Muse model from the dropdown. Then just type your request naturally in the chat box; if Agent Mode is enabled and a supported model is selected, the agent loop activates automatically.
If a model from any other provider is selected, the request is handled by the regular (non-agentic) chat path.
All supported providers share the same tool registry, system prompt, safety gates and iteration limits; only the wire format differs (Anthropic tool_use blocks, OpenAI-compatible function tool_calls, or Gemini functionCall/functionResponse parts). When optional Jev Smart Routing is enabled, the provider receives a focused subset for confident single-purpose requests and the complete registry for complex, uncertain, or failed routing.
💡 OpenAI note: temperature is left at the model default for agent runs, so reasoning models (
o1,o3,o4,gpt-5) work without extra configuration.jmeter.ai.agent.max.tokensmaps tomax_completion_tokens. For gpt-5.1 and later (gpt-5.6-terra,gpt-5.6-sol, ...) the agent automatically sendsreasoning_effort=none, because those models reject function tools on/v1/chat/completionswhile reasoning is on, so tool calling works out of the box.
💡 OpenAI-compatible provider note: for DeepSeek, Grok, OpenRouter, and Meta Muse agent runs,
reasoning_effortis not sent; the vendor default applies. For Meta Muse, agent runs go through Chat Completions rather than the Responses API used for plain chat, so the Thoughts card is not populated during agent runs.
💡 Thinking in Agent Mode (Claude & Gemini): when the Thinking checkbox is on, each agent turn's reasoning accumulates in a collapsed Thoughts card next to the tool-activity group. Agent loops pay the thinking budget on every iteration; keep the effort at
medium, or pin an agent-only level withjmeter.ai.agent.thinking.effort(empty = follows the toolbar).
Type @fix or click the Make it pass offer after a recording or a failed Agent test. Feather Wand backs up the plan, runs a real JMeter check with one user and one loop, and asks the selected Agent provider to repair failures using a restricted set of plan-editing tools. It also detects common soft failures such as an error page returned with HTTP 200, adds title-based response assertions, and installs View Results Tree and Summary Report listeners when missing. After a passing one-user run, it checks two users for two loops and repairs multi-user failures within the same round limit by default. JMeter results decide whether the plan passes. A result card includes a redacted plan diff, remaining failures, and an Open report action. Markdown reports are retained under ~/.jmeter-ai/fix-reports/, keeping the latest 20.
Before the first run, Feather Wand shows the HTTP sampler and HTTP Request Defaults hosts and asks for confirmation. Host variables are resolved when their values are available; unresolved placeholders remain literal. Confirmed host sets are remembered only for the current JMeter session. The repair agent cannot remove, disable, or move requests; remove or weaken assertions; add or change scripts (JSR223, BeanShell, or __groovy-style functions) or non-HTTP samplers; or change Thread Group settings or variables used in hosts. A result card offers Keep, Undo all, and JMeter's Save as action. Undo all asks before discarding edits made after the fix finished. Automatic backups are saved under ~/.jmeter-ai/fix-backups/; the newest ten are kept.
| Property | Description | Default |
|---|---|---|
jmeter.ai.fix.max.rounds | Maximum repair rounds, clamped from 1 to 10 | 5 |
jmeter.ai.fix.timeout.seconds | Timeout for each JMeter check, clamped from 10 to 900 seconds | 120 |
jmeter.ai.fix.concurrency.check | Run the final two-user, two-loop check and repair its failures | true |
Jev is an optional TypeSafe judgment model used only to classify an Agent Mode request before the selected chat model starts. A confident single-purpose classification advertises a focused tool pack; complex requests, low confidence, or service failures receive the complete standard registry. Without a real typesafe.api.key Jev stays off entirely: no TypeSafe requests and no Jev card, even if the flags below are true.
jmeter.ai.typesafe.enabled=true
jmeter.ai.typesafe.agent.routing.enabled=true
jmeter.ai.typesafe.agent.routing.min.confidence=0.75
jmeter.ai.typesafe.agent.routing.expansion.enabled=false
jmeter.ai.typesafe.agent.routing.expansion.max=1
jmeter.ai.typesafe.agent.triage.enabled=false
jmeter.ai.typesafe.agent.triage.max.failures=5
typesafe.api.key=YOUR_TYPESAFE_API_KEY
typesafe.base.url=https://api.typesafe.ai
typesafe.model=jev-latest
typesafe.timeout.seconds=15
When enabled with a real API key, every request shows a dedicated Jev Smart Route card before agent activity. It identifies the selected route, confidence, focused/total tool counts, and the chat model that still performs the work. Uncertain routing shows that all tools are being used; a TypeSafe failure shows that standard Agent Mode is being used.
With jmeter.ai.typesafe.agent.routing.expansion.enabled=true, a focused pack also advertises an expand_tools escape hatch: if the model realises it needs a capability outside its pack, it can ask for more tools instead of giving up. Jev re-classifies the request with the model's stated need, the live tool set grows (never shrinks, capped by expansion.max, never beyond the full registry), and a second Jev Expanded Tools card lists what was added. The grown tool set is re-advertised to the provider on the model's next request, so newly added tools are actually callable for the API-backed models (Claude, OpenAI, Gemini, DeepSeek, Grok, OpenRouter, Meta Muse) and the subscription CLIs, whose tool protocol is re-issued in the next prompt. Newly exposed destructive tools still require the usual confirmation.
With jmeter.ai.typesafe.agent.triage.enabled=true (independent of routing), Jev also classifies failures reported by get_test_results. Each unique failure signature: sampler label, response code, and a bounded message gets one Choice judgment into a root-cause bucket (connection, timeout, 4xx/5xx, auth/session, assertion, script, config), capped at triage.max.failures signatures per run. The verdicts surface in a Jev Failure Triage card grouped by category with a dominant-cause callout, and an advisory summary is appended to the tool result so the chat model diagnoses from a categorized picture. When at least one failure is classified, triage on every backend (Jev, OpenAI Decisions and OpenRouter Decisions) then asks one extra score question for the whole run and shows a severity of Low, Medium, High or Critical on the card and in the advisory, for example "severity: High (88%)". The score uses the same confidence threshold; a low-confidence or failed score just leaves severity out and keeps the categories. Triage is advisory only: unavailable Jev, a missing key, or a clean run leaves the result untouched.
Jev never appears in the model picker, generates the answer, selects tool arguments, executes a tool, or bypasses destructive-operation confirmation. Feather Wand sends TypeSafe only the current request with attachment bodies removed plus, for triage, failure labels/codes/truncated messages; it does not send conversation history, the serialized test plan, element properties, result bodies, headers, cookies, or provider credentials. Leave either feature flag false to make zero TypeSafe requests and preserve the original Agent Mode path.
TypeSafe currently publishes Python and JavaScript/TypeScript SDKs; Feather Wand's Java 17 integration uses the documented POST /v1/systemone HTTP API directly.
Routing and triage can run on the OpenAI Decisions API (POST /v1/decisions, public beta) instead of TypeSafe Jev. Each judgment is sent as a single choice question whose options are the route or failure categories, and OpenAI's per-option probabilities and confidence feed the same thresholds and fallbacks described above. It reuses your openai.api.key, so no TypeSafe key is needed:
jmeter.ai.judgment.provider=openai
jmeter.ai.typesafe.enabled=true
jmeter.ai.typesafe.agent.routing.enabled=true
jmeter.ai.typesafe.agent.triage.enabled=true
openai.api.key=YOUR_OPENAI_API_KEY
openai.decisions.model=gpt-6-luna
The jmeter.ai.typesafe.* flags, confidence threshold, expansion and triage limits, and typesafe.timeout.seconds apply to both backends; the cards are labelled OpenAI Decisions instead of Jev. Without a real openai.api.key the OpenAI backend stays off entirely, exactly like Jev without a TypeSafe key. The same data boundaries apply: only the current request (or failure label, code and truncated message) is sent. Requests go through the official openai-java SDK and honour openai.base.url, openai.extra.headers and openai.max.retries, so a corporate gateway must implement /v1/decisions; if it does not, routing shows the unavailable card and Agent Mode falls back to all tools. Decisions bills input tokens only.
Routing and triage can also run on OpenRouter's Decisions API (POST /api/alpha/decisions, alpha) with Microsoft-Decision-1, Microsoft's small decision-scoring model that returns a calibrated probability for each fixed option instead of generating text. Each judgment is the same single choice question used by the other backends, and the per-option probabilities and confidence feed the same thresholds and fallbacks:
jmeter.ai.judgment.provider=openrouter
jmeter.ai.typesafe.enabled=true
jmeter.ai.typesafe.agent.routing.enabled=true
jmeter.ai.typesafe.agent.triage.enabled=true
openrouter.api.key=YOUR_OPENROUTER_API_KEY
openrouter.decisions.model=microsoft/microsoft-decision-1
The cards are named after the model's vendor: Microsoft Decision (for example "Microsoft Decision Smart Route") for microsoft/* models, Jev for typesafe/* models, and OpenRouter Decisions otherwise. Without a real openrouter.api.key the backend stays off entirely; TypeSafe and OpenAI keys never switch it on. openrouter.decisions.model accepts any model OpenRouter serves on the Decisions API (for example typesafe/jev-1.13), and openrouter.base.url defaults to https://openrouter.ai/api/v1. The same key and base URL are shared with OpenRouter Chat and Agent Mode. The Decisions endpoint normalizes /v1 before calling the alpha API. The same data boundaries apply as for Jev. Microsoft-Decision-1 bills input tokens only.
Smart routing runs locally with no API key when Ollama is served on the default localhost endpoint; request text and embeddings stay on your machine. Feather Wand reports the selected backend in its separate anonymous telemetry; see Privacy and telemetry. Ollama's POST /api/embed compares the request with example requests for each tool pack; the closest pack wins. The similarity scores become per-pack probabilities, so the same confidence threshold, expansion and all-tools fallback apply:
jmeter.ai.judgment.provider=ollama
jmeter.ai.typesafe.enabled=true
jmeter.ai.typesafe.agent.routing.enabled=true
jmeter.ai.embeddings.model=embeddinggemma
Pull the model once with ollama pull embeddinggemma (about 620 MB). The backend reuses ollama.host and ollama.port, and typesafe.timeout.seconds as the request timeout. The examples are embedded on the first Agent Mode request and kept in memory for the JMeter session (until the Ollama endpoint or model changes), so later requests send one short embedding call (typically under 150 ms on a laptop CPU). The cards are labelled EmbeddingGemma (for example "EmbeddingGemma Smart Route") for EmbeddingGemma models and Local embeddings for any other embedding model. If Ollama is not running or the model is not pulled, routing shows the unavailable card, jmeter.log says what to run, and Agent Mode keeps all tools.
FEATHER_WAND_OLLAMA_EMBED_MODEL=<model> mvn test -Dtest=EmbeddingAgentRequestRouterOllamaTest before relying on it; low-confidence requests still fall back to all tools.jmeter.ai.typesafe.agent.triage.enabled has no effect with this backend.embeddinggemma-2 tags on Ollama currently need Apple's MLX runtime, so they only run on Apple Silicon Macs. On Windows and Linux the community GGUF build works: ollama pull hf.co/ggml-org/embeddinggemma-2-GGUF:Q8_0, then set jmeter.ai.embeddings.model=hf.co/ggml-org/embeddinggemma-2-GGUF:Q8_0. In our routing check EmbeddingGemma 1 made more confident picks, so it stays the default.jmeter.ai.judgment.provider=local runs the same EmbeddingGemma router inside JMeter with ONNX Runtime, so it needs neither an API key nor Ollama:
jmeter.ai.typesafe.enabled=true
jmeter.ai.typesafe.agent.routing.enabled=true
jmeter.ai.judgment.provider=local
# optional
jmeter.ai.embeddings.local.model=onnx-community/embeddinggemma-300m-ONNX
jmeter.ai.embeddings.local.dir=~/.jmeter-ai/models
jmeter.ai.embeddings.local.download=true
jmeter.ai.embeddings.local.threads=
~/.jmeter-ai/models/<repo>/<revision>/. While it downloads, routing shows the unavailable card and Agent Mode keeps all tools. Both supported models are pinned to a revision and every file is checked by size and SHA-256 before it is used. Later JMeter sessions load the model from disk in about a second.-Dhttps.proxyHost, -Dhttps.proxyPort). jmeter.ai.embeddings.local.base.url points it at a Hugging Face mirror. With jmeter.ai.embeddings.local.download=false, copy onnx/model_q4.onnx, onnx/model_q4.onnx_data and tokenizer.json into the folder named in jmeter.log.onnx-community/embeddinggemma-300m-ONNX (EmbeddingGemma 1, the default) or onnx-community/embeddinggemma-2-ONNX (EmbeddingGemma 2, the model Quick Links uses). On the routing check below, EmbeddingGemma 1 made 21 confident picks and EmbeddingGemma 2 made 4, both with no wrong confident picks, so EmbeddingGemma 1 stays the default.jmeter.ai.embeddings.local.threads overrides that. On first use, the plugin downloads the ONNX Runtime JAR (about 40 MB) from Maven Central, verifies its SHA-256, and installs only the native libraries for your platform under ~/.jmeter-ai/onnxruntime/1.24.3/<os-arch>/. On locked-down networks, set jmeter.ai.onnx.runtime.url to an internal mirror of the same JAR. The download supports Windows x64, Linux x64/aarch64, and Apple Silicon macOS only; Intel Macs and other OS/architecture combinations are unsupported, so use Ollama there.FEATHER_WAND_ONNX_EMBED_MODEL=onnx-community/embeddinggemma-300m-ONNX mvn test -Dtest=EmbeddingAgentRequestRouterOnnxTest runs the same accuracy check as the Ollama one.The API-backed Agent Mode providers are driven through the exact same provider-neutral ChatModel seam (start/next) and share one JsonSchemaMapper, so every tool looks byte-identical across them; only the wire format differs:
| Aspect | Anthropic Claude (anthropic-java) | OpenAI (openai-java) | Google Gemini (google-genai) |
|---|---|---|---|
| Tool definition | Tool (native tool schema) | ChatCompletionFunctionTool (function-type only; non-function "custom" tool calls are ignored) | FunctionDeclaration inside a Tool |
| System prompt | Top-level system string, separate from messages | A message inside the rolling messages list | systemInstruction on the request config, separate from contents |
| Message roles | user / assistant only (tool outcomes ride back as a user turn of tool_result content blocks) | system / user / assistant / tool | user / model only (tool outcomes ride back as a user turn of functionResponse parts) |
| Tool-call arguments | Already a JsonValue → converted to a Map directly | A JSON string → parsed with Jackson (tolerates malformed JSON) | Already a Map<String, Object> - no parsing needed |
| Tool-result error signaling | Native is_error boolean | No native error flag; errors are conveyed via an ERROR [...] prefix in the content | No native error flag either; the same ERROR [...] prefix is also placed under an error response key |
| Model-specific quirks | None needed | reasoning_effort=none forced for gpt-5.1+ (else tool calls 400); temperature never sent (o1/o3/o4/gpt-5 reject non-default values) | JSON-Schema type keywords are upper-cased (string → STRING) to match Gemini's Type enum; FunctionCall.id() is rarely populated outside the Live API, so calls fall back to a positional id |
Feather Wand already talks to more providers than Agent Mode currently supports; most of the remaining gap is wiring, not feasibility, since several already share Claude's or OpenAI's SDK under the hood:
| Provider | Already in Feather Wand? | Tool-calling on the wire? | Adapter effort |
|---|---|---|---|
| Anthropic Claude | ✅ Agent Mode | Native tool_use | Done |
| OpenAI | ✅ Agent Mode | Native tool_calls | Done |
| Google Gemini | ✅ Agent Mode | Native FunctionDeclaration/functionCall via the official google-genai SDK | Done |
| ChatGPT / Codex CLI | ✅ Agent Mode | No native tool API; driven through a JSON tool protocol in the prompt over codex exec | Done |
| Claude Code CLI | ✅ Agent Mode | No native tool API; same JSON tool protocol over claude -p | Done |
| OpenCode CLI | ✅ Agent Mode | No native tool API; same JSON tool protocol over opencode run with restricted tools | Done |
| DeepSeek | ✅ Agent Mode | Yes: OpenAI-compatible tools/tool_choice (or Anthropic-compatible via /anthropic) | Done |
| Grok (xAI) | ✅ Agent Mode | Yes: OpenAI-style function tools | Done |
| OpenRouter | ✅ Chat and Agent Mode | OpenAI-compatible Chat Completions with tool-capable model discovery | Done |
| Meta "Muse" | ✅ Agent Mode | Yes: OpenAI-compatible function tool_calls on Chat Completions | Done |
| Kimi K2/K3 (Moonshot AI) | Not yet added | Yes: standard OpenAI-shaped tools/tool_calls | 🟢 Trivial (same "point openai-java at a new base URL" pattern) |
| Poolside (Laguna models) | Not yet added | Yes: OpenAI-compatible tools/tool_choice at inference.poolside.ai (also via OpenRouter/Bedrock) |
| Property | Description | Default |
|---|---|---|
jmeter.ai.agent.enabled | Enable the agent tool-calling loop (set false to keep the Agent option off) | true |
jmeter.ai.agent.max.tokens | Max tokens per agent response | 4096 |
jmeter.ai.agent.max.iterations | Max reason-act iterations per request | 500 |
jmeter.ai.agent.confirm.destructive | Show confirmation dialog before destructive ops | true |
jmeter.ai.typesafe.enabled | Master switch for TypeSafe/Jev integrations | false |
jmeter.ai.typesafe.agent.routing.enabled | Use Jev to select a focused Agent Mode tool pack | false |
jmeter.ai.typesafe.agent.routing.min.confidence | Minimum confidence for using a focused pack; lower values use all tools | 0.75 |
jmeter.ai.typesafe.agent.routing.expansion.enabled | Let a focused run ask for more tools via expand_tools | false |
jmeter.ai.typesafe.agent.routing.expansion.max | Max tool-set expansions per Agent Mode run | 1 |
jmeter.ai.typesafe.agent.triage.enabled | Use Jev to classify get_test_results failures into root-cause buckets | false |
jmeter.ai.typesafe.agent.triage.max.failures | Max unique failure signatures classified per run | 5 |
typesafe.api.key | TypeSafe API key; required for Jev routing and triage (without a real key Jev stays off) | (empty) |
typesafe.base.url | TypeSafe API root | https://api.typesafe.ai |
typesafe.model | TypeSafe System One model | jev-latest |
typesafe.timeout.seconds |
💡 Undo support: JMeter's Undo/Redo is disabled by default (
undo.history.size=0). Addundo.history.size=50touser.propertiesand restart JMeter so you can Ctrl+Z agent-made changes. The agent will remind you once if it's off.
The agent has 21 tools at its disposal. Jev Smart Routing can advertise a focused subset for one request, but all tools remain available through the standard fallback:
Read
| Tool | What it does |
|---|---|
get_tree_state | Returns the full test-plan tree with element names, types, and enabled state. |
get_element_config | Returns all properties of a specific element. |
get_element_children | Returns the children of a specific element. |
get_element_schema | Returns the property schema and allowed values for an element type. |
Write
| Tool | What it does |
|---|---|
add_element | Adds a new element (e.g. HTTPSamplerProxy) as a child of a parent element. |
update_element_property | Sets a scalar property (e.g. HTTPSampler.path) on an element; rejects unknown keys when a catalog suggestion exists. |
set_property_list | Sets a flat string-list property (e.g. ResponseAssertion test patterns). |
set_structured_property_list | Sets a structured list (e.g. HeaderManager.headers, Arguments.arguments, AuthManager.auth_list). |
delete_element | Deletes an element and its subtree. Confirmation gated. |
toggle_element | Enables or disables an element (disabled elements are skipped at run time). |
move_element | Reparents an element to become the last child of a new parent. Confirmation gated. |
duplicate_element | Deep-clones an element's subtree as the next sibling. |
rename_element | Renames an element (non-destructive; reports the new tree-path id). |
reorder_element | Repositions an element among its current siblings by index. |
Run
| Tool | What it does |
|---|---|
run_test | Starts the test plan (same as JMeter's Start button). |
stop_test | Stops the running test (force=true for immediate shutdown). |
get_test_results | Runs the plan in a private engine, blocks until completion or timeout, and reports pass/fail counts with failure details. |
Correlation
| Tool | What it does |
|---|---|
find_correlation_candidates | Probes the test plan (1 thread/1 loop) and detects dynamic values that need correlation. |
apply_correlation | Applies selected correlation candidates: adds extractors and rewrites matching values to ${variable}. Confirmation gated. |
File
| Tool | What it does |
|---|---|
save_plan | Saves the test plan to a .jmx file. |
open_plan | Opens a .jmx file, replacing the current plan. Confirmation gated. |
get_tree_stateadd_element to create the HTTP Request samplerupdate_element_property to set the pathget_element_config to verify the changeEach tool call and result is streamed to the chat in real time, so you can follow along. The agent's final answer is replayed token-by-token (gated by jmeter.ai.streaming.enabled).
delete_element, move_element, open_plan, apply_correlation) show an Allow/Deny confirmation dialog before executing - with an impact preview, not just a tool name: deletes list the subtree (child count + names, force flag), moves show from → to, open_plan shows the file and warns about unsaved changes, and correlation lists how many candidates it will touch. Disable with jmeter.ai.agent.confirm.destructive=false.jmeter.ai.agent.max.iterations (default 500) even if the task isn't complete.undo.history.size > 0.Try these in the chat box with Agent Mode enabled and a Claude, OpenAI, Google Gemini, DeepSeek, Grok, OpenRouter, or Meta Muse model selected:
| Request | What the agent does |
|---|---|
| Add an HTTP Request under the Thread Group and set its path to /login | get_tree_state → add_element → update_element_property → get_element_config |
| Disable the second HTTP Request | get_tree_state → toggle_element |
| Add a Response Assertion that checks for 200 | get_tree_state → add_element → set_property_list |
| Move the JSON Extractor under the first HTTP Request | get_tree_state → move_element (asks confirmation) |
| Run the test and tell me if it passed | run_test → get_test_results |
| Find dynamic values that need correlation | find_correlation_candidates |
| Apply correlation for candidates 1 and 3 | apply_correlation (asks confirmation) |
| Save the test plan to /tmp/my-plan.jmx | save_plan |
For isolated manual testing, Feather Wand adds dev menu items under Run → AI Dev: that exercise individual tools against the selected tree node without going through the agent loop. These are intended for development and debugging:
Feather Wand can run a Model Context Protocol server inside JMeter, so Claude Code, Cursor, Codex or any other MCP client can read, edit, run and analyze the test plan open in your JMeter GUI. The client gets the same 21 tools as Agent Mode, with the same names and parameters, and every change shows up in the JMeter tree as it happens. No AI provider key is needed in Feather Wand for this: the client brings its own model.
jmeter.ai.mcp.server.enabled=true to user.properties.http://127.0.0.1:7397/mcp. Change the port with jmeter.ai.mcp.server.port.| Property | Description | Default |
|---|---|---|
jmeter.ai.mcp.server.enabled | Start the MCP server when JMeter starts | false |
jmeter.ai.mcp.server.port | Loopback port the server listens on | 7397 |
The Feather Wand MCP Server window shows the setup for each client with your token filled in. Click Copy setup and paste it:
Claude Code (run in a terminal):
claude mcp add --transport http feather-wand http://127.0.0.1:7397/mcp --header "Authorization: Bearer <token>"
Cursor (~/.cursor/mcp.json, or .cursor/mcp.json in a project):
{
"mcpServers": {
"feather-wand": {
"url": "http://127.0.0.1:7397/mcp",
"headers": { "Authorization": "Bearer <token>" }
}
}
}
Codex (~/.codex/config.toml):
[mcp_servers.feather-wand]
url = "http://127.0.0.1:7397/mcp"
http_headers = { "Authorization" = "Bearer <token>" }
Clients that only speak stdio can connect through mcp-remote: npx -y mcp-remote http://127.0.0.1:7397/mcp --header "Authorization: Bearer <token>".
Then ask the client something like "Open checkout.jmx in JMeter, add a Response Assertion for HTTP 200 to every sampler, run it and tell me which requests fail."
127.0.0.1, so other machines on your network cannot reach it.Authorization: Bearer <token>. The token is a random 256-bit value stored in ~/.jmeter-ai/mcp-token (readable only by you on macOS and Linux). Click New token to replace it; clients set up with the old one stop working.Origin header are refused, so a web page cannot talk to the server even through DNS rebinding.delete_element, move_element, open_plan and apply_correlation wait for you to click Allow in JMeter, unless jmeter.ai.agent.confirm.destructive=false. Tool annotations tell the client which tools are read-only and which are destructive.save_plan or you save in JMeter.-n) mode.2025-06-18, 2025-03-26 and 2024-11-05). It offers tools only, with no resources, prompts or SSE stream.get_test_results and find_correlation_candidates run the plan and block until it finishes, so keep thread counts and durations small when a client calls them.busy error and can retry. Read tools wait for an edit in progress so they never see half of it, but keep working during a long run; stop_test always runs at once. When too many requests arrive together, the extras get HTTP 503, but ping and stop_test still get through.All configured AI services that support streaming provide real-time responses. AWS Bedrock uses the ConverseStream API for compatible chat models. Responses appear token-by-token as they are generated.
| Control | What it does |
|---|---|
| Stop | Appears next to the Send button during streaming; click to cancel mid-response. |
Disable streaming:
jmeter.ai.streaming.enabled=false
Next to the model selector, a Thinking checkbox and an effort dropdown appear automatically when the selected model supports them - models with no reasoning support (e.g. gpt-4o) hide both. Reasoning streams into a collapsible Thoughts card above the answer, in both plain chat and Agent Mode.
Effort levels shown in the dropdown come straight from the vendored per-model data (models.dev), so newer levels like xhigh/max appear automatically where supported.
| Model family | Thinking checkbox | Effort levels | Notes |
|---|---|---|---|
| Claude 4.x (Anthropic, Bedrock) | yes | low / medium / high / max | Thinking budget per level (property-overridable); temperature is dropped and max_tokens auto-bumped when thinking is on |
| Claude 5 (fable) | yes | low / medium / high / xhigh / max | Adaptive thinking + output_config effort; summarized thoughts shown in the Thoughts card |
| OpenAI o1 / o3 / o4 | always on | low / medium / high | reasoning_effort |
| OpenAI gpt-5* | always on | minimal / low / medium / high | reasoning_effort |
| OpenAI gpt-5.x | yes (off -> none) | none / low / medium / high | Agent Mode still forces none (chat-completions rejects tools + effort) |
| Gemini 2.5 Flash | yes | low / medium / high | thinkingBudget; unchecked sends budget 0 (thinking disabled) |
| Gemini 2.5 Pro | always on | low / medium / high | thinkingBudget (Pro cannot disable thinking) |
| Gemini 3 | always on | low / high | thinkingLevel |
| Ollama (thinking models) | yes | low / medium / high | Capability probed live via /api/show; UI overrides ollama.thinking.* properties |
| Grok 4.5 | always on | low / medium / high | reasoning_effort (cannot be disabled); summarized reasoning shown in the Thoughts card |
| Meta Muse Spark | always on | minimal / low / medium / high / xhigh | reasoning_effort via the Responses API with reasoning.summary; summary shown in the Thoughts card (verbosity via meta.reasoning.summary=auto|concise|detailed, default auto) |
| Bedrock: Claude | yes | low / medium / high / max (+ xhigh on newer) | Thinking JSON in additionalModelRequestFields (budget or adaptive) |
| Bedrock: Nova 2 Lite |
Defaults via properties:
jmeter.ai.thinking.enabled=false
jmeter.ai.thinking.effort=medium
# Optional Anthropic budget overrides (tokens):
#anthropic.thinking.budget.low=2048
#anthropic.thinking.budget.medium=8192
#anthropic.thinking.budget.high=16384
The ollama.thinking.mode / ollama.thinking.level properties now act as defaults for the Ollama toolbar controls; the UI choice wins once changed.
How capability detection works: whether a model supports reasoning - and exactly which effort values it accepts - comes from a vendored copy of models.dev data (src/main/resources/org/qainsights/jmeter/ai/reasoning/model-capabilities.json), refreshed at build time via scripts/update-model-capabilities.sh (review the git diff like any dependency bump). Nothing is fetched at runtime; models absent from the file simply hide the controls, and dated/variant ids from live provider APIs resolve to their family entry. Ollama is the exception: its local /api/show endpoint reports real per-model capabilities (thinking, vision, ...), so the toggle is probed live on selection (optimistically shown until the probe answers).
Attach files to your chat messages and let the AI analyze them - built for performance-engineering workflows: "why did p99 spike?", "any errors in this run?", "compare these two result files".
Ways to attach:
Pending attachments appear as chips above the input (name · size · mode). Click a chip to switch smart ↔ raw processing; × removes it. The sent message shows the same chips in the transcript.
Smart vs. raw processing:
| Mode | What the model receives |
|---|---|
| smart (default) | .jtl/.csv results → compact digest: sample count, error rate, avg/median/p90/p95/p99, throughput, per-label breakdown, slowest + failing samples. Log files → ERROR/WARN lines (capped), counts by logger, exceptions with stack lines, first/last lines. Other text → head + tail excerpt. |
| raw | Head + tail of the file within the character budget, with an explicit truncation marker. |
Attachments are inlined as text at request time, so they work on every provider - no vision or document-upload capability needed. Follow-up questions re-include the file automatically until the history window trims it. Each message can carry up to 3 attachments (configurable), files up to 10 MB, and only valid UTF-8 text files are accepted.
# Processing mode: smart (default) or raw
#jmeter.ai.file.mode=smart
# Character budget for excerpts/digests (default 50000)
#jmeter.ai.file.max.chars=50000
# Max attachments per message (default 3)
#jmeter.ai.file.max.count=3
Every conversation is autosaved after each turn to ~/.jmeter-ai/sessions/ - one JSON file per conversation, including attachment contents, the model in use, and per-turn timestamps. Starting a new chat (the + button) archives the current session and begins a fresh one; the directory keeps the 20 most recent sessions.
Restore on startup is opt-in: with the property below, reopening the chat panel brings back the last conversation - transcript, history (so follow-ups keep their context), attachments, and the model it used.
# Restore the last conversation when the chat panel opens (default false)
#jmeter.ai.session.restore=true
⚠️ Sessions are stored unencrypted, including the full text of attached logs/results. Avoid attaching files that contain credentials or secrets.
Export for reports: the Export menu in the chat header writes the current conversation as Markdown (paste into tickets/wikis) or a self-contained styled HTML page - file attachments appear by name. Perfect for attaching AI analysis to a test report.
The input options row (the row with the paperclip) carries a live readout: ctx 12.3k/400k · $0.04.
~. Attachments are the usual context hogs - watch this number when you attach big logs.The one-shot CLI providers do not currently feed token usage into the session-cost total, so their context remains locally estimated. OpenCode reports token and cost fields, but Feather Wand does not yet import them.
Hover the label for the exact breakdown: precise context count and percentage, session input/output tokens over N responses, and the exact estimated cost. The denominator hides for models with unknown context windows (e.g. local Ollama models), and the label resets when you start a new conversation.
Record a real browser session into a JMeter test plan without leaving the chat. Recording is off by default; enabling it adds a recording control panel to the chat panel. Traffic is captured through JMeter's built-in proxy recorder into a Recording Controller (the same shape as JMeter's recording.jmx template, with its suggested excludes), then finalized into a proper plan - think-time injection and artifact cleanup included.
# Master switch (default false) - adds the recording control panel to the chat
jmeter.ai.record.enabled=true
# Optional knobs (defaults shown):
#jmeter.ai.record.artifacts.dir= # empty = java.io.tmpdir/jmeter-ai-recordings
#jmeter.ai.record.retention.days=7
#jmeter.ai.record.think_time.scale=1.0 # 1.0 = think time as recorded
#jmeter.ai.record.think_time.min.ms=0
#jmeter.ai.record.think_time.max.ms=10000
#jmeter.ai.record.tool.output.max.chars= # empty = 8000 (~2000 tokens)
#jmeter.ai.record.max.iterations=500 # maximum agent iterations per recording
The recording agent defaults to 500 iterations and can be adjusted with jmeter.ai.record.max.iterations.
Get an audible cue when the AI finishes responding so you can multitask across windows.
jmeter.ai.response.chime=true
The bundled WAV plays from src/main/resources/org/qainsights/jmeter/ai/sound/jmeter-chime.wav with an MP3 fallback.
A draggable animated companion that lives on the JMeter canvas and reacts to your test runs. The pet gets excited when a test starts, works while samplers run, frowns on sampler failures, and celebrates clean runs. Drag it anywhere on the screen.
Available pets: quill (default), glim, peacock, monkey
# Enable the companion pet (off by default)
jmeter.pet.enable=true
# Which pet to show: quill, glim, peacock, or monkey
jmeter.pet.name=quill
# Render scale (0.25 - 2.0); 0.5 shows the pet at about 96x104 pixels
jmeter.pet.scale=0.5
| Property | Description | Default |
|---|---|---|
jmeter.pet.enable | Show the companion pet on the canvas | false |
jmeter.pet.name | Which pet sprite to display (one of quill, glim, peacock, monkey) | quill |
jmeter.pet.scale | Render scale, clamped to [0.25, 2.0] | 0.5 |
Invalid values never fail; they log a warning and fall back to the defaults above.
Each pet spritesheet is an 8-column × 9-row atlas (192×208 px cells). Frame counts are auto-detected per row by scanning for the first fully transparent cell, so no per-pet frame configuration is needed. The nine rows map to these states:
| Row | State | Loops | When it plays |
|---|---|---|---|
| 0 | IDLE | yes | Resting between runs |
| 1 | RUNNING_RIGHT | yes | Test running (moving right) |
| 2 | RUNNING_LEFT | yes | Test running (moving left) |
| 3 | WAVING | no | Greeting / celebrating a clean run |
| 4 | JUMPING | no | Celebratory jump |
| 5 | FAILED | no | A sampler failed |
| 6 | WAITING | yes | Waiting for the test to progress |
| 7 | RUNNING | yes | General running animation |
| 8 | REVIEW | yes | Reviewing results at the end of a run |
Looping states play continuously; one-shot states play a fixed number of loops and then revert to the animator's base state.
An embedded interactive terminal (JediTerm) that brings agentic AI CLIs directly into JMeter. The same terminal mark appears in the JMeter Run menu, toolbar, and terminal header.
Supported CLIs: Claude Code · OpenAI Codex · OpenCode · Antigravity · Grok CLI
How it works
PATH..jmx context via an auto-generated CLAUDE.md.Buttons
Architecture
Built on an Adapter Pattern: AiCliAdapter → BaseCliAdapter → concrete adapters (ClaudeCodeCliAdapter, OpenAiCodexCliAdapter, ...). To add a new CLI, implement AiCliAdapter and register it in detectAvailableClis().
⚠️ Caution: AI CLIs can execute commands and modify files. Review each CLI's documentation before enabling.
The terminal supports configurable fonts and CJK fallback. By default, jmeter.ai.terminal.font.cjk.fallback=true automatically picks the best CJK-capable font on your system. If you prefer a Western monospaced font, set jmeter.ai.terminal.font.cjk.fallback=false.
See the AI CLI Terminal configuration section for the full property table and recommended setups.
| Provider | Steps | Property |
|---|---|---|
| Claude | Sign up at anthropic.com → create API key | anthropic.api.key |
| OpenAI | Sign up at platform.openai.com → create API key | openai.api.key |
| Gemini | Sign in at Google AI Studio → Get API Key | google.api.key |
| Ollama | Install from ollama.com → ollama pull llama3.1 | No key needed |
| Grok (xAI) | Sign up at console.x.ai → create API key | grok.api.key |
| OpenRouter | Create an API key at openrouter.ai | openrouter.api.key |
| AWS Bedrock | Configure a Bedrock API key or AWS IAM/default credentials in the selected Region | bedrock.api.key or IAM properties |
Set jmeter.ai.service.type=ollama to switch to a local model. All other providers work side-by-side; just pick the model from the UI dropdown.
Feather Wand automatically hides non-chat models so you only see useful options:
gpt-*, o-series (o<digit>*, e.g. o1, o3, o4-mini) and chat-latest chat models; hides audio, TTS, whisper, davinci, search, realtime, instruct, embedding, moderation and image models, plus models that only work on /v1/responses (*-pro, *codex*, deep research, computer use).gemini-* and gemma-* chat models.grok-* chat models.bedrock.model.providers; unavailable profiles are hidden.openai.base.url or anthropic.base.url set, gateway-specific model names are kept (audio, embedding, moderation, image and other non-chat models are still hidden), or listed verbatim from openai.models / anthropic.models.Default models: claude-sonnet-4-6 · gpt-4o · gemini-3.5-flash · deepseek-chat · deepseek-r1:1.5b · grok-4.5 · anthropic/claude-sonnet-5.5 · anthropic.claude-3-5-sonnet-20241022-v2:0
Starting with v3.8.6, Feather Wand sends one small, anonymous usage ping per day. It is on by default, and you can turn it off at any time. These numbers are the only way we know how many people use Feather Wand, which JMeter versions and providers to keep supporting, and which features are worth improving.
| Field | Example | Notes |
|---|---|---|
| Install ID | 3f1c9a2e-7b4d-4e8a-9c1f-2d5e6a7b8c9d | Random UUID created once and stored in ~/.jmeter-ai/telemetry.json. It is not derived from your machine, user name, or account. |
| Plugin version | 3.9.0 | |
| JMeter version | 5.6.3 | |
| Java version | 17.0.12 | |
| OS and CPU architecture | Mac OS X, aarch64 | |
| AI provider | anthropic | The value of jmeter.ai.service.type only. |
| Agent mode enabled | true | |
| Decisions backend | openrouter:microsoft | Which backend serves smart routing and failure triage: none (off, or its key is blank or a placeholder), typesafe, openai, openrouter: plus the model vendor (the part before /), or ollama (local embedding routing). |
| Model | gpt-4o-mini | The chat-panel model of your latest Chat or Agent message, or the configured default model before you send one. Only well-known public model ids (GPT, o-series, Claude, Gemini, DeepSeek, Grok, Muse, Llama, Qwen, Mistral and similar) are sent as-is. Fine-tunes, gateway aliases and every other name are sent as custom. unknown means none is set. |
| First run | false | true only on the first ping from a new install. |
| Feature counts | {"chat_message": 12, "agent_run": 3} | How many times each feature was used since the last ping. |
The same ping fields are also forwarded from our server to PostHog's US cloud for product analytics, without an IP address or location, and turning telemetry off stops both.
Counted features: panel opens, chat messages, agent runs, recording runs, @this, @optimize, @lint, @wrap, @usage, @testplan, @sla, @fix, Correlation Studio opens, AI terminal toggles, JSR223 refactors, MCP server starts (mcp_server_start) and MCP tool calls (mcp_tool_call). Additional counts cover provider families used for Chat and Agent messages, provider setup opens/tests/saves, model-picker actions, reasoning changes, attachments, exports, undo and redo, @ suggestion picks, JSR223 formatting and try/catch, Extract This, correlation runs and applies, CLI launches, MCP setup copies, Donate clicks, and Make it pass outcomes, guard rollbacks, and round buckets. Only fixed counter names and counts are sent, never prompts, responses, plan content, hosts, or file paths.
These counters are also sent, again as counts only:
| Counter | Meaning |
|---|---|
outcome_completed, outcome_step_limit, outcome_error, outcome_fallback | How each Agent run ended: finished, stopped at the step limit, failed with a provider or configuration error, or fell back to a plain AI response. |
outcome_tool_failed | Agent tool calls that returned an error, including calls with a missing required parameter or an unknown tool name. |
outcome_tool_declined | Agent tool calls you declined at a confirmation prompt. The tool never ran. |
fix_already_passing, fix_fixed, fix_partly_fixed, fix_not_fixed, fix_cancelled, fix_nothing_ran | Make it pass result status. |
fix_guard_rollback, fix_undo, fix_declined | Safety rollback, user undo, and declined host confirmation counts. |
fix_rounds_1, fix_rounds_2_3, fix_rounds_4p | Make it pass round-count buckets. |
fix_soft_failure, fix_assertions_added, fix_concurrency_fixed, fix_concurrency_warn | Soft failures, deterministic assertions, and multi-user check outcomes. |
outcome_cancelled | A response stopped by the user while a cancel handle was active. |
msg_<provider> and agentrun_<provider> | Chat messages and Agent runs grouped by provider family, using fixed provider labels such as anthropic, openai, opencode, or claudecode. These are counts, not model names. |
agent_steps_1_5, agent_steps_6_15, agent_steps_16p | Agent run size grouped into iteration buckets. |
setup_open, setup_test_<option>_<outcome>, setup_save_<option>, welcome_no_provider | Provider setup funnel counts. Options and outcomes come from fixed code enums. |
decide_ok_<backend>, decide_fail_<backend>_<type>, |
At most 100 counters go in one ping; any extra ones wait for the next day's ping.
A complete ping looks like this:
{
"installId": "3f1c9a2e-7b4d-4e8a-9c1f-2d5e6a7b8c9d",
"event": "daily",
"pluginVersion": "3.9.0",
"jmeterVersion": "5.6.3",
"javaVersion": "17.0.12",
"os": "Mac OS X",
"arch": "aarch64",
"provider": "anthropic",
"judgmentProvider": "openrouter:microsoft",
"model": "gpt-4o-mini",
"agentEnabled": true,
"firstRun": false,
"features": { "chat_message": 12, "agent_run": 3, "outcome_completed": 3, "tool_add_element": 5 }
}
Prompts, AI responses, chat history, error messages, tool arguments or results, custom or fine-tuned model names, API keys or tokens, base URLs or gateway settings, file names or paths, test plan content, hostnames, user names, or IP addresses. No prompts, keys, or test plan content are sent.
https://telemetry.jmeter.ai/v1/ping, a Cloudflare Worker. The server code is in telemetry-worker/.jmeter.log saying so.Any one of these switches turns telemetry off completely. Restart JMeter after changing it. When telemetry is off, nothing is sent and Feather Wand does not read or write ~/.jmeter-ai/telemetry.json.
JMeter property. Add this to user.properties (or jmeter.properties) in JMeter's bin folder:
jmeter.ai.telemetry.enabled=false
Or pass it when starting JMeter: jmeter -Jjmeter.ai.telemetry.enabled=false
DO_NOT_TRACK environment variable (the common convention used by many tools). Set it to 1 or true:
# macOS / Linux
export DO_NOT_TRACK=1
# Windows, current PowerShell session
$env:DO_NOT_TRACK = "1"
# Windows, permanently for your user (open a new terminal afterwards)
setx DO_NOT_TRACK 1
FEATHER_WAND_TELEMETRY environment variable (affects only Feather Wand). Set it to 0 or false, the same way as above.
Delete ~/.jmeter-ai/telemetry.json (on Windows, %USERPROFILE%\.jmeter-ai\telemetry.json). If telemetry is on, a new random ID is created the next time JMeter starts.
Set jmeter.ai.telemetry.url to send pings somewhere else, for example your own copy of the Worker.
Found a bug or have an idea? Open an issue.
See what's next on the project board.
.jmx before letting AI refactor it.@usage to keep an eye on it.Feather Wand is an assistant, not a replacement for engineering judgment.
| A draggable animated pet that reacts to your test runs: cheers on success, frowns on failures. Pick from quill, glim, peacock, or monkey. |
| 🤖 Agent Mode | AI autonomously edits your test plan through 21 tools with API-backed Claude, OpenAI, Gemini, DeepSeek, Grok, OpenRouter, Meta Muse, or the ChatGPT/Codex, Claude Code, and OpenCode CLI providers. |
| 🛠️ Make it pass | Run a plan once with one user, let a restricted Agent repair failures, and verify each round with a real JMeter run. |
| Jev Smart Routing | Optional TypeSafe Jev (or OpenAI Decisions, OpenRouter Decisions with Microsoft-Decision-1, or local EmbeddingGemma through Ollama) intent routing gives Agent Mode a focused tool pack, with a visible route/confidence card and automatic full-tool fallback. |
| 🔧 Searchable Model Picker | Search by model or provider, inspect context/cost/capabilities, pin favorites, reuse recent models, and hide non-chat clutter. |
| ⚙️ Fully Configurable | Customize prompts, temperature, tokens, history, CLI timeouts/sandboxing, and more via JMeter properties. |
| 🧠 Thinking & Effort | Per-model Thinking checkbox and effort dropdown in the toolbar; reasoning streams into a collapsible Thoughts card in the transcript. |
| 📎 File Attachments | Attach jmeter.log, results (.jtl/.csv), or any text file via the paperclip, drag-drop, or paste. Smart digests (percentiles, error breakdowns) instead of raw dumps - on every provider. |
| 💾 Conversation Persistence | Chats autosave to ~/.jmeter-ai/sessions/ and can be restored after a JMeter restart. Export any conversation to Markdown or HTML for your test reports. |
| 📊 Context & Cost Stats | A live readout shows context-window fill and estimated session cost, using server-reported usage when available and a marked local estimate otherwise. |
ThreadGroup.num_threads, ThreadGroup.ramp_time, ThreadGroup.scheduler=true, ThreadGroup.duration and infinite loops on its main Loop Controller. Rate targets add or update an enabled Constant Throughput Timer; positive think time adds or updates an enabled Constant Timer. Clearing an @load rate or think time disables its managed timer. Values use ${__P(load.*)} properties by default, with the prefix editable in the dialog. Ctrl+Z in chat restores the prior values and removes timers added by the command.-J properties for one generator or -R and -G properties for distributed generators. Heavy listeners, large responses and scripts may need more heap, so confirm the estimate with a short run.| 🟢 Trivial (same pattern) |
| Mistral AI | Not yet added | Yes: native function-calling, OpenAI-similar shape | 🟢 Trivial (same pattern) |
| Alibaba Qwen | Not yet added | Yes: OpenAI-compatible DashScope endpoint | 🟢 Trivial (same pattern) |
| Zhipu GLM | Not yet added | Yes: OpenAI-compatible tool calling | 🟢 Trivial (same pattern) |
| Ollama (local) | Plain chat only | Yes: ollama4j has native Tools.Tool registration for tool-capable local models (Llama 3.1+, Qwen, Mistral, ...) | 🟡 Medium (new adapter; also gated by which local model is pulled) |
| AWS Bedrock | Plain chat only | Yes: the Converse/ConverseStream API's toolConfig is provider-agnostic across every model family Bedrock hosts (Anthropic, Meta Llama, Mistral, Amazon Nova, Cohere, AI21) | 🟡 Medium, high leverage (one BedrockToolAdapter unlocks tool-calling for every Bedrock-hosted model at once) |
| Cohere (Command R+) | Not yet added | Yes, but its own (non-OpenAI-shaped) tool-use API | 🔴 Bespoke adapter needed |
| Routing and triage request timeout (both backends) |
15 |
jmeter.ai.judgment.provider | Backend for routing and triage: typesafe (Jev), openai (OpenAI Decisions, uses openai.api.key) or openrouter (OpenRouter Decisions, uses openrouter.api.key) or ollama (local embeddings, routing only, no key) | typesafe |
jmeter.ai.embeddings.model | Ollama embedding model when jmeter.ai.judgment.provider=ollama | embeddinggemma |
jmeter.ai.embeddings.local.model | Hugging Face ONNX model when jmeter.ai.judgment.provider=local | onnx-community/embeddinggemma-300m-ONNX |
jmeter.ai.embeddings.local.dir | Folder for downloaded local models | ~/.jmeter-ai/models |
jmeter.ai.embeddings.local.download | Download the local model on first use | true |
jmeter.ai.embeddings.local.base.url | Hugging Face base URL or mirror for the download | https://huggingface.co |
jmeter.ai.embeddings.local.threads | ONNX Runtime CPU threads for the local model | half the cores, at most 4 |
openai.decisions.model | OpenAI Decisions model when jmeter.ai.judgment.provider=openai | gpt-6-luna |
openrouter.api.key | OpenRouter API key; required when jmeter.ai.judgment.provider=openrouter (without a real key the backend stays off) | (empty) |
openrouter.decisions.model | OpenRouter Decisions model | microsoft/microsoft-decision-1 |
openrouter.base.url | OpenRouter API root, shared with Chat; requests go to the normalized Decisions endpoint | https://openrouter.ai/api/v1 |
| yes |
| low / medium / high |
reasoningConfig.maxReasoningEffort; off by default; high drops temperature per AWS requirement |
| Bedrock: OpenAI (gpt-oss, gpt-5.x) | gpt-5.x only | low / medium / high (+ none / xhigh / max on gpt-5.x) | reasoning_effort (snake_case) |
| Bedrock: others (deepseek, qwen, glm, kimi, ...) | always on | - | No params sent; reasoning shown in the Thoughts card when streamed |
| DeepSeek reasoner | always on | - | Reasoning is shown in the Thoughts card |
decide_slow_<backend>| Smart routing and triage Decisions call results, error category, and calls taking over two seconds. |
picker_* | Model picker opens, searches, pins, selections, and custom-id selections. |
reasoning_set, attach_*, export_md, export_html, undo, redo, intellisense_pick, jsr223_trycatch, jsr223_format, extract_opened, correlation_run, correlation_apply | UI action counts. |
cli_claudecode, cli_codex, cli_opencode, cli_copilot, cli_antigravity, cli_grok, cli_other | Successful AI CLI process starts grouped by adapter type. |
mcp_setup_copy, donate_click | MCP client setup snippet copies and Donate clicks. |
tool_<name> | Agent tool calls per built-in tool, for example tool_add_element. Tool arguments and results are never sent. |
toolfail_<name> | Agent tool calls per built-in tool that returned an error (including a missing required parameter), for example toolfail_update_element_property. Calls to a tool name that does not exist count as toolfail__unknown, and the requested name is not sent. The error message is never sent. |
route_focused, route_all_tools, route_unavailable, route_expanded | Smart routing cards: a focused tool pack, all tools (low confidence), backend unavailable, and mid-run tool expansion. |
triage_run, triage_unclassified | Failure triage advisories, and those where no failure could be classified. |
err_<provider>_<type> | Provider errors, for example err_openai_auth. Types: auth, forbidden, not_found, bad_request, rate_limit, server, timeout, unreachable, other. The error message is never sent. |