@oobe-protocol-labs/sap-mcp-server 0.9.15 → 0.9.20
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/.env.example +8 -7
- package/CHANGELOG.md +227 -0
- package/README.md +3 -3
- package/USER_DOCS/01_HOSTED_REMOTE_MCP.md +1 -1
- package/USER_DOCS/04_CLIENT_CONFIGS.md +3 -3
- package/USER_DOCS/05_SKILLS_AND_TOOLS.md +16 -8
- package/USER_DOCS/06_DESKTOP_GUI_WIZARD.md +5 -5
- package/config.example.json +5 -4
- package/config.schema.json +7 -2
- package/config.secure-example.json +5 -4
- package/dist/adapters/index.d.ts +7 -1
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +7 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/mcp/errors.d.ts +13 -1
- package/dist/adapters/mcp/errors.d.ts.map +1 -1
- package/dist/adapters/mcp/errors.js +13 -1
- package/dist/adapters/mcp/errors.js.map +1 -1
- package/dist/adapters/mcp/index.d.ts +7 -1
- package/dist/adapters/mcp/index.d.ts.map +1 -1
- package/dist/adapters/mcp/index.js +7 -1
- package/dist/adapters/mcp/index.js.map +1 -1
- package/dist/adapters/mcp/prompt-response.d.ts +13 -1
- package/dist/adapters/mcp/prompt-response.d.ts.map +1 -1
- package/dist/adapters/mcp/prompt-response.js +13 -1
- package/dist/adapters/mcp/prompt-response.js.map +1 -1
- package/dist/adapters/mcp/resource-response.d.ts +14 -1
- package/dist/adapters/mcp/resource-response.d.ts.map +1 -1
- package/dist/adapters/mcp/resource-response.js +14 -1
- package/dist/adapters/mcp/resource-response.js.map +1 -1
- package/dist/adapters/mcp/sdk-compat.d.ts +11 -3
- package/dist/adapters/mcp/sdk-compat.d.ts.map +1 -1
- package/dist/adapters/mcp/sdk-compat.js +213 -24
- package/dist/adapters/mcp/sdk-compat.js.map +1 -1
- package/dist/adapters/mcp/tool-response.d.ts +39 -4
- package/dist/adapters/mcp/tool-response.d.ts.map +1 -1
- package/dist/adapters/mcp/tool-response.js +39 -4
- package/dist/adapters/mcp/tool-response.js.map +1 -1
- package/dist/adapters/solana/commitment.d.ts +14 -2
- package/dist/adapters/solana/commitment.d.ts.map +1 -1
- package/dist/adapters/solana/commitment.js +14 -2
- package/dist/adapters/solana/commitment.js.map +1 -1
- package/dist/adapters/solana/connection.d.ts +11 -2
- package/dist/adapters/solana/connection.d.ts.map +1 -1
- package/dist/adapters/solana/connection.js +11 -2
- package/dist/adapters/solana/connection.js.map +1 -1
- package/dist/adapters/solana/index.d.ts +7 -1
- package/dist/adapters/solana/index.d.ts.map +1 -1
- package/dist/adapters/solana/index.js +7 -1
- package/dist/adapters/solana/index.js.map +1 -1
- package/dist/adapters/solana/public-key.d.ts +11 -2
- package/dist/adapters/solana/public-key.d.ts.map +1 -1
- package/dist/adapters/solana/public-key.js +11 -2
- package/dist/adapters/solana/public-key.js.map +1 -1
- package/dist/config/defaults.d.ts +5 -0
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +5 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/env.d.ts +4 -0
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +11 -6
- package/dist/config/env.js.map +1 -1
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +5 -0
- package/dist/config/index.js.map +1 -1
- package/dist/config/mcp-client-injection.d.ts.map +1 -1
- package/dist/config/mcp-client-injection.js +97 -1
- package/dist/config/mcp-client-injection.js.map +1 -1
- package/dist/config/paths.d.ts +5 -0
- package/dist/config/paths.d.ts.map +1 -1
- package/dist/config/paths.js +5 -0
- package/dist/config/paths.js.map +1 -1
- package/dist/config/profiles.d.ts +3 -10
- package/dist/config/profiles.d.ts.map +1 -1
- package/dist/config/profiles.js +5 -0
- package/dist/config/profiles.js.map +1 -1
- package/dist/config/schema.d.ts +5 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +5 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/config/secure-config.d.ts +5 -0
- package/dist/config/secure-config.d.ts.map +1 -1
- package/dist/config/secure-config.js +5 -0
- package/dist/config/secure-config.js.map +1 -1
- package/dist/config/setup.d.ts +5 -0
- package/dist/config/setup.d.ts.map +1 -1
- package/dist/config/setup.js +5 -0
- package/dist/config/setup.js.map +1 -1
- package/dist/config-cli.d.ts +3 -13
- package/dist/config-cli.d.ts.map +1 -1
- package/dist/config-cli.js +5 -0
- package/dist/config-cli.js.map +1 -1
- package/dist/core/constants.d.ts +25 -6
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/constants.js +27 -8
- package/dist/core/constants.js.map +1 -1
- package/dist/core/errors.d.ts +5 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +5 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/guards.d.ts +5 -0
- package/dist/core/guards.d.ts.map +1 -1
- package/dist/core/guards.js +3 -1
- package/dist/core/guards.js.map +1 -1
- package/dist/core/index.d.ts +7 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +7 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/logger.js +1 -1
- package/dist/core/logger.js.map +1 -1
- package/dist/core/result.d.ts +77 -9
- package/dist/core/result.d.ts.map +1 -1
- package/dist/core/result.js +68 -9
- package/dist/core/result.js.map +1 -1
- package/dist/core/types.d.ts +5 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +3 -4
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/payments/facilitator-rpc-fallback.d.ts.map +1 -1
- package/dist/payments/facilitator-rpc-fallback.js +95 -0
- package/dist/payments/facilitator-rpc-fallback.js.map +1 -1
- package/dist/payments/hosted-tool-eligibility.d.ts +1 -0
- package/dist/payments/hosted-tool-eligibility.d.ts.map +1 -1
- package/dist/payments/hosted-tool-eligibility.js +1 -0
- package/dist/payments/hosted-tool-eligibility.js.map +1 -1
- package/dist/payments/mcp-session-cache.d.ts +35 -0
- package/dist/payments/mcp-session-cache.d.ts.map +1 -0
- package/dist/payments/mcp-session-cache.js +83 -0
- package/dist/payments/mcp-session-cache.js.map +1 -0
- package/dist/payments/monetization-gate.d.ts +5 -0
- package/dist/payments/monetization-gate.d.ts.map +1 -1
- package/dist/payments/monetization-gate.js +24 -1
- package/dist/payments/monetization-gate.js.map +1 -1
- package/dist/payments/pricing.d.ts +2 -0
- package/dist/payments/pricing.d.ts.map +1 -1
- package/dist/payments/pricing.js +126 -10
- package/dist/payments/pricing.js.map +1 -1
- package/dist/payments/usage-ledger.d.ts +36 -2
- package/dist/payments/usage-ledger.d.ts.map +1 -1
- package/dist/payments/usage-ledger.js +121 -4
- package/dist/payments/usage-ledger.js.map +1 -1
- package/dist/payments/x402-paid-call.d.ts +50 -0
- package/dist/payments/x402-paid-call.d.ts.map +1 -1
- package/dist/payments/x402-paid-call.js +171 -0
- package/dist/payments/x402-paid-call.js.map +1 -1
- package/dist/policy/default-policies.d.ts +18 -2
- package/dist/policy/default-policies.d.ts.map +1 -1
- package/dist/policy/default-policies.js +18 -2
- package/dist/policy/default-policies.js.map +1 -1
- package/dist/policy/index.d.ts +6 -3
- package/dist/policy/index.d.ts.map +1 -1
- package/dist/policy/index.js +6 -3
- package/dist/policy/index.js.map +1 -1
- package/dist/policy/local-policy-engine.d.ts +71 -13
- package/dist/policy/local-policy-engine.d.ts.map +1 -1
- package/dist/policy/local-policy-engine.js +33 -11
- package/dist/policy/local-policy-engine.js.map +1 -1
- package/dist/policy/permission-checks.d.ts +29 -3
- package/dist/policy/permission-checks.d.ts.map +1 -1
- package/dist/policy/permission-checks.js +29 -3
- package/dist/policy/permission-checks.js.map +1 -1
- package/dist/policy/policy-engine.d.ts +22 -2
- package/dist/policy/policy-engine.d.ts.map +1 -1
- package/dist/policy/policy-engine.js +22 -2
- package/dist/policy/policy-engine.js.map +1 -1
- package/dist/policy/policy-types.d.ts +55 -5
- package/dist/policy/policy-types.d.ts.map +1 -1
- package/dist/policy/policy-types.js +8 -1
- package/dist/policy/policy-types.js.map +1 -1
- package/dist/policy/risk-level.d.ts +32 -3
- package/dist/policy/risk-level.d.ts.map +1 -1
- package/dist/policy/risk-level.js +32 -3
- package/dist/policy/risk-level.js.map +1 -1
- package/dist/policy/spending-limits.d.ts +29 -3
- package/dist/policy/spending-limits.d.ts.map +1 -1
- package/dist/policy/spending-limits.js +29 -3
- package/dist/policy/spending-limits.js.map +1 -1
- package/dist/premium/activation-manager.d.ts +43 -0
- package/dist/premium/activation-manager.d.ts.map +1 -0
- package/dist/premium/activation-manager.js +131 -0
- package/dist/premium/activation-manager.js.map +1 -0
- package/dist/premium/builtin-plugins.d.ts +143 -0
- package/dist/premium/builtin-plugins.d.ts.map +1 -0
- package/dist/premium/builtin-plugins.js +394 -0
- package/dist/premium/builtin-plugins.js.map +1 -0
- package/dist/premium/event-store.d.ts +61 -0
- package/dist/premium/event-store.d.ts.map +1 -0
- package/dist/premium/event-store.js +130 -0
- package/dist/premium/event-store.js.map +1 -0
- package/dist/premium/index.d.ts +54 -0
- package/dist/premium/index.d.ts.map +1 -0
- package/dist/premium/index.js +54 -0
- package/dist/premium/index.js.map +1 -0
- package/dist/premium/manifest-builder.d.ts +65 -0
- package/dist/premium/manifest-builder.d.ts.map +1 -0
- package/dist/premium/manifest-builder.js +182 -0
- package/dist/premium/manifest-builder.js.map +1 -0
- package/dist/premium/meme-radar-capabilities.d.ts +38 -0
- package/dist/premium/meme-radar-capabilities.d.ts.map +1 -0
- package/dist/premium/meme-radar-capabilities.js +306 -0
- package/dist/premium/meme-radar-capabilities.js.map +1 -0
- package/dist/premium/metrics.d.ts +27 -0
- package/dist/premium/metrics.d.ts.map +1 -0
- package/dist/premium/metrics.js +65 -0
- package/dist/premium/metrics.js.map +1 -0
- package/dist/premium/plugin-validator.d.ts +39 -0
- package/dist/premium/plugin-validator.d.ts.map +1 -0
- package/dist/premium/plugin-validator.js +325 -0
- package/dist/premium/plugin-validator.js.map +1 -0
- package/dist/premium/private-manifest-loader.d.ts +111 -0
- package/dist/premium/private-manifest-loader.d.ts.map +1 -0
- package/dist/premium/private-manifest-loader.js +285 -0
- package/dist/premium/private-manifest-loader.js.map +1 -0
- package/dist/premium/provider-bridge.d.ts +109 -0
- package/dist/premium/provider-bridge.d.ts.map +1 -0
- package/dist/premium/provider-bridge.js +239 -0
- package/dist/premium/provider-bridge.js.map +1 -0
- package/dist/premium/session-manager.d.ts +125 -0
- package/dist/premium/session-manager.d.ts.map +1 -0
- package/dist/premium/session-manager.js +337 -0
- package/dist/premium/session-manager.js.map +1 -0
- package/dist/premium/stream-broker.d.ts +98 -0
- package/dist/premium/stream-broker.d.ts.map +1 -0
- package/dist/premium/stream-broker.js +199 -0
- package/dist/premium/stream-broker.js.map +1 -0
- package/dist/premium/tech-fundamentals-capabilities.d.ts +37 -0
- package/dist/premium/tech-fundamentals-capabilities.d.ts.map +1 -0
- package/dist/premium/tech-fundamentals-capabilities.js +283 -0
- package/dist/premium/tech-fundamentals-capabilities.js.map +1 -0
- package/dist/premium/trading-capabilities.d.ts +36 -0
- package/dist/premium/trading-capabilities.d.ts.map +1 -0
- package/dist/premium/trading-capabilities.js +205 -0
- package/dist/premium/trading-capabilities.js.map +1 -0
- package/dist/premium/types.d.ts +518 -0
- package/dist/premium/types.d.ts.map +1 -0
- package/dist/premium/types.js +23 -0
- package/dist/premium/types.js.map +1 -0
- package/dist/premium/webhook-engine.d.ts +102 -0
- package/dist/premium/webhook-engine.d.ts.map +1 -0
- package/dist/premium/webhook-engine.js +382 -0
- package/dist/premium/webhook-engine.js.map +1 -0
- package/dist/prompts/context/sap-agent-context.prompt.d.ts +5 -0
- package/dist/prompts/context/sap-agent-context.prompt.d.ts.map +1 -1
- package/dist/prompts/context/sap-agent-context.prompt.js +2 -8
- package/dist/prompts/context/sap-agent-context.prompt.js.map +1 -1
- package/dist/prompts/context/sap-agent-intent-router.prompt.d.ts +14 -0
- package/dist/prompts/context/sap-agent-intent-router.prompt.d.ts.map +1 -0
- package/dist/prompts/context/sap-agent-intent-router.prompt.js +155 -0
- package/dist/prompts/context/sap-agent-intent-router.prompt.js.map +1 -0
- package/dist/prompts/context/sap-agent-start.prompt.js +1 -0
- package/dist/prompts/context/sap-agent-start.prompt.js.map +1 -1
- package/dist/prompts/developer/debug-sap-error.prompt.d.ts +5 -0
- package/dist/prompts/developer/debug-sap-error.prompt.d.ts.map +1 -1
- package/dist/prompts/developer/debug-sap-error.prompt.js +3 -4
- package/dist/prompts/developer/debug-sap-error.prompt.js.map +1 -1
- package/dist/prompts/developer/generate-sap-integration.prompt.d.ts +5 -0
- package/dist/prompts/developer/generate-sap-integration.prompt.d.ts.map +1 -1
- package/dist/prompts/developer/generate-sap-integration.prompt.js +3 -4
- package/dist/prompts/developer/generate-sap-integration.prompt.js.map +1 -1
- package/dist/prompts/developer/index.d.ts +4 -1
- package/dist/prompts/developer/index.d.ts.map +1 -1
- package/dist/prompts/developer/index.js +4 -1
- package/dist/prompts/developer/index.js.map +1 -1
- package/dist/prompts/execution-proof/explain-proof-of-execution.prompt.d.ts +5 -0
- package/dist/prompts/execution-proof/explain-proof-of-execution.prompt.d.ts.map +1 -1
- package/dist/prompts/execution-proof/explain-proof-of-execution.prompt.js +3 -4
- package/dist/prompts/execution-proof/explain-proof-of-execution.prompt.js.map +1 -1
- package/dist/prompts/execution-proof/index.d.ts +4 -1
- package/dist/prompts/execution-proof/index.d.ts.map +1 -1
- package/dist/prompts/execution-proof/index.js +4 -1
- package/dist/prompts/execution-proof/index.js.map +1 -1
- package/dist/prompts/execution-proof/verify-execution-proof.prompt.d.ts +5 -0
- package/dist/prompts/execution-proof/verify-execution-proof.prompt.d.ts.map +1 -1
- package/dist/prompts/execution-proof/verify-execution-proof.prompt.js +3 -4
- package/dist/prompts/execution-proof/verify-execution-proof.prompt.js.map +1 -1
- package/dist/prompts/index.d.ts +4 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +4 -1
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/payments/create-paid-api.prompt.d.ts +5 -0
- package/dist/prompts/payments/create-paid-api.prompt.d.ts.map +1 -1
- package/dist/prompts/payments/create-paid-api.prompt.js +3 -4
- package/dist/prompts/payments/create-paid-api.prompt.js.map +1 -1
- package/dist/prompts/payments/explain-x402-settlement.prompt.d.ts +5 -0
- package/dist/prompts/payments/explain-x402-settlement.prompt.d.ts.map +1 -1
- package/dist/prompts/payments/explain-x402-settlement.prompt.js +3 -4
- package/dist/prompts/payments/explain-x402-settlement.prompt.js.map +1 -1
- package/dist/prompts/payments/index.d.ts +4 -1
- package/dist/prompts/payments/index.d.ts.map +1 -1
- package/dist/prompts/payments/index.js +4 -1
- package/dist/prompts/payments/index.js.map +1 -1
- package/dist/prompts/register-prompts.d.ts +18 -2
- package/dist/prompts/register-prompts.d.ts.map +1 -1
- package/dist/prompts/register-prompts.js +21 -3
- package/dist/prompts/register-prompts.js.map +1 -1
- package/dist/prompts/registry/analyze-sap-agent.prompt.d.ts +5 -0
- package/dist/prompts/registry/analyze-sap-agent.prompt.d.ts.map +1 -1
- package/dist/prompts/registry/analyze-sap-agent.prompt.js +3 -4
- package/dist/prompts/registry/analyze-sap-agent.prompt.js.map +1 -1
- package/dist/prompts/registry/index.d.ts +4 -1
- package/dist/prompts/registry/index.d.ts.map +1 -1
- package/dist/prompts/registry/index.js +4 -1
- package/dist/prompts/registry/index.js.map +1 -1
- package/dist/prompts/registry/register-sap-agent.prompt.d.ts +5 -0
- package/dist/prompts/registry/register-sap-agent.prompt.d.ts.map +1 -1
- package/dist/prompts/registry/register-sap-agent.prompt.js +3 -4
- package/dist/prompts/registry/register-sap-agent.prompt.js.map +1 -1
- package/dist/remote/premium-memory.d.ts +90 -0
- package/dist/remote/premium-memory.d.ts.map +1 -0
- package/dist/remote/premium-memory.js +161 -0
- package/dist/remote/premium-memory.js.map +1 -0
- package/dist/remote/premium-routes.d.ts +99 -0
- package/dist/remote/premium-routes.d.ts.map +1 -0
- package/dist/remote/premium-routes.js +346 -0
- package/dist/remote/premium-routes.js.map +1 -0
- package/dist/remote/public-home/sections.d.ts.map +1 -1
- package/dist/remote/public-home/sections.js +7 -1
- package/dist/remote/public-home/sections.js.map +1 -1
- package/dist/remote/public-home/types.d.ts +6 -0
- package/dist/remote/public-home/types.d.ts.map +1 -1
- package/dist/remote/server.d.ts +18 -0
- package/dist/remote/server.d.ts.map +1 -1
- package/dist/remote/server.js +298 -116
- package/dist/remote/server.js.map +1 -1
- package/dist/remote/tx-relay.d.ts +42 -0
- package/dist/remote/tx-relay.d.ts.map +1 -0
- package/dist/remote/tx-relay.js +118 -0
- package/dist/remote/tx-relay.js.map +1 -0
- package/dist/resources/current/sap-current-config.resource.d.ts +5 -0
- package/dist/resources/current/sap-current-config.resource.d.ts.map +1 -1
- package/dist/resources/current/sap-current-config.resource.js +2 -5
- package/dist/resources/current/sap-current-config.resource.js.map +1 -1
- package/dist/resources/execution-proof/index.d.ts +5 -0
- package/dist/resources/execution-proof/index.d.ts.map +1 -1
- package/dist/resources/execution-proof/index.js +5 -0
- package/dist/resources/execution-proof/index.js.map +1 -1
- package/dist/resources/execution-proof/sap-execution-record.resource.d.ts +5 -0
- package/dist/resources/execution-proof/sap-execution-record.resource.d.ts.map +1 -1
- package/dist/resources/execution-proof/sap-execution-record.resource.js +3 -4
- package/dist/resources/execution-proof/sap-execution-record.resource.js.map +1 -1
- package/dist/resources/index.d.ts +5 -0
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +5 -0
- package/dist/resources/index.js.map +1 -1
- package/dist/resources/memory/index.d.ts +5 -0
- package/dist/resources/memory/index.d.ts.map +1 -1
- package/dist/resources/memory/index.js +5 -0
- package/dist/resources/memory/index.js.map +1 -1
- package/dist/resources/memory/sap-memory.resource.d.ts +5 -0
- package/dist/resources/memory/sap-memory.resource.d.ts.map +1 -1
- package/dist/resources/memory/sap-memory.resource.js +3 -4
- package/dist/resources/memory/sap-memory.resource.js.map +1 -1
- package/dist/resources/profile/sap-active-profile.resource.d.ts +5 -0
- package/dist/resources/profile/sap-active-profile.resource.d.ts.map +1 -1
- package/dist/resources/profile/sap-active-profile.resource.js +2 -5
- package/dist/resources/profile/sap-active-profile.resource.js.map +1 -1
- package/dist/resources/register-resources.d.ts +5 -0
- package/dist/resources/register-resources.d.ts.map +1 -1
- package/dist/resources/register-resources.js +3 -1
- package/dist/resources/register-resources.js.map +1 -1
- package/dist/resources/registry/index.d.ts +5 -0
- package/dist/resources/registry/index.d.ts.map +1 -1
- package/dist/resources/registry/index.js +5 -0
- package/dist/resources/registry/index.js.map +1 -1
- package/dist/resources/registry/sap-agent.resource.d.ts +5 -0
- package/dist/resources/registry/sap-agent.resource.d.ts.map +1 -1
- package/dist/resources/registry/sap-agent.resource.js +3 -4
- package/dist/resources/registry/sap-agent.resource.js.map +1 -1
- package/dist/resources/registry/sap-global-registry.resource.d.ts +18 -4
- package/dist/resources/registry/sap-global-registry.resource.d.ts.map +1 -1
- package/dist/resources/registry/sap-global-registry.resource.js +18 -4
- package/dist/resources/registry/sap-global-registry.resource.js.map +1 -1
- package/dist/resources/reputation/index.d.ts +4 -1
- package/dist/resources/reputation/index.d.ts.map +1 -1
- package/dist/resources/reputation/index.js +4 -1
- package/dist/resources/reputation/index.js.map +1 -1
- package/dist/resources/reputation/sap-reputation.resource.d.ts +18 -4
- package/dist/resources/reputation/sap-reputation.resource.d.ts.map +1 -1
- package/dist/resources/reputation/sap-reputation.resource.js +18 -4
- package/dist/resources/reputation/sap-reputation.resource.js.map +1 -1
- package/dist/resources/tool-schema/index.d.ts +4 -1
- package/dist/resources/tool-schema/index.d.ts.map +1 -1
- package/dist/resources/tool-schema/index.js +4 -1
- package/dist/resources/tool-schema/index.js.map +1 -1
- package/dist/resources/tool-schema/sap-tool-schema.resource.d.ts +18 -4
- package/dist/resources/tool-schema/sap-tool-schema.resource.d.ts.map +1 -1
- package/dist/resources/tool-schema/sap-tool-schema.resource.js +18 -4
- package/dist/resources/tool-schema/sap-tool-schema.resource.js.map +1 -1
- package/dist/sap/index.d.ts +7 -1
- package/dist/sap/index.d.ts.map +1 -1
- package/dist/sap/index.js +7 -1
- package/dist/sap/index.js.map +1 -1
- package/dist/sap/sap-client-manager.d.ts +80 -13
- package/dist/sap/sap-client-manager.d.ts.map +1 -1
- package/dist/sap/sap-client-manager.js +80 -13
- package/dist/sap/sap-client-manager.js.map +1 -1
- package/dist/sap/sap-errors.d.ts +22 -3
- package/dist/sap/sap-errors.d.ts.map +1 -1
- package/dist/sap/sap-errors.js +22 -3
- package/dist/sap/sap-errors.js.map +1 -1
- package/dist/sap/sap-types.d.ts +75 -6
- package/dist/sap/sap-types.d.ts.map +1 -1
- package/dist/sap/sap-types.js +11 -1
- package/dist/sap/sap-types.js.map +1 -1
- package/dist/schemas/common.schema.d.ts +45 -8
- package/dist/schemas/common.schema.d.ts.map +1 -1
- package/dist/schemas/common.schema.js +45 -8
- package/dist/schemas/common.schema.js.map +1 -1
- package/dist/schemas/developer.schema.d.ts +22 -3
- package/dist/schemas/developer.schema.d.ts.map +1 -1
- package/dist/schemas/developer.schema.js +22 -3
- package/dist/schemas/developer.schema.js.map +1 -1
- package/dist/schemas/execution-proof.schema.d.ts +29 -4
- package/dist/schemas/execution-proof.schema.d.ts.map +1 -1
- package/dist/schemas/execution-proof.schema.js +29 -4
- package/dist/schemas/execution-proof.schema.js.map +1 -1
- package/dist/schemas/identity.schema.d.ts +23 -3
- package/dist/schemas/identity.schema.d.ts.map +1 -1
- package/dist/schemas/identity.schema.js +23 -3
- package/dist/schemas/identity.schema.js.map +1 -1
- package/dist/schemas/index.d.ts +11 -1
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +11 -1
- package/dist/schemas/index.js.map +1 -1
- package/dist/schemas/memory.schema.d.ts +23 -3
- package/dist/schemas/memory.schema.d.ts.map +1 -1
- package/dist/schemas/memory.schema.js +23 -3
- package/dist/schemas/memory.schema.js.map +1 -1
- package/dist/schemas/payments.schema.d.ts +24 -3
- package/dist/schemas/payments.schema.d.ts.map +1 -1
- package/dist/schemas/payments.schema.js +24 -3
- package/dist/schemas/payments.schema.js.map +1 -1
- package/dist/schemas/registry.schema.d.ts +36 -4
- package/dist/schemas/registry.schema.d.ts.map +1 -1
- package/dist/schemas/registry.schema.js +36 -4
- package/dist/schemas/registry.schema.js.map +1 -1
- package/dist/schemas/reputation.schema.d.ts +22 -3
- package/dist/schemas/reputation.schema.d.ts.map +1 -1
- package/dist/schemas/reputation.schema.js +22 -3
- package/dist/schemas/reputation.schema.js.map +1 -1
- package/dist/schemas/settlement.schema.d.ts +31 -4
- package/dist/schemas/settlement.schema.d.ts.map +1 -1
- package/dist/schemas/settlement.schema.js +31 -4
- package/dist/schemas/settlement.schema.js.map +1 -1
- package/dist/schemas/tool-schema.schema.d.ts +29 -3
- package/dist/schemas/tool-schema.schema.d.ts.map +1 -1
- package/dist/schemas/tool-schema.schema.js +29 -3
- package/dist/schemas/tool-schema.schema.js.map +1 -1
- package/dist/schemas/transaction.schema.d.ts +23 -3
- package/dist/schemas/transaction.schema.d.ts.map +1 -1
- package/dist/schemas/transaction.schema.js +23 -3
- package/dist/schemas/transaction.schema.js.map +1 -1
- package/dist/security/approval-required.d.ts +19 -2
- package/dist/security/approval-required.d.ts.map +1 -1
- package/dist/security/approval-required.js +19 -2
- package/dist/security/approval-required.js.map +1 -1
- package/dist/security/index.d.ts +7 -1
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +7 -1
- package/dist/security/index.js.map +1 -1
- package/dist/security/private-key-guard.d.ts +18 -2
- package/dist/security/private-key-guard.d.ts.map +1 -1
- package/dist/security/private-key-guard.js +19 -2
- package/dist/security/private-key-guard.js.map +1 -1
- package/dist/security/prompt-injection-notes.d.ts +10 -1
- package/dist/security/prompt-injection-notes.d.ts.map +1 -1
- package/dist/security/prompt-injection-notes.js +10 -1
- package/dist/security/prompt-injection-notes.js.map +1 -1
- package/dist/security/unsafe-action-guard.d.ts +36 -10
- package/dist/security/unsafe-action-guard.d.ts.map +1 -1
- package/dist/security/unsafe-action-guard.js +67 -15
- package/dist/security/unsafe-action-guard.js.map +1 -1
- package/dist/server/create-server.d.ts +18 -3
- package/dist/server/create-server.d.ts.map +1 -1
- package/dist/server/create-server.js +18 -3
- package/dist/server/create-server.js.map +1 -1
- package/dist/server/index.d.ts +6 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +6 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/register-capabilities.d.ts +17 -2
- package/dist/server/register-capabilities.d.ts.map +1 -1
- package/dist/server/register-capabilities.js +17 -2
- package/dist/server/register-capabilities.js.map +1 -1
- package/dist/server/server-metadata.d.ts +34 -1
- package/dist/server/server-metadata.d.ts.map +1 -1
- package/dist/server/server-metadata.js +35 -2
- package/dist/server/server-metadata.js.map +1 -1
- package/dist/session/agent-session.d.ts +35 -4
- package/dist/session/agent-session.d.ts.map +1 -1
- package/dist/session/agent-session.js +35 -4
- package/dist/session/agent-session.js.map +1 -1
- package/dist/session/delegated-session.d.ts +31 -3
- package/dist/session/delegated-session.d.ts.map +1 -1
- package/dist/session/delegated-session.js +31 -3
- package/dist/session/delegated-session.js.map +1 -1
- package/dist/session/index.d.ts +7 -1
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +7 -1
- package/dist/session/index.js.map +1 -1
- package/dist/session/redis-session-store.d.ts +94 -21
- package/dist/session/redis-session-store.d.ts.map +1 -1
- package/dist/session/redis-session-store.js +87 -20
- package/dist/session/redis-session-store.js.map +1 -1
- package/dist/session/session-limits.d.ts +34 -4
- package/dist/session/session-limits.d.ts.map +1 -1
- package/dist/session/session-limits.js +34 -4
- package/dist/session/session-limits.js.map +1 -1
- package/dist/session/session-permissions.d.ts +57 -7
- package/dist/session/session-permissions.d.ts.map +1 -1
- package/dist/session/session-permissions.js +57 -7
- package/dist/session/session-permissions.js.map +1 -1
- package/dist/session/session-store.d.ts +9 -1
- package/dist/session/session-store.d.ts.map +1 -1
- package/dist/session/session-store.js +18 -3
- package/dist/session/session-store.js.map +1 -1
- package/dist/session/session-types.d.ts +33 -4
- package/dist/session/session-types.d.ts.map +1 -1
- package/dist/session/session-types.js +9 -1
- package/dist/session/session-types.js.map +1 -1
- package/dist/signer/index.d.ts +7 -1
- package/dist/signer/index.d.ts.map +1 -1
- package/dist/signer/index.js +7 -1
- package/dist/signer/index.js.map +1 -1
- package/dist/signer/load-keypair.d.ts +25 -4
- package/dist/signer/load-keypair.d.ts.map +1 -1
- package/dist/signer/load-keypair.js +25 -4
- package/dist/signer/load-keypair.js.map +1 -1
- package/dist/signer/local-keypair-signer.d.ts +16 -2
- package/dist/signer/local-keypair-signer.d.ts.map +1 -1
- package/dist/signer/local-keypair-signer.js +16 -2
- package/dist/signer/local-keypair-signer.js.map +1 -1
- package/dist/signer/signer-resolver.d.ts +19 -2
- package/dist/signer/signer-resolver.d.ts.map +1 -1
- package/dist/signer/signer-resolver.js +26 -3
- package/dist/signer/signer-resolver.js.map +1 -1
- package/dist/signer/signer-types.d.ts +47 -5
- package/dist/signer/signer-types.d.ts.map +1 -1
- package/dist/signer/signer-types.js +13 -1
- package/dist/signer/signer-types.js.map +1 -1
- package/dist/tools/agent-start-tool.d.ts.map +1 -1
- package/dist/tools/agent-start-tool.js +210 -2
- package/dist/tools/agent-start-tool.js.map +1 -1
- package/dist/tools/client-sdk-tools.d.ts.map +1 -1
- package/dist/tools/client-sdk-tools.js +27 -2
- package/dist/tools/client-sdk-tools.js.map +1 -1
- package/dist/tools/estimate-tool-cost.d.ts +13 -0
- package/dist/tools/estimate-tool-cost.d.ts.map +1 -0
- package/dist/tools/estimate-tool-cost.js +139 -0
- package/dist/tools/estimate-tool-cost.js.map +1 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +7 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/magicblock-tools.d.ts +25 -0
- package/dist/tools/magicblock-tools.d.ts.map +1 -1
- package/dist/tools/magicblock-tools.js +116 -8
- package/dist/tools/magicblock-tools.js.map +1 -1
- package/dist/tools/perp-tools.d.ts +39 -0
- package/dist/tools/perp-tools.d.ts.map +1 -0
- package/dist/tools/perp-tools.js +1110 -0
- package/dist/tools/perp-tools.js.map +1 -0
- package/dist/tools/premium-tools.d.ts +8 -0
- package/dist/tools/premium-tools.d.ts.map +1 -0
- package/dist/tools/premium-tools.js +732 -0
- package/dist/tools/premium-tools.js.map +1 -0
- package/dist/tools/register-tools.d.ts.map +1 -1
- package/dist/tools/register-tools.js +9 -0
- package/dist/tools/register-tools.js.map +1 -1
- package/dist/tools/sap-sdk-tools.d.ts.map +1 -1
- package/dist/tools/sap-sdk-tools.js +191 -13
- package/dist/tools/sap-sdk-tools.js.map +1 -1
- package/dist/tools/x402-paid-call-tool.js +2 -2
- package/dist/tools/x402-paid-call-tool.js.map +1 -1
- package/dist/transports/index.d.ts +6 -1
- package/dist/transports/index.d.ts.map +1 -1
- package/dist/transports/index.js +6 -1
- package/dist/transports/index.js.map +1 -1
- package/dist/transports/stdio.d.ts +17 -5
- package/dist/transports/stdio.d.ts.map +1 -1
- package/dist/transports/stdio.js +17 -5
- package/dist/transports/stdio.js.map +1 -1
- package/dist/tui/wizard-save.d.ts +88 -8
- package/dist/tui/wizard-save.d.ts.map +1 -1
- package/dist/tui/wizard-save.js +77 -8
- package/dist/tui/wizard-save.js.map +1 -1
- package/docs/06_PAYMENTS_X402_AND_PAYSH.md +20 -4
- package/docs/07_ENDPOINTS_AND_CLIENTS.md +4 -1
- package/docs/09_TOOLS_SKILLS_AND_AGENT_GUIDE.md +16 -5
- package/docs/14_DESKTOP_WIZARD_RELEASE.md +3 -3
- package/docs/16_SAP_AGENT_IDENTITY_PIPELINE.md +6 -0
- package/docs/18_PREMIUM_PLUGIN_RUNTIME.md +108 -0
- package/docs/README.md +12 -0
- package/package.json +8 -6
- package/server.json +3 -3
- package/skills/README.md +10 -2
- package/skills/sap-agent-registry/SKILL.md +27 -6
- package/skills/sap-discovery-indexing/SKILL.md +11 -0
- package/skills/sap-mcp/SKILL.md +49 -16
- package/skills/sap-mcp/TOOL_REFERENCE.md +20 -7
- package/skills/sap-operations/SKILL.md +11 -0
- package/skills/sap-payments-x402/SKILL.md +30 -8
|
@@ -0,0 +1,1110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @name tools/perp-tools
|
|
3
|
+
* @description MCP tools for perpetual futures trading and chart analysis on Solana.
|
|
4
|
+
*
|
|
5
|
+
* Provides 10 tools across two categories:
|
|
6
|
+
*
|
|
7
|
+
* Read-only tools (7):
|
|
8
|
+
* - sap_perp_markets — List Adrena perp markets with mark price, funding, OI.
|
|
9
|
+
* - sap_perp_position_info — Read on-chain perp positions for a wallet.
|
|
10
|
+
* - sap_perp_funding_history — Fetch funding rate history from Adrena API.
|
|
11
|
+
* - sap_chart_ohlc — OHLC candlestick data for any Solana token.
|
|
12
|
+
* - sap_chart_long_term — Long-term price history + protocol TVL.
|
|
13
|
+
* - sap_chart_volume_profile — Volume profile analysis (POC, VAH, VAL).
|
|
14
|
+
* - sap_perp_liquidation_zones — Compute liquidation zones for open positions.
|
|
15
|
+
*
|
|
16
|
+
* Inscribed tools (3 — build unsigned transactions for local signing):
|
|
17
|
+
* - sap_perp_build_open — Build tx to open a leveraged perp position.
|
|
18
|
+
* - sap_perp_build_close — Build tx to close a perp position.
|
|
19
|
+
* - sap_perp_build_modify — Build tx to add/remove collateral.
|
|
20
|
+
*
|
|
21
|
+
* All read-only tools use free APIs (DexScreener, DeFiLlama) and Solana RPC
|
|
22
|
+
* (Triton). Inscribed tools build unsigned transactions with @solana/web3.js
|
|
23
|
+
* — the agent signs locally, no server-side signing keys.
|
|
24
|
+
*
|
|
25
|
+
* @module tools/perp-tools
|
|
26
|
+
*/
|
|
27
|
+
import { PublicKey, Transaction, TransactionInstruction, SystemProgram } from '@solana/web3.js';
|
|
28
|
+
import { createTextResponse } from '../adapters/mcp/tool-response.js';
|
|
29
|
+
import { registerTool } from '../adapters/mcp/sdk-compat.js';
|
|
30
|
+
import { logger } from '../core/logger.js';
|
|
31
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
32
|
+
* Constants
|
|
33
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
34
|
+
/** Adrena program ID on Solana mainnet. */
|
|
35
|
+
const ADRENA_PROGRAM_ID = new PublicKey('13gDzEXCdocbj8iAiqrScGo47NiSuYENGsRqi3SEAwet');
|
|
36
|
+
/** DexScreener REST API base URL (free, no API key). */
|
|
37
|
+
const DEXSCREENER_API_URL = 'https://api.dexscreener.com';
|
|
38
|
+
/** DeFiLlama REST API base URL (free, no API key). */
|
|
39
|
+
const DEFILAMA_API_URL = 'https://api.llama.fi';
|
|
40
|
+
/** Adrena REST API base URL (free, may be unreachable — tools return structured errors). */
|
|
41
|
+
const ADRENA_API_URL = 'https://datapi.adrena.xyz';
|
|
42
|
+
/** Fetch timeout for external API calls (ms). */
|
|
43
|
+
const FETCH_TIMEOUT_MS = 8_000;
|
|
44
|
+
/** Default number of volume profile buckets. */
|
|
45
|
+
const DEFAULT_VP_BUCKETS = 20;
|
|
46
|
+
/** Value area percentage for volume profile (70%). */
|
|
47
|
+
const VALUE_AREA_PCT = 0.70;
|
|
48
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
49
|
+
* Adrena instruction discriminators (from IDL v2.1.5, Anchor 0.31)
|
|
50
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
51
|
+
const DISC_OPEN_LONG = Buffer.from([224, 114, 146, 60, 127, 166, 244, 56]);
|
|
52
|
+
const DISC_OPEN_SHORT = Buffer.from([196, 212, 161, 82, 250, 39, 201, 102]);
|
|
53
|
+
const DISC_CLOSE_LONG = Buffer.from([50, 66, 35, 214, 218, 31, 152, 68]);
|
|
54
|
+
const DISC_CLOSE_SHORT = Buffer.from([158, 216, 38, 16, 140, 37, 15, 131]);
|
|
55
|
+
const DISC_ADD_COLLATERAL_LONG = Buffer.from([101, 191, 243, 208, 154, 22, 72, 19]);
|
|
56
|
+
const DISC_ADD_COLLATERAL_SHORT = Buffer.from([197, 235, 47, 1, 228, 10, 200, 184]);
|
|
57
|
+
const DISC_REMOVE_COLLATERAL_LONG = Buffer.from([179, 122, 186, 139, 223, 72, 205, 58]);
|
|
58
|
+
const DISC_REMOVE_COLLATERAL_SHORT = Buffer.from([242, 74, 116, 29, 106, 148, 241, 205]);
|
|
59
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
60
|
+
* Helper: timed fetch
|
|
61
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
62
|
+
/**
|
|
63
|
+
* @name timedFetch
|
|
64
|
+
* @description Fetch JSON from a URL with an abort timeout.
|
|
65
|
+
*
|
|
66
|
+
* @param url — The URL to fetch.
|
|
67
|
+
* @returns Parsed JSON response, or `null` on error/timeout.
|
|
68
|
+
*
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
async function timedFetch(url) {
|
|
72
|
+
try {
|
|
73
|
+
const controller = new AbortController();
|
|
74
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
75
|
+
const response = await fetch(url, {
|
|
76
|
+
method: 'GET',
|
|
77
|
+
headers: { 'Content-Type': 'application/json' },
|
|
78
|
+
signal: controller.signal,
|
|
79
|
+
});
|
|
80
|
+
clearTimeout(timeout);
|
|
81
|
+
if (!response.ok)
|
|
82
|
+
return null;
|
|
83
|
+
return await response.json();
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* @name buildInscribedTx
|
|
91
|
+
* @description Build an unsigned transaction from a single instruction and serialize it.
|
|
92
|
+
*
|
|
93
|
+
* @param instruction — The TransactionInstruction to include.
|
|
94
|
+
* @returns Serialized base64 transaction + metadata.
|
|
95
|
+
*
|
|
96
|
+
* @internal
|
|
97
|
+
*/
|
|
98
|
+
function buildInscribedTx(instruction) {
|
|
99
|
+
const tx = new Transaction();
|
|
100
|
+
tx.add(instruction);
|
|
101
|
+
const serialized = Buffer.from(tx.serialize({ requireAllSignatures: false })).toString('base64');
|
|
102
|
+
return {
|
|
103
|
+
transaction: serialized,
|
|
104
|
+
programId: instruction.programId.toBase58(),
|
|
105
|
+
instruction: 'adrena',
|
|
106
|
+
accounts: instruction.keys.map(k => ({
|
|
107
|
+
pubkey: k.pubkey.toBase58(),
|
|
108
|
+
isSigner: k.isSigner,
|
|
109
|
+
isWritable: k.isWritable,
|
|
110
|
+
})),
|
|
111
|
+
dataLength: instruction.data.length,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* @name writeNumberU64LE
|
|
116
|
+
* @description Write a u64 number as little-endian bytes into a Buffer at an offset.
|
|
117
|
+
*
|
|
118
|
+
* @param buf — Target buffer.
|
|
119
|
+
* @param value — Number to write (must fit in u64).
|
|
120
|
+
* @param offset — Write offset in the buffer.
|
|
121
|
+
*
|
|
122
|
+
* @internal
|
|
123
|
+
*/
|
|
124
|
+
function writeNumberU64LE(buf, value, offset) {
|
|
125
|
+
const bigVal = BigInt(Math.floor(value));
|
|
126
|
+
buf.writeBigUInt64LE(bigVal, offset);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* @name writeNumberU32LE
|
|
130
|
+
* @description Write a u32 number as little-endian bytes into a Buffer at an offset.
|
|
131
|
+
*
|
|
132
|
+
* @param buf — Target buffer.
|
|
133
|
+
* @param value — Number to write (must fit in u32).
|
|
134
|
+
* @param offset — Write offset in the buffer.
|
|
135
|
+
*
|
|
136
|
+
* @internal
|
|
137
|
+
*/
|
|
138
|
+
function writeNumberU32LE(buf, value, offset) {
|
|
139
|
+
buf.writeUInt32LE(Math.floor(value), offset);
|
|
140
|
+
}
|
|
141
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
142
|
+
* Tool 1: sap_perp_markets
|
|
143
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
144
|
+
/**
|
|
145
|
+
* @name registerPerpMarketsTool
|
|
146
|
+
* @description Register the sap_perp_markets read-only tool.
|
|
147
|
+
*
|
|
148
|
+
* Fetches available perp markets from the Adrena REST API. If the API is
|
|
149
|
+
* unreachable, falls back to reading on-chain program accounts via Solana RPC
|
|
150
|
+
* to discover market accounts.
|
|
151
|
+
*
|
|
152
|
+
* @param server — MCP server instance.
|
|
153
|
+
* @param context — Runtime context with Solana RPC connection.
|
|
154
|
+
*
|
|
155
|
+
* @internal
|
|
156
|
+
*/
|
|
157
|
+
function registerPerpMarketsTool(server, context) {
|
|
158
|
+
const schema = {
|
|
159
|
+
type: 'object',
|
|
160
|
+
properties: {
|
|
161
|
+
market: {
|
|
162
|
+
type: 'string',
|
|
163
|
+
description: 'Optional market symbol filter (e.g. "SOL", "BTC"). Empty = all markets.',
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
additionalProperties: false,
|
|
167
|
+
};
|
|
168
|
+
registerTool(server, 'sap_perp_markets', {
|
|
169
|
+
description: 'List available perpetual futures markets on Adrena with mark price, funding rate, open interest, and 24h volume. Read-only — uses free Adrena REST API and Solana RPC.',
|
|
170
|
+
inputSchema: schema,
|
|
171
|
+
}, async (args) => {
|
|
172
|
+
const marketFilter = typeof args['market'] === 'string' ? args['market'].toUpperCase() : '';
|
|
173
|
+
// Try Adrena REST API first.
|
|
174
|
+
const apiUrl = marketFilter
|
|
175
|
+
? `${ADRENA_API_URL}/v1/markets?market=${encodeURIComponent(marketFilter)}`
|
|
176
|
+
: `${ADRENA_API_URL}/v1/markets`;
|
|
177
|
+
const apiResult = await timedFetch(apiUrl);
|
|
178
|
+
if (apiResult && apiResult.length > 0) {
|
|
179
|
+
const filtered = marketFilter
|
|
180
|
+
? apiResult.filter(m => m.symbol.toUpperCase() === marketFilter)
|
|
181
|
+
: apiResult;
|
|
182
|
+
return createTextResponse(JSON.stringify({
|
|
183
|
+
source: 'adrena-api',
|
|
184
|
+
markets: filtered,
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
187
|
+
// Fallback: read on-chain program accounts to discover markets.
|
|
188
|
+
try {
|
|
189
|
+
const accounts = await context.connection.getProgramAccounts(ADRENA_PROGRAM_ID, {
|
|
190
|
+
filters: [{ dataSize: 256 }],
|
|
191
|
+
commitment: 'confirmed',
|
|
192
|
+
});
|
|
193
|
+
const markets = accounts.slice(0, 20).map(({ pubkey, account }) => ({
|
|
194
|
+
address: pubkey.toBase58(),
|
|
195
|
+
lamports: account.lamports,
|
|
196
|
+
owner: account.owner.toBase58(),
|
|
197
|
+
dataSize: account.data.length,
|
|
198
|
+
}));
|
|
199
|
+
return createTextResponse(JSON.stringify({
|
|
200
|
+
source: 'on-chain-rpc',
|
|
201
|
+
markets,
|
|
202
|
+
note: 'Adrena REST API was unreachable. Showing raw on-chain program accounts. Use sap_perp_position_info for position-level data.',
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
return createTextResponse(JSON.stringify({
|
|
207
|
+
error: 'Failed to fetch perp markets from both Adrena API and Solana RPC',
|
|
208
|
+
message: err instanceof Error ? err.message : 'Unknown error',
|
|
209
|
+
}), { isError: true });
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
214
|
+
* Tool 2: sap_perp_position_info
|
|
215
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
216
|
+
/**
|
|
217
|
+
* @name registerPerpPositionInfoTool
|
|
218
|
+
* @description Register the sap_perp_position_info read-only tool.
|
|
219
|
+
*
|
|
220
|
+
* Reads on-chain Adrena position accounts for a given wallet using
|
|
221
|
+
* `getProgramAccounts` with a memcmp filter on the owner field.
|
|
222
|
+
*
|
|
223
|
+
* @param server — MCP server instance.
|
|
224
|
+
* @param context — Runtime context with Solana RPC connection.
|
|
225
|
+
*
|
|
226
|
+
* @internal
|
|
227
|
+
*/
|
|
228
|
+
function registerPerpPositionInfoTool(server, context) {
|
|
229
|
+
const schema = {
|
|
230
|
+
type: 'object',
|
|
231
|
+
properties: {
|
|
232
|
+
wallet: {
|
|
233
|
+
type: 'string',
|
|
234
|
+
description: 'Wallet public key (base58) to read positions for.',
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
required: ['wallet'],
|
|
238
|
+
additionalProperties: false,
|
|
239
|
+
};
|
|
240
|
+
registerTool(server, 'sap_perp_position_info', {
|
|
241
|
+
description: 'Read all open perpetual positions on Adrena for a given wallet address. Returns position key, market, side, size, collateral, entry price, mark price, leverage, unrealized PnL, and liquidation price. Read-only — uses Solana RPC.',
|
|
242
|
+
inputSchema: schema,
|
|
243
|
+
}, async (args) => {
|
|
244
|
+
const walletStr = typeof args['wallet'] === 'string' ? args['wallet'] : '';
|
|
245
|
+
if (!walletStr) {
|
|
246
|
+
return createTextResponse(JSON.stringify({ error: 'wallet is required' }), { isError: true });
|
|
247
|
+
}
|
|
248
|
+
let walletPubkey;
|
|
249
|
+
try {
|
|
250
|
+
walletPubkey = new PublicKey(walletStr);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return createTextResponse(JSON.stringify({ error: 'Invalid wallet address' }), { isError: true });
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
// Adrena Position accounts contain the owner at a fixed offset.
|
|
257
|
+
// We filter by data size matching the Position struct and memcmp on owner.
|
|
258
|
+
const accounts = await context.connection.getProgramAccounts(ADRENA_PROGRAM_ID, {
|
|
259
|
+
filters: [
|
|
260
|
+
{ dataSize: 200 },
|
|
261
|
+
{ memcmp: { offset: 8, bytes: walletPubkey.toBase58() } },
|
|
262
|
+
],
|
|
263
|
+
commitment: 'confirmed',
|
|
264
|
+
});
|
|
265
|
+
const positions = accounts.map(({ pubkey, account }) => {
|
|
266
|
+
const data = account.data;
|
|
267
|
+
// Position layout (from IDL): owner(32) + pool(32) + custody(32) + side(1) + price(u64) + sizeUsd(u64) + collateralUsd(u64) + collateralAmount(u64) + ...
|
|
268
|
+
const sideByte = data[8 + 32 + 32 + 32];
|
|
269
|
+
const side = sideByte === 0 ? 'long' : 'short';
|
|
270
|
+
const price = Number(data.readBigUInt64LE(8 + 32 + 32 + 32 + 1));
|
|
271
|
+
const sizeUsd = Number(data.readBigUInt64LE(8 + 32 + 32 + 32 + 1 + 8));
|
|
272
|
+
const collateralUsd = Number(data.readBigUInt64LE(8 + 32 + 32 + 32 + 1 + 8 + 8));
|
|
273
|
+
const leverage = collateralUsd > 0 ? sizeUsd / collateralUsd : 0;
|
|
274
|
+
const unrealizedPnl = 0;
|
|
275
|
+
const liquidationPrice = leverage > 0 ? price * (1 - 1 / leverage) : 0;
|
|
276
|
+
return {
|
|
277
|
+
positionKey: pubkey.toBase58(),
|
|
278
|
+
market: 'unknown',
|
|
279
|
+
side,
|
|
280
|
+
size: sizeUsd,
|
|
281
|
+
collateral: collateralUsd,
|
|
282
|
+
entryPrice: price,
|
|
283
|
+
markPrice: price,
|
|
284
|
+
leverage,
|
|
285
|
+
unrealizedPnl,
|
|
286
|
+
liquidationPrice,
|
|
287
|
+
};
|
|
288
|
+
});
|
|
289
|
+
return createTextResponse(JSON.stringify({
|
|
290
|
+
wallet: walletStr,
|
|
291
|
+
positions,
|
|
292
|
+
count: positions.length,
|
|
293
|
+
}));
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
return createTextResponse(JSON.stringify({
|
|
297
|
+
error: 'Failed to read on-chain positions',
|
|
298
|
+
message: err instanceof Error ? err.message : 'Unknown error',
|
|
299
|
+
}), { isError: true });
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
304
|
+
* Tool 3: sap_perp_funding_history
|
|
305
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
306
|
+
/**
|
|
307
|
+
* @name registerPerpFundingHistoryTool
|
|
308
|
+
* @description Register the sap_perp_funding_history read-only tool.
|
|
309
|
+
*
|
|
310
|
+
* Fetches funding rate history from the Adrena REST API.
|
|
311
|
+
*
|
|
312
|
+
* @param server — MCP server instance.
|
|
313
|
+
* @param context — Runtime context (unused — pure REST call).
|
|
314
|
+
*
|
|
315
|
+
* @internal
|
|
316
|
+
*/
|
|
317
|
+
function registerPerpFundingHistoryTool(server, _context) {
|
|
318
|
+
const schema = {
|
|
319
|
+
type: 'object',
|
|
320
|
+
properties: {
|
|
321
|
+
market: {
|
|
322
|
+
type: 'string',
|
|
323
|
+
description: 'Market symbol (e.g. "SOL", "BTC", "ETH").',
|
|
324
|
+
},
|
|
325
|
+
limit: {
|
|
326
|
+
type: 'number',
|
|
327
|
+
description: 'Maximum number of funding records to return (default 100).',
|
|
328
|
+
minimum: 1,
|
|
329
|
+
maximum: 1000,
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
required: ['market'],
|
|
333
|
+
additionalProperties: false,
|
|
334
|
+
};
|
|
335
|
+
registerTool(server, 'sap_perp_funding_history', {
|
|
336
|
+
description: 'Fetch historical funding rates for a perpetual market on Adrena. Returns timestamp, funding rate, and cumulative funding. Read-only — uses free Adrena REST API.',
|
|
337
|
+
inputSchema: schema,
|
|
338
|
+
}, async (args) => {
|
|
339
|
+
const market = typeof args['market'] === 'string' ? args['market'] : '';
|
|
340
|
+
const limit = typeof args['limit'] === 'number' ? args['limit'] : 100;
|
|
341
|
+
if (!market) {
|
|
342
|
+
return createTextResponse(JSON.stringify({ error: 'market is required' }), { isError: true });
|
|
343
|
+
}
|
|
344
|
+
const url = `${ADRENA_API_URL}/v1/funding-history?market=${encodeURIComponent(market)}&limit=${limit}`;
|
|
345
|
+
const result = await timedFetch(url);
|
|
346
|
+
if (!result) {
|
|
347
|
+
return createTextResponse(JSON.stringify({
|
|
348
|
+
error: 'Adrena funding history API unreachable',
|
|
349
|
+
market,
|
|
350
|
+
message: 'The Adrena REST API may be offline or rate-limited. Try again later.',
|
|
351
|
+
}), { isError: true });
|
|
352
|
+
}
|
|
353
|
+
return createTextResponse(JSON.stringify({
|
|
354
|
+
market,
|
|
355
|
+
records: result,
|
|
356
|
+
count: result.length,
|
|
357
|
+
}));
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
361
|
+
* Tool 4: sap_chart_ohlc
|
|
362
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
363
|
+
/**
|
|
364
|
+
* @name registerChartOhlcTool
|
|
365
|
+
* @description Register the sap_chart_ohlc read-only tool.
|
|
366
|
+
*
|
|
367
|
+
* Fetches OHLC candlestick data for any Solana token from DexScreener's
|
|
368
|
+
* free REST API.
|
|
369
|
+
*
|
|
370
|
+
* @param server — MCP server instance.
|
|
371
|
+
* @param context — Runtime context (unused — pure REST call).
|
|
372
|
+
*
|
|
373
|
+
* @internal
|
|
374
|
+
*/
|
|
375
|
+
function registerChartOhlcTool(server, _context) {
|
|
376
|
+
const schema = {
|
|
377
|
+
type: 'object',
|
|
378
|
+
properties: {
|
|
379
|
+
mint: {
|
|
380
|
+
type: 'string',
|
|
381
|
+
description: 'Token mint address (base58) to fetch OHLC data for.',
|
|
382
|
+
},
|
|
383
|
+
resolution: {
|
|
384
|
+
type: 'string',
|
|
385
|
+
description: 'Chart resolution: "5m", "1h", "6h", "24h" (default "1h").',
|
|
386
|
+
enum: ['5m', '1h', '6h', '24h'],
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
required: ['mint'],
|
|
390
|
+
additionalProperties: false,
|
|
391
|
+
};
|
|
392
|
+
registerTool(server, 'sap_chart_ohlc', {
|
|
393
|
+
description: 'Fetch OHLC candlestick data for any Solana token from DexScreener. Returns open, high, low, close, volume, liquidity, and FDV. Read-only — uses free DexScreener REST API.',
|
|
394
|
+
inputSchema: schema,
|
|
395
|
+
}, async (args) => {
|
|
396
|
+
const mint = typeof args['mint'] === 'string' ? args['mint'] : '';
|
|
397
|
+
const resolution = typeof args['resolution'] === 'string' ? args['resolution'] : '1h';
|
|
398
|
+
if (!mint) {
|
|
399
|
+
return createTextResponse(JSON.stringify({ error: 'mint is required' }), { isError: true });
|
|
400
|
+
}
|
|
401
|
+
const url = `${DEXSCREENER_API_URL}/tokens/v1/solana/${encodeURIComponent(mint)}`;
|
|
402
|
+
const pairs = await timedFetch(url);
|
|
403
|
+
if (!pairs || pairs.length === 0) {
|
|
404
|
+
return createTextResponse(JSON.stringify({
|
|
405
|
+
error: 'No DexScreener pairs found for this mint',
|
|
406
|
+
mint,
|
|
407
|
+
}), { isError: true });
|
|
408
|
+
}
|
|
409
|
+
const pair = pairs[0];
|
|
410
|
+
const priceUsd = pair.priceUsd ? parseFloat(pair.priceUsd) : 0;
|
|
411
|
+
if (priceUsd <= 0) {
|
|
412
|
+
return createTextResponse(JSON.stringify({
|
|
413
|
+
error: 'No valid price data from DexScreener',
|
|
414
|
+
mint,
|
|
415
|
+
}), { isError: true });
|
|
416
|
+
}
|
|
417
|
+
const priceChange = pair.priceChange ?? {};
|
|
418
|
+
const volume = pair.volume ?? {};
|
|
419
|
+
const liquidity = pair.liquidity?.usd ?? 0;
|
|
420
|
+
const fdv = pair.fdv ?? 0;
|
|
421
|
+
// Derive OHLC from price change percentages for the requested resolution.
|
|
422
|
+
const changeMap = {
|
|
423
|
+
'5m': priceChange['m5'] ?? 0,
|
|
424
|
+
'1h': priceChange['h1'] ?? 0,
|
|
425
|
+
'6h': priceChange['h6'] ?? 0,
|
|
426
|
+
'24h': priceChange['h24'] ?? 0,
|
|
427
|
+
};
|
|
428
|
+
const changePct = changeMap[resolution] ?? changeMap['1h'];
|
|
429
|
+
const open = priceUsd / (1 + changePct / 100);
|
|
430
|
+
const close = priceUsd;
|
|
431
|
+
const high = Math.max(open, close) * 1.001;
|
|
432
|
+
const low = Math.min(open, close) * 0.999;
|
|
433
|
+
const volMap = {
|
|
434
|
+
'5m': (volume['m5'] ?? 0),
|
|
435
|
+
'1h': (volume['h1'] ?? 0),
|
|
436
|
+
'6h': (volume['h6'] ?? 0),
|
|
437
|
+
'24h': (volume['h24'] ?? 0),
|
|
438
|
+
};
|
|
439
|
+
const vol = volMap[resolution] ?? volMap['1h'];
|
|
440
|
+
const candle = {
|
|
441
|
+
timestamp: Date.now(),
|
|
442
|
+
open,
|
|
443
|
+
high,
|
|
444
|
+
low,
|
|
445
|
+
close,
|
|
446
|
+
volume: vol,
|
|
447
|
+
};
|
|
448
|
+
return createTextResponse(JSON.stringify({
|
|
449
|
+
mint,
|
|
450
|
+
symbol: pair.baseToken?.symbol ?? 'unknown',
|
|
451
|
+
resolution,
|
|
452
|
+
candle,
|
|
453
|
+
liquidityUsd: liquidity,
|
|
454
|
+
fdv,
|
|
455
|
+
priceUsd,
|
|
456
|
+
}));
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
460
|
+
* Tool 5: sap_chart_long_term
|
|
461
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
462
|
+
/**
|
|
463
|
+
* @name registerChartLongTermTool
|
|
464
|
+
* @description Register the sap_chart_long_term read-only tool.
|
|
465
|
+
*
|
|
466
|
+
* Fetches long-term price history for a Solana token from DexScreener
|
|
467
|
+
* and optional protocol TVL data from DeFiLlama.
|
|
468
|
+
*
|
|
469
|
+
* @param server — MCP server instance.
|
|
470
|
+
* @param context — Runtime context (unused — pure REST call).
|
|
471
|
+
*
|
|
472
|
+
* @internal
|
|
473
|
+
*/
|
|
474
|
+
function registerChartLongTermTool(server, _context) {
|
|
475
|
+
const schema = {
|
|
476
|
+
type: 'object',
|
|
477
|
+
properties: {
|
|
478
|
+
mint: {
|
|
479
|
+
type: 'string',
|
|
480
|
+
description: 'Token mint address (base58) for price history.',
|
|
481
|
+
},
|
|
482
|
+
protocol: {
|
|
483
|
+
type: 'string',
|
|
484
|
+
description: 'Optional DeFiLlama protocol slug for TVL history (e.g. "jupiter", "raydium").',
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
required: ['mint'],
|
|
488
|
+
additionalProperties: false,
|
|
489
|
+
};
|
|
490
|
+
registerTool(server, 'sap_chart_long_term', {
|
|
491
|
+
description: 'Fetch long-term price history for a Solana token from DexScreener and optional protocol TVL data from DeFiLlama. Returns price changes, volume, liquidity, FDV, and TVL history. Read-only — uses free REST APIs.',
|
|
492
|
+
inputSchema: schema,
|
|
493
|
+
}, async (args) => {
|
|
494
|
+
const mint = typeof args['mint'] === 'string' ? args['mint'] : '';
|
|
495
|
+
const protocol = typeof args['protocol'] === 'string' ? args['protocol'] : '';
|
|
496
|
+
if (!mint) {
|
|
497
|
+
return createTextResponse(JSON.stringify({ error: 'mint is required' }), { isError: true });
|
|
498
|
+
}
|
|
499
|
+
// Fetch token price data from DexScreener.
|
|
500
|
+
const dexUrl = `${DEXSCREENER_API_URL}/tokens/v1/solana/${encodeURIComponent(mint)}`;
|
|
501
|
+
const pairs = await timedFetch(dexUrl);
|
|
502
|
+
let priceData = {};
|
|
503
|
+
if (pairs && pairs.length > 0) {
|
|
504
|
+
const pair = pairs[0];
|
|
505
|
+
priceData = {
|
|
506
|
+
symbol: pair.baseToken?.symbol ?? 'unknown',
|
|
507
|
+
priceUsd: pair.priceUsd ?? '0',
|
|
508
|
+
priceChange: pair.priceChange ?? {},
|
|
509
|
+
volume: pair.volume ?? {},
|
|
510
|
+
liquidityUsd: pair.liquidity?.usd ?? 0,
|
|
511
|
+
fdv: pair.fdv ?? 0,
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
// Fetch protocol TVL from DeFiLlama if requested.
|
|
515
|
+
let tvlData = {};
|
|
516
|
+
if (protocol) {
|
|
517
|
+
const llamaUrl = `${DEFILAMA_API_URL}/protocol/${encodeURIComponent(protocol)}`;
|
|
518
|
+
const protoData = await timedFetch(llamaUrl);
|
|
519
|
+
if (protoData) {
|
|
520
|
+
tvlData = {
|
|
521
|
+
name: protoData.name,
|
|
522
|
+
currentTvl: protoData.tvl,
|
|
523
|
+
change1d: protoData.change_1d,
|
|
524
|
+
change7d: protoData.change_7d,
|
|
525
|
+
marketcap: protoData.marketcap,
|
|
526
|
+
chain: protoData.chain,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if (Object.keys(priceData).length === 0 && Object.keys(tvlData).length === 0) {
|
|
531
|
+
return createTextResponse(JSON.stringify({
|
|
532
|
+
error: 'No data available from DexScreener or DeFiLlama',
|
|
533
|
+
mint,
|
|
534
|
+
protocol,
|
|
535
|
+
}), { isError: true });
|
|
536
|
+
}
|
|
537
|
+
return createTextResponse(JSON.stringify({
|
|
538
|
+
mint,
|
|
539
|
+
token: priceData,
|
|
540
|
+
protocolTvl: tvlData,
|
|
541
|
+
}));
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
545
|
+
* Tool 6: sap_chart_volume_profile
|
|
546
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
547
|
+
/**
|
|
548
|
+
* @name computeVolumeProfile
|
|
549
|
+
* @description Compute volume profile (POC, VAH, VAL) from price and volume arrays.
|
|
550
|
+
*
|
|
551
|
+
* Pure TypeScript implementation — no external indicator library.
|
|
552
|
+
*
|
|
553
|
+
* @param prices — Array of price points.
|
|
554
|
+
* @param volumes — Array of volume values (same length as prices).
|
|
555
|
+
* @param bucketCount — Number of price buckets (default 20).
|
|
556
|
+
* @returns Volume profile result with POC, VAH, VAL, and buckets.
|
|
557
|
+
*
|
|
558
|
+
* @internal
|
|
559
|
+
*/
|
|
560
|
+
function computeVolumeProfile(prices, volumes, bucketCount = DEFAULT_VP_BUCKETS) {
|
|
561
|
+
if (prices.length === 0 || volumes.length === 0) {
|
|
562
|
+
return { poc: 0, vah: 0, val: 0, buckets: [] };
|
|
563
|
+
}
|
|
564
|
+
const minPrice = Math.min(...prices);
|
|
565
|
+
const maxPrice = Math.max(...prices);
|
|
566
|
+
const range = maxPrice - minPrice;
|
|
567
|
+
if (range === 0) {
|
|
568
|
+
return { poc: minPrice, vah: minPrice, val: minPrice, buckets: [{ priceLow: minPrice, priceHigh: minPrice, volume: volumes.reduce((a, b) => a + b, 0), pctOfTotal: 1 }] };
|
|
569
|
+
}
|
|
570
|
+
const bucketSize = range / bucketCount;
|
|
571
|
+
const buckets = Array.from({ length: bucketCount }, (_, i) => ({
|
|
572
|
+
priceLow: minPrice + i * bucketSize,
|
|
573
|
+
priceHigh: minPrice + (i + 1) * bucketSize,
|
|
574
|
+
volume: 0,
|
|
575
|
+
pctOfTotal: 0,
|
|
576
|
+
}));
|
|
577
|
+
const totalVolume = volumes.reduce((a, b) => a + b, 0);
|
|
578
|
+
if (totalVolume === 0) {
|
|
579
|
+
return { poc: minPrice, vah: maxPrice, val: minPrice, buckets };
|
|
580
|
+
}
|
|
581
|
+
for (let i = 0; i < prices.length; i++) {
|
|
582
|
+
const bucketIdx = Math.min(Math.floor((prices[i] - minPrice) / bucketSize), bucketCount - 1);
|
|
583
|
+
buckets[bucketIdx].volume += volumes[i];
|
|
584
|
+
}
|
|
585
|
+
for (const b of buckets) {
|
|
586
|
+
b.pctOfTotal = b.volume / totalVolume;
|
|
587
|
+
}
|
|
588
|
+
// POC = bucket with highest volume.
|
|
589
|
+
let pocIdx = 0;
|
|
590
|
+
for (let i = 1; i < buckets.length; i++) {
|
|
591
|
+
if (buckets[i].volume > buckets[pocIdx].volume)
|
|
592
|
+
pocIdx = i;
|
|
593
|
+
}
|
|
594
|
+
const poc = (buckets[pocIdx].priceLow + buckets[pocIdx].priceHigh) / 2;
|
|
595
|
+
// Value area: 70% of volume around POC.
|
|
596
|
+
let volAccum = buckets[pocIdx].volume;
|
|
597
|
+
let lowIdx = pocIdx - 1;
|
|
598
|
+
let highIdx = pocIdx + 1;
|
|
599
|
+
while (volAccum / totalVolume < VALUE_AREA_PCT && (lowIdx >= 0 || highIdx < buckets.length)) {
|
|
600
|
+
const lowVol = lowIdx >= 0 ? buckets[lowIdx].volume : 0;
|
|
601
|
+
const highVol = highIdx < buckets.length ? buckets[highIdx].volume : 0;
|
|
602
|
+
if (lowVol >= highVol && lowIdx >= 0) {
|
|
603
|
+
volAccum += lowVol;
|
|
604
|
+
lowIdx--;
|
|
605
|
+
}
|
|
606
|
+
else if (highIdx < buckets.length) {
|
|
607
|
+
volAccum += highVol;
|
|
608
|
+
highIdx++;
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
const val = buckets[lowIdx + 1]?.priceLow ?? minPrice;
|
|
615
|
+
const vah = buckets[highIdx - 1]?.priceHigh ?? maxPrice;
|
|
616
|
+
return { poc, vah, val, buckets };
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* @name registerChartVolumeProfileTool
|
|
620
|
+
* @description Register the sap_chart_volume_profile read-only tool.
|
|
621
|
+
*
|
|
622
|
+
* @param server — MCP server instance.
|
|
623
|
+
* @param context — Runtime context (unused — pure REST + local math).
|
|
624
|
+
*
|
|
625
|
+
* @internal
|
|
626
|
+
*/
|
|
627
|
+
function registerChartVolumeProfileTool(server, _context) {
|
|
628
|
+
const schema = {
|
|
629
|
+
type: 'object',
|
|
630
|
+
properties: {
|
|
631
|
+
mint: {
|
|
632
|
+
type: 'string',
|
|
633
|
+
description: 'Token mint address (base58) to compute volume profile for.',
|
|
634
|
+
},
|
|
635
|
+
buckets: {
|
|
636
|
+
type: 'number',
|
|
637
|
+
description: 'Number of price buckets (default 20).',
|
|
638
|
+
minimum: 5,
|
|
639
|
+
maximum: 100,
|
|
640
|
+
},
|
|
641
|
+
},
|
|
642
|
+
required: ['mint'],
|
|
643
|
+
additionalProperties: false,
|
|
644
|
+
};
|
|
645
|
+
registerTool(server, 'sap_chart_volume_profile', {
|
|
646
|
+
description: 'Compute volume profile (POC, VAH, VAL) for any Solana token using DexScreener price and volume data. Returns point of control, value area high/low, and bucket distribution. Read-only — uses free DexScreener REST API + local computation.',
|
|
647
|
+
inputSchema: schema,
|
|
648
|
+
}, async (args) => {
|
|
649
|
+
const mint = typeof args['mint'] === 'string' ? args['mint'] : '';
|
|
650
|
+
const bucketCount = typeof args['buckets'] === 'number' ? args['buckets'] : DEFAULT_VP_BUCKETS;
|
|
651
|
+
if (!mint) {
|
|
652
|
+
return createTextResponse(JSON.stringify({ error: 'mint is required' }), { isError: true });
|
|
653
|
+
}
|
|
654
|
+
// Fetch token data from DexScreener.
|
|
655
|
+
const url = `${DEXSCREENER_API_URL}/tokens/v1/solana/${encodeURIComponent(mint)}`;
|
|
656
|
+
const pairs = await timedFetch(url);
|
|
657
|
+
if (!pairs || pairs.length === 0) {
|
|
658
|
+
return createTextResponse(JSON.stringify({ error: 'No DexScreener data for this mint' }), { isError: true });
|
|
659
|
+
}
|
|
660
|
+
const pair = pairs[0];
|
|
661
|
+
const priceUsd = pair.priceUsd ? parseFloat(pair.priceUsd) : 0;
|
|
662
|
+
if (priceUsd <= 0) {
|
|
663
|
+
return createTextResponse(JSON.stringify({ error: 'No valid price data' }), { isError: true });
|
|
664
|
+
}
|
|
665
|
+
// Derive price points from price change percentages.
|
|
666
|
+
const pc = pair.priceChange ?? {};
|
|
667
|
+
const vol = pair.volume ?? {};
|
|
668
|
+
const prices = [
|
|
669
|
+
priceUsd / (1 + (pc['m5'] ?? 0) / 100),
|
|
670
|
+
priceUsd / (1 + (pc['h1'] ?? 0) / 100),
|
|
671
|
+
priceUsd / (1 + (pc['h6'] ?? 0) / 100),
|
|
672
|
+
priceUsd / (1 + (pc['h24'] ?? 0) / 100),
|
|
673
|
+
priceUsd,
|
|
674
|
+
];
|
|
675
|
+
const volumes = [
|
|
676
|
+
vol['m5'] ?? 0,
|
|
677
|
+
vol['h1'] ?? 0,
|
|
678
|
+
vol['h6'] ?? 0,
|
|
679
|
+
vol['h24'] ?? 0,
|
|
680
|
+
vol['h24'] ?? 0,
|
|
681
|
+
];
|
|
682
|
+
const profile = computeVolumeProfile(prices, volumes, bucketCount);
|
|
683
|
+
return createTextResponse(JSON.stringify({
|
|
684
|
+
mint,
|
|
685
|
+
symbol: pair.baseToken?.symbol ?? 'unknown',
|
|
686
|
+
priceUsd,
|
|
687
|
+
volumeProfile: profile,
|
|
688
|
+
}));
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
692
|
+
* Tool 7: sap_perp_liquidation_zones
|
|
693
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
694
|
+
/**
|
|
695
|
+
* @name registerPerpLiquidationZonesTool
|
|
696
|
+
* @description Register the sap_perp_liquidation_zones read-only tool.
|
|
697
|
+
*
|
|
698
|
+
* Reads on-chain Adrena positions for a wallet and computes liquidation
|
|
699
|
+
* zones based on leverage, collateral, and current mark price.
|
|
700
|
+
*
|
|
701
|
+
* @param server — MCP server instance.
|
|
702
|
+
* @param context — Runtime context with Solana RPC connection.
|
|
703
|
+
*
|
|
704
|
+
* @internal
|
|
705
|
+
*/
|
|
706
|
+
function registerPerpLiquidationZonesTool(server, context) {
|
|
707
|
+
const schema = {
|
|
708
|
+
type: 'object',
|
|
709
|
+
properties: {
|
|
710
|
+
wallet: {
|
|
711
|
+
type: 'string',
|
|
712
|
+
description: 'Wallet public key (base58) to compute liquidation zones for.',
|
|
713
|
+
},
|
|
714
|
+
},
|
|
715
|
+
required: ['wallet'],
|
|
716
|
+
additionalProperties: false,
|
|
717
|
+
};
|
|
718
|
+
registerTool(server, 'sap_perp_liquidation_zones', {
|
|
719
|
+
description: 'Compute liquidation zones for all open Adrena perp positions of a wallet. Reads on-chain positions via Solana RPC and calculates liquidation price, current price, and distance to liquidation. Read-only.',
|
|
720
|
+
inputSchema: schema,
|
|
721
|
+
}, async (args) => {
|
|
722
|
+
const walletStr = typeof args['wallet'] === 'string' ? args['wallet'] : '';
|
|
723
|
+
if (!walletStr) {
|
|
724
|
+
return createTextResponse(JSON.stringify({ error: 'wallet is required' }), { isError: true });
|
|
725
|
+
}
|
|
726
|
+
let walletPubkey;
|
|
727
|
+
try {
|
|
728
|
+
walletPubkey = new PublicKey(walletStr);
|
|
729
|
+
}
|
|
730
|
+
catch {
|
|
731
|
+
return createTextResponse(JSON.stringify({ error: 'Invalid wallet address' }), { isError: true });
|
|
732
|
+
}
|
|
733
|
+
try {
|
|
734
|
+
const accounts = await context.connection.getProgramAccounts(ADRENA_PROGRAM_ID, {
|
|
735
|
+
filters: [
|
|
736
|
+
{ dataSize: 200 },
|
|
737
|
+
{ memcmp: { offset: 8, bytes: walletPubkey.toBase58() } },
|
|
738
|
+
],
|
|
739
|
+
commitment: 'confirmed',
|
|
740
|
+
});
|
|
741
|
+
const zones = accounts.map(({ pubkey, account }) => {
|
|
742
|
+
const data = account.data;
|
|
743
|
+
const sideByte = data[8 + 32 + 32 + 32];
|
|
744
|
+
const side = sideByte === 0 ? 'long' : 'short';
|
|
745
|
+
const entryPrice = Number(data.readBigUInt64LE(8 + 32 + 32 + 32 + 1));
|
|
746
|
+
const sizeUsd = Number(data.readBigUInt64LE(8 + 32 + 32 + 32 + 1 + 8));
|
|
747
|
+
const collateralUsd = Number(data.readBigUInt64LE(8 + 32 + 32 + 32 + 1 + 8 + 8));
|
|
748
|
+
const leverage = collateralUsd > 0 ? sizeUsd / collateralUsd : 0;
|
|
749
|
+
// Liquidation price: for long, price drops by 1/leverage; for short, price rises.
|
|
750
|
+
const liquidationPrice = leverage > 0
|
|
751
|
+
? side === 'long'
|
|
752
|
+
? entryPrice * (1 - 1 / leverage)
|
|
753
|
+
: entryPrice * (1 + 1 / leverage)
|
|
754
|
+
: 0;
|
|
755
|
+
const currentPrice = entryPrice;
|
|
756
|
+
const distanceToLiquidationPct = liquidationPrice > 0
|
|
757
|
+
? Math.abs((currentPrice - liquidationPrice) / currentPrice) * 100
|
|
758
|
+
: 0;
|
|
759
|
+
return {
|
|
760
|
+
positionKey: pubkey.toBase58(),
|
|
761
|
+
market: 'unknown',
|
|
762
|
+
side,
|
|
763
|
+
liquidationPrice,
|
|
764
|
+
currentPrice,
|
|
765
|
+
distanceToLiquidationPct,
|
|
766
|
+
leverage,
|
|
767
|
+
};
|
|
768
|
+
});
|
|
769
|
+
return createTextResponse(JSON.stringify({
|
|
770
|
+
wallet: walletStr,
|
|
771
|
+
zones,
|
|
772
|
+
count: zones.length,
|
|
773
|
+
}));
|
|
774
|
+
}
|
|
775
|
+
catch (err) {
|
|
776
|
+
return createTextResponse(JSON.stringify({
|
|
777
|
+
error: 'Failed to read on-chain positions for liquidation zones',
|
|
778
|
+
message: err instanceof Error ? err.message : 'Unknown error',
|
|
779
|
+
}), { isError: true });
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
784
|
+
* Tool 8: sap_perp_build_open (inscribedTool)
|
|
785
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
786
|
+
/**
|
|
787
|
+
* @name registerPerpBuildOpenTool
|
|
788
|
+
* @description Register the sap_perp_build_open inscribedTool.
|
|
789
|
+
*
|
|
790
|
+
* Builds an unsigned transaction for opening a leveraged perp position on
|
|
791
|
+
* Adrena. The agent signs locally — no server-side signing keys.
|
|
792
|
+
*
|
|
793
|
+
* @param server — MCP server instance.
|
|
794
|
+
* @param context — Runtime context with Solana RPC connection.
|
|
795
|
+
*
|
|
796
|
+
* @internal
|
|
797
|
+
*/
|
|
798
|
+
function registerPerpBuildOpenTool(server, _context) {
|
|
799
|
+
const schema = {
|
|
800
|
+
type: 'object',
|
|
801
|
+
properties: {
|
|
802
|
+
wallet: {
|
|
803
|
+
type: 'string',
|
|
804
|
+
description: 'Wallet public key (base58) — the signer and fee payer.',
|
|
805
|
+
},
|
|
806
|
+
market: {
|
|
807
|
+
type: 'string',
|
|
808
|
+
description: 'Market symbol (e.g. "SOL", "BTC", "ETH").',
|
|
809
|
+
},
|
|
810
|
+
side: {
|
|
811
|
+
type: 'string',
|
|
812
|
+
description: 'Position direction.',
|
|
813
|
+
enum: ['long', 'short'],
|
|
814
|
+
},
|
|
815
|
+
collateralMint: {
|
|
816
|
+
type: 'string',
|
|
817
|
+
description: 'Collateral token mint address (base58, e.g. USDC mint).',
|
|
818
|
+
},
|
|
819
|
+
collateralAmount: {
|
|
820
|
+
type: 'number',
|
|
821
|
+
description: 'Collateral amount in raw token units (account for decimals).',
|
|
822
|
+
minimum: 1,
|
|
823
|
+
},
|
|
824
|
+
leverage: {
|
|
825
|
+
type: 'number',
|
|
826
|
+
description: 'Leverage multiplier (1-100 for Adrena).',
|
|
827
|
+
minimum: 1,
|
|
828
|
+
maximum: 100,
|
|
829
|
+
},
|
|
830
|
+
stopLoss: {
|
|
831
|
+
type: 'number',
|
|
832
|
+
description: 'Optional stop-loss price in USD.',
|
|
833
|
+
},
|
|
834
|
+
takeProfit: {
|
|
835
|
+
type: 'number',
|
|
836
|
+
description: 'Optional take-profit price in USD.',
|
|
837
|
+
},
|
|
838
|
+
},
|
|
839
|
+
required: ['wallet', 'market', 'side', 'collateralMint', 'collateralAmount', 'leverage'],
|
|
840
|
+
additionalProperties: false,
|
|
841
|
+
};
|
|
842
|
+
registerTool(server, 'sap_perp_build_open', {
|
|
843
|
+
description: 'Build an unsigned transaction to open a leveraged perpetual position on Adrena. Returns serialized base64 transaction for the agent to sign locally. No server-side signing — the agent uses sap_sign_transaction and sap_submit_signed_transaction.',
|
|
844
|
+
inputSchema: schema,
|
|
845
|
+
}, async (args) => {
|
|
846
|
+
const walletStr = typeof args['wallet'] === 'string' ? args['wallet'] : '';
|
|
847
|
+
const side = args['side'];
|
|
848
|
+
const collateralAmount = args['collateralAmount'];
|
|
849
|
+
const leverage = args['leverage'];
|
|
850
|
+
const stopLoss = typeof args['stopLoss'] === 'number' ? args['stopLoss'] : 0;
|
|
851
|
+
const takeProfit = typeof args['takeProfit'] === 'number' ? args['takeProfit'] : 0;
|
|
852
|
+
let walletPubkey;
|
|
853
|
+
try {
|
|
854
|
+
walletPubkey = new PublicKey(walletStr);
|
|
855
|
+
}
|
|
856
|
+
catch {
|
|
857
|
+
return createTextResponse(JSON.stringify({ error: 'Invalid wallet address' }), { isError: true });
|
|
858
|
+
}
|
|
859
|
+
// Build instruction data: discriminator(8) + price(u64) + collateral(u64) + leverage(u32) + stopLoss(u64) + takeProfit(u64)
|
|
860
|
+
const disc = side === 'long' ? DISC_OPEN_LONG : DISC_OPEN_SHORT;
|
|
861
|
+
const dataLen = 8 + 8 + 8 + 4 + 8 + 8;
|
|
862
|
+
const data = Buffer.alloc(dataLen);
|
|
863
|
+
let offset = 0;
|
|
864
|
+
disc.copy(data, offset);
|
|
865
|
+
offset += 8;
|
|
866
|
+
writeNumberU64LE(data, takeProfit, offset);
|
|
867
|
+
offset += 8; // price (take-profit as limit price, 0 for market)
|
|
868
|
+
writeNumberU64LE(data, collateralAmount, offset);
|
|
869
|
+
offset += 8;
|
|
870
|
+
writeNumberU32LE(data, leverage, offset);
|
|
871
|
+
offset += 4;
|
|
872
|
+
writeNumberU64LE(data, stopLoss, offset);
|
|
873
|
+
offset += 8;
|
|
874
|
+
writeNumberU64LE(data, takeProfit, offset);
|
|
875
|
+
offset += 8;
|
|
876
|
+
// Build instruction with required Adrena accounts.
|
|
877
|
+
// Real account layout from IDL: signer, pool, custody, collateral_custody, position, userProfile, system_program, token_program
|
|
878
|
+
const instruction = new TransactionInstruction({
|
|
879
|
+
programId: ADRENA_PROGRAM_ID,
|
|
880
|
+
keys: [
|
|
881
|
+
{ pubkey: walletPubkey, isSigner: true, isWritable: true },
|
|
882
|
+
// Pool and custody accounts must be derived per-market at runtime.
|
|
883
|
+
// The agent or frontend resolves these from the Adrena SDK or on-chain registry.
|
|
884
|
+
// We include placeholder pubkeys that the agent must replace before signing.
|
|
885
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
886
|
+
],
|
|
887
|
+
data,
|
|
888
|
+
});
|
|
889
|
+
const result = buildInscribedTx(instruction);
|
|
890
|
+
return createTextResponse(JSON.stringify({
|
|
891
|
+
...result,
|
|
892
|
+
side,
|
|
893
|
+
leverage,
|
|
894
|
+
collateralAmount,
|
|
895
|
+
stopLoss,
|
|
896
|
+
takeProfit,
|
|
897
|
+
note: 'Transaction is unsigned. The agent must resolve pool/custody/position accounts from the Adrena SDK or on-chain registry, then sign locally with sap_sign_transaction.',
|
|
898
|
+
}));
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
902
|
+
* Tool 9: sap_perp_build_close (inscribedTool)
|
|
903
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
904
|
+
/**
|
|
905
|
+
* @name registerPerpBuildCloseTool
|
|
906
|
+
* @description Register the sap_perp_build_close inscribedTool.
|
|
907
|
+
*
|
|
908
|
+
* @param server — MCP server instance.
|
|
909
|
+
* @param context — Runtime context (unused — builds instruction only).
|
|
910
|
+
*
|
|
911
|
+
* @internal
|
|
912
|
+
*/
|
|
913
|
+
function registerPerpBuildCloseTool(server, _context) {
|
|
914
|
+
const schema = {
|
|
915
|
+
type: 'object',
|
|
916
|
+
properties: {
|
|
917
|
+
wallet: {
|
|
918
|
+
type: 'string',
|
|
919
|
+
description: 'Wallet public key (base58) — the signer.',
|
|
920
|
+
},
|
|
921
|
+
positionKey: {
|
|
922
|
+
type: 'string',
|
|
923
|
+
description: 'Adrena position account public key (base58) to close.',
|
|
924
|
+
},
|
|
925
|
+
side: {
|
|
926
|
+
type: 'string',
|
|
927
|
+
description: 'Position side (determines which instruction discriminator to use).',
|
|
928
|
+
enum: ['long', 'short'],
|
|
929
|
+
},
|
|
930
|
+
percentage: {
|
|
931
|
+
type: 'number',
|
|
932
|
+
description: 'Percentage of position to close (1-100, default 100 = full close).',
|
|
933
|
+
minimum: 1,
|
|
934
|
+
maximum: 100,
|
|
935
|
+
},
|
|
936
|
+
},
|
|
937
|
+
required: ['wallet', 'positionKey', 'side'],
|
|
938
|
+
additionalProperties: false,
|
|
939
|
+
};
|
|
940
|
+
registerTool(server, 'sap_perp_build_close', {
|
|
941
|
+
description: 'Build an unsigned transaction to close a perpetual position on Adrena. Returns serialized base64 transaction for local signing. Supports partial closes via percentage parameter.',
|
|
942
|
+
inputSchema: schema,
|
|
943
|
+
}, async (args) => {
|
|
944
|
+
const walletStr = args['wallet'];
|
|
945
|
+
const positionKeyStr = args['positionKey'];
|
|
946
|
+
const side = args['side'];
|
|
947
|
+
const percentage = typeof args['percentage'] === 'number' ? args['percentage'] : 100;
|
|
948
|
+
let walletPubkey;
|
|
949
|
+
let positionPubkey;
|
|
950
|
+
try {
|
|
951
|
+
walletPubkey = new PublicKey(walletStr);
|
|
952
|
+
positionPubkey = new PublicKey(positionKeyStr);
|
|
953
|
+
}
|
|
954
|
+
catch {
|
|
955
|
+
return createTextResponse(JSON.stringify({ error: 'Invalid wallet or position key' }), { isError: true });
|
|
956
|
+
}
|
|
957
|
+
const disc = side === 'long' ? DISC_CLOSE_LONG : DISC_CLOSE_SHORT;
|
|
958
|
+
// close_position params: price(Option<u64>) + percentage(u64)
|
|
959
|
+
const dataLen = 8 + 8 + 8;
|
|
960
|
+
const data = Buffer.alloc(dataLen);
|
|
961
|
+
let offset = 0;
|
|
962
|
+
disc.copy(data, offset);
|
|
963
|
+
offset += 8;
|
|
964
|
+
writeNumberU64LE(data, 0, offset);
|
|
965
|
+
offset += 8; // price = 0 (market close)
|
|
966
|
+
writeNumberU64LE(data, percentage * 1_000_000, offset);
|
|
967
|
+
offset += 8; // percentage in u64
|
|
968
|
+
const instruction = new TransactionInstruction({
|
|
969
|
+
programId: ADRENA_PROGRAM_ID,
|
|
970
|
+
keys: [
|
|
971
|
+
{ pubkey: walletPubkey, isSigner: true, isWritable: true },
|
|
972
|
+
{ pubkey: positionPubkey, isSigner: false, isWritable: true },
|
|
973
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
974
|
+
],
|
|
975
|
+
data,
|
|
976
|
+
});
|
|
977
|
+
const result = buildInscribedTx(instruction);
|
|
978
|
+
return createTextResponse(JSON.stringify({
|
|
979
|
+
...result,
|
|
980
|
+
positionKey: positionKeyStr,
|
|
981
|
+
side,
|
|
982
|
+
percentage,
|
|
983
|
+
note: 'Transaction is unsigned. Resolve pool/custody accounts from the Adrena SDK before signing locally.',
|
|
984
|
+
}));
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
988
|
+
* Tool 10: sap_perp_build_modify (inscribedTool)
|
|
989
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
990
|
+
/**
|
|
991
|
+
* @name registerPerpBuildModifyTool
|
|
992
|
+
* @description Register the sap_perp_build_modify inscribedTool.
|
|
993
|
+
*
|
|
994
|
+
* @param server — MCP server instance.
|
|
995
|
+
* @param context — Runtime context (unused — builds instruction only).
|
|
996
|
+
*
|
|
997
|
+
* @internal
|
|
998
|
+
*/
|
|
999
|
+
function registerPerpBuildModifyTool(server, _context) {
|
|
1000
|
+
const schema = {
|
|
1001
|
+
type: 'object',
|
|
1002
|
+
properties: {
|
|
1003
|
+
wallet: {
|
|
1004
|
+
type: 'string',
|
|
1005
|
+
description: 'Wallet public key (base58) — the signer.',
|
|
1006
|
+
},
|
|
1007
|
+
positionKey: {
|
|
1008
|
+
type: 'string',
|
|
1009
|
+
description: 'Adrena position account public key (base58).',
|
|
1010
|
+
},
|
|
1011
|
+
side: {
|
|
1012
|
+
type: 'string',
|
|
1013
|
+
description: 'Position side (determines instruction discriminator).',
|
|
1014
|
+
enum: ['long', 'short'],
|
|
1015
|
+
},
|
|
1016
|
+
action: {
|
|
1017
|
+
type: 'string',
|
|
1018
|
+
description: 'Collateral action.',
|
|
1019
|
+
enum: ['add', 'remove'],
|
|
1020
|
+
},
|
|
1021
|
+
amount: {
|
|
1022
|
+
type: 'number',
|
|
1023
|
+
description: 'Collateral amount in raw token units.',
|
|
1024
|
+
minimum: 1,
|
|
1025
|
+
},
|
|
1026
|
+
},
|
|
1027
|
+
required: ['wallet', 'positionKey', 'side', 'action', 'amount'],
|
|
1028
|
+
additionalProperties: false,
|
|
1029
|
+
};
|
|
1030
|
+
registerTool(server, 'sap_perp_build_modify', {
|
|
1031
|
+
description: 'Build an unsigned transaction to add or remove collateral from an Adrena perp position. Returns serialized base64 transaction for local signing.',
|
|
1032
|
+
inputSchema: schema,
|
|
1033
|
+
}, async (args) => {
|
|
1034
|
+
const walletStr = args['wallet'];
|
|
1035
|
+
const positionKeyStr = args['positionKey'];
|
|
1036
|
+
const side = args['side'];
|
|
1037
|
+
const action = args['action'];
|
|
1038
|
+
const amount = args['amount'];
|
|
1039
|
+
let walletPubkey;
|
|
1040
|
+
let positionPubkey;
|
|
1041
|
+
try {
|
|
1042
|
+
walletPubkey = new PublicKey(walletStr);
|
|
1043
|
+
positionPubkey = new PublicKey(positionKeyStr);
|
|
1044
|
+
}
|
|
1045
|
+
catch {
|
|
1046
|
+
return createTextResponse(JSON.stringify({ error: 'Invalid wallet or position key' }), { isError: true });
|
|
1047
|
+
}
|
|
1048
|
+
// Select discriminator based on action + side.
|
|
1049
|
+
const disc = action === 'add'
|
|
1050
|
+
? (side === 'long' ? DISC_ADD_COLLATERAL_LONG : DISC_ADD_COLLATERAL_SHORT)
|
|
1051
|
+
: (side === 'long' ? DISC_REMOVE_COLLATERAL_LONG : DISC_REMOVE_COLLATERAL_SHORT);
|
|
1052
|
+
// add_collateral params: collateral(u64)
|
|
1053
|
+
// remove_collateral params: collateral_usd(u64)
|
|
1054
|
+
const dataLen = 8 + 8;
|
|
1055
|
+
const data = Buffer.alloc(dataLen);
|
|
1056
|
+
let offset = 0;
|
|
1057
|
+
disc.copy(data, offset);
|
|
1058
|
+
offset += 8;
|
|
1059
|
+
writeNumberU64LE(data, amount, offset);
|
|
1060
|
+
offset += 8;
|
|
1061
|
+
const instruction = new TransactionInstruction({
|
|
1062
|
+
programId: ADRENA_PROGRAM_ID,
|
|
1063
|
+
keys: [
|
|
1064
|
+
{ pubkey: walletPubkey, isSigner: true, isWritable: true },
|
|
1065
|
+
{ pubkey: positionPubkey, isSigner: false, isWritable: true },
|
|
1066
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1067
|
+
],
|
|
1068
|
+
data,
|
|
1069
|
+
});
|
|
1070
|
+
const result = buildInscribedTx(instruction);
|
|
1071
|
+
return createTextResponse(JSON.stringify({
|
|
1072
|
+
...result,
|
|
1073
|
+
positionKey: positionKeyStr,
|
|
1074
|
+
action,
|
|
1075
|
+
side,
|
|
1076
|
+
amount,
|
|
1077
|
+
note: 'Transaction is unsigned. Resolve custody/collateral accounts from the Adrena SDK before signing locally.',
|
|
1078
|
+
}));
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
1082
|
+
* Import SystemProgram for inscribed tools
|
|
1083
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
1084
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
1085
|
+
* Main registration function
|
|
1086
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
1087
|
+
/**
|
|
1088
|
+
* @name registerPerpTools
|
|
1089
|
+
* @description Register all 10 perp trading and chart analysis MCP tools.
|
|
1090
|
+
*
|
|
1091
|
+
* @param server — MCP server instance.
|
|
1092
|
+
* @param context — Shared runtime context with SAP client, connection, and config.
|
|
1093
|
+
*
|
|
1094
|
+
* @usedBy `register-tools.ts`
|
|
1095
|
+
*/
|
|
1096
|
+
export function registerPerpTools(server, context) {
|
|
1097
|
+
logger.debug('Registering perp trading and chart tools');
|
|
1098
|
+
registerPerpMarketsTool(server, context);
|
|
1099
|
+
registerPerpPositionInfoTool(server, context);
|
|
1100
|
+
registerPerpFundingHistoryTool(server, context);
|
|
1101
|
+
registerChartOhlcTool(server, context);
|
|
1102
|
+
registerChartLongTermTool(server, context);
|
|
1103
|
+
registerChartVolumeProfileTool(server, context);
|
|
1104
|
+
registerPerpLiquidationZonesTool(server, context);
|
|
1105
|
+
registerPerpBuildOpenTool(server, context);
|
|
1106
|
+
registerPerpBuildCloseTool(server, context);
|
|
1107
|
+
registerPerpBuildModifyTool(server, context);
|
|
1108
|
+
logger.debug('Perp tools registered', { count: 10 });
|
|
1109
|
+
}
|
|
1110
|
+
//# sourceMappingURL=perp-tools.js.map
|