# 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.



## 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);
```