Skip to content

Commit c505ec8

Browse files
Enforce TOOLS-based access control with exact tool validation (#3)
* - Enforce TOOLS-based access control with exact tool validation (no wildcards) - Add AccessControlService (readonly config) and integrate checks in ListTools/CallTool - Centralize valid tool names from modules - Require TOOLS env; update envs, compose, mcp.json; docs and tests updated - Update README.md - Add test cases for access control * Fix not found issue for valid-tools file * docs: document TOOLS environment variable and access control patterns - Add TOOLS to environment setup section with examples - Add TOOLS to environment variables table as required - Add TOOLS verification to configuration check - Improve Tool Access Control section with clearer documentation - Add common configuration patterns (read-only, payment, finance) - Update error handling documentation with better examples - Enhance security best practices with specific role examples --------- Co-authored-by: nitish.k <nitish@opn.ooo>
1 parent 67f9cae commit c505ec8

File tree

9 files changed

+943
-43
lines changed

9 files changed

+943
-43
lines changed

README.md

Lines changed: 200 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868

6969
### Prerequisites
7070

71-
- Node.js 20+
71+
- Node.js 20+
7272
- npm or yarn
7373
- [Omise Account](https://dashboard.omise.co/) and API keys
7474

@@ -96,6 +96,11 @@ export OMISE_ENVIRONMENT=test
9696
export OMISE_API_VERSION=2019-05-29
9797
export OMISE_BASE_URL=https://api.omise.co
9898
export OMISE_VAULT_URL=https://vault.omise.co
99+
100+
# Set tool access control (mandatory)
101+
export TOOLS=all # For development only
102+
# Or for production, specify exact tools:
103+
# export TOOLS=create_charge,retrieve_charge,list_charges,create_customer
99104
```
100105

101106
#### 2.4. Environment-Specific Configuration
@@ -130,6 +135,7 @@ npm run dev
130135
# Or verify with a simple check
131136
echo $OMISE_PUBLIC_KEY | grep -q "pkey_" && echo "✅ Public key configured" || echo "❌ Public key missing"
132137
echo $OMISE_SECRET_KEY | grep -q "skey_" && echo "✅ Secret key configured" || echo "❌ Secret key missing"
138+
echo $TOOLS | grep -q "." && echo "✅ TOOLS configured: $TOOLS" || echo "❌ TOOLS not set (required)"
133139
```
134140

135141
### 3. Start Development Server
@@ -160,27 +166,27 @@ curl http://localhost:3000/tools
160166
```typescript
161167
// Create a charge
162168
const charge = await mcpClient.callTool('create_charge', {
163-
amount: 10000, // 100.00 THB (smallest currency unit)
164-
currency: 'THB',
165-
description: 'Test payment',
166-
capture: true
169+
amount: 10000, // 100.00 THB (smallest currency unit)
170+
currency: 'THB',
171+
description: 'Test payment',
172+
capture: true
167173
});
168174

169175
// Create a customer
170176
const customer = await mcpClient.callTool('create_customer', {
171-
email: 'customer@example.com',
172-
description: 'Test customer'
177+
email: 'customer@example.com',
178+
description: 'Test customer'
173179
});
174180

175181
// Create a card token
176182
const token = await mcpClient.callTool('create_token', {
177-
card: {
178-
name: 'John Doe',
179-
number: '4242424242424242',
180-
expiration_month: 12,
181-
expiration_year: 2025,
182-
security_code: '123'
183-
}
183+
card: {
184+
name: 'John Doe',
185+
number: '4242424242424242',
186+
expiration_month: 12,
187+
expiration_year: 2025,
188+
security_code: '123'
189+
}
184190
});
185191
```
186192

@@ -189,15 +195,15 @@ const token = await mcpClient.callTool('create_token', {
189195
```typescript
190196
// Create a schedule
191197
const schedule = await mcpClient.callTool('create_schedule', {
192-
every: 1,
193-
period: 'month',
194-
start_date: '2024-01-01',
195-
charge: {
196-
customer: 'cust_123',
197-
amount: 5000,
198-
currency: 'THB',
199-
description: 'Monthly subscription'
200-
}
198+
every: 1,
199+
period: 'month',
200+
start_date: '2024-01-01',
201+
charge: {
202+
customer: 'cust_123',
203+
amount: 5000,
204+
currency: 'THB',
205+
description: 'Monthly subscription'
206+
}
201207
});
202208
```
203209

@@ -206,20 +212,20 @@ const schedule = await mcpClient.callTool('create_schedule', {
206212
```typescript
207213
// Create a recipient
208214
const recipient = await mcpClient.callTool('create_recipient', {
209-
name: 'John Doe',
210-
email: 'john@example.com',
211-
type: 'individual',
212-
bank_account: {
213-
brand: 'bbl',
214-
number: '1234567890',
215-
name: 'John Doe'
216-
}
215+
name: 'John Doe',
216+
email: 'john@example.com',
217+
type: 'individual',
218+
bank_account: {
219+
brand: 'bbl',
220+
number: '1234567890',
221+
name: 'John Doe'
222+
}
217223
});
218224

219225
// Execute transfer
220226
const transfer = await mcpClient.callTool('create_transfer', {
221-
amount: 10000,
222-
recipient: recipient.id
227+
amount: 10000,
228+
recipient: recipient.id
223229
});
224230
```
225231

@@ -232,6 +238,7 @@ const transfer = await mcpClient.callTool('create_transfer', {
232238
| `OMISE_PUBLIC_KEY` | Omise public key || - |
233239
| `OMISE_SECRET_KEY` | Omise secret key || - |
234240
| `OMISE_ENVIRONMENT` | Environment (test/production) || - |
241+
| `TOOLS` | Comma-separated list of allowed tools or 'all' || - |
235242
| `PORT` | Server port | - | 3000 |
236243
| `HOST` | Server host | - | localhost |
237244
| `LOG_LEVEL` | Log level | - | info |
@@ -425,6 +432,159 @@ docker-compose logs -f
425432
- **Rate limiting**: API call restrictions
426433
- **Sensitive data masking**: Hide sensitive information in logs
427434
- **Environment isolation**: Complete separation of test and production environments
435+
- **Tool Access Control**: Granular control over which API tools clients can access
436+
437+
### Tool Access Control
438+
439+
The MCP server requires explicit tool access configuration for enhanced security. Each client must specify which Omise API tools they are authorized to use.
440+
441+
#### Configuration
442+
443+
Set the `TOOLS` environment variable (**mandatory**). The server will not start without this configuration.
444+
445+
**Options:**
446+
- `TOOLS=all` - Full access to all 50 tools (development only, not recommended for production)
447+
- `TOOLS=tool1,tool2,...` - Comma-separated list of specific tools (recommended for production)
448+
449+
**Common Patterns:**
450+
- **Read-only access**: `TOOLS=list_charges,retrieve_charge,list_customers,retrieve_customer`
451+
- **Payment processing**: `TOOLS=create_charge,retrieve_charge,capture_charge,create_customer,create_token`
452+
- **Finance operations**: `TOOLS=list_charges,retrieve_charge,create_refund,create_transfer`
453+
454+
#### Examples
455+
456+
**Full access (development/testing):**
457+
```bash
458+
export TOOLS=all
459+
docker-compose up
460+
```
461+
462+
**Read-only access (monitoring/analytics):**
463+
```bash
464+
export TOOLS=list_charges,retrieve_charge,list_customers,retrieve_customer
465+
docker-compose up
466+
```
467+
468+
**Payment processing only:**
469+
```bash
470+
export TOOLS=create_charge,retrieve_charge,capture_charge,create_customer,create_token
471+
docker-compose up
472+
```
473+
474+
**Podman with specific tools:**
475+
```bash
476+
podman run --rm -i \
477+
-e OMISE_PUBLIC_KEY=pkey_test_xxx \
478+
-e OMISE_SECRET_KEY=skey_test_xxx \
479+
-e OMISE_ENVIRONMENT=test \
480+
-e TOOLS=create_charge,list_charges,create_customer \
481+
omise-mcp-server:latest
482+
```
483+
484+
#### Available Tools by Category
485+
486+
| Category | Tools | Description |
487+
|----------|-------|-------------|
488+
| **Charges** | `create_charge`, `retrieve_charge`, `list_charges`, `update_charge`, `capture_charge`, `reverse_charge`, `expire_charge` | Payment charge operations |
489+
| **Customers** | `create_customer`, `retrieve_customer`, `list_customers`, `update_customer`, `destroy_customer` | Customer management |
490+
| **Cards** | `list_customer_cards`, `retrieve_customer_card`, `update_customer_card`, `destroy_customer_card` | Card management |
491+
| **Tokens** | `create_token`, `retrieve_token` | Tokenization |
492+
| **Sources** | `create_source`, `retrieve_source` | Payment sources |
493+
| **Transfers** | `create_transfer`, `retrieve_transfer`, `list_transfers`, `update_transfer`, `destroy_transfer` | Transfer operations |
494+
| **Recipients** | `create_recipient`, `retrieve_recipient`, `list_recipients`, `update_recipient`, `destroy_recipient`, `verify_recipient` | Recipient management |
495+
| **Refunds** | `create_refund`, `retrieve_refund`, `list_refunds` | Refund processing |
496+
| **Disputes** | `list_disputes`, `retrieve_dispute`, `accept_dispute`, `update_dispute`, `list_dispute_documents`, `retrieve_dispute_document`, `upload_dispute_document`, `destroy_dispute_document` | Dispute handling |
497+
| **Schedules** | `create_schedule`, `retrieve_schedule`, `list_schedules`, `destroy_schedule`, `list_schedule_occurrences` | Recurring payments |
498+
| **Events** | `list_events`, `retrieve_event` | Event tracking |
499+
| **Capabilities** | `retrieve_capability` | Feature verification |
500+
501+
#### Error Handling
502+
503+
The server will **fail to start** if:
504+
- `TOOLS` environment variable is not set
505+
- `TOOLS` is empty or contains only whitespace
506+
- `TOOLS` contains invalid tool names (e.g., `TOOLS=hello,invalid_tool`)
507+
508+
Clients will receive an **authorization error** if:
509+
- They attempt to call a tool not in their allowed list
510+
511+
**Example Errors:**
512+
513+
```bash
514+
# Missing TOOLS environment variable
515+
Error: Missing required environment variable: TOOLS
516+
Set TOOLS=all for full access, or specify comma-separated tool names.
517+
Example: TOOLS=create_charge,list_charges,create_customer
518+
519+
# Invalid tool names
520+
Error: Invalid tool names: hello, invalid_tool
521+
Valid tools are: create_charge, retrieve_charge, list_charges, ... (50 total)
522+
Use TOOLS=all for full access.
523+
```
524+
525+
**Runtime Behavior:**
526+
527+
When `TOOLS` is properly configured:
528+
- Only authorized tools appear in `list_tools` responses
529+
- Unauthorized tools are not accessible to clients
530+
- Access control is enforced at the MCP protocol level
531+
532+
#### Security Best Practices
533+
534+
1. **Principle of Least Privilege**: Only grant access to tools absolutely necessary for the role
535+
2. **Production Restrictions**: Never use `TOOLS=all` in production - always specify exact tools
536+
3. **Role-Based Deployment**: Run separate MCP server instances for different user roles:
537+
- **Read-Only (Analytics/Support)**: `list_charges,retrieve_charge,list_customers,retrieve_customer`
538+
- **Payment Processing (Merchants)**: `create_charge,retrieve_charge,capture_charge,create_customer,create_token`
539+
- **Finance Operations**: `list_charges,create_refund,create_transfer,create_recipient`
540+
- **Admin (Development/Emergency)**: `all` (use with caution)
541+
4. **Regular Audits**: Review and document tool access configurations periodically
542+
5. **Environment Separation**: Use different TOOLS configurations for dev, staging, and production
543+
6. **Configuration Management**: Store TOOLS settings in environment-specific config files
544+
545+
#### Multiple Client Configurations
546+
547+
Use Cursor's `mcp.json` to configure multiple clients with different access levels:
548+
549+
```json
550+
{
551+
"mcpServers": {
552+
"omise-admin": {
553+
"command": "docker",
554+
"args": [
555+
"run", "--rm", "-i",
556+
"-e", "OMISE_PUBLIC_KEY=pkey_xxx",
557+
"-e", "OMISE_SECRET_KEY=skey_xxx",
558+
"-e", "OMISE_ENVIRONMENT=production",
559+
"-e", "TOOLS=all",
560+
"omise-mcp-server:latest"
561+
]
562+
},
563+
"omise-readonly": {
564+
"command": "docker",
565+
"args": [
566+
"run", "--rm", "-i",
567+
"-e", "OMISE_PUBLIC_KEY=pkey_xxx",
568+
"-e", "OMISE_SECRET_KEY=skey_xxx",
569+
"-e", "OMISE_ENVIRONMENT=production",
570+
"-e", "TOOLS=list_charges,retrieve_charge,list_customers,retrieve_customer",
571+
"omise-mcp-server:latest"
572+
]
573+
},
574+
"omise-payment": {
575+
"command": "docker",
576+
"args": [
577+
"run", "--rm", "-i",
578+
"-e", "OMISE_PUBLIC_KEY=pkey_xxx",
579+
"-e", "OMISE_SECRET_KEY=skey_xxx",
580+
"-e", "OMISE_ENVIRONMENT=production",
581+
"-e", "TOOLS=create_charge,retrieve_charge,capture_charge,create_customer,create_token",
582+
"omise-mcp-server:latest"
583+
]
584+
}
585+
}
586+
}
587+
```
428588

429589
### SSL/TLS Configuration
430590

@@ -545,13 +705,13 @@ Create a secure card token for payment processing.
545705

546706
**Parameters:**
547707
- `card` (required): Card information
548-
- `name` (required): Cardholder name
549-
- `number` (required): Card number
550-
- `expiration_month` (required): Expiration month (1-12)
551-
- `expiration_year` (required): Expiration year (4 digits)
552-
- `city` (optional): Billing address city
553-
- `postal_code` (optional): Billing address postal code
554-
- `security_code` (optional): Security code (CVV/CVC)
708+
- `name` (required): Cardholder name
709+
- `number` (required): Card number
710+
- `expiration_month` (required): Expiration month (1-12)
711+
- `expiration_year` (required): Expiration year (4 digits)
712+
- `city` (optional): Billing address city
713+
- `postal_code` (optional): Billing address postal code
714+
- `security_code` (optional): Security code (CVV/CVC)
555715

556716
## 🔗 External Links
557717

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ services:
2121
environment:
2222
- NODE_ENV=production
2323
- PORT=3000
24+
- TOOLS=${TOOLS:-all}
2425
- LOG_LEVEL=info
2526
- LOG_FORMAT=json
2627
- OMISE_TIMEOUT=30000

0 commit comments

Comments
 (0)