@x0333/bitrix24-mcp-server 2.0.1 → 2.1.1

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.
Files changed (3) hide show
  1. package/README.md +56 -0
  2. package/index.js +161 -229
  3. package/package.json +4 -5
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # Bitrix24 MCP Server
2
+
3
+ This is a Model Context Protocol (MCP) server that provides tools for interacting with Bitrix24 via incoming webhooks. It allows AI agents (like Manus, Claude Desktop, etc.) to manage tasks, search for groups, and view user profiles.
4
+
5
+ ## Features
6
+
7
+ - **get_profile**: Fetch current user profile information.
8
+ - **get_task**: Retrieve detailed information about a specific task by ID.
9
+ - **search_tasks**: Search for tasks by title with sorting and pagination.
10
+ - **get_group**: Get detailed information about a workgroup or project.
11
+ - **search_groups**: Search for workgroups or projects by name.
12
+
13
+ ## Installation
14
+
15
+ You can run this server directly using `npx`:
16
+
17
+ ```bash
18
+ # To start the MCP server (default)
19
+ npx -y @x0333/bitrix24-mcp-server
20
+
21
+ # To call a specific tool via CLI (example)
22
+ npx -y @x0333/bitrix24-mcp-server get_profile
23
+ ```
24
+
25
+ ## Configuration
26
+
27
+ The server requires a Bitrix24 incoming webhook URL. You must set the `B24_BASE` environment variable.
28
+
29
+ ### Example for Manus / Claude Desktop
30
+
31
+ Add this to your configuration:
32
+
33
+ ```json
34
+ {
35
+ "mcpServers": {
36
+ "bitrix24": {
37
+ "command": "npx",
38
+ "args": ["-y", "@x0333/bitrix24-mcp-server"],
39
+ "env": {
40
+ "B24_BASE": "https://your-domain.bitrix24.ru/rest/USER_ID/WEBHOOK_CODE/"
41
+ }
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## How to get Webhook URL
48
+
49
+ 1. Go to your Bitrix24 portal.
50
+ 2. Navigate to **Developer Resources** -> **Other** -> **Inbound Webhook**.
51
+ 3. Select the required permissions (Tasks, Social Network, User).
52
+ 4. Copy the **URL for REST API call** (it should look like `https://domain.bitrix24.ru/rest/1/abcdef12345/`).
53
+
54
+ ## License
55
+
56
+ MIT
package/index.js CHANGED
@@ -1,45 +1,52 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
 
4
- /*
5
- * CLI entry point for the Bitrix24 skill.
6
- *
7
- * This version uses axios for HTTP requests and commander for argument parsing.
8
- * Users can call this binary via `npx bitrix24-skill` once published.
9
- *
10
- * Each command prints the JSON response from Bitrix24 to stdout so it can be
11
- * consumed by downstream tools (including AI models) without additional
12
- * formatting. If the base URL is not provided via the --base option, the
13
- * environment variable B24_BASE will be used as a fallback. See README for
14
- * usage examples.
15
- */
16
-
4
+ const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
5
+ const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+ const {
7
+ CallToolRequestSchema,
8
+ ListToolsRequestSchema,
9
+ } = require("@modelcontextprotocol/sdk/types.js");
17
10
  const axios = require("axios");
18
- const { Command } = require("commander");
19
11
 
20
12
  /**
21
- * Helper to send a POST request to a Bitrix24 REST method.
22
- *
23
- * @param {string} method The name of the method (e.g. "profile", "tasks.task.get").
24
- * @param {object} body The request payload, encoded as JSON.
25
- * @param {string} base The base URL of the Bitrix24 webhook (no trailing slash).
26
- * @returns {Promise<any>} Resolves with the parsed JSON data from the response.
13
+ * Bitrix24 MCP Server
14
+ *
15
+ * This server implements the Model Context Protocol (MCP) to provide
16
+ * Bitrix24 integration for AI agents.
27
17
  */
28
- async function callBitrix(method, body, base) {
29
- const baseUrl = (base || process.env.B24_BASE || "").replace(/\/$/, "");
30
- if (!baseUrl) {
31
- throw new Error(
32
- "Base URL for Bitrix24 webhook is not set. Use --base option or set B24_BASE environment variable."
33
- );
18
+
19
+ const B24_BASE = process.env.B24_BASE;
20
+
21
+ if (!B24_BASE) {
22
+ console.error("Error: B24_BASE environment variable is not set.");
23
+ console.error("Please set it to your Bitrix24 webhook URL (e.g., https://domain.bitrix24.ru/rest/1/abcde/).");
24
+ process.exit(1);
25
+ }
26
+
27
+ const server = new Server(
28
+ {
29
+ name: "bitrix24-mcp-server",
30
+ version: "2.1.0",
31
+ },
32
+ {
33
+ capabilities: {
34
+ tools: {},
35
+ },
34
36
  }
35
- const url = `${baseUrl}/${method}`;
37
+ );
38
+
39
+ /**
40
+ * Helper to call Bitrix24 REST API
41
+ */
42
+ async function callBitrix(method, body) {
43
+ const url = `${B24_BASE.replace(/\/$/, "")}/${method}`;
36
44
  try {
37
45
  const response = await axios.post(url, body, {
38
46
  headers: { "Content-Type": "application/json" },
39
47
  });
40
48
  return response.data;
41
49
  } catch (err) {
42
- // Rethrow with a human‑readable message
43
50
  const msg = err.response
44
51
  ? `HTTP ${err.response.status}: ${JSON.stringify(err.response.data)}`
45
52
  : err.message;
@@ -48,218 +55,143 @@ async function callBitrix(method, body, base) {
48
55
  }
49
56
 
50
57
  /**
51
- * Parse a comma‑separated list into an array. Returns undefined if the input
52
- * is falsy.
53
- *
54
- * @param {string|undefined} value The comma‑separated string.
58
+ * Define available tools
55
59
  */
56
- function parseList(value) {
57
- if (!value) return undefined;
58
- return value
59
- .split(/,/) // split on commas
60
- .map((s) => s.trim())
61
- .filter((s) => s.length > 0);
62
- }
60
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
61
+ return {
62
+ tools: [
63
+ {
64
+ name: "get_profile",
65
+ description: "Fetch current user profile information from Bitrix24",
66
+ inputSchema: {
67
+ type: "object",
68
+ properties: {},
69
+ },
70
+ },
71
+ {
72
+ name: "get_task",
73
+ description: "Retrieve task details by ID from Bitrix24",
74
+ inputSchema: {
75
+ type: "object",
76
+ properties: {
77
+ id: { type: "number", description: "The unique ID of the task" },
78
+ select: {
79
+ type: "array",
80
+ items: { type: "string" },
81
+ description: "Fields to return (default: ['*'])"
82
+ },
83
+ },
84
+ required: ["id"],
85
+ },
86
+ },
87
+ {
88
+ name: "search_tasks",
89
+ description: "Search for tasks by title in Bitrix24",
90
+ inputSchema: {
91
+ type: "object",
92
+ properties: {
93
+ title: { type: "string", description: "Substring of the task title to search for" },
94
+ order: { type: "string", description: "Field to sort by (default: 'ID')" },
95
+ dir: { type: "string", enum: ["asc", "desc"], description: "Sort direction (default: 'desc')" },
96
+ start: { type: "number", description: "Pagination offset (default: 0)" },
97
+ },
98
+ required: ["title"],
99
+ },
100
+ },
101
+ {
102
+ name: "search_groups",
103
+ description: "Search for workgroups or projects by name in Bitrix24",
104
+ inputSchema: {
105
+ type: "object",
106
+ properties: {
107
+ name: { type: "string", description: "Substring of the group name to search for" },
108
+ start: { type: "number", description: "Pagination offset (default: 0)" },
109
+ },
110
+ required: ["name"],
111
+ },
112
+ },
113
+ {
114
+ name: "get_group",
115
+ description: "Retrieve detailed information about a workgroup or project by ID",
116
+ inputSchema: {
117
+ type: "object",
118
+ properties: {
119
+ id: { type: "number", description: "The unique ID of the group" },
120
+ },
121
+ required: ["id"],
122
+ },
123
+ },
124
+ ],
125
+ };
126
+ });
63
127
 
64
- async function main() {
65
- const program = new Command();
66
- program
67
- .name("bitrix24-skill")
68
- .description(
69
- "CLI for interacting with Bitrix24 via incoming webhook. Provides commands to query profile, tasks, and workgroups."
70
- )
71
- .option(
72
- "--base <base>",
73
- "Base URL for Bitrix24 webhook (e.g. https://domain.bitrix24.ru/rest/USER_ID/WEBHOOK_CODE). Defaults to environment variable B24_BASE.",
74
- undefined
75
- );
128
+ /**
129
+ * Handle tool calls
130
+ */
131
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
132
+ const { name, arguments: args } = request.params;
76
133
 
77
- // Profile command
78
- program
79
- .command("profile")
80
- .description("Fetch current user profile information")
81
- .action(async (cmdOptions) => {
82
- const opts = program.opts();
83
- const data = await callBitrix("profile", {}, opts.base);
84
- console.log(JSON.stringify(data, null, 2));
85
- });
134
+ try {
135
+ switch (name) {
136
+ case "get_profile": {
137
+ const data = await callBitrix("profile", {});
138
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
139
+ }
86
140
 
87
- // Get task by ID
88
- program
89
- .command("get-task")
90
- .description("Retrieve task details by ID")
91
- .requiredOption("--id <id>", "Task identifier")
92
- .option(
93
- "--select <fields>",
94
- "Comma‑separated list of fields to select (default '*')",
95
- undefined
96
- )
97
- .action(async (options) => {
98
- const opts = program.opts();
99
- const taskId = Number(options.id);
100
- if (isNaN(taskId)) {
101
- throw new Error("The --id option must be a number");
141
+ case "get_task": {
142
+ const body = { taskId: args.id, select: args.select || ["*"] };
143
+ const data = await callBitrix("tasks.task.get", body);
144
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
102
145
  }
103
- const select = parseList(options.select) || ["*"];
104
- const body = { taskId, select };
105
- const data = await callBitrix("tasks.task.get", body, opts.base);
106
- console.log(JSON.stringify(data, null, 2));
107
- });
108
146
 
109
- // Search tasks by title
110
- program
111
- .command("search-task")
112
- .description("Search tasks by part of the title")
113
- .requiredOption("--title <title>", "Substring of the task title to search for")
114
- .option(
115
- "--select <fields>",
116
- "Comma‑separated list of fields to select (default ID,TITLE,STATUS,RESPONSIBLE_ID,GROUP_ID,CREATED_DATE,CHANGED_DATE)",
117
- undefined
118
- )
119
- .option(
120
- "--order <field>",
121
- "Field to sort by (default: ID)",
122
- "ID"
123
- )
124
- .option(
125
- "--dir <direction>",
126
- "Sort direction: asc or desc (default: desc)",
127
- "desc"
128
- )
129
- .option(
130
- "--start <offset>",
131
- "Pagination offset (multiple of 50; default 0)",
132
- (value) => Number(value),
133
- 0
134
- )
135
- .action(async (options) => {
136
- const opts = program.opts();
137
- const select =
138
- parseList(options.select) || [
139
- "ID",
140
- "TITLE",
141
- "STATUS",
142
- "RESPONSIBLE_ID",
143
- "GROUP_ID",
144
- "CREATED_DATE",
145
- "CHANGED_DATE",
146
- ];
147
- const order = { [options.order]: options.dir.toUpperCase() };
148
- const body = {
149
- order,
150
- filter: { "%TITLE": options.title },
151
- select,
152
- start: options.start,
153
- };
154
- const data = await callBitrix("tasks.task.list", body, opts.base);
155
- console.log(JSON.stringify(data, null, 2));
156
- });
147
+ case "search_tasks": {
148
+ const body = {
149
+ order: { [args.order || "ID"]: (args.dir || "desc").toUpperCase() },
150
+ filter: { "%TITLE": args.title },
151
+ select: ["ID", "TITLE", "STATUS", "RESPONSIBLE_ID", "GROUP_ID"],
152
+ start: args.start || 0,
153
+ };
154
+ const data = await callBitrix("tasks.task.list", body);
155
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
156
+ }
157
157
 
158
- // Search workgroup by name
159
- program
160
- .command("search-group")
161
- .description("Search workgroups/projects by part of the name")
162
- .requiredOption("--name <name>", "Substring of the group name to search for")
163
- .option(
164
- "--select <fields>",
165
- "Comma‑separated list of fields to select (default ID,NAME,PROJECT,ACTIVE,VISIBLE,CLOSED)",
166
- undefined
167
- )
168
- .option(
169
- "--order <field>",
170
- "Field to sort by (default: ID)",
171
- "ID"
172
- )
173
- .option(
174
- "--dir <direction>",
175
- "Sort direction: ASC or DESC (default: DESC)",
176
- "DESC"
177
- )
178
- .option(
179
- "--start <offset>",
180
- "Pagination offset (multiple of 50; default 0)",
181
- (value) => Number(value),
182
- 0
183
- )
184
- .action(async (options) => {
185
- const opts = program.opts();
186
- const select =
187
- parseList(options.select) || [
188
- "ID",
189
- "NAME",
190
- "PROJECT",
191
- "ACTIVE",
192
- "VISIBLE",
193
- "CLOSED",
194
- ];
195
- const order = { [options.order]: options.dir.toUpperCase() };
196
- const body = {
197
- filter: { "%NAME": options.name },
198
- select,
199
- order,
200
- start: options.start,
201
- };
202
- const data = await callBitrix(
203
- "socialnetwork.api.workgroup.list",
204
- body,
205
- opts.base
206
- );
207
- console.log(JSON.stringify(data, null, 2));
208
- });
158
+ case "search_groups": {
159
+ const body = {
160
+ filter: { "%NAME": args.name },
161
+ order: { ID: "DESC" },
162
+ start: args.start || 0,
163
+ };
164
+ const data = await callBitrix("socialnetwork.api.workgroup.list", body);
165
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
166
+ }
209
167
 
210
- // Get workgroup by ID
211
- program
212
- .command("get-group")
213
- .description("Retrieve detailed information about a workgroup/project by ID")
214
- .requiredOption("--id <id>", "Workgroup identifier")
215
- .option(
216
- "--select <fields>",
217
- "Comma‑separated list of fields to select (default includes common fields)",
218
- undefined
219
- )
220
- .action(async (options) => {
221
- const opts = program.opts();
222
- const groupId = Number(options.id);
223
- if (isNaN(groupId)) {
224
- throw new Error("The --id option must be a number");
168
+ case "get_group": {
169
+ const body = { params: { groupId: args.id } };
170
+ const data = await callBitrix("socialnetwork.api.workgroup.get", body);
171
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
225
172
  }
226
- const select =
227
- parseList(options.select) || [
228
- "ACTIONS",
229
- "AVATAR",
230
- "AVATAR_DATA",
231
- "COUNTERS",
232
- "DATE_CREATE",
233
- "DEPARTMENTS",
234
- "EFFICIENCY",
235
- "FEATURES",
236
- "GROUP_MEMBERS_LIST",
237
- "LIST_OF_MEMBERS",
238
- "OWNER_DATA",
239
- "PRIVACY_TYPE",
240
- "SUBJECT_DATA",
241
- "TAGS",
242
- "USER_DATA",
243
- ];
244
- const body = { params: { groupId, select } };
245
- const data = await callBitrix(
246
- "socialnetwork.api.workgroup.get",
247
- body,
248
- opts.base
249
- );
250
- console.log(JSON.stringify(data, null, 2));
251
- });
252
173
 
253
- // Parse command line arguments. Use async to allow await inside actions.
254
- try {
255
- await program.parseAsync(process.argv);
256
- } catch (err) {
257
- console.error(err.message || err);
258
- process.exit(1);
174
+ default:
175
+ throw new Error(`Unknown tool: ${name}`);
176
+ }
177
+ } catch (error) {
178
+ return {
179
+ content: [{ type: "text", text: `Error: ${error.message}` }],
180
+ isError: true,
181
+ };
259
182
  }
183
+ });
184
+
185
+ /**
186
+ * Start the server
187
+ */
188
+ async function main() {
189
+ const transport = new StdioServerTransport();
190
+ await server.connect(transport);
191
+ console.error("Bitrix24 MCP Server running on stdio");
260
192
  }
261
193
 
262
- main().catch((err) => {
263
- console.error(err.message || err);
194
+ main().catch((error) => {
195
+ console.error("Fatal error in main():", error);
264
196
  process.exit(1);
265
- });
197
+ });
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@x0333/bitrix24-mcp-server",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "description": "Bitrix24 MCP Server for AI Agent Integration.",
5
5
  "main": "index.js",
6
6
  "bin": {
7
- "bitrix24-skill": "index.js"
7
+ "bitrix24-mcp": "index.js"
8
8
  },
9
9
  "type": "commonjs",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "axios": "^1.5.0",
13
- "commander": "^11.0.0"
12
+ "@modelcontextprotocol/sdk": "^1.29.0",
13
+ "axios": "^1.5.0"
14
14
  },
15
15
  "keywords": [
16
16
  "mcp",
@@ -20,7 +20,6 @@
20
20
  "automation"
21
21
  ],
22
22
  "author": "Lukentui",
23
- "license": "MIT",
24
23
  "publishConfig": {
25
24
  "access": "public"
26
25
  }