
Mastra Smoke Test
- 42 installs
- 26.9k repo stars
- Updated August 5, 2026
- mastra-ai/mastra
Helps with testing & qa tasks during AI-assisted development.
About
mastra-smoke-test is a Claude Code skill for testing & qa. It helps solo builders move faster with AI-assisted coding.
- mastra-smoke-test
- Testing & QA
- AI-coding skill
Mastra Smoke Test by the numbers
- 42 all-time installs (skills.sh)
- +3 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #1,262 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mastra-ai/mastra --skill mastra-smoke-testAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 42 |
|---|---|
| repo stars | ★ 26.9k |
| Last updated | August 5, 2026 |
| Repository | mastra-ai/mastra ↗ |
What it does
Helps with testing & qa tasks during AI-assisted development.
Files
Mastra Smoke Test
Comprehensive smoke testing for Mastra projects.
Release smoke workflows
Use progressive disclosure: stay in this file until the workflow branches, then read only the reference for the branch you are on. references/release-smoke.md is a short index if you need the full map.
Alpha release branch point
Before alpha smoke testing, identify the alpha versioning PR state. Prefer the standard Changesets release branch:
gh pr view changeset-release/main \
--json number,title,state,url,headRefName,baseRefName,isDraft,mergeable,reviewDecision,updatedAt,mergedAt,mergeCommitExpected shape:
title: chore: version packages (alpha)
head: changeset-release/main
base: mainIf that branch lookup fails, search open and recently merged PRs:
gh pr list --state open --search 'version packages alpha in:title' --limit 20
gh pr list --state merged --search 'version packages alpha in:title' --limit 20Then branch:
- If the versioning PR is open, read
references/alpha-versioning-pr.md. - If the versioning PR is merged, read
references/alpha-publish.md. - If no versioning PR exists, report that and wait for the scheduled alpha versioning flow or user direction.
Do not create the alpha smoke-test project until the automatic alpha publish workflow has completed and the intended packages are installable.
Stable release branch point
If the user is running the stable/full release workflow, read references/stable-release-smoke.md. If that workflow fails after some packages publish, switch to references/stable-partial-publish-recovery.md.
Scope and targeted checks
After the release package is published and before running smoke tests, read references/release-scope-discovery.md to identify changed features. Use the default generated project for the baseline checklist, then add targeted checks for changed features the generated project does not exercise.
When scope discovery identifies a branch:
- For general changed-feature coverage, read
references/targeted-feature-smoke.md. - For storage/provider schema or migration changes, read
references/storage-provider-migration-smoke.md.
⚠️ Mandatory Test Checklist
Use `task_write` to track progress. Run ALL tests unless --test specifies otherwise.
Do not skip tests unless you hit an actual blocker. "Seemed complex" or "wasn't sure" are not valid reasons. Attempt everything - only stop a test when you literally cannot proceed. Report what you tried and what blocked you.
| # | Test | Reference | When Required |
|---|---|---|---|
| 1 | Setup | references/tests/setup.md | Always |
| 2 | Agents | references/tests/agents.md | --test agents or full |
| 3 | Tools | references/tests/tools.md | --test tools or full |
| 4 | Workflows | references/tests/workflows.md | --test workflows or full |
| 5 | Traces | references/tests/traces.md | --test traces or full |
| 6 | Scorers | references/tests/scorers.md | --test scorers or full |
| 7 | Memory | references/tests/memory.md | --test memory or full |
| 8 | MCP | references/tests/mcp.md | --test mcp or full |
| 9 | Errors | references/tests/errors.md | --test errors or full |
| 10 | Studio Deploy | references/tests/studio.md | --test studio (cloud only) |
| 11 | Server Deploy | references/tests/server.md | --test server (cloud only) |
Execution Flow
1. Read the reference file for each test you're about to run 2. Execute the steps in that reference file 3. Mark the test complete before moving to the next
Partial Testing (--test)
If --test is provided:
1. Always run Setup (step 1) 2. Run only the specified test(s) 3. Skip other tests
Example: --test agents,traces → Run steps 1, 2, and 5 only.
Local Studio Browser Smoke
For local release smoke tests, do both API/curl checks and a Studio browser pass unless --skip-browser is explicitly requested or browser access is genuinely blocked. API checks prove runtime endpoints work; browser checks prove the Playground/Studio UI can load, submit forms, and display results.
Before opening the browser:
1. Confirm the dev server is alive on the expected port:
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:4111
lsof -i :4111 || true2. If the process died, restart it from the generated project and wait for readiness:
cd "$SMOKE_DIR/smoke-project"
pnpm run dev > "$SMOKE_DIR/logs/dev-server-browser.log" 2>&1 &
for i in {1..60}; do
code=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:4111 || true)
[ "$code" = 200 ] && break
sleep 1
done3. Use browser tools to navigate to http://localhost:4111. If networkidle times out but domcontentloaded succeeds and the UI is usable, continue and note the timeout.
Recommended browser task list:
1. Verify Studio shell loads
2. Smoke test agent chat UI
3. Smoke test tools UI
4. Smoke test workflows UI
5. Smoke test observability, scorers, and MCP pages
6. Report browser smoke resultsRun these page checks:
| Area | Route | What to verify |
|---|---|---|
| Studio shell | / or /agents | Sidebar/nav visible, Mastra version visible, no crash/error overlay |
| Agents | /agents → agent chat | Agent list shows expected agent, chat input is visible, sending What's the weather in Tokyo? returns a coherent response, tool call badge/result appears when expected |
| Tools | /tools → tool detail | Tool list shows get-weather, input form renders, submitting a city such as Paris displays JSON result with weather fields |
| Workflows | /workflows → workflow detail | Workflow list shows weather-workflow, graph/details render, running with a city such as Berlin completes as success, steps show timings/output controls |
| Traces | /observability | Recent agent/workflow traces appear, including runs triggered during the browser pass |
| Scorers | /scorers | Registered scorers appear with names/descriptions, e.g. Tool Call Accuracy, Completeness, Translation Quality |
| MCP | /mcps | Page loads. Empty state is a pass for default templates: No MCP Servers yet |
If a browser interaction does not expose enough text in the accessibility snapshot, inspect document.body.innerText or take a screenshot, then record the visible evidence. Do not rely only on API output for browser smoke.
Append browser results to $SMOKE_DIR/smoke-report.md with a separate section, for example:
## Studio Browser Smoke Results
| Area | Result | Evidence |
| ------------ | ------ | ----------------------------------------------------------------- |
| Studio shell | PASS | Browser loaded localhost:4111; sidebar/nav visible; version shown |
| Agents UI | PASS | Weather Agent chat returned Tokyo weather and displayed tool call |
| Tools UI | PASS | get-weather form returned Paris weather JSON |
| Workflows UI | PASS | weather-workflow Berlin run completed as success |
| Traces UI | PASS | Recent agent/workflow traces listed |
| Scorers UI | PASS | Expected scorers listed |
| MCP UI | PASS | Expected empty MCP state shown |Call out separately whether browser smoke was local Studio only or cloud Studio/deployed server.
---
Usage
# Full smoke test
smoke test --env local --existing-project ~/my-app
smoke test --env staging -d ~/projects -n test-app
# Partial testing
smoke test --env local --existing-project ~/my-app --test agents
smoke test --env production --existing-project ~/my-app --test studio,server,traces
# Multi-environment: same project, different targets
smoke test --env staging --existing-project ~/my-app # Uses .mastra-project-staging.json
smoke test --env production --existing-project ~/my-app # Uses .mastra-project.jsonMulti-Environment Support
One project can target all environments using separate config files:
| Environment | Config File | What Happens |
|---|---|---|
| Local | N/A | pnpm dev → localhost:4111 |
| Staging | .mastra-project-staging.json | Deploys to staging.mastra.cloud |
| Production | .mastra-project.json | Deploys to mastra.cloud |
See references/tests/setup.md for setup details.
Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
--env | Yes | - | local, staging, production |
--directory | \* | ~/mastra-smoke-tests | Parent dir for new project |
--name | \* | - | Project name |
--existing-project | \* | - | Path to existing project |
--tag | No | latest | Version tag (e.g., alpha) |
--pm | No | pnpm | Package manager |
--llm | No | openai | LLM provider |
--db | No | libsql | Storage: libsql, pg, turso |
--test | No | (full) | Specific test(s) to run |
--browser-agent | No | false | Add browser agent |
--skip-browser | No | false | Curl-only (no browser UI) |
--byok | No | false | Test bring-your-own-key |
\* Either --directory + --name OR --existing-project required
Test Options (--test)
| Option | Description | Environments |
|---|---|---|
agents | Agent page and chat | All |
tools | Tools page and execution | All |
workflows | Workflows page and run | All |
traces | Observability/traces | All |
scorers | Evaluation/scorers page | All |
memory | Conversation persistence | All |
mcp | MCP servers page | All |
errors | Error handling | All |
studio | Studio deploy only | Cloud |
server | Server deploy only | Cloud |
Prerequisites
All environments:
- Node.js + package manager
- LLM API key in env or
.env
Local (`--env local`):
- Browser tools enabled (
/browser on)
Cloud (`--env staging/production`):
- Mastra platform account
Quick Start Flow
1. Setup → Read references/tests/setup.md, create/verify project
2. Start → `pnpm run dev` (local) or deploy (cloud)
3. Test → For each test, read its reference file and execute
4. Verify → Check all items in reference file's checklist
5. Report → Summarize pass/fail for each testReferences
| File | Purpose |
|---|---|
references/tests/*.md | Detailed steps for each mandatory test |
references/release-smoke.md | Short release-smoke reference index |
references/alpha-versioning-pr.md | Open alpha versioning PR readiness |
references/alpha-publish.md | Merged alpha PR publish verification |
references/stable-release-smoke.md | Stable publish and final stable smoke |
references/stable-partial-publish-recovery.md | Partial stable publish recovery |
references/release-scope-discovery.md | Release PR scope discovery and planning |
references/targeted-feature-smoke.md | Targeted changed-feature smoke patterns |
references/storage-provider-migration-smoke.md | Storage/provider migration smoke pattern |
references/local-setup.md | Local dev server setup |
references/cloud-deploy.md | Cloud deploy details |
references/cloud-advanced.md | BYOK, storage testing |
references/common-errors.md | Troubleshooting |
references/gcp-debugging.md | Infrastructure debugging |
references/architecture.md | Smoke-test architecture notes |
references/environment-variables.md | Environment variable setup |
scripts/test-server.sh | Server API test script |
scripts/discover-release-scope.sh | Release PR scope discovery |
scripts/check-versioning-pr.sh | Alpha versioning PR spot-check helper |
Platform Dashboards
- Production:
https://projects.mastra.ai - Staging:
https://projects.staging.mastra.ai
For Gateway API testing (memory, threads, BYOK via gateway), use platform-smoke-test.Result Reporting
After testing, provide:
## Smoke Test Results
**Environment**: local/staging/production
**Project**: <name>
| Test | Status | Notes |
| ------ | ------ | ----- |
| Setup | ✅/❌ | |
| Agents | ✅/❌ | |
| Tools | ✅/❌ | |
| ... | | |
**Issues Found**: (list any)
**Warnings**: (list any deploy/runtime warnings)
**Skipped Tests**: (list with reason - e.g., "Server Deploy - not applicable in local environment")Alpha Publish Verification
Use this after the alpha versioning PR has merged.
Confirm the automatic alpha publish workflow
Merging the alpha versioning PR to main automatically kicks off the Publish to npm workflow on a push event. Do not advise manually starting the alpha publish workflow.
Check for the automatic run:
gh run list --workflow "Publish to npm" --branch main --limit 5Inspect the newest run. The alpha path should be the prerelease job, with snapshot, stable, and enter_prerelease skipped:
gh run view <run-id> --json name,event,status,conclusion,workflowName,headBranch,headSha,jobs,url --jq .Watch it:
gh run watch <run-id>Offer to open the prerelease run in the user's browser when helpful:
gh run view <run-id> --webUse gh run view --web instead of browser automation because it opens the page in the user's normal browser/session.
If no automatic publish run starts after the merge, report that as a release automation issue and ask the user before taking any recovery action.
Confirm alpha is published
Before smoke testing, confirm the alpha package is installable:
npm view @mastra/core@alpha version
npm view mastra@alpha version
npm view create-mastra@alpha versionOnly then create the smoke-test project with the alpha tag/version.
Next step
After alpha packages are published, continue with release scope discovery in references/release-scope-discovery.md, then run the mandatory local checklist from SKILL.md plus any targeted checks.
Alpha Versioning PR Readiness
Use this only when the alpha versioning PR is still open.
Open the PR for the user
Offer to open the open versioning PR in the user's browser when helpful:
gh pr view <pr-number> --webUse gh pr view --web instead of browser automation because it opens the page in the user's normal browser/session.
Check readiness
gh pr view <pr-number> --json number,title,isDraft,mergeable,reviewDecision,url
gh pr checks <pr-number> --watch=falseIf checks are still running, wait:
gh pr checks <pr-number> --watch --interval 30Spot check the versioning diff
Before telling the user it is ready to merge, spot check the versioning diff yourself and advise the user to spot check it too. Prefer the helper script:
.claude/skills/mastra-smoke-test/scripts/check-versioning-pr.sh <pr-number> --workspace "$SMOKE_DIR"Or inspect manually:
gh pr diff <pr-number> --name-only
gh pr view <pr-number> --json files --jq '.files[].path' \
| rg '(package\.json|CHANGELOG\.md|\.changeset/)'
gh pr diff <pr-number>Check:
- package versions look intentional
- there are no unintended major version bumps or breaking-change releases
- changelog entries match the PRs expected in the alpha
- CI is green and the PR is not a draft
Summarize your spot-check findings for the user before they approve/merge.
Merge guidance
The user must review, approve, and merge the versioning PR. The agent may check readiness and advise, but should not merge the PR without explicit user instruction.
If the PR is ready, tell the user to approve and merge it in GitHub, or ask whether they want you to merge it. If branch protection rejects the merge because review is required, stop and ask for the required approval.
After the PR merges, continue with references/alpha-publish.md.
Architecture Overview
For detailed internal architecture documentation, see the Notion page: [Architecture Overview](https://www.notion.so/33bebffbc9f8816280b8eb09c72fed4f)
High-Level Summary
When you deploy a Studio or Server to Mastra platform:
1. Deploy → CLI sends your project to platform services 2. Token → Platform signs a JWT for your deployment 3. Traces → Your deployment sends traces using that token 4. Storage → Traces are stored and queryable in Studio UI
Key Points for Testing
- Both Studio and Server deploys generate traces
- Traces should appear in Studio's Observability tab
- If traces don't appear, check deploy logs for warnings
Troubleshooting
If you have GCP access and need to debug infrastructure issues, see the internal docs in Notion.
Cloud Advanced Testing
Advanced test flows for --env staging and --env production.
Note: For account creation, team invites, and RBAC testing, use the platform-smoke-test skill.Those features are part of the platform dashboard (projects.mastra.ai/gateway.mastra.ai), not deployed Studio/Server projects.
BYOK Testing (--byok)
Tests bring-your-own-key functionality for deployed servers.
This tests passing your own API key to your deployed Mastra server (not the Gateway API).
Via HTTP Header
# Test with OpenAI key via header
curl -X POST https://<project>.server.mastra.cloud/api/agents/weather-agent/generate \
-H "Content-Type: application/json" \
-H "x-openai-api-key: sk-your-openai-key" \
-d '{"messages": [{"role": "user", "content": "What is the weather in Tokyo?"}]}'
# For staging environment
curl -X POST https://<project>.server.staging.mastra.cloud/api/agents/weather-agent/generate \
-H "Content-Type: application/json" \
-H "x-openai-api-key: sk-your-openai-key" \
-d '{"messages": [{"role": "user", "content": "What is the weather in Tokyo?"}]}'Supported headers:
x-openai-api-key- OpenAIx-anthropic-api-key- Anthropicx-google-api-key- Google
Via Project Settings
1. Navigate to deployed Studio → Settings → API Keys 2. Add OpenAI/Anthropic/Google API key 3. Verify agents use the configured key instead of default
Storage Backend Testing (--db)
Tests that the project works with the selected database backend.
LibSQL (Default)
# No additional setup required
# Uses local SQLite file in developmentPostgreSQL (--db pg)
Requires DATABASE_URL environment variable:
export DATABASE_URL="postgresql://user:pass@host:5432/db"Turso (--db turso)
Requires Turso credentials:
export TURSO_DATABASE_URL="libsql://your-db.turso.io"
export TURSO_AUTH_TOKEN="your-token"Extended Test Verification Checklist
| Category | Test | Expected Result | Status |
|---|---|---|---|
| BYOK | Header key | Agent uses key from header | ⬜ |
| BYOK | Settings key | Agent uses key from project settings | ⬜ |
| Storage | DB connector | Project works with selected DB | ⬜ |
| Storage | Data persists | Data survives server restart | ⬜ |
Cloud Deployment Setup
Instructions specific to --env staging and --env production testing.
Prerequisites
- Mastra platform account with deploy access
pnpx(ornpx) available- For debugging: GCP Console access (see
gcp-debugging.md)
Multi-Environment Config
Deploy to staging and production from the same project using separate config files:
| Environment | Config File | Platform API URL | Deploy URLs |
|---|---|---|---|
| Production | .mastra-project.json | https://platform.mastra.ai | <project>.studio.mastra.cloud |
| Staging | .mastra-project-staging.json | https://platform.staging.mastra.ai | <project>.studio.staging.mastra.cloud |
Each environment gets its own project ID, so they don't interfere.
Environment Setup
Set the platform URL based on target environment:
# For production
export MASTRA_PLATFORM_API_URL=https://platform.mastra.ai
# For staging
export MASTRA_PLATFORM_API_URL=https://platform.staging.mastra.aiLLM API Key
Ensure .env has the required API key:
| Provider | Environment Variable |
|---|---|
| openai | OPENAI_API_KEY |
| anthropic | ANTHROPIC_API_KEY |
| groq | GROQ_API_KEY |
GOOGLE_GENERATIVE_AI_API_KEY |
Authenticate with Platform
Check Existing Credentials
Before triggering a browser login, check if credentials exist and are valid:
# Check if credentials file exists
cat ~/.mastra/credentials.json | jq '{email: .user.email, organizationId}'
# Verify token is still valid
TOKEN=$(jq -r '.token' ~/.mastra/credentials.json)
ORG_ID=$(jq -r '.currentOrgId // .organizationId' ~/.mastra/credentials.json)
curl -s "$MASTRA_PLATFORM_API_URL/v1/auth/verify" \
-H "Authorization: Bearer $TOKEN" \
-H "x-organization-id: $ORG_ID" | jq '.user.email'Token Refresh (if expired)
WorkOS tokens expire in 5 minutes, but can be refreshed without re-login:
REFRESH_TOKEN=$(jq -r '.refreshToken' ~/.mastra/credentials.json)
curl -s "$MASTRA_PLATFORM_API_URL/v1/auth/refresh-token" \
-X POST \
-H "Content-Type: application/json" \
-d "{\"refreshToken\": \"$REFRESH_TOKEN\"}"See references/tests/traces.md for the full get_valid_token helper function.
Login (only if refresh fails)
⚠️ Always warn the user before running this — it opens a browser:
# Logout first for clean state (optional)
pnpx mastra@latest auth logout
# Login to target environment
pnpx mastra@latest auth loginThis opens a browser for OAuth. Complete the login flow.
Verify Organization
The browser may default to a different account/org. Always verify after login:
cat ~/.mastra/credentials.json | jq '{email: .user.email, organizationId}'Deploy Studio
# Production (uses .mastra-project.json by default)
pnpx mastra@latest studio deploy -y
# Staging (specify config file)
pnpx mastra@latest studio deploy --config .mastra-project-staging.json -yWait for deployment. Note the URL from output:
- Production:
https://<project>.studio.mastra.cloud - Staging:
https://<project>.studio.staging.mastra.cloud
Verify: Open URL, sign in, confirm Studio UI loads.
Deploy Server
# Production
pnpx mastra@latest server deploy -y
# Staging
pnpx mastra@latest server deploy --config .mastra-project-staging.json -yThe -y flag auto-confirms settings.
Note the URL from output:
- Production:
https://<project>.server.mastra.cloud - Staging:
https://<project>.server.staging.mastra.cloud
Verify health:
# Staging
curl https://<project>.server.staging.mastra.cloud/health
# Production (no environment subdomain)
curl https://<project>.server.mastra.cloud/health
# Expected: {"success":true}Test Server API
Use the helper script:
.claude/skills/mastra-smoke-test/scripts/test-server.sh <server-url> [agent-id] [message]
# Examples
.claude/skills/mastra-smoke-test/scripts/test-server.sh https://my-app.server.staging.mastra.cloud
.claude/skills/mastra-smoke-test/scripts/test-server.sh https://my-app.server.mastra.cloud weather-agent "Weather in Tokyo?"The script:
1. Checks /health endpoint 2. Calls agent's /generate endpoint 3. Parses and displays response 4. Exits with error if checks fail
Verify Server Traces in Studio
Critical step — verifies the full trace pipeline works:
1. Make a Server API call (using script or curl) 2. Return to Studio UI → Observability → Traces 3. Refresh the page 4. Verify traces from Server API call appear
If traces don't appear, see gcp-debugging.md.
Server Trace Verification
| Source | How to Identify |
|---|---|
| Studio traces | Generated from Studio UI interactions |
| Server traces | Generated from direct API calls to deployed server |
Both should appear in the Studio's Traces page. If only Studio traces appear, there's a trace pipeline issue.
Testing Custom API Routes (Deployed)
After deploying a server with custom routes:
# Staging
curl https://<project>.server.staging.mastra.cloud/hello
# Production
curl https://<project>.server.mastra.cloud/hello
# Expected: {"message":"Hello from custom route!"}Browser Agent (Deployed)
When testing browser agents in deployed environments:
- Set
headless: truein the browser config - Browser runs server-side in the deployed container
Quick Commands Reference
# === Environment ===
export MASTRA_PLATFORM_API_URL=https://platform.mastra.ai # production
export MASTRA_PLATFORM_API_URL=https://platform.staging.mastra.ai # staging
# === Auth (warn user before login - opens browser!) ===
pnpx mastra@latest auth login
pnpx mastra@latest auth logout
# === Deploy (production) ===
pnpx mastra@latest studio deploy -y
pnpx mastra@latest server deploy -y
# === Deploy (staging) ===
pnpx mastra@latest studio deploy --config .mastra-project-staging.json -y
pnpx mastra@latest server deploy --config .mastra-project-staging.json -y
# === Test (use actual URLs from deploy output) ===
curl https://<project>.server.mastra.cloud/health # production
curl https://<project>.server.staging.mastra.cloud/health # staging
# === Agent call ===
curl -X POST <server-url>/api/agents/<agent-id>/generate \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello"}]}'
# === Check traces directly ===
TOKEN=$(jq -r '.token' ~/.mastra/credentials.json)
PROJECT_ID=$(jq -r '.projectId' .mastra-project.json)
ORG_ID=$(jq -r '.organizationId' .mastra-project.json)
curl -s "https://mobs-query-vgvrl5lbxq-uc.a.run.app/api/observability/traces?resourceId=$PROJECT_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "x-organization-id: $ORG_ID" | jq '.traces | length'Troubleshooting
Wrong Organization on Login
The browser may default to a different account/org. Always verify after login:
cat ~/.mastra/credentials.json | jq '{email: .user.email, organizationId}'Token Expired (401 errors)
WorkOS tokens expire after 5 minutes. Check token validity before re-logging in — use the refresh token first. See the get_valid_token helper in references/tests/traces.md.
"Session expired" errors in Studio
Known issue with cookie domain mismatch. The Studio may need re-authentication periodically.
Custom Routes Not Working
Custom routes must use apiRoutes (not routes) in the server config:
server: {
apiRoutes: [helloRoute], // ✅ Correct
// routes: [helloRoute], // ❌ Wrong - silently fails
}This is a common typo that causes routes to silently not register.
CORS Errors
Server deploys inject CORS config via SERVER_WRAPPER. If you see CORS errors:
1. Check MASTRA_CORS_ORIGIN env var is set correctly on the deploy 2. Verify the origin domain matches the studio domain pattern
Server traces not appearing
1. Check mobs-collector logs (GCP Console)
POST 200= traces receivedPOST 401= JWT auth failedPOST 404= wrong endpoint
2. If 401 invalid signature: JWT_SECRET mismatch between services
3. If "mastra-cloud-observability-exporter disabled" in deploy logs:
JWT_SECRETnot configured on platform-api- Server can't get
MASTRA_CLOUD_ACCESS_TOKEN
See gcp-debugging.md for detailed debugging steps.
Deploy fails with auth error
pnpx mastra@latest auth logout
pnpx mastra@latest auth loginThen retry deploy.
Common Errors and Fixes
For detailed error documentation with infrastructure context, see the Notion page: [Common Errors and Fixes](https://www.notion.so/33bebffbc9f881da8415c12fae971872)
Quick Troubleshooting
Traces Not Appearing
| Symptom | Likely Cause | Quick Fix |
|---|---|---|
| Server traces missing, Studio traces work | Server has old token | Redeploy server: pnpx mastra@latest server deploy -y |
| No traces at all | Deploy warning about observability | Check deploy logs for MASTRA_CLOUD_ACCESS_TOKEN warning |
| "Session expired" in Studio logs | Known cookie domain issue | Re-authenticate in Studio |
Deploy Issues
| Symptom | Likely Cause | Quick Fix |
|---|---|---|
| Deploy hangs/times out | Network or platform issue | Check if deploy succeeded at projects.mastra.ai, retry |
| "Cannot determine project name" | Missing package.json | Run from project root with valid package.json |
When to Escalate
Contact the platform team if:
- Redeploy doesn't fix trace issues
- You see
401or404errors in deploy logs - Issues persist across multiple projects
For infrastructure debugging (requires GCP access), see the detailed Notion docs.
Environment Variables Reference
For detailed internal infrastructure variables, see the Notion page: [Environment Variables Reference](https://www.notion.so/33bebffbc9f881e694f5f8af9df85a4a)
Variables You Need to Set
For Smoke Testing
| Variable | Purpose | When to Set |
|---|---|---|
MASTRA_PLATFORM_API_URL | Target staging vs production | Before mastra auth login |
OPENAI_API_KEY | LLM API access | Before running agents |
ANTHROPIC_API_KEY | Alternative LLM | If using Anthropic |
Environment Values
Staging:
export MASTRA_PLATFORM_API_URL=https://platform.staging.mastra.aiProduction (default):
export MASTRA_PLATFORM_API_URL=https://platform.mastra.ai
# Or just don't set it — production is the defaultVariables Set Automatically
These are injected by the platform during deployment — you don't need to set them:
MASTRA_CLOUD_ACCESS_TOKEN— JWT for trace authenticationMASTRA_CLOUD_TRACES_ENDPOINT— Where traces are sent
Checking Your Environment
# Verify which environment you're targeting
echo $MASTRA_PLATFORM_API_URL
# Check if you're authenticated
mastra auth statusGCP Debugging Guide
Internal: This guide requires GCP Console access. Contact a team member with infrastructure access if you need help.
For detailed GCP debugging instructions, see the Notion page: [GCP Debugging Guide](https://www.notion.so/33bebffbc9f881758e0bd10be1ef7e84)
Quick Reference
When to Check GCP Logs
- Server traces not appearing in Studio
- Deploy failing silently
- Authentication/session issues
What You'll Need
- GCP Console access to the appropriate project (staging or production)
- Knowledge of which service to check based on the symptom
Who to Contact
If you don't have GCP access, reach out to the platform team for help debugging infrastructure issues.
Local Development Setup
Instructions specific to --env local testing.
Prerequisites
- Browser tools enabled via
/browser on - Works with either Stagehand (AI-powered) or AgentBrowser (deterministic) providers
If browser tools are not available, run /browser to configure.
Environment Variables
Based on the selected LLM provider, ensure the API key is available:
| Provider | Environment Variable |
|---|---|
| openai | OPENAI_API_KEY |
| anthropic | ANTHROPIC_API_KEY |
| groq | GROQ_API_KEY |
GOOGLE_GENERATIVE_AI_API_KEY | |
| cerebras | CEREBRAS_API_KEY |
| mistral | MISTRAL_API_KEY |
Check order:
1. Global environment: echo $<ENV_VAR_NAME> 2. Project .env file 3. Ask user only if not found
Start Development Server
1. Check for a zombie on :4111 first
mastra dev auto-increments the port if :4111 is already in use (e.g. :4112, :4113). If you don't notice, your subsequent curls will hit the wrong project (any earlier test session left running). Always check:
lsof -i :4111
# If a node process is listening, kill it before starting:
kill $(lsof -ti :4111) 2>/dev/null2. Start
cd <project-directory>
<pm> run devServer starts on http://localhost:4111. Wait for "Mastra API running" in the output and confirm the printed URL before running tests:
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost:4111/api/agents
# HTTP 200 → dev server upIf the dev server prints `url: "http://localhost:4112/api"` instead of `:4111`, port 4111 was already taken. Stop, kill the zombie, and restart, or pass --port 4111 if supported — otherwise all the curl examples in the test references will target the wrong server.
Local Observability Setup
Verify observability is configured before testing traces:
1. Check src/mastra/index.ts
create-mastra now scaffolds PinoLogger + Observability (not the older createLogger / OtelConfig), and wires a MastraCompositeStore with a default LibSQL store plus a DuckDB store for the observability domain:
import { Mastra } from '@mastra/core/mastra';
import { PinoLogger } from '@mastra/loggers';
import { LibSQLStore } from '@mastra/libsql';
import { DuckDBStore } from '@mastra/duckdb';
import { MastraCompositeStore } from '@mastra/core/storage';
import {
Observability,
MastraStorageExporter,
MastraPlatformExporter,
SensitiveDataFilter,
} from '@mastra/observability';
export const mastra = new Mastra({
// ... agents, workflows, scorers
storage: new MastraCompositeStore({
id: 'composite-storage',
default: new LibSQLStore({ id: 'mastra-storage', url: 'file:./mastra.db' }),
domains: {
observability: await new DuckDBStore().getStore('observability'),
},
}),
logger: new PinoLogger({ name: 'Mastra', level: 'info' }),
observability: new Observability({
configs: {
default: {
serviceName: 'mastra',
exporters: [new MastraStorageExporter(), new MastraPlatformExporter()],
spanOutputProcessors: [new SensitiveDataFilter()],
},
},
}),
});2. Check Dependencies
Verify package.json includes:
@mastra/observability@mastra/loggers@mastra/libsql(default store) and@mastra/duckdb(observability domain)
3. Check Dev Server Output
When starting the dev server, look for the version banner and Studio URL, e.g. mastra 1.13.0-alpha.4 ready in … followed by Studio at http://localhost:4111 and the API at http://localhost:4111/api. Should NOT see MASTRA_CLOUD_ACCESS_TOKEN not set (that's for cloud only).
Troubleshooting Local Traces
If traces are missing:
1. Verify telemetry config — Check telemetry is configured in Mastra instance 2. Restart dev server — Config changes require restart 3. Check browser console — Look for OTel export errors 4. Check dependencies — Ensure @mastra/observability is installed
Testing Custom API Routes
After adding a custom route (see main SKILL.md):
curl http://localhost:4111/hello
# Expected: {"message":"Hello from custom route!"}Browser Agent Testing (Local)
When testing browser agents locally, you'll experience "browserception" — your MastraCode browser watching the project's agent browser.
Ensure Playwright browsers are installed:
<pm> exec playwright install chromiumNotes
- Local traces are stored in-memory by default
- Traces persist only while the dev server is running
- For persistent traces, configure a storage backend
Release Scope Discovery
Before running release smoke tests, determine what changed since the last release. Do not rely only on the default smoke-test checklist; use the release diff to decide whether targeted checks are needed.
0. Create a dated smoke-test workspace
Create a date-scoped workspace before collecting release artifacts. Keep the generated Mastra project, PR list, logs, and smoke reports together.
SMOKE_DATE=$(date +%F)
SMOKE_DIR="$HOME/mastra-smoke-tests/$SMOKE_DATE"
mkdir -p "$SMOKE_DIR/logs"If $HOME/mastra-smoke-tests is outside the repo sandbox, request filesystem access before writing there.
Expected layout:
~/mastra-smoke-tests/YYYY-MM-DD/
merged-prs.tsv
smoke-scope.md
smoke-report.md
logs/
smoke-project/
stable-smoke-project/Prefer the helper script when available:
.claude/skills/mastra-smoke-test/scripts/discover-release-scope.sh --release-tag '@mastra/core@1.28.0'The script writes merged-prs.tsv and a starter smoke-scope.md to the workspace.
1. Identify the last release baseline
Find the previous stable release tag and timestamp. Prefer the tag/release that corresponds to the package being released, usually @mastra/core@<version> for monorepo releases.
gh release list --limit 20
gh release view '@mastra/core@<version>' --json tagName,publishedAt,createdAt,targetCommitish,url
git show -s --format='%H %cI %s' '@mastra/core@<version>'Use the tag commit date or release createdAt as the cutoff for merged PR discovery. State which cutoff you used.
2. List merged PRs since the cutoff
Export merged PRs since the baseline into the dated workspace. Exclude Dependabot unless the release specifically needs dependency smoke coverage.
gh pr list \
--state merged \
--search 'merged:>=YYYY-MM-DDTHH:MM:SSZ -author:app/dependabot' \
--limit 200 \
--json number,title,author,mergedAt,labels,url \
--jq '.[] | [.number, .mergedAt, .author.login, .title, .url] | @tsv' \
> "$SMOKE_DIR/merged-prs.tsv"If the release has more than 200 PRs, paginate or narrow by date ranges until the full set is captured. Cross-check with first-parent merge commits when needed:
git log --first-parent --oneline '@mastra/core@<previous>'..origin/main3. Categorize the PRs
Group PRs by runtime surface area and smoke implication:
| Category | Match criteria | Smoke implication |
|---|---|---|
| Core agent loop/streaming | packages/core/src/agent, stream/resume, message conversion, loop control | Agent generate + stream/resume + memory/thread checks |
| Tools/processors | tool execution, dynamic tools, approval, processors | Tool list/execute + agent tool-call path + changed processor behavior |
| Workflows | workflows, suspend/resume, start-async, workflow output | Workflow API + Studio workflow run + traces |
| Memory/observability | memory, threads, traces, scorers, logs, metrics | Memory persistence + trace/span/scores verification |
| Server/adapters/API | server, route registration, Express/Fastify/Hono/Koa adapters | /health, /api/*, custom route, invalid route checks |
| CLI/create-mastra | create-mastra, templates, generated deps | Fresh project install from target tag/version |
| Studio/Playground UI | packages/playground-ui, packages/playground | Browser smoke for affected pages |
| Agent Builder/auth/stored entities | stored agents/skills, auth, visibility, starring, avatar, permissions | Authenticated staging/cloud checks when local cannot cover |
| MCP/A2A | MCP server/client/schema, A2A protocol | MCP endpoints plus configured server/client if changed |
| Storage/providers | Postgres, LibSQL, Redis, S3, Azure, vector stores | Provider install/import and targeted backend smoke when feasible |
| Mastra Code/TUI | mastracode, subagents, slash commands | Separate Mastra Code smoke; default create-mastra does not cover it |
| Docs/examples only | docs, examples | Docs/example validation; no runtime smoke unless example is executable |
4. Convert categories into a smoke plan
Use full smoke as the baseline, but do not stop at the default generated-project happy path. The goal is to prove the actual changed feature or bug fix works in the published package, not just that a nearby happy path still works.
For every material PR, ask:
1. What user-visible behavior, API behavior, persistence behavior, or integration path changed? 2. Does the generated smoke project execute that exact path? 3. If not, what is the smallest targeted check that proves the changed behavior? 4. What evidence will show the fix worked, not merely that the app did not crash?
If the default project does not exercise the changed feature, add a targeted check or explicitly record why it cannot be tested in this environment.
For targeted check patterns, read references/targeted-feature-smoke.md. For storage/provider schema or migration changes, read references/storage-provider-migration-smoke.md.
Add a Coverage vs Changes table to $SMOKE_DIR/smoke-scope.md before testing:
| Feature / PRs | Generated project covers it? | Targeted check to run | Result / reason omitted |
|---|---|---|---|
Example: resume-stream | No | Call resume-stream after starting a stream and verify resumed chunks/final response | PASS/FAIL or blocked reason |
| Example: tool approval change | No | Configure a tool with requireApproval, trigger it from an agent, approve/reject, verify the changed approval behavior | PASS/FAIL or blocked reason |
| Example: Playground save persistence | No | Edit the affected Studio/Agent Builder form, save, reload/refetch, verify the changed field persists | PASS/FAIL or blocked reason |
| Example: PG OM migration column | No | Run Postgres in Docker, configure smoke project with @mastra/pg, drop old/missing column, restart, verify migration restores it and memory/OM writes succeed | PASS/FAIL or blocked reason |
| Example: default weather tool | Yes | Agent/tool smoke | PASS |
Write the scope analysis to $SMOKE_DIR/smoke-scope.md before running tests so it is not trapped in terminal output. Include:
- release baseline tag and cutoff timestamp
- command used to collect PRs
- categorized PR table with PR number, title, author, merged time, and category
- targeted smoke plan derived from the categories
- coverage-vs-changes table showing what the generated project covers naturally and what needs targeted checks
- any omitted PRs/commits and why, including Dependabot, direct non-PR commits, cloud-only features, missing credentials, or product areas outside create-mastra
Report the category summary and the coverage-vs-changes summary before running tests so the user can see why each targeted check is included and where the default smoke project is insufficient.
Release Smoke Index
This file is intentionally short. Use the smallest reference needed for the branch you are on.
Alpha release
Start in the top-level SKILL.md under Release smoke workflows. It includes the commands to identify whether the alpha versioning PR is open, merged, or missing.
- If the alpha versioning PR is open, read
references/alpha-versioning-pr.md. - If the alpha versioning PR is merged, read
references/alpha-publish.md. - After alpha packages publish, read
references/release-scope-discovery.md.
Stable release
- For normal stable publish monitoring and final smoke, read
references/stable-release-smoke.md. - If stable publish partially fails, read
references/stable-partial-publish-recovery.md.
Targeted checks
- For changed features not covered by the generated project, read
references/targeted-feature-smoke.md. - For storage/provider schema or migration changes, read
references/storage-provider-migration-smoke.md.
Stable Partial Publish Recovery
Use this only when the stable publish fails after some packages have already published.
Treat it as a partial release. Do not create a new versioning PR or bump versions. First identify the failed step and whether npm latest is split across old and new versions.
Recovery flow
1. Record which packages already reached npm latest and which are still old. 2. Rerun the same stable Publish to npm workflow from the same release commit on main. 3. After the rerun succeeds, verify all intended package versions are on npm latest. 4. Verify release git tags were created after the successful publish. 5. Only then run final stable smoke against create-mastra@latest.
Tag behavior
The Add tags step runs after publish:
pnpm changeset-cli tag
git push origin --tagsIt creates and pushes git tags only for packages Changesets considers part of the current release/version bump, not every package in the monorepo. The tags look like @mastra/core@1.29.0, mastra@1.7.0, and create-mastra@1.7.0. If the first publish attempt fails before Add tags, the rerun should create tags for the full changed-package release set after npm publish completes.
Verify changed-package tags, not every workspace package:
git fetch --tags
# Spot-check release-critical tags
git tag -l '@mastra/core@<version>'
git tag -l 'mastra@<version>'
git tag -l 'create-mastra@<version>'
git tag -l '@mastra/server@<version>'
git tag -l '@mastra/playground-ui@<version>'If a package version is on npm latest but its expected release tag is missing after a successful rerun, stop and report it before smoke testing.
Stable Release Smoke
Use this after the stable/full release workflow starts or completes.
After the stable/full release publishes, run smoke again against the published stable packages. Do not rely on alpha smoke as final stable-release signoff because the stable publish path, npm dist-tags, generated project install path, and package versions are distinct release surfaces.
1. Confirm the stable publish workflow completed
Inspect the full release run. The stable path should complete successfully, with snapshot/prerelease jobs skipped when this is a normal full release.
gh run view <run-id> --json name,event,status,conclusion,workflowName,headBranch,headSha,jobs,url --jq .
gh run watch <run-id>If the run fails or is cancelled, stop and report the failed job/step before creating a fresh smoke project.
If it fails after some packages published, use references/stable-partial-publish-recovery.md before smoke testing.
2. Confirm stable packages are installable
Check the published latest versions and make sure they match the intended stable release versions:
npm view @mastra/core@latest version
npm view mastra@latest version
npm view create-mastra@latest versionIf npm returns the previous stable version, wait for publish/registry propagation and retry. Do not run final stable smoke against stale latest packages.
3. Create a fresh stable smoke project
Use the same dated workspace, but create a separate project from the alpha project so dependency resolution and generated files prove the stable release path independently.
# Reuse the existing SMOKE_DIR from the alpha/release-scope run.
: "${SMOKE_DIR:?Set SMOKE_DIR via references/release-scope-discovery.md before stable smoke}"
mkdir -p "$SMOKE_DIR/logs"
cd "$SMOKE_DIR"
pnpm create mastra@latest stable-smoke-project -c agents,tools,workflows,scorers -l openai -e
cd stable-smoke-project
pnpm run devIf an existing dev server is holding port 4111 or a DuckDB lock, stop that process before starting the stable project. Do not run alpha and stable smoke projects against the same generated project directory or storage file.
4. Rerun the required smoke coverage
For stable release signoff, rerun at least:
- mandatory local checklist: setup, agents, tools, workflows, traces, scorers, memory, MCP, errors
- Local Studio browser smoke: shell/version, agent chat, tool execution, workflow run, traces, scorers, MCP
- targeted release-scope checks identified from the PR categorization, especially any checks added because the generated project does not cover changed features
Append stable results to the dated smoke-report.md in a separate section from alpha results and clearly record the package versions tested.
Storage/Provider Migration Smoke Pattern
Use this when a release includes storage/provider schema, init, or migration changes. The goal is to prove the released provider package works against a real backend and repairs the old/broken schema state.
Pattern
1. Start the affected backend locally when feasible.
docker run -d --name mastra-smoke-pg \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_DB=mastra \
-p 5544:5432 \
postgres:162. Add the released provider package to the generated smoke project.
pnpm add @mastra/pg@alpha pg
# or, for stable smoke:
pnpm add @mastra/pg@latest pg3. Configure the smoke project to use the provider through public Mastra config. 4. Enable the feature that depends on the changed schema, such as observational memory. 5. Create or mutate backend state to mimic the pre-fix schema. 6. Restart the Mastra dev server so provider init/migration runs from the released package. 7. Verify the missing column/table/index is restored. 8. Run a real API/UI flow that writes and reads through the affected provider path.
Example evidence: Postgres observational-memory migration
For a Postgres observational-memory migration fix, collect evidence such as:
information_schema.columnsshows the missing column exists after restart, for examplereflectedObservationLineCount- an agent call using PG-backed memory succeeds
- a second call recalls the test phrase/thread context
- memory messages and observational memory rows exist in Postgres
Record the backend image/version, package version, schema mutation, verification query, and API result in smoke-report.md.
Targeted Feature Smoke Pattern
Use this when release scope discovery shows that the generated smoke project does not exercise a changed runtime feature.
When a PR changes a specific feature, smoke the smallest real scenario that proves that feature. Avoid vague substitutes like "agent chat works" for a streaming fix, "tools list loads" for a tool approval fix, or "Studio loads" for a persistence/save bug.
Pattern
1. Identify the exact changed path from the PR title, files, changelog, and tests. 2. Configure or modify the smoke project so that path is reachable with the released package. 3. Trigger the behavior through the public API or UI a user would use. 4. Assert the before/after condition that would have failed before the fix. 5. Capture concrete evidence: response fields, stream chunks, persisted rows, saved config after reload, trace/span contents, or visible UI text. 6. If the feature requires cloud auth, external credentials, or a separate product like Mastra Code, either run that targeted environment or mark it PARTIAL/NOT COVERED with the exact reason.
Targeted additions by category
- CLI/create-mastra changed: create a brand-new project at
$SMOKE_DIR/smoke-projectwith the release tag and runpnpm run dev. - Server/adapters/API changed: add curl checks for
/health,/api/agents,/generate,/streamif applicable, tool execute, workflow start, custom routes, and invalid routes. If route prefixing changed, add or use a custom route and verify built-in/api/*routes remain reserved. - Agent streaming changed: test
/stream,resume-stream,streamUntilIdle, abort/length/error behavior, or another endpoint that actually uses the changed streaming path. - Tools changed: test the exact changed tool behavior, such as dynamic tools, approval functions,
requireApproval, programmatic tool calls, or preserved args. A single static weather tool call is not enough for dynamic/approval/tool-merge changes. - Workflows changed: test the exact changed behavior, such as suspend/resume, background task progress, long-running runs, dataset/experiment workflows, or
start-asyncoutput shape. - Memory changed: test thread/resource isolation plus the changed memory mode, such as current-thread recall defaults, observational memory boundaries, or agent network incompatibility. A basic two-call memory check is necessary but may not be sufficient. If the change only affects memory under a specific storage backend, configure the smoke project to use that backend.
- Memory storage migrations changed: run an end-to-end migration smoke against the affected backend. Use
references/storage-provider-migration-smoke.md. - Forked subagents changed: create or use a Mastra Code/subagent scenario that proves parent thread/resource inheritance and prompt cache prefix behavior. Do not assume a normal agent run covers forked subagents.
- Studio/Playground changed: run browser smoke for the affected pages, especially observability traces/logs/metrics and theme/layout changes.
- Auth/permissions/Agent Builder changed: prefer staging/production cloud smoke with an authenticated user and targeted permission flows. Local create-mastra usually does not cover stored agents/skills, starring, visibility, avatar upload, or server-side session refresh.
- MCP/A2A changed: default empty MCP state is only a baseline. For SDK/client/server or schema-validator changes, run a targeted MCP/A2A integration check with a configured server/client when feasible.
- Storage/provider packages changed: at minimum verify package installation/import. If the changed provider can run locally in Docker, run a provider-backed smoke against the released package rather than stopping at import.
- Mastra Code changed: run a separate Mastra Code/TUI smoke path; do not assume standard create-mastra smoke covers it.
- Docs/examples changed: run docs validation or example-specific checks; do not replace runtime smoke with docs-only checks.
Examples
- For streaming fixes: call
/streamorresume-stream, inspect event chunks and final response shape. - For tool approval/dynamic tool fixes: configure the affected tool mode, run an agent that triggers it, and verify approval/rejection or dynamic resolution behavior.
- For workflow fixes: run the specific workflow mode changed, such as suspend/resume, background progress, or long-running output shape.
- For Studio persistence fixes: change the affected form field, save, reload/refetch, and verify the value persisted.
- For observability fixes: generate the affected run type and verify trace/span/scores/logs include the corrected data.
- For CLI/create fixes: create a fresh project with the published CLI and verify generated files/dependencies/scripts match the intended output.
Agents Testing (--test agents)
Purpose
Verify agents page loads and agent chat functionality works.
Steps
1. Navigate to Agents Page
- [ ] Open
/agentsin Studio - [ ] Note if agents list loads and any errors displayed
- [ ] Record which agents appear (e.g., "Weather Agent")
2. Open Agent Chat
- [ ] Click on an agent (e.g., Weather Agent)
- [ ] Note if chat interface loads
- [ ] Record whether input field is visible
3. Send Test Message
- [ ] Enter:
What's the weather in Tokyo? - [ ] Click Send or press Enter
- [ ] Wait for response (may take 5-30 seconds)
4. Observe Response
- [ ] Record the agent's response content
- [ ] Note if response is coherent and relevant
- [ ] Record any error messages displayed
5. Test Follow-up (Memory Check)
- [ ] Send:
What about London? - [ ] Note if agent references the previous question
- [ ] Record whether context appears to be maintained
Observations to Report
| Check | What to Record |
|---|---|
| Agents list | Number of agents shown, any errors |
| Chat loads | Whether input field appears, any errors |
| First message | Agent response content and relevance |
| Follow-up | Whether agent references previous context |
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| "Failed to load agents" | Server not running | Start dev server / check deploy |
| Agent doesn't respond | Missing API key | Check .env has LLM API key |
| Timeout | Slow LLM response | Wait longer, check network |
Browser Actions
Navigate to: /agents
Click: First agent in list
Type in chat: "What's the weather in Tokyo?"
Click: Send button
Wait: For response
Type in chat: "What about London?"
Click: Send button
Wait: For responseCurl / API (for --skip-browser)
`<agentKey>` is the key used in `Mastra({ agents: { weatherAgent } })`, not the agent's `id` field. A template where agents: { weatherAgent } and the agent's id: 'weather-agent' is addressed as /api/agents/weatherAgent/..., not /api/agents/weather-agent/....
List agents:
curl -s http://localhost:4111/api/agentsGenerate (single call, no memory):
curl -s -X POST "http://localhost:4111/api/agents/<agentKey>/generate" \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"What is the weather in Tokyo?"}]}'Generate with memory (see memory.md for the two-call persistence check):
curl -s -X POST "http://localhost:4111/api/agents/<agentKey>/generate" \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"..."}],"memory":{"thread":"<tid>","resource":"<rid>"}}'Pass criteria:
/api/agentsreturns a JSON object keyed by agent key/generatereturns HTTP 200 with atextfield containing a coherent
response (and, if the agent has a tool, toolCalls / toolResults in steps)
Common mistake: sending threadId / resourceId at the top level to /generate — these are silently discarded. Use memory: { thread, resource }. Top-level threadId / resourceId are only read by the deprecated /generate-legacy route.
Browser agents (browser: new StagehandBrowser(...)) require memory: { thread, resource } on every call — without it the auto-attached BrowserContextProcessor throws computeStateSignal requires Mastra memory with an active resourceId and threadId. See tests/setup.md → "Runtime requirement".
Error Handling Testing (--test errors)
Purpose
Verify the application handles errors gracefully with user-friendly messages.
Steps
1. Test Agent Error Handling
- [ ] Navigate to
/agents - [ ] Select an agent
- [ ] Send intentionally problematic input:
- Empty message
- Very long message (10000+ chars)
- Special characters only:
@#$%^&*() - [ ] Record the error message displayed (note if stack trace or user-friendly)
2. Test Tool Error Handling
- [ ] Navigate to
/tools - [ ] Select a tool
- [ ] Submit with invalid input:
- Empty required fields
- Wrong data type (text for number field)
- Invalid format
- [ ] Record the error message displayed
3. Test API Error Handling (Cloud)
For --env staging or --env production:
Replace<server-url>with your environment URL,<agent-id>with an agent from your setup, and<your-api-key>from the platform dashboard.
# Invalid agent
curl -X POST <server-url>/api/agents/nonexistent-agent/generate \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"test"}]}'
# Invalid JSON
curl -X POST <server-url>/api/agents/<agent-id>/generate \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-d 'not valid json'
# Missing required fields
curl -X POST <server-url>/api/agents/<agent-id>/generate \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-d '{}'- [ ] Record HTTP status codes returned
- [ ] Record error message content
- [ ] Note if stack traces appear in response
4. Test Navigation Errors
- [ ] Navigate to invalid route:
/nonexistent-page - [ ] Record what page/behavior appears
- [ ] Navigate to invalid agent:
/agents/fake-agent-id - [ ] Record the error handling behavior
5. Test Network Error Recovery
- [ ] Start a long-running operation
- [ ] Briefly disconnect network (if possible)
- [ ] Record error-handling behavior
- [ ] Note if retry or recovery options appear
Observations to Report
| Check | What to Record |
|---|---|
| Agent errors | Error message text, whether stack trace shown |
| Tool errors | Validation message content |
| API errors | HTTP status codes, error message content |
| 404 pages | Page behavior and content |
| Network errors | Error handling behavior |
Error Message Quality
Note these aspects of error messages:
- Explain what went wrong
- Suggest how to fix it
- Not expose internal details
- Be readable by non-developers
Bad: TypeError: Cannot read property 'x' of undefined Good: Unable to process your request. Please try again.
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| Stack trace shown | Error not caught | Add error boundary |
| Generic "Error" | Missing error message | Improve error handling |
| Page crashes | Unhandled exception | Check error boundaries |
Browser Actions
# Agent error test
Navigate to: /agents
Click: Select agent
Type: "@#$%^&*()"
Send: Message
Verify: Error is user-friendly
# Tool error test
Navigate to: /tools
Click: Select tool
Clear: All inputs
Click: Submit
Verify: Validation error shown
# 404 test
Navigate to: /this-page-does-not-exist
Verify: 404 or redirect, not crashCurl / API (for --skip-browser)
Same curls for local and cloud; cloud needs Authorization: Bearer <api-key>.
# Unknown agent
curl -sw "\nHTTP %{http_code}\n" -X POST \
http://localhost:4111/api/agents/nonexistent/generate \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"hi"}]}'
# Workflow with missing required input field
curl -sw "\nHTTP %{http_code}\n" -X POST \
http://localhost:4111/api/workflows/<workflowId>/start-async \
-H "Content-Type: application/json" \
-d '{"inputData":{}}'
# Tool with missing required input field
curl -sw "\nHTTP %{http_code}\n" -X POST \
http://localhost:4111/api/tools/<toolId>/execute \
-H "Content-Type: application/json" \
-d '{"data":{}}'
# Unknown tool
curl -sw "\nHTTP %{http_code}\n" -X POST \
http://localhost:4111/api/tools/nonexistent/execute \
-H "Content-Type: application/json" \
-d '{"data":{}}'Expected behavior
The current server returns the following. These are the values to assert against — flag any deviation as a regression.
| Case | HTTP | Body shape |
|---|---|---|
| Unknown agent id | 404 | { error: "Agent with id <id> not found" } (or similar) |
| Unknown tool id | 404 | { error: "Tool not found" } |
| Unknown workflow id | 404 | { error: "Workflow not found" } |
| Workflow missing required input | 500 | { error: "Invalid input data: <field> expected ..." } |
| Tool missing required input | 200 | { error: true, validationErrors: { ... } } |
| Invalid JSON body | 400 | { error: "..." } (Hono body parse failure) |
Known inconsistencies (document if you observe, don't treat as failures unless they change):
- Workflow invalid input returns 500 (arguably should be 400)
- Tool invalid input returns 200 with
error: truein the body
(inconsistent with HTTP semantics vs. workflow/agent error responses)
Pass criteria
- Every error response includes a readable
errorfield (or
validationErrors for tools)
- No stack traces leak into the response body
- HTTP status codes match the table above (or are documented deviations)
MCP Servers Testing (--test mcp)
Purpose
Verify MCP (Model Context Protocol) servers page loads and connections work.
Steps
1. Navigate to MCP Page
- [ ] Open
/mcpsin Studio - [ ] Note if page loads and any errors displayed
- [ ] Record what MCP servers list shows
2. Observe Empty State
If no MCP servers configured:
- [ ] Record the empty state message shown
- [ ] Note any errors displayed
- [ ] Record if instructions for adding servers appear
3. Observe Configured Servers
If MCP servers are configured:
- [ ] Record which servers appear in list
- [ ] Note connection status shown (connected/disconnected)
- [ ] Record server names and types visible
4. Test Server Connection
For each configured server:
- [ ] Record connection status
- [ ] Note available tools from server
- [ ] Record which tools are discoverable
5. Test MCP Tool (if available)
- [ ] Navigate to
/tools - [ ] Find MCP-provided tool
- [ ] Execute tool
- [ ] Record the result and whether it calls external server
Observations to Report
| Check | What to Record |
|---|---|
| MCP page | Load behavior, any errors |
| Empty state | Message content if no servers |
| Server list | Servers shown and their details |
| Connection | Status indicator behavior |
| Tools | Which MCP tools are discoverable |
MCP Configuration
Servers are typically configured in project code:
import { MCPConfiguration } from '@mastra/core/mcp';
const mcp = new MCPConfiguration({
servers: {
myServer: {
command: 'node',
args: ['path/to/server.js'],
},
},
});Common Issues
| Issue | Cause | Fix |
|---|---|---|
| Page error | MCP not supported | Check Mastra version |
| Server disconnected | Server process failed | Check server logs |
| No tools | Server not exposing tools | Check server implementation |
Notes
- MCP is optional - empty state is acceptable
- External MCP servers may require separate processes
- Connection issues may be transient
Browser Actions
Navigate to: /mcps
Wait: For page to load
Verify: Page loads without errors
Verify: Server list OR empty state visible
# If servers configured:
Click: On server in list
Verify: Connection status shown
Verify: Available tools listedCurl / API (for --skip-browser)
The server exposes MCP endpoints under /api/mcp/v0/... (not /api/mcps — that's the Studio route). The MCP here refers to Mastra hosting MCP servers for external clients; it does not list external MCP clients the project consumes.
1. List MCP servers this Mastra instance exposes
curl -s "http://localhost:4111/api/mcp/v0/servers" | jq '.'Response shape: { servers: [...], total_count: N, next: null }. An empty array is a valid pass if the project declares no MCP servers (the default create-mastra template does not).
2. Get one server's metadata
curl -s "http://localhost:4111/api/mcp/v0/servers/<serverId>" | jq '.'3. List tools a server exposes, and execute one
# List tools
curl -s "http://localhost:4111/api/mcp/<serverId>/tools" | jq '.'
# Inspect one tool
curl -s "http://localhost:4111/api/mcp/<serverId>/tools/<toolId>" | jq '.'
# Execute the tool
curl -s -X POST "http://localhost:4111/api/mcp/<serverId>/tools/<toolId>/execute" \
-H "Content-Type: application/json" \
-d '{"data":{ ... }}'Pass criteria:
GET /api/mcp/v0/serversreturns HTTP 200 with the expected shape
(empty servers array is OK)
- If servers are declared: each shows up in the list and
GET /api/mcp/v0/servers/:id returns a non-null object
- If tools are exposed:
GET /api/mcp/:serverId/toolslists them and
POST /.../execute returns a successful result
Common mistakes:
- Hitting
/api/mcpsor/api/mcp/servers— neither exists server-side - Treating an empty
serverslist as failure on the default template —
it's expected
Memory Testing (--test memory)
Purpose
Verify conversation memory persists and context is maintained.
Prerequisites
- Agent with memory configured
- Completed at least one agent chat
Steps
1. Start Fresh Conversation
- [ ] Navigate to
/agents - [ ] Select an agent (e.g., Weather Agent)
- [ ] Send:
What's the weather in Tokyo? - [ ] Wait for response and record it
2. Test Context Retention
- [ ] Send follow-up:
What about comparing it to London? - [ ] Note if agent references Tokyo in response
- [ ] Record whether agent understands "it" refers to weather
3. Test Navigation Persistence
- [ ] Navigate away (e.g., to
/tools) - [ ] Navigate back to
/agents→ same agent - [ ] Note if conversation history is visible
- [ ] Record which previous messages are displayed
4. Test Cross-Session (if applicable)
- [ ] Note the current thread/conversation
- [ ] Refresh the page (F5)
- [ ] Navigate back to the same agent
- [ ] Record whether history persists
5. Test New Thread
- [ ] Start a new conversation (if UI supports)
- [ ] Note if new thread has no history
- [ ] Record whether old thread is still accessible
Observations to Report
| Check | What to Record |
|---|---|
| Context retention | Whether agent references previous messages |
| Navigation | History visibility after navigating away |
| Page refresh | Whether history persists |
| New thread | Behavior when starting fresh conversation |
Memory Configurations
| Type | Persistence | Configuration |
|---|---|---|
| In-memory | Session only | Default |
| LibSQL | Persistent | @mastra/libsql storage |
| PostgreSQL | Persistent | @mastra/pg storage |
| Turso | Persistent | @mastra/turso storage |
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| No history after refresh | In-memory storage | Configure persistent storage |
| Agent forgets context | Memory not configured | Add memory to agent config |
| Thread not found | Invalid thread ID | Start new conversation |
Browser Actions
Navigate to: /agents
Click: Select agent
Type: "What's the weather in Tokyo?"
Send: Message
Wait: For response
Type: "What about comparing it to London?"
Send: Message
Verify: Response references Tokyo
Navigate to: /tools
Navigate to: /agents
Click: Same agent
Verify: Previous messages visible
Refresh: Page (F5)
Navigate to: /agents
Click: Same agent
Verify: History still visible (if persistent storage)Curl / API (for --skip-browser)
The current /agents/:agentId/generate route expects thread/resource under a memory object. Top-level threadId / resourceId are only read by the deprecated /generate-legacy route — sending them to /generate silently discards them and the agent will appear to "forget" context.
Correct request shape:
{
"messages": [{ "role": "user", "content": "..." }],
"memory": { "thread": "<thread-id>", "resource": "<resource-id>" }
}Two-call persistence check:
TID="smoke-memory-$(date +%s)"
RID="smoke-user"
# Call 1: seed context
curl -s -X POST "http://localhost:4111/api/agents/<agentKey>/generate" \
-H "Content-Type: application/json" \
-d "{\"messages\":[{\"role\":\"user\",\"content\":\"Remember: my name is Abhi.\"}],\"memory\":{\"thread\":\"$TID\",\"resource\":\"$RID\"}}"
# Call 2: same thread, verify recall
curl -s -X POST "http://localhost:4111/api/agents/<agentKey>/generate" \
-H "Content-Type: application/json" \
-d "{\"messages\":[{\"role\":\"user\",\"content\":\"What is my name?\"}],\"memory\":{\"thread\":\"$TID\",\"resource\":\"$RID\"}}"
# Assert: thread exists in storage
curl -s "http://localhost:4111/api/memory/threads?resourceId=$RID" | \
jq '{total, ids: (.threads | map(.id))}'Response shape: GET /api/memory/threads returns { threads: [...], total, page, perPage, hasMore } — not a bare array. Each entry has { id, resourceId, title, metadata, createdAt, updatedAt }.
Query params: resourceId is case-sensitive (capital I). Lowercase resourceid is silently ignored and returns all threads, which can make a broken test look like it passed. agentId is optional.
Pass criteria:
- Call 2 response references "Abhi"
GET /api/memory/threads?resourceId=<rid>returns.total >= 1with a
thread whose id matches $TID
- To harden: seed a second thread under a different
resourceIdand
confirm the filter excludes it
If call 2 forgets context: check you sent memory: { thread, resource } (not top-level threadId / resourceId) and that <agentKey> matches the key used in the Mastra({ agents }) config, not the agent's id field.
If `/memory/threads` returns threads from other resources: you typed resourceid instead of resourceId — the unknown param is dropped and no filter is applied.
Scorers Testing (--test scorers)
Purpose
Verify evaluation scorers page loads and displays available scorers.
Steps
1. Navigate to Scorers Page
- [ ] Open
/evaluation?tab=scorersin Studio - [ ] Note if page loads and any errors displayed
- [ ] Record what scorers list shows
2. Observe Scorers Display
- [ ] Record which scorers are listed
- [ ] Note what information is shown for each scorer (name, description)
- [ ] Record any error messages
3. Check Scorer Details (if available)
- [ ] Click on a scorer to view details
- [ ] Record what configuration is visible
- [ ] Note any run history shown
Observations to Report
| Check | What to Record |
|---|---|
| Scorers page | Load behavior, any errors |
| Scorers list | Which scorers appear |
| Scorer details | Configuration and history shown |
Notes
- Scorers are optional - empty state is OK if none configured
- Default project may include example scorers
- Scorer runs appear in traces as
scorer run: <name>
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| Empty scorers list | None configured | OK - just verify page loads |
| Page error | Missing dependencies | Check @mastra/evals installed |
Browser Actions
Navigate to: /evaluation?tab=scorers
Wait: For page to load
Verify: Page loads without errors
Verify: Scorers list visible (may be empty)Curl / API (for --skip-browser)
There is no "execute scorer" HTTP endpoint. Scorers run automatically as part of agent / workflow execution (live scoring) and record score rows. To smoke-test scorers over the API you verify (1) they are registered, and (2) they emit scores when an agent / workflow runs.
1. List registered scorers
curl -s http://localhost:4111/api/scores/scorers | jq 'keys'Pass: returns an object keyed by scorer id (weather-scorer, translation-quality-scorer, etc.) for the scorers declared in the project. Empty {} is only acceptable if the project genuinely declares none.
2. Get a single scorer's config
curl -s http://localhost:4111/api/scores/scorers/<scorerId> | jq '.'Pass: returns a non-null object with config and the agents/workflows it is attached to. null means the id is wrong or the scorer is not registered.
3. Trigger scoring by running an agent / workflow, then read scores
# Run the workflow (or agent) that has scorers attached
curl -s -X POST "http://localhost:4111/api/workflows/<workflowId>/start-async" \
-H "Content-Type: application/json" \
-d '{"inputData":{"city":"Tokyo"}}' | jq '.traceId'
# Scores recorded for that run
curl -s "http://localhost:4111/api/observability/scores?page=0&perPage=20" \
| jq '.scores | map({scorerId, score, reason})'Pass criteria:
/api/scores/scorerslists every scorer the template declares- After a workflow/agent run completes,
/api/observability/scoreshas
new entries with the expected scorerIds and numeric score values
- Each recorded score has a
runId/traceIdtying it back to the
invoking run
Common mistakes:
- Treating
POST /api/scores/scorers/<id>/executeas real — it does not
exist. If you see agent output that claims a score without a corresponding row in /observability/scores, you hallucinated the result.
- Expecting scorers to run on ad-hoc text input — they need a live
agent/workflow run.
Server Deploy Testing (--test server)
Cloud only: For --env staging or --env production.
Purpose
Verify Server deployment works and API is accessible.
Prerequisites
- Mastra platform account
- Project with at least one agent
- Authenticated via
mastra auth login - Studio deployed first (recommended)
Steps
1. Set Environment
# For staging
export MASTRA_PLATFORM_API_URL=https://platform.staging.mastra.ai
# For production (default)
unset MASTRA_PLATFORM_API_URL2. Authenticate (if not already)
pnpx mastra@latest auth login3. Deploy Server
pnpx mastra@latest server deploy -yWatch for:
- [ ] Build starts
- [ ] Build completes (note any warnings)
- [ ] Deploy starts
- [ ] Capture Server URL from output
Critical warnings to note:
mastra-cloud-observability-exporter disabled- traces won't workCLOUD_EXPORTER_FAILED_TO_BATCH_UPLOAD_LOGS- trace endpoint issue
4. Test Health Endpoint
curl <server-url>/health- [ ] Record HTTP status code returned
- [ ] Record response body content
5. Test Agent API
curl -X POST <server-url>/api/agents/weather-agent/generate \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Weather in Tokyo?"}]}'- [ ] Record HTTP status code returned
- [ ] Record response content
6. Use Test Script
.claude/skills/mastra-smoke-test/scripts/test-server.sh <server-url>- [ ] Record health check result
- [ ] Record agent call result
- [ ] Note script exit code
7. Check Traces in Studio
- [ ] Open Studio
/observability - [ ] Refresh page
- [ ] Note if trace from Server API call appears
- [ ] Record how long until trace appears (if at all)
Observations to Report
| Check | What to Record |
|---|---|
| Deploy | Completion status, any errors or warnings |
| URL | Server URL returned |
| Health | HTTP status and response from /health |
| Agent API | HTTP status and response content |
| Traces | Whether traces appear, timing |
Deploy URLs
| Environment | URL Pattern |
|---|---|
| Staging | https://<project>.server.staging.mastra.cloud |
| Production | https://<project>.server.mastra.cloud |
API Endpoints
| Endpoint | Method | Purpose |
|---|---|---|
/health | GET | Health check |
/api/agents/<id>/generate | POST | Agent generation |
/api/agents/<id>/stream | POST | Streaming generation |
/<custom-route> | ANY | Custom API routes |
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| 403 on health | Not deployed yet | Wait or redeploy |
| Agent 404 | Wrong agent ID | Check agent IDs in project |
| Traces missing | Token issue | Check deploy warnings, redeploy |
| Timeout | Cold start | Retry after 30 seconds |
Notes
- Server cold starts may take 10-30 seconds
- First request after deploy may be slow
- Traces may take up to 30 seconds to appear
- Redeploy if traces consistently missing
Project Setup
Purpose
Set up or verify a Mastra project for smoke testing.
Multi-Environment Config
One project can target multiple environments using separate config files:
| Environment | Config File | Platform API URL | Studio/Server Domain |
|---|---|---|---|
| Local | N/A (no deploy) | N/A | localhost:4111 |
| Staging | .mastra-project-staging.json | https://platform.staging.mastra.ai | *.studio.staging.mastra.cloud |
| Production | .mastra-project.json | https://platform.mastra.ai | *.studio.mastra.cloud |
- Local = Running your Mastra project with
pnpm dev(no cloud deploy) - Staging/Production = Deploying to Mastra platform
Setting Up Multi-Environment
After creating a project:
# Deploy to staging (creates .mastra-project-staging.json)
export MASTRA_PLATFORM_API_URL=https://platform.staging.mastra.ai
pnpx mastra@latest auth login
pnpx mastra@latest studio deploy --config .mastra-project-staging.json -y
pnpx mastra@latest server deploy --config .mastra-project-staging.json -y
# Deploy to production (creates .mastra-project.json)
export MASTRA_PLATFORM_API_URL=https://platform.mastra.ai
pnpx mastra@latest auth login
pnpx mastra@latest studio deploy -y
pnpx mastra@latest server deploy -yEach deploy creates a separate project ID in its config file, so staging and production don't interfere.
Note: Always warn user before running auth login as it opens a browser.
---
Option A: Create New Project
1. Navigate to Directory
cd <directory>
# Default: ~/mastra-smoke-tests2. Create Project
<pm> create mastra@<tag> <project-name> -c agents,tools,workflows,scorers -l <llm> -e| Flag | Purpose |
|---|---|
-c agents,tools,workflows,scorers | Include all components |
-l <provider> | Set LLM provider (openai, anthropic, etc.) |
-e | Include example code |
3. Enter Project
cd <project-name>4. Record Structure
- [ ] Note if
package.jsonexists - [ ] Note if
src/mastra/index.tsexists - [ ] Record agents found in
src/mastra/agents/ - [ ] Record tools found in
src/mastra/tools/
Option B: Use Existing Project
1. Navigate to Project
cd <existing-project-path>2. Record Requirements
- [ ] Note if
package.jsoncontains@mastra/core - [ ] Note if
src/mastra/index.tshas Mastra instance - [ ] Record which agents are configured
3. Update Dependencies (if --tag provided)
# Update ALL @mastra/* packages to avoid version drift
<pm> add @mastra/core@<tag> @mastra/memory@<tag> mastra@<tag>
# Also update any adapters in package.json:
# @mastra/libsql, @mastra/pg, @mastra/turso, @mastra/duckdb
# @mastra/evals, @mastra/observability, @mastra/stagehandImportant: Check package.json first — only update packages that exist.
Storage Backend (--db)
| Backend | Package | Env Variables |
|---|---|---|
libsql (default) | @mastra/libsql | None |
pg | @mastra/pg | DATABASE_URL |
turso | @mastra/turso | TURSO_DATABASE_URL, TURSO_AUTH_TOKEN |
Install Non-Default Backend
<pm> add @mastra/<backend>Configure in src/mastra/index.ts
import { LibSQLStore } from '@mastra/libsql'; // or PgStore, TursoStore
export const mastra = new Mastra({
// ...
storage: new LibSQLStore({
/* config */
}),
});Browser Agent (--browser-agent)
1. Install Packages
<pm> add @mastra/stagehand @mastra/memory2. Create Agent
Create src/mastra/agents/browser-agent.ts:
import { Agent } from '@mastra/core/agent';
import { Memory } from '@mastra/memory';
import { StagehandBrowser } from '@mastra/stagehand';
export const browserAgent = new Agent({
id: 'browser-agent',
name: 'Browser Agent',
instructions: `You are a helpful assistant that can browse the web.`,
model: '<provider>/<model>',
memory: new Memory(),
browser: new StagehandBrowser({
headless: false, // true for cloud deploys
}),
});3. Register Agent
Update src/mastra/index.ts:
import { browserAgent } from './agents/browser-agent';
export const mastra = new Mastra({
agents: { weatherAgent, browserAgent },
// ...
});4. Install Playwright
<pm> exec playwright install chromium5. Runtime requirement: pass thread + resource on every call
Passing browser: new StagehandBrowser(...) to Agent auto-attaches the BrowserContextProcessor input processor. That processor reads/writes browser state via Mastra memory, so every call to the browser agent must provide both a thread and a resource id, or the processor throws:
[Processor:browser-context] computeStateSignal requires Mastra memory with an active resourceId and threadIdUse the memory: { thread, resource } payload shape:
curl -s -X POST 'http://localhost:4111/api/agents/browser-agent/generate' \
-H 'Content-Type: application/json' \
-d '{
"messages":[{"role":"user","content":"Navigate to https://example.com and tell me the page title."}],
"memory":{"thread":"<tid>","resource":"<rid>"}
}'Top-level threadId / resourceId are silently discarded (same as other agents). The Studio chat works without explicit IDs because the chat UI allocates them for you.
Custom API Routes
To add custom API routes:
1. Create Route
Create src/mastra/routes/hello.ts:
import { registerApiRoute } from '@mastra/core/server';
export const helloRoute = registerApiRoute('/hello', {
method: 'GET',
requiresAuth: false, // Set to true if auth required
handler: async c => {
return c.json({ message: 'Hello from custom route!' });
},
});2. Register Route
Update src/mastra/index.ts:
import { helloRoute } from './routes/hello';
export const mastra = new Mastra({
// ...
server: {
apiRoutes: [helloRoute], // ⚠️ Must be "apiRoutes", not "routes"
},
});Common mistake: Using routes instead of apiRoutes - this will silently fail.
3. Verify Locally
# Start dev server
<pm> run dev
# Test route
curl http://localhost:4111/helloEnvironment Variables
Check/Set LLM API Key
# Check if set
echo $OPENAI_API_KEY # or ANTHROPIC_API_KEY, etc.
# Or check .env file
cat .env | grep API_KEYIf not set, add to .env:
OPENAI_API_KEY=sk-...Platform URL (Cloud Only)
# Staging
export MASTRA_PLATFORM_API_URL=https://platform.staging.mastra.ai
# Production (default - can be unset)
unset MASTRA_PLATFORM_API_URLVerification Checklist
| Check | Command |
|---|---|
| Project exists | ls package.json |
| Dependencies | <pm> list @mastra/core |
| Mastra config | cat src/mastra/index.ts |
| Agents exist | ls src/mastra/agents/ |
| Env vars | cat .env |
| TypeScript check | <pm> tsc --noEmit |
TypeScript Check (Required)
Always run `tsc --noEmit` after modifying config files. This catches most config mistakes that mastra build silently ignores:
- Wrong property names (
routesvsapiRoutes) - Type mismatches (
timeout: "30"vstimeout: 30) - Missing imports
- Unknown properties
<pm> tsc --noEmit
# Record any errors that appearIf errors appear, fix them before proceeding. Don't rely on mastra build or pnpm dev to catch these.
Common Issues
| Issue | Fix |
|---|---|
| "Cannot find module '@mastra/core'" | Run <pm> install |
| "Missing API key" | Add to .env file |
| "No agents found" | Check agent exports in index.ts |
| Custom routes not working | Use server.apiRoutes, not server.routes |
| Config errors not caught by build | Run tsc --noEmit - build doesn't type-check |
Studio Deploy Testing (--test studio)
Cloud only: For --env staging or --env production.
Purpose
Verify Studio deployment works and UI is accessible.
Prerequisites
- Mastra platform account
- Project with at least one agent
- Authenticated via
mastra auth login
Steps
1. Set Environment
# For staging
export MASTRA_PLATFORM_API_URL=https://platform.staging.mastra.ai
# For production (default)
unset MASTRA_PLATFORM_API_URL2. Authenticate
pnpx mastra@latest auth login- [ ] Note if browser opens for OAuth
- [ ] Record login flow completion
- [ ] Record CLI authentication confirmation
3. Deploy Studio
pnpx mastra@latest studio deploy -yRecord:
- [ ] Note if build starts
- [ ] Record build completion and any warnings
- [ ] Note if deploy starts
- [ ] Capture Studio URL from output
4. Handle Deploy Output
| Output | Action |
|---|---|
| Error/Failed | STOP - report error |
| Warning (observability, session) | Note and continue |
| Success + URL | Continue to verification |
5. Observe Studio Access
- [ ] Open Studio URL in browser
- [ ] Note if sign-in is prompted
- [ ] Record whether Studio UI loads
- [ ] Record which agents appear in list
6. Test Basic Functionality
- [ ] Navigate to
/agents - [ ] Click on an agent
- [ ] Send a test message
- [ ] Record the response
Observations to Report
| Check | What to Record |
|---|---|
| Deploy | Completion status, any errors or warnings |
| URL | Studio URL returned |
| Access | Sign-in behavior, UI load status |
| UI | What interface elements appear |
| Agents | Which agents are visible |
Deploy URLs
| Environment | URL Pattern |
|---|---|
| Staging | https://<project>.studio.staging.mastra.cloud |
| Production | https://<project>.studio.mastra.cloud |
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| Deploy hangs | Network issue | Check connectivity, retry |
| "Session expired" | Auth timeout | Re-run auth login |
| 404 after deploy | DNS propagation | Wait 1-2 minutes |
| Build fails | Code errors | Check build output |
Studio vs Server: When to Use Each
| Deploy | What It Does | Use For |
|---|---|---|
studio deploy | Deploys the Studio UI | Interactive testing, viewing traces, debugging |
server deploy | Deploys the API server | API access, production use, programmatic access |
Typical flow:
1. Deploy Studio first (for UI access) 2. Deploy Server (for API access) 3. Test via both UI and API 4. Check if Server traces appear in Studio
You can deploy one without the other, but:
- Studio-only: No API access, can't test server traces
- Server-only: No UI, must use curl/API clients
Notes
- First deploy may take longer (2-5 minutes)
- Subsequent deploys are faster
- Studio URL persists across deploys
- Check
projects.mastra.aito view all deployments
Tools Testing (--test tools)
Purpose
Verify tools page loads and tool execution works.
Steps
1. Navigate to Tools Page
- [ ] Open
/toolsin Studio - [ ] Note if tools list loads and any errors displayed
- [ ] Record which tools appear (e.g., "get-weather")
2. Select a Tool
- [ ] Click on a tool (e.g.,
get-weather) - [ ] Note if tool details panel opens
- [ ] Record which input fields are visible
3. Execute Tool
- [ ] Enter test input (e.g., "London" for city field)
- [ ] Click "Submit" or "Run"
- [ ] Wait for execution
4. Observe Output
- [ ] Record the output format (JSON, text, etc.)
- [ ] Record the output content
- [ ] Note any error messages
5. Test Error Handling
- [ ] Enter invalid input (e.g., empty or special characters)
- [ ] Record the error message displayed
- [ ] Note if tool crashes or handles gracefully
Observations to Report
| Check | What to Record |
|---|---|
| Tools list | Which tools appear, any errors |
| Tool details | Input fields shown |
| Execution | Output format and content |
| Output data | Data returned |
| Error handling | Error message content and behavior |
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| "No tools found" | Tools not registered | Check src/mastra/tools/ exports |
| Tool execution fails | Missing dependencies | Check tool implementation |
| Invalid JSON output | Tool error | Check server logs |
Browser Actions
Navigate to: /tools
Click: First tool in list (e.g., get-weather)
Type in input field: "London"
Click: Submit button
Wait: For output
Verify: JSON output appearsCurl / API (for --skip-browser)
`<toolId>` is the tool's `.id` property, not the export name. A weatherTool export with id: 'get-weather' is addressed as /api/tools/get-weather/execute, not /api/tools/weatherTool/execute.
List tools:
curl -s http://localhost:4111/api/toolsExecute a tool:
curl -s -X POST "http://localhost:4111/api/tools/<toolId>/execute" \
-H "Content-Type: application/json" \
-d '{"data":{"location":"San Francisco"}}'Note the data wrapper — the tool's input schema fields go inside data, not at the top level.
Pass criteria:
/api/toolsreturns a JSON object keyed by tool id/executewith valid input returns HTTP 200 with the tool's result object/executewith invalid input returns HTTP 200 with
{ error: true, validationErrors: {...} } (see errors.md)
Common mistakes:
- Using the export name (e.g.
weatherTool) instead of the tool'sid
(e.g. get-weather) → 404 "Tool not found"
- Sending input fields at the top level instead of under
data→ validation
error on every field
- External API failures surface as HTTP 500 with upstream error content.
Retry with a different input (e.g. a well-known city) before concluding the tool itself is broken.
Traces Testing (--test traces)
Purpose
Verify observability traces are being collected and displayed.
Prerequisites
- Must have run agent/tool/workflow tests first (to generate traces)
- For cloud: Both Studio and Server need to be deployed
Steps
1. Navigate to Observability
- [ ] Open
/observabilityin Studio - [ ] Note if page loads and any errors displayed
- [ ] Record existing traces shown
2. Observe Studio-Originated Traces
- [ ] Look for traces from previous tests (agent chat, tool runs)
- [ ] Record what information traces show (name, timestamp, duration, status)
- [ ] Click on a trace to expand details
3. Check Trace Details
- [ ] Record what input/output is shown
- [ ] Note timing information displayed
- [ ] Record any error states shown
4. Generate Server Trace (Cloud Only)
For --env staging or --env production:
curl -X POST <server-url>/api/agents/weather-agent/generate \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Weather in Paris?"}]}'- [ ] Execute the curl command
- [ ] Record the response
5. Check for Server Trace
- [ ] Refresh
/observabilitypage - [ ] Note if new trace from Server API call appears
- [ ] Record how long until trace appears (if at all)
Observations to Report
| Check | What to Record |
|---|---|
| Traces page | Load behavior, any errors |
| Studio traces | Which traces appear from previous actions |
| Trace details | Input, output, duration shown |
| Server traces | Whether traces appear after API call, timing |
Trace Sources
| Source | How Generated | Identifier |
|---|---|---|
| Studio | UI interactions (chat, tool runs) | From Studio domain |
| Server | Direct API calls | From server domain |
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
| No traces at all | OTel not configured | Check telemetry in mastra config |
| Studio traces only | Server token issue | Redeploy server |
| "Something went wrong" | Auth/session issue | Re-authenticate in Studio |
CLOUD_EXPORTER warnings | Missing token | Infrastructure issue - note it |
Local vs Cloud
Local (`--env local`):
- Traces stored in-memory
- Only persist while dev server runs
- Check
@mastra/observabilityis installed
Cloud (`--env staging/production`):
- Traces sent to cloud collector
- Persist across sessions
- Note if both Studio and Server traces appear
Curl / API (for --skip-browser, local)
The same /api/observability/traces endpoint works locally (no auth needed):
# List recent spans (response shape: { pagination, spans })
curl -s "http://localhost:4111/api/observability/traces?page=0&perPage=20" | jq '.'
# Get a specific trace by id (captured from a prior agent/workflow response)
curl -s "http://localhost:4111/api/observability/traces/<traceId>" | jq '.'Response shape: GET /api/observability/traces returns { pagination: { total, page, perPage, hasMore }, spans: [...] } — not a bare array and not a traces key. Each entry in spans has spanType (agent_run, tool_call, workflow_run, scorer_run), traceId, timestamps, and payload.
Quick pass check:
curl -s "http://localhost:4111/api/observability/traces?page=0&perPage=100" | \
jq '{total: .pagination.total, byType: ([.spans[].spanType] | group_by(.) | map({t: .[0], n: length}))}'Pass criteria (local):
- After running agent / tool / workflow tests,
.pagination.total > 0 .spanscontains the expectedspanTypes:agent_run,workflow_run,
scorer_run (and tool_call if the agent invoked a tool)
traceIdvalues returned in earlier generate/workflow responses resolve
via /observability/traces/:traceId
Note: local traces are in-memory only (MastraStorageExporter). They disappear on dev server restart. Run the agent/tool/workflow tests in the same dev server session as the traces test.
Direct Trace API (Cloud Only)
If UI traces aren't appearing but you need to verify the trace pipeline:
Mobs-Query URLs
| Environment | URL |
|---|---|
| Production | https://mobs-query-vgvrl5lbxq-uc.a.run.app |
| Staging | https://mobs-query-pvyw2kfhjq-uc.a.run.app |
Get Auth Token
Credentials are stored in ~/.mastra/credentials.json after mastra auth login:
{
"token": "eyJhbG...", // Access token (5 min expiry)
"refreshToken": "eyJhbG...", // Refresh token (long-lived)
"user": { "id": "...", "email": "..." },
"organizationId": "org_01KN...",
"currentOrgId": "org_01KN..."
}Token Refresh Helper
WorkOS tokens expire in 5 minutes. Use this helper to auto-refresh:
get_valid_token() {
local PLATFORM_URL="${1:-https://platform.mastra.ai}"
local TOKEN=$(jq -r '.token' ~/.mastra/credentials.json)
local ORG_ID=$(jq -r '.currentOrgId // .organizationId' ~/.mastra/credentials.json)
# Try current token
local VERIFY=$(curl -s "$PLATFORM_URL/v1/auth/verify" \
-H "Authorization: Bearer $TOKEN" \
-H "x-organization-id: $ORG_ID")
if echo "$VERIFY" | jq -e '.user' > /dev/null 2>&1; then
echo "$TOKEN"
return 0
fi
# Token expired — try refresh
local REFRESH_TOKEN=$(jq -r '.refreshToken' ~/.mastra/credentials.json)
if [ -z "$REFRESH_TOKEN" ] || [ "$REFRESH_TOKEN" = "null" ]; then
echo "No refresh token. Re-login required." >&2
return 1
fi
local REFRESH_RESULT=$(curl -s "$PLATFORM_URL/v1/auth/refresh-token" \
-X POST \
-H "Content-Type: application/json" \
-d "{\"refreshToken\": \"$REFRESH_TOKEN\"}")
if echo "$REFRESH_RESULT" | jq -e '.accessToken' > /dev/null 2>&1; then
local NEW_TOKEN=$(echo "$REFRESH_RESULT" | jq -r '.accessToken')
local NEW_REFRESH=$(echo "$REFRESH_RESULT" | jq -r '.refreshToken')
# Update credentials file
jq --arg t "$NEW_TOKEN" --arg r "$NEW_REFRESH" \
'.token = $t | .refreshToken = $r' \
~/.mastra/credentials.json > ~/.mastra/credentials.json.tmp \
&& mv ~/.mastra/credentials.json.tmp ~/.mastra/credentials.json
echo "$NEW_TOKEN"
return 0
fi
echo "Refresh failed. Re-login required." >&2
return 1
}
# Usage
TOKEN=$(get_valid_token "https://platform.mastra.ai") || exit 1Query Traces
# Get project info from config file
PROJECT_ID=$(jq -r '.projectId' .mastra-project.json) # or .mastra-project-staging.json
ORG_ID=$(jq -r '.organizationId' .mastra-project.json)
TOKEN=$(get_valid_token "https://platform.mastra.ai")
# Production
curl -s "https://mobs-query-vgvrl5lbxq-uc.a.run.app/api/observability/traces?page=0&perPage=10&resourceId=$PROJECT_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "x-organization-id: $ORG_ID" | jq '.'
# Staging
TOKEN=$(get_valid_token "https://platform.staging.mastra.ai")
curl -s "https://mobs-query-pvyw2kfhjq-uc.a.run.app/api/observability/traces?page=0&perPage=10&resourceId=$PROJECT_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "x-organization-id: $ORG_ID" | jq '.'Trace Response Structure
{
"pagination": { "total": 10, "page": 0, "perPage": 10, "hasMore": false },
"traces": [
{
"traceId": "37f0d68d760887e994135c984ebd7b89",
"name": "agent run: 'weather-agent'",
"spanType": "agent_run",
"startedAt": "2026-04-08T15:29:27.123Z",
"endedAt": "2026-04-08T15:29:30.456Z",
"metadata": { "buildId": "...", "runId": "..." },
"requestContext": { "user": { "id": "...", "email": "..." } },
"status": "success"
}
]
}| Field | Description |
|---|---|
metadata.buildId | Deploy ID (studio or server) |
requestContext | Present for studio traces (authenticated), null for server traces |
spanType | agent_run, tool_call, workflow_run, etc. |
status | success, error, running |
Filter Traces
# By time range (URL-encoded JSON)
curl -s "...?startedAt=%7B%22start%22%3A%222026-04-08T15%3A00%3A00.000Z%22%7D" ...
# By resource ID (project)
curl -s "...?resourceId=$PROJECT_ID" ...
# By run ID
curl -s "...?runId=$RUN_ID" ...Browser Actions
Navigate to: /observability
Wait: For traces to load
Verify: At least one trace visible
Click: On a trace row
Verify: Details panel shows input/output
# For cloud only:
Execute: curl command to server
Navigate to: /observability
Click: Refresh or wait
Verify: New server trace appearsWorkflows Testing (--test workflows)
Purpose
Verify workflows page loads and workflow execution works.
Steps
1. Navigate to Workflows Page
- [ ] Open
/workflowsin Studio - [ ] Note if workflows list loads and any errors displayed
- [ ] Record which workflows appear
2. Select a Workflow
- [ ] Click on a workflow (e.g.,
weather-workflow) - [ ] Note if workflow details/run panel opens
- [ ] Record which input fields are visible (if any)
3. Execute Workflow
- [ ] Enter the required input (e.g., "Berlin" for city)
- [ ] Click "Run" or "Execute"
- [ ] Wait for workflow to complete
4. Observe Execution
- [ ] Record the workflow state shown (Running, etc.)
- [ ] Note completion status (success, failure, steps shown)
- [ ] Record output/result displayed
5. Check Workflow Steps
- [ ] Record which individual steps executed
- [ ] Note step-by-step output if available
- [ ] Record the final result
Observations to Report
| Check | What to Record |
|---|---|
| Workflows list | Which workflows appear, any errors |
| Run panel | Input fields and controls shown |
| Execution | State transitions, completion status |
| Steps | Which steps executed, their output |
| Output | Final result content |
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| "No workflows found" | Workflows not registered | Check src/mastra/workflows/ |
| Workflow fails | Step error | Check individual step logs |
| Timeout | Long-running workflow | Increase timeout or simplify |
Browser Actions
Navigate to: /workflows
Click: First workflow in list
Type in input (if required): "Berlin"
Click: Run button
Wait: For completion
Verify: Success state and outputCurl / API (for --skip-browser)
`<workflowId>` is the workflow's registered id (the key used in Mastra({ workflows: { weatherWorkflow } }) or the workflow's .id, depending on how it was registered).
List workflows:
curl -s http://localhost:4111/api/workflowsRun a workflow synchronously:
curl -s -X POST "http://localhost:4111/api/workflows/<workflowId>/start-async" \
-H "Content-Type: application/json" \
-d '{"inputData":{"city":"Tokyo"}}'Note the inputData wrapper — the workflow's input schema fields go inside inputData, not at the top level.
Pass criteria:
/api/workflowsreturns a JSON object keyed by workflow id/start-asyncreturns HTTP 200 with a result object containing the final
workflow output and a run id
- Response typically includes a
traceId— capture it to cross-reference in
traces.md verification
Common mistakes:
- Sending input fields at the top level instead of under
inputData→
HTTP 500 "Invalid input data: <field> expected <type>, received undefined"
- Using the workflow's display name instead of its registered id → 404
"Workflow not found"
#!/bin/bash
#
# Spot check a Changesets versioning PR before an alpha release.
#
# Usage:
# ./check-versioning-pr.sh <pr-number> [--workspace <dir>]
#
# Examples:
# ./check-versioning-pr.sh 15857
# ./check-versioning-pr.sh 15857 --workspace ~/mastra-smoke-tests/2026-04-28
set -euo pipefail
usage() {
sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'
}
require_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "Error: required dependency '$1' is not installed." >&2
exit 1
fi
}
PR_NUMBER=""
WORKSPACE="$HOME/mastra-smoke-tests/$(date +%F)"
while [ $# -gt 0 ]; do
case "$1" in
--workspace)
if [ $# -lt 2 ] || [ -z "${2:-}" ]; then
echo "Error: --workspace requires a value." >&2
exit 1
fi
WORKSPACE="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
if [ -z "$PR_NUMBER" ]; then
PR_NUMBER="$1"
shift
else
echo "Unknown argument: $1" >&2
echo "" >&2
usage >&2
exit 1
fi
;;
esac
done
if [ -z "$PR_NUMBER" ]; then
usage >&2
exit 1
fi
require_cmd gh
require_cmd python3
LOG_DIR="$WORKSPACE/logs"
mkdir -p "$LOG_DIR"
FILES_PATH="$LOG_DIR/pr-$PR_NUMBER-files.txt"
DIFF_PATH="$LOG_DIR/pr-$PR_NUMBER-full.diff"
SUMMARY_PATH="$LOG_DIR/pr-$PR_NUMBER-version-summary.tsv"
printf 'Checking PR #%s\n' "$PR_NUMBER"
printf 'Workspace: %s\n\n' "$WORKSPACE"
gh pr diff "$PR_NUMBER" --name-only > "$FILES_PATH"
gh pr diff "$PR_NUMBER" > "$DIFF_PATH"
python3 - "$FILES_PATH" "$DIFF_PATH" "$SUMMARY_PATH" <<'PY'
import pathlib
import re
import sys
files_path = pathlib.Path(sys.argv[1])
diff_path = pathlib.Path(sys.argv[2])
summary_path = pathlib.Path(sys.argv[3])
files = [line.strip() for line in files_path.read_text().splitlines() if line.strip()]
diff = diff_path.read_text(errors="ignore")
diff = re.sub(r"\x1b\[[0-9;]*m", "", diff)
version_rows = []
current = None
old = None
new = None
for line in diff.splitlines():
match = re.match(r"diff --git a/(.*?package\.json) b/(.*?package\.json)", line)
if match:
if current and (old or new):
version_rows.append((current, old or "", new or ""))
current = match.group(1)
old = None
new = None
continue
if current:
old_match = re.match(r'-\s+"version": "([^"]+)"', line)
new_match = re.match(r'\+\s+"version": "([^"]+)"', line)
if old_match:
old = old_match.group(1)
if new_match:
new = new_match.group(1)
if current and (old or new):
version_rows.append((current, old or "", new or ""))
changelog_versions = []
for line in diff.splitlines():
match = re.match(r"\+## ([^\s]+)", line)
if match:
changelog_versions.append(match.group(1))
non_release_files = [
f
for f in files
if not (
f == ".changeset/pre.json"
or f == "package.json"
or f == "CHANGELOG.md"
or f.endswith("/package.json")
or f.endswith("/CHANGELOG.md")
)
]
major_bumps = []
non_alpha_new_versions = []
stable_to_alpha = []
semver_re = re.compile(r"^(\d+)\.(\d+)\.(\d+)(-.+)?$")
for package_file, old_version, new_version in version_rows:
old_match = semver_re.match(old_version)
new_match = semver_re.match(new_version)
if old_match and new_match:
old_major = int(old_match.group(1))
new_major = int(new_match.group(1))
if new_major > old_major:
major_bumps.append((package_file, old_version, new_version))
if new_version and "-alpha." not in new_version:
non_alpha_new_versions.append((package_file, old_version, new_version))
if old_version and "-" not in old_version and "-alpha." in new_version:
stable_to_alpha.append((package_file, old_version, new_version))
with summary_path.open("w") as f:
f.write("package_json\told_version\tnew_version\n")
for row in version_rows:
f.write("\t".join(row) + "\n")
print("Changed files:", len(files))
print("Package version changes:", len(version_rows))
print("Changelog version headings:", len(changelog_versions))
print("Non release/version files:", len(non_release_files))
print("Major version bumps:", len(major_bumps))
print("New versions without -alpha:", len(non_alpha_new_versions))
print("Stable -> alpha transitions:", len(stable_to_alpha))
print()
print("Version changes:")
for package_file, old_version, new_version in version_rows:
print(f" {package_file}: {old_version} -> {new_version}")
print()
if stable_to_alpha:
print("Stable -> alpha transitions to review:")
for package_file, old_version, new_version in stable_to_alpha:
print(f" {package_file}: {old_version} -> {new_version}")
print()
if major_bumps:
print("WARNING: major version bumps found:")
for package_file, old_version, new_version in major_bumps:
print(f" {package_file}: {old_version} -> {new_version}")
print()
if non_alpha_new_versions:
print("WARNING: new versions without -alpha found:")
for package_file, old_version, new_version in non_alpha_new_versions:
print(f" {package_file}: {old_version} -> {new_version}")
print()
if non_release_files:
print("WARNING: files outside .changeset/pre.json, package.json, and CHANGELOG.md changed:")
for file in non_release_files:
print(f" {file}")
print()
if not major_bumps and not non_alpha_new_versions and not non_release_files:
print("Summary: no major bumps, non-alpha new versions, or non-release files detected.")
else:
print("Summary: review warnings above before approving/merging.")
PY
printf '\nWrote:\n'
printf ' %s\n' "$FILES_PATH"
printf ' %s\n' "$DIFF_PATH"
printf ' %s\n' "$SUMMARY_PATH"
#!/bin/bash
#
# Discover release smoke-test scope by exporting merged PRs since a release cutoff.
#
# Usage:
# ./discover-release-scope.sh [--release-tag <tag>] [--cutoff <iso-timestamp>] [--date <YYYY-MM-DD>] [--workspace <dir>]
#
# Examples:
# ./discover-release-scope.sh --release-tag '@mastra/core@1.28.0'
# ./discover-release-scope.sh --cutoff 2026-04-24T08:53:08Z
# ./discover-release-scope.sh --date 2026-04-28 --release-tag '@mastra/core@1.28.0'
set -euo pipefail
usage() {
sed -n '2,14p' "$0" | sed 's/^# \{0,1\}//'
}
require_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "Error: required dependency '$1' is not installed." >&2
exit 1
fi
}
SMOKE_DATE="$(date +%F)"
WORKSPACE=""
RELEASE_TAG=""
CUTOFF=""
LIMIT="200"
while [ $# -gt 0 ]; do
case "$1" in
--release-tag|--tag)
if [ $# -lt 2 ] || [ -z "${2:-}" ]; then
echo "Error: $1 requires a value." >&2
exit 1
fi
RELEASE_TAG="$2"
shift 2
;;
--cutoff)
if [ $# -lt 2 ] || [ -z "${2:-}" ]; then
echo "Error: --cutoff requires a value." >&2
exit 1
fi
CUTOFF="$2"
shift 2
;;
--date)
if [ $# -lt 2 ] || [ -z "${2:-}" ]; then
echo "Error: --date requires a value." >&2
exit 1
fi
SMOKE_DATE="$2"
shift 2
;;
--workspace)
if [ $# -lt 2 ] || [ -z "${2:-}" ]; then
echo "Error: --workspace requires a value." >&2
exit 1
fi
WORKSPACE="$2"
shift 2
;;
--limit)
if [ $# -lt 2 ] || ! [[ "${2:-}" =~ ^[1-9][0-9]*$ ]]; then
echo "Error: --limit requires a positive integer." >&2
exit 1
fi
LIMIT="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown argument: $1" >&2
echo "" >&2
usage >&2
exit 1
;;
esac
done
require_cmd gh
require_cmd git
require_cmd jq
if [ -z "$WORKSPACE" ]; then
WORKSPACE="$HOME/mastra-smoke-tests/$SMOKE_DATE"
fi
mkdir -p "$WORKSPACE/logs"
if [ -f "$WORKSPACE/merged-prs.tsv" ]; then
cp "$WORKSPACE/merged-prs.tsv" "$WORKSPACE/merged-prs.previous.tsv"
fi
if [ -z "$RELEASE_TAG" ]; then
RELEASE_TAG=$(gh release list \
--limit 50 \
--json tagName,isPrerelease,isDraft \
--jq '.[] | select(.isDraft == false and .isPrerelease == false and (.tagName | startswith("@mastra/core@"))) | .tagName' \
| head -n 1)
fi
if [ -z "$RELEASE_TAG" ] && [ -z "$CUTOFF" ]; then
echo "Error: could not infer a release tag. Pass --release-tag or --cutoff." >&2
exit 1
fi
if [ -z "$CUTOFF" ]; then
CUTOFF=$(gh release view "$RELEASE_TAG" --json createdAt --jq '.createdAt')
fi
if [ -z "$CUTOFF" ]; then
echo "Error: could not determine cutoff. Pass --cutoff." >&2
exit 1
fi
echo "Smoke workspace: $WORKSPACE"
echo "Release tag: ${RELEASE_TAG:-'(none; cutoff supplied)'}"
echo "Cutoff: $CUTOFF"
echo "Limit: $LIMIT"
echo ""
if [ -n "$RELEASE_TAG" ]; then
gh release view "$RELEASE_TAG" \
--json tagName,name,createdAt,publishedAt,targetCommitish,url \
--jq . \
> "$WORKSPACE/release.json"
git show -s --format='%H%n%ci%n%D%n%s' "$RELEASE_TAG" > "$WORKSPACE/release-git.txt" || true
fi
gh pr list \
--state merged \
--limit "$LIMIT" \
--search "merged:>=$CUTOFF -author:app/dependabot" \
--json number,title,author,mergedAt,labels \
--jq '. | sort_by(.mergedAt) | .[] | [.mergedAt, ("#"+(.number|tostring)), .author.login, .title, (([.labels[].name] | join(",")))] | @tsv' \
> "$WORKSPACE/merged-prs.tsv"
PR_COUNT=$(wc -l < "$WORKSPACE/merged-prs.tsv" | tr -d ' ')
cat > "$WORKSPACE/smoke-scope.md" <<EOF
# Release Smoke Scope
- Workspace: \`$WORKSPACE\`
- Release tag: \`${RELEASE_TAG:-none; cutoff supplied}\`
- Cutoff: \`$CUTOFF\`
- Merged PR export: \`merged-prs.tsv\`
- Non-Dependabot PR count: $PR_COUNT
## Categorization
Group every PR in \`merged-prs.tsv\` using the buckets from \`.claude/skills/mastra-smoke-test/SKILL.md\`, then add the targeted checks here before running smoke tests.
## Targeted checks
- [ ] Always: setup, agents, tools, workflows, traces, scorers, memory, MCP, errors
- [ ] CLI/create-mastra changes: fresh project at \`$WORKSPACE/smoke-project\`
- [ ] Server/API changes: curl checks for health, agents, tools, workflows, custom/invalid routes
- [ ] Studio/Playground changes: browser smoke affected pages
- [ ] Auth/permissions/Agent Builder changes: authenticated cloud smoke
- [ ] Storage/provider changes: package install/import or provider-specific smoke
- [ ] Mastra Code changes: separate Mastra Code/TUI smoke path
EOF
echo "Wrote:"
echo " $WORKSPACE/merged-prs.tsv ($PR_COUNT PRs)"
echo " $WORKSPACE/smoke-scope.md"
if [ -f "$WORKSPACE/merged-prs.previous.tsv" ]; then
PREVIOUS_COUNT=$(wc -l < "$WORKSPACE/merged-prs.previous.tsv" | tr -d ' ')
echo " $WORKSPACE/merged-prs.previous.tsv ($PREVIOUS_COUNT PRs)"
fi
if [ "$PR_COUNT" -ge "$LIMIT" ]; then
echo "" >&2
echo "Warning: PR count reached --limit ($LIMIT). Page or narrow by date range; do not silently truncate scope." >&2
fi
#!/bin/bash
#
# Test a deployed Mastra server by calling an agent endpoint
#
# Usage: ./test-server.sh <server-url> [agent-id] [message]
#
# Examples:
# ./test-server.sh https://my-project.server.staging.mastra.cloud
# ./test-server.sh https://my-project.server.mastra.cloud weather-agent
# ./test-server.sh https://my-project.server.staging.mastra.cloud weather-agent "What's the weather in Tokyo?"
set -e
# Check required dependencies
for cmd in curl jq; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "Error: required dependency '$cmd' is not installed."
exit 1
fi
done
SERVER_URL="${1:-}"
AGENT_ID="${2:-weather-agent}"
MESSAGE="${3:-What is the weather in Paris?}"
if [ -z "$SERVER_URL" ]; then
echo "Usage: $0 <server-url> [agent-id] [message]"
echo ""
echo "Examples:"
echo " $0 https://my-project.server.staging.mastra.cloud"
echo " $0 https://my-project.server.mastra.cloud weather-agent"
echo " $0 https://my-project.server.staging.mastra.cloud weather-agent \"What's the weather in Tokyo?\""
exit 1
fi
# Remove trailing slash if present
SERVER_URL="${SERVER_URL%/}"
echo "=== Testing Mastra Server ==="
echo "Server URL: $SERVER_URL"
echo "Agent ID: $AGENT_ID"
echo "Message: $MESSAGE"
echo ""
# Test health endpoint
echo "--- Health Check ---"
HEALTH_RESPONSE=$(curl -sS --connect-timeout 10 --max-time 30 -w "\n%{http_code}" "$SERVER_URL/health")
HEALTH_STATUS=$(echo "$HEALTH_RESPONSE" | tail -n 1)
HEALTH_BODY=$(echo "$HEALTH_RESPONSE" | sed '$d')
if [ "$HEALTH_STATUS" = "200" ]; then
echo "✅ Health check passed: $HEALTH_BODY"
else
echo "❌ Health check failed (HTTP $HEALTH_STATUS): $HEALTH_BODY"
exit 1
fi
echo ""
# Test agent endpoint
echo "--- Agent Test ---"
echo "Calling $AGENT_ID with: \"$MESSAGE\""
echo ""
# Use jq for safe JSON construction (handles special characters in MESSAGE)
JSON_BODY=$(jq -n --arg msg "$MESSAGE" '{"messages":[{"role":"user","content":$msg}]}')
RESPONSE=$(curl -sS --connect-timeout 10 --max-time 60 -w "\n%{http_code}" \
-X POST "$SERVER_URL/api/agents/$AGENT_ID/generate" \
-H "Content-Type: application/json" \
-d "$JSON_BODY")
# Parse response and status (status is always last line)
STATUS=$(printf '%s\n' "$RESPONSE" | tail -n 1)
BODY=$(printf '%s\n' "$RESPONSE" | sed '$d')
if [ "$STATUS" = "200" ]; then
echo "✅ Agent response (HTTP $STATUS):"
echo "$BODY" | jq -r '.text // .content // .' 2>/dev/null || echo "$BODY"
else
echo "❌ Agent call failed (HTTP $STATUS):"
echo "$BODY" | jq '.' 2>/dev/null || echo "$BODY"
exit 1
fi
echo ""
echo "=== Test Complete ==="