@purplesquirrel/oracle-mcp-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "npm"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ open-pull-requests-limit: 10
8
+ labels:
9
+ - "dependencies"
10
+ commit-message:
11
+ prefix: "chore(deps)"
12
+
13
+ - package-ecosystem: "github-actions"
14
+ directory: "/"
15
+ schedule:
16
+ interval: "weekly"
17
+ labels:
18
+ - "dependencies"
19
+ - "github-actions"
20
+ commit-message:
21
+ prefix: "chore(ci)"
@@ -0,0 +1,36 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+
13
+ strategy:
14
+ matrix:
15
+ node-version: [18.x, 20.x]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Use Node.js ${{ matrix.node-version }}
21
+ uses: actions/setup-node@v4
22
+ with:
23
+ node-version: ${{ matrix.node-version }}
24
+ cache: 'npm'
25
+
26
+ - name: Install dependencies
27
+ run: npm ci
28
+
29
+ - name: Run linter
30
+ run: npm run lint --if-present
31
+
32
+ - name: Run tests
33
+ run: npm test --if-present
34
+
35
+ - name: Build
36
+ run: npm run build --if-present
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Purple Squirrel Media
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,192 @@
1
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
2
+ [![MCP](https://img.shields.io/badge/MCP-Server-blue)](https://modelcontextprotocol.io)
3
+ [![OCI](https://img.shields.io/badge/Oracle-Cloud-F80000)](https://cloud.oracle.com)
4
+ [![CI](https://github.com/PurpleSquirrelMedia/oracle-mcp-server/actions/workflows/ci.yml/badge.svg)](https://github.com/PurpleSquirrelMedia/oracle-mcp-server/actions/workflows/ci.yml)
5
+
6
+ # Oracle Cloud Infrastructure MCP Server
7
+
8
+ MCP server for Oracle Cloud Infrastructure (OCI) integration with Claude Code. Provides comprehensive access to OCI services including Compute, Storage, Networking, Database, and IAM.
9
+
10
+ ## Features
11
+
12
+ - **Compute** - List, manage, and control VM instances
13
+ - **Object Storage** - Manage buckets and objects
14
+ - **Block Storage** - List block and boot volumes
15
+ - **Networking** - VCNs, subnets, and network management
16
+ - **Autonomous Database** - ATP/ADW database operations
17
+ - **IAM** - Users, groups, policies, and compartments
18
+
19
+ ## Available Tools (21 total)
20
+
21
+ ### Compute (4 tools)
22
+ | Tool | Description |
23
+ |------|-------------|
24
+ | `oci_compute_list_instances` | List all compute instances in a compartment |
25
+ | `oci_compute_get_instance` | Get detailed info about a specific instance |
26
+ | `oci_compute_list_shapes` | List available shapes (incl. Always Free) |
27
+ | `oci_compute_instance_action` | Perform actions (START, STOP, RESET, etc.) |
28
+
29
+ ### Object Storage (5 tools)
30
+ | Tool | Description |
31
+ |------|-------------|
32
+ | `oci_os_get_namespace` | Get the Object Storage namespace |
33
+ | `oci_os_list_buckets` | List all buckets in compartment |
34
+ | `oci_os_create_bucket` | Create a new bucket |
35
+ | `oci_os_list_objects` | List objects in a bucket |
36
+ | `oci_os_delete_bucket` | Delete an empty bucket |
37
+
38
+ ### Block Storage (2 tools)
39
+ | Tool | Description |
40
+ |------|-------------|
41
+ | `oci_bv_list_volumes` | List block volumes |
42
+ | `oci_bv_list_boot_volumes` | List boot volumes |
43
+
44
+ ### Networking (3 tools)
45
+ | Tool | Description |
46
+ |------|-------------|
47
+ | `oci_vcn_list` | List Virtual Cloud Networks |
48
+ | `oci_subnet_list` | List subnets in a VCN |
49
+ | `oci_vcn_create` | Create a new VCN |
50
+
51
+ ### Autonomous Database (4 tools)
52
+ | Tool | Description |
53
+ |------|-------------|
54
+ | `oci_adb_list` | List Autonomous Databases |
55
+ | `oci_adb_get` | Get database details |
56
+ | `oci_adb_start` | Start a stopped database |
57
+ | `oci_adb_stop` | Stop a running database |
58
+
59
+ ### IAM (5 tools)
60
+ | Tool | Description |
61
+ |------|-------------|
62
+ | `oci_iam_list_users` | List IAM users |
63
+ | `oci_iam_list_groups` | List IAM groups |
64
+ | `oci_iam_list_policies` | List IAM policies |
65
+ | `oci_iam_list_compartments` | List compartments |
66
+ | `oci_iam_list_availability_domains` | List availability domains |
67
+
68
+ ## Setup
69
+
70
+ ### 1. Install Dependencies
71
+
72
+ ```bash
73
+ cd ~/mcp-servers/oracle-mcp
74
+ npm install
75
+ ```
76
+
77
+ ### 2. Configure OCI
78
+
79
+ Ensure you have OCI CLI configured with a valid config file:
80
+
81
+ ```bash
82
+ # ~/.oci/config should contain:
83
+ [DEFAULT]
84
+ user=ocid1.user.oc1..xxx
85
+ fingerprint=xx:xx:xx:xx:xx
86
+ tenancy=ocid1.tenancy.oc1..xxx
87
+ region=us-chicago-1
88
+ key_file=~/.oci/api_keys/oci_api_key.pem
89
+ ```
90
+
91
+ Or use session token authentication:
92
+ ```bash
93
+ oci session authenticate
94
+ ```
95
+
96
+ ### 3. Add to Claude Code
97
+
98
+ Add to `~/.claude.json`:
99
+
100
+ ```json
101
+ {
102
+ "mcpServers": {
103
+ "oracle": {
104
+ "type": "stdio",
105
+ "command": "node",
106
+ "args": ["/Users/matthewkarsten/mcp-servers/oracle-mcp/index.js"],
107
+ "env": {
108
+ "OCI_CONFIG_FILE": "/Users/matthewkarsten/.oci/config",
109
+ "OCI_PROFILE": "DEFAULT",
110
+ "OCI_REGION": "us-chicago-1"
111
+ }
112
+ }
113
+ }
114
+ }
115
+ ```
116
+
117
+ ## Free Tier Resources
118
+
119
+ Oracle Cloud Free Tier includes:
120
+
121
+ | Resource | Free Allocation |
122
+ |----------|-----------------|
123
+ | Compute (Ampere A1) | 4 OCPUs, 24 GB RAM |
124
+ | Compute (AMD E2.1.Micro) | 2 instances |
125
+ | Object Storage | 20 GB Standard + 20 GB Archive |
126
+ | Block Storage | 200 GB total |
127
+ | Autonomous Database | 2 Always Free databases |
128
+ | Outbound Data | 10 TB/month |
129
+
130
+ ## Architecture
131
+
132
+ ```
133
+ Claude Code (Opus 4.5)
134
+
135
+ └──▶ Oracle MCP Server
136
+
137
+ └──▶ OCI SDK
138
+
139
+ ├── Compute Service
140
+ ├── Object Storage
141
+ ├── Block Storage
142
+ ├── Virtual Network
143
+ ├── Database Service
144
+ └── Identity Service
145
+ ```
146
+
147
+ ## Authentication
148
+
149
+ The server supports two authentication methods:
150
+
151
+ 1. **Session Token** (recommended for interactive use)
152
+ - Uses `security_token_file` from config
153
+ - Refreshable with `oci session refresh`
154
+
155
+ 2. **API Key** (for automation)
156
+ - Uses RSA key pair
157
+ - Requires fingerprint in OCI config
158
+
159
+ ## Usage Examples
160
+
161
+ ```
162
+ User: List my OCI compute instances
163
+
164
+ Claude: [Uses oci_compute_list_instances tool]
165
+ Result:
166
+ - web-server-1 (VM.Standard.A1.Flex) - RUNNING
167
+ - db-server (VM.Standard.E2.1.Micro) - STOPPED
168
+
169
+ User: Start the db-server instance
170
+
171
+ Claude: [Uses oci_compute_instance_action with action=START]
172
+ Result: Instance db-server is now starting...
173
+ ```
174
+
175
+ ## Files
176
+
177
+ - `index.js` - MCP server implementation
178
+ - `package.json` - Dependencies
179
+ - `README.md` - This file
180
+
181
+ ## Dependencies
182
+
183
+ - `@modelcontextprotocol/sdk` - MCP SDK
184
+ - `oci-sdk` - Official Oracle Cloud SDK
185
+
186
+ ## Author
187
+
188
+ Matthew Karsten
189
+
190
+ ## License
191
+
192
+ MIT
package/index.js ADDED
@@ -0,0 +1,813 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Oracle Cloud Infrastructure (OCI) MCP Server
5
+ *
6
+ * Provides Claude Code with access to Oracle Cloud services:
7
+ * - Compute (VM instances, shapes)
8
+ * - Object Storage (buckets, objects)
9
+ * - Block Storage (boot/block volumes)
10
+ * - Networking (VCNs, subnets, security lists)
11
+ * - Autonomous Database (ATP, ADW)
12
+ * - IAM (users, groups, policies)
13
+ */
14
+
15
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
16
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
17
+ import {
18
+ CallToolRequestSchema,
19
+ ListToolsRequestSchema,
20
+ } from "@modelcontextprotocol/sdk/types.js";
21
+ import * as oci from "oci-sdk";
22
+ import * as fs from "fs";
23
+ import * as path from "path";
24
+
25
+ // Configuration
26
+ const CONFIG_FILE = process.env.OCI_CONFIG_FILE || path.join(process.env.HOME, ".oci", "config");
27
+ const PROFILE = process.env.OCI_PROFILE || "DEFAULT";
28
+ const TENANCY_OCID = process.env.OCI_TENANCY_OCID;
29
+ const REGION = process.env.OCI_REGION || "us-chicago-1";
30
+
31
+ // Initialize OCI clients
32
+ let computeClient, objectStorageClient, blockStorageClient,
33
+ virtualNetworkClient, databaseClient, identityClient;
34
+
35
+ async function initializeClients() {
36
+ try {
37
+ // Try session token auth first, then fall back to API key
38
+ let provider;
39
+
40
+ const configFile = fs.readFileSync(CONFIG_FILE, "utf8");
41
+ if (configFile.includes("security_token_file")) {
42
+ // Session token authentication
43
+ provider = new oci.common.SessionAuthDetailProvider(CONFIG_FILE, PROFILE);
44
+ } else {
45
+ // API key authentication
46
+ provider = new oci.common.ConfigFileAuthenticationDetailsProvider(CONFIG_FILE, PROFILE);
47
+ }
48
+
49
+ computeClient = new oci.core.ComputeClient({ authenticationDetailsProvider: provider });
50
+ objectStorageClient = new oci.objectstorage.ObjectStorageClient({ authenticationDetailsProvider: provider });
51
+ blockStorageClient = new oci.core.BlockstorageClient({ authenticationDetailsProvider: provider });
52
+ virtualNetworkClient = new oci.core.VirtualNetworkClient({ authenticationDetailsProvider: provider });
53
+ databaseClient = new oci.database.DatabaseClient({ authenticationDetailsProvider: provider });
54
+ identityClient = new oci.identity.IdentityClient({ authenticationDetailsProvider: provider });
55
+
56
+ console.error("OCI clients initialized successfully");
57
+ return true;
58
+ } catch (error) {
59
+ console.error("Failed to initialize OCI clients:", error.message);
60
+ return false;
61
+ }
62
+ }
63
+
64
+ // Helper to get compartment ID (default to tenancy)
65
+ function getCompartmentId(params) {
66
+ return params.compartment_id || TENANCY_OCID || process.env.OCI_TENANCY_OCID;
67
+ }
68
+
69
+ // Tool handlers
70
+ const toolHandlers = {
71
+ // ==================== COMPUTE ====================
72
+
73
+ async oci_compute_list_instances(params) {
74
+ const response = await computeClient.listInstances({
75
+ compartmentId: getCompartmentId(params),
76
+ limit: params.limit || 50,
77
+ });
78
+
79
+ return response.items.map(instance => ({
80
+ id: instance.id,
81
+ displayName: instance.displayName,
82
+ shape: instance.shape,
83
+ lifecycleState: instance.lifecycleState,
84
+ availabilityDomain: instance.availabilityDomain,
85
+ region: instance.region,
86
+ timeCreated: instance.timeCreated,
87
+ }));
88
+ },
89
+
90
+ async oci_compute_get_instance(params) {
91
+ const response = await computeClient.getInstance({
92
+ instanceId: params.instance_id,
93
+ });
94
+
95
+ return {
96
+ id: response.instance.id,
97
+ displayName: response.instance.displayName,
98
+ shape: response.instance.shape,
99
+ lifecycleState: response.instance.lifecycleState,
100
+ availabilityDomain: response.instance.availabilityDomain,
101
+ faultDomain: response.instance.faultDomain,
102
+ region: response.instance.region,
103
+ imageId: response.instance.imageId,
104
+ timeCreated: response.instance.timeCreated,
105
+ metadata: response.instance.metadata,
106
+ shapeConfig: response.instance.shapeConfig,
107
+ };
108
+ },
109
+
110
+ async oci_compute_list_shapes(params) {
111
+ const response = await computeClient.listShapes({
112
+ compartmentId: getCompartmentId(params),
113
+ limit: params.limit || 100,
114
+ });
115
+
116
+ return response.items.map(shape => ({
117
+ shape: shape.shape,
118
+ processorDescription: shape.processorDescription,
119
+ ocpus: shape.ocpus,
120
+ memoryInGBs: shape.memoryInGBs,
121
+ networkingBandwidthInGbps: shape.networkingBandwidthInGbps,
122
+ maxVnicAttachments: shape.maxVnicAttachments,
123
+ gpus: shape.gpus,
124
+ isFlexible: shape.isFlexible,
125
+ }));
126
+ },
127
+
128
+ async oci_compute_instance_action(params) {
129
+ const response = await computeClient.instanceAction({
130
+ instanceId: params.instance_id,
131
+ action: params.action, // START, STOP, RESET, SOFTSTOP, SOFTRESET
132
+ });
133
+
134
+ return {
135
+ id: response.instance.id,
136
+ displayName: response.instance.displayName,
137
+ lifecycleState: response.instance.lifecycleState,
138
+ action: params.action,
139
+ };
140
+ },
141
+
142
+ // ==================== OBJECT STORAGE ====================
143
+
144
+ async oci_os_get_namespace(params) {
145
+ const response = await objectStorageClient.getNamespace({
146
+ compartmentId: getCompartmentId(params),
147
+ });
148
+ return { namespace: response.value };
149
+ },
150
+
151
+ async oci_os_list_buckets(params) {
152
+ const nsResponse = await objectStorageClient.getNamespace({
153
+ compartmentId: getCompartmentId(params),
154
+ });
155
+
156
+ const response = await objectStorageClient.listBuckets({
157
+ namespaceName: nsResponse.value,
158
+ compartmentId: getCompartmentId(params),
159
+ limit: params.limit || 100,
160
+ });
161
+
162
+ return response.items.map(bucket => ({
163
+ name: bucket.name,
164
+ namespace: bucket.namespace,
165
+ compartmentId: bucket.compartmentId,
166
+ createdBy: bucket.createdBy,
167
+ timeCreated: bucket.timeCreated,
168
+ etag: bucket.etag,
169
+ }));
170
+ },
171
+
172
+ async oci_os_create_bucket(params) {
173
+ const nsResponse = await objectStorageClient.getNamespace({
174
+ compartmentId: getCompartmentId(params),
175
+ });
176
+
177
+ const response = await objectStorageClient.createBucket({
178
+ namespaceName: nsResponse.value,
179
+ createBucketDetails: {
180
+ name: params.bucket_name,
181
+ compartmentId: getCompartmentId(params),
182
+ publicAccessType: params.public_access || "NoPublicAccess",
183
+ storageTier: params.storage_tier || "Standard",
184
+ },
185
+ });
186
+
187
+ return {
188
+ name: response.bucket.name,
189
+ namespace: response.bucket.namespace,
190
+ compartmentId: response.bucket.compartmentId,
191
+ timeCreated: response.bucket.timeCreated,
192
+ };
193
+ },
194
+
195
+ async oci_os_list_objects(params) {
196
+ const nsResponse = await objectStorageClient.getNamespace({
197
+ compartmentId: getCompartmentId(params),
198
+ });
199
+
200
+ const response = await objectStorageClient.listObjects({
201
+ namespaceName: nsResponse.value,
202
+ bucketName: params.bucket_name,
203
+ prefix: params.prefix,
204
+ limit: params.limit || 100,
205
+ });
206
+
207
+ return response.listObjects.objects.map(obj => ({
208
+ name: obj.name,
209
+ size: obj.size,
210
+ md5: obj.md5,
211
+ timeCreated: obj.timeCreated,
212
+ timeModified: obj.timeModified,
213
+ }));
214
+ },
215
+
216
+ async oci_os_delete_bucket(params) {
217
+ const nsResponse = await objectStorageClient.getNamespace({
218
+ compartmentId: getCompartmentId(params),
219
+ });
220
+
221
+ await objectStorageClient.deleteBucket({
222
+ namespaceName: nsResponse.value,
223
+ bucketName: params.bucket_name,
224
+ });
225
+
226
+ return { deleted: true, bucket: params.bucket_name };
227
+ },
228
+
229
+ // ==================== BLOCK STORAGE ====================
230
+
231
+ async oci_bv_list_volumes(params) {
232
+ const response = await blockStorageClient.listVolumes({
233
+ compartmentId: getCompartmentId(params),
234
+ limit: params.limit || 50,
235
+ });
236
+
237
+ return response.items.map(vol => ({
238
+ id: vol.id,
239
+ displayName: vol.displayName,
240
+ sizeInGBs: vol.sizeInGBs,
241
+ lifecycleState: vol.lifecycleState,
242
+ availabilityDomain: vol.availabilityDomain,
243
+ vpusPerGB: vol.vpusPerGB,
244
+ timeCreated: vol.timeCreated,
245
+ }));
246
+ },
247
+
248
+ async oci_bv_list_boot_volumes(params) {
249
+ const response = await blockStorageClient.listBootVolumes({
250
+ compartmentId: getCompartmentId(params),
251
+ availabilityDomain: params.availability_domain,
252
+ limit: params.limit || 50,
253
+ });
254
+
255
+ return response.items.map(vol => ({
256
+ id: vol.id,
257
+ displayName: vol.displayName,
258
+ sizeInGBs: vol.sizeInGBs,
259
+ lifecycleState: vol.lifecycleState,
260
+ availabilityDomain: vol.availabilityDomain,
261
+ imageId: vol.imageId,
262
+ timeCreated: vol.timeCreated,
263
+ }));
264
+ },
265
+
266
+ // ==================== NETWORKING ====================
267
+
268
+ async oci_vcn_list(params) {
269
+ const response = await virtualNetworkClient.listVcns({
270
+ compartmentId: getCompartmentId(params),
271
+ limit: params.limit || 50,
272
+ });
273
+
274
+ return response.items.map(vcn => ({
275
+ id: vcn.id,
276
+ displayName: vcn.displayName,
277
+ cidrBlock: vcn.cidrBlock,
278
+ cidrBlocks: vcn.cidrBlocks,
279
+ lifecycleState: vcn.lifecycleState,
280
+ dnsLabel: vcn.dnsLabel,
281
+ defaultRouteTableId: vcn.defaultRouteTableId,
282
+ defaultSecurityListId: vcn.defaultSecurityListId,
283
+ timeCreated: vcn.timeCreated,
284
+ }));
285
+ },
286
+
287
+ async oci_subnet_list(params) {
288
+ const response = await virtualNetworkClient.listSubnets({
289
+ compartmentId: getCompartmentId(params),
290
+ vcnId: params.vcn_id,
291
+ limit: params.limit || 50,
292
+ });
293
+
294
+ return response.items.map(subnet => ({
295
+ id: subnet.id,
296
+ displayName: subnet.displayName,
297
+ cidrBlock: subnet.cidrBlock,
298
+ availabilityDomain: subnet.availabilityDomain,
299
+ lifecycleState: subnet.lifecycleState,
300
+ virtualRouterIp: subnet.virtualRouterIp,
301
+ securityListIds: subnet.securityListIds,
302
+ timeCreated: subnet.timeCreated,
303
+ }));
304
+ },
305
+
306
+ async oci_vcn_create(params) {
307
+ const response = await virtualNetworkClient.createVcn({
308
+ createVcnDetails: {
309
+ compartmentId: getCompartmentId(params),
310
+ displayName: params.display_name,
311
+ cidrBlocks: params.cidr_blocks || ["10.0.0.0/16"],
312
+ dnsLabel: params.dns_label,
313
+ },
314
+ });
315
+
316
+ return {
317
+ id: response.vcn.id,
318
+ displayName: response.vcn.displayName,
319
+ cidrBlocks: response.vcn.cidrBlocks,
320
+ lifecycleState: response.vcn.lifecycleState,
321
+ timeCreated: response.vcn.timeCreated,
322
+ };
323
+ },
324
+
325
+ // ==================== AUTONOMOUS DATABASE ====================
326
+
327
+ async oci_adb_list(params) {
328
+ const response = await databaseClient.listAutonomousDatabases({
329
+ compartmentId: getCompartmentId(params),
330
+ limit: params.limit || 50,
331
+ });
332
+
333
+ return response.items.map(db => ({
334
+ id: db.id,
335
+ displayName: db.displayName,
336
+ dbName: db.dbName,
337
+ dbWorkload: db.dbWorkload,
338
+ lifecycleState: db.lifecycleState,
339
+ cpuCoreCount: db.cpuCoreCount,
340
+ dataStorageSizeInTBs: db.dataStorageSizeInTBs,
341
+ isFreeTier: db.isFreeTier,
342
+ connectionStrings: db.connectionStrings?.profiles?.map(p => p.displayName),
343
+ timeCreated: db.timeCreated,
344
+ }));
345
+ },
346
+
347
+ async oci_adb_get(params) {
348
+ const response = await databaseClient.getAutonomousDatabase({
349
+ autonomousDatabaseId: params.database_id,
350
+ });
351
+
352
+ return {
353
+ id: response.autonomousDatabase.id,
354
+ displayName: response.autonomousDatabase.displayName,
355
+ dbName: response.autonomousDatabase.dbName,
356
+ dbWorkload: response.autonomousDatabase.dbWorkload,
357
+ lifecycleState: response.autonomousDatabase.lifecycleState,
358
+ cpuCoreCount: response.autonomousDatabase.cpuCoreCount,
359
+ dataStorageSizeInTBs: response.autonomousDatabase.dataStorageSizeInTBs,
360
+ isFreeTier: response.autonomousDatabase.isFreeTier,
361
+ connectionStrings: response.autonomousDatabase.connectionStrings,
362
+ serviceConsoleUrl: response.autonomousDatabase.serviceConsoleUrl,
363
+ timeCreated: response.autonomousDatabase.timeCreated,
364
+ };
365
+ },
366
+
367
+ async oci_adb_start(params) {
368
+ const response = await databaseClient.startAutonomousDatabase({
369
+ autonomousDatabaseId: params.database_id,
370
+ });
371
+
372
+ return {
373
+ id: response.autonomousDatabase.id,
374
+ displayName: response.autonomousDatabase.displayName,
375
+ lifecycleState: response.autonomousDatabase.lifecycleState,
376
+ action: "START",
377
+ };
378
+ },
379
+
380
+ async oci_adb_stop(params) {
381
+ const response = await databaseClient.stopAutonomousDatabase({
382
+ autonomousDatabaseId: params.database_id,
383
+ });
384
+
385
+ return {
386
+ id: response.autonomousDatabase.id,
387
+ displayName: response.autonomousDatabase.displayName,
388
+ lifecycleState: response.autonomousDatabase.lifecycleState,
389
+ action: "STOP",
390
+ };
391
+ },
392
+
393
+ // ==================== IAM ====================
394
+
395
+ async oci_iam_list_users(params) {
396
+ const response = await identityClient.listUsers({
397
+ compartmentId: getCompartmentId(params),
398
+ limit: params.limit || 100,
399
+ });
400
+
401
+ return response.items.map(user => ({
402
+ id: user.id,
403
+ name: user.name,
404
+ email: user.email,
405
+ description: user.description,
406
+ lifecycleState: user.lifecycleState,
407
+ isMfaActivated: user.isMfaActivated,
408
+ timeCreated: user.timeCreated,
409
+ }));
410
+ },
411
+
412
+ async oci_iam_list_groups(params) {
413
+ const response = await identityClient.listGroups({
414
+ compartmentId: getCompartmentId(params),
415
+ limit: params.limit || 100,
416
+ });
417
+
418
+ return response.items.map(group => ({
419
+ id: group.id,
420
+ name: group.name,
421
+ description: group.description,
422
+ lifecycleState: group.lifecycleState,
423
+ timeCreated: group.timeCreated,
424
+ }));
425
+ },
426
+
427
+ async oci_iam_list_policies(params) {
428
+ const response = await identityClient.listPolicies({
429
+ compartmentId: getCompartmentId(params),
430
+ limit: params.limit || 100,
431
+ });
432
+
433
+ return response.items.map(policy => ({
434
+ id: policy.id,
435
+ name: policy.name,
436
+ description: policy.description,
437
+ statements: policy.statements,
438
+ lifecycleState: policy.lifecycleState,
439
+ timeCreated: policy.timeCreated,
440
+ }));
441
+ },
442
+
443
+ async oci_iam_list_compartments(params) {
444
+ const response = await identityClient.listCompartments({
445
+ compartmentId: getCompartmentId(params),
446
+ limit: params.limit || 100,
447
+ accessLevel: "ANY",
448
+ compartmentIdInSubtree: true,
449
+ });
450
+
451
+ return response.items.map(compartment => ({
452
+ id: compartment.id,
453
+ name: compartment.name,
454
+ description: compartment.description,
455
+ lifecycleState: compartment.lifecycleState,
456
+ timeCreated: compartment.timeCreated,
457
+ }));
458
+ },
459
+
460
+ async oci_iam_list_availability_domains(params) {
461
+ const response = await identityClient.listAvailabilityDomains({
462
+ compartmentId: getCompartmentId(params),
463
+ });
464
+
465
+ return response.items.map(ad => ({
466
+ id: ad.id,
467
+ name: ad.name,
468
+ }));
469
+ },
470
+ };
471
+
472
+ // Tool definitions
473
+ const tools = [
474
+ // Compute
475
+ {
476
+ name: "oci_compute_list_instances",
477
+ description: "List all compute instances in a compartment. Shows VM details including shape, state, and availability domain.",
478
+ inputSchema: {
479
+ type: "object",
480
+ properties: {
481
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
482
+ limit: { type: "number", description: "Maximum number of instances to return", default: 50 },
483
+ },
484
+ },
485
+ },
486
+ {
487
+ name: "oci_compute_get_instance",
488
+ description: "Get detailed information about a specific compute instance",
489
+ inputSchema: {
490
+ type: "object",
491
+ properties: {
492
+ instance_id: { type: "string", description: "Instance OCID" },
493
+ },
494
+ required: ["instance_id"],
495
+ },
496
+ },
497
+ {
498
+ name: "oci_compute_list_shapes",
499
+ description: "List available compute shapes including Always Free shapes (VM.Standard.A1.Flex, VM.Standard.E2.1.Micro)",
500
+ inputSchema: {
501
+ type: "object",
502
+ properties: {
503
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
504
+ limit: { type: "number", description: "Maximum shapes to return", default: 100 },
505
+ },
506
+ },
507
+ },
508
+ {
509
+ name: "oci_compute_instance_action",
510
+ description: "Perform action on a compute instance (START, STOP, RESET, SOFTSTOP, SOFTRESET)",
511
+ inputSchema: {
512
+ type: "object",
513
+ properties: {
514
+ instance_id: { type: "string", description: "Instance OCID" },
515
+ action: { type: "string", enum: ["START", "STOP", "RESET", "SOFTSTOP", "SOFTRESET"], description: "Action to perform" },
516
+ },
517
+ required: ["instance_id", "action"],
518
+ },
519
+ },
520
+
521
+ // Object Storage
522
+ {
523
+ name: "oci_os_get_namespace",
524
+ description: "Get the Object Storage namespace for the tenancy",
525
+ inputSchema: {
526
+ type: "object",
527
+ properties: {
528
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
529
+ },
530
+ },
531
+ },
532
+ {
533
+ name: "oci_os_list_buckets",
534
+ description: "List all Object Storage buckets in a compartment (Free Tier: 20GB standard, 20GB archive)",
535
+ inputSchema: {
536
+ type: "object",
537
+ properties: {
538
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
539
+ limit: { type: "number", description: "Maximum buckets to return", default: 100 },
540
+ },
541
+ },
542
+ },
543
+ {
544
+ name: "oci_os_create_bucket",
545
+ description: "Create a new Object Storage bucket",
546
+ inputSchema: {
547
+ type: "object",
548
+ properties: {
549
+ bucket_name: { type: "string", description: "Name for the new bucket" },
550
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
551
+ public_access: { type: "string", enum: ["NoPublicAccess", "ObjectRead", "ObjectReadWithoutList"], default: "NoPublicAccess" },
552
+ storage_tier: { type: "string", enum: ["Standard", "Archive"], default: "Standard" },
553
+ },
554
+ required: ["bucket_name"],
555
+ },
556
+ },
557
+ {
558
+ name: "oci_os_list_objects",
559
+ description: "List objects in a bucket with optional prefix filter",
560
+ inputSchema: {
561
+ type: "object",
562
+ properties: {
563
+ bucket_name: { type: "string", description: "Bucket name" },
564
+ prefix: { type: "string", description: "Filter objects by prefix" },
565
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
566
+ limit: { type: "number", description: "Maximum objects to return", default: 100 },
567
+ },
568
+ required: ["bucket_name"],
569
+ },
570
+ },
571
+ {
572
+ name: "oci_os_delete_bucket",
573
+ description: "Delete an empty Object Storage bucket",
574
+ inputSchema: {
575
+ type: "object",
576
+ properties: {
577
+ bucket_name: { type: "string", description: "Bucket name to delete" },
578
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
579
+ },
580
+ required: ["bucket_name"],
581
+ },
582
+ },
583
+
584
+ // Block Storage
585
+ {
586
+ name: "oci_bv_list_volumes",
587
+ description: "List block volumes in a compartment (Free Tier: 200GB total)",
588
+ inputSchema: {
589
+ type: "object",
590
+ properties: {
591
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
592
+ limit: { type: "number", description: "Maximum volumes to return", default: 50 },
593
+ },
594
+ },
595
+ },
596
+ {
597
+ name: "oci_bv_list_boot_volumes",
598
+ description: "List boot volumes in an availability domain",
599
+ inputSchema: {
600
+ type: "object",
601
+ properties: {
602
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
603
+ availability_domain: { type: "string", description: "Availability domain name" },
604
+ limit: { type: "number", description: "Maximum volumes to return", default: 50 },
605
+ },
606
+ },
607
+ },
608
+
609
+ // Networking
610
+ {
611
+ name: "oci_vcn_list",
612
+ description: "List Virtual Cloud Networks (VCNs) in a compartment",
613
+ inputSchema: {
614
+ type: "object",
615
+ properties: {
616
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
617
+ limit: { type: "number", description: "Maximum VCNs to return", default: 50 },
618
+ },
619
+ },
620
+ },
621
+ {
622
+ name: "oci_subnet_list",
623
+ description: "List subnets in a VCN",
624
+ inputSchema: {
625
+ type: "object",
626
+ properties: {
627
+ vcn_id: { type: "string", description: "VCN OCID" },
628
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
629
+ limit: { type: "number", description: "Maximum subnets to return", default: 50 },
630
+ },
631
+ required: ["vcn_id"],
632
+ },
633
+ },
634
+ {
635
+ name: "oci_vcn_create",
636
+ description: "Create a new Virtual Cloud Network",
637
+ inputSchema: {
638
+ type: "object",
639
+ properties: {
640
+ display_name: { type: "string", description: "Display name for the VCN" },
641
+ cidr_blocks: { type: "array", items: { type: "string" }, description: "CIDR blocks (e.g., ['10.0.0.0/16'])" },
642
+ dns_label: { type: "string", description: "DNS label for the VCN" },
643
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
644
+ },
645
+ required: ["display_name"],
646
+ },
647
+ },
648
+
649
+ // Autonomous Database
650
+ {
651
+ name: "oci_adb_list",
652
+ description: "List Autonomous Databases (ATP/ADW). Free Tier includes 2 Always Free ATP or ADW databases.",
653
+ inputSchema: {
654
+ type: "object",
655
+ properties: {
656
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
657
+ limit: { type: "number", description: "Maximum databases to return", default: 50 },
658
+ },
659
+ },
660
+ },
661
+ {
662
+ name: "oci_adb_get",
663
+ description: "Get detailed information about an Autonomous Database",
664
+ inputSchema: {
665
+ type: "object",
666
+ properties: {
667
+ database_id: { type: "string", description: "Autonomous Database OCID" },
668
+ },
669
+ required: ["database_id"],
670
+ },
671
+ },
672
+ {
673
+ name: "oci_adb_start",
674
+ description: "Start a stopped Autonomous Database",
675
+ inputSchema: {
676
+ type: "object",
677
+ properties: {
678
+ database_id: { type: "string", description: "Autonomous Database OCID" },
679
+ },
680
+ required: ["database_id"],
681
+ },
682
+ },
683
+ {
684
+ name: "oci_adb_stop",
685
+ description: "Stop a running Autonomous Database",
686
+ inputSchema: {
687
+ type: "object",
688
+ properties: {
689
+ database_id: { type: "string", description: "Autonomous Database OCID" },
690
+ },
691
+ required: ["database_id"],
692
+ },
693
+ },
694
+
695
+ // IAM
696
+ {
697
+ name: "oci_iam_list_users",
698
+ description: "List IAM users in the tenancy",
699
+ inputSchema: {
700
+ type: "object",
701
+ properties: {
702
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
703
+ limit: { type: "number", description: "Maximum users to return", default: 100 },
704
+ },
705
+ },
706
+ },
707
+ {
708
+ name: "oci_iam_list_groups",
709
+ description: "List IAM groups in the tenancy",
710
+ inputSchema: {
711
+ type: "object",
712
+ properties: {
713
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
714
+ limit: { type: "number", description: "Maximum groups to return", default: 100 },
715
+ },
716
+ },
717
+ },
718
+ {
719
+ name: "oci_iam_list_policies",
720
+ description: "List IAM policies in a compartment",
721
+ inputSchema: {
722
+ type: "object",
723
+ properties: {
724
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
725
+ limit: { type: "number", description: "Maximum policies to return", default: 100 },
726
+ },
727
+ },
728
+ },
729
+ {
730
+ name: "oci_iam_list_compartments",
731
+ description: "List compartments in the tenancy hierarchy",
732
+ inputSchema: {
733
+ type: "object",
734
+ properties: {
735
+ compartment_id: { type: "string", description: "Parent compartment OCID (defaults to tenancy)" },
736
+ limit: { type: "number", description: "Maximum compartments to return", default: 100 },
737
+ },
738
+ },
739
+ },
740
+ {
741
+ name: "oci_iam_list_availability_domains",
742
+ description: "List availability domains in the region",
743
+ inputSchema: {
744
+ type: "object",
745
+ properties: {
746
+ compartment_id: { type: "string", description: "Compartment OCID (defaults to tenancy)" },
747
+ },
748
+ },
749
+ },
750
+ ];
751
+
752
+ // Create and start the server
753
+ const server = new Server(
754
+ {
755
+ name: "oracle-cloud-mcp",
756
+ version: "1.0.0",
757
+ },
758
+ {
759
+ capabilities: {
760
+ tools: {},
761
+ },
762
+ }
763
+ );
764
+
765
+ // Handle list tools request
766
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
767
+ return { tools };
768
+ });
769
+
770
+ // Handle call tool request
771
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
772
+ const { name, arguments: params } = request.params;
773
+
774
+ if (!toolHandlers[name]) {
775
+ throw new Error(`Unknown tool: ${name}`);
776
+ }
777
+
778
+ try {
779
+ const result = await toolHandlers[name](params || {});
780
+ return {
781
+ content: [
782
+ {
783
+ type: "text",
784
+ text: JSON.stringify(result, null, 2),
785
+ },
786
+ ],
787
+ };
788
+ } catch (error) {
789
+ return {
790
+ content: [
791
+ {
792
+ type: "text",
793
+ text: `Error: ${error.message}`,
794
+ },
795
+ ],
796
+ isError: true,
797
+ };
798
+ }
799
+ });
800
+
801
+ // Start the server
802
+ async function main() {
803
+ await initializeClients();
804
+
805
+ const transport = new StdioServerTransport();
806
+ await server.connect(transport);
807
+ console.error("Oracle Cloud MCP server running");
808
+ }
809
+
810
+ main().catch((error) => {
811
+ console.error("Server error:", error);
812
+ process.exit(1);
813
+ });
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@purplesquirrel/oracle-mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "Oracle Cloud Infrastructure MCP Server for Claude Code",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "start": "node index.js",
9
+ "test": "echo \"Error: no test specified\" && exit 1"
10
+ },
11
+ "keywords": ["oracle", "oci", "cloud", "mcp"],
12
+ "author": "Matthew Karsten",
13
+ "license": "MIT",
14
+ "dependencies": {
15
+ "@modelcontextprotocol/sdk": "^1.24.3",
16
+ "oci-sdk": "^2.122.1"
17
+ }
18
+ }