#
tokens: 3376/50000 4/4 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitignore
├── index.js
├── LICENSE
├── package.json
├── README.md
└── yarn.lock
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
node_modules
.env
docker/
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# Jupiter MCP Server

An MCP server for executing token swaps on the Solana blockchain using Jupiter's new Ultra API. 

![License](https://img.shields.io/badge/license-MIT-blue.svg)
![Node.js](https://img.shields.io/badge/Node.js-18.x-green.svg)
![Status](https://img.shields.io/badge/status-active-brightgreen.svg)

## Features

- Fetch swap orders from Jupiter's Ultra API, combining DEX routing and RFQ (Request for Quote) for optimal pricing.
- Execute swaps via Jupiter's Ultra API, handling slippage, priority fees, and transaction landing.


## Prerequisites

- **Node.js**: Version 18 or higher (for native `fetch` support).
- **Solana Wallet**: A private key (base58-encoded) for signing transactions.
- **RPC Endpoint**: Access to a Solana RPC node (e.g., `https://api.mainnet-beta.solana.com`).

## Installation

1. **Clone the Repository**:
   ```bash
   git clone https://github.com/kukapay/jupiter-mcp.git
   cd jupiter-mcp
   ```

2. **Install Dependencies**:
   Ensure you have the MCP Server package installed along with other required dependencies:
   ```bash
   npm install
   ```

3. **Client Configuration**:

```json
{
  "mcpServers": {
    "Jupiter-MCP": {
      "command": "node",
      "args": ["path/to/jupiter-mcp/server/index.js"],
      "env": {
        "SOLANA_RPC_URL": "solana rpc url you can access",
        "PRIVATE_KEY": "your private key"
      }
    }
  }
}
```

## Tools

### Ultra API Tools
- **`get-ultra-order`**:
  - **Description**: Fetches a swap order from Jupiter's Ultra API, leveraging both DEX routing and RFQ for optimal pricing.
  - **Inputs**: 
    - `inputMint`: Input token mint address (e.g., SOL or token pubkey).
    - `outputMint`: Output token mint address (e.g., USDC or token pubkey).
    - `amount`: Input amount as a string (e.g., "1.23").
    - `slippageBps`: Slippage tolerance in basis points (e.g., 50 for 0.5%). 
  - **Output**: JSON with `requestId`, `transaction` (base64-encoded), `inputMint`, `outputMint`, `inAmount`, `outAmount`, `price`.

- **`execute-ultra-order`**:
  - **Description**: Requests Jupiter to execute the swap transaction on behalf of the wallet owner, handling slippage, priority fees, and transaction landing.
  - **Inputs**: 
    - `requestId`: Unique identifier from `get-ultra-order`.
    - `transaction`: Base64-encoded transaction from `get-ultra-order`.
  - **Output**: JSON with `status`, `transactionId`, `slot`, `inputAmountResult`, `outputAmountResult`, `swapEvents`.

## Example Interaction

Below are examples of interacting with the server using natural language prompts and expected responses:

### Fetching a Swap Order
- **Prompt**: "Get a swap order to trade 1.23 SOL for USDC."
- **Input**: 
  - Tool: `get-ultra-order`
  - Arguments: 
    - `inputMint`: "So11111111111111111111111111111111111111112" (SOL)
    - `outputMint`: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" (USDC)
    - `amount`: "1.23"
    - `slippageBps`: 50
- **Response**:
  ```
  {
    "requestId": "a770110b-82c9-46c8-ba61-09d955b27503",
    "transaction": "AQAAAA...base64-encoded-transaction...==",
    "inputMint": "So11111111111111111111111111111111111111112",
    "outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "inAmount": "1230000000",
    "outAmount": "19950000",
    "price": 0.01621951219512195
  }
  ```

### Executing a Swap
- **Prompt**: "Execute the swap order with request ID 'a770110b-82c9-46c8-ba61-09d955b27503' using the transaction provided."
- **Input**: 
  - Tool: `execute-ultra-order`
  - Arguments: 
    - `requestId`: "a770110b-82c9-46c8-ba61-09d955b27503"
    - `transaction`: "AQAAAA...base64-encoded-transaction...=="
- **Response**:
  ```
  {
    "status": "Success",
    "transactionId": "5x...solana-transaction-signature...",
    "slot": 299283763,
    "inputAmountResult": "1230000000",
    "outputAmountResult": "19950000",
    "swapEvents": [
      {
        "type": "swap",
        "inputMint": "So11111111111111111111111111111111111111112",
        "outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
        "inAmount": "1230000000",
        "outAmount": "19950000"
      }
    ]
  }
  ```


## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.



```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "jupiter-mcp",
  "version": "1.0.1",
  "main": "index.js",
  "homepage": "https://github.com/kukapay/jupiter-mcp",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.7.0",
    "@solana/spl-token": "^0.4.13",
    "@solana/web3.js": "^1.98.0",
    "bigint-buffer": "^1.1.5",
    "dotenv": "^16.4.7",
    "undici": "^7.5.0",
    "zod": "^3.24.2"
  }
}

```

--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------

```javascript
const { McpServer } =require("@modelcontextprotocol/sdk/server/mcp.js");
const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
const { z } = require("zod");
const { Connection, Keypair, VersionedTransaction, PublicKey} = require("@solana/web3.js");
const { getMint } = require("@solana/spl-token");
const bs58 = require("bs58");

const dotenv = require("dotenv")
dotenv.config()

// Configuration
const ULTRA_API = "https://api.jup.ag/ultra/v1";
const RPC_URL = process.env.SOLANA_RPC_URL;
const PRIVATE_KEY = process.env.PRIVATE_KEY; // Base58 encoded private key

// Configure Solana Connection with proxy
const connection = new Connection(RPC_URL, {
  commitment: "confirmed"
});

// Load wallet from private key
const walletKeypair = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY));
const walletPublicKey = walletKeypair.publicKey.toString();

// Initialize MCP server
const server = new McpServer({
  name: "Jupiter MCP",
  version: "1.0.0"
});

server.tool(
  "get-ultra-order",
  "Get a swap order from both Jupiter DEX Routing Engine and Jupiter Z (RFQ).",
  {
    inputMint: z.string().describe("Input token mint address"),
    outputMint: z.string().describe("Output token mint address"),
    amount: z.string().describe("Input amount as a string (e.g., '1.23')"),
    slippageBps: z.number().describe("Slippage tolerance in basis points (e.g., 50 for 0.5%).")
  },
  async ({ inputMint, outputMint, amount, slippageBps }) => {
    try {
      const effectiveInputMint = inputMint;
      const effectiveOutputMint = outputMint;

      const inputMintPublicKey = new PublicKey(effectiveInputMint);
      const inputMintInfo = await getMint(connection, inputMintPublicKey);
      const decimals = inputMintInfo.decimals;

      const amountFloat = parseFloat(amount);
      if (isNaN(amountFloat)) {
        throw new Error("Invalid amount format");
      }
      const amountInt = Math.floor(amountFloat * Math.pow(10, decimals)).toString();

      const params = new URLSearchParams({
        inputMint: effectiveInputMint,
        outputMint: effectiveOutputMint,
        amount: amountInt,
        slippageBps: slippageBps.toString(),
        taker: walletPublicKey
      });

      const response = await fetch(`${ULTRA_API}/order?${params}`);
      const order = await response.json();
      if (!order.transaction) {
        throw new Error("No transaction field in response. Ensure taker address is valid.");
      }

      return {
        content: [{
          type: "text",
          text: JSON.stringify({
            requestId: order.requestId,
            transaction: order.transaction,
            inputMint: effectiveInputMint,
            outputMint: effectiveOutputMint,
            inAmount: order.inAmount,
            outAmount: order.outAmount,
            price: Number(order.outAmount) / Number(order.inAmount)
          }, null, 2)
        }]
      };
    } catch (error) {
      console.log(error)
      return {
        content: [{
          type: "text",
          text: `Error fetching order: ${error.message}`
        }],
        isError: true
      };
    }
  }
);

server.tool(
  "execute-ultra-order",
  "Request Jupiter to execute the swap transaction on behalf of the wallet owner. This includes handling of slippage, priority fees, transaction landing and more.",  
  {
    requestId: z.string().describe("Request ID from get-swap-order"),
    transaction: z.string().describe("Base64 encoded transaction from get-swap-order")
  },
  async ({ requestId, transaction, inputMint, outputMint, amount }) => {
    try {

      let tx = VersionedTransaction.deserialize(Buffer.from(transaction, "base64"));
      tx.sign([walletKeypair]);
      const signedTransaction = Buffer.from(tx.serialize()).toString("base64");

      const executeResponse = await fetch(`${ULTRA_API}/execute`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          signedTransaction,
          requestId
        })
      });

      if (!executeResponse.ok) {
        throw new Error(`HTTP error! status: ${executeResponse.status}`);
      }

      const result = await executeResponse.json();

      return {
        content: [{
          type: "text",
          text: JSON.stringify({
            status: result.status,
            transactionId: result.signature,
            slot: result.slot,
            inputAmountResult: result.inputAmountResult,
            outputAmountResult: result.outputAmountResult,
            swapEvents: result.swapEvents
          }, null, 2)
        }]
      };
    } catch (error) {
      return {
        content: [{
          type: "text",
          text: `Error executing swap: ${error.message}`
        }],
        isError: true
      };
    }
  }
);

// Start the server
async function startServer() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  /*
  console.log(await server._registeredTools['get-swap-order'].callback({
    inputMint: "So11111111111111111111111111111111111111112",
    outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    amount: "1.23",
    token: walletPublicKey,
    slippageBps: 50
  }))
  console.log(await server._registeredTools['execute-swap-order'].callback({
    requestId: "a770110b-82c9-46c8-ba61-09d955b27503",
    transaction: "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAHC+oaDbp9I+uYeae3ayA/1sT4qamJ7tq3b9PZmkdhJZ1mH9RsYXp41YrTtfB/VqrENVYdGHG6rtaCOqWfAPswrbh9iVaqrUHPNEIwuvJkSS4mZY8ggefu+qFI49PsepOULZXdFYpJfuoa+lkMfRsGXmW453vsGMQqadwJft+fT84EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACMlyWPTiSJ8bs9ECkUjg2DC1oTmdr/EIQEjnvY2+n4Wawfg/25zlUN6V1VjNx5VGHM9DdKxojsE6mEACIKeNoGAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAC0P/on9df2SnTAmx8pWHneSwmrNt/J3VFLMhqns4zl6AR51VvyMcBu7nTFbs5oFQf9sbLeo/SOUQKxzaJWvBOPBt324ddloZPZy+FGzut5rBy0he1fWzeROoz1hX7/AKmrpZYSiXiYJPloNZKFXzIx+ssAA5/HzJnPbcFMqSRo6AcHAAUCwFwVAAcACQNpRAgAAAAAAAQCAAIMAgAAAHBtb0kAAAAACQUCAA8KBAmT8Xtk9ISudv4FBgADAA4ECgEBCRMKAAIDCQ4BCAkQAA0MCwIDChEGJOUXy5d6460qAQAAAD0AZAABgE9QSQAAAAAZSXkJAAAAAFMABQoDAgAAAQkBXebA5bRGJSJ69exFtoMFfhkdbXv3/0Pj0l8x1dXoHawDum+4BMATuXA="
  }))
  */
}

startServer().catch(console.error);


```