Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that e

## Quick Start

Clone the repo and build locally:
Install and run via npm (recommended):

```bash
npx @paystack/mcp-server --api-key sk_test_your_key_here
```

Or for local development, clone and build:

```bash
git clone https://github.com/PaystackOSS/paystack-mcp-server.git
Expand All @@ -16,7 +22,7 @@ npm install
npm run build
```

Then configure your MCP client to use the built server (see [Client Integration](#client-integration)).
Then configure your MCP client to use the server (see [Client Integration](#client-integration)).

## Requirements

Expand All @@ -28,15 +34,19 @@ Then configure your MCP client to use the built server (see [Client Integration]

| Environment Variable | Purpose |
| -------------------------- | ------------------------------------------------------ |
| `PAYSTACK_TEST_SECRET_KEY` | Your Paystack test secret key **(required)** |
| `PAYSTACK_TEST_SECRET_KEY` | Your Paystack test secret key (fallback if no CLI arg) |

You can provide your API key in two ways:
1. **CLI argument (recommended):** `--api-key sk_test_...`
2. **Environment variable:** Set `PAYSTACK_TEST_SECRET_KEY`

> **Security note:** Only test keys (`sk_test_*`) are allowed. The server validates this at startup and will reject live keys.

## Client Integration

The Paystack MCP Server works with any MCP-compatible client. Below is the standard configuration schema used by most clients (Claude Desktop, ChatGPT Desktop, Cursor, Windsurf, etc.).

### Using a local build
### Using npm (recommended)\n\nFor npm-installed server:\n\n```json\n{\n \"mcpServers\": {\n \"paystack\": {\n \"command\": \"npx\",\n \"args\": [\"@paystack/mcp-server\", \"--api-key\", \"sk_test_...\"]\n }\n }\n}\n```\n\n### Using a local build
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you've got some formatting issues here @Andrew-Paystack, I think you need to remove the newline characters


If you've cloned and built the server locally:

Expand Down
174 changes: 174 additions & 0 deletions SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Editor Setup
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have README.md, why the additional file? Seems like this should all just go in the existing file instead

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tolu-paystack I figured that as tools keep increasing, people would like to add how to setup theirs. I wanted to keep Setup separate from the ReadMe so that the read-me is clear.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think let's wait till we get to that point to create a separate file. The readme isn't too long as-is. Plus you can just add a table of contents to the top of the file to make for easier navigation


Configure the Paystack MCP Server in supported editors with secure `.env`-based API key management.

- [Environment Setup](#environment-setup)
- [VS Code](#vs-code)
- [Cursor](#cursor)
- [Claude Desktop](#claude-desktop)

---

## Environment Setup

Create your environment file:

1. **Copy the example file:**

```bash
cp .env.example .env
```

2. **Add your Paystack test secret key:**

```env
PAYSTACK_TEST_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

> [!IMPORTANT]
> - Only **test keys** (starting with `sk_test_`) are accepted. The server rejects live keys.
> - The `.env` file is already in `.gitignore`—never commit it to version control.

---

## VS Code

VS Code supports the `envFile` property, allowing you to load environment variables from a file instead of hardcoding them.

### Configuration

Create or update `.vscode/mcp.json` in your project:

```json
{
"servers": {
"paystack": {
"command": "node",
"args": ["/path/to/paystack-mcp/build/index.js"],
"envFile": "${workspaceFolder}/.env"
}
}
}
```

> [!NOTE]
> Replace `/path/to/paystack-mcp` with the actual path to your cloned repository.

### Reload the MCP Server

After saving the configuration, reload VS Code or run the **"MCP: Restart Server"** command from the Command Palette.

---

## Cursor

Cursor supports both `envFile` and environment variable interpolation via `${env:VAR_NAME}`.

### Configuration Locations

| Scope | File Path |
| ------- | ------------------------ |
| Project | `.cursor/mcp.json` |
| Global | `~/.cursor/mcp.json` |

### Using `envFile` (Recommended)

Create `.cursor/mcp.json` in your project:

```json
{
"mcpServers": {
"paystack": {
"command": "node",
"args": ["/path/to/paystack-mcp/build/index.js"],
"envFile": "${workspaceFolder}/.env"
}
}
}
```

---

## Claude Desktop

Claude Desktop uses an inline `env` object for environment variables. It does not support `envFile`.

### Configuration Location

| OS | File Path |
| ------- | ------------------------------------------------------------ |
| macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Windows | `%APPDATA%\Claude\claude_desktop_config.json` |

### Approach A: Inline Environment Variables (Simple)

Edit your `claude_desktop_config.json`:

```json
{
"mcpServers": {
"paystack": {
"command": "node",
"args": ["/path/to/paystack-mcp/build/index.js"],
"env": {
"PAYSTACK_SECRET_KEY": "sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}
}
}
```

> [!WARNING]
> This approach stores your API key directly in the config file. Ensure this file is not shared or committed to version control.

### Approach B: Using a Wrapper Script (Secure)

For better security, create a shell script that loads your `.env` file before starting the server.

1. **Create a wrapper script** (e.g., `run-paystack-mcp.sh`):

```bash
#!/bin/bash
set -a
source /path/to/paystack-mcp/.env
set +a
exec node /path/to/paystack-mcp/build/index.js
```

2. **Make it executable:**

```bash
chmod +x /path/to/run-paystack-mcp.sh
```

3. **Update your Claude Desktop config:**

```json
{
"mcpServers": {
"paystack": {
"command": "/path/to/run-paystack-mcp.sh"
}
}
}
```

---

## Troubleshooting

### Server not starting

- Verify Node.js v18+ is installed: `node --version`
- Check the path to `build/index.js` is correct
- Ensure your `.env` file exists and contains a valid `sk_test_*` key

### Environment variables not loading

- For VS Code/Cursor: Confirm `envFile` path is correct and the file exists
- For Claude Desktop: Restart the application after config changes

### "Invalid API key" errors

- Ensure your key starts with `sk_test_` (live will be rejected)
- Check for trailing whitespace in your `.env` file
23 changes: 17 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
{
"name": "paystack-mcp",
"name": "@paystack/mcp-server",
"version": "0.0.1",
"description": "",
"description": "Model Context Protocol (MCP) server for Paystack API integration",
"mcpName": "io.github.PaystackOSS/paystack",
"repository": {
"type": "git",
"url": "https://github.com/PaystackOSS/paystack-mcp-server.git"
},
"bin": {
"paystack": "./build/index.js"
"paystack-mcp": "./build/index.js"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use paystack-mcp-server

},
"scripts": {
"build": "tsc && cp -r src/data build/",
Expand All @@ -15,14 +20,20 @@
"files": [
"build"
],
"keywords": [],
"keywords": [
"paystack",
"mcp",
"model-context-protocol",
"api",
"payment",
"integration"
],
"author": "Andrew-Paystack",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/inspector": "^0.18.0",
"@modelcontextprotocol/sdk": "^1.26.0",
"dotenv": "^17.2.3",
"zod": "^4.3.6"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see zod is still being imported at the beginning of make-paystack-request.ts, won't the build fail if you've removed it as a dependency?

"dotenv": "^17.2.3"
},
"devDependencies": {
"@apidevtools/swagger-parser": "^12.1.0",
Expand Down
71 changes: 38 additions & 33 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,46 @@
import dotenv from 'dotenv';
import { z } from 'zod';

// Load environment variables from .env file
dotenv.config();
dotenv.config({quiet: true});

// Define schema for required environment variables
const envSchema = z.object({
PAYSTACK_TEST_SECRET_KEY: z.string().min(30, 'PAYSTACK_TEST_SECRET_KEY is required').refine(val => val.startsWith('sk_test_'), {
message: 'PAYSTACK_TEST_SECRET_KEY must begin with "sk_test_. No live keys allowed."',
}),
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
});

// Validate environment variables
function validateEnv() {
try {
return envSchema.parse({
PAYSTACK_TEST_SECRET_KEY: process.env.PAYSTACK_TEST_SECRET_KEY,
NODE_ENV: process.env.NODE_ENV || 'development',
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
});
} catch (error) {
if (error instanceof z.ZodError) {
// Environment validation failed - exit silently
process.exit(1);
}
throw error;
// Get configuration with optional CLI API key
export function getConfig(cliApiKey?: string) {
const apiKey = cliApiKey || process.env.PAYSTACK_TEST_SECRET_KEY;

if (!apiKey) {
console.error('Error: PAYSTACK_TEST_SECRET_KEY is required');
process.exit(1);
}

if (!apiKey.startsWith('sk_test_')) {
console.error('Error: PAYSTACK_TEST_SECRET_KEY must begin with "sk_test_". No live keys allowed.');
process.exit(1);
}

if (apiKey.length < 30) {
console.error('Error: PAYSTACK_TEST_SECRET_KEY appears to be too short');
process.exit(1);
}

return {
PAYSTACK_TEST_SECRET_KEY: apiKey,
NODE_ENV: (process.env.NODE_ENV as 'development' | 'production' | 'test') || 'development',
LOG_LEVEL: (process.env.LOG_LEVEL as 'debug' | 'info' | 'warn' | 'error') || 'info',
};
}

// Export validated configuration
export const config = validateEnv();
// Export validated configuration (for backward compatibility, will use env var)
export const config = getConfig();

// Paystack API configuration factory
export function createPaystackConfig(cliApiKey?: string) {
const cfg = getConfig(cliApiKey);
return {
baseURL: 'https://api.paystack.co',
secretKey: cfg.PAYSTACK_TEST_SECRET_KEY,
timeout: 30000, // 30 seconds
} as const;
}

// Paystack API configuration
export const paystackConfig = {
baseURL: 'https://api.paystack.co',
secretKey: config.PAYSTACK_TEST_SECRET_KEY,
timeout: 30000, // 30 seconds
} as const;
// Default paystack config (for backward compatibility)
export const paystackConfig = createPaystackConfig();
Loading