@igorromero/ciphersuite-mcp
by igorgrv1
README.md
# ciphersuite-mcp
An [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server that provides AES-256-CBC encryption and decryption tools, resources describing each algorithm, and ready-to-use prompts — all runnable directly inside VS Code Copilot Chat.

---
## What it does
| Capability | Name | Description |
|---|---|---|
| 🔧 Tool | `encrypt_message` | Encrypts any plain-text message with a passphrase |
| 🔧 Tool | `decrypt_message` | Decrypts a previously encrypted message with the same passphrase |
| 📄 Resource | `encryption://info` | Returns details about the encryption algorithm, key derivation, and output format |
| 📄 Resource | `decryption://info` | Returns how to use the decrypt tool: expected format, passphrase rules, and common errors |
| 💬 Prompt | `encrypt_message_prompt` | Pre-built prompt that asks the agent to encrypt a message |
| 💬 Prompt | `decrypt_message_prompt` | Pre-built prompt that asks the agent to decrypt a message |
### How encryption works
- **Algorithm**: AES-256-CBC
- **Key derivation**: `scrypt(passphrase, fixedSalt, 32)` — you pass any passphrase string; the server derives a strong 32-byte key automatically
- **Output format**: `<IV in hex>:<ciphertext in hex>` — keep the full string to decrypt later
- **IV**: a fresh random 16-byte IV is generated on every encryption call, so the same message encrypted twice produces different output
---
## Prerequisites
- **Node.js v24+** (see `engines` in `package.json`)
---
## Installation
```bash
npm install
```
No build step is needed — the server runs TypeScript directly via Node.js native TypeScript support.
---
## Using in VS Code
### 1. Add the MCP server configuration
Create (or open) `.vscode/mcp.json` in your workspace and add:
```json
{
"servers": {
"ciphersuite-mcp": {
"command": "node",
"args": ["--experimental-strip-types", "ABSOLUTE_PATH_TO_PROJECT/src/index.ts"]
}
}
}
```
or via npm:
```json
{
"servers": {
"ciphersuite-mcp": {
"command": "npx",
"args": ["-y", "@igorromero/ciphersuite-mcp"]
}
}
}
```
> **Tip:** You can also add this server to your user-level MCP config at `~/.vscode/mcp.json` to make it available in every workspace.
### 2. Reload VS Code
Open the Command Palette (`Cmd+Shift+P`) and run **Developer: Reload Window** (or just restart VS Code).
### 3. Use it in Copilot Chat
Open Copilot Chat (Agent mode) and try:
```
Encrypt the message "Hello, World!" using the passphrase "my-secret-key"
```
```
Decrypt this message: a3f1...:<ciphertext> using the passphrase "my-secret-key"
```
```
Show me the encryption://info resource
```
The agent will automatically call the appropriate tool and return the result.
---
## Running the MCP Inspector
The MCP Inspector lets you explore and test all tools, resources, and prompts interactively in a browser UI:
```bash
npm run mcp:inspect
```
This opens the inspector at `http://localhost:5173` and connects it to the running server.
---
## Running tests
```bash
# Run all tests once
npm test
# Run tests in watch mode (with debugger)
npm run test:dev
```
The test suite covers:
- Encrypting a message
- Decrypting a message with the correct passphrase
- Listing and reading the `encryption://info` resource
- Fetching both prompts
- Error: decrypting with the wrong passphrase
- Error: decrypting a malformed ciphertext
---
## Project structure
```
src/
index.ts # Entry point — connects the server to stdio transport
mcp.ts # All tools, resources, and prompts are registered here
tests/
mcp.test.ts
```
---
## Available scripts
| Script | Description |
|---|---|
| `npm start` | Start the server (used by MCP clients) |
| `npm run dev` | Start with file-watch and Node.js inspector |
| `npm test` | Run all tests |
| `npm run test:dev` | Run tests in watch mode |
| `npm run mcp:inspect` | Open the MCP Inspector UI |
---
---
## Creating from Scratch
This section documents how this MCP server was built step by step — useful for creating new MCP servers in the future.
### MCP Transport Types
There are 3 types of MCP transport:
| Type | Class | Description |
|---|---|---|
| `stdio` | `StdioServerTransport` | Runs locally on the machine — the most common for local tools |
| `http` | — | Runs as an API over HTTP |
| `sse` | — | Server-Sent Events — processes data on demand (streaming) |
### Dependencies
```json
// package.json
"dependencies": {
"@modelcontextprotocol/sdk": "^1.27.1",
"@types/node": "^24.11.0",
"zod": "^3.25.76"
}
```
---
### 1. Entry Point — `src/index.ts`
The entry point creates a `StdioServerTransport` and connects the MCP server to it:
```typescript
// src/index.ts
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { server } from "./mcp.ts";
async function main() {
const transport = new StdioServerTransport()
await server.connect(transport)
console.error('Encrypt MCP Server running on stdio')
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
```
---
### 2. Server Setup — `src/mcp.ts`
Create the MCP server instance with a name and version:
```typescript
// src/mcp.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
export const server = new McpServer({
name: '@igorromero/ciphersuite-mcp',
version: '0.0.1'
})
```
---
### 3. Registering Tools

Tools are functions the LLM can call to perform actions. Use `server.registerTool`, which takes 3 arguments:
1. **Name** of the tool (string)
2. **Config object** containing:
- `description` — what the tool does; the LLM uses this to decide when to call it
- `inputSchema` — equivalent to the request body, defined with Zod
- `outputSchema` — equivalent to the response body, defined with Zod
3. **Async handler function** — the actual implementation
```typescript
server.registerTool(
'encrypt_message',
{
description: 'Encrypt a message',
inputSchema: {
message: z.string().describe("The message to encrypt"),
encryptionKey: z.string().describe(
"Any passphrase to use for encryption — the server derives a strong key from it automatically"
)
},
outputSchema: {
encryptedMessage: z.string().describe(
"The encrypted message (format: iv:ciphertext)"
)
}
},
async ({ message, encryptionKey }) => {
try {
const encryptedMessage = encrypt(message, encryptionKey)
return {
content: [{ type: "text", text: encryptedMessage }],
structuredContent: { encryptedMessage }
}
} catch (error) {
return {
isError: true,
content: [{
type: 'text',
text: `Failed to encrypt message! Error: ${error instanceof Error ? error.message : String(error)}`
}]
}
}
}
)
```
The same pattern applies to `decrypt_message` — just swap the input/output schema fields and call `decrypt()` instead.
---
### 4. Registering Resources

Resources provide static or computed information that helps the LLM understand the context around a tool. Use `server.registerResource`, which takes 4 arguments:
1. **Name** of the resource
2. **URI template** (usually the same as the name)
3. **Config object** containing a `description`
4. **Handler function** that returns `contents` — an array of objects with `uri`, `mimeType`, and `text`
```typescript
server.registerResource(
'encryption://info',
'encryption://info',
{
description: 'Describes the encryption algorithm, key requirements, and output format used by this server',
},
() => ({
contents: [
{
uri: "encryption://info",
mimeType: "text/plain",
text: `
Algorithm : AES-256-CBC
Key derivation: scrypt (passphrase + fixed server salt → 32-byte key)
Output format: <16-byte IV in hex>:<ciphertext in hex> (separated by ":")
Notes:
- Users pass any passphrase — the server derives a strong 32-byte key automatically using scrypt.
- A random IV is generated for every encryption — the same message encrypted twice will produce different output.
- Use the exact same passphrase to decrypt.
- Keep the full "iv:ciphertext" string to decrypt later.
`.trim(),
},
]
})
)
```
The `decryption://info` resource follows the same pattern, describing the expected input format, passphrase requirements, and common error scenarios for the decrypt tool.
---
### 5. Registering Prompts

Prompts are pre-built message templates that the LLM can use to invoke tools in a guided way. Use `server.registerPrompt`, which takes 3 arguments:
1. **Name** of the prompt
2. **Config object** containing:
- `description` — what the prompt does
- `argsSchema` — the input parameters, defined with Zod
3. **Handler function** that returns `messages` — an array of objects with `role` (`user` or `assistant`) and `content`
```typescript
server.registerPrompt(
"encrypt_message_prompt",
{
description: "Prompt to encrypt a plain-text message using the encrypt_message tool",
argsSchema: {
message: z.string().describe("The message to encrypt"),
encryptionKey: z.string().describe(
"Any passphrase to use for encryption — the server derives a strong key from it automatically"
)
}
},
({ message, encryptionKey }) => ({
messages: [
{
role: 'user',
content: {
type: "text",
text: `Please encrypt the following message using the encrypt_message tool.\nMessage: ${message}\nEncryption key: ${encryptionKey}`,
}
}
]
})
)
```
The `decrypt_message_prompt` follows the same pattern — takes `encryptedMessage` and `encryptionKey` as args and instructs the LLM to call `decrypt_message`.
---
### 6. Connecting the MCP Server to an IDE
#### VS Code (automatic)
Create `.vscode/mcp.json` in the project root. VS Code will detect it automatically:
```json
{
"servers": {
"ciphersuite-mcp": {
"command": "node",
"args": [
"--experimental-strip-types",
"src/index.ts"
]
}
}
}
```
#### Other IDEs / Other Projects
Copy the `ciphersuite-mcp` server entry into the MCP config file of the target project or IDE. The server runs as a subprocess via `stdio`, so any MCP-compatible client can connect to it.
TDQS
A3.6/5.0
Scored across 2 tools
Disambiguation5/5
The two tools are perfectly distinct: one encrypts, the other decrypts. There is no overlap or potential for confusion between them.
Naming Consistency5/5
Both tools follow the same verb_noun pattern: encrypt_message and decrypt_message. Naming is consistent and intuitive.
Tool Count4/5
At only 2 tools, the server is minimal, but the domain of encryption/decryption inherently requires exactly these two operations. The count is slightly under the typical 3-15 range but perfectly appropriate for its narrow scope.
Completeness5/5
The tool surface covers the full encryption/decryption lifecycle with no missing operations. For a cipher suite, encrypt and decrypt are the only necessary functions.
Maintenance
ActivityMaintained
ResponsivenessNo issues