The @centure/node-sdk provides a transport wrapper for the Model Context Protocol (MCP) that automatically scans messages for prompt injection attacks before they reach your MCP server.
What is MCP?
The Model Context Protocol is a standard for connecting AI models to external tools and data sources. The Centure SDK intercepts MCP messages to detect and block malicious prompts.
Installation
Install both the Centure SDK and the MCP SDK:
npm install @centure/node-sdk @modelcontextprotocol/sdk
Basic Setup
Wrap any MCP transport with CentureMCPClientTransport to add automatic scanning:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { CentureClient } from "@centure/node-sdk";
import { CentureMCPClientTransport } from "@centure/node-sdk/mcp";
// Create your underlying MCP transport
const stdioTransport = new StdioClientTransport({
command: "your-mcp-server",
args: ["--arg1", "--arg2"],
});
// Wrap it with Centure security scanning
const secureTransport = new CentureMCPClientTransport({
client: new CentureClient({
apiKey: process.env.CENTURE_API_KEY,
}),
transport: stdioTransport,
});
// Use with MCP client
const client = new Client({
name: "your-client",
version: "1.0.0",
});
await client.connect(secureTransport);
The transport intercepts all messages sent through the MCP client and scans them before forwarding to the server.
Configuration Hooks
The transport provides hooks to customize scanning behavior:
shouldScanMessage
Determines whether a specific message should be scanned. Return false to skip scanning.
const secureTransport = new CentureMCPClientTransport({
client: centureClient,
transport: stdioTransport,
shouldScanMessage: ({ message, extra }) => {
// Skip scanning for tool list requests
if (message.method === "tools/list") {
return false;
}
// Skip scanning for specific notification types
if (extra?.notification && extra.notification === "progress") {
return false;
}
return true;
},
});
Parameters:
message (JSONRPCMessage) - The MCP message to potentially scan
extra (MessageExtraInfo) - Additional context about the message
Returns: boolean or { scan: boolean }
onAfterScan
Called after a message is scanned, regardless of the result. Use this for logging or metrics.
const secureTransport = new CentureMCPClientTransport({
client: centureClient,
transport: stdioTransport,
onAfterScan: ({ message, scanResult }) => {
// Log all scan results
console.log(`Scanned ${message.method}:`, {
safe: scanResult.is_safe,
categories: scanResult.categories,
requestId: scanResult.request_id,
});
// Optionally allow unsafe messages to pass through
if (scanResult.categories.every((c) => c.confidence === "low")) {
return { passthrough: true };
}
},
});
Parameters:
message (JSONRPCMessage) - The scanned message
extra (MessageExtraInfo) - Additional context
scanResult (ScanResponse) - The scan result from Centure API
Returns: void or { passthrough: boolean }
onUnsafeMessage
Called when an unsafe message is detected. Controls whether to block or allow the message.
const secureTransport = new CentureMCPClientTransport({
client: centureClient,
transport: stdioTransport,
onUnsafeMessage: ({ message, scanResult }) => {
// Allow medium-confidence threats
const isMedium = scanResult.categories.every(
(c) => c.confidence === "medium"
);
if (isMedium) {
console.warn("Allowing medium-confidence threat:", scanResult.categories);
return { passthrough: true };
}
// Block high-confidence threats with custom error
console.error("Blocking high-confidence threat:", scanResult.categories);
return {
passthrough: false,
replace: {
jsonrpc: "2.0",
id: message.id,
error: {
code: -32001,
message: "Security violation: prompt injection detected",
data: {
categories: scanResult.categories,
requestId: scanResult.request_id,
},
},
},
};
},
});
Parameters:
message (JSONRPCMessage) - The unsafe message
extra (MessageExtraInfo) - Additional context
scanResult (ScanResponse) - The scan result from Centure API
Returns: { passthrough: boolean, replace?: JSONRPCMessage }
Always return an object from onUnsafeMessage. Set passthrough: false to block the message and optionally provide a replace message to send instead.
onBeforeSend
Called before any message is sent through the transport, before scanning occurs.
const secureTransport = new CentureMCPClientTransport({
client: centureClient,
transport: stdioTransport,
onBeforeSend: ({ message, extra }) => {
console.log(`Sending ${message.method} to MCP server`);
},
});
Parameters:
message (JSONRPCMessage) - The message about to be sent
extra (MessageExtraInfo) - Additional context
Returns: void
Complete Example
Here’s a production-ready configuration with all hooks:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { CentureClient } from "@centure/node-sdk";
import { CentureMCPClientTransport } from "@centure/node-sdk/mcp";
// Initialize Centure client
const centureClient = new CentureClient({
apiKey: process.env.CENTURE_API_KEY,
});
// Create base MCP transport
const stdioTransport = new StdioClientTransport({
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
});
// Wrap with security scanning
const secureTransport = new CentureMCPClientTransport({
client: centureClient,
transport: stdioTransport,
// Skip scanning for safe methods
shouldScanMessage: ({ message }) => {
const safeMethods = [
"initialize",
"tools/list",
"resources/list",
"prompts/list",
];
if (safeMethods.includes(message.method)) {
return false;
}
return true;
},
// Log all scan results
onAfterScan: ({ message, scanResult }) => {
if (!scanResult.is_safe) {
console.warn(`Unsafe message detected in ${message.method}:`, {
categories: scanResult.categories,
requestId: scanResult.request_id,
});
}
},
// Handle unsafe messages
onUnsafeMessage: ({ message, scanResult }) => {
// Allow low and medium confidence detections with logging
const maxConfidence = scanResult.categories.reduce(
(max, cat) => {
const levels = { low: 1, medium: 2, high: 3 };
return Math.max(max, levels[cat.confidence]);
},
0
);
if (maxConfidence < 3) {
console.warn("Allowing non-high confidence threat");
return { passthrough: true };
}
// Block high-confidence threats
console.error("BLOCKED: High-confidence prompt injection");
return {
passthrough: false,
replace: {
jsonrpc: "2.0",
id: message.id,
error: {
code: -32001,
message: "Request blocked: security violation detected",
data: {
requestId: scanResult.request_id,
categories: scanResult.categories.map((c) => c.code),
},
},
},
};
},
// Log all outgoing messages
onBeforeSend: ({ message }) => {
console.log(`→ ${message.method}`);
},
});
// Connect MCP client
const client = new Client({
name: "secure-mcp-client",
version: "1.0.0",
});
await client.connect(secureTransport);
// Use the client normally
const tools = await client.listTools();
console.log("Available tools:", tools);
Transport Types
CentureMCPClientTransportOptions
Configuration options for the transport wrapper:
interface CentureMCPClientTransportOptions {
client: CentureClient;
transport: Transport;
shouldScanMessage?: (context: ShouldScanMessageContext) => boolean | ShouldScanMessageResult;
onAfterScan?: (context: OnAfterScanContext) => void | OnAfterScanResult;
onUnsafeMessage?: (context: OnUnsafeMessageContext) => OnUnsafeMessageResult;
onBeforeSend?: (context: { message: JSONRPCMessage; extra?: MessageExtraInfo }) => void;
}
Hook Context Types
type ShouldScanMessageContext = {
message: JSONRPCMessage;
extra?: MessageExtraInfo;
};
type OnAfterScanContext = {
message: JSONRPCMessage;
extra?: MessageExtraInfo;
scanResult: ScanResponse;
};
type OnUnsafeMessageContext = {
message: JSONRPCMessage;
extra?: MessageExtraInfo;
scanResult: ScanResponse;
};
type OnUnsafeMessageResult = {
passthrough: boolean;
replace?: JSONRPCMessage;
};
Best Practices
Block high-confidence threats: Always block messages with high confidence detections in production environments.
Log all detections: Use onAfterScan to log scanning results for security monitoring and incident response.
Skip safe methods: Use shouldScanMessage to skip scanning for methods that cannot carry threats (e.g., tools/list, initialize).
Provide clear error messages: When blocking messages, include the request_id in error responses for debugging and support.
Do not allow high confidence detections to pass through without careful review. These indicate strong evidence of prompt injection attacks.
Supported Transports
The CentureMCPClientTransport works with any MCP transport implementation:
- StdioClientTransport - Communicate with servers via stdin/stdout
- SSEClientTransport - Communicate with servers via Server-Sent Events
- Custom transports - Any transport implementing the MCP Transport interface
Error Handling
The transport handles scanning errors gracefully:
const secureTransport = new CentureMCPClientTransport({
client: centureClient,
transport: stdioTransport,
onAfterScan: ({ message, scanResult }) => {
// This hook runs even if scanning fails
// Check if scanResult indicates an error
},
});
If the Centure API is unreachable or returns an error, the message is blocked by default. Use onAfterScan to implement custom fallback behavior.
Next Steps