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/mcp—McpServerController, 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 rail —
marketplace_usage_recordsledger + hourlymarketplace:meter-usage→BatchMeterUsage; MCP calls already writeSOURCE_AGENTusage records (per-call qty 1) viaMcpToolDispatcher::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)¶
- Endpoint fails AWS live-200 check —
/api/v1/mcpis inside theauth:apiPassport 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 bareinitialize/OPTIONS/GET return 200, or add OAuth protected-resource discovery. S (½–1d) - Machine auth not accepted —
auth:apirejects client-credentials (M2M) tokens (nooauth_user_id→ 401); no.well-known/oauth-authorization-serverdiscovery doc;mcp:*scopes never registered viaPassport::tokensCan()(default-open to FULL);vp3_PartnerApiKeykeys exist but have no inbound verification middleware. AWS needsOAUTH2(w/ discovery URL) orAPI_KEY. M (1–2d) - No fulfillment — zero Quick Launch (
PutDeploymentParameter→ buyer Secrets Manager) AND no redirect fulfillment. Biggest net-new. L - Metering not agent-correct — MCP path writes lowercase
dimension_key(api_requests/gtm_workflows/seo_credits) but AWS needs PascalCaseaws_dimension_name(APIRequests/SEOCredits);gtm_workflowshas no config dimension at all → rejected. No dedicatedMCPRequests/Tokensdimension. Uses pre-June-2026 fields (ProductCode/CustomerIdentifier; missingCustomerAWSAccountId+LicenseArn). Gate off +AWS_MARKETPLACE_PRODUCT_CODEunset → nothing submits live. M (1–2d) — key filesMcpToolDispatcher.php:139-205,MarketplaceEntitlementService.php:335-385,MarketplaceMeteringBatch.php:195-218,config/services.php:104-219. - No agent listing — both drafts empty shells (
dims:null, delivery:null), no offers. Needs (reuseprod-i6z5brjwijo7ior new):AddDimensions(usage) →AddDeliveryOptionsApiDeliveryOptionDetails{ApiType:MCP_SERVER, Endpoints:[{EndpointUrl, AuthorizationTypes, IntegrationProtocols:[MCP]}], CompatibleServices:[Bedrock-AgentCore]}→CreateOffer+UpdatePricingTerms(UsageBasedPricingTerm) →UpdateLegalTerms(StandardEula) →ReleaseProduct+ReleaseOffer→UpdateVisibility. S (½d once 1–4 ready) - Pre-launch hygiene — reconcile
tools/list(13) vs dispatcher (17): the 4get_partner_health*tools missing fromContentWorkflowController::mcpSchema(). S - 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_KEYauth + redirect fulfillment. Build one inboundvp3_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):
OAUTH2client-credentials + Quick Launch + AgentCore-native. Discovery.well-known+CheckClientCredentialsguard on MCP +PutDeploymentParameterprovisioning 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.
- Track B MUST be its own product, registered Metered-FIRST. The first
AddDimensionson a product fixes its pricing model irreversibly. Track A'sprod-fcqhg7gqf2nxeis now locked toContract(Entitled) — addingMeteredfacets 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. Useprod-i6z5brjwijo7i(empty draft) or a fresh product, and make its firstAddDimensionsregister the usage/Metered dimensions (Metered/ExternallyMetered). Get this right once. - Proven
start-change-setschema (reuse for #5).PricingModelenum =Byol · Free · Usage · Contract— Track B metered =Usage+UsageBasedPricingTerm(Track A usedContract+ConfigurableUpfrontPricingTerm). Durations are months (P1M…P96M, notP1Y). 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-setvalidates async → polldescribe-change-set; failed change-sets are non-destructive (safe to probe). Stage on aLimited/Privateoffer + your own buyer account beforePublic. - 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 theAgreementServiceterms/duration parse. - Metering dims = the config
usage_dimensions.Words/Images/AudioMinutes/VideoMinutes/ APIRequests/Presentations/SEOCredits(+ a dedicatedMCPRequests/Tokensper gap #4) are the Metered facets to register first on the Track B product. TheBatchMeterUsagerail already runs (Track A); gap #4's PascalCaseaws_dimension_namefix applies here too.
Net: gap #5's "reuse
prod-i6z5brjwijo7ior new" → decision = its own product, Metered-first, to avoid the Contract lock. The change-set mechanics are now battle-tested on Track A.