@haimkastner/workforce-ai-mcp 1.0.0-rc.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Check Point Software Technologies Ltd.
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,135 @@
1
+ # Check Point - Workforce AI MCP Server
2
+
3
+ [![License](https://img.shields.io/github/license/CheckPointSW/workforce-ai-mcp.svg?style=plastic)](https://github.com/CheckPointSW/workforce-ai-mcp/blob/release/LICENSE) [![npm version](https://img.shields.io/npm/v/@haimkastner/workforce-ai-mcp.svg?style=plastic)](https://www.npmjs.com/package/@haimkastner/workforce-ai-mcp)
4
+
5
+ <!-- TODO: Remove this disclaimer before GA release -->
6
+ > **Pre-release disclaimer:** This package is currently in release candidate (RC) stage and is intended for testing and evaluation purposes only. APIs and tool definitions may change before the stable release. Do not use in production environments.
7
+
8
+ An [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that exposes Check Point Workforce AI capabilities as LLM tools — enabling AI assistants to query, analyze, and manage AI & Browse security policies, assets, and applications through natural language.
9
+
10
+ ## Getting started
11
+
12
+ ### Obtaining API credentials
13
+
14
+ 1. Go to the [Infinity Portal API Keys page](https://portal.checkpoint.com/dashboard/settings/api-keys).
15
+ 2. Click **New** > **New Account API Key**.
16
+ 3. In the **Service** dropdown select **Workforce AI Security** (and for Browse, **Browse Security**) and create the key.
17
+ 4. Copy the **Client ID**, **Secret Key**, and **Authentication URL** (gateway).
18
+
19
+ For more information, see [Infinity Portal Administration Guide](https://sc1.checkpoint.com/documents/Infinity_Portal/WebAdminGuides/EN/Infinity-Portal-Admin-Guide/Content/Topics-Infinity-Portal/API-Keys.htm?tocpath=Global%20Settings%7C_____7#API_Keys).
20
+
21
+ ### Available gateways
22
+
23
+ | Region | Gateway URL |
24
+ |---|---|
25
+ | Europe | `https://cloudinfra-gw.portal.checkpoint.com` |
26
+ | United States | `https://cloudinfra-gw-us.portal.checkpoint.com` |
27
+
28
+ ### Environment variables
29
+
30
+ | Variable | Required | Description |
31
+ |---|---|---|
32
+ | `CP_CI_CLIENT_ID` | Yes | CloudInfra API key client ID |
33
+ | `CP_CI_ACCESS_KEY` | Yes | CloudInfra API key secret |
34
+ | `CP_CI_GATEWAY` | Yes | CloudInfra gateway URL |
35
+ | `MCP_MODE` | Yes | Transport mode: `stdio` or `http` |
36
+ | `PORT` | When `http` | HTTP server port |
37
+ | `WRITE_MODE` | No | Set to `true` to enable write tools (default: `false`). <br/>**Warning:** enabling write mode allows the LLM to create, modify, and delete security policy rules. Use with caution. |
38
+
39
+ ### Running with stdio transport
40
+
41
+ Use stdio mode when connecting directly from an MCP client such as Claude Desktop, VS Code, or Cursor:
42
+
43
+ ```bash
44
+ CP_CI_CLIENT_ID="your-client-id" \
45
+ CP_CI_ACCESS_KEY="your-access-key" \
46
+ CP_CI_GATEWAY="https://cloudinfra-gw-us.portal.checkpoint.com" \
47
+ MCP_MODE=stdio \
48
+ npx @haimkastner/workforce-ai-mcp
49
+ ```
50
+
51
+ #### Claude Desktop configuration
52
+
53
+ Add to your `claude_desktop_config.json`:
54
+
55
+ ```json
56
+ {
57
+ "mcpServers": {
58
+ "workforce-ai": {
59
+ "command": "npx",
60
+ "args": ["@haimkastner/workforce-ai-mcp"],
61
+ "env": {
62
+ "CP_CI_CLIENT_ID": "your-client-id",
63
+ "CP_CI_ACCESS_KEY": "your-access-key",
64
+ "CP_CI_GATEWAY": "https://cloudinfra-gw-us.portal.checkpoint.com",
65
+ "MCP_MODE": "stdio"
66
+ }
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ ### Running with HTTP transport
73
+
74
+ Use HTTP mode when running the server as a standalone service:
75
+
76
+ ```bash
77
+ CP_CI_CLIENT_ID="your-client-id" \
78
+ CP_CI_ACCESS_KEY="your-access-key" \
79
+ CP_CI_GATEWAY="https://cloudinfra-gw-us.portal.checkpoint.com" \
80
+ MCP_MODE=http \
81
+ PORT=3000 \
82
+ npx @haimkastner/workforce-ai-mcp
83
+ ```
84
+
85
+ The server exposes:
86
+ - `POST /mcp` — MCP StreamableHTTP endpoint
87
+ - `GET /health` — Health check
88
+
89
+ ## Capabilities
90
+
91
+ ### Read mode (default)
92
+
93
+ By default, the server starts in **read-only mode**, exposing tools for querying and analyzing policies without making any changes. This is safe for exploration and auditing.
94
+
95
+ #### Policy inspection
96
+ - **List rulebases** — View all rules for Chats (GenAI DLP), AI Access, Web Access, Agents, Secure Browsing, and DLP policies
97
+ - **Analyze shadow rules** — Detect rules that are shadowed (never matched) by higher-priority rules
98
+ - **Simulate policy matching** — Given a user and target, resolve which rule in the rulebase would apply
99
+
100
+ #### Assets and users
101
+ - **Search assets** — Find managed assets by name or attributes
102
+ - **Count assets** — Get asset counts with optional filters
103
+ - **Search users** — Look up users and groups in the organization
104
+
105
+ #### Applications and data types
106
+ - **Search apps** — Search the GenAI application catalog by name, description, or URL
107
+ - **Get apps by ID** — Retrieve application details by their IDs
108
+ - **List DLP data types** — Browse predefined and custom DLP data types
109
+ - **Get tenant DLP data types** — View data types configured for the tenant
110
+
111
+ #### Policy objects
112
+ - **List domain objects** — View domain-based policy objects
113
+ - **List file protection objects** — View file protection configurations
114
+
115
+ ### Write mode
116
+
117
+ To enable write operations, set `WRITE_MODE=true`. This unlocks tools that modify the policy configuration:
118
+
119
+ #### Rule management
120
+ - **Create rules** — Create new Chats, AI Access, Agents, DLP, and Secure Browsing rules with full policy configuration including actions, services, data types, and user/group assignments
121
+ - **Edit rules** — Update rule name, description, and other properties
122
+ - **Activate / deactivate rules** — Toggle rules on or off
123
+ - **Reorder rules** — Change rule priority in the rulebase
124
+ - **Delete rules** — Permanently remove rules from the rulebase
125
+
126
+ ## Available tools
127
+
128
+ For a full list of all available tools with descriptions and access modes, see [TOOLS.md](TOOLS.md).
129
+
130
+ ## Report Bug
131
+
132
+ In case of an issue or a bug found in the MCP server, please open an [issue](https://github.com/CheckPointSW/workforce-ai-mcp/issues).
133
+
134
+ ## Contributors
135
+ - Haim Kastner - [haimk@checkpoint.com](mailto:haimk@checkpoint.com)
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Server configuration constants
3
+ */
4
+ export declare const SERVER_NAME = "workforce-ai";
5
+ export declare const SERVER_VERSION = "1.0.0";
6
+ /** Grace period (seconds) before token expiry to trigger refresh */
7
+ export declare const KEEP_ALIVE_GRACE_SECONDS = 30;
8
+ /** Auth endpoint path on CloudInfra gateway */
9
+ export declare const CI_AUTH_PATH = "/auth/external";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Server configuration constants
3
+ */
4
+ export const SERVER_NAME = 'workforce-ai';
5
+ export const SERVER_VERSION = '1.0.0';
6
+ /** Grace period (seconds) before token expiry to trigger refresh */
7
+ export const KEEP_ALIVE_GRACE_SECONDS = 30;
8
+ /** Auth endpoint path on CloudInfra gateway */
9
+ export const CI_AUTH_PATH = '/auth/external';
@@ -0,0 +1,29 @@
1
+ /**
2
+ * CloudInfra JWT session management.
3
+ *
4
+ * Handles login via /auth/external, token storage, and automatic refresh
5
+ * before expiry.
6
+ */
7
+ export declare class SessionManager {
8
+ private jwtToken;
9
+ private tokenExpiresIn;
10
+ private refreshTimer;
11
+ private gateway;
12
+ private clientId;
13
+ private accessKey;
14
+ /**
15
+ * The gateway base URL (protocol + host only).
16
+ * API calls are proxied through this gateway.
17
+ */
18
+ get baseUrl(): string;
19
+ /** Current JWT token. */
20
+ get authToken(): string;
21
+ /** Connect and obtain initial JWT token. */
22
+ connect(gateway: string, clientId: string, accessKey: string): Promise<void>;
23
+ /** Disconnect and clear token. */
24
+ disconnect(): void;
25
+ private performLogin;
26
+ private scheduleRefresh;
27
+ }
28
+ /** Shared singleton session manager instance */
29
+ export declare const sessionManager: SessionManager;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * CloudInfra JWT session management.
3
+ *
4
+ * Handles login via /auth/external, token storage, and automatic refresh
5
+ * before expiry.
6
+ */
7
+ import { CI_AUTH_PATH, KEEP_ALIVE_GRACE_SECONDS } from './consts.js';
8
+ export class SessionManager {
9
+ jwtToken = '';
10
+ tokenExpiresIn = 0;
11
+ refreshTimer;
12
+ gateway = '';
13
+ clientId = '';
14
+ accessKey = '';
15
+ /**
16
+ * The gateway base URL (protocol + host only).
17
+ * API calls are proxied through this gateway.
18
+ */
19
+ get baseUrl() {
20
+ return this.gateway;
21
+ }
22
+ /** Current JWT token. */
23
+ get authToken() {
24
+ if (!this.jwtToken) {
25
+ throw new Error('JWT token is not set. Please connect first.');
26
+ }
27
+ return this.jwtToken;
28
+ }
29
+ /** Connect and obtain initial JWT token. */
30
+ async connect(gateway, clientId, accessKey) {
31
+ // Normalize gateway to protocol + host only
32
+ try {
33
+ const url = new URL(gateway);
34
+ this.gateway = url.origin;
35
+ }
36
+ catch {
37
+ this.gateway = gateway;
38
+ }
39
+ this.clientId = clientId;
40
+ this.accessKey = accessKey;
41
+ await this.performLogin();
42
+ this.scheduleRefresh();
43
+ console.error(`[auth] Connected to ${this.gateway}, token expires in ${this.tokenExpiresIn}s`);
44
+ }
45
+ /** Disconnect and clear token. */
46
+ disconnect() {
47
+ this.jwtToken = '';
48
+ this.tokenExpiresIn = 0;
49
+ if (this.refreshTimer) {
50
+ clearTimeout(this.refreshTimer);
51
+ this.refreshTimer = undefined;
52
+ }
53
+ console.error('[auth] Disconnected');
54
+ }
55
+ async performLogin() {
56
+ const authUrl = `${this.gateway}${CI_AUTH_PATH}`;
57
+ const response = await fetch(authUrl, {
58
+ method: 'POST',
59
+ headers: { 'Content-Type': 'application/json' },
60
+ body: JSON.stringify({ clientId: this.clientId, accessKey: this.accessKey }),
61
+ });
62
+ const body = (await response.json());
63
+ if (!response.ok || !body.success) {
64
+ throw new Error(`CI login failed (${response.status}): ${JSON.stringify(body)}`);
65
+ }
66
+ this.jwtToken = body.data.token;
67
+ this.tokenExpiresIn = body.data.expiresIn ?? 1800;
68
+ }
69
+ scheduleRefresh() {
70
+ const delaySeconds = Math.max(this.tokenExpiresIn - KEEP_ALIVE_GRACE_SECONDS, 1);
71
+ this.refreshTimer = setTimeout(async () => {
72
+ try {
73
+ console.error('[auth] Refreshing token...');
74
+ await this.performLogin();
75
+ this.scheduleRefresh();
76
+ console.error(`[auth] Token refreshed, expires in ${this.tokenExpiresIn}s`);
77
+ }
78
+ catch (err) {
79
+ console.error(`[auth] Token refresh failed: ${err}`);
80
+ }
81
+ }, delaySeconds * 1000);
82
+ }
83
+ }
84
+ /** Shared singleton session manager instance */
85
+ export const sessionManager = new SessionManager();
@@ -0,0 +1,9 @@
1
+ /**
2
+ * StreamableHTTP server setup for HTTP-based MCP communication using Hono
3
+ */
4
+ import { Hono } from 'hono';
5
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
+ /**
7
+ * Sets up a web server for the MCP server using StreamableHTTP transport
8
+ */
9
+ export declare function setupStreamableHttpServer(server: Server, port?: number): Promise<Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">>;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * StreamableHTTP server setup for HTTP-based MCP communication using Hono
3
+ */
4
+ import { Hono } from 'hono';
5
+ import { cors } from 'hono/cors';
6
+ import { serve } from '@hono/node-server';
7
+ import { v4 as uuid } from 'uuid';
8
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
9
+ import { InitializeRequestSchema } from '@modelcontextprotocol/sdk/types.js';
10
+ import { toReqRes, toFetchResponse } from 'fetch-to-node';
11
+ import { SERVER_NAME, SERVER_VERSION } from './consts.js';
12
+ const SESSION_ID_HEADER_NAME = 'mcp-session-id';
13
+ const JSON_RPC = '2.0';
14
+ class MCPStreamableHttpServer {
15
+ server;
16
+ transports = {};
17
+ constructor(server) {
18
+ this.server = server;
19
+ }
20
+ async handleGetRequest(c) {
21
+ console.error('GET request received - StreamableHTTP transport only supports POST');
22
+ return c.text('Method Not Allowed', 405, { Allow: 'POST' });
23
+ }
24
+ async handlePostRequest(c) {
25
+ const sessionId = c.req.header(SESSION_ID_HEADER_NAME);
26
+ console.error(`POST request received ${sessionId ? 'with session ID: ' + sessionId : 'without session ID'}`);
27
+ try {
28
+ const body = await c.req.json();
29
+ const { req, res } = toReqRes(c.req.raw);
30
+ // Reuse existing transport if we have a session ID
31
+ if (sessionId && this.transports[sessionId]) {
32
+ const transport = this.transports[sessionId];
33
+ await transport.handleRequest(req, res, body);
34
+ res.on('close', () => {
35
+ console.error(`Request closed for session ${sessionId}`);
36
+ });
37
+ return toFetchResponse(res);
38
+ }
39
+ // Create new transport for initialize requests
40
+ if (!sessionId && this.isInitializeRequest(body)) {
41
+ console.error('Creating new StreamableHTTP transport for initialize request');
42
+ const transport = new StreamableHTTPServerTransport({
43
+ sessionIdGenerator: () => uuid(),
44
+ });
45
+ transport.onerror = (err) => {
46
+ console.error('StreamableHTTP transport error:', err);
47
+ };
48
+ await this.server.connect(transport);
49
+ await transport.handleRequest(req, res, body);
50
+ const newSessionId = transport.sessionId;
51
+ if (newSessionId) {
52
+ console.error(`New session established: ${newSessionId}`);
53
+ this.transports[newSessionId] = transport;
54
+ transport.onclose = () => {
55
+ console.error(`Session closed: ${newSessionId}`);
56
+ delete this.transports[newSessionId];
57
+ };
58
+ }
59
+ res.on('close', () => {
60
+ console.error(`Request closed for new session`);
61
+ });
62
+ return toFetchResponse(res);
63
+ }
64
+ return c.json(this.createErrorResponse('Bad Request: invalid session ID or method.'), 400);
65
+ }
66
+ catch (error) {
67
+ console.error('Error handling MCP request:', error);
68
+ return c.json(this.createErrorResponse('Internal server error.'), 500);
69
+ }
70
+ }
71
+ createErrorResponse(message) {
72
+ return {
73
+ jsonrpc: JSON_RPC,
74
+ error: { code: -32000, message },
75
+ id: uuid(),
76
+ };
77
+ }
78
+ isInitializeRequest(body) {
79
+ const isInitial = (data) => InitializeRequestSchema.safeParse(data).success;
80
+ if (Array.isArray(body)) {
81
+ return body.some((request) => isInitial(request));
82
+ }
83
+ return isInitial(body);
84
+ }
85
+ }
86
+ /**
87
+ * Sets up a web server for the MCP server using StreamableHTTP transport
88
+ */
89
+ export async function setupStreamableHttpServer(server, port = 9096) {
90
+ const app = new Hono();
91
+ app.use('*', cors());
92
+ const mcpHandler = new MCPStreamableHttpServer(server);
93
+ app.get('/health', (c) => {
94
+ return c.json({ status: 'OK', server: SERVER_NAME, version: SERVER_VERSION });
95
+ });
96
+ app.get('/mcp', (c) => mcpHandler.handleGetRequest(c));
97
+ app.post('/mcp', (c) => mcpHandler.handlePostRequest(c));
98
+ app.get('/*', async (c) => {
99
+ return c.text('Not Found', 404);
100
+ });
101
+ serve({ fetch: app.fetch, port }, (info) => {
102
+ console.error(`MCP StreamableHTTP Server running at http://localhost:${info.port}`);
103
+ console.error(`- MCP Endpoint: http://localhost:${info.port}/mcp`);
104
+ console.error(`- Health Check: http://localhost:${info.port}/health`);
105
+ });
106
+ return app;
107
+ }
@@ -0,0 +1,5 @@
1
+ import { AxiosError } from 'axios';
2
+ /**
3
+ * Formats API errors for better readability
4
+ */
5
+ export declare function formatApiError(error: AxiosError): string;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Formats API errors for better readability
3
+ */
4
+ export function formatApiError(error) {
5
+ let message = 'API request failed.';
6
+ if (error.response) {
7
+ message = `API Error: Status ${error.response.status} (${error.response.statusText || 'Status text not available'}). `;
8
+ const responseData = error.response.data;
9
+ const MAX_LEN = 500;
10
+ if (typeof responseData === 'string') {
11
+ message += `Response: ${responseData.substring(0, MAX_LEN)}${responseData.length > MAX_LEN ? '...' : ''}`;
12
+ }
13
+ else if (responseData) {
14
+ try {
15
+ const jsonString = JSON.stringify(responseData);
16
+ message += `Response: ${jsonString.substring(0, MAX_LEN)}${jsonString.length > MAX_LEN ? '...' : ''}`;
17
+ }
18
+ catch {
19
+ message += 'Response: [Could not serialize data]';
20
+ }
21
+ }
22
+ else {
23
+ message += 'No response body received.';
24
+ }
25
+ }
26
+ else if (error.request) {
27
+ message = 'API Network Error: No response received from server.';
28
+ if (error.code)
29
+ message += ` (Code: ${error.code})`;
30
+ }
31
+ else {
32
+ message += `API Request Setup Error: ${error.message}`;
33
+ }
34
+ return message;
35
+ }
@@ -0,0 +1,19 @@
1
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import { AxiosRequestConfig, AxiosResponse } from 'axios';
3
+ import { JsonObject, McpToolDefinition } from '../types/types.js';
4
+ /**
5
+ * Executes an API tool with the provided arguments
6
+ */
7
+ export declare function executeTool(toolName: string, definition: McpToolDefinition, toolArgs: JsonObject): Promise<CallToolResult>;
8
+ /**
9
+ * Builds an Axios request configuration from a tool definition and arguments
10
+ */
11
+ export declare function buildRequest(definition: McpToolDefinition, toolArgs: JsonObject): AxiosRequestConfig;
12
+ /**
13
+ * Builds a tool result from an API response
14
+ */
15
+ export declare function buildResult(response: AxiosResponse): CallToolResult;
16
+ /**
17
+ * Builds an error result from an exception
18
+ */
19
+ export declare function buildErrorResult(error: unknown): CallToolResult;
@@ -0,0 +1,104 @@
1
+ import axios from 'axios';
2
+ import { validateLLMArgs } from './validator.js';
3
+ import { formatApiError } from '../core/utils.js';
4
+ import { sessionManager } from '../core/session.js';
5
+ /**
6
+ * Executes an API tool with the provided arguments
7
+ */
8
+ export async function executeTool(toolName, definition, toolArgs) {
9
+ try {
10
+ const validatedArgs = await validateLLMArgs(toolName, definition, toolArgs);
11
+ const request = buildRequest(definition, validatedArgs);
12
+ const response = await axios(request);
13
+ return buildResult(response);
14
+ }
15
+ catch (error) {
16
+ const result = buildErrorResult(error);
17
+ const errMessage = result.content[0].text || 'Unknown error';
18
+ console.error(`Error during execution of tool '${toolName}':`, errMessage);
19
+ return result;
20
+ }
21
+ }
22
+ /**
23
+ * Builds an Axios request configuration from a tool definition and arguments
24
+ */
25
+ export function buildRequest(definition, toolArgs) {
26
+ let urlPath = definition.pathTemplate;
27
+ const queryParams = {};
28
+ const headers = { Accept: 'application/json' };
29
+ let requestBodyData = undefined;
30
+ // Apply parameters to URL path, query, or headers
31
+ definition.executionParameters.forEach((param) => {
32
+ const value = toolArgs[param.name];
33
+ if (typeof value !== 'undefined' && value !== null) {
34
+ if (param.in === 'path') {
35
+ urlPath = urlPath.replace(`{${param.name}}`, encodeURIComponent(String(value)));
36
+ }
37
+ else if (param.in === 'query') {
38
+ queryParams[param.name] = value;
39
+ }
40
+ else if (param.in === 'header') {
41
+ headers[param.name.toLowerCase()] = String(value);
42
+ }
43
+ }
44
+ });
45
+ // Ensure all path parameters are resolved
46
+ if (urlPath.includes('{')) {
47
+ throw new Error(`Failed to resolve path parameters: ${urlPath}`);
48
+ }
49
+ // Handle request body
50
+ if (definition.requestBodyContentType && typeof toolArgs['requestBody'] !== 'undefined') {
51
+ requestBodyData = toolArgs['requestBody'];
52
+ headers['content-type'] = definition.requestBodyContentType;
53
+ }
54
+ const requestUrl = `${sessionManager.baseUrl}${urlPath}`;
55
+ headers['Authorization'] = `Bearer ${sessionManager.authToken}`;
56
+ return {
57
+ method: definition.method.toUpperCase(),
58
+ url: requestUrl,
59
+ params: queryParams,
60
+ headers,
61
+ ...(requestBodyData !== undefined && { data: requestBodyData }),
62
+ };
63
+ }
64
+ /**
65
+ * Builds a tool result from an API response
66
+ */
67
+ export function buildResult(response) {
68
+ let responseText = '';
69
+ if (!response.data) {
70
+ responseText = `(Status: ${response.status} - No body content)`;
71
+ }
72
+ else {
73
+ try {
74
+ responseText = JSON.stringify(response.data, null, 2);
75
+ }
76
+ catch {
77
+ responseText = '[Stringify Error]';
78
+ }
79
+ }
80
+ return {
81
+ content: [
82
+ {
83
+ type: 'text',
84
+ text: `API Response (Status: ${response.status}):\n${responseText}`,
85
+ },
86
+ ],
87
+ };
88
+ }
89
+ /**
90
+ * Builds an error result from an exception
91
+ */
92
+ export function buildErrorResult(error) {
93
+ let errorMessage;
94
+ if (axios.isAxiosError(error)) {
95
+ errorMessage = formatApiError(error);
96
+ }
97
+ else if (error instanceof Error) {
98
+ errorMessage = error.message;
99
+ }
100
+ else {
101
+ errorMessage = 'Unexpected error: ' + String(error);
102
+ }
103
+ return { content: [{ type: 'text', text: errorMessage }] };
104
+ }
@@ -0,0 +1,5 @@
1
+ import { JsonObject, McpToolDefinition } from '../types/types.js';
2
+ /**
3
+ * Validates arguments provided to a tool against its Zod schema
4
+ */
5
+ export declare function validateLLMArgs(toolName: string, definition: McpToolDefinition, toolArgs: JsonObject): Promise<JsonObject>;
@@ -0,0 +1,19 @@
1
+ import { ZodError } from 'zod';
2
+ /**
3
+ * Validates arguments provided to a tool against its Zod schema
4
+ */
5
+ export async function validateLLMArgs(toolName, definition, toolArgs) {
6
+ try {
7
+ const zodSchema = definition.zodValidationSchema;
8
+ const argsToParse = typeof toolArgs === 'object' && toolArgs !== null ? toolArgs : {};
9
+ return zodSchema.parse(argsToParse);
10
+ }
11
+ catch (error) {
12
+ if (error instanceof ZodError) {
13
+ const validationErrorMessage = `Invalid arguments for tool '${toolName}': ${error.errors.map((e) => `${e.path.join('.')} (${e.code}): ${e.message}`).join(', ')}`;
14
+ throw new Error(validationErrorMessage);
15
+ }
16
+ const errorMessage = error instanceof Error ? error.message : String(error);
17
+ throw new Error(`Internal error during validation setup: ${errorMessage}`);
18
+ }
19
+ }
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Workforce AI MCP Server
4
+ *
5
+ * MCP server that exposes Check Point Workforce AI tools for LLM consumption.
6
+ * Supports both stdio and HTTP (StreamableHTTP) transport modes.
7
+ *
8
+ * Environment variables:
9
+ * CP_CI_CLIENT_ID - CloudInfra API key client ID (required)
10
+ * CP_CI_ACCESS_KEY - CloudInfra API key secret (required)
11
+ * CP_CI_GATEWAY - CloudInfra gateway URL (required)
12
+ * WRITE_MODE - Enable write tools (default: false)
13
+ * MCP_MODE - Transport mode: "stdio" or "http" (required)
14
+ * PORT - HTTP port (required when MCP_MODE=http)
15
+ */
16
+ export {};