Skip to main content
Glama
satorntcg

satorntcg-mcp-server

Official
by satorntcg

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

get_price_alerts

Active price alerts, gainers/losers, stale listings, listing-price drift

get_inventory_summary

Current inventory: quantities owned/listed, latest prices

get_latest_prices

TCGplayer market price + cost basis lookup by card name

get_box_pnl

Profit & loss per box (purchase price vs. realized/unrealized pull value)

get_global_pnl

Overall business P&L across all boxes and listings

get_ebay_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_alerts

Every 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 start

The 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 inputSchema from tools/list before tools/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 what

  • mcp.tool.row_count — best-effort row count from the result, when there's an obvious array to count

  • mcp.tool.duration_ms — total handler wall time

  • mcp.tool.slow — true once mcp.tool.duration_ms exceeds SLOW_TOOL_THRESHOLD_MS (1000ms, in src/index.js) — lets you filter straight to the calls worth looking at without eyeballing durations

  • span 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/SIGINT the process calls process.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_TIMEOUT or OTEL_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=1

agent.request attributes:

Attribute

Value

gen_ai.request.model

ANTHROPIC_MODEL as sent

agent.outcome

error or unknown today (see below)

agent.stop_reason

Last Claude stop_reason, or max_tool_rounds

agent.tool_call_count

Tool uses Claude requested that the agent handled (executed or rejected). Same count as the diagnostics block

agent.tool_round_count

Rounds of tool execution whose results went back to Claude. Same as Tool rounds: in diagnostics

agent.usage.input_tokens / agent.usage.output_tokens

Totals across every Claude call that returned. These are agent.*, not gen_ai.usage.*, so a backend summing gen_ai.usage.* over spans doesn't count tokens twice

agent.outcome uses only what a run actually shows:

  • error: MCP setup failed, the Anthropic API returned an error, something threw, or the run hit max_tool_rounds. The span status is ERROR as well.

  • unknown: everything else, including every normal end_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, so answered, clarification and unsupported are defined in OUTCOMES but 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 keeps unknown, with an interrupted 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_ms and an OK/ERROR status. agent.tool.status is ok, tool_error (the tool returned isError) or mcp_error (the protocol call threw). Tool uses the agent rejected before calling MCP (not allowlisted, invalid arguments) appear as tool_use_rejected events on agent.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.model and gen_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 records mcp.tool.args as described above.

  • Known gap: NodeSDK's default resource detectors attach process.command_args to every agent trace, and for this CLI that includes the question. They also attach process.owner, host.name and process.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 an interrupted by SIGINT/SIGTERM error status and flushed before exit.

  • OTEL_SDK_DISABLED=true turns 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/call carries context. initialize and tools/list aren't traced on the server, so mcp.setup has 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

agent.requests

counter, {request}

gen_ai.request.model, agent.outcome

Once per request, whether it succeeds, fails or is interrupted

agent.request.duration

histogram, ms

gen_ai.request.model, agent.outcome

Same moment. Covers MCP server spawn through shutdown. Buckets 250ms–120s

agent.tool.calls

counter, {call}

mcp.tool.name, agent.tool.outcome (success/error)

Per MCP tools/call. error covers both an isError result and a failed call. Tool uses the agent rejects before MCP aren't calls and aren't counted (they show up as tool_use_rejected span events)

agent.tokens.input / agent.tokens.output

counter, {token}

gen_ai.request.model

Per Claude response, from usage.input_tokens / usage.output_tokens. Cache-read and cache-write tokens aren't included

  • agent.outcome is the same value as on the agent.request span (error or unknown; see above). An interrupted run is counted as unknown.

  • 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-metrics 1.x, shutting down a PeriodicExportingMetricReader does not export what was recorded since the last 60s interval. That's every metric from a CLI run. shutdownTracing() in src/tracing.js therefore calls forceFlush() 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.js checks 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: no MeterProvider is registered, and the instruments are no-ops.

  • otlp: OTLP/HTTP to the local Collector in observability/ (see below). The default URL is http://localhost:4319/v1/metrics; override it with OTEL_EXPORTER_OTLP_METRICS_ENDPOINT. The agent never derives this URL from OTEL_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.log
  • anything 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

otel-collector

otel/opentelemetry-collector-contrib:0.161.0

4319 → OTLP/HTTP 4318

Receives agent metrics (metrics pipeline only)

8889

Prometheus exposition of the Collector's running totals

prometheus

prom/prometheus:v3.14.0

9090

Scrapes otel-collector:8889 every 15s

grafana

grafana/grafana:12.2.0

3000

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 the prometheus-data volume.

  • 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=error is 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_MS in agent/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_cumulative is an alpha Collector component.

Name and unit changes. The Collector's Prometheus exporter rewrites OTel names:

OTel metric (unit)

Prometheus series

agent.requests ({request})

agent_requests_total

agent.request.duration (ms)

agent_request_duration_milliseconds_bucket / _sum / _count

agent.tool.calls ({call})

agent_tool_calls_total

agent.tokens.input / .output ({token})

agent_tokens_input_total / agent_tokens_output_total

  • 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; ms becomes _milliseconds.

  • service.name becomes the job label (satorntcg-agent, kept by honor_labels: true), and otel_scope_name is added.

  • Prometheus 3 stores histogram le values 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_EXPORTER

Grafana. 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: OK

Or open http://localhost:9090 and try these queries:

  • agent_requests_total

  • sum by (agent_outcome) (agent_requests_total)

  • agent_tokens_input_total

  • agent_tool_calls_total

  • histogram_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:latest

Then 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.

Jaeger trace view of a satorntcg-mcp-server tool call, showing the tool.* span and its nested db.query child 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_listing tool — draft an eBay listing from inventory + pricing data

  • HTTP/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 tools
create_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.

NameRequiredDescriptionDefault
itemsYesLine items parsed from the order email.
ship_byNoShip-by date, YYYY-MM-DD.
ordered_atNoISO timestamp of when the order was placed.
order_totalNoAggregate order total from the email.
order_numberYese.g. F0DFDDC3-1965A5-DEF21
gmail_message_idNo
manage_order_urlNo

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

NameRequiredDescriptionDefault
limitNo
box_idNoOptional specific box id to filter to. Omit to get all boxes.

TDQS

A3.8/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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".

NameRequiredDescriptionDefault
limitNo
statusYesWhich set of listings to return.active

TDQS

A3.9/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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".

NameRequiredDescriptionDefault
limitNoMax rows to return.
name_containsNoOptional case-insensitive filter on card name.

TDQS

A3.7/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

NameRequiredDescriptionDefault
limitNo
name_containsNoOptional case-insensitive filter on card name.

TDQS

A3.8/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

NameRequiredDescriptionDefault
limitNoMax number of missing orders to return. checked_count/missing_count still reflect the full since-filtered set.
sinceNoOptional ISO date/datetime — only check orders with created_at >= this. Omit to check every order on file.

TDQS

A4.5/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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".

NameRequiredDescriptionDefault
limitNoMax rows to return per category.
include_listing_alertsNoAlso include eBay listings whose price has drifted vs. market (from v_listing_price_alerts).
include_stale_listingsNoAlso include listings that have been active a long time without selling (from v_stale_listings).

TDQS

A3.9/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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".

NameRequiredDescriptionDefault
limitNo
statusNoFilter by status: 'new' or 'shipped'. Omit for all.
order_numberNoOptional exact order number to look up.

TDQS

A3.7/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 11 tool updatesv0.1.0
    • First observedcreate_tcgplayer_order
    • First observeddebug_network
    • First observedget_box_pnl
    • First observedget_ebay_listings
    • First observedget_global_pnl
    • First observedget_inventory_summary
    • First observedget_latest_prices
    • First observedget_orders_missing_listings
    • First observedget_price_alerts
    • First observedget_tcgplayer_orders
    • First observedprint_packing_slip

TDQS

A4/5.0

Scored across 11 tools

Disambiguation4/5

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.

Naming Consistency5/5

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.

Tool Count5/5

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.

Completeness3/5

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

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables querying multi-language trading card game data (Pokémon TCG and more) through natural language or direct tools, integrated with Pipeworx MCP gateway.
    12 npm
    MIT
  • F
    license
    Not graded
    quality
    A
    maintenance
    Provides Magic: The Gathering card, deck, provider, and statistical evidence tools for LLMs to make informed deckbuilding decisions.
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    Provides access to TCGplayer trading card data, including search, product details, pricing, and market information, enabling natural language queries for card analysis.
    1
    -