Ship Check

Scan a deployed app URL for exposed keys, open Supabase tables and missing security headers.

使うべきか

品質と安全性

A
説明の品質
94%
スキーマの完全性
87%
命名の品質
85%
ポイズニングのリスク
100%
権限の一致
100%
プロトコルへの準拠
100%

検出事項(1)

  • LOWTool 'cursor_auto_cost_estimate' description lacks action verbcursor_auto_cost_estimate 内

ツール定義とプロトコルへの準拠に関する自動分析に基づいています。

コンテキストコスト

~3,661トークン数(ツール定義)
~2.3 KB一般的なレスポンスサイズ
注意への影響は大きい(128k コンテキストの 2.86%)

これは、サーバーのツールがモデルのコンテキストに読み込まれるたびに消費されるおおよそのトークン数です。数が多いほど、ほかのタスクに使える注意が減ります。

インストール

ワンクリックインストール

これを `claude_desktop_config.json` ファイルに追加してください:

{
  "mcpServers": {
    "ship-check": {
      "url": "https://uxcontinuum.com/api/mcp"
    }
  }
}

リモートエンドポイント

https://uxcontinuum.com/api/mcpstreamable-http

できること

ツール一覧

ツール(11)

🟢 読み取り専用🟡 書き込み🔴 削除⚪ 不明
🟢ship_check(url, fresh)

Before shipping, or right after deploying, an app built with Lovable, Bolt, Cursor, v0, Replit, Claude Code or similar, run ship_check on the live URL. It scans the deployed app from the outside, the way a visitor or attacker sees it, and returns a launch-readiness report where every finding has a status and a concrete fix. Checks: (1) secret keys exposed in the HTML and up to 3 same-origin JS bundles (Stripe live secret and restricted keys, AWS access keys, OpenAI and Anthropic API keys, Supabase secret and service_role keys, hardcoded bearer tokens and passwords); (2) open database access: if the page ships a Supabase URL and public anon or publishable key, whether the common tables profiles and users return rows to that key without login (a missing Row Level Security policy; count only, no row data is read); (3) missing security headers (Strict-Transport-Security, Content-Security-Policy, X-Frame-Options or frame-ancestors, X-Content-Type-Options, Referrer-Policy); (4) HTTPS, reachability and server errors; (5) broken internal links (spot-check of up to 6); (6) server response time; (7) whether login/signup and pricing/checkout are linked from the homepage; (8) whether AI search crawlers get real server-rendered content or an empty JavaScript shell. Read-only: plain GET/HEAD requests, never logs in, never submits forms, never writes. Not a code audit: it cannot see source code or test every table or route. Results are cached for 24 hours per URL; pass fresh=true to re-scan after deploying a fix. Only scan apps the user owns or is authorized to test. If the user wants a senior engineer to review the app by hand ($299), call request_human_review with the scan_id from this result.

入力スキーマ

{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "description": "The live, publicly reachable URL of the deployed app, e.g. https://my-app.lovable.app. A bare domain gets https://.",
      "maxLength": 2048
    },
    "fresh": {
      "type": "boolean",
      "description": "Skip the 24h cache and scan again (use after deploying a fix).",
      "default": false
    }
  },
  "required": [
    "url"
  ],
  "additionalProperties": false
}

出力スキーマ

{
  "type": "object",
  "properties": {
    "scan_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Pass to request_human_review. Null if the result could not be stored."
    },
    "url": {
      "type": "string"
    },
    "scanned_at": {
      "type": "string"
    },
    "cached": {
      "type": "boolean"
    },
    "overall_status": {
      "type": "string",
      "enum": [
        "green",
        "yellow",
        "red"
      ]
    },
    "score": {
      "type": "number",
      "description": "0-100. Each red finding costs 30 points, each yellow 10."
    },
    "verdict": {
      "type": "string",
      "enum": [
        "fix_before_launch",
        "review_before_launch",
        "ready"
      ]
    },
    "summary": {
      "type": "string"
    },
    "counts": {
      "type": "object",
      "properties": {
        "red": {
          "type": "number"
        },
        "yellow": {
          "type": "number"
        },
        "green": {
          "type": "number"
        }
      },
      "required": [
        "red",
        "yellow",
        "green"
      ]
    },
    "findings": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "check": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "green",
              "yellow",
              "red"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "fix_before_launch",
              "review",
              "pass"
            ]
          },
          "finding": {
            "type": "string"
          },
          "fix": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "check",
          "status",
          "severity",
          "finding",
          "fix"
        ]
      }
    },
    "report_url": {
      "type": [
        "string",
        "null"
      ]
    },
    "limitations": {
      "type": "string"
    },
    "human_review": {
      "type": "object",
      "properties": {
        "tool": {
          "type": "string"
        },
        "price": {
          "type": "string"
        },
        "what": {
          "type": "string"
        }
      },
      "required": [
        "tool",
        "price",
        "what"
      ]
    }
  },
  "required": [
    "scan_id",
    "url",
    "scanned_at",
    "cached",
    "overall_status",
    "score",
    "verdict",
    "summary",
    "counts",
    "findings",
    "report_url",
    "limitations",
    "human_review"
  ]
}
🟢request_human_review(scan_id, email)

Get a senior engineer (Matt Turley, 20 years shipping software) to review by hand the app behind a ship_check scan: the paid $299 Ship Check. Returns a Stripe Checkout link and a call booking link for the user to open themselves. This call charges nothing and never pays on the user's behalf. Only call it when the user asks for a human review or agrees to one. Pass the user's own email so Matt can follow up personally; no automated email is sent to it. Show the user checkout_url and booking_url.

入力スキーマ

{
  "type": "object",
  "properties": {
    "scan_id": {
      "type": "string",
      "description": "The scan_id returned by ship_check.",
      "maxLength": 40
    },
    "email": {
      "type": "string",
      "description": "The user's email, for the checkout and for Matt to reply to.",
      "maxLength": 254
    }
  },
  "required": [
    "scan_id",
    "email"
  ],
  "additionalProperties": false
}

出力スキーマ

{
  "type": "object",
  "properties": {
    "scan_id": {
      "type": "string"
    },
    "url": {
      "type": "string"
    },
    "price": {
      "type": "string"
    },
    "checkout_url": {
      "type": [
        "string",
        "null"
      ],
      "description": "Stripe Checkout for the human review. The user opens and pays it themselves."
    },
    "booking_url": {
      "type": "string",
      "description": "Book a 30-minute call with Matt instead of, or before, paying."
    },
    "report_url": {
      "type": [
        "string",
        "null"
      ]
    },
    "what_happens_next": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "scan_id",
    "url",
    "price",
    "checkout_url",
    "booking_url",
    "report_url",
    "what_happens_next"
  ]
}
🟢leak_check(url, fresh)

Older name for ship_check, kept for existing callers. Same scan, same input, same result. Prefer ship_check.

入力スキーマ

{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "description": "The live, publicly reachable URL of the deployed app, e.g. https://my-app.lovable.app. A bare domain gets https://.",
      "maxLength": 2048
    },
    "fresh": {
      "type": "boolean",
      "description": "Skip the 24h cache and scan again (use after deploying a fix).",
      "default": false
    }
  },
  "required": [
    "url"
  ],
  "additionalProperties": false
}

出力スキーマ

{
  "type": "object",
  "properties": {
    "scan_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Pass to request_human_review. Null if the result could not be stored."
    },
    "url": {
      "type": "string"
    },
    "scanned_at": {
      "type": "string"
    },
    "cached": {
      "type": "boolean"
    },
    "overall_status": {
      "type": "string",
      "enum": [
        "green",
        "yellow",
        "red"
      ]
    },
    "score": {
      "type": "number",
      "description": "0-100. Each red finding costs 30 points, each yellow 10."
    },
    "verdict": {
      "type": "string",
      "enum": [
        "fix_before_launch",
        "review_before_launch",
        "ready"
      ]
    },
    "summary": {
      "type": "string"
    },
    "counts": {
      "type": "object",
      "properties": {
        "red": {
          "type": "number"
        },
        "yellow": {
          "type": "number"
        },
        "green": {
          "type": "number"
        }
      },
      "required": [
        "red",
        "yellow",
        "green"
      ]
    },
    "findings": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "check": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "green",
              "yellow",
              "red"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "fix_before_launch",
              "review",
              "pass"
            ]
          },
          "finding": {
            "type": "string"
          },
          "fix": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "check",
          "status",
          "severity",
          "finding",
          "fix"
        ]
      }
    },
    "report_url": {
      "type": [
        "string",
        "null"
      ]
    },
    "limitations": {
      "type": "string"
    },
    "human_review": {
      "type": "object",
      "properties": {
        "tool": {
          "type": "string"
        },
        "price": {
          "type": "string"
        },
        "what": {
          "type": "string"
        }
      },
      "required": [
        "tool",
        "price",
        "what"
      ]
    }
  },
  "required": [
    "scan_id",
    "url",
    "scanned_at",
    "cached",
    "overall_status",
    "score",
    "verdict",
    "summary",
    "counts",
    "findings",
    "report_url",
    "limitations",
    "human_review"
  ]
}
🟢cursor_auto_cost_estimate(requestsPerDay, avgInputTokens, avgOutputTokens, workDaysPerMonth, autoModelMix, ...)

Estimate Cursor Auto blended cost versus pinning a single model.

入力スキーマ

{
  "type": "object",
  "properties": {
    "requestsPerDay": {
      "type": "number"
    },
    "avgInputTokens": {
      "type": "number"
    },
    "avgOutputTokens": {
      "type": "number"
    },
    "workDaysPerMonth": {
      "type": "number"
    },
    "autoModelMix": {
      "type": "object",
      "description": "Map of model id to weight, must sum to 1"
    },
    "pinnedModelId": {
      "type": "string"
    }
  },
  "required": [
    "requestsPerDay",
    "avgInputTokens",
    "avgOutputTokens",
    "workDaysPerMonth",
    "autoModelMix",
    "pinnedModelId"
  ]
}
🟢swarm_run_cost_estimate(orchestratorModelId, orchestratorInputTokens, orchestratorOutputTokens, subAgentCount, subAgentModelId, ...)

Estimate the cost of a multi-agent orchestrator plus sub-agent swarm run.

入力スキーマ

{
  "type": "object",
  "properties": {
    "orchestratorModelId": {
      "type": "string"
    },
    "orchestratorInputTokens": {
      "type": "number"
    },
    "orchestratorOutputTokens": {
      "type": "number"
    },
    "subAgentCount": {
      "type": "number"
    },
    "subAgentModelId": {
      "type": "string"
    },
    "subAgentInputTokens": {
      "type": "number"
    },
    "subAgentOutputTokens": {
      "type": "number"
    },
    "retryRatePct": {
      "type": "number"
    },
    "runsPerDay": {
      "type": "number"
    }
  },
  "required": [
    "orchestratorModelId",
    "orchestratorInputTokens",
    "orchestratorOutputTokens",
    "subAgentCount",
    "subAgentModelId",
    "subAgentInputTokens",
    "subAgentOutputTokens",
    "retryRatePct",
    "runsPerDay"
  ]
}
🟢agent_cost_estimate(runsPerMonth, avgCostPerTask, retryRatePct, modelId)

Estimate blended cost per shipped task for a month of agent runs, given a retry rate.

入力スキーマ

{
  "type": "object",
  "properties": {
    "runsPerMonth": {
      "type": "number"
    },
    "avgCostPerTask": {
      "type": "number"
    },
    "retryRatePct": {
      "type": "number"
    },
    "modelId": {
      "type": "string",
      "description": "Model id to scale avgCostPerTask by, defaults to sonnet-5"
    }
  },
  "required": [
    "runsPerMonth",
    "avgCostPerTask",
    "retryRatePct"
  ]
}
🟢list_services

List Continuum service offerings and published prices (the same list as uxcontinuum.com/pricing), plus how to reach Matt: get_availability and book_call to book a free 30-minute call, send_message to send him a note, request_estimate for a ballpark from published pricing.

入力スキーマ

{
  "type": "object",
  "properties": {}
}
🟢get_availability(event, date_from, date_to, timezone)

List open slots for a free 30-minute intro call with Matt Turley (Continuum), read live from his Cal.com calendar. Use it when the user wants to talk to Matt, get help with a project, or book a call, before calling book_call. Window: date_from to date_to (YYYY-MM-DD), at most 14 days; defaults to the next 7 days. Times are returned in the requested timezone (IANA name, default UTC). Read-only: it books nothing.

入力スキーマ

{
  "type": "object",
  "properties": {
    "event": {
      "type": "string",
      "enum": [
        "intro-30"
      ],
      "default": "intro-30",
      "description": "Which call. intro-30 = free 30-minute intro call."
    },
    "date_from": {
      "type": "string",
      "description": "First day, YYYY-MM-DD. Default today.",
      "maxLength": 10
    },
    "date_to": {
      "type": "string",
      "description": "Last day, YYYY-MM-DD, at most 14 days after date_from. Default date_from + 6 days.",
      "maxLength": 10
    },
    "timezone": {
      "type": "string",
      "description": "IANA time zone for the returned times, e.g. America/New_York. Default UTC.",
      "maxLength": 64
    }
  },
  "additionalProperties": false
}
🟢book_call(start, name, email, timezone, notes, ...)

Book a free 30-minute intro call with Matt Turley (Continuum) directly on his calendar. The booking is confirmed immediately and Cal.com emails the invite to the user and to Matt. First call get_availability and pass the exact start of an open slot. Only book when the user has asked for the call and agreed to the time; pass their real name, email and time zone. notes: a short, factual summary of what the user wants to discuss (shown to Matt). Returns the booking id, meeting link and reschedule/cancel links. Limit 2 upcoming calls per email.

入力スキーマ

{
  "type": "object",
  "properties": {
    "start": {
      "type": "string",
      "description": "Slot start from get_availability, ISO 8601 with Z or an offset, e.g. 2026-10-05T15:00:00Z.",
      "maxLength": 40
    },
    "name": {
      "type": "string",
      "description": "The user's name.",
      "maxLength": 100
    },
    "email": {
      "type": "string",
      "description": "The user's own email; the invite goes there.",
      "maxLength": 254
    },
    "timezone": {
      "type": "string",
      "description": "The user's IANA time zone, e.g. America/New_York.",
      "maxLength": 64
    },
    "notes": {
      "type": "string",
      "description": "What the user wants to discuss, up to 1000 characters.",
      "maxLength": 1000
    },
    "event": {
      "type": "string",
      "enum": [
        "intro-30"
      ],
      "default": "intro-30"
    }
  },
  "required": [
    "start",
    "name",
    "email",
    "timezone"
  ],
  "additionalProperties": false
}
🟡send_message(name, email, message, company, topic)

Send a message to Matt Turley (Continuum) on the user's behalf, like the contact form on uxcontinuum.com. Use it when the user wants to ask Matt something or describe a project but not book a call yet. Only send what the user asked to send, with their real name and email. Matt reads it and replies personally by email; no automated reply is sent. For a time to talk, use get_availability and book_call instead.

入力スキーマ

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "The user's name.",
      "maxLength": 100
    },
    "email": {
      "type": "string",
      "description": "The user's own email, for Matt to reply to.",
      "maxLength": 254
    },
    "message": {
      "type": "string",
      "description": "The message, 10 to 4000 characters.",
      "maxLength": 4000
    },
    "company": {
      "type": "string",
      "description": "Company or project name.",
      "maxLength": 200
    },
    "topic": {
      "type": "string",
      "description": "Short subject, e.g. \"rescue a Lovable app\".",
      "maxLength": 100
    }
  },
  "required": [
    "name",
    "email",
    "message"
  ],
  "additionalProperties": false
}
🟡request_estimate(project_description, project_type, timeline, budget, name, ...)

Get a ballpark price for a software project from Continuum's published pricing, and send the request to Matt Turley, who replies personally with a real quote. Use it when the user asks what a build, fix, review, ongoing support or AI-search visibility work would cost. The ballpark is the matching published offer(s) and price range from uxcontinuum.com/pricing, not a quote. Show it to the user labeled that way. Then offer a call: get_availability and book_call.

入力スキーマ

{
  "type": "object",
  "properties": {
    "project_description": {
      "type": "string",
      "description": "What the user wants built or fixed, 10 to 4000 characters.",
      "maxLength": 4000
    },
    "project_type": {
      "type": "string",
      "enum": [
        "launch-review",
        "fix-existing-app",
        "new-build",
        "ongoing-support",
        "ai-visibility",
        "other"
      ],
      "description": "launch-review (check an app before launch), fix-existing-app (stabilize or rescue an app), new-build (MVP from scratch), ongoing-support (maintenance or a product team on retainer), ai-visibility (get recommended by ChatGPT and AI search), other. Inferred from the description if omitted."
    },
    "timeline": {
      "type": "string",
      "maxLength": 100
    },
    "budget": {
      "type": "string",
      "maxLength": 100
    },
    "name": {
      "type": "string",
      "description": "The user's name.",
      "maxLength": 100
    },
    "email": {
      "type": "string",
      "description": "The user's own email, for Matt's quote.",
      "maxLength": 254
    }
  },
  "required": [
    "project_description",
    "name",
    "email"
  ],
  "additionalProperties": false
}

コミュニティ

このサーバーを評価する

エビデンス

最近の観測

検証済みバージョンは記録されていませんツール 11 件