settld 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -3
- package/SETTLD_VERSION +1 -1
- package/bin/settld-mcp +2 -0
- package/bin/settld.js +71 -0
- package/conformance/kernel-v0/README.md +7 -0
- package/conformance/kernel-v0/run.mjs +292 -4
- package/docs/ACCESS.md +57 -0
- package/docs/ADOPTION_CHECKLIST.md +44 -0
- package/docs/ALERTS.md +198 -0
- package/docs/ARCHITECTURE.md +69 -0
- package/docs/ARCHITECTURE_FOUNDER_GUIDE.md +284 -0
- package/docs/ARTIFACTS.md +60 -0
- package/docs/CERTIFICATION_CHECKLIST.md +33 -0
- package/docs/CIRCLE_SANDBOX_E2E.md +152 -0
- package/docs/CONFIG.md +297 -0
- package/docs/CONTRACTS_APIS.md +23 -0
- package/docs/DEPRECATION.md +31 -0
- package/docs/DOMAIN_MODEL.md +92 -0
- package/docs/EVENT_ENVELOPE.md +53 -0
- package/docs/FINANCE_PACK_FORMAT.md +53 -0
- package/docs/INCIDENT_TAXONOMY.md +30 -0
- package/docs/JOB_STATE_MACHINE.md +66 -0
- package/docs/KERNEL_COMPATIBLE.md +60 -0
- package/docs/KERNEL_V0.md +40 -0
- package/docs/KEY_ROTATION.md +80 -0
- package/docs/LEDGER.md +82 -0
- package/docs/LIVENESS.md +76 -0
- package/docs/MVP_BUILD_ORDER.md +36 -0
- package/docs/ONCALL_PLAYBOOK.md +39 -0
- package/docs/OPERATIONS_SIGNING.md +20 -0
- package/docs/OVERVIEW.md +190 -0
- package/docs/PERF_BASELINE.md +85 -0
- package/docs/PRD.md +77 -0
- package/docs/QUICKSTART_KERNEL_V0.md +96 -0
- package/docs/QUICKSTART_MCP.md +377 -0
- package/docs/QUICKSTART_MCP_HOSTS.md +210 -0
- package/docs/QUICKSTART_POLICY_PACKS.md +65 -0
- package/docs/QUICKSTART_PRODUCE.md +61 -0
- package/docs/QUICKSTART_PROFILES.md +198 -0
- package/docs/QUICKSTART_RELEASE_VERIFY.md +39 -0
- package/docs/QUICKSTART_SDK.md +125 -0
- package/docs/QUICKSTART_SDK_PYTHON.md +111 -0
- package/docs/QUICKSTART_VERIFY.md +54 -0
- package/docs/QUICKSTART_X402_GATEWAY.md +317 -0
- package/docs/README.md +33 -0
- package/docs/RELEASE_CHECKLIST.md +182 -0
- package/docs/RELEASING.md +82 -0
- package/docs/REPO_SETTINGS.md +37 -0
- package/docs/RUNBOOK.md +86 -0
- package/docs/SKILLS.md +42 -0
- package/docs/SKILL_BUNDLE_FORMAT.md +48 -0
- package/docs/SLO.md +131 -0
- package/docs/SUMMARY.md +17 -0
- package/docs/SUPPORT.md +31 -0
- package/docs/THREAT_MODEL.md +36 -0
- package/docs/TRUST.md +59 -0
- package/docs/WORKFLOW.md +35 -0
- package/docs/X402_BATCH_SETTLEMENT.md +126 -0
- package/docs/blog/2026-02-14-your-ai-agent-just-spent-500-where-is-the-receipt.md +73 -0
- package/docs/examples/x402-provider-payout-registry.example.json +14 -0
- package/docs/gitbook/README.md +64 -0
- package/docs/gitbook/SETUP.md +25 -0
- package/docs/gitbook/SUMMARY.md +15 -0
- package/docs/gitbook/api-reference.md +73 -0
- package/docs/gitbook/closepacks.md +55 -0
- package/docs/gitbook/conformance.md +59 -0
- package/docs/gitbook/core-primitives.md +85 -0
- package/docs/gitbook/dispute-lifecycle.md +33 -0
- package/docs/gitbook/faq.md +21 -0
- package/docs/gitbook/guides.md +49 -0
- package/docs/gitbook/operations-runbook.md +36 -0
- package/docs/gitbook/quickstart.md +103 -0
- package/docs/gitbook/replay-and-audit.md +30 -0
- package/docs/gitbook/sdk-reference.md +35 -0
- package/docs/gitbook/security-model.md +58 -0
- package/docs/integrations/README.md +15 -0
- package/docs/integrations/github-actions-verify.yml +31 -0
- package/docs/integrations/github-actions.md +34 -0
- package/docs/integrations/openclaw/CLAWHUB_PUBLISH_CHECKLIST.md +65 -0
- package/docs/integrations/openclaw/PUBLIC_QUICKSTART.md +95 -0
- package/docs/integrations/openclaw/settld-mcp-skill/SKILL.md +69 -0
- package/docs/integrations/openclaw/settld-mcp-skill/mcp-server.example.json +12 -0
- package/docs/kernel-compatible/capabilities.json +36 -0
- package/docs/marketing/agent-commerce-substrate.md +78 -0
- package/docs/marketing/hn-repost-2026-02-17.md +102 -0
- package/docs/marketing/show-hn-post.md +45 -0
- package/docs/ops/ARTIFACT_VERIFICATION_STATUS.md +43 -0
- package/docs/ops/BILLING_WEBHOOK_REPLAY.md +105 -0
- package/docs/ops/CI_FLAKE_BUDGET.md +31 -0
- package/docs/ops/DISPUTE_FINANCE_RECONCILIATION_PACKET.md +56 -0
- package/docs/ops/GO_LIVE_GATE_S13.md +27 -0
- package/docs/ops/HOSTED_BASELINE_R2.md +129 -0
- package/docs/ops/KERNEL_V0_SHIP_GATE.md +69 -0
- package/docs/ops/LIGHTHOUSE_PRODUCTION_CLOSE.md +51 -0
- package/docs/ops/MCP_COMPATIBILITY_MATRIX.md +30 -0
- package/docs/ops/MINIMUM_PRODUCTION_TOPOLOGY.md +89 -0
- package/docs/ops/P0_BACKEND_PROGRESS.md +150 -0
- package/docs/ops/PAYMENTS_ALPHA_R5.md +105 -0
- package/docs/ops/PILOT_ONBOARDING_RUNBOOK.md +112 -0
- package/docs/ops/PRODUCTION_DEPLOYMENT_CHECKLIST.md +140 -0
- package/docs/ops/R1_SLOS.md +66 -0
- package/docs/ops/RELEASE_SIGNING_INCIDENT.md +58 -0
- package/docs/ops/SELF_SERVE_LAUNCH_AUTOMATION.md +89 -0
- package/docs/ops/THROUGHPUT_DRILL_10X.md +48 -0
- package/docs/ops/TRUST_CONFIG_WIZARD.md +60 -0
- package/docs/ops/X402_PILOT_WEEKLY_METRICS.md +76 -0
- package/docs/ops/tool-call-disputes-holdback.md +52 -0
- package/docs/pilot-kit/PILOT_PACKAGE_SCORECARD_X402.md +46 -0
- package/docs/pilot-kit/README.md +29 -0
- package/docs/pilot-kit/architecture-one-pager.md +48 -0
- package/docs/pilot-kit/buyer-email.txt +19 -0
- package/docs/pilot-kit/buyer-one-pager.md +31 -0
- package/docs/pilot-kit/gtm-pilot-playbook.md +182 -0
- package/docs/pilot-kit/offline-verify.md +33 -0
- package/docs/pilot-kit/procurement-one-pager.md +50 -0
- package/docs/pilot-kit/rfp-clause.md +46 -0
- package/docs/pilot-kit/roi-calculator-template.csv +2 -0
- package/docs/pilot-kit/security-qa.md +153 -0
- package/docs/pilot-kit/security-summary.md +35 -0
- package/docs/plans/2026-02-13-mcp-spike-design.md +113 -0
- package/docs/plans/2026-02-20-trust-os-v1-jira-backlog.md +348 -0
- package/docs/plans/2026-02-21-agent-economic-actor-operating-model.md +169 -0
- package/docs/plans/2026-02-21-trust-os-v1-strategy.md +241 -0
- package/docs/research/2026-02-21-agent-spend-host-landscape.md +57 -0
- package/docs/spec/AcceptanceCriteria.v1.md +17 -0
- package/docs/spec/AcceptanceEvaluation.v1.md +10 -0
- package/docs/spec/AgentEvent.v1.md +47 -0
- package/docs/spec/AgentIdentity.v1.md +62 -0
- package/docs/spec/AgentPassport.v1.md +95 -0
- package/docs/spec/AgentReputation.v1.md +59 -0
- package/docs/spec/AgentReputation.v2.md +52 -0
- package/docs/spec/AgentRun.v1.md +47 -0
- package/docs/spec/AgentRunSettlement.v1.md +52 -0
- package/docs/spec/AgentWallet.v1.md +43 -0
- package/docs/spec/AgreementDelegation.v1.md +109 -0
- package/docs/spec/ArbitrationCase.v1.md +67 -0
- package/docs/spec/ArbitrationOutcomeMapping.v1.md +62 -0
- package/docs/spec/ArbitrationVerdict.v1.md +60 -0
- package/docs/spec/BundleHeadAttestation.v1.md +32 -0
- package/docs/spec/CANONICAL_JSON.md +31 -0
- package/docs/spec/CRYPTOGRAPHY.md +61 -0
- package/docs/spec/ClosePack.v1.md +49 -0
- package/docs/spec/ClosePackManifest.v1.md +24 -0
- package/docs/spec/DelegationGrant.v1.md +90 -0
- package/docs/spec/DisputeCaseLifecycle.v1.md +51 -0
- package/docs/spec/DisputeOpenEnvelope.v1.md +43 -0
- package/docs/spec/ERRORS.md +76 -0
- package/docs/spec/ESCROW_NETTING_INVARIANTS.md +71 -0
- package/docs/spec/EvidenceIndex.v1.md +20 -0
- package/docs/spec/ExecutionIntent.v1.md +90 -0
- package/docs/spec/FinancePackBundleManifest.v1.md +24 -0
- package/docs/spec/FundingHold.v1.md +60 -0
- package/docs/spec/GovernancePolicy.v1.md +34 -0
- package/docs/spec/GovernancePolicy.v2.md +30 -0
- package/docs/spec/INVARIANTS.md +389 -0
- package/docs/spec/InteractionDirectionMatrix.v1.md +30 -0
- package/docs/spec/InvoiceBundleManifest.v1.md +24 -0
- package/docs/spec/InvoiceClaim.v1.md +11 -0
- package/docs/spec/MONEY_RAIL_STATE_MACHINE.md +58 -0
- package/docs/spec/MarketplaceAcceptance.v2.md +46 -0
- package/docs/spec/MarketplaceOffer.v2.md +54 -0
- package/docs/spec/MeteringReport.v1.md +18 -0
- package/docs/spec/OperatorAction.v1.md +90 -0
- package/docs/spec/PRODUCER_ERRORS.md +42 -0
- package/docs/spec/PolicyDecision.v1.md +83 -0
- package/docs/spec/PricingMatrix.v1.md +20 -0
- package/docs/spec/PricingMatrixSignatures.v1.md +30 -0
- package/docs/spec/PricingMatrixSignatures.v2.md +29 -0
- package/docs/spec/ProduceCliOutput.v1.md +46 -0
- package/docs/spec/ProofBundleManifest.v1.md +24 -0
- package/docs/spec/README.md +109 -0
- package/docs/spec/REFERENCE_IMPLEMENTATIONS.md +29 -0
- package/docs/spec/REFERENCE_VERIFIER_BEHAVIOR.md +68 -0
- package/docs/spec/REMOTE_SIGNER.md +66 -0
- package/docs/spec/ReleaseIndex.v1.md +32 -0
- package/docs/spec/ReleaseIndexSignatures.v1.md +17 -0
- package/docs/spec/ReleaseTrust.v1.md +13 -0
- package/docs/spec/ReleaseTrust.v2.md +26 -0
- package/docs/spec/RemoteSignerRequest.v1.md +21 -0
- package/docs/spec/RemoteSignerResponse.v1.md +16 -0
- package/docs/spec/ReputationEvent.v1.md +63 -0
- package/docs/spec/RevocationList.v1.md +28 -0
- package/docs/spec/SIGNER_PROVIDER_PLUGIN.md +32 -0
- package/docs/spec/STRICTNESS.md +68 -0
- package/docs/spec/SUPPLY_CHAIN.md +33 -0
- package/docs/spec/SettlementAdjustment.v1.md +45 -0
- package/docs/spec/SettlementDecisionRecord.v1.md +48 -0
- package/docs/spec/SettlementDecisionRecord.v2.md +53 -0
- package/docs/spec/SettlementDecisionReport.v1.md +44 -0
- package/docs/spec/SettlementKernel.v1.md +59 -0
- package/docs/spec/SettlementReceipt.v1.md +63 -0
- package/docs/spec/SlaDefinition.v1.md +24 -0
- package/docs/spec/SlaEvaluation.v1.md +12 -0
- package/docs/spec/THREAT_MODEL.md +113 -0
- package/docs/spec/TOOL_PROVENANCE.md +30 -0
- package/docs/spec/TRUST_ANCHORS.md +84 -0
- package/docs/spec/TenantSettings.v1.md +90 -0
- package/docs/spec/TenantSettings.v2.md +99 -0
- package/docs/spec/TimestampProof.v1.md +25 -0
- package/docs/spec/ToolCallAgreement.v1.md +34 -0
- package/docs/spec/ToolCallEvidence.v1.md +47 -0
- package/docs/spec/ToolManifest.v1.md +47 -0
- package/docs/spec/VERIFIER_ENVIRONMENT.md +38 -0
- package/docs/spec/VERSIONING.md +107 -0
- package/docs/spec/VerificationReport.v1.md +50 -0
- package/docs/spec/VerifyAboutOutput.v1.md +10 -0
- package/docs/spec/VerifyCliOutput.v1.md +28 -0
- package/docs/spec/WARNINGS.md +83 -0
- package/docs/spec/error-codes.v1.txt +285 -0
- package/docs/spec/examples/agreement_delegation_v1.example.json +21 -0
- package/docs/spec/examples/arbitration_case_v1.example.json +26 -0
- package/docs/spec/examples/arbitration_verdict_v1.example.json +32 -0
- package/docs/spec/examples/dispute_open_envelope_v1.example.json +18 -0
- package/docs/spec/examples/produce_cli_output_v1.example.json +32 -0
- package/docs/spec/examples/release_index_signature_v1.example.json +9 -0
- package/docs/spec/examples/release_index_signatures_v1.example.json +14 -0
- package/docs/spec/examples/release_index_v1.example.json +15 -0
- package/docs/spec/examples/release_trust_v1.example.json +7 -0
- package/docs/spec/examples/release_trust_v2.example.json +22 -0
- package/docs/spec/examples/remote_signer_request_v1.example.json +18 -0
- package/docs/spec/examples/remote_signer_response_v1.example.json +8 -0
- package/docs/spec/examples/reputation_event_v1.example.json +29 -0
- package/docs/spec/examples/verification_report_v1.example.json +24 -0
- package/docs/spec/examples/verify_about_output_v1.example.json +29 -0
- package/docs/spec/examples/verify_cli_output_v1.example.json +13 -0
- package/docs/spec/legacy/MarketplaceAcceptance.v1.md +48 -0
- package/docs/spec/legacy/MarketplaceOffer.v1.md +56 -0
- package/docs/spec/legacy/schemas/MarketplaceAcceptance.v1.schema.json +53 -0
- package/docs/spec/legacy/schemas/MarketplaceOffer.v1.schema.json +61 -0
- package/docs/spec/producer-error-codes.v1.txt +14 -0
- package/docs/spec/schemas/AcceptanceCriteria.v1.schema.json +24 -0
- package/docs/spec/schemas/AcceptanceEvaluation.v1.schema.json +26 -0
- package/docs/spec/schemas/AgentEvent.v1.schema.json +49 -0
- package/docs/spec/schemas/AgentIdentity.v1.schema.json +129 -0
- package/docs/spec/schemas/AgentPassport.v1.schema.json +112 -0
- package/docs/spec/schemas/AgentReputation.v1.schema.json +151 -0
- package/docs/spec/schemas/AgentReputation.v2.schema.json +120 -0
- package/docs/spec/schemas/AgentRun.v1.schema.json +71 -0
- package/docs/spec/schemas/AgentRunSettlement.v1.schema.json +75 -0
- package/docs/spec/schemas/AgentWallet.v1.schema.json +54 -0
- package/docs/spec/schemas/AgreementDelegation.v1.schema.json +50 -0
- package/docs/spec/schemas/ArbitrationCase.v1.schema.json +133 -0
- package/docs/spec/schemas/ArbitrationVerdict.v1.schema.json +149 -0
- package/docs/spec/schemas/BundleHeadAttestation.v1.schema.json +21 -0
- package/docs/spec/schemas/ClosePackManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/DelegationGrant.v1.schema.json +102 -0
- package/docs/spec/schemas/DisputeOpenEnvelope.v1.schema.json +78 -0
- package/docs/spec/schemas/EvidenceIndex.v1.schema.json +41 -0
- package/docs/spec/schemas/ExecutionIntent.v1.schema.json +85 -0
- package/docs/spec/schemas/FinancePackBundleManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/FundingHold.v1.schema.json +46 -0
- package/docs/spec/schemas/GovernancePolicy.v1.schema.json +45 -0
- package/docs/spec/schemas/GovernancePolicy.v2.schema.json +70 -0
- package/docs/spec/schemas/InteractionDirectionMatrix.v1.schema.json +43 -0
- package/docs/spec/schemas/InvoiceBundleManifest.v1.schema.json +38 -0
- package/docs/spec/schemas/InvoiceClaim.v1.schema.json +39 -0
- package/docs/spec/schemas/MarketplaceAcceptance.v2.schema.json +53 -0
- package/docs/spec/schemas/MarketplaceOffer.v2.schema.json +61 -0
- package/docs/spec/schemas/MeteringReport.v1.schema.json +45 -0
- package/docs/spec/schemas/OperatorAction.v1.schema.json +113 -0
- package/docs/spec/schemas/PolicyDecision.v1.schema.json +74 -0
- package/docs/spec/schemas/PricingMatrix.v1.schema.json +24 -0
- package/docs/spec/schemas/PricingMatrixSignatures.v1.schema.json +24 -0
- package/docs/spec/schemas/PricingMatrixSignatures.v2.schema.json +24 -0
- package/docs/spec/schemas/ProduceCliOutput.v1.schema.json +107 -0
- package/docs/spec/schemas/ProofBundleManifest.v1.schema.json +37 -0
- package/docs/spec/schemas/PublicKeys.v1.schema.json +33 -0
- package/docs/spec/schemas/ReleaseIndex.v1.schema.json +45 -0
- package/docs/spec/schemas/ReleaseIndexSignature.v1.schema.json +16 -0
- package/docs/spec/schemas/ReleaseIndexSignatures.v1.schema.json +16 -0
- package/docs/spec/schemas/ReleaseTrust.v1.schema.json +15 -0
- package/docs/spec/schemas/ReleaseTrust.v2.schema.json +37 -0
- package/docs/spec/schemas/RemoteSignerPublicKeyResponse.v1.schema.json +14 -0
- package/docs/spec/schemas/RemoteSignerRequest.v1.schema.json +24 -0
- package/docs/spec/schemas/RemoteSignerResponse.v1.schema.json +10 -0
- package/docs/spec/schemas/RemoteSignerSignRequest.v1.schema.json +27 -0
- package/docs/spec/schemas/RemoteSignerSignResponse.v1.schema.json +16 -0
- package/docs/spec/schemas/ReputationEvent.v1.schema.json +164 -0
- package/docs/spec/schemas/RevocationList.v1.schema.json +51 -0
- package/docs/spec/schemas/SettlementAdjustment.v1.schema.json +44 -0
- package/docs/spec/schemas/SettlementDecisionRecord.v1.schema.json +66 -0
- package/docs/spec/schemas/SettlementDecisionRecord.v2.schema.json +149 -0
- package/docs/spec/schemas/SettlementDecisionReport.v1.schema.json +61 -0
- package/docs/spec/schemas/SettlementReceipt.v1.schema.json +135 -0
- package/docs/spec/schemas/SlaDefinition.v1.schema.json +33 -0
- package/docs/spec/schemas/SlaEvaluation.v1.schema.json +26 -0
- package/docs/spec/schemas/TenantSettings.v1.schema.json +90 -0
- package/docs/spec/schemas/TenantSettings.v2.schema.json +161 -0
- package/docs/spec/schemas/TimestampProof.v1.schema.json +17 -0
- package/docs/spec/schemas/ToolCallAgreement.v1.schema.json +34 -0
- package/docs/spec/schemas/ToolCallEvidence.v1.schema.json +45 -0
- package/docs/spec/schemas/ToolManifest.v1.schema.json +54 -0
- package/docs/spec/schemas/VerificationReport.v1.schema.json +83 -0
- package/docs/spec/schemas/VerifyAboutOutput.v1.schema.json +54 -0
- package/docs/spec/schemas/VerifyCliOutput.v1.schema.json +75 -0
- package/docs/spec/schemas/VerifyReleaseOutput.v1.schema.json +47 -0
- package/docs/spec/x402-error-codes.v1.txt +35 -0
- package/docs/templates/buyer-email.txt +18 -0
- package/docs/templates/buyer-one-pager.md +24 -0
- package/package.json +53 -6
- package/scripts/acceptance/full-stack.mjs +734 -0
- package/scripts/acceptance/full-stack.sh +99 -0
- package/scripts/audit/build-audit-packet.mjs +242 -0
- package/scripts/backup-pg.sh +45 -0
- package/scripts/backup-restore/README.md +18 -0
- package/scripts/backup-restore/capture-state.mjs +130 -0
- package/scripts/backup-restore/client.mjs +97 -0
- package/scripts/backup-restore/seed-workload.mjs +235 -0
- package/scripts/backup-restore/verify-state.mjs +139 -0
- package/scripts/backup-restore-test.sh +217 -0
- package/scripts/chaos.js +221 -0
- package/scripts/ci/build-launch-cutover-packet.mjs +304 -0
- package/scripts/ci/build-self-serve-benchmark-report.mjs +122 -0
- package/scripts/ci/changelog-guard.mjs +145 -0
- package/scripts/ci/check-kernel-v0-launch-gate.mjs +233 -0
- package/scripts/ci/check-secret-hygiene.mjs +78 -0
- package/scripts/ci/check-version-consistency.mjs +42 -0
- package/scripts/ci/cli-pack-smoke.mjs +160 -0
- package/scripts/ci/flake-budget-guard.mjs +68 -0
- package/scripts/ci/generate-error-codes.mjs +54 -0
- package/scripts/ci/lib/lighthouse-tracker.mjs +90 -0
- package/scripts/ci/lib/self-serve-launch-gate.mjs +89 -0
- package/scripts/ci/npm-pack-smoke.mjs +454 -0
- package/scripts/ci/run-10x-throughput-drill.mjs +318 -0
- package/scripts/ci/run-10x-throughput-incident-rehearsal.mjs +368 -0
- package/scripts/ci/run-arbitration-workspace-browser-e2e.sh +22 -0
- package/scripts/ci/run-circle-sandbox-smoke.mjs +237 -0
- package/scripts/ci/run-go-live-gate.mjs +150 -0
- package/scripts/ci/run-kernel-v0-ship-gate.mjs +97 -0
- package/scripts/ci/run-mcp-host-cert-matrix.mjs +201 -0
- package/scripts/ci/run-mcp-host-smoke.mjs +473 -0
- package/scripts/ci/run-offline-verification-parity-gate.mjs +762 -0
- package/scripts/ci/run-onboarding-host-success-gate.mjs +516 -0
- package/scripts/ci/run-onboarding-policy-slo-gate.mjs +537 -0
- package/scripts/ci/run-production-cutover-gate.mjs +540 -0
- package/scripts/ci/run-public-openclaw-npx-smoke.mjs +148 -0
- package/scripts/ci/run-release-promotion-guard.mjs +756 -0
- package/scripts/ci/run-self-serve-launch-gate.mjs +56 -0
- package/scripts/ci/runtime-import-smoke.mjs +58 -0
- package/scripts/ci/update-lighthouse-tracker.mjs +112 -0
- package/scripts/closepack/lib.mjs +286 -0
- package/scripts/collect-debug.sh +263 -0
- package/scripts/demo/compositional-settlement-3hop.mjs +237 -0
- package/scripts/demo/delivery-robot/export-ui-fixture.mjs +188 -0
- package/scripts/demo/delivery-robot/generate.mjs +377 -0
- package/scripts/demo/kernel-agent-goes-shopping.mjs +202 -0
- package/scripts/demo/magic-link-first-green.mjs +118 -0
- package/scripts/demo/magic-link-kind-smoke.mjs +577 -0
- package/scripts/demo/mcp-paid-exa.mjs +1110 -0
- package/scripts/dev/billing-doctor.sh +145 -0
- package/scripts/dev/billing-smoke-prod.sh +219 -0
- package/scripts/dev/billing-webhook-replay.sh +161 -0
- package/scripts/dev/env.dev.example +29 -0
- package/scripts/dev/env.sh +37 -0
- package/scripts/dev/new-sdk-key.sh +81 -0
- package/scripts/dev/sdk-first-run.sh +21 -0
- package/scripts/dev/smoke-x402-gateway.sh +115 -0
- package/scripts/dev/start-api.sh +24 -0
- package/scripts/doctor/mcp-host.mjs +120 -0
- package/scripts/examples/produce-and-verify-jobproof.mjs +191 -0
- package/scripts/examples/sdk-first-paid-rfq.py +105 -0
- package/scripts/examples/sdk-first-verified-run.mjs +85 -0
- package/scripts/examples/sdk-first-verified-run.py +99 -0
- package/scripts/examples/sdk-tenant-analytics.mjs +103 -0
- package/scripts/examples/sdk-tenant-analytics.py +118 -0
- package/scripts/finance-pack/bundle.mjs +284 -0
- package/scripts/fixtures/generate-bundle-fixtures.mjs +877 -0
- package/scripts/governance/export.mjs +169 -0
- package/scripts/load/delivery-stress.k6.js +183 -0
- package/scripts/load/ingest-burst.k6.js +236 -0
- package/scripts/load/run-delivery-load.js +66 -0
- package/scripts/load/webhook-receiver.js +131 -0
- package/scripts/magic-link/migrate-run-records-to-db.mjs +35 -0
- package/scripts/mcp/probe.mjs +238 -0
- package/scripts/mcp/settld-mcp-http-gateway.mjs +178 -0
- package/scripts/mcp/settld-mcp-server.mjs +1511 -0
- package/scripts/openapi/write.mjs +13 -0
- package/scripts/ops/bootstrap-tenant-conformance.mjs +185 -0
- package/scripts/ops/build-x402-pilot-reliability-report.mjs +489 -0
- package/scripts/ops/check-x402-receipt-sample.mjs +181 -0
- package/scripts/ops/design-partner-run-packet.mjs +466 -0
- package/scripts/ops/dispute-finance-reconciliation-packet.mjs +313 -0
- package/scripts/ops/hosted-baseline-evidence.mjs +890 -0
- package/scripts/ops/money-rails-chargeback-evidence.mjs +509 -0
- package/scripts/ops/money-rails-reconcile-evidence.mjs +180 -0
- package/scripts/ops/p0-seed-money-rail-operation.mjs +432 -0
- package/scripts/ops/run-x402-hitl-smoke.mjs +607 -0
- package/scripts/pilot/finance-pack.mjs +495 -0
- package/scripts/pilot/fixtures/robot-keypair.json +4 -0
- package/scripts/pilot/fixtures/server-signer.json +4 -0
- package/scripts/policy/cli.mjs +600 -0
- package/scripts/profile/cli.mjs +1324 -0
- package/scripts/proof-bundle/job.mjs +109 -0
- package/scripts/proof-bundle/lib.mjs +92 -0
- package/scripts/proof-bundle/month.mjs +103 -0
- package/scripts/provider/conformance-run.mjs +159 -0
- package/scripts/provider/keys-generate.mjs +135 -0
- package/scripts/provider/publish.mjs +420 -0
- package/scripts/quickstart/x402.mjs +334 -0
- package/scripts/register-entity-secret.mjs +102 -0
- package/scripts/release/build-artifacts.mjs +181 -0
- package/scripts/release/generate-release-index.mjs +112 -0
- package/scripts/release/release-index-lib.mjs +232 -0
- package/scripts/release/sign-release-index.mjs +85 -0
- package/scripts/release/validate-release-assets.mjs +170 -0
- package/scripts/release/verify-release.mjs +261 -0
- package/scripts/restore-pg.sh +34 -0
- package/scripts/scaffold/create-settld-paid-tool.mjs +19 -0
- package/scripts/sdk/smoke-python.py +30 -0
- package/scripts/sdk/smoke.mjs +16 -0
- package/scripts/settlement/x402-batch-worker.mjs +1091 -0
- package/scripts/setup/circle-bootstrap.mjs +310 -0
- package/scripts/setup/host-config.mjs +617 -0
- package/scripts/setup/onboard.mjs +1337 -0
- package/scripts/setup/openclaw-onboard.mjs +423 -0
- package/scripts/setup/wizard.mjs +986 -0
- package/scripts/slo/check.mjs +239 -0
- package/scripts/smoke/k8s-smoke.mjs +214 -0
- package/scripts/spec/generate-protocol-vectors.mjs +1019 -0
- package/scripts/test/check-no-generated-artifacts.sh +12 -0
- package/scripts/test/run.sh +59 -0
- package/scripts/trust/validate-trust-file.mjs +57 -0
- package/scripts/trust-config/rotate-settld-pay.mjs +277 -0
- package/scripts/trust-config/wizard.mjs +161 -0
- package/scripts/vendor-contract-test-lib.mjs +182 -0
- package/scripts/vendor-contract-test.mjs +55 -0
- package/scripts/vercel/build-mkdocs.sh +9 -0
- package/scripts/vercel/ignore-mkdocs.sh +25 -0
- package/scripts/vercel/install-mkdocs.sh +6 -0
- package/scripts/verify-pg.js +217 -0
- package/scripts/x402/receipt-verify.mjs +289 -0
- package/services/finance-sink/src/dedupe-store.js +29 -6
- package/services/receiver/src/dedupe-store.js +29 -5
- package/services/x402-gateway/Dockerfile +13 -0
- package/services/x402-gateway/README.md +58 -0
- package/services/x402-gateway/examples/upstream-mock.js +337 -0
- package/services/x402-gateway/src/server.js +1058 -0
- package/src/api/app.js +34658 -16940
- package/src/api/maintenance.js +70 -0
- package/src/api/middleware/trust-kernel.js +114 -0
- package/src/api/openapi.js +1778 -70
- package/src/api/persistence.js +456 -0
- package/src/api/server.js +81 -5
- package/src/api/store.js +1581 -62
- package/src/api/workers/deliveries.js +99 -4
- package/src/api/workers/insolvency-sweep.js +159 -0
- package/src/core/agent-card.js +69 -0
- package/src/core/agent-wallets.js +231 -0
- package/src/core/agreement-delegation.js +549 -0
- package/src/core/billing-plans.js +40 -6
- package/src/core/circle-reserve-adapter.js +845 -0
- package/src/core/event-policy.js +21 -2
- package/src/core/maintenance-locks.js +1 -0
- package/src/core/operator-action.js +303 -0
- package/src/core/paid-tool-manifest.js +318 -0
- package/src/core/policy-decision.js +322 -0
- package/src/core/policy-packs.js +207 -0
- package/src/core/profile-fingerprint.js +27 -0
- package/src/core/profile-simulation-reasons.js +84 -0
- package/src/core/profile-templates.js +242 -0
- package/src/core/provider-publish-conformance.js +525 -0
- package/src/core/provider-publish-proof.js +396 -0
- package/src/core/provider-quote-signature.js +170 -0
- package/src/core/settld-keys.js +112 -0
- package/src/core/settld-pay-token.js +344 -0
- package/src/core/settlement-kernel.js +239 -2
- package/src/core/settlement-verifier.js +335 -0
- package/src/core/tool-call-agreement.js +112 -0
- package/src/core/tool-call-evidence.js +144 -0
- package/src/core/tool-provider-signature.js +98 -0
- package/src/core/wallet-assignment-resolver.js +129 -0
- package/src/core/wallet-provider-bootstrap.js +365 -0
- package/src/core/x402-escalation-override.js +258 -0
- package/src/core/x402-gate.js +118 -0
- package/src/core/x402-provider-refund-decision.js +220 -0
- package/src/core/x402-receipt-verifier.js +708 -0
- package/src/core/x402-reversal-command.js +251 -0
- package/src/core/x402-wallet-issuer-decision.js +252 -0
- package/src/core/zk-verifier.js +300 -0
- package/src/db/migrations/029_reputation_event_index.sql +54 -0
- package/src/db/migrations/030_artifacts_source_event_unique_job_only.sql +15 -0
- package/src/db/pg.js +18 -7
- package/src/db/store-pg.js +1508 -111
|
@@ -0,0 +1,1511 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Sprint 23 MCP spike: JSON-RPC 2.0 over stdio exposing curated Settld tools.
|
|
4
|
+
*
|
|
5
|
+
* - Transport: stdio
|
|
6
|
+
* - Framing: newline-delimited JSON; also accepts Content-Length framed messages.
|
|
7
|
+
* - Auth: x-proxy-api-key (SETTLD_API_KEY)
|
|
8
|
+
*
|
|
9
|
+
* Production hardening (SSE, auth variants, rate limiting, telemetry) is Sprint 25+.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import crypto from "node:crypto";
|
|
13
|
+
|
|
14
|
+
import { fetchWithSettldAutopay } from "../../packages/api-sdk/src/x402-autopay.js";
|
|
15
|
+
|
|
16
|
+
function nowMs() {
|
|
17
|
+
return Date.now();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function sleep(ms) {
|
|
21
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function assertNonEmptyString(v, name) {
|
|
25
|
+
if (typeof v !== "string" || v.trim() === "") throw new TypeError(`${name} must be a non-empty string`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parseOptionalJsonObject(value, name) {
|
|
29
|
+
if (value === null || value === undefined || String(value).trim() === "") return null;
|
|
30
|
+
const raw = String(value).trim();
|
|
31
|
+
const tryParse = (text) => {
|
|
32
|
+
const parsed = JSON.parse(text);
|
|
33
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new TypeError(`${name} must decode to a JSON object`);
|
|
34
|
+
return parsed;
|
|
35
|
+
};
|
|
36
|
+
try {
|
|
37
|
+
return tryParse(raw);
|
|
38
|
+
} catch {
|
|
39
|
+
try {
|
|
40
|
+
const decoded = Buffer.from(raw, "base64url").toString("utf8");
|
|
41
|
+
return tryParse(decoded);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
throw new TypeError(`${name} must be JSON or base64url-encoded JSON`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function safeJsonParse(text) {
|
|
49
|
+
try {
|
|
50
|
+
return { ok: true, value: JSON.parse(text) };
|
|
51
|
+
} catch (err) {
|
|
52
|
+
return { ok: false, error: err };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function jsonRpcError(code, message, data = null) {
|
|
57
|
+
return { code, message, data };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function redactSecrets(value) {
|
|
61
|
+
// Best-effort redaction for logs/results; do not rely on this for security.
|
|
62
|
+
if (value === null || value === undefined) return value;
|
|
63
|
+
if (typeof value === "string") {
|
|
64
|
+
if (value.startsWith("sk_") || value.startsWith("tok_")) return "[redacted]";
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
if (Array.isArray(value)) return value.map(redactSecrets);
|
|
68
|
+
if (typeof value === "object") {
|
|
69
|
+
const out = {};
|
|
70
|
+
for (const [k, v] of Object.entries(value)) {
|
|
71
|
+
if (String(k).toLowerCase().includes("secret") || String(k).toLowerCase().includes("password") || String(k).toLowerCase().includes("token")) {
|
|
72
|
+
out[k] = "[redacted]";
|
|
73
|
+
} else {
|
|
74
|
+
out[k] = redactSecrets(v);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function contentText(text) {
|
|
83
|
+
return { type: "text", text: String(text ?? "") };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function asTextResult(obj) {
|
|
87
|
+
const text = typeof obj === "string" ? obj : JSON.stringify(obj, null, 2);
|
|
88
|
+
return { content: [contentText(text)], isError: false };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function asErrorResult(err) {
|
|
92
|
+
const out = {
|
|
93
|
+
error: true,
|
|
94
|
+
message: err?.message ?? String(err ?? "error"),
|
|
95
|
+
details: err?.details ?? null,
|
|
96
|
+
statusCode: err?.statusCode ?? null
|
|
97
|
+
};
|
|
98
|
+
return { content: [contentText(JSON.stringify(out, null, 2))], isError: true };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function makeIdempotencyKey(prefix) {
|
|
102
|
+
return `${prefix}_${crypto.randomUUID()}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function sha256HexText(value) {
|
|
106
|
+
return crypto.createHash("sha256").update(String(value ?? ""), "utf8").digest("hex");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function isSha256Hex(value) {
|
|
110
|
+
return typeof value === "string" && /^[0-9a-f]{64}$/i.test(value.trim());
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function collectSettldHeaders(responseHeaders) {
|
|
114
|
+
const out = {};
|
|
115
|
+
for (const [k, v] of responseHeaders.entries()) {
|
|
116
|
+
const key = String(k).toLowerCase();
|
|
117
|
+
if (key.startsWith("x-settld-")) out[key] = v;
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function parseCsvHeader(value) {
|
|
123
|
+
if (typeof value !== "string" || value.trim() === "") return [];
|
|
124
|
+
return value
|
|
125
|
+
.split(",")
|
|
126
|
+
.map((row) => row.trim())
|
|
127
|
+
.filter(Boolean);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function normalizePolicyDecision(value) {
|
|
131
|
+
const raw = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
132
|
+
return ["allow", "challenge", "deny", "escalate"].includes(raw) ? raw : null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function parseSettldDecisionMetadata(headers) {
|
|
136
|
+
const policyVersionRaw = Number(headers["x-settld-policy-version"] ?? Number.NaN);
|
|
137
|
+
const reasonCodes = parseCsvHeader(headers["x-settld-verification-codes"]);
|
|
138
|
+
const reasonCode = typeof headers["x-settld-reason-code"] === "string" && headers["x-settld-reason-code"].trim() !== ""
|
|
139
|
+
? headers["x-settld-reason-code"].trim()
|
|
140
|
+
: reasonCodes[0] ?? null;
|
|
141
|
+
return {
|
|
142
|
+
policyDecision: normalizePolicyDecision(headers["x-settld-policy-decision"]),
|
|
143
|
+
decisionId: typeof headers["x-settld-decision-id"] === "string" && headers["x-settld-decision-id"].trim() !== ""
|
|
144
|
+
? headers["x-settld-decision-id"].trim()
|
|
145
|
+
: null,
|
|
146
|
+
policyHash:
|
|
147
|
+
typeof headers["x-settld-policy-hash"] === "string" && /^[0-9a-f]{64}$/i.test(headers["x-settld-policy-hash"].trim())
|
|
148
|
+
? headers["x-settld-policy-hash"].trim().toLowerCase()
|
|
149
|
+
: null,
|
|
150
|
+
policyVersion: Number.isSafeInteger(policyVersionRaw) && policyVersionRaw > 0 ? policyVersionRaw : null,
|
|
151
|
+
reasonCode,
|
|
152
|
+
reasonCodes
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function assertPolicyRuntimeMetadata({ headers, toolName }) {
|
|
157
|
+
const required = [
|
|
158
|
+
"x-settld-settlement-status",
|
|
159
|
+
"x-settld-verification-status",
|
|
160
|
+
"x-settld-policy-decision",
|
|
161
|
+
"x-settld-policy-hash",
|
|
162
|
+
"x-settld-decision-id"
|
|
163
|
+
];
|
|
164
|
+
const missing = required.filter((key) => typeof headers[key] !== "string" || headers[key].trim() === "");
|
|
165
|
+
if (missing.length > 0) {
|
|
166
|
+
const err = new Error(`${toolName} response missing settld policy runtime metadata`);
|
|
167
|
+
err.code = "SETTLD_POLICY_RUNTIME_METADATA_MISSING";
|
|
168
|
+
err.details = { missingHeaders: missing };
|
|
169
|
+
throw err;
|
|
170
|
+
}
|
|
171
|
+
const metadata = parseSettldDecisionMetadata(headers);
|
|
172
|
+
if (!metadata.policyDecision) {
|
|
173
|
+
const err = new Error(`${toolName} returned unsupported x-settld-policy-decision value`);
|
|
174
|
+
err.code = "SETTLD_POLICY_RUNTIME_DECISION_INVALID";
|
|
175
|
+
err.details = { policyDecision: headers["x-settld-policy-decision"] ?? null };
|
|
176
|
+
throw err;
|
|
177
|
+
}
|
|
178
|
+
return metadata;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function generateEd25519PublicKeyPem() {
|
|
182
|
+
const { publicKey } = crypto.generateKeyPairSync("ed25519");
|
|
183
|
+
return publicKey.export({ format: "pem", type: "spki" });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
class StdioJsonRpcStream {
|
|
187
|
+
constructor({ input, output }) {
|
|
188
|
+
this.input = input;
|
|
189
|
+
this.output = output;
|
|
190
|
+
this._buf = Buffer.alloc(0);
|
|
191
|
+
this._onMessage = null;
|
|
192
|
+
this._onError = null;
|
|
193
|
+
input.on("data", (chunk) => this._onData(chunk));
|
|
194
|
+
input.on("error", (err) => this._emitError(err));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
onMessage(fn) {
|
|
198
|
+
this._onMessage = fn;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
onError(fn) {
|
|
202
|
+
this._onError = fn;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
send(obj) {
|
|
206
|
+
const payload = Buffer.from(JSON.stringify(obj), "utf8");
|
|
207
|
+
// Newline-delimited JSON output is sufficient for MCP clients; we also accept Content-Length on input.
|
|
208
|
+
this.output.write(payload);
|
|
209
|
+
this.output.write("\n");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
_emitMessage(text) {
|
|
213
|
+
if (this._onMessage) this._onMessage(text);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
_emitError(err) {
|
|
217
|
+
if (this._onError) this._onError(err);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
_onData(chunk) {
|
|
221
|
+
if (!chunk || chunk.length === 0) return;
|
|
222
|
+
this._buf = Buffer.concat([this._buf, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]);
|
|
223
|
+
this._drain();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
_drain() {
|
|
227
|
+
for (;;) {
|
|
228
|
+
if (this._buf.length === 0) return;
|
|
229
|
+
|
|
230
|
+
// Content-Length framed messages (LSP-style).
|
|
231
|
+
const prefix = this._buf.slice(0, 15).toString("utf8");
|
|
232
|
+
if (prefix === "Content-Length:") {
|
|
233
|
+
const headerEnd = this._buf.indexOf("\r\n\r\n");
|
|
234
|
+
if (headerEnd === -1) return;
|
|
235
|
+
const header = this._buf.slice(0, headerEnd).toString("utf8");
|
|
236
|
+
const m = header.match(/Content-Length:\\s*(\\d+)/i);
|
|
237
|
+
if (!m) {
|
|
238
|
+
this._emitError(new Error("invalid Content-Length framing (missing length)"));
|
|
239
|
+
this._buf = Buffer.alloc(0);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const len = Number(m[1]);
|
|
243
|
+
if (!Number.isFinite(len) || len < 0) {
|
|
244
|
+
this._emitError(new Error("invalid Content-Length framing (bad length)"));
|
|
245
|
+
this._buf = Buffer.alloc(0);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const start = headerEnd + 4;
|
|
249
|
+
const end = start + len;
|
|
250
|
+
if (this._buf.length < end) return;
|
|
251
|
+
const jsonText = this._buf.slice(start, end).toString("utf8");
|
|
252
|
+
this._buf = this._buf.slice(end);
|
|
253
|
+
this._emitMessage(jsonText);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Newline-delimited JSON messages.
|
|
258
|
+
const nl = this._buf.indexOf("\n");
|
|
259
|
+
if (nl === -1) return;
|
|
260
|
+
const line = this._buf.slice(0, nl).toString("utf8").trim();
|
|
261
|
+
this._buf = this._buf.slice(nl + 1);
|
|
262
|
+
if (!line) continue;
|
|
263
|
+
this._emitMessage(line);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function makeSettldClient({ baseUrl, tenantId, apiKey, protocol }) {
|
|
269
|
+
let cachedProtocol = protocol || null;
|
|
270
|
+
|
|
271
|
+
async function discoverProtocol() {
|
|
272
|
+
if (cachedProtocol) return cachedProtocol;
|
|
273
|
+
try {
|
|
274
|
+
const res = await fetch(new URL("/healthz", baseUrl), {
|
|
275
|
+
method: "GET",
|
|
276
|
+
headers: {
|
|
277
|
+
"x-proxy-tenant-id": tenantId,
|
|
278
|
+
"x-proxy-api-key": apiKey
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
const hdr = res.headers.get("x-settld-protocol");
|
|
282
|
+
if (hdr && String(hdr).trim() !== "") {
|
|
283
|
+
cachedProtocol = String(hdr).trim();
|
|
284
|
+
return cachedProtocol;
|
|
285
|
+
}
|
|
286
|
+
} catch {
|
|
287
|
+
// Ignore; we will fallback.
|
|
288
|
+
}
|
|
289
|
+
cachedProtocol = cachedProtocol || "1.0";
|
|
290
|
+
return cachedProtocol;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function requestJson(path, { method = "GET", body = null, write = false, headers = {}, idem = null } = {}) {
|
|
294
|
+
const url = new URL(path, baseUrl);
|
|
295
|
+
const protocolHeader = write ? await discoverProtocol() : null;
|
|
296
|
+
const h = {
|
|
297
|
+
"x-proxy-tenant-id": tenantId,
|
|
298
|
+
"x-proxy-api-key": apiKey,
|
|
299
|
+
...(write ? { "x-settld-protocol": protocolHeader } : {}),
|
|
300
|
+
...(body !== null ? { "content-type": "application/json" } : {}),
|
|
301
|
+
...(idem ? { "x-idempotency-key": String(idem) } : {}),
|
|
302
|
+
...headers
|
|
303
|
+
};
|
|
304
|
+
const res = await fetch(url, {
|
|
305
|
+
method,
|
|
306
|
+
headers: h,
|
|
307
|
+
body: body === null ? undefined : JSON.stringify(body)
|
|
308
|
+
});
|
|
309
|
+
const text = await res.text();
|
|
310
|
+
let json = null;
|
|
311
|
+
try {
|
|
312
|
+
json = text ? JSON.parse(text) : null;
|
|
313
|
+
} catch {
|
|
314
|
+
json = null;
|
|
315
|
+
}
|
|
316
|
+
if (!res.ok) {
|
|
317
|
+
const msg =
|
|
318
|
+
(json && (json.message || json.error)) ? String(json.message || json.error) :
|
|
319
|
+
text ? String(text) :
|
|
320
|
+
`HTTP ${res.status}`;
|
|
321
|
+
const err = new Error(msg);
|
|
322
|
+
err.statusCode = res.status;
|
|
323
|
+
err.details = json && (json.details || json.errorDetails) ? (json.details || json.errorDetails) : json;
|
|
324
|
+
throw err;
|
|
325
|
+
}
|
|
326
|
+
return json;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async function getRunPrevChainHash({ agentId, runId }) {
|
|
330
|
+
const out = await requestJson(`/agents/${encodeURIComponent(agentId)}/runs/${encodeURIComponent(runId)}/events`, { method: "GET" });
|
|
331
|
+
const events = Array.isArray(out?.events) ? out.events : [];
|
|
332
|
+
const last = events.length ? events[events.length - 1] : null;
|
|
333
|
+
const prev = last && typeof last.chainHash === "string" && last.chainHash.trim() !== "" ? last.chainHash : null;
|
|
334
|
+
return { prevChainHash: prev, events };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
discoverProtocol,
|
|
339
|
+
requestJson,
|
|
340
|
+
getRunPrevChainHash
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function makePaidToolsClient({ baseUrl, tenantId, fetchImpl = fetch, agentPassport = null }) {
|
|
345
|
+
const normalizedBaseUrl = (() => {
|
|
346
|
+
if (typeof baseUrl !== "string" || baseUrl.trim() === "") return null;
|
|
347
|
+
return baseUrl.trim();
|
|
348
|
+
})();
|
|
349
|
+
|
|
350
|
+
async function exaSearch({ query, numResults = 5 } = {}) {
|
|
351
|
+
if (!normalizedBaseUrl) throw new Error("SETTLD_PAID_TOOLS_BASE_URL is required for settld.exa_search_paid");
|
|
352
|
+
const normalizedQuery = String(query ?? "").trim();
|
|
353
|
+
assertNonEmptyString(normalizedQuery, "query");
|
|
354
|
+
|
|
355
|
+
const normalizedNumResultsRaw = Number(numResults ?? 5);
|
|
356
|
+
if (!Number.isSafeInteger(normalizedNumResultsRaw) || normalizedNumResultsRaw < 1 || normalizedNumResultsRaw > 10) {
|
|
357
|
+
throw new TypeError("numResults must be an integer between 1 and 10");
|
|
358
|
+
}
|
|
359
|
+
const normalizedNumResults = normalizedNumResultsRaw;
|
|
360
|
+
|
|
361
|
+
const url = new URL("/exa/search", normalizedBaseUrl);
|
|
362
|
+
url.searchParams.set("q", normalizedQuery);
|
|
363
|
+
url.searchParams.set("numResults", String(normalizedNumResults));
|
|
364
|
+
|
|
365
|
+
let challenge = null;
|
|
366
|
+
const res = await fetchWithSettldAutopay(
|
|
367
|
+
url,
|
|
368
|
+
{
|
|
369
|
+
method: "GET",
|
|
370
|
+
headers: { "x-proxy-tenant-id": tenantId }
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
fetch: fetchImpl,
|
|
374
|
+
...(agentPassport ? { agentPassport } : {}),
|
|
375
|
+
onChallenge: (metadata) => {
|
|
376
|
+
challenge = metadata;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
);
|
|
380
|
+
const text = await res.text();
|
|
381
|
+
let json = null;
|
|
382
|
+
try {
|
|
383
|
+
json = text ? JSON.parse(text) : null;
|
|
384
|
+
} catch {
|
|
385
|
+
json = null;
|
|
386
|
+
}
|
|
387
|
+
if (!res.ok) {
|
|
388
|
+
const msg = json?.error ?? json?.message ?? text ?? `HTTP ${res.status}`;
|
|
389
|
+
const err = new Error(String(msg));
|
|
390
|
+
err.statusCode = res.status;
|
|
391
|
+
err.details = json;
|
|
392
|
+
throw err;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const headers = collectSettldHeaders(res.headers);
|
|
396
|
+
const decision = assertPolicyRuntimeMetadata({ headers, toolName: "settld.exa_search_paid" });
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
ok: true,
|
|
400
|
+
query: normalizedQuery,
|
|
401
|
+
numResults: normalizedNumResults,
|
|
402
|
+
response: json,
|
|
403
|
+
headers,
|
|
404
|
+
decision,
|
|
405
|
+
challenge
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async function weatherCurrent({ city, unit = "c" } = {}) {
|
|
410
|
+
if (!normalizedBaseUrl) throw new Error("SETTLD_PAID_TOOLS_BASE_URL is required for settld.weather_current_paid");
|
|
411
|
+
const normalizedCity = String(city ?? "").trim();
|
|
412
|
+
assertNonEmptyString(normalizedCity, "city");
|
|
413
|
+
|
|
414
|
+
const normalizedUnitRaw = String(unit ?? "c").trim().toLowerCase();
|
|
415
|
+
if (normalizedUnitRaw !== "c" && normalizedUnitRaw !== "f") {
|
|
416
|
+
throw new TypeError("unit must be c or f");
|
|
417
|
+
}
|
|
418
|
+
const normalizedUnit = normalizedUnitRaw;
|
|
419
|
+
|
|
420
|
+
const url = new URL("/weather/current", normalizedBaseUrl);
|
|
421
|
+
url.searchParams.set("city", normalizedCity);
|
|
422
|
+
url.searchParams.set("unit", normalizedUnit);
|
|
423
|
+
|
|
424
|
+
let challenge = null;
|
|
425
|
+
const res = await fetchWithSettldAutopay(
|
|
426
|
+
url,
|
|
427
|
+
{
|
|
428
|
+
method: "GET",
|
|
429
|
+
headers: { "x-proxy-tenant-id": tenantId }
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
fetch: fetchImpl,
|
|
433
|
+
...(agentPassport ? { agentPassport } : {}),
|
|
434
|
+
onChallenge: (metadata) => {
|
|
435
|
+
challenge = metadata;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
const text = await res.text();
|
|
440
|
+
let json = null;
|
|
441
|
+
try {
|
|
442
|
+
json = text ? JSON.parse(text) : null;
|
|
443
|
+
} catch {
|
|
444
|
+
json = null;
|
|
445
|
+
}
|
|
446
|
+
if (!res.ok) {
|
|
447
|
+
const msg = json?.error ?? json?.message ?? text ?? `HTTP ${res.status}`;
|
|
448
|
+
const err = new Error(String(msg));
|
|
449
|
+
err.statusCode = res.status;
|
|
450
|
+
err.details = json;
|
|
451
|
+
throw err;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const headers = collectSettldHeaders(res.headers);
|
|
455
|
+
const decision = assertPolicyRuntimeMetadata({ headers, toolName: "settld.weather_current_paid" });
|
|
456
|
+
|
|
457
|
+
return {
|
|
458
|
+
ok: true,
|
|
459
|
+
city: normalizedCity,
|
|
460
|
+
unit: normalizedUnit,
|
|
461
|
+
response: json,
|
|
462
|
+
headers,
|
|
463
|
+
decision,
|
|
464
|
+
challenge
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
async function llmCompletion({ prompt, model = "gpt-4o-mini", maxTokens = 128 } = {}) {
|
|
469
|
+
if (!normalizedBaseUrl) throw new Error("SETTLD_PAID_TOOLS_BASE_URL is required for settld.llm_completion_paid");
|
|
470
|
+
const normalizedPrompt = String(prompt ?? "").trim();
|
|
471
|
+
assertNonEmptyString(normalizedPrompt, "prompt");
|
|
472
|
+
|
|
473
|
+
const normalizedModel = String(model ?? "").trim() || "gpt-4o-mini";
|
|
474
|
+
const normalizedMaxTokensRaw = Number(maxTokens ?? 128);
|
|
475
|
+
if (!Number.isSafeInteger(normalizedMaxTokensRaw) || normalizedMaxTokensRaw < 1 || normalizedMaxTokensRaw > 512) {
|
|
476
|
+
throw new TypeError("maxTokens must be an integer between 1 and 512");
|
|
477
|
+
}
|
|
478
|
+
const normalizedMaxTokens = normalizedMaxTokensRaw;
|
|
479
|
+
|
|
480
|
+
const url = new URL("/llm/completions", normalizedBaseUrl);
|
|
481
|
+
url.searchParams.set("prompt", normalizedPrompt);
|
|
482
|
+
url.searchParams.set("model", normalizedModel);
|
|
483
|
+
url.searchParams.set("maxTokens", String(normalizedMaxTokens));
|
|
484
|
+
|
|
485
|
+
let challenge = null;
|
|
486
|
+
const res = await fetchWithSettldAutopay(
|
|
487
|
+
url,
|
|
488
|
+
{
|
|
489
|
+
method: "GET",
|
|
490
|
+
headers: { "x-proxy-tenant-id": tenantId }
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
fetch: fetchImpl,
|
|
494
|
+
...(agentPassport ? { agentPassport } : {}),
|
|
495
|
+
onChallenge: (metadata) => {
|
|
496
|
+
challenge = metadata;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
);
|
|
500
|
+
const text = await res.text();
|
|
501
|
+
let json = null;
|
|
502
|
+
try {
|
|
503
|
+
json = text ? JSON.parse(text) : null;
|
|
504
|
+
} catch {
|
|
505
|
+
json = null;
|
|
506
|
+
}
|
|
507
|
+
if (!res.ok) {
|
|
508
|
+
const msg = json?.error ?? json?.message ?? text ?? `HTTP ${res.status}`;
|
|
509
|
+
const err = new Error(String(msg));
|
|
510
|
+
err.statusCode = res.status;
|
|
511
|
+
err.details = json;
|
|
512
|
+
throw err;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const headers = collectSettldHeaders(res.headers);
|
|
516
|
+
const decision = assertPolicyRuntimeMetadata({ headers, toolName: "settld.llm_completion_paid" });
|
|
517
|
+
|
|
518
|
+
return {
|
|
519
|
+
ok: true,
|
|
520
|
+
prompt: normalizedPrompt,
|
|
521
|
+
model: normalizedModel,
|
|
522
|
+
maxTokens: normalizedMaxTokens,
|
|
523
|
+
response: json,
|
|
524
|
+
headers,
|
|
525
|
+
decision,
|
|
526
|
+
challenge
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return { exaSearch, weatherCurrent, llmCompletion };
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function buildTools() {
|
|
534
|
+
return [
|
|
535
|
+
{
|
|
536
|
+
name: "settld.create_agreement",
|
|
537
|
+
description:
|
|
538
|
+
"Create a marketplace-backed agreement + run by registering payer/payee agents, funding payer, creating an RFQ, submitting a bid, and accepting it.",
|
|
539
|
+
inputSchema: {
|
|
540
|
+
type: "object",
|
|
541
|
+
additionalProperties: false,
|
|
542
|
+
properties: {
|
|
543
|
+
amountCents: { type: "integer", minimum: 1, default: 500 },
|
|
544
|
+
currency: { type: "string", default: "USD" },
|
|
545
|
+
title: { type: "string", default: "MCP spike agreement" },
|
|
546
|
+
description: { type: ["string", "null"], default: null },
|
|
547
|
+
capability: { type: "string", default: "agent-task:demo" },
|
|
548
|
+
disputeWindowDays: { type: "integer", minimum: 0, default: 7 },
|
|
549
|
+
payerDisplayName: { type: "string", default: "MCP Payer" },
|
|
550
|
+
payeeDisplayName: { type: "string", default: "MCP Payee" }
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
name: "settld.submit_evidence",
|
|
556
|
+
description: "Append an EVIDENCE_ADDED event to an agent run (handles expected prevChainHash precondition).",
|
|
557
|
+
inputSchema: {
|
|
558
|
+
type: "object",
|
|
559
|
+
additionalProperties: false,
|
|
560
|
+
required: ["agentId", "runId", "evidenceRef"],
|
|
561
|
+
properties: {
|
|
562
|
+
agentId: { type: "string" },
|
|
563
|
+
runId: { type: "string" },
|
|
564
|
+
evidenceRef: { type: "string" }
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
name: "settld.settle_run",
|
|
570
|
+
description: "Append a RUN_COMPLETED or RUN_FAILED event (triggers settlement auto-resolution).",
|
|
571
|
+
inputSchema: {
|
|
572
|
+
type: "object",
|
|
573
|
+
additionalProperties: false,
|
|
574
|
+
required: ["agentId", "runId"],
|
|
575
|
+
properties: {
|
|
576
|
+
agentId: { type: "string" },
|
|
577
|
+
runId: { type: "string" },
|
|
578
|
+
outcome: { type: "string", enum: ["completed", "failed"], default: "completed" },
|
|
579
|
+
outputRef: { type: ["string", "null"], default: null },
|
|
580
|
+
errorCode: { type: ["string", "null"], default: null },
|
|
581
|
+
errorMessage: { type: ["string", "null"], default: null }
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
name: "settld.exa_search_paid",
|
|
587
|
+
description: "Execute a paid Exa-style search through the x402 gateway with transparent Settld autopay.",
|
|
588
|
+
inputSchema: {
|
|
589
|
+
type: "object",
|
|
590
|
+
additionalProperties: false,
|
|
591
|
+
required: ["query"],
|
|
592
|
+
properties: {
|
|
593
|
+
query: { type: "string" },
|
|
594
|
+
numResults: { type: "integer", minimum: 1, maximum: 10, default: 5 }
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
name: "settld.weather_current_paid",
|
|
600
|
+
description: "Fetch paid current weather through the x402 gateway with transparent Settld autopay.",
|
|
601
|
+
inputSchema: {
|
|
602
|
+
type: "object",
|
|
603
|
+
additionalProperties: false,
|
|
604
|
+
required: ["city"],
|
|
605
|
+
properties: {
|
|
606
|
+
city: { type: "string" },
|
|
607
|
+
unit: { type: "string", enum: ["c", "f"], default: "c" }
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
name: "settld.llm_completion_paid",
|
|
613
|
+
description: "Execute a paid LLM completion through the x402 gateway with transparent Settld autopay.",
|
|
614
|
+
inputSchema: {
|
|
615
|
+
type: "object",
|
|
616
|
+
additionalProperties: false,
|
|
617
|
+
required: ["prompt"],
|
|
618
|
+
properties: {
|
|
619
|
+
prompt: { type: "string" },
|
|
620
|
+
model: { type: "string", default: "gpt-4o-mini" },
|
|
621
|
+
maxTokens: { type: "integer", minimum: 1, maximum: 512, default: 128 }
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
name: "settld.resolve_settlement",
|
|
627
|
+
description:
|
|
628
|
+
"Manually resolve a run settlement (released/refunded). Useful for demos where the policy engine would otherwise leave the settlement locked.",
|
|
629
|
+
inputSchema: {
|
|
630
|
+
type: "object",
|
|
631
|
+
additionalProperties: false,
|
|
632
|
+
required: ["runId"],
|
|
633
|
+
properties: {
|
|
634
|
+
runId: { type: "string" },
|
|
635
|
+
status: { type: "string", enum: ["released", "refunded"], default: "released" },
|
|
636
|
+
releaseRatePct: { type: ["integer", "null"], minimum: 0, maximum: 100, default: null },
|
|
637
|
+
releasedAmountCents: { type: ["integer", "null"], minimum: 0, default: null },
|
|
638
|
+
refundedAmountCents: { type: ["integer", "null"], minimum: 0, default: null },
|
|
639
|
+
reason: { type: ["string", "null"], default: null },
|
|
640
|
+
resolvedByAgentId: { type: ["string", "null"], default: null }
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
name: "settld.open_dispute",
|
|
646
|
+
description: "Open a dispute on a resolved run settlement (requires dispute window still open).",
|
|
647
|
+
inputSchema: {
|
|
648
|
+
type: "object",
|
|
649
|
+
additionalProperties: false,
|
|
650
|
+
required: ["runId"],
|
|
651
|
+
properties: {
|
|
652
|
+
runId: { type: "string" },
|
|
653
|
+
reason: { type: ["string", "null"], default: null },
|
|
654
|
+
evidenceRefs: { type: "array", items: { type: "string" }, default: [] },
|
|
655
|
+
waitMs: {
|
|
656
|
+
type: "integer",
|
|
657
|
+
minimum: 0,
|
|
658
|
+
default: 0,
|
|
659
|
+
description: "Optional: wait up to this many milliseconds for settlement resolution before opening the dispute."
|
|
660
|
+
},
|
|
661
|
+
disputeType: { type: ["string", "null"], default: null },
|
|
662
|
+
priority: { type: ["string", "null"], default: null },
|
|
663
|
+
channel: { type: ["string", "null"], default: null }
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
name: "settld.dispute_add_evidence",
|
|
669
|
+
description: "Attach dispute evidence to an open dispute.",
|
|
670
|
+
inputSchema: {
|
|
671
|
+
type: "object",
|
|
672
|
+
additionalProperties: false,
|
|
673
|
+
required: ["runId", "evidenceRef"],
|
|
674
|
+
properties: {
|
|
675
|
+
runId: { type: "string" },
|
|
676
|
+
disputeId: { type: ["string", "null"], default: null },
|
|
677
|
+
evidenceRef: { type: "string" },
|
|
678
|
+
submittedByAgentId: { type: ["string", "null"], default: null },
|
|
679
|
+
reason: { type: ["string", "null"], default: null },
|
|
680
|
+
idempotencyKey: { type: ["string", "null"], default: null }
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
name: "settld.dispute_escalate",
|
|
686
|
+
description: "Escalate an open dispute to a higher level (counterparty/arbiter/external).",
|
|
687
|
+
inputSchema: {
|
|
688
|
+
type: "object",
|
|
689
|
+
additionalProperties: false,
|
|
690
|
+
required: ["runId", "escalationLevel"],
|
|
691
|
+
properties: {
|
|
692
|
+
runId: { type: "string" },
|
|
693
|
+
disputeId: { type: ["string", "null"], default: null },
|
|
694
|
+
escalationLevel: { type: "string", enum: ["l1_counterparty", "l2_arbiter", "l3_external"] },
|
|
695
|
+
channel: { type: ["string", "null"], default: null },
|
|
696
|
+
escalatedByAgentId: { type: ["string", "null"], default: null },
|
|
697
|
+
reason: { type: ["string", "null"], default: null },
|
|
698
|
+
idempotencyKey: { type: ["string", "null"], default: null }
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
name: "settld.dispute_close",
|
|
704
|
+
description: "Close an open dispute with optional signed verdict material.",
|
|
705
|
+
inputSchema: {
|
|
706
|
+
type: "object",
|
|
707
|
+
additionalProperties: false,
|
|
708
|
+
required: ["runId"],
|
|
709
|
+
properties: {
|
|
710
|
+
runId: { type: "string" },
|
|
711
|
+
disputeId: { type: ["string", "null"], default: null },
|
|
712
|
+
resolution: { type: ["object", "null"], additionalProperties: true, default: null },
|
|
713
|
+
verdict: { type: ["object", "null"], additionalProperties: true, default: null },
|
|
714
|
+
arbitrationVerdict: { type: ["object", "null"], additionalProperties: true, default: null },
|
|
715
|
+
idempotencyKey: { type: ["string", "null"], default: null }
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
name: "settld.arbitration_open",
|
|
721
|
+
description: "Open an arbitration case for an already-open dispute.",
|
|
722
|
+
inputSchema: {
|
|
723
|
+
type: "object",
|
|
724
|
+
additionalProperties: false,
|
|
725
|
+
required: ["runId"],
|
|
726
|
+
properties: {
|
|
727
|
+
runId: { type: "string" },
|
|
728
|
+
caseId: { type: ["string", "null"], default: null },
|
|
729
|
+
disputeId: { type: ["string", "null"], default: null },
|
|
730
|
+
arbiterAgentId: { type: ["string", "null"], default: null },
|
|
731
|
+
panelCandidateAgentIds: { type: ["array", "null"], items: { type: "string" }, default: null },
|
|
732
|
+
evidenceRefs: { type: ["array", "null"], items: { type: "string" }, default: null },
|
|
733
|
+
summary: { type: ["string", "null"], default: null },
|
|
734
|
+
idempotencyKey: { type: ["string", "null"], default: null }
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
name: "settld.arbitration_issue_verdict",
|
|
740
|
+
description: "Issue a signed arbitration verdict for a case.",
|
|
741
|
+
inputSchema: {
|
|
742
|
+
type: "object",
|
|
743
|
+
additionalProperties: false,
|
|
744
|
+
required: ["runId", "caseId", "arbitrationVerdict"],
|
|
745
|
+
properties: {
|
|
746
|
+
runId: { type: "string" },
|
|
747
|
+
caseId: { type: "string" },
|
|
748
|
+
arbitrationVerdict: { type: "object", additionalProperties: true },
|
|
749
|
+
idempotencyKey: { type: ["string", "null"], default: null }
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
name: "settld.agreement_delegation_create",
|
|
755
|
+
description: "Create an AgreementDelegation.v1 edge (idempotent via idempotencyKey).",
|
|
756
|
+
inputSchema: {
|
|
757
|
+
type: "object",
|
|
758
|
+
additionalProperties: false,
|
|
759
|
+
required: ["parentAgreementHash", "childAgreementHash", "delegatorAgentId", "delegateeAgentId", "budgetCapCents"],
|
|
760
|
+
properties: {
|
|
761
|
+
parentAgreementHash: { type: "string" },
|
|
762
|
+
childAgreementHash: { type: "string" },
|
|
763
|
+
delegatorAgentId: { type: "string" },
|
|
764
|
+
delegateeAgentId: { type: "string" },
|
|
765
|
+
budgetCapCents: { type: "integer", minimum: 1 },
|
|
766
|
+
currency: { type: "string", default: "USD" },
|
|
767
|
+
delegationDepth: { type: ["integer", "null"], minimum: 1, default: null },
|
|
768
|
+
maxDelegationDepth: { type: ["integer", "null"], minimum: 1, default: null },
|
|
769
|
+
ancestorChain: { type: ["array", "null"], items: { type: "string" }, default: null },
|
|
770
|
+
delegationId: { type: ["string", "null"], default: null },
|
|
771
|
+
idempotencyKey: { type: ["string", "null"], default: null }
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
name: "settld.agreement_delegation_list",
|
|
777
|
+
description: "List agreement delegations touching an agreement hash.",
|
|
778
|
+
inputSchema: {
|
|
779
|
+
type: "object",
|
|
780
|
+
additionalProperties: false,
|
|
781
|
+
required: ["agreementHash"],
|
|
782
|
+
properties: {
|
|
783
|
+
agreementHash: { type: "string" },
|
|
784
|
+
status: { type: ["string", "null"], default: null },
|
|
785
|
+
limit: { type: ["integer", "null"], minimum: 1, default: null },
|
|
786
|
+
offset: { type: ["integer", "null"], minimum: 0, default: null }
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
name: "settld.x402_gate_create",
|
|
792
|
+
description: "Create an x402 gate (idempotent via idempotencyKey). Generates payer/payee agent ids when omitted.",
|
|
793
|
+
inputSchema: {
|
|
794
|
+
type: "object",
|
|
795
|
+
additionalProperties: false,
|
|
796
|
+
properties: {
|
|
797
|
+
gateId: { type: ["string", "null"], default: null },
|
|
798
|
+
payerAgentId: { type: ["string", "null"], default: null },
|
|
799
|
+
payeeAgentId: { type: ["string", "null"], default: null },
|
|
800
|
+
amountCents: { type: "integer", minimum: 1, default: 500 },
|
|
801
|
+
currency: { type: "string", default: "USD" },
|
|
802
|
+
autoFundPayerCents: { type: ["integer", "null"], minimum: 0, default: null },
|
|
803
|
+
toolId: { type: ["string", "null"], default: null },
|
|
804
|
+
idempotencyKey: { type: ["string", "null"], default: null }
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
name: "settld.x402_gate_verify",
|
|
810
|
+
description:
|
|
811
|
+
"Verify and resolve an x402 gate. By default it first authorizes payment so MCP probe can run create -> verify -> get.",
|
|
812
|
+
inputSchema: {
|
|
813
|
+
type: "object",
|
|
814
|
+
additionalProperties: false,
|
|
815
|
+
required: ["gateId"],
|
|
816
|
+
properties: {
|
|
817
|
+
gateId: { type: "string" },
|
|
818
|
+
ensureAuthorized: { type: "boolean", default: true },
|
|
819
|
+
authorizeIdempotencyKey: { type: ["string", "null"], default: null },
|
|
820
|
+
idempotencyKey: { type: ["string", "null"], default: null },
|
|
821
|
+
verificationStatus: { type: "string", enum: ["green", "amber", "red"], default: "green" },
|
|
822
|
+
runStatus: { type: "string", enum: ["completed", "failed"], default: "completed" },
|
|
823
|
+
requestSha256: { type: ["string", "null"], default: null },
|
|
824
|
+
responseSha256: { type: ["string", "null"], default: null }
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
name: "settld.x402_gate_get",
|
|
830
|
+
description: "Fetch the current x402 gate record and linked settlement state.",
|
|
831
|
+
inputSchema: {
|
|
832
|
+
type: "object",
|
|
833
|
+
additionalProperties: false,
|
|
834
|
+
required: ["gateId"],
|
|
835
|
+
properties: {
|
|
836
|
+
gateId: { type: "string" }
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
name: "settld.about",
|
|
842
|
+
description: "Return MCP server configuration (redacted).",
|
|
843
|
+
inputSchema: {
|
|
844
|
+
type: "object",
|
|
845
|
+
additionalProperties: false,
|
|
846
|
+
properties: {}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
];
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
async function main() {
|
|
853
|
+
const baseUrl = process.env.SETTLD_BASE_URL || "http://127.0.0.1:3000";
|
|
854
|
+
const tenantId = process.env.SETTLD_TENANT_ID || "tenant_default";
|
|
855
|
+
const apiKey = process.env.SETTLD_API_KEY || "";
|
|
856
|
+
const protocol = process.env.SETTLD_PROTOCOL || null;
|
|
857
|
+
const paidToolsBaseUrl = process.env.SETTLD_PAID_TOOLS_BASE_URL || "http://127.0.0.1:8402";
|
|
858
|
+
const paidToolsAgentPassport = parseOptionalJsonObject(
|
|
859
|
+
process.env.SETTLD_PAID_TOOLS_AGENT_PASSPORT ?? null,
|
|
860
|
+
"SETTLD_PAID_TOOLS_AGENT_PASSPORT"
|
|
861
|
+
);
|
|
862
|
+
|
|
863
|
+
assertNonEmptyString(baseUrl, "SETTLD_BASE_URL");
|
|
864
|
+
assertNonEmptyString(tenantId, "SETTLD_TENANT_ID");
|
|
865
|
+
assertNonEmptyString(apiKey, "SETTLD_API_KEY");
|
|
866
|
+
|
|
867
|
+
// Operational hint: this server speaks JSON-RPC over stdin/stdout (MCP stdio transport).
|
|
868
|
+
// Keep stdout strictly for JSON-RPC messages; print hints to stderr only.
|
|
869
|
+
process.stderr.write("[mcp] ready (stdio). Use `npm run mcp:probe` or an MCP client; do not paste shell prompts.\n");
|
|
870
|
+
|
|
871
|
+
const client = makeSettldClient({ baseUrl, tenantId, apiKey, protocol });
|
|
872
|
+
const paidToolsClient = makePaidToolsClient({ baseUrl: paidToolsBaseUrl, tenantId, agentPassport: paidToolsAgentPassport });
|
|
873
|
+
const tools = buildTools();
|
|
874
|
+
const toolByName = new Map(tools.map((t) => [t.name, t]));
|
|
875
|
+
|
|
876
|
+
const stream = new StdioJsonRpcStream({ input: process.stdin, output: process.stdout });
|
|
877
|
+
stream.onError((err) => {
|
|
878
|
+
// Never write logs to stdout (reserved for JSON-RPC). stderr only.
|
|
879
|
+
process.stderr.write(`[mcp] stream error: ${err?.message ?? err}\n`);
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
let negotiatedProtocolVersion = null;
|
|
883
|
+
|
|
884
|
+
stream.onMessage(async (text) => {
|
|
885
|
+
const parsed = safeJsonParse(text);
|
|
886
|
+
if (!parsed.ok) {
|
|
887
|
+
// Invalid JSON from client: ignore (spike) but report on stderr.
|
|
888
|
+
process.stderr.write(`[mcp] invalid json: ${String(parsed.error?.message ?? parsed.error)}\n`);
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
const msg = parsed.value;
|
|
892
|
+
const id = msg?.id;
|
|
893
|
+
const method = msg?.method;
|
|
894
|
+
const isNotification = id === undefined || id === null;
|
|
895
|
+
|
|
896
|
+
if (typeof method !== "string" || method.trim() === "") {
|
|
897
|
+
if (!isNotification) {
|
|
898
|
+
stream.send({ jsonrpc: "2.0", id, error: jsonRpcError(-32600, "Invalid Request") });
|
|
899
|
+
}
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
try {
|
|
904
|
+
if (method === "initialize") {
|
|
905
|
+
const pv = msg?.params?.protocolVersion ? String(msg.params.protocolVersion) : null;
|
|
906
|
+
negotiatedProtocolVersion = pv || negotiatedProtocolVersion || "2024-11-05";
|
|
907
|
+
if (!isNotification) {
|
|
908
|
+
stream.send({
|
|
909
|
+
jsonrpc: "2.0",
|
|
910
|
+
id,
|
|
911
|
+
result: {
|
|
912
|
+
protocolVersion: negotiatedProtocolVersion,
|
|
913
|
+
serverInfo: { name: "settld-mcp-spike", version: "s23" },
|
|
914
|
+
capabilities: { tools: {} }
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
if (method === "ping") {
|
|
922
|
+
if (!isNotification) stream.send({ jsonrpc: "2.0", id, result: {} });
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
if (method === "initialized" || method === "notifications/initialized") {
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
if (method === "tools/list") {
|
|
931
|
+
if (!isNotification) stream.send({ jsonrpc: "2.0", id, result: { tools } });
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
if (method === "tools/call") {
|
|
936
|
+
const name = msg?.params?.name ? String(msg.params.name) : "";
|
|
937
|
+
const args = msg?.params?.arguments && typeof msg.params.arguments === "object" ? msg.params.arguments : {};
|
|
938
|
+
if (!toolByName.has(name)) {
|
|
939
|
+
if (!isNotification) stream.send({ jsonrpc: "2.0", id, result: { content: [contentText(`unknown tool: ${name}`)], isError: true } });
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
const started = nowMs();
|
|
943
|
+
let result = null;
|
|
944
|
+
try {
|
|
945
|
+
if (name === "settld.about") {
|
|
946
|
+
const discovered = await client.discoverProtocol();
|
|
947
|
+
result = {
|
|
948
|
+
ok: true,
|
|
949
|
+
server: { name: "settld-mcp-spike", version: "s23" },
|
|
950
|
+
config: redactSecrets({ baseUrl, tenantId, protocol: discovered, paidToolsBaseUrl })
|
|
951
|
+
};
|
|
952
|
+
} else if (name === "settld.exa_search_paid") {
|
|
953
|
+
const query = String(args?.query ?? "").trim();
|
|
954
|
+
assertNonEmptyString(query, "query");
|
|
955
|
+
const numResults = args?.numResults ?? 5;
|
|
956
|
+
result = await paidToolsClient.exaSearch({ query, numResults });
|
|
957
|
+
} else if (name === "settld.weather_current_paid") {
|
|
958
|
+
const city = String(args?.city ?? "").trim();
|
|
959
|
+
assertNonEmptyString(city, "city");
|
|
960
|
+
const unit = args?.unit ?? "c";
|
|
961
|
+
result = await paidToolsClient.weatherCurrent({ city, unit });
|
|
962
|
+
} else if (name === "settld.llm_completion_paid") {
|
|
963
|
+
const prompt = String(args?.prompt ?? "").trim();
|
|
964
|
+
assertNonEmptyString(prompt, "prompt");
|
|
965
|
+
const model = args?.model ?? "gpt-4o-mini";
|
|
966
|
+
const maxTokens = args?.maxTokens ?? 128;
|
|
967
|
+
result = await paidToolsClient.llmCompletion({ prompt, model, maxTokens });
|
|
968
|
+
} else if (name === "settld.create_agreement") {
|
|
969
|
+
const amountCents = Number(args?.amountCents ?? 500);
|
|
970
|
+
if (!Number.isSafeInteger(amountCents) || amountCents <= 0) throw new TypeError("amountCents must be a positive safe integer");
|
|
971
|
+
const currency = args?.currency ? String(args.currency).trim().toUpperCase() : "USD";
|
|
972
|
+
const title = args?.title ? String(args.title).trim() : "MCP spike agreement";
|
|
973
|
+
const description = args?.description === undefined ? null : args.description === null ? null : String(args.description);
|
|
974
|
+
const capability = args?.capability ? String(args.capability).trim() : "agent-task:demo";
|
|
975
|
+
const disputeWindowDaysRaw = args?.disputeWindowDays ?? 7;
|
|
976
|
+
const disputeWindowDays = Number(disputeWindowDaysRaw);
|
|
977
|
+
if (!Number.isSafeInteger(disputeWindowDays) || disputeWindowDays < 0) {
|
|
978
|
+
throw new TypeError("disputeWindowDays must be a non-negative safe integer");
|
|
979
|
+
}
|
|
980
|
+
const payerDisplayName = args?.payerDisplayName ? String(args.payerDisplayName).trim() : "MCP Payer";
|
|
981
|
+
const payeeDisplayName = args?.payeeDisplayName ? String(args.payeeDisplayName).trim() : "MCP Payee";
|
|
982
|
+
|
|
983
|
+
// Register payer + payee agents.
|
|
984
|
+
const payerPublicKeyPem = generateEd25519PublicKeyPem();
|
|
985
|
+
const payeePublicKeyPem = generateEd25519PublicKeyPem();
|
|
986
|
+
const payer = await client.requestJson("/agents/register", {
|
|
987
|
+
method: "POST",
|
|
988
|
+
body: { publicKeyPem: payerPublicKeyPem, displayName: payerDisplayName },
|
|
989
|
+
idem: makeIdempotencyKey("mcp_agents_register_payer")
|
|
990
|
+
});
|
|
991
|
+
const payee = await client.requestJson("/agents/register", {
|
|
992
|
+
method: "POST",
|
|
993
|
+
body: { publicKeyPem: payeePublicKeyPem, displayName: payeeDisplayName },
|
|
994
|
+
idem: makeIdempotencyKey("mcp_agents_register_payee")
|
|
995
|
+
});
|
|
996
|
+
const payerAgentId = payer?.agentIdentity?.agentId;
|
|
997
|
+
const payeeAgentId = payee?.agentIdentity?.agentId;
|
|
998
|
+
assertNonEmptyString(payerAgentId, "payerAgentId");
|
|
999
|
+
assertNonEmptyString(payeeAgentId, "payeeAgentId");
|
|
1000
|
+
|
|
1001
|
+
// Fund payer so escrow can lock later.
|
|
1002
|
+
await client.requestJson(`/agents/${encodeURIComponent(payerAgentId)}/wallet/credit`, {
|
|
1003
|
+
method: "POST",
|
|
1004
|
+
write: true,
|
|
1005
|
+
body: { amountCents: amountCents * 2, currency },
|
|
1006
|
+
idem: makeIdempotencyKey("mcp_wallet_credit")
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
// Create RFQ.
|
|
1010
|
+
const rfqOut = await client.requestJson("/marketplace/rfqs", {
|
|
1011
|
+
method: "POST",
|
|
1012
|
+
body: {
|
|
1013
|
+
title,
|
|
1014
|
+
description,
|
|
1015
|
+
capability,
|
|
1016
|
+
posterAgentId: payerAgentId,
|
|
1017
|
+
budgetCents: amountCents,
|
|
1018
|
+
currency,
|
|
1019
|
+
metadata: { source: "mcp_spike" }
|
|
1020
|
+
},
|
|
1021
|
+
idem: makeIdempotencyKey("mcp_rfq")
|
|
1022
|
+
});
|
|
1023
|
+
const rfqId = rfqOut?.rfq?.rfqId;
|
|
1024
|
+
assertNonEmptyString(rfqId, "rfqId");
|
|
1025
|
+
|
|
1026
|
+
// Submit bid.
|
|
1027
|
+
const bidOut = await client.requestJson(`/marketplace/rfqs/${encodeURIComponent(rfqId)}/bids`, {
|
|
1028
|
+
method: "POST",
|
|
1029
|
+
body: {
|
|
1030
|
+
bidderAgentId: payeeAgentId,
|
|
1031
|
+
amountCents,
|
|
1032
|
+
currency,
|
|
1033
|
+
etaSeconds: 60,
|
|
1034
|
+
note: "MCP spike bid"
|
|
1035
|
+
},
|
|
1036
|
+
idem: makeIdempotencyKey("mcp_bid")
|
|
1037
|
+
});
|
|
1038
|
+
const bidId = bidOut?.bid?.bidId;
|
|
1039
|
+
assertNonEmptyString(bidId, "bidId");
|
|
1040
|
+
|
|
1041
|
+
// Accept bid => creates run + settlement + agreement.
|
|
1042
|
+
const acceptOut = await client.requestJson(`/marketplace/rfqs/${encodeURIComponent(rfqId)}/accept`, {
|
|
1043
|
+
method: "POST",
|
|
1044
|
+
body: {
|
|
1045
|
+
bidId,
|
|
1046
|
+
payerAgentId,
|
|
1047
|
+
disputeWindowDays,
|
|
1048
|
+
settlement: { payerAgentId }
|
|
1049
|
+
},
|
|
1050
|
+
idem: makeIdempotencyKey("mcp_accept")
|
|
1051
|
+
});
|
|
1052
|
+
const runId = acceptOut?.run?.runId ?? acceptOut?.run?.id ?? acceptOut?.run?.runId;
|
|
1053
|
+
const settlementId = acceptOut?.settlement?.settlementId ?? null;
|
|
1054
|
+
const agreementId = acceptOut?.agreement?.agreementId ?? acceptOut?.agreement?.id ?? null;
|
|
1055
|
+
|
|
1056
|
+
result = {
|
|
1057
|
+
ok: true,
|
|
1058
|
+
payerAgentId,
|
|
1059
|
+
payeeAgentId,
|
|
1060
|
+
rfqId,
|
|
1061
|
+
bidId,
|
|
1062
|
+
runId,
|
|
1063
|
+
settlementId,
|
|
1064
|
+
agreementId,
|
|
1065
|
+
raw: redactSecrets(acceptOut)
|
|
1066
|
+
};
|
|
1067
|
+
} else if (name === "settld.x402_gate_create") {
|
|
1068
|
+
const gateId = args?.gateId ? String(args.gateId).trim() : `x402gate_mcp_${crypto.randomUUID()}`;
|
|
1069
|
+
const payerAgentId = args?.payerAgentId ? String(args.payerAgentId).trim() : `agt_x402_payer_${crypto.randomUUID()}`;
|
|
1070
|
+
const payeeAgentId = args?.payeeAgentId ? String(args.payeeAgentId).trim() : `agt_x402_payee_${crypto.randomUUID()}`;
|
|
1071
|
+
assertNonEmptyString(gateId, "gateId");
|
|
1072
|
+
assertNonEmptyString(payerAgentId, "payerAgentId");
|
|
1073
|
+
assertNonEmptyString(payeeAgentId, "payeeAgentId");
|
|
1074
|
+
if (payerAgentId === payeeAgentId) throw new TypeError("payerAgentId and payeeAgentId must differ");
|
|
1075
|
+
|
|
1076
|
+
const amountCents = Number(args?.amountCents ?? 500);
|
|
1077
|
+
if (!Number.isSafeInteger(amountCents) || amountCents <= 0) throw new TypeError("amountCents must be a positive safe integer");
|
|
1078
|
+
const currency = args?.currency ? String(args.currency).trim().toUpperCase() : "USD";
|
|
1079
|
+
assertNonEmptyString(currency, "currency");
|
|
1080
|
+
|
|
1081
|
+
const autoFundRaw = args?.autoFundPayerCents ?? amountCents;
|
|
1082
|
+
const autoFundPayerCents = Number(autoFundRaw);
|
|
1083
|
+
if (!Number.isSafeInteger(autoFundPayerCents) || autoFundPayerCents < 0) {
|
|
1084
|
+
throw new TypeError("autoFundPayerCents must be a non-negative safe integer");
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
const idempotencyKey =
|
|
1088
|
+
typeof args?.idempotencyKey === "string" && args.idempotencyKey.trim() !== ""
|
|
1089
|
+
? args.idempotencyKey.trim()
|
|
1090
|
+
: makeIdempotencyKey("mcp_x402_gate_create");
|
|
1091
|
+
|
|
1092
|
+
const body = {
|
|
1093
|
+
gateId,
|
|
1094
|
+
payerAgentId,
|
|
1095
|
+
payeeAgentId,
|
|
1096
|
+
amountCents,
|
|
1097
|
+
currency,
|
|
1098
|
+
autoFundPayerCents
|
|
1099
|
+
};
|
|
1100
|
+
if (typeof args?.toolId === "string" && args.toolId.trim() !== "") body.toolId = args.toolId.trim();
|
|
1101
|
+
const out = await client.requestJson("/x402/gate/create", {
|
|
1102
|
+
method: "POST",
|
|
1103
|
+
write: true,
|
|
1104
|
+
body,
|
|
1105
|
+
idem: idempotencyKey
|
|
1106
|
+
});
|
|
1107
|
+
result = {
|
|
1108
|
+
ok: true,
|
|
1109
|
+
gateId,
|
|
1110
|
+
payerAgentId,
|
|
1111
|
+
payeeAgentId,
|
|
1112
|
+
idempotencyKey,
|
|
1113
|
+
...redactSecrets(out)
|
|
1114
|
+
};
|
|
1115
|
+
} else if (name === "settld.agreement_delegation_create") {
|
|
1116
|
+
const parentAgreementHash = String(args?.parentAgreementHash ?? "").trim().toLowerCase();
|
|
1117
|
+
const childAgreementHash = String(args?.childAgreementHash ?? "").trim().toLowerCase();
|
|
1118
|
+
if (!isSha256Hex(parentAgreementHash)) throw new TypeError("parentAgreementHash must be a sha256 hex string");
|
|
1119
|
+
if (!isSha256Hex(childAgreementHash)) throw new TypeError("childAgreementHash must be a sha256 hex string");
|
|
1120
|
+
const delegatorAgentId = String(args?.delegatorAgentId ?? "").trim();
|
|
1121
|
+
const delegateeAgentId = String(args?.delegateeAgentId ?? "").trim();
|
|
1122
|
+
assertNonEmptyString(delegatorAgentId, "delegatorAgentId");
|
|
1123
|
+
assertNonEmptyString(delegateeAgentId, "delegateeAgentId");
|
|
1124
|
+
const budgetCapCents = Number(args?.budgetCapCents);
|
|
1125
|
+
if (!Number.isSafeInteger(budgetCapCents) || budgetCapCents <= 0) {
|
|
1126
|
+
throw new TypeError("budgetCapCents must be a positive safe integer");
|
|
1127
|
+
}
|
|
1128
|
+
const currency = args?.currency ? String(args.currency).trim().toUpperCase() : "USD";
|
|
1129
|
+
assertNonEmptyString(currency, "currency");
|
|
1130
|
+
|
|
1131
|
+
const idempotencyKey =
|
|
1132
|
+
typeof args?.idempotencyKey === "string" && args.idempotencyKey.trim() !== ""
|
|
1133
|
+
? args.idempotencyKey.trim()
|
|
1134
|
+
: makeIdempotencyKey("mcp_agreement_delegation_create");
|
|
1135
|
+
|
|
1136
|
+
const body = {
|
|
1137
|
+
childAgreementHash,
|
|
1138
|
+
delegatorAgentId,
|
|
1139
|
+
delegateeAgentId,
|
|
1140
|
+
budgetCapCents,
|
|
1141
|
+
currency
|
|
1142
|
+
};
|
|
1143
|
+
if (typeof args?.delegationId === "string" && args.delegationId.trim() !== "") body.delegationId = args.delegationId.trim();
|
|
1144
|
+
if (Number.isSafeInteger(Number(args?.delegationDepth)) && Number(args.delegationDepth) > 0) {
|
|
1145
|
+
body.delegationDepth = Number(args.delegationDepth);
|
|
1146
|
+
}
|
|
1147
|
+
if (Number.isSafeInteger(Number(args?.maxDelegationDepth)) && Number(args.maxDelegationDepth) > 0) {
|
|
1148
|
+
body.maxDelegationDepth = Number(args.maxDelegationDepth);
|
|
1149
|
+
}
|
|
1150
|
+
if (Array.isArray(args?.ancestorChain)) {
|
|
1151
|
+
body.ancestorChain = args.ancestorChain.map((v) => String(v ?? "").trim()).filter(Boolean);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
const out = await client.requestJson(`/agreements/${encodeURIComponent(parentAgreementHash)}/delegations`, {
|
|
1155
|
+
method: "POST",
|
|
1156
|
+
write: true,
|
|
1157
|
+
body,
|
|
1158
|
+
idem: idempotencyKey
|
|
1159
|
+
});
|
|
1160
|
+
result = {
|
|
1161
|
+
ok: true,
|
|
1162
|
+
parentAgreementHash,
|
|
1163
|
+
idempotencyKey,
|
|
1164
|
+
...redactSecrets(out)
|
|
1165
|
+
};
|
|
1166
|
+
} else if (name === "settld.agreement_delegation_list") {
|
|
1167
|
+
const agreementHash = String(args?.agreementHash ?? "").trim().toLowerCase();
|
|
1168
|
+
if (!isSha256Hex(agreementHash)) throw new TypeError("agreementHash must be a sha256 hex string");
|
|
1169
|
+
const query = new URLSearchParams();
|
|
1170
|
+
if (typeof args?.status === "string" && args.status.trim() !== "") query.set("status", args.status.trim());
|
|
1171
|
+
if (Number.isSafeInteger(Number(args?.limit)) && Number(args.limit) > 0) query.set("limit", String(Number(args.limit)));
|
|
1172
|
+
if (Number.isSafeInteger(Number(args?.offset)) && Number(args.offset) >= 0) query.set("offset", String(Number(args.offset)));
|
|
1173
|
+
const path = `/agreements/${encodeURIComponent(agreementHash)}/delegations${query.toString() ? `?${query.toString()}` : ""}`;
|
|
1174
|
+
const out = await client.requestJson(path, { method: "GET" });
|
|
1175
|
+
result = { ok: true, agreementHash, ...redactSecrets(out) };
|
|
1176
|
+
} else if (name === "settld.x402_gate_verify") {
|
|
1177
|
+
const gateId = String(args?.gateId ?? "").trim();
|
|
1178
|
+
assertNonEmptyString(gateId, "gateId");
|
|
1179
|
+
const ensureAuthorized = args?.ensureAuthorized !== false;
|
|
1180
|
+
|
|
1181
|
+
const verificationStatusRaw = args?.verificationStatus ? String(args.verificationStatus).trim().toLowerCase() : "green";
|
|
1182
|
+
if (!["green", "amber", "red"].includes(verificationStatusRaw)) {
|
|
1183
|
+
throw new TypeError("verificationStatus must be green|amber|red");
|
|
1184
|
+
}
|
|
1185
|
+
const verificationStatus = verificationStatusRaw;
|
|
1186
|
+
|
|
1187
|
+
const runStatusRaw = args?.runStatus ? String(args.runStatus).trim().toLowerCase() : "completed";
|
|
1188
|
+
if (!["completed", "failed"].includes(runStatusRaw)) {
|
|
1189
|
+
throw new TypeError("runStatus must be completed|failed");
|
|
1190
|
+
}
|
|
1191
|
+
const runStatus = runStatusRaw;
|
|
1192
|
+
|
|
1193
|
+
const requestSha256 = (() => {
|
|
1194
|
+
if (typeof args?.requestSha256 === "string" && args.requestSha256.trim() !== "") return args.requestSha256.trim().toLowerCase();
|
|
1195
|
+
return sha256HexText(`mcp:x402:request:${gateId}`).toLowerCase();
|
|
1196
|
+
})();
|
|
1197
|
+
if (!isSha256Hex(requestSha256)) throw new TypeError("requestSha256 must be a sha256 hex string");
|
|
1198
|
+
const responseSha256 = (() => {
|
|
1199
|
+
if (typeof args?.responseSha256 === "string" && args.responseSha256.trim() !== "") return args.responseSha256.trim().toLowerCase();
|
|
1200
|
+
return sha256HexText(`mcp:x402:response:${gateId}`).toLowerCase();
|
|
1201
|
+
})();
|
|
1202
|
+
if (!isSha256Hex(responseSha256)) throw new TypeError("responseSha256 must be a sha256 hex string");
|
|
1203
|
+
|
|
1204
|
+
const verifyIdempotencyKey =
|
|
1205
|
+
typeof args?.idempotencyKey === "string" && args.idempotencyKey.trim() !== ""
|
|
1206
|
+
? args.idempotencyKey.trim()
|
|
1207
|
+
: makeIdempotencyKey("mcp_x402_gate_verify");
|
|
1208
|
+
const authorizeIdempotencyKey =
|
|
1209
|
+
typeof args?.authorizeIdempotencyKey === "string" && args.authorizeIdempotencyKey.trim() !== ""
|
|
1210
|
+
? args.authorizeIdempotencyKey.trim()
|
|
1211
|
+
: makeIdempotencyKey("mcp_x402_gate_authorize");
|
|
1212
|
+
|
|
1213
|
+
let authorizeOut = null;
|
|
1214
|
+
if (ensureAuthorized) {
|
|
1215
|
+
authorizeOut = await client.requestJson("/x402/gate/authorize-payment", {
|
|
1216
|
+
method: "POST",
|
|
1217
|
+
write: true,
|
|
1218
|
+
body: { gateId },
|
|
1219
|
+
idem: authorizeIdempotencyKey
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
const verifyOut = await client.requestJson("/x402/gate/verify", {
|
|
1224
|
+
method: "POST",
|
|
1225
|
+
write: true,
|
|
1226
|
+
body: {
|
|
1227
|
+
gateId,
|
|
1228
|
+
verificationStatus,
|
|
1229
|
+
runStatus,
|
|
1230
|
+
policy: {
|
|
1231
|
+
mode: "automatic",
|
|
1232
|
+
rules: {
|
|
1233
|
+
autoReleaseOnGreen: true,
|
|
1234
|
+
greenReleaseRatePct: 100,
|
|
1235
|
+
autoReleaseOnAmber: false,
|
|
1236
|
+
amberReleaseRatePct: 0,
|
|
1237
|
+
autoReleaseOnRed: true,
|
|
1238
|
+
redReleaseRatePct: 0
|
|
1239
|
+
}
|
|
1240
|
+
},
|
|
1241
|
+
verificationMethod: { mode: "deterministic", source: "mcp_x402_gate_verify_v1" },
|
|
1242
|
+
evidenceRefs: [`http:request_sha256:${requestSha256}`, `http:response_sha256:${responseSha256}`]
|
|
1243
|
+
},
|
|
1244
|
+
idem: verifyIdempotencyKey
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
result = {
|
|
1248
|
+
ok: true,
|
|
1249
|
+
gateId,
|
|
1250
|
+
ensureAuthorized,
|
|
1251
|
+
authorizeIdempotencyKey: ensureAuthorized ? authorizeIdempotencyKey : null,
|
|
1252
|
+
idempotencyKey: verifyIdempotencyKey,
|
|
1253
|
+
requestSha256,
|
|
1254
|
+
responseSha256,
|
|
1255
|
+
authorize: authorizeOut ? redactSecrets(authorizeOut) : null,
|
|
1256
|
+
verify: redactSecrets(verifyOut)
|
|
1257
|
+
};
|
|
1258
|
+
} else if (name === "settld.x402_gate_get") {
|
|
1259
|
+
const gateId = String(args?.gateId ?? "").trim();
|
|
1260
|
+
assertNonEmptyString(gateId, "gateId");
|
|
1261
|
+
const out = await client.requestJson(`/x402/gate/${encodeURIComponent(gateId)}`, { method: "GET" });
|
|
1262
|
+
result = { ok: true, gateId, ...redactSecrets(out) };
|
|
1263
|
+
} else if (name === "settld.submit_evidence") {
|
|
1264
|
+
const agentId = String(args?.agentId ?? "").trim();
|
|
1265
|
+
const runId = String(args?.runId ?? "").trim();
|
|
1266
|
+
const evidenceRef = String(args?.evidenceRef ?? "").trim();
|
|
1267
|
+
assertNonEmptyString(agentId, "agentId");
|
|
1268
|
+
assertNonEmptyString(runId, "runId");
|
|
1269
|
+
assertNonEmptyString(evidenceRef, "evidenceRef");
|
|
1270
|
+
|
|
1271
|
+
const head = await client.getRunPrevChainHash({ agentId, runId });
|
|
1272
|
+
const out = await client.requestJson(`/agents/${encodeURIComponent(agentId)}/runs/${encodeURIComponent(runId)}/events`, {
|
|
1273
|
+
method: "POST",
|
|
1274
|
+
write: true,
|
|
1275
|
+
headers: { "x-proxy-expected-prev-chain-hash": head.prevChainHash === null ? "null" : String(head.prevChainHash) },
|
|
1276
|
+
// Note: event schemaVersion is an integer (default=1). Omit to use the default.
|
|
1277
|
+
body: { type: "EVIDENCE_ADDED", payload: { evidenceRef } },
|
|
1278
|
+
idem: makeIdempotencyKey("mcp_run_event_evidence")
|
|
1279
|
+
});
|
|
1280
|
+
result = { ok: true, ...redactSecrets(out) };
|
|
1281
|
+
} else if (name === "settld.settle_run") {
|
|
1282
|
+
const agentId = String(args?.agentId ?? "").trim();
|
|
1283
|
+
const runId = String(args?.runId ?? "").trim();
|
|
1284
|
+
assertNonEmptyString(agentId, "agentId");
|
|
1285
|
+
assertNonEmptyString(runId, "runId");
|
|
1286
|
+
|
|
1287
|
+
const outcome = args?.outcome ? String(args.outcome).trim().toLowerCase() : "completed";
|
|
1288
|
+
if (outcome !== "completed" && outcome !== "failed") throw new TypeError("outcome must be completed|failed");
|
|
1289
|
+
|
|
1290
|
+
const head = await client.getRunPrevChainHash({ agentId, runId });
|
|
1291
|
+
const body =
|
|
1292
|
+
outcome === "completed"
|
|
1293
|
+
? { type: "RUN_COMPLETED", payload: { outputRef: args?.outputRef ?? null, metrics: null } }
|
|
1294
|
+
: { type: "RUN_FAILED", payload: { code: args?.errorCode ?? null, message: args?.errorMessage ?? null } };
|
|
1295
|
+
|
|
1296
|
+
const out = await client.requestJson(`/agents/${encodeURIComponent(agentId)}/runs/${encodeURIComponent(runId)}/events`, {
|
|
1297
|
+
method: "POST",
|
|
1298
|
+
write: true,
|
|
1299
|
+
headers: { "x-proxy-expected-prev-chain-hash": head.prevChainHash === null ? "null" : String(head.prevChainHash) },
|
|
1300
|
+
body,
|
|
1301
|
+
idem: makeIdempotencyKey("mcp_run_event_terminal")
|
|
1302
|
+
});
|
|
1303
|
+
let settlement = null;
|
|
1304
|
+
try {
|
|
1305
|
+
settlement = await client.requestJson(`/runs/${encodeURIComponent(runId)}/settlement`, { method: "GET" });
|
|
1306
|
+
} catch {
|
|
1307
|
+
settlement = null;
|
|
1308
|
+
}
|
|
1309
|
+
result = { ok: true, ...redactSecrets(out), settlement };
|
|
1310
|
+
} else if (name === "settld.open_dispute") {
|
|
1311
|
+
const runId = String(args?.runId ?? "").trim();
|
|
1312
|
+
assertNonEmptyString(runId, "runId");
|
|
1313
|
+
const evidenceRefs = Array.isArray(args?.evidenceRefs) ? args.evidenceRefs.map((v) => String(v ?? "").trim()).filter(Boolean) : [];
|
|
1314
|
+
|
|
1315
|
+
const waitMsRaw = args?.waitMs ?? 0;
|
|
1316
|
+
const waitMs = Number(waitMsRaw);
|
|
1317
|
+
if (!Number.isSafeInteger(waitMs) || waitMs < 0) throw new TypeError("waitMs must be a non-negative safe integer");
|
|
1318
|
+
const deadline = nowMs() + Math.min(waitMs, 60_000);
|
|
1319
|
+
|
|
1320
|
+
if (waitMs > 0) {
|
|
1321
|
+
// Wait for settlement resolution so `open` doesn't immediately 409 on interactive demos.
|
|
1322
|
+
// (The API requires settlement.status != locked to open a dispute.)
|
|
1323
|
+
// eslint-disable-next-line no-constant-condition
|
|
1324
|
+
while (true) {
|
|
1325
|
+
let settlement = null;
|
|
1326
|
+
try {
|
|
1327
|
+
settlement = await client.requestJson(`/runs/${encodeURIComponent(runId)}/settlement`, { method: "GET" });
|
|
1328
|
+
} catch {
|
|
1329
|
+
settlement = null;
|
|
1330
|
+
}
|
|
1331
|
+
const status = String(settlement?.status ?? "").toLowerCase();
|
|
1332
|
+
if (status && status !== "locked") break;
|
|
1333
|
+
if (nowMs() >= deadline) break;
|
|
1334
|
+
await sleep(500);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
const out = await client.requestJson(`/runs/${encodeURIComponent(runId)}/dispute/open`, {
|
|
1338
|
+
method: "POST",
|
|
1339
|
+
write: true,
|
|
1340
|
+
body: {
|
|
1341
|
+
reason: args?.reason ?? null,
|
|
1342
|
+
evidenceRefs,
|
|
1343
|
+
disputeType: args?.disputeType ?? null,
|
|
1344
|
+
disputePriority: args?.priority ?? null,
|
|
1345
|
+
disputeChannel: args?.channel ?? null
|
|
1346
|
+
},
|
|
1347
|
+
idem: makeIdempotencyKey("mcp_dispute_open")
|
|
1348
|
+
});
|
|
1349
|
+
result = { ok: true, ...redactSecrets(out) };
|
|
1350
|
+
} else if (name === "settld.dispute_add_evidence") {
|
|
1351
|
+
const runId = String(args?.runId ?? "").trim();
|
|
1352
|
+
const evidenceRef = String(args?.evidenceRef ?? "").trim();
|
|
1353
|
+
assertNonEmptyString(runId, "runId");
|
|
1354
|
+
assertNonEmptyString(evidenceRef, "evidenceRef");
|
|
1355
|
+
const idempotencyKey =
|
|
1356
|
+
typeof args?.idempotencyKey === "string" && args.idempotencyKey.trim() !== ""
|
|
1357
|
+
? args.idempotencyKey.trim()
|
|
1358
|
+
: makeIdempotencyKey("mcp_dispute_evidence");
|
|
1359
|
+
const out = await client.requestJson(`/runs/${encodeURIComponent(runId)}/dispute/evidence`, {
|
|
1360
|
+
method: "POST",
|
|
1361
|
+
write: true,
|
|
1362
|
+
body: {
|
|
1363
|
+
disputeId: args?.disputeId ?? null,
|
|
1364
|
+
evidenceRef,
|
|
1365
|
+
submittedByAgentId: args?.submittedByAgentId ?? null,
|
|
1366
|
+
reason: args?.reason ?? null
|
|
1367
|
+
},
|
|
1368
|
+
idem: idempotencyKey
|
|
1369
|
+
});
|
|
1370
|
+
result = { ok: true, runId, idempotencyKey, ...redactSecrets(out) };
|
|
1371
|
+
} else if (name === "settld.dispute_escalate") {
|
|
1372
|
+
const runId = String(args?.runId ?? "").trim();
|
|
1373
|
+
assertNonEmptyString(runId, "runId");
|
|
1374
|
+
const escalationLevel = String(args?.escalationLevel ?? "").trim().toLowerCase();
|
|
1375
|
+
if (!["l1_counterparty", "l2_arbiter", "l3_external"].includes(escalationLevel)) {
|
|
1376
|
+
throw new TypeError("escalationLevel must be l1_counterparty|l2_arbiter|l3_external");
|
|
1377
|
+
}
|
|
1378
|
+
const idempotencyKey =
|
|
1379
|
+
typeof args?.idempotencyKey === "string" && args.idempotencyKey.trim() !== ""
|
|
1380
|
+
? args.idempotencyKey.trim()
|
|
1381
|
+
: makeIdempotencyKey("mcp_dispute_escalate");
|
|
1382
|
+
const out = await client.requestJson(`/runs/${encodeURIComponent(runId)}/dispute/escalate`, {
|
|
1383
|
+
method: "POST",
|
|
1384
|
+
write: true,
|
|
1385
|
+
body: {
|
|
1386
|
+
disputeId: args?.disputeId ?? null,
|
|
1387
|
+
escalationLevel,
|
|
1388
|
+
channel: args?.channel ?? null,
|
|
1389
|
+
escalatedByAgentId: args?.escalatedByAgentId ?? null,
|
|
1390
|
+
reason: args?.reason ?? null
|
|
1391
|
+
},
|
|
1392
|
+
idem: idempotencyKey
|
|
1393
|
+
});
|
|
1394
|
+
result = { ok: true, runId, idempotencyKey, ...redactSecrets(out) };
|
|
1395
|
+
} else if (name === "settld.dispute_close") {
|
|
1396
|
+
const runId = String(args?.runId ?? "").trim();
|
|
1397
|
+
assertNonEmptyString(runId, "runId");
|
|
1398
|
+
const idempotencyKey =
|
|
1399
|
+
typeof args?.idempotencyKey === "string" && args.idempotencyKey.trim() !== ""
|
|
1400
|
+
? args.idempotencyKey.trim()
|
|
1401
|
+
: makeIdempotencyKey("mcp_dispute_close");
|
|
1402
|
+
const out = await client.requestJson(`/runs/${encodeURIComponent(runId)}/dispute/close`, {
|
|
1403
|
+
method: "POST",
|
|
1404
|
+
write: true,
|
|
1405
|
+
body: {
|
|
1406
|
+
disputeId: args?.disputeId ?? null,
|
|
1407
|
+
resolution: args?.resolution ?? null,
|
|
1408
|
+
verdict: args?.verdict ?? null,
|
|
1409
|
+
arbitrationVerdict: args?.arbitrationVerdict ?? null
|
|
1410
|
+
},
|
|
1411
|
+
idem: idempotencyKey
|
|
1412
|
+
});
|
|
1413
|
+
result = { ok: true, runId, idempotencyKey, ...redactSecrets(out) };
|
|
1414
|
+
} else if (name === "settld.arbitration_open") {
|
|
1415
|
+
const runId = String(args?.runId ?? "").trim();
|
|
1416
|
+
assertNonEmptyString(runId, "runId");
|
|
1417
|
+
const idempotencyKey =
|
|
1418
|
+
typeof args?.idempotencyKey === "string" && args.idempotencyKey.trim() !== ""
|
|
1419
|
+
? args.idempotencyKey.trim()
|
|
1420
|
+
: makeIdempotencyKey("mcp_arbitration_open");
|
|
1421
|
+
const panelCandidateAgentIds = Array.isArray(args?.panelCandidateAgentIds)
|
|
1422
|
+
? args.panelCandidateAgentIds.map((v) => String(v ?? "").trim()).filter(Boolean)
|
|
1423
|
+
: null;
|
|
1424
|
+
const evidenceRefs = Array.isArray(args?.evidenceRefs) ? args.evidenceRefs.map((v) => String(v ?? "").trim()).filter(Boolean) : null;
|
|
1425
|
+
const out = await client.requestJson(`/runs/${encodeURIComponent(runId)}/arbitration/open`, {
|
|
1426
|
+
method: "POST",
|
|
1427
|
+
write: true,
|
|
1428
|
+
body: {
|
|
1429
|
+
caseId: args?.caseId ?? null,
|
|
1430
|
+
disputeId: args?.disputeId ?? null,
|
|
1431
|
+
arbiterAgentId: args?.arbiterAgentId ?? null,
|
|
1432
|
+
panelCandidateAgentIds,
|
|
1433
|
+
evidenceRefs,
|
|
1434
|
+
summary: args?.summary ?? null
|
|
1435
|
+
},
|
|
1436
|
+
idem: idempotencyKey
|
|
1437
|
+
});
|
|
1438
|
+
result = { ok: true, runId, idempotencyKey, ...redactSecrets(out) };
|
|
1439
|
+
} else if (name === "settld.arbitration_issue_verdict") {
|
|
1440
|
+
const runId = String(args?.runId ?? "").trim();
|
|
1441
|
+
const caseId = String(args?.caseId ?? "").trim();
|
|
1442
|
+
assertNonEmptyString(runId, "runId");
|
|
1443
|
+
assertNonEmptyString(caseId, "caseId");
|
|
1444
|
+
if (!args?.arbitrationVerdict || typeof args.arbitrationVerdict !== "object" || Array.isArray(args.arbitrationVerdict)) {
|
|
1445
|
+
throw new TypeError("arbitrationVerdict must be an object");
|
|
1446
|
+
}
|
|
1447
|
+
const idempotencyKey =
|
|
1448
|
+
typeof args?.idempotencyKey === "string" && args.idempotencyKey.trim() !== ""
|
|
1449
|
+
? args.idempotencyKey.trim()
|
|
1450
|
+
: makeIdempotencyKey("mcp_arbitration_verdict");
|
|
1451
|
+
const out = await client.requestJson(`/runs/${encodeURIComponent(runId)}/arbitration/verdict`, {
|
|
1452
|
+
method: "POST",
|
|
1453
|
+
write: true,
|
|
1454
|
+
body: {
|
|
1455
|
+
caseId,
|
|
1456
|
+
arbitrationVerdict: args.arbitrationVerdict
|
|
1457
|
+
},
|
|
1458
|
+
idem: idempotencyKey
|
|
1459
|
+
});
|
|
1460
|
+
result = { ok: true, runId, caseId, idempotencyKey, ...redactSecrets(out) };
|
|
1461
|
+
} else if (name === "settld.resolve_settlement") {
|
|
1462
|
+
const runId = String(args?.runId ?? "").trim();
|
|
1463
|
+
assertNonEmptyString(runId, "runId");
|
|
1464
|
+
const status = args?.status ? String(args.status).trim().toLowerCase() : "released";
|
|
1465
|
+
if (status !== "released" && status !== "refunded") throw new TypeError("status must be released|refunded");
|
|
1466
|
+
|
|
1467
|
+
const out = await client.requestJson(`/runs/${encodeURIComponent(runId)}/settlement/resolve`, {
|
|
1468
|
+
method: "POST",
|
|
1469
|
+
write: true,
|
|
1470
|
+
body: {
|
|
1471
|
+
status,
|
|
1472
|
+
releaseRatePct: args?.releaseRatePct ?? null,
|
|
1473
|
+
releasedAmountCents: args?.releasedAmountCents ?? null,
|
|
1474
|
+
refundedAmountCents: args?.refundedAmountCents ?? null,
|
|
1475
|
+
reason: args?.reason ?? null,
|
|
1476
|
+
resolvedByAgentId: args?.resolvedByAgentId ?? null
|
|
1477
|
+
},
|
|
1478
|
+
idem: makeIdempotencyKey("mcp_settlement_resolve")
|
|
1479
|
+
});
|
|
1480
|
+
result = { ok: true, ...redactSecrets(out) };
|
|
1481
|
+
} else {
|
|
1482
|
+
throw new Error(`tool not implemented: ${name}`);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
const durationMs = nowMs() - started;
|
|
1486
|
+
const toolResult = { ...asTextResult({ tool: name, durationMs, result }), isError: false };
|
|
1487
|
+
if (!isNotification) stream.send({ jsonrpc: "2.0", id, result: toolResult });
|
|
1488
|
+
} catch (err) {
|
|
1489
|
+
const durationMs = nowMs() - started;
|
|
1490
|
+
const toolResult = { ...asErrorResult(err), content: [contentText(JSON.stringify({ tool: name, durationMs, error: err?.message ?? String(err) }, null, 2))] };
|
|
1491
|
+
if (!isNotification) stream.send({ jsonrpc: "2.0", id, result: toolResult });
|
|
1492
|
+
}
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
// Unknown method.
|
|
1497
|
+
if (!isNotification) {
|
|
1498
|
+
stream.send({ jsonrpc: "2.0", id, error: jsonRpcError(-32601, `Method not found: ${method}`) });
|
|
1499
|
+
}
|
|
1500
|
+
} catch (err) {
|
|
1501
|
+
if (!isNotification) {
|
|
1502
|
+
stream.send({ jsonrpc: "2.0", id, error: jsonRpcError(-32603, "Internal error", { message: err?.message ?? String(err) }) });
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
main().catch((err) => {
|
|
1509
|
+
process.stderr.write(`[mcp] fatal: ${err?.stack || err?.message || String(err)}\n`);
|
|
1510
|
+
process.exitCode = 1;
|
|
1511
|
+
});
|