Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ LOG_LEVEL=DEBUG
PRIVATE_KEY=your_private_key

# Server configuration (optional, will use default values if not set)
PORT=3001 # Port for HTTP server (only used in SSE mode)
PORT=3001 # Port for HTTP/SSE server (default: 3001)
107 changes: 94 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,32 @@ To connect to the MCP server from Cursor:
3. Click "Add new global MCP server"
4. Enter the following details:

Default mode
**Streamable HTTP mode (Recommended)**

```json
{
"mcpServers": {
"bnbchain-mcp": {
"command": "npx",
"args": ["-y", "@bnb-chain/mcp@latest"],
"args": ["-y", "@bnb-chain/mcp@latest", "--http"],
"env": {
"PRIVATE_KEY": "your_private_key_here. (optional)"
"PRIVATE_KEY": "your_private_key_here (optional)"
}
}
}
}
```

SSE mode
**stdio mode (Alternative)**

```json
{
"mcpServers": {
"bnbchain-mcp": {
"command": "npx",
"args": ["-y", "@bnb-chain/mcp@latest", "--sse"],
"args": ["-y", "@bnb-chain/mcp@latest"],
"env": {
"PRIVATE_KEY": "your_private_key_here. (optional)"
"PRIVATE_KEY": "your_private_key_here (optional)"
}
}
}
Expand All @@ -75,14 +75,32 @@ To connect to the MCP server from Claude Desktop:
3. Click the "Edit Config" Button
4. Add the following configuration to the `claude_desktop_config.json` file:

**Streamable HTTP mode (Recommended)**

```json
{
"mcpServers": {
"bnbchain-mcp": {
"command": "npx",
"args": ["-y", "@bnb-chain/mcp@latest", "--http"],
"env": {
"PRIVATE_KEY": "your_private_key_here (optional)"
}
}
}
}
```

**stdio mode (Alternative)**

```json
{
"mcpServers": {
"bnbchain-mcp": {
"command": "npx",
"args": ["-y", "@bnb-chain/mcp@latest"],
"env": {
"PRIVATE_KEY": "your_private_key_here"
"PRIVATE_KEY": "your_private_key_here (optional)"
}
}
}
Expand All @@ -97,6 +115,30 @@ Once connected, you can use all the MCP prompts and tools directly in your Claud
- "Explain the EVM concept of gas"
- "Check the latest block on BSC"

## Integration with Claude Code

To connect to the MCP server from Claude Code:

**Streamable HTTP mode (Recommended)**

```bash
# Add the server (it will auto-start when needed)
claude mcp add bnb-chain npx -y @bnb-chain/mcp@latest --http

# Verify the connection
claude mcp list
```

The server will automatically start on `http://localhost:3001/mcp` when Claude Code needs it.

**stdio mode (Alternative)**

```bash
claude mcp add bnb-chain npx -y @bnb-chain/mcp@latest
```

**Note:** To set a private key for write operations (transfers, contract writes, etc.), set the `PRIVATE_KEY` environment variable before starting Claude Code.

## Integration with Other Clients

If you want to integrate BNBChain MCP into your own client, please check out the [examples](./examples) directory for more detailed information and reference implementations.
Expand Down Expand Up @@ -143,21 +185,57 @@ Edit `.env` file with your configuration:
# Install project dependencies
bun install

# Start the development server
# Start with Streamable HTTP transport (recommended, protocol 2025-03-26)
bun dev:http

# OR start with SSE transport (deprecated, protocol 2024-11-05)
bun dev:sse

# OR start with stdio mode (for local MCP clients)
bun dev
```

### Testing with MCP Clients

Configure the local server in your MCP clients using this template:
**For Streamable HTTP (`bun dev:http`):**

```json
{
"mcpServers": {
"bnbchain-mcp": {
"url": "http://localhost:3001/mcp"
}
}
}
```

Or using Claude Code CLI:
```bash
claude mcp add --transport http bnbchain-mcp http://localhost:3001/mcp
```

**For SSE mode (`bun dev:sse`):**

```json
{
"mcpServers": {
"bnbchain-mcp": {
"url": "http://localhost:3001/sse"
}
}
}
```

**For stdio mode (`bun dev`):**

```json
{
"mcpServers": {
"bnbchain-mcp": {
"url": "http://localhost:3001/sse",
"command": "bun",
"args": ["run", "dev"],
"env": {
"PRIVATE_KEY": "your_private_key_here"
"PRIVATE_KEY": "your_private_key_here (optional)"
}
}
}
Expand All @@ -174,9 +252,12 @@ bun run test

### Available Scripts

- `bun dev:sse`: Start development server with hot reload
- `bun dev:http`: Start server with Streamable HTTP transport (recommended, protocol 2025-03-26)
- `bun dev:sse`: Start server with SSE transport (deprecated, protocol 2024-11-05)
- `bun dev`: Start server with stdio transport
- `bun build`: Build the project
- `bun test`: Run test suite
- `bun test`: Run test suite with MCP inspector
- `bun e2e`: Run end-to-end tests

## Available Prompts and Tools

Expand Down
Binary file modified bun.lockb
Binary file not shown.
76 changes: 76 additions & 0 deletions e2e/http-transport.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { afterAll, beforeAll, describe, expect, it } from "bun:test"
import { Client } from "@modelcontextprotocol/sdk/client/index.js"
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
import { spawn, type ChildProcess } from "child_process"

describe("HTTP Transport Test", async () => {
let serverProcess: ChildProcess
let client: Client
let transport: StreamableHTTPClientTransport

beforeAll(async () => {
// Start the server with HTTP transport
serverProcess = spawn(process.execPath, ["dist/index.js", "--http"], {
env: {
...process.env,
PORT: "3002",
LOG_LEVEL: "ERROR"
}
})

await new Promise((resolve) => setTimeout(resolve, 2000))

// Create client and connect
client = new Client({
name: "bnbchain-mcp-http-test-client",
version: "1.0.0"
})

transport = new StreamableHTTPClientTransport(
new URL("http://localhost:3002/mcp")
)

await client.connect(transport)
})

afterAll(async () => {
// Clean up
await transport.close()
serverProcess.kill()
})

it("should connect to HTTP server", async () => {
expect(client).toBeDefined()
expect(transport.sessionId).toBeDefined()
})

it("should list all MCP tools via HTTP", async () => {
const toolResult = await client.listTools()
const names = toolResult.tools.map((tool) => tool.name)

expect(names).toBeArray()
expect(names.length).toBeGreaterThan(0)

// Verify some expected tools exist
expect(names).toContain("get_latest_block")
expect(names).toContain("get_chain_info")
})

it("should call a tool via HTTP", async () => {
const result = await client.callTool({
name: "get_supported_networks",
arguments: {}
})

expect(result).toBeDefined()
expect(result.content).toBeArray()
expect(result.content).toEqual(expect.any(Array));
expect(result.content).not.toHaveLength(0)
})

it("should list prompts via HTTP", async () => {
const promptResult = await client.listPrompts()
expect(promptResult.prompts).toBeArray()
})
})
30 changes: 16 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"description": "A MCP server for BNB Chain that supports BSC, opBNB, Greenfield, and other popular EVM-compatible networks.",
"scripts": {
"start": "node dist/index.js",
"start:http": "npm run start --http",
"start:sse": "npm run start --sse",
"dev:http": "bun run --watch src/index.ts --http",
"dev:sse": "bun run --watch src/index.ts --sse",
"dev": "bun run --watch src/index.ts",
"build": "bun build src/*.ts --outdir dist --target node --format cjs",
Expand All @@ -24,28 +26,28 @@
"access": "public"
},
"dependencies": {
"@bnb-chain/greenfield-js-sdk": "^2.2.1",
"@bnb-chain/greenfield-js-sdk": "^2.2.2",
"@bnb-chain/reed-solomon": "^1.1.4",
"@modelcontextprotocol/sdk": "^1.11.0",
"@modelcontextprotocol/sdk": "^1.21.1",
"cors": "^2.8.5",
"dotenv": "^16.5.0",
"express": "^4.18.2",
"mime": "^4.0.7",
"dotenv": "^16.6.1",
"express": "^4.21.2",
"mime": "^4.1.0",
"reflect-metadata": "^0.2.2",
"viem": "^2.27.2",
"zod": "^3.22.4"
"viem": "^2.38.6",
"zod": "^3.25.76"
},
"devDependencies": {
"@commitlint/cli": "^19.8.0",
"@commitlint/config-conventional": "^19.8.0",
"@commitlint/cli": "^20.1.0",
"@commitlint/config-conventional": "^20.0.0",
"@ianvs/prettier-plugin-sort-imports": "4.1.1",
"@types/bun": "^1.2.12",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/node": "^20.0.0",
"@types/bun": "^1.3.2",
"@types/cors": "^2.8.19",
"@types/express": "^4.17.25",
"@types/node": "^20.19.24",
"husky": "^9.1.7",
"prettier": "3.2.4",
"typescript": "^5.0.0"
"typescript": "^5.9.3"
},
"license": "MIT",
"commitlint": {
Expand Down
10 changes: 7 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
#!/usr/bin/env node
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp"

import { startHTTPServer } from "./server/http"
import { startSSEServer } from "./server/sse"
import { startStdioServer } from "./server/stdio"
import logger from "./utils/logger"

const args = process.argv.slice(2)
const httpMode = args.includes("--http") || args.includes("-h")
const sseMode = args.includes("--sse") || args.includes("-s")

async function main() {
let server: McpServer | undefined
if (sseMode) {
server = await startSSEServer()
if (httpMode) {
server = await startHTTPServer() // Streamable HTTP only
} else if (sseMode) {
server = await startSSEServer() // SSE (deprecated)
} else {
server = await startStdioServer()
}
Expand All @@ -22,7 +26,7 @@ async function main() {
}

const handleShutdown = async () => {
await server.close()
await server?.close()
process.exit(0)
}
// Handle process termination
Expand Down
Loading