Skip to content

Track B — AWS Marketplace "AI Agents & Tools" listing (MCP server) — punch list

Audited 2026-06-16. Track A = classic SaaS contract listing (the human platform). Track B = list Vellocity's MCP server as a usage-metered AI Agents & Tools product. Both are SaaSProduct@1.0; the agent-ness is an API delivery option. Decision: do Track A first, Track B as a scoped sprint.

Already built (the hard parts) ✅

  • MCP server POST/GET/DELETE https://vell.ai/api/v1/mcpMcpServerController, spec 2025-11-25, JSON-RPC + SSE, real tool execution into CapabilityRegistry, per-user attribution. McpToolDispatcher = 17 tools.
  • OAuth server — Laravel Passport (/oauth/token, /oauth/authorize); client-credentials grant can mint tokens today.
  • Metering railmarketplace_usage_records ledger + hourly marketplace:meter-usageBatchMeterUsage; MCP calls already write SOURCE_AGENT usage records (per-call qty 1) via McpToolDispatcher::trackAgenticInvocation().
  • Move 1 gate MpAgenticEntitlement (mp-agentic:mcp) shipped to prod in shadow/telemetry mode (AWS_MP_AGENTIC_GATE_REQUIRED=false). Agent canary (tests/canary/specs/agent/agent-mcp.spec.ts) green on all envs.

Blocking gaps ❌ (none done)

  1. Endpoint fails AWS live-200 check/api/v1/mcp is inside the auth:api Passport group (routes/api_v1.php:63…622), so AWS's unauth probe gets a 302 login redirect (NOT the mp-agentic gate, which is permissive-by-default and never runs). Fix: let a bare initialize/OPTIONS/GET return 200, or add OAuth protected-resource discovery. S (½–1d)
  2. Machine auth not acceptedauth:api rejects client-credentials (M2M) tokens (no oauth_user_id → 401); no .well-known/oauth-authorization-server discovery doc; mcp:* scopes never registered via Passport::tokensCan() (default-open to FULL); vp3_ PartnerApiKey keys exist but have no inbound verification middleware. AWS needs OAUTH2 (w/ discovery URL) or API_KEY. M (1–2d)
  3. No fulfillment — zero Quick Launch (PutDeploymentParameter → buyer Secrets Manager) AND no redirect fulfillment. Biggest net-new. L
  4. Metering not agent-correct — MCP path writes lowercase dimension_key (api_requests/gtm_workflows/seo_credits) but AWS needs PascalCase aws_dimension_name (APIRequests/SEOCredits); gtm_workflows has no config dimension at all → rejected. No dedicated MCPRequests/Tokens dimension. Uses pre-June-2026 fields (ProductCode/CustomerIdentifier; missing CustomerAWSAccountId+LicenseArn). Gate off + AWS_MARKETPLACE_PRODUCT_CODE unset → nothing submits live. M (1–2d) — key files McpToolDispatcher.php:139-205, MarketplaceEntitlementService.php:335-385, MarketplaceMeteringBatch.php:195-218, config/services.php:104-219.
  5. No agent listing — both drafts empty shells (dims:null, delivery:null), no offers. Needs (reuse prod-i6z5brjwijo7i or new): AddDimensions (usage) → AddDeliveryOptions ApiDeliveryOptionDetails{ApiType:MCP_SERVER, Endpoints:[{EndpointUrl, AuthorizationTypes, IntegrationProtocols:[MCP]}], CompatibleServices:[Bedrock-AgentCore]}CreateOffer+UpdatePricingTerms(UsageBasedPricingTerm) → UpdateLegalTerms(StandardEula) → ReleaseProduct+ReleaseOfferUpdateVisibility. S (½d once 1–4 ready)
  6. Pre-launch hygiene — reconcile tools/list (13) vs dispatcher (17): the 4 get_partner_health* tools missing from ContentWorkflowController::mcpSchema(). S
  7. Move 2 (/api/v2/agentic + 402 + x402) = design-doc-only; Skill-Library policy 2b/3 not started. Defer — not required for a basic metered listing.

Two paths (this sizes the sprint)

  • Lean (~3–5 days): API_KEY auth + redirect fulfillment. Build one inbound vp3_ middleware on the MCP route + issue a key on subscribe (reuse register flow) + metering fixes (#4) + listing (#5). No AgentCore auto-integration (API-key MCP needs an OpenAPI spec for that).
  • Full (~1.5–2 weeks): OAUTH2 client-credentials + Quick Launch + AgentCore-native. Discovery .well-known + CheckClientCredentials guard on MCP + PutDeploymentParameter provisioning into buyer Secrets Manager.

Critical path

1 (endpoint 200) → 2 (auth) → 4 (metering correctness) → 3 (fulfillment) → 5 (listing change-set) → AWS review. #6 anytime. Flip AWS_MP_AGENTIC_GATE_REQUIRED=true + set AWS_MARKETPLACE_PRODUCT_CODE at go-live.


Strategic addendum — pricing-model lock & proven change-set schema (2026-06-17)

Learned firsthand while shipping Track A's self-serve annual re-model. These sharpen gap #5.

  1. Track B MUST be its own product, registered Metered-FIRST. The first AddDimensions on a product fixes its pricing model irreversibly. Track A's prod-fcqhg7gqf2nxe is now locked to Contract (Entitled) — adding Metered facets to it would require the AWS Marketplace Seller Operations team, not a self-serve change-set. So do not bolt the metered MCP onto Track A. Use prod-i6z5brjwijo7i (empty draft) or a fresh product, and make its first AddDimensions register the usage/Metered dimensions (Metered/ExternallyMetered). Get this right once.
  2. Proven start-change-set schema (reuse for #5). PricingModel enum = Byol · Free · Usage · Contract — Track B metered = Usage + UsageBasedPricingTerm (Track A used Contract + ConfigurableUpfrontPricingTerm). Durations are months (P1MP96M, not P1Y). For any committed-base card, every rate card must price the same dimension-key set across all durations (that rule blocked Track A's first attempt). start-change-set validates async → poll describe-change-set; failed change-sets are non-destructive (safe to probe). Stage on a Limited/Private offer + your own buyer account before Public.
  3. Contract-with-Consumption is now half-built. Track A self-serve annual (P1M+P12M) means a committed contract can anchor a prepaid token/credit balance that Track B's metering burns down — the canonical agent shape (commit + consume). If Track B uses Contract-with-Consumption, the duration-aware entitlement code from Track A transfers directly: resolveBillingFrequency() + resolvePlanId() (MarketplaceEntitlementService) and the AgreementService terms/duration parse.
  4. Metering dims = the config usage_dimensions. Words/Images/AudioMinutes/VideoMinutes/ APIRequests/Presentations/SEOCredits (+ a dedicated MCPRequests/Tokens per gap #4) are the Metered facets to register first on the Track B product. The BatchMeterUsage rail already runs (Track A); gap #4's PascalCase aws_dimension_name fix applies here too.

Net: gap #5's "reuse prod-i6z5brjwijo7i or new" → decision = its own product, Metered-first, to avoid the Contract lock. The change-set mechanics are now battle-tested on Track A.