satorntcg-mcp-server
OfficialSummary: An MCP server that lets an LLM client (Claude Desktop/Code) query live TCG resale data and run parts of a TCGplayer order/shipping workflow conversationally, backed by Supabase/Postgres views.
Pricing & alerts — list active non-dismissed price alerts, gainers/losers, stale listings, and eBay listings whose price has drifted from market (
get_price_alerts).Inventory — see cards on hand, quantities owned vs. listed, and latest market prices, filterable by card name (
get_inventory_summary).Price lookups — get TCGplayer market price and your cost basis per card to compare paid-vs-worth (
get_latest_prices).Profit & loss — per-box P&L from purchase price vs. realized/unrealized pulled-card value (
get_box_pnl), plus overall business P&L (get_global_pnl).eBay listings — retrieve currently active or recently sold listings (
get_ebay_listings).TCGplayer orders — view orders and line items, filtered by status (new/shipped) or order number (
get_tcgplayer_orders).Order capture (write) — create a TCGplayer order plus line items and matching auto-listings, with idempotent re-runs and a duplicate-sale guard against manual listings (
create_tcgplayer_order).Sync auditing — find orders whose listing/inventory sync never completed, optionally since a given date (
get_orders_missing_listings).Packing slips — generate TCGplayer packing-slip PDFs for one or more orders (auto-selecting envelope vs. full-page by order total), saved locally (
print_packing_slip); needs a Seller Portal session cookie.Diagnostics — test outbound HTTPS connectivity to Supabase (
debug_network).Operational extras — every call is OpenTelemetry-traced (tool spans + nested DB query spans) exported to Jaeger; a bundled CLI agent (
agent/run-agent.js) drives the read-only tools with Claude and reports token/tool-call diagnostics.
Provides tools for retrieving active and sold eBay listings, enabling users to query eBay sales and listing data related to their TCG inventory.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@satorntcg-mcp-serverWhat's underpriced in my inventory right now?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
satorntcg-mcp-server
An MCP server that exposes live TCG resale/inventory data — pricing, alerts, profit & loss, eBay listings — as tools an LLM client (Claude Desktop, Claude Code, etc.) can call directly. Instead of opening a dashboard, you can just ask: "what's underpriced right now" or "how is the Gothic box performing" and get an answer grounded in real data.
Built on top of SatornTCG, a Sorcery: Contested Realm resale/content operation backed by Supabase/Postgres.
Why this exists
This started as a resume project to learn MCP server design and observability, and turned into a tool I actually use day to day to query my own inventory and pricing data conversationally instead of clicking through a UI.
Related MCP server: mcp-tcgdex
What it exposes
Tool | Description |
| Active price alerts, gainers/losers, stale listings, listing-price drift |
| Current inventory: quantities owned/listed, latest prices |
| TCGplayer market price + cost basis lookup by card name |
| Profit & loss per box (purchase price vs. realized/unrealized pull value) |
| Overall business P&L across all boxes and listings |
| Active or sold eBay listings |
Each tool is a thin wrapper around a Postgres view — see src/tools/.
Architecture
Claude Desktop / Claude Code (MCP client)
│ stdio
▼
src/index.js ── registers tools, routes tool calls
│
▼
src/tools/*.js ── one file per domain, each tool = { name, description, inputSchema, handler }
│
▼
src/supabaseClient.js ── service-role Supabase client
│
▼
Postgres (Supabase) ── views: v_active_alerts, v_inventory_dashboard,
v_latest_prices, v_box_pnl, v_global_pnl,
v_ebay_active, v_ebay_sold, v_stale_listings,
v_price_gainers_losers, v_listing_price_alertsEvery tool call is traced with OpenTelemetry, exported to Jaeger — see Observability below.
Schema this expects
This server doesn't ship a demo database — it's meant to be pointed at a Postgres/Supabase project with equivalent views. If you're adapting this for your own data, the views above are just SELECTs over your own tables; swap the query in each src/tools/*.js file to match your schema. Column names referenced directly (e.g. card_id, name, tcg_market_price, cost_basis, dismissed) are the main thing to line up.
Setup
git clone <this-repo>
cd satorntcg-mcp-server
npm install
cp .env.example .env
# fill in SUPABASE_URL and SUPABASE_SERVICE_KEY in .env
npm startThe server speaks MCP over stdio, so it's meant to be launched by an MCP client, not run standalone in a terminal for interactive use. To test with Claude Desktop, add it to your MCP config (claude_desktop_config.json):
{
"mcpServers": {
"satorntcg": {
"command": "node",
"args": ["/absolute/path/to/satorntcg-mcp-server/src/index.js"]
}
}
}Restart Claude Desktop, and the tools above become available in conversation.
Agent runner (CLI)
agent/run-agent.js asks Claude one question and lets it call this server's read-only tools (get_inventory_summary, get_latest_prices) over MCP until it has an answer. It spawns src/index.js itself, so no MCP client is needed.
# .env needs SUPABASE_URL, SUPABASE_SERVICE_KEY and ANTHROPIC_API_KEY
ANTHROPIC_MODEL=claude-opus-5-5 node agent/run-agent.js "Show me my 5 most profitable cards"(PowerShell: $env:ANTHROPIC_MODEL="claude-opus-5-5"; node agent/run-agent.js "...")
It prints Claude's answer, then a diagnostics block with the stop reason, token counts, and each tool call (arguments, status, row count, duration). Tool output rows are never printed in diagnostics. Safeguards:
Only the two allowlisted tools are offered. Any other tool name Claude requests is rejected without being forwarded.
Arguments are checked against the tool's
inputSchemafromtools/listbeforetools/call. Unknown keys are rejected too. The server still runs its own validation.After 5 rounds of tool calls it stops and exits non-zero.
This queries the real Supabase project (read-only views). The loop logic is in agent/agentCore.js and is covered offline by test/agent.test.js.
Observability
Every tool call is wrapped in an OpenTelemetry span (tool.<name>), with each Supabase query the tool makes nested underneath it as its own db.query child span. That split is the point: a slow tool call in Jaeger tells you at a glance whether the time went into the DB round-trip or into everything else the handler did (matching, mapping, duplicate-guard checks, etc.), instead of being one opaque block of latency.
Each tool.* span carries:
mcp.tool.name,mcp.tool.args— which tool, called with whatmcp.tool.row_count— best-effort row count from the result, when there's an obvious array to countmcp.tool.duration_ms— total handler wall timemcp.tool.slow—trueoncemcp.tool.duration_msexceedsSLOW_TOOL_THRESHOLD_MS(1000ms, insrc/index.js) — lets you filter straight to the calls worth looking at without eyeballing durationsspan status
OK/ERROR, with the exception recorded on error
Each db.query child span carries db.table and, on success, db.row_count.
Traces export over OTLP/HTTP to OTEL_EXPORTER_OTLP_ENDPOINT (defaults to http://localhost:4318/v1/traces, i.e. a local collector/Jaeger — see .env.example). Spans are flushed on shutdown (SIGTERM/SIGINT, or the client closing the server's stdin), so a normal Claude Desktop restart doesn't lose the last few tool calls; see src/tracing.js for the shutdown handling and the reasoning behind its timeout.
Shutdown timing. The MCP server and the agent CLI both wait at most 2s (SHUTDOWN_TIMEOUT_MS in src/tracing.js) for spans, and the agent's metrics, to flush. That bounds the wait. It is not a guaranteed deadline for the process to exit:
On
SIGTERM/SIGINTthe process callsprocess.exit()as soon as the wait ends. It exits within about 2s, and anything still being sent is dropped.On a natural exit (the CLI finishing, or the server's stdin closing), Node only exits once nothing is left running, including an export still in flight. So each trace export is capped at 1.5s, retries included (
TRACE_EXPORT_TIMEOUT_MS; the OTLP default is 10s).Jaeger hung, or answering 429/502/503/504: exit happens about 1.5s late.
Jaeger not running (connection refused): no delay.
Either way, that run's spans are lost.
OTEL_EXPORTER_OTLP_TRACES_TIMEOUTorOTEL_EXPORTER_OTLP_TIMEOUT(milliseconds) replaces the 1.5s default, e.g. for a slower remote backend. A value above 2000 lets a natural exit wait that long.The timeout covers every export, including the batches the server sends every 5s while running. A local Jaeger answers in milliseconds; a batch that takes longer than 1.5s is dropped.
Agent traces
agent/run-agent.js exports its own spans (service satorntcg-agent, same exporter setup from src/tracing.js), one trace per run:
agent.request request-level attributes (table below)
├─ mcp.setup server spawn + tools/list
├─ claude.messages.create agent.round=0, tokens, finish reason
├─ mcp.call_tool get_inventory_summary agent.round=1, arg key names, agent.tool.status
│ └─ tool.get_inventory_summary (satorntcg-mcp-server)
│ └─ db.query (satorntcg-mcp-server)
└─ claude.messages.create agent.round=1agent.request attributes:
Attribute | Value |
|
|
|
|
| Last Claude |
| Tool uses Claude requested that the agent handled (executed or rejected). Same count as the diagnostics block |
| Rounds of tool execution whose results went back to Claude. Same as |
| Totals across every Claude call that returned. These are |
agent.outcome uses only what a run actually shows:
error: MCP setup failed, the Anthropic API returned an error, something threw, or the run hitmax_tool_rounds. The span status isERRORas well.unknown: everything else, including every normalend_turn. The agent gets back only free text, and an answer, a clarifying question ("which metric?") and a "can't do that with these tools" reply all end the same way. Classifying them would mean guessing from the text or from whether tools were called, soanswered,clarificationandunsupportedare defined inOUTCOMESbut never emitted. Emitting them needs a structured signal from the model, such as a final-response tool or output schema, which would change the agent's behavior. A run interrupted by a signal also keepsunknown, with aninterrupted by …error status.When a run fails before the agent loop returns, the two tool counts are left unset rather than reconstructed. Token totals still cover the Claude calls that completed.
Every span has
agent.duration_msand anOK/ERRORstatus.agent.tool.statusisok,tool_error(the tool returnedisError) ormcp_error(the protocol call threw). Tool uses the agent rejected before calling MCP (not allowlisted, invalid arguments) appear astool_use_rejectedevents onagent.request.Claude spans carry
gen_ai.usage.input_tokens/output_tokens(plus cache read/creation tokens when present),gen_ai.request.model,gen_ai.response.modelandgen_ai.response.finish_reasons.Not recorded in span attributes: the API key, the question or system prompt, message content, tool argument values, or anything from a tool result. The server's own
tool.*span still recordsmcp.tool.argsas described above.Known gap: NodeSDK's default resource detectors attach
process.command_argsto every agent trace, and for this CLI that includes the question. They also attachprocess.owner,host.nameandprocess.pid. Metrics avoid this with their own resource (see Agent metrics). Traces still carry it.On Ctrl+C or
SIGTERM, open spans are ended with aninterrupted by SIGINT/SIGTERMerror status and flushed before exit.OTEL_SDK_DISABLED=trueturns agent tracing off.
How the trace crosses into the server. stdio has no headers, so the agent sends W3C traceparent/tracestate in the tools/call request's params._meta, MCP's general-purpose metadata field, and src/index.js extracts it as the parent of its tool.* span. These keys are a convention between this agent and this server, not something MCP itself defines. Limitations:
Only
tools/callcarries context.initializeandtools/listaren't traced on the server, somcp.setuphas no server children.Clients that don't send these keys (Claude Desktop, Claude Code) get the old behavior: each tool call starts its own trace under satorntcg-mcp-server.
Because both processes export separately, the server's spans can show up a moment after the agent's if you open the trace immediately.
Agent metrics
agent/run-agent.js also records OpenTelemetry metrics (agent/agentMetrics.js):
Metric | Type | Labels | Recorded |
| counter, |
| Once per request, whether it succeeds, fails or is interrupted |
| histogram, |
| Same moment. Covers MCP server spawn through shutdown. Buckets 250ms–120s |
| counter, |
| Per MCP |
| counter, |
| Per Claude response, from |
agent.outcomeis the same value as on theagent.requestspan (errororunknown; see above). An interrupted run is counted asunknown.Labels are limited to the model ID, the outcome, and an allowlisted tool name. Any other name is recorded as
other. The question, card names, argument values and request IDs are never used as labels.A usage error (no question, no
ANTHROPIC_MODEL) never starts a request, so it isn't counted.Flush on exit: in
sdk-metrics1.x, shutting down aPeriodicExportingMetricReaderdoes not export what was recorded since the last 60s interval. That's every metric from a CLI run.shutdownTracing()insrc/tracing.jstherefore callsforceFlush()on the reader before shutting it down. The trace flush runs at the same time, not after it, so a slow Collector can't hold up traces.test/agentMetrics.test.jschecks both in child processes. For how long exit can take when the Collector misbehaves, see the limitations under Local Prometheus.
Where metrics go. Off by default. OTEL_METRICS_EXPORTER (the standard OTel variable) selects the exporter:
unset /
none: noMeterProvideris registered, and the instruments are no-ops.otlp: OTLP/HTTP to the local Collector inobservability/(see below). The default URL ishttp://localhost:4319/v1/metrics; override it withOTEL_EXPORTER_OTLP_METRICS_ENDPOINT. The agent never derives this URL fromOTEL_EXPORTER_OTLP_ENDPOINT, which is Jaeger's full traces URL.console: at exit, writes one JSON line per series to stderr (stdout stays just the answer). Use it for debugging without the stack:$env:OTEL_METRICS_EXPORTER = "console" node agent/run-agent.js "What does cost basis mean?" 2> metrics.log Select-String "\[metrics\]" metrics.loganything else: prints a warning, and nothing is exported.
Jaeger is a tracing backend and has no metrics receiver, so these metrics never appear in Jaeger.
Metrics use their own MeterProvider (src/tracing.js), whose resource is just service.name plus the SDK's constant telemetry.sdk.* attributes. NodeSDK's auto-detected resource, which traces still use, includes process.command_args (the question), process.owner, host.name and process.pid. The Prometheus exporter would turn those into labels, and a per-run pid would make every run its own series.
Local Prometheus (OpenTelemetry Collector)
observability/docker-compose.yml runs three containers, with every port bound to 127.0.0.1:
Service | Image | Host port | Role |
|
|
| Receives agent metrics (metrics pipeline only) |
| Prometheus exposition of the Collector's running totals | ||
|
|
| Scrapes |
|
|
| UI over Prometheus (provisioned data source, no dashboards yet) |
Traces don't go through the Collector. The agent and MCP server keep exporting straight to the standalone Jaeger container on 4318 (below), which is why the Collector takes 4319. Jaeger isn't in the Compose file, because it would collide with that container's name and ports.
Short-lived processes and counters. Every CLI run is a new process, and its cumulative counters start at 0. If each run exported cumulative values, the Collector would just replace the last run's agent.requests = 1 with the next run's 1, and the total would never grow. So the agent exports delta temporality, and the Collector's delta_to_cumulative processor adds each run onto one running series per label set. That works because the metric resource is identical for every run. Limitations:
The totals live in Collector memory. Restarting the Collector, or
docker compose down, resets them to 0. Prometheus treats that as an ordinary counter reset, and its stored history survives in theprometheus-datavolume.Idle series expire. A series not updated for 168h (
max_stale/metric_expiration) is dropped and starts again from 0 on its next run.increase()/rate()are approximate here. Prometheus extrapolates across sparse samples, and doesn't count a series' first value as an increase. Over a short window,sum(increase(agent_requests_total[1h]))came out as 3.2 when the true number was 1. Use them for trends. For exact counts, read the counter itself:sum(agent_requests_total)is the total since the Collector started.Metrics from a run are lost if the Collector is down, hung or returning errors when the run exits. Nothing is buffered on disk, and the failure is silent unless
OTEL_LOG_LEVEL=erroris set. How much later the CLI exits:Collector down (connection refused): no noticeable delay, the export fails at once.
Collector hung, or answering 429/502/503/504: about 1.5s. The metric exporter's
timeoutMillis(OTLP_EXPORT_TIMEOUT_MSinagent/agentMetrics.js) covers the request and its retries. The default would be 10s.Traces still reach Jaeger in both cases, because they're flushed alongside metrics rather than after them.
The 2s shutdown wait isn't a process deadline (see Shutdown timing). Traces and metrics time out separately and at the same time, so even with both the Collector and Jaeger hung, the CLI exits about 1.5s late.
delta_to_cumulativeis an alpha Collector component.
Name and unit changes. The Collector's Prometheus exporter rewrites OTel names:
OTel metric (unit) | Prometheus series |
|
|
|
|
|
|
|
|
Dots become underscores, in metric names and in labels:
gen_ai_request_model,agent_outcome,mcp_tool_name,agent_tool_outcome.Monotonic counters get
_total.Units in braces (
{request},{token}) are dropped;msbecomes_milliseconds.service.namebecomes thejoblabel (satorntcg-agent, kept byhonor_labels: true), andotel_scope_nameis added.Prometheus 3 stores histogram
levalues as floats ("2500.0").
Start, verify, stop (PowerShell, from the repo root):
# Start the stack (Jaeger keeps running separately)
docker compose -f observability/docker-compose.yml up -d
docker compose -f observability/docker-compose.yml ps
# Run the agent with OTLP metrics
$env:OTEL_METRICS_EXPORTER = "otlp"
node agent/run-agent.js "What does cost basis mean?"
# Wait one scrape interval (15s), then query Prometheus
Start-Sleep 20
Invoke-RestMethod "http://localhost:9090/api/v1/query?query=agent_requests_total" | ConvertTo-Json -Depth 6
Invoke-RestMethod "http://localhost:9090/api/v1/query?query=agent_tokens_input_total" | ConvertTo-Json -Depth 6
# What the Collector is currently exposing, before Prometheus scrapes it
(Invoke-WebRequest http://localhost:8889/metrics -UseBasicParsing).Content -split "`n" | Select-String "^agent_"
# Stop (add -v to also delete Prometheus' stored history)
docker compose -f observability/docker-compose.yml down
Remove-Item Env:OTEL_METRICS_EXPORTERGrafana. Prometheus is provisioned as the default data source from observability/grafana/provisioning/datasources/prometheus.yml, at http://prometheus:9090 (the Compose service name, not localhost, because Grafana's backend makes the request from inside its container). Log in at http://localhost:3000 as admin with Grafana's default password, which it asks you to change on first login. To choose the password up front instead, set GRAFANA_ADMIN_PASSWORD (shell or .env) before the first up; it only applies when the grafana-data volume is created. To check the connection (prompts for the password):
$cred = Get-Credential admin
Invoke-RestMethod http://localhost:3000/api/datasources/uid/prometheus/health -Credential $cred # status: OKOr open http://localhost:9090 and try these queries:
agent_requests_totalsum by (agent_outcome) (agent_requests_total)agent_tokens_input_totalagent_tool_calls_totalhistogram_quantile(0.5, sum by (le) (agent_request_duration_milliseconds_bucket))
After a run, each counter should have grown by that run's values. The token counters grow by the Tokens: line in that run's diagnostics block.
Running Jaeger locally
Jaeger's all-in-one image accepts OTLP/HTTP on the default port this server already exports to, so no extra config is needed:
docker run -d --name jaeger \
-p 16686:16686 \
-p 4318:4318 \
jaegertracing/all-in-one:latestThen start the server as usual (npm start, launched by Claude Desktop or run directly) and make a few tool calls. Open the Jaeger UI at http://localhost:16686, pick satorntcg-mcp-server from the Service dropdown, and click Find Traces. For agent runs, pick satorntcg-agent instead and open an agent.request trace; the server's tool.* and db.query spans appear nested under the agent's mcp.call_tool spans.

Security note
SUPABASE_SERVICE_KEY is the elevated service-role key, not the public anon key. This is safe here because the server only ever runs as a local process launched by a trusted MCP client (never in a browser bundle) — the same reasoning as using a service role key inside a Supabase Edge Function. Never commit .env; only .env.example (placeholder values) is tracked.
Roadmap
suggest_listingtool — draft an eBay listing from inventory + pricing dataHTTP/SSE transport for remote access, alongside the local stdio transport
Export OTel traces to a hosted backend (Honeycomb/Grafana) for a demo, in addition to local Jaeger
License
MIT — see LICENSE.
Available Tools
11 toolscreate_tcgplayer_orderA
Create a TCGplayer order and its line items, matching each line item to a card by exact name (foil printings are matched literally, e.g. "Sinterfee (Foil)" — do not strip "(Foil)" from card_name_raw). Also creates a matching "active" listing in tcgplayer_listings (same shape the dashboard's manual "create listing" flow produces: quantity_listed bumped, no sold_price/fee/cost_basis/net_profit/quantity_owned changes yet) — even though the order email means TCGplayer already has a committed buyer, these are booked as placeholders on file and finalized later through the existing chat-based "mark sold" flow once the packing slip is printed and the order actually ships. Safe to re-run: if order_number already exists, its line items are never (re-)created, and its listing/inventory sync is only (re-)attempted if it never completed the first time (e.g. a prior call errored or timed out after the order was recorded but before the listing was synced) — so a retry after a failed call finishes the sync instead of silently no-oping. Also guards against duplicating a sale that was already recorded manually: if any of this order's cards has a manually-created tcgplayer_listings row (not one this tool made) within 3 days of the order date, listing creation is skipped (the order/items are still recorded) and the response says which existing listing to check. Two auto-created listings for the same card from different orders are never treated as duplicates — each has its own order_number, so they're genuinely separate sales.
| Name | Required | Description | Default |
|---|---|---|---|
| items | Yes | Line items parsed from the order email. | |
| ship_by | No | Ship-by date, YYYY-MM-DD. | |
| ordered_at | No | ISO timestamp of when the order was placed. | |
| order_total | No | Aggregate order total from the email. | |
| order_number | Yes | e.g. F0DFDDC3-1965A5-DEF21 | |
| gmail_message_id | No | ||
| manage_order_url | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It thoroughly explains the placeholder listing creation, idempotency (safe to re-run), retry semantics, and the manual-listing duplicate guard. It also clarifies that two auto-created listings for the same card are not duplicates. This is exceptional transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence earns its place, covering essential behavioral nuances. It is front-loaded with the main purpose before diving into edge cases. While it could be tightened, the length is justified by the complexity of the tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with this complexity (order creation, listing sync, duplicate detection, retry logic), the description is remarkably complete. It explains the placeholder nature, the manual-listing check, the retry behavior, and even mentions the response indicates which existing listing to check. Nothing critical is missing for an agent to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 71%, so the baseline is 3. The description adds significant meaning beyond the schema: it explains that card_name_raw must include the '(Foil)' suffix and not be stripped, and it details how items relate to listing creation. It does not explain gmail_message_id or manage_order_url, but these are likely self-explanatory metadata. The description compensates well for the coverage gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool creates a TCGplayer order and its line items, matching cards by exact name. It distinguishes itself from read-only siblings like get_tcgplayer_orders by emphasizing the creation and listing side-effects. The verb+resource is specific and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context: it is used when processing an order email, and it explains the downstream 'mark sold' flow for finalization. It also covers retry behavior and duplicate handling, but does not explicitly name alternative tools for similar actions. The context is sufficient for an agent to decide when to invoke it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
debug_networkA
DIAGNOSTIC ONLY: tests outbound HTTPS connectivity from this server process to a generic endpoint and to the configured Supabase URL, returning full error detail for troubleshooting.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure. It states the operation is diagnostic-only, which implies no data mutation, and promises full error detail for troubleshooting. It does not explicitly state that the tool makes no changes to system state or that network requests could be slow, but 'DIAGNOSTIC ONLY' covers the most important trait.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One efficient sentence with the 'DIAGNOSTIC ONLY' warning front-loaded, followed by the action and its purpose. Every word earns its place and there is no redundant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter diagnostic tool with no output schema, the description covers what it tests, where it tests to, and the nature of the result (full error detail). It provides sufficient context for an agent to decide to invoke it during connectivity troubleshooting, though it could add a note about potential network timeout behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and 100% schema coverage, so the baseline is 4 per the rubric. The description complements the empty schema by stating exactly what connectivity will be tested, but there is no parameter information to add.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('tests') with a clear resource ('outbound HTTPS connectivity'), and specifies two targets: a generic endpoint and the configured Supabase URL. It is immediately distinguishable from the sibling tools, which are all business-data operations (inventories, prices, orders), not diagnostics.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The leading 'DIAGNOSTIC ONLY' label clearly signals that this tool is for troubleshooting, not routine use, providing an implicit exclusion. It names its purpose—testing connectivity to the Supabase URL—but does not explicitly name alternative diagnostic tools or state when not to use it. Still, the diagnostic context is unmistakable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_box_pnlA
Get profit & loss per box (purchase price vs. realized/unrealized value of pulled cards). Use for "was Box X worth it" or "how is Gothic performing overall" questions.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| box_id | No | Optional specific box id to filter to. Omit to get all boxes. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the behavioral burden. It usefully defines the PnL formula, but it does not disclose how results are aggregated or ordered, what the default behavior is when box_id is omitted, or what the return structure looks like.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences with no filler. The core purpose is front-loaded, followed by concrete example questions that help an agent select the tool quickly.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is adequate for tool selection but not fully sufficient for invocation: there is no output schema, no annotations, limit semantics are unexplained, and the relationship to the similar sibling get_global_pnl is not addressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema covers box_id with a description, but limit has no schema description. The tool description adds 'per box' context and implies optional filtering, but it never explains the limit parameter's meaning, default, or effect, leaving one of two parameters effectively undocumented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb and resource: 'Get profit & loss per box' and explains the calculation basis ('purchase price vs. realized/unrealized value of pulled cards'). This distinguishes it from sibling tools like get_global_pnl, which would address portfolio-level P&L.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit use cases: 'was Box X worth it' or 'how is Gothic performing overall' questions. It gives clear context for when to use the tool, though it does not explicitly contrast it with alternatives like get_global_pnl or state when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_ebay_listingsA
Get eBay listings, either currently active or sold, for questions like "what do I have listed right now" or "what has sold recently".
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| status | Yes | Which set of listings to return. | active |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It implies a read-only operation by using 'Get', but does not explicitly state that it has no side effects or that it returns a list of listing objects. It also does not mention pagination, sorting, or any rate limits. For a simple get operation, this is acceptable but not thorough; a 3 reflects the absence of any explicit behavioral caveats.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, front-loaded sentence that states the core action and includes two illustrative examples. There is no redundant wording or unnecessary detail. It is concise and immediately informative, earning full marks for structure.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has only two parameters and no output schema, so the description should clarify what the response contains. It states that it returns 'listings' but does not specify fields (e.g., listing ID, title, price) or the shape of the response. It also does not mention how 'limit' affects the output. For a simple list tool, this is a moderate gap; an agent might need to infer the response structure. Given the low complexity, a 3 is appropriate.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already describes the 'status' parameter with an enum and a short description, and the tool description reinforces its meaning with 'active or sold'. The 'limit' parameter has no schema description and is not mentioned in the tool description, but its purpose (limiting the number of results) is self-evident. Since schema coverage is 50%, the description partially compensates for status but not for limit, which is a minor gap. Overall, the description adds some value beyond the schema but does not fully compensate for the missing limit explanation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'Get' and the resource 'eBay listings', and further specifies the two possible statuses (active/sold) with concrete example questions. This distinguishes it from sibling tools like get_inventory_summary or get_latest_prices, which focus on different data. The purpose is unambiguous and immediately actionable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear usage context through example questions ('what do I have listed right now' or 'what has sold recently'), which effectively tells an agent when to invoke this tool. It does not explicitly name alternatives or exclusions, but the examples and the tool's focus on listings make the appropriate scenario obvious. A small deduction for not explicitly stating when not to use it, but the guidance is strong.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_global_pnlA
Get overall business P&L across all boxes/listings (total spend vs. total realized + unrealized value). Use for "how is the business doing overall" questions.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It clearly explains what the result represents (total spend vs. realized + unrealized value), but it does not explicitly state read-only behavior, data freshness, or any side effects. The 'Get' verb implies non-destructive behavior, but that is not made explicit.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two short sentences with no filler. The first sentence states the core function and formula, and the second sentence gives a direct usage example. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter tool with no annotations and no output schema, the description is largely complete: it states what is computed, the scope, and when to use it. It does not explicitly describe the return format, but the named components ('total spend', 'realized + unrealized value') give sufficient context for an agent to interpret the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so there is no parameter semantics to document. Per the calibration baseline, 0 parameters warrants a 4; the description appropriately focuses on the tool's purpose rather than on nonexistent inputs.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Get') and a clear resource ('overall business P&L across all boxes/listings'). It also specifies the computation basis ('total spend vs. total realized + unrealized value'), which distinguishes it from more granular siblings like get_box_pnl.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives an explicit use case: 'Use for "how is the business doing overall" questions.' This provides clear context for when to invoke the tool, though it does not explicitly name alternatives or state when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_inventory_summaryA
Get the current inventory dashboard: cards on hand, quantities owned/listed, and latest market prices. Use for questions like "what do I have in stock" or "what am I sitting on that I should list".
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max rows to return. | |
| name_contains | No | Optional case-insensitive filter on card name. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It only says 'Get' which implies a read operation, but does not explicitly state it is read-only, nor does it mention any side effects, caching, or data freshness. For a tool with zero annotation coverage, this is a significant gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no filler. The first sentence states the exact functionality and outputs; the second provides concrete usage examples. It is front-loaded and every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description should explain the return format. It mentions the data types (cards on hand, quantities, prices) but not the structure (e.g., single summary object, array, pagination). The limit parameter hints at possible pagination but it's not described. Given the absence of annotations and output schema, the description is adequate but not fully complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%: both parameters (limit, name_contains) have descriptions in the schema. The tool description adds no parameter-specific information, but the schema already documents them adequately. The baseline of 3 applies because the description doesn't compensate or add extra context, but the schema covers the essentials.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool retrieves the current inventory dashboard, listing specific data (cards on hand, quantities owned/listed, latest market prices). It provides example questions ('what do I have in stock', 'what am I sitting on that I should list') that make the purpose unmistakable and distinguish it from siblings like get_latest_prices (price-specific) or get_box_pnl (profit/loss).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says 'Use for questions like...' giving direct when-to-use guidance. It doesn't mention when not to use it or name alternatives, but the provided examples are sufficient to route an agent correctly for inventory summary queries.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_latest_pricesA
Get latest TCGplayer market prices and cost basis for cards, optionally filtered by name. Use for "what is X worth" or "what did I pay vs. what is it worth now" questions.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| name_contains | No | Optional case-insensitive filter on card name. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry behavioral disclosure. It correctly conveys a read operation and the returned data type (market prices plus cost basis), which makes side effects unlikely. However, it omits response shape, pagination/limit behavior, and any freshness or authorization caveats, so transparency is adequate but incomplete.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with no filler: the first states function and filtering capability, the second provides concrete use cases. The key action is front-loaded and every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple optional-parameter getter, the description covers purpose and common use cases. Yet because there is no output schema, it should clarify return format and limit semantics; those gaps prevent full completeness for an agent selecting and invoking the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 50%; 'limit' has no schema description. The tool description rephrases name_contains as 'optionally filtered by name' but adds no detail about the meaning or effect of limit (e.g., maximum result count). Thus the description does not meaningfully compensate for the undocumented parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description opens with a specific verb and resource: 'Get latest TCGplayer market prices and cost basis for cards.' It also notes the optional name filter. This clearly separates it from siblings like get_price_alerts or get_global_pnl, which concern alerts or P&L rather than current market prices.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The second sentence explicitly maps the tool to user intents: 'what is X worth' and 'what did I pay vs. what is it worth now.' It gives clear contexts for use but does not name sibling tools or state when not to use it, so it stops short of full exclusion guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_orders_missing_listingsA
Find TCGplayer orders whose listing/inventory sync never completed — a direct comparison of tcgplayer_orders/tcgplayer_order_items against tcgplayer_listings, not an inference from quantity_listed or updated_at (those can shift for unrelated reasons, e.g. a price refresh, and give a false read). An order counts as synced if it has its own auto-created listing (tcgplayer_listings.notes = "Auto-created from TCGplayer order <order_number>"), or if listing creation was correctly skipped because a manual listing already covered one of its cards within 3 days of the order date — the same duplicate-sale guard create_tcgplayer_order itself uses. An auto-created listing belonging to a different order never counts as coverage, since that guard only ever fires against manual listings. Anything else is flagged as missing, listing every line item on the order since a failed sync never lists any of them. Run with since set to the start of an order-capture run to catch a bad sync within the hour; run with no since for a full backlog sweep.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max number of missing orders to return. checked_count/missing_count still reflect the full since-filtered set. | |
| since | No | Optional ISO date/datetime — only check orders with created_at >= this. Omit to check every order on file. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It explains the exact matching logic, the duplicate-sale guard, the false-positive risk from price refreshes, and the edge case where auto-created listings from other orders do not count as coverage. This is unusually transparent about how the tool computes results.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and the most important caveat about false reads, followed by precise edge-case details that genuinely matter. It is long and dense, but no sentence is filler; a slightly tighter structure could make it easier to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex tool with no output schema and no annotations, the description provides the core output semantics ('listing every line item on the order') and covers all important matching/coverage rules. It stops short of explicitly describing the full response envelope, such as order fields or counts, but an agent still has enough to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the parameters are already documented. The description adds meaningful usage context for `since` by explaining when to set it versus omit it, and the `limit` parameter is already well explained by the schema. This is a clear improvement over the baseline, though it does not add much beyond the schema for `limit`.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource: 'Find TCGplayer orders whose listing/inventory sync never completed.' It also distinguishes itself from naive alternatives by explaining that it is not an inference from quantity_listed or updated_at, which makes the purpose and scope unmistakable even among sibling order tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives concrete guidance on when to invoke it: with `since` set to the start of an order-capture run to catch a bad sync quickly, or with no `since` for a full backlog sweep. It does not explicitly name an alternative or state when-not-to-use this tool, but the context is clear enough for an agent to select it correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_price_alertsA
Get current, non-dismissed price alerts, plus optional context on eBay listing price alerts and stale listings. Use this to answer questions like "what should I look at today" or "anything moving I should react to".
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max rows to return per category. | |
| include_listing_alerts | No | Also include eBay listings whose price has drifted vs. market (from v_listing_price_alerts). | |
| include_stale_listings | No | Also include listings that have been active a long time without selling (from v_stale_listings). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the full burden. It discloses that the tool returns current, non-dismissed alerts and optionally includes listing and stale data, which implies a read-only operation. It does not mention permissions, rate limits, or return format, but for a simple getter the information is adequate; it adds value beyond the schema by clarifying the 'non-dismissed' state and the optional inclusions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, with the primary function stated first and the usage examples following. There is no fluff; every word contributes to understanding what the tool does and when to use it. It is efficiently structured and front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has no output schema, the description does not explain the response structure, which could be important for an agent to parse the returned data. It implies the output includes sections for price alerts, listing alerts, and stale listings, but does not detail fields or pagination. For a tool of moderate complexity, the description is somewhat incomplete without return format details.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so each parameter already has a clear description. The tool description adds minimal extra meaning—only the phrase 'optional context' for the two boolean parameters, which mirrors the schema. It does not provide syntax, types, or additional semantics beyond what the schema already gives.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool retrieves current, non-dismissed price alerts with optional context for eBay listing and stale listings. It provides concrete example questions the tool answers, making its purpose unambiguous and distinct from siblings like get_ebay_listings or get_latest_prices by focusing on the alert aggregation and non-dismissed filter.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly gives usage guidance via example questions ('what should I look at today' or 'anything moving I should react to'), which tells an agent when to invoke this tool. However, it does not explicitly mention when not to use it or name alternative tools, leaving the comparison to siblings implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_tcgplayer_ordersA
Get TCGplayer orders and their line items, optionally filtered by status (new/shipped) or order number. Use for questions like "what TCGplayer orders do I need to ship" or "what did order X contain".
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| status | No | Filter by status: 'new' or 'shipped'. Omit for all. | |
| order_number | No | Optional exact order number to look up. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. The verb 'Get' implies a read operation, but the description doesn't mention authentication, rate limits, whether it modifies data, how results are ordered, or what happens when both status and order_number are provided. The only extra bit is 'and their line items', which is a return-value detail, not a behavioral trait.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no wasted words. The core function and filtering options are front-loaded in the first sentence, and the second sentence provides practical examples. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has no output schema and no annotations, so the description must carry more weight. It does state that orders and their line items are returned, which is a helpful high-level overview. However, it doesn't mention default limit behavior, pagination, or any caveats about the data returned, making it adequate but not complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 67%, above the 50% threshold, so baseline is 3. The description adds some meaning by relating parameters to use cases ('what did order X contain' maps to order_number, 'what orders do I need to ship' maps to status), but it does not add anything about the limit parameter or clarify interactions between filters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource: 'Get TCGplayer orders and their line items', which clearly distinguishes this tool from siblings like get_inventory_summary or get_orders_missing_listings. It also provides concrete example questions that anchor the tool's purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit context by mentioning questions like 'what TCGplayer orders do I need to ship' and 'what did order X contain', which tells the agent when to use this tool. It does not explicitly exclude alternatives or mention when not to use it, but the context is clear enough.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
print_packing_slipA
Generate TCGplayer packing-slip PDF(s) for one or more order numbers by calling the Seller Portal's export API directly (bypasses the "Packing Slip" button, which stopped responding to automated clicks). Saves each PDF to a local packing-slips/ folder and returns its path — open or print that file yourself. If format is omitted, it's chosen automatically per order from tcgplayer_orders.order_total: orders below $20 print as "LeftWindowedEnvelope" (single #10 windowed envelope), orders at or above that print as "Default" (standard full-page). A mixed batch under auto mode is split into one export call per format, producing multiple PDFs. Pass format explicitly to force one format for every requested order instead. Requires TCGPLAYER_COOKIE in .env: a session cookie captured from a logged-in Seller Portal tab (DevTools → Network → any request → Copy as cURL → take the Cookie header value). That cookie expires/rotates periodically — a 401/403 here means it needs refreshing.
| Name | Required | Description | Default |
|---|---|---|---|
| format | No | Optional explicit override applied to every order in the request. "Default" is the standard full-page packing slip. "LeftWindowedEnvelope" is the Seller Portal's "Print #10 Left Window (Single)" option. Omit to auto-select per order by order_total (see tool description). | |
| order_numbers | Yes | One or more order_number values, e.g. ["F0DFDDC3-36957C-6C74A"]. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It reveals that it calls an export API directly, saves PDFs locally, returns paths, requires a session cookie, and that the cookie expires (with 401/403 as a signal). It also explains the auto-format splitting behavior for mixed batches, making side effects and prerequisites explicit.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but dense, with every sentence earning its place. It is front-loaded with the core purpose, then details format logic, cookie requirements, and error handling. The structure is logical and efficient, avoiding redundancy while covering all necessary aspects.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (format selection, multiple orders, cookie dependency), the description is remarkably complete. It explains the return value (path), the auto-format rules, the cookie acquisition and refresh, and the error indication. There is no output schema, so the description adequately covers what an agent needs to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description adds significant meaning beyond the schema: it explains the auto-selection of format based on order_total, the meaning of each format enum value, and the behavior of mixed batches. This goes well beyond the schema's basic descriptions, providing the agent with decision-making context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the specific verb (generate), resource (packing-slip PDFs), and scope (for one or more order numbers). It distinguishes itself from sibling tools by being the only one that produces packing slips, and it explicitly mentions bypassing the Seller Portal button, which further clarifies its unique role.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains exactly when to use this tool (to generate packing slips) and provides the alternative (the Packing Slip button) that it replaces. It also details the auto-format selection logic and the cookie prerequisite, giving clear operational context without ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
11 tool updates
v0.1.0- First observed
create_tcgplayer_order - First observed
debug_network - First observed
get_box_pnl - First observed
get_ebay_listings - First observed
get_global_pnl - First observed
get_inventory_summary - First observed
get_latest_prices - First observed
get_orders_missing_listings - First observed
get_price_alerts - First observed
get_tcgplayer_orders - First observed
print_packing_slip
TDQS
Scored across 11 tools
Most tools map to a distinct resource/action (inventory, prices, alerts, PnL, orders, listings, packing slips), but get_inventory_summary and get_latest_prices both surface TCGplayer market prices, so an agent could pick either for price questions. get_orders_missing_listings also requires careful reading to separate it from create_tcgplayer_order's sync behavior.
Every tool uses snake_case with a clear verb first (get, create, print, debug) and a noun object, and read tools consistently start with get_. There are no mixed conventions or vague verbs.
11 tools is well within the ideal 3-15 range and the set is scoped to a specific TCG reseller operation: reads, order intake, reconciliation, and packing slips. No tool feels redundant or filler; even debug_network serves a clear ops purpose.
The set covers read-only dashboards, TCGplayer order creation, missing-sync reconciliation, and packing slips, but it lacks lifecycle operations such as marking orders shipped/sold, dismissing price alerts, or updating listings. create_tcgplayer_order explicitly defers finalization to an external chat-based 'mark sold' flow, which is a notable dead end for agents.
Maintenance
Related MCP Connectors
Live TCG card and decklist prices for AI assistants. 22+ games, no account, ready-to-buy links.
Provide detailed Pokémon data and information through a standardized MCP interface. Enable LLMs an…
Connect to your CollectHolo Pokémon card collection. Check your portfolio value, look up card, sealed and graded (PSA/BGS/CGC) prices from Cardmarket, TCGplayer, eBay, Goldin and Fanatics, search the catalog in six languages, import a whole collection from a spreadsheet, and add or update holdings in plain language. Every change asks for confirmation.
Real-time Pokemon TCG restock alerts, MSRP lookups and restock timing for shops, apps and AI agents.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables querying Yu-Gi-Oh! TCG card data, such as card information, sets, and prices, through natural language.12 npm1MIT
- AlicenseNot gradedqualityBmaintenanceEnables querying multi-language trading card game data (Pokémon TCG and more) through natural language or direct tools, integrated with Pipeworx MCP gateway.12 npmMIT
- FlicenseNot gradedqualityAmaintenanceProvides Magic: The Gathering card, deck, provider, and statistical evidence tools for LLMs to make informed deckbuilding decisions.-
- FlicenseNot gradedqualityDmaintenanceProvides access to TCGplayer trading card data, including search, product details, pricing, and market information, enabling natural language queries for card analysis.1-