@mixrpay/agent-sdk 0.3.2 → 0.3.4

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/dist/index.cjs CHANGED
@@ -1467,6 +1467,540 @@ var AgentWallet = class {
1467
1467
  createdAt: new Date(data.createdAt || data.created_at)
1468
1468
  };
1469
1469
  }
1470
+ // ===========================================================================
1471
+ // MCP (Model Context Protocol) Methods
1472
+ // ===========================================================================
1473
+ /**
1474
+ * Get authentication headers for MCP wallet-based authentication.
1475
+ *
1476
+ * These headers prove wallet ownership without transmitting the private key.
1477
+ * Use for direct pay-per-call mode (no session needed).
1478
+ *
1479
+ * @returns Headers object with X-Mixr-Wallet, X-Mixr-Signature, X-Mixr-Timestamp
1480
+ *
1481
+ * @example
1482
+ * ```typescript
1483
+ * const headers = await wallet.getMCPAuthHeaders();
1484
+ * const response = await fetch('https://mixrpay.com/api/mcp', {
1485
+ * method: 'POST',
1486
+ * headers: {
1487
+ * 'Content-Type': 'application/json',
1488
+ * ...headers,
1489
+ * },
1490
+ * body: JSON.stringify({
1491
+ * jsonrpc: '2.0',
1492
+ * method: 'tools/list',
1493
+ * id: 1,
1494
+ * }),
1495
+ * });
1496
+ * ```
1497
+ */
1498
+ async getMCPAuthHeaders() {
1499
+ const timestamp = Date.now().toString();
1500
+ const message = `MixrPay MCP Auth
1501
+ Wallet: ${this.walletAddress}
1502
+ Timestamp: ${timestamp}`;
1503
+ const signature = await this.sessionKey.signMessage(message);
1504
+ return {
1505
+ "X-Mixr-Wallet": this.walletAddress,
1506
+ "X-Mixr-Signature": signature,
1507
+ "X-Mixr-Timestamp": timestamp
1508
+ };
1509
+ }
1510
+ /**
1511
+ * List available MCP tools from the MixrPay gateway.
1512
+ *
1513
+ * Returns all tools exposed by MCP providers on the MixrPay marketplace.
1514
+ * Each tool includes pricing information.
1515
+ *
1516
+ * @returns Array of MCP tools with pricing and metadata
1517
+ *
1518
+ * @example
1519
+ * ```typescript
1520
+ * const tools = await wallet.listMCPTools();
1521
+ * for (const tool of tools) {
1522
+ * console.log(`${tool.name}: $${tool.priceUsd} - ${tool.description}`);
1523
+ * }
1524
+ * ```
1525
+ */
1526
+ async listMCPTools() {
1527
+ this.logger.debug("listMCPTools");
1528
+ const response = await fetch(`${this.baseUrl}/api/mcp`, {
1529
+ method: "POST",
1530
+ headers: { "Content-Type": "application/json" },
1531
+ body: JSON.stringify({
1532
+ jsonrpc: "2.0",
1533
+ method: "tools/list",
1534
+ id: Date.now()
1535
+ })
1536
+ });
1537
+ if (!response.ok) {
1538
+ throw new MixrPayError(`Failed to list MCP tools: ${response.status}`);
1539
+ }
1540
+ const result = await response.json();
1541
+ if (result.error) {
1542
+ throw new MixrPayError(result.error.message || "Failed to list MCP tools");
1543
+ }
1544
+ return (result.result?.tools || []).map((tool) => ({
1545
+ name: tool.name,
1546
+ description: tool.description,
1547
+ inputSchema: tool.inputSchema,
1548
+ priceUsd: tool["x-mixrpay"]?.priceUsd || 0,
1549
+ merchantName: tool["x-mixrpay"]?.merchantName,
1550
+ merchantSlug: tool["x-mixrpay"]?.merchantSlug,
1551
+ verified: tool["x-mixrpay"]?.verified || false
1552
+ }));
1553
+ }
1554
+ /**
1555
+ * Call an MCP tool with wallet authentication (direct pay per call).
1556
+ *
1557
+ * This method signs a fresh auth message for each call, charging
1558
+ * directly from your wallet balance without needing a session.
1559
+ *
1560
+ * @param toolName - The tool name in format "merchant/tool"
1561
+ * @param args - Arguments to pass to the tool
1562
+ * @returns Tool execution result
1563
+ *
1564
+ * @example
1565
+ * ```typescript
1566
+ * const result = await wallet.callMCPTool('firecrawl/scrape', {
1567
+ * url: 'https://example.com',
1568
+ * });
1569
+ * console.log(result.data);
1570
+ * console.log(`Charged: $${result.chargedUsd}`);
1571
+ * ```
1572
+ */
1573
+ async callMCPTool(toolName, args = {}) {
1574
+ this.logger.debug("callMCPTool", { toolName, args });
1575
+ const authHeaders = await this.getMCPAuthHeaders();
1576
+ const response = await fetch(`${this.baseUrl}/api/mcp`, {
1577
+ method: "POST",
1578
+ headers: {
1579
+ "Content-Type": "application/json",
1580
+ ...authHeaders
1581
+ },
1582
+ body: JSON.stringify({
1583
+ jsonrpc: "2.0",
1584
+ method: "tools/call",
1585
+ params: { name: toolName, arguments: args },
1586
+ id: Date.now()
1587
+ })
1588
+ });
1589
+ const result = await response.json();
1590
+ if (result.error) {
1591
+ throw new MixrPayError(result.error.message || "MCP tool call failed");
1592
+ }
1593
+ const content = result.result?.content?.[0];
1594
+ const data = content?.text ? JSON.parse(content.text) : null;
1595
+ const mixrpay = result.result?._mixrpay || {};
1596
+ if (mixrpay.chargedUsd) {
1597
+ const payment = {
1598
+ amountUsd: mixrpay.chargedUsd,
1599
+ recipient: toolName.split("/")[0] || toolName,
1600
+ txHash: mixrpay.txHash,
1601
+ timestamp: /* @__PURE__ */ new Date(),
1602
+ description: `MCP: ${toolName}`,
1603
+ url: `${this.baseUrl}/api/mcp`
1604
+ };
1605
+ this.payments.push(payment);
1606
+ this.totalSpentUsd += mixrpay.chargedUsd;
1607
+ this.logger.payment(mixrpay.chargedUsd, toolName, "MCP call");
1608
+ if (this.onPayment) {
1609
+ this.onPayment(payment);
1610
+ }
1611
+ }
1612
+ return {
1613
+ data,
1614
+ chargedUsd: mixrpay.chargedUsd || 0,
1615
+ txHash: mixrpay.txHash,
1616
+ latencyMs: mixrpay.latencyMs
1617
+ };
1618
+ }
1619
+ // ===========================================================================
1620
+ // Agent Runtime API
1621
+ // ===========================================================================
1622
+ /**
1623
+ * Run an AI agent with LLM and tool execution.
1624
+ *
1625
+ * This method orchestrates a full agentic loop:
1626
+ * - Multi-turn reasoning with LLM
1627
+ * - Automatic tool execution
1628
+ * - Bundled billing (single charge at end)
1629
+ * - Optional streaming via SSE
1630
+ *
1631
+ * @param options - Agent run options
1632
+ * @returns Agent run result with response and cost breakdown
1633
+ *
1634
+ * @example Basic usage
1635
+ * ```typescript
1636
+ * const result = await wallet.runAgent({
1637
+ * sessionId: 'sess_abc123',
1638
+ * messages: [{ role: 'user', content: 'Find AI startups in SF' }],
1639
+ * });
1640
+ *
1641
+ * console.log(result.response);
1642
+ * console.log(`Cost: $${result.cost.totalUsd.toFixed(4)}`);
1643
+ * ```
1644
+ *
1645
+ * @example With custom config
1646
+ * ```typescript
1647
+ * const result = await wallet.runAgent({
1648
+ * sessionId: 'sess_abc123',
1649
+ * messages: [{ role: 'user', content: 'Research quantum computing' }],
1650
+ * config: {
1651
+ * model: 'gpt-4o',
1652
+ * maxIterations: 15,
1653
+ * tools: ['platform/exa-search', 'platform/firecrawl-scrape'],
1654
+ * systemPrompt: 'You are a research assistant.',
1655
+ * },
1656
+ * });
1657
+ * ```
1658
+ *
1659
+ * @example With streaming
1660
+ * ```typescript
1661
+ * await wallet.runAgent({
1662
+ * sessionId: 'sess_abc123',
1663
+ * messages: [{ role: 'user', content: 'Analyze this company' }],
1664
+ * stream: true,
1665
+ * onEvent: (event) => {
1666
+ * if (event.type === 'llm_chunk') {
1667
+ * process.stdout.write(event.delta);
1668
+ * } else if (event.type === 'tool_call') {
1669
+ * console.log(`Calling tool: ${event.tool}`);
1670
+ * }
1671
+ * },
1672
+ * });
1673
+ * ```
1674
+ */
1675
+ async runAgent(options) {
1676
+ const {
1677
+ sessionId,
1678
+ messages,
1679
+ config = {},
1680
+ stream = false,
1681
+ idempotencyKey,
1682
+ onEvent
1683
+ } = options;
1684
+ this.logger.debug("runAgent", { sessionId, messageCount: messages.length, config, stream });
1685
+ const body = {
1686
+ session_id: sessionId,
1687
+ messages: messages.map((m) => ({ role: m.role, content: m.content })),
1688
+ config: {
1689
+ model: config.model,
1690
+ max_iterations: config.maxIterations,
1691
+ tools: config.tools,
1692
+ system_prompt: config.systemPrompt
1693
+ },
1694
+ stream,
1695
+ idempotency_key: idempotencyKey
1696
+ };
1697
+ const AGENT_RUN_TIMEOUT = 18e4;
1698
+ if (!stream) {
1699
+ const response = await fetch(`${this.baseUrl}/api/v2/agent/run`, {
1700
+ method: "POST",
1701
+ headers: {
1702
+ "Content-Type": "application/json",
1703
+ "X-Mixr-Session": sessionId
1704
+ },
1705
+ body: JSON.stringify(body),
1706
+ signal: AbortSignal.timeout(AGENT_RUN_TIMEOUT)
1707
+ });
1708
+ if (!response.ok) {
1709
+ const error = await response.json().catch(() => ({}));
1710
+ throw new MixrPayError(error.error || `Agent run failed: ${response.status}`);
1711
+ }
1712
+ const data = await response.json();
1713
+ if (data.cost?.total_usd > 0) {
1714
+ const payment = {
1715
+ amountUsd: data.cost.total_usd,
1716
+ recipient: "mixrpay-agent-run",
1717
+ txHash: data.tx_hash,
1718
+ timestamp: /* @__PURE__ */ new Date(),
1719
+ description: `Agent run: ${data.run_id}`,
1720
+ url: `${this.baseUrl}/api/v2/agent/run`
1721
+ };
1722
+ this.payments.push(payment);
1723
+ this.totalSpentUsd += data.cost.total_usd;
1724
+ this.logger.payment(data.cost.total_usd, "agent-run", data.run_id);
1725
+ if (this.onPayment) {
1726
+ this.onPayment(payment);
1727
+ }
1728
+ }
1729
+ return {
1730
+ runId: data.run_id,
1731
+ status: data.status,
1732
+ response: data.response,
1733
+ iterations: data.iterations,
1734
+ toolsUsed: data.tools_used,
1735
+ cost: {
1736
+ llmUsd: data.cost.llm_usd,
1737
+ toolsUsd: data.cost.tools_usd,
1738
+ totalUsd: data.cost.total_usd
1739
+ },
1740
+ tokens: data.tokens,
1741
+ sessionRemainingUsd: data.session_remaining_usd,
1742
+ txHash: data.tx_hash
1743
+ };
1744
+ }
1745
+ return this.runAgentStreaming(sessionId, body, onEvent);
1746
+ }
1747
+ /**
1748
+ * Internal: Handle streaming agent run via SSE
1749
+ */
1750
+ async runAgentStreaming(sessionId, body, onEvent) {
1751
+ const STREAMING_TIMEOUT = 3e5;
1752
+ const response = await fetch(`${this.baseUrl}/api/v2/agent/run`, {
1753
+ method: "POST",
1754
+ headers: {
1755
+ "Content-Type": "application/json",
1756
+ "X-Mixr-Session": sessionId
1757
+ },
1758
+ body: JSON.stringify(body),
1759
+ signal: AbortSignal.timeout(STREAMING_TIMEOUT)
1760
+ });
1761
+ if (!response.ok) {
1762
+ const error = await response.json().catch(() => ({}));
1763
+ throw new MixrPayError(error.error || `Agent run failed: ${response.status}`);
1764
+ }
1765
+ const reader = response.body?.getReader();
1766
+ if (!reader) {
1767
+ throw new MixrPayError("No response body for streaming");
1768
+ }
1769
+ const decoder = new TextDecoder();
1770
+ let buffer = "";
1771
+ let result = null;
1772
+ while (true) {
1773
+ const { done, value } = await reader.read();
1774
+ if (done) break;
1775
+ buffer += decoder.decode(value, { stream: true });
1776
+ const lines = buffer.split("\n");
1777
+ buffer = lines.pop() || "";
1778
+ let currentEvent = "";
1779
+ for (const line of lines) {
1780
+ if (line.startsWith("event: ")) {
1781
+ currentEvent = line.slice(7).trim();
1782
+ } else if (line.startsWith("data: ") && currentEvent) {
1783
+ try {
1784
+ const data = JSON.parse(line.slice(6));
1785
+ const event = this.parseSSEEvent(currentEvent, data);
1786
+ if (event) {
1787
+ onEvent?.(event);
1788
+ if (currentEvent === "complete") {
1789
+ result = {
1790
+ runId: data.run_id,
1791
+ status: "completed",
1792
+ response: data.response,
1793
+ iterations: data.iterations,
1794
+ toolsUsed: data.tools_used,
1795
+ cost: {
1796
+ llmUsd: 0,
1797
+ // Not provided in streaming complete
1798
+ toolsUsd: 0,
1799
+ totalUsd: data.total_cost_usd
1800
+ },
1801
+ tokens: { prompt: 0, completion: 0 },
1802
+ sessionRemainingUsd: 0,
1803
+ txHash: data.tx_hash
1804
+ };
1805
+ if (data.total_cost_usd > 0) {
1806
+ const payment = {
1807
+ amountUsd: data.total_cost_usd,
1808
+ recipient: "mixrpay-agent-run",
1809
+ txHash: data.tx_hash,
1810
+ timestamp: /* @__PURE__ */ new Date(),
1811
+ description: `Agent run: ${data.run_id}`,
1812
+ url: `${this.baseUrl}/api/v2/agent/run`
1813
+ };
1814
+ this.payments.push(payment);
1815
+ this.totalSpentUsd += data.total_cost_usd;
1816
+ if (this.onPayment) {
1817
+ this.onPayment(payment);
1818
+ }
1819
+ }
1820
+ }
1821
+ if (currentEvent === "error") {
1822
+ throw new MixrPayError(data.error || "Agent run failed");
1823
+ }
1824
+ }
1825
+ } catch (e) {
1826
+ if (e instanceof MixrPayError) throw e;
1827
+ this.logger.warn("Failed to parse SSE event:", e);
1828
+ }
1829
+ currentEvent = "";
1830
+ }
1831
+ }
1832
+ }
1833
+ if (!result) {
1834
+ throw new MixrPayError("Agent run completed without final result");
1835
+ }
1836
+ return result;
1837
+ }
1838
+ /**
1839
+ * Internal: Parse SSE event into typed event object
1840
+ */
1841
+ parseSSEEvent(eventType, data) {
1842
+ switch (eventType) {
1843
+ case "run_start":
1844
+ return { type: "run_start", runId: data.run_id };
1845
+ case "iteration_start":
1846
+ return { type: "iteration_start", iteration: data.iteration };
1847
+ case "llm_chunk":
1848
+ return { type: "llm_chunk", delta: data.delta };
1849
+ case "tool_call":
1850
+ return { type: "tool_call", tool: data.tool, arguments: data.arguments };
1851
+ case "tool_result":
1852
+ return {
1853
+ type: "tool_result",
1854
+ tool: data.tool,
1855
+ success: data.success,
1856
+ costUsd: data.cost_usd,
1857
+ error: data.error
1858
+ };
1859
+ case "iteration_complete":
1860
+ return {
1861
+ type: "iteration_complete",
1862
+ iteration: data.iteration,
1863
+ tokens: data.tokens,
1864
+ costUsd: data.cost_usd
1865
+ };
1866
+ case "complete":
1867
+ return {
1868
+ type: "complete",
1869
+ runId: data.run_id,
1870
+ response: data.response,
1871
+ totalCostUsd: data.total_cost_usd,
1872
+ txHash: data.tx_hash,
1873
+ iterations: data.iterations,
1874
+ toolsUsed: data.tools_used
1875
+ };
1876
+ case "error":
1877
+ return {
1878
+ type: "error",
1879
+ error: data.error,
1880
+ partialCostUsd: data.partial_cost_usd
1881
+ };
1882
+ default:
1883
+ return null;
1884
+ }
1885
+ }
1886
+ /**
1887
+ * Get the status of an agent run by ID.
1888
+ *
1889
+ * @param runId - The agent run ID
1890
+ * @param sessionId - The session ID (for authentication)
1891
+ * @returns Agent run status and results
1892
+ *
1893
+ * @example
1894
+ * ```typescript
1895
+ * const status = await wallet.getAgentRunStatus('run_abc123', 'sess_xyz789');
1896
+ * console.log(`Status: ${status.status}`);
1897
+ * if (status.status === 'completed') {
1898
+ * console.log(status.response);
1899
+ * }
1900
+ * ```
1901
+ */
1902
+ async getAgentRunStatus(runId, sessionId) {
1903
+ this.logger.debug("getAgentRunStatus", { runId, sessionId });
1904
+ const response = await fetch(`${this.baseUrl}/api/v2/agent/run/${runId}`, {
1905
+ headers: {
1906
+ "X-Mixr-Session": sessionId
1907
+ }
1908
+ });
1909
+ if (!response.ok) {
1910
+ const error = await response.json().catch(() => ({}));
1911
+ throw new MixrPayError(error.error || `Failed to get run status: ${response.status}`);
1912
+ }
1913
+ const data = await response.json();
1914
+ return {
1915
+ runId: data.run_id,
1916
+ status: data.status,
1917
+ response: data.response,
1918
+ iterations: data.iterations,
1919
+ toolsUsed: data.tools_used,
1920
+ cost: {
1921
+ llmUsd: data.cost.llm_usd,
1922
+ toolsUsd: data.cost.tools_usd,
1923
+ totalUsd: data.cost.total_usd
1924
+ },
1925
+ tokens: data.tokens,
1926
+ txHash: data.tx_hash,
1927
+ error: data.error,
1928
+ startedAt: new Date(data.started_at),
1929
+ completedAt: data.completed_at ? new Date(data.completed_at) : void 0
1930
+ };
1931
+ }
1932
+ /**
1933
+ * Call an MCP tool using session authorization (pre-authorized spending limit).
1934
+ *
1935
+ * Use this when you've already created a session with the tool provider
1936
+ * and want to use that spending limit instead of direct wallet charges.
1937
+ *
1938
+ * @param sessionId - The session ID for the tool provider
1939
+ * @param toolName - The tool name in format "merchant/tool"
1940
+ * @param args - Arguments to pass to the tool
1941
+ * @returns Tool execution result
1942
+ *
1943
+ * @example
1944
+ * ```typescript
1945
+ * // Create session with provider first
1946
+ * const session = await wallet.getOrCreateSession({
1947
+ * merchantPublicKey: 'pk_live_firecrawl_...',
1948
+ * spendingLimitUsd: 50,
1949
+ * });
1950
+ *
1951
+ * // Use session for multiple calls
1952
+ * const result = await wallet.callMCPToolWithSession(
1953
+ * session.id,
1954
+ * 'firecrawl/scrape',
1955
+ * { url: 'https://example.com' }
1956
+ * );
1957
+ * ```
1958
+ */
1959
+ async callMCPToolWithSession(sessionId, toolName, args = {}) {
1960
+ this.logger.debug("callMCPToolWithSession", { sessionId, toolName, args });
1961
+ const response = await fetch(`${this.baseUrl}/api/mcp`, {
1962
+ method: "POST",
1963
+ headers: {
1964
+ "Content-Type": "application/json",
1965
+ "X-Mixr-Session": sessionId
1966
+ },
1967
+ body: JSON.stringify({
1968
+ jsonrpc: "2.0",
1969
+ method: "tools/call",
1970
+ params: { name: toolName, arguments: args },
1971
+ id: Date.now()
1972
+ })
1973
+ });
1974
+ const result = await response.json();
1975
+ if (result.error) {
1976
+ throw new MixrPayError(result.error.message || "MCP tool call failed");
1977
+ }
1978
+ const content = result.result?.content?.[0];
1979
+ const data = content?.text ? JSON.parse(content.text) : null;
1980
+ const mixrpay = result.result?._mixrpay || {};
1981
+ if (mixrpay.chargedUsd) {
1982
+ const payment = {
1983
+ amountUsd: mixrpay.chargedUsd,
1984
+ recipient: toolName.split("/")[0] || toolName,
1985
+ txHash: mixrpay.txHash,
1986
+ timestamp: /* @__PURE__ */ new Date(),
1987
+ description: `MCP: ${toolName}`,
1988
+ url: `${this.baseUrl}/api/mcp`
1989
+ };
1990
+ this.payments.push(payment);
1991
+ this.totalSpentUsd += mixrpay.chargedUsd;
1992
+ this.logger.payment(mixrpay.chargedUsd, toolName, "MCP call (session)");
1993
+ if (this.onPayment) {
1994
+ this.onPayment(payment);
1995
+ }
1996
+ }
1997
+ return {
1998
+ data,
1999
+ chargedUsd: mixrpay.chargedUsd || 0,
2000
+ txHash: mixrpay.txHash,
2001
+ latencyMs: mixrpay.latencyMs
2002
+ };
2003
+ }
1470
2004
  };
1471
2005
  // Annotate the CommonJS export names for ESM import in node:
1472
2006
  0 && (module.exports = {