@purplesquirrel/ibmz-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.
- package/.github/dependabot.yml +21 -0
- package/.github/workflows/ci.yml +36 -0
- package/LICENSE +21 -0
- package/README.md +217 -0
- package/TROUBLESHOOTING.md +241 -0
- package/docs/index.html +761 -0
- package/docs/specs.html +646 -0
- package/index.js +681 -0
- package/linkedin-post.md +90 -0
- package/package.json +29 -0
- package/test-keyprotect.js +46 -0
- package/test-watsonx-generation.js +72 -0
package/linkedin-post.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# LinkedIn Post - IBM Z MCP Server
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Post Option 1 (Technical Focus)
|
|
6
|
+
|
|
7
|
+
**Just shipped: IBM Z MCP Server** š
|
|
8
|
+
|
|
9
|
+
I built an MCP server that brings IBM Z mainframe capabilities to Claude Code.
|
|
10
|
+
|
|
11
|
+
The result? Claude can now manage HSM-backed encryption keys and call mainframe APIs directly.
|
|
12
|
+
|
|
13
|
+
**Key Protect Integration:**
|
|
14
|
+
⢠Create/manage encryption keys in FIPS 140-2 Level 3 HSMs
|
|
15
|
+
⢠Envelope encryption for securing data at rest
|
|
16
|
+
⢠Key rotation and lifecycle management
|
|
17
|
+
|
|
18
|
+
**z/OS Connect Integration:**
|
|
19
|
+
⢠REST APIs to CICS, IMS, and batch programs
|
|
20
|
+
⢠OpenAPI spec discovery
|
|
21
|
+
⢠Bridge modern AI agents to legacy systems
|
|
22
|
+
|
|
23
|
+
This pairs with my watsonx MCP server for a complete IBM Cloud + AI integration:
|
|
24
|
+
- Claude orchestrates complex tasks
|
|
25
|
+
- watsonx.ai handles foundation model workloads
|
|
26
|
+
- Key Protect secures sensitive data
|
|
27
|
+
- z/OS Connect bridges to enterprise systems
|
|
28
|
+
|
|
29
|
+
š Demo: https://purplesquirrelmedia.github.io/ibmz-mcp-server/
|
|
30
|
+
š» Source: https://github.com/PurpleSquirrelMedia/ibmz-mcp-server
|
|
31
|
+
|
|
32
|
+
#IBM #IBMz #Mainframe #Claude #MCP #Anthropic #Security #EnterpriseAI
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Post Option 2 (Enterprise Value Focus)
|
|
37
|
+
|
|
38
|
+
**Connecting AI Agents to Enterprise Mainframes** š¢
|
|
39
|
+
|
|
40
|
+
Just released an open-source integration that lets Claude Code interact with IBM Z infrastructure.
|
|
41
|
+
|
|
42
|
+
Why this matters:
|
|
43
|
+
ā 70% of Fortune 500 companies run mission-critical workloads on IBM Z
|
|
44
|
+
ā AI agents need access to enterprise data and systems
|
|
45
|
+
ā Security can't be compromised
|
|
46
|
+
|
|
47
|
+
What I built:
|
|
48
|
+
1. **Key Protect MCP Tools** - Claude can create and manage encryption keys stored in hardware security modules
|
|
49
|
+
2. **z/OS Connect Tools** - Claude can discover and call mainframe APIs
|
|
50
|
+
|
|
51
|
+
The security model:
|
|
52
|
+
- Keys never leave the HSM
|
|
53
|
+
- All operations are audited
|
|
54
|
+
- Claude handles orchestration, not secrets
|
|
55
|
+
|
|
56
|
+
Real use case: An AI agent analyzing financial data can request a wrapped DEK, decrypt locally, process, then discard - the root key stays in the HSM the entire time.
|
|
57
|
+
|
|
58
|
+
Links:
|
|
59
|
+
š https://github.com/PurpleSquirrelMedia/ibmz-mcp-server
|
|
60
|
+
š https://purplesquirrelmedia.github.io/ibmz-mcp-server/
|
|
61
|
+
|
|
62
|
+
#EnterpriseAI #IBMz #Security #CloudSecurity #MCP
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Post Option 3 (Short & Punchy)
|
|
67
|
+
|
|
68
|
+
Two new MCP servers for IBM Cloud:
|
|
69
|
+
|
|
70
|
+
**watsonx-mcp-server**
|
|
71
|
+
ā Claude delegates to Granite, Llama, Mistral models
|
|
72
|
+
ā Two-agent AI architecture
|
|
73
|
+
|
|
74
|
+
**ibmz-mcp-server**
|
|
75
|
+
ā HSM-backed key management
|
|
76
|
+
ā z/OS Connect mainframe APIs
|
|
77
|
+
|
|
78
|
+
The stack:
|
|
79
|
+
āļø IBM Cloud free tier
|
|
80
|
+
š Key Protect HSM
|
|
81
|
+
š¤ watsonx.ai foundation models
|
|
82
|
+
š¢ z/OS Connect (optional)
|
|
83
|
+
š§ Claude Code orchestration
|
|
84
|
+
|
|
85
|
+
All tested. All working. All open source.
|
|
86
|
+
|
|
87
|
+
š watsonx: https://github.com/PurpleSquirrelMedia/watsonx-mcp-server
|
|
88
|
+
š IBM Z: https://github.com/PurpleSquirrelMedia/ibmz-mcp-server
|
|
89
|
+
|
|
90
|
+
#MCP #IBM #Claude #OpenSource
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@purplesquirrel/ibmz-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for IBM Z mainframe integration - Key Protect HSM and z/OS Connect",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"start": "node index.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"mcp",
|
|
12
|
+
"ibm",
|
|
13
|
+
"ibm-z",
|
|
14
|
+
"mainframe",
|
|
15
|
+
"key-protect",
|
|
16
|
+
"hsm",
|
|
17
|
+
"zos-connect",
|
|
18
|
+
"claude",
|
|
19
|
+
"anthropic"
|
|
20
|
+
],
|
|
21
|
+
"author": "Matthew Karsten",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@ibm-cloud/ibm-key-protect": "^0.4.1",
|
|
25
|
+
"@ibm-cloud/watsonx-ai": "^1.7.5",
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
27
|
+
"ibm-cloud-sdk-core": "^5.1.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import IbmKeyProtectApiV2 from '@ibm-cloud/ibm-key-protect/ibm-key-protect-api/v2.js';
|
|
3
|
+
import { IamAuthenticator } from 'ibm-cloud-sdk-core';
|
|
4
|
+
import crypto from 'crypto';
|
|
5
|
+
|
|
6
|
+
const client = new IbmKeyProtectApiV2({
|
|
7
|
+
authenticator: new IamAuthenticator({
|
|
8
|
+
apikey: process.env.IBM_CLOUD_API_KEY,
|
|
9
|
+
}),
|
|
10
|
+
serviceUrl: process.env.KEY_PROTECT_URL,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const instanceId = process.env.KEY_PROTECT_INSTANCE_ID;
|
|
14
|
+
const rootKeyId = 'dcd74ed6-5e33-45db-a25c-38f668f2f664';
|
|
15
|
+
|
|
16
|
+
// Generate a random data encryption key (DEK) - 32 bytes for AES-256
|
|
17
|
+
const dek = crypto.randomBytes(32);
|
|
18
|
+
const dekBase64 = dek.toString('base64');
|
|
19
|
+
console.log('Original DEK:', dekBase64);
|
|
20
|
+
|
|
21
|
+
// Wrap the DEK with our root key (envelope encryption)
|
|
22
|
+
console.log('\nWrapping DEK with root key...');
|
|
23
|
+
const wrapResult = await client.wrapKey({
|
|
24
|
+
bluemixInstance: instanceId,
|
|
25
|
+
id: rootKeyId,
|
|
26
|
+
keyActionWrapBody: {
|
|
27
|
+
plaintext: dekBase64
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const wrappedDek = wrapResult.result.ciphertext;
|
|
32
|
+
console.log('Wrapped DEK:', wrappedDek.substring(0, 50) + '...');
|
|
33
|
+
|
|
34
|
+
// Unwrap the DEK to verify it works
|
|
35
|
+
console.log('\nUnwrapping DEK...');
|
|
36
|
+
const unwrapResult = await client.unwrapKey({
|
|
37
|
+
bluemixInstance: instanceId,
|
|
38
|
+
id: rootKeyId,
|
|
39
|
+
keyActionUnwrapBody: {
|
|
40
|
+
ciphertext: wrappedDek
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const unwrappedDek = unwrapResult.result.plaintext;
|
|
45
|
+
console.log('Unwrapped DEK:', unwrappedDek);
|
|
46
|
+
console.log('\nā
Envelope encryption test:', dekBase64 === unwrappedDek ? 'SUCCESS!' : 'FAILED');
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Test watsonx.ai text generation
|
|
4
|
+
* Requires either a project_id or space_id
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { WatsonXAI } from '@ibm-cloud/watsonx-ai';
|
|
8
|
+
import { IamAuthenticator } from 'ibm-cloud-sdk-core';
|
|
9
|
+
|
|
10
|
+
const apiKey = process.env.WATSONX_API_KEY || 'YveH06g9T_XKZoKapGJsjm9q9xCz6WJ6z4GBRM_LJ9R5';
|
|
11
|
+
const projectId = process.env.WATSONX_PROJECT_ID;
|
|
12
|
+
|
|
13
|
+
const client = WatsonXAI.newInstance({
|
|
14
|
+
version: '2024-05-31',
|
|
15
|
+
serviceUrl: 'https://us-south.ml.cloud.ibm.com',
|
|
16
|
+
authenticator: new IamAuthenticator({ apikey: apiKey }),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
console.log('=== watsonx.ai Text Generation Test ===\n');
|
|
20
|
+
|
|
21
|
+
// First, list models to confirm connection
|
|
22
|
+
console.log('1. Checking available models...');
|
|
23
|
+
const modelsResp = await client.listFoundationModelSpecs({ limit: 20 });
|
|
24
|
+
const models = modelsResp.result.resources || [];
|
|
25
|
+
|
|
26
|
+
// Find text generation capable models
|
|
27
|
+
const textGenModels = models.filter(m =>
|
|
28
|
+
m.model_id?.includes('granite') ||
|
|
29
|
+
m.model_id?.includes('llama') ||
|
|
30
|
+
m.model_id?.includes('mistral')
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
console.log(` Found ${textGenModels.length} text generation models:`);
|
|
34
|
+
textGenModels.slice(0, 5).forEach(m => console.log(` ⢠${m.model_id}`));
|
|
35
|
+
|
|
36
|
+
if (!projectId) {
|
|
37
|
+
console.log('\nā ļø No WATSONX_PROJECT_ID set.');
|
|
38
|
+
console.log('\nTo enable text generation:');
|
|
39
|
+
console.log('1. Go to https://dataplatform.cloud.ibm.com');
|
|
40
|
+
console.log('2. Create a new project');
|
|
41
|
+
console.log('3. Copy the Project ID from Settings ā General');
|
|
42
|
+
console.log('4. Add to ~/.claude.json under mcpServers.watsonx.env:');
|
|
43
|
+
console.log(' "WATSONX_PROJECT_ID": "your-project-id"');
|
|
44
|
+
console.log('\nOr set environment variable:');
|
|
45
|
+
console.log(' export WATSONX_PROJECT_ID=your-project-id');
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Test generation if project ID is available
|
|
50
|
+
console.log('\n2. Testing text generation...');
|
|
51
|
+
console.log(` Using project: ${projectId}`);
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const response = await client.generateText({
|
|
55
|
+
input: 'Explain envelope encryption in one sentence:',
|
|
56
|
+
modelId: 'ibm/granite-3-8b-instruct',
|
|
57
|
+
projectId: projectId,
|
|
58
|
+
parameters: {
|
|
59
|
+
max_new_tokens: 100,
|
|
60
|
+
temperature: 0.7,
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
console.log('\n Generated response:');
|
|
65
|
+
console.log(` "${response.result.results[0].generated_text.trim()}"`);
|
|
66
|
+
console.log('\nā
Text generation working!');
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.log('\nā Generation failed:', error.message);
|
|
69
|
+
if (error.message.includes('project_id')) {
|
|
70
|
+
console.log('\n The project ID may be invalid or not properly configured.');
|
|
71
|
+
}
|
|
72
|
+
}
|