backlog-mcp-server 0.18.0 → 0.19.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.
Files changed (86) hide show
  1. package/README.ja.md +16 -0
  2. package/README.md +17 -0
  3. package/build/auth/oauthRoutes.js +1 -1
  4. package/build/backlog/customFields.js +12 -9
  5. package/build/handlers/builders/composeDynamicToolHandler.d.ts +147 -0
  6. package/build/handlers/builders/composeDynamicToolHandler.js +55 -0
  7. package/build/httpMcpServer.js +8 -1
  8. package/build/lib.d.ts +2 -0
  9. package/build/lib.js +1 -0
  10. package/build/loadDescriptionOverrides.js +2 -2
  11. package/build/registerTools.js +18 -0
  12. package/build/tools/addDocument.d.ts +1 -1
  13. package/build/tools/addDocument.js +2 -1
  14. package/build/tools/addIssue.d.ts +1 -1
  15. package/build/tools/addIssueComment.d.ts +1 -1
  16. package/build/tools/addProject.d.ts +1 -1
  17. package/build/tools/addPullRequest.d.ts +1 -1
  18. package/build/tools/addPullRequestComment.d.ts +1 -1
  19. package/build/tools/addRelatedIssue.d.ts +1 -1
  20. package/build/tools/addVersionMilestone.d.ts +1 -1
  21. package/build/tools/addWatching.d.ts +1 -1
  22. package/build/tools/addWiki.d.ts +1 -1
  23. package/build/tools/countIssues.d.ts +1 -1
  24. package/build/tools/deleteIssue.d.ts +1 -1
  25. package/build/tools/deleteProject.d.ts +1 -1
  26. package/build/tools/deleteVersion.d.ts +1 -1
  27. package/build/tools/deleteWatching.d.ts +1 -1
  28. package/build/tools/getCategories.d.ts +1 -1
  29. package/build/tools/getCustomFields.d.ts +1 -1
  30. package/build/tools/getDocument.d.ts +1 -1
  31. package/build/tools/getDocument.js +1 -0
  32. package/build/tools/getDocumentTree.d.ts +1 -1
  33. package/build/tools/getDocuments.d.ts +1 -1
  34. package/build/tools/getDocuments.js +1 -0
  35. package/build/tools/getGitRepositories.d.ts +1 -1
  36. package/build/tools/getGitRepository.d.ts +1 -1
  37. package/build/tools/getIssue.d.ts +1 -1
  38. package/build/tools/getIssueAttachment.d.ts +16 -0
  39. package/build/tools/getIssueAttachment.js +345 -0
  40. package/build/tools/getIssueComments.d.ts +1 -1
  41. package/build/tools/getIssueTypes.d.ts +1 -1
  42. package/build/tools/getIssues.d.ts +15 -15
  43. package/build/tools/getMyself.d.ts +1 -1
  44. package/build/tools/getNotifications.d.ts +1 -1
  45. package/build/tools/getNotifications.js +3 -0
  46. package/build/tools/getNotificationsCount.d.ts +1 -1
  47. package/build/tools/getPriorities.d.ts +1 -1
  48. package/build/tools/getProject.d.ts +1 -1
  49. package/build/tools/getProjectList.d.ts +1 -1
  50. package/build/tools/getProjectUsers.d.ts +1 -1
  51. package/build/tools/getPullRequest.d.ts +1 -1
  52. package/build/tools/getPullRequestComments.d.ts +1 -1
  53. package/build/tools/getPullRequests.d.ts +1 -1
  54. package/build/tools/getPullRequestsCount.d.ts +1 -1
  55. package/build/tools/getRelatedIssues.d.ts +1 -1
  56. package/build/tools/getResolutions.d.ts +1 -1
  57. package/build/tools/getSpace.d.ts +1 -1
  58. package/build/tools/getSpaceActivities.d.ts +1 -1
  59. package/build/tools/getUserRecentUpdates.d.ts +1 -1
  60. package/build/tools/getUserStarsCount.d.ts +1 -1
  61. package/build/tools/getUsers.d.ts +1 -1
  62. package/build/tools/getVersionMilestoneList.d.ts +1 -1
  63. package/build/tools/getWatchingListCount.d.ts +1 -1
  64. package/build/tools/getWatchingListItems.d.ts +1 -1
  65. package/build/tools/getWiki.d.ts +1 -1
  66. package/build/tools/getWikiPages.d.ts +1 -1
  67. package/build/tools/getWikisCount.d.ts +1 -1
  68. package/build/tools/markNotificationAsRead.d.ts +1 -1
  69. package/build/tools/markWatchingAsRead.d.ts +1 -1
  70. package/build/tools/removeRelatedIssue.d.ts +1 -1
  71. package/build/tools/resetUnreadNotificationCount.d.ts +1 -1
  72. package/build/tools/shared/customFieldFiltersSchema.d.ts +1 -1
  73. package/build/tools/tools.js +2 -0
  74. package/build/tools/updateIssue.d.ts +1 -1
  75. package/build/tools/updateIssueComment.d.ts +1 -1
  76. package/build/tools/updateProject.d.ts +1 -1
  77. package/build/tools/updatePullRequest.d.ts +1 -1
  78. package/build/tools/updatePullRequestComment.d.ts +1 -1
  79. package/build/tools/updateVersionMilestone.d.ts +1 -1
  80. package/build/tools/updateWatching.d.ts +1 -1
  81. package/build/tools/updateWiki.d.ts +1 -1
  82. package/build/types/tool.d.ts +1 -1
  83. package/build/types/toolsets.d.ts +12 -1
  84. package/build/utils/logger.js +41 -3
  85. package/build/utils/resolveIdOrKey.d.ts +2 -2
  86. package/package.json +18 -25
package/README.ja.md CHANGED
@@ -469,6 +469,22 @@ MAX_TOKENS=10000
469
469
 
470
470
  > 注:これはベストエフォートの緩和策であり、保証された強制ではありません。
471
471
 
472
+ ### ログ出力
473
+
474
+ ログは **stderr** に出力されます(stdio トランスポートでは stdout が JSON-RPC のストリームに使われるため)。
475
+
476
+ | 変数 | 説明 |
477
+ | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
478
+ | `LOG_LEVEL` | `fatal` / `error` / `warn` / `info` / `debug` / `trace` / `silent`。既定は `NODE_ENV` が `production`(未設定時もこれに含まれる)のとき `error`、それ以外は `debug`。認識できない値は通知した上で既定値にフォールバックします。 |
479
+
480
+ 出力**フォーマット**は引き続き `NODE_ENV` が決めます(`production` 以外では `pino-pretty` が利用可能なら人間向けの整形出力に切り替わります)。ログ量を変えたいときは `NODE_ENV` ではなく `LOG_LEVEL` を設定してください。デプロイ先で構造化 JSON のまま保てます。
481
+
482
+ `pino-pretty` は開発用の依存なので、公開されている npm パッケージにもコンテナイメージにも含まれていません。そのため、これらでは `NODE_ENV` の値に関わらず常に構造化 JSON が出力され、出力を変えられるのは `LOG_LEVEL` だけです。
483
+
484
+ ```
485
+ LOG_LEVEL=info node build/index.js --transport http
486
+ ```
487
+
472
488
  ### 完全なカスタム設定例
473
489
 
474
490
  このセクションでは、複数の環境変数を使用した高度な設定を示します。これらは実験的な機能であり、すべてのMCPクライアントでサポートされているとは限りません。これはMCP標準仕様の一部ではなく、注意して使用する必要があります。
package/README.md CHANGED
@@ -294,6 +294,7 @@ Tools for managing projects, categories, custom fields, and issue types.
294
294
  Tools for managing issues, their comments, and related items like priorities, categories, custom fields, issue types, resolutions, and watching lists.
295
295
 
296
296
  - `get_issue`: Returns information about a specific issue.
297
+ - `get_issue_attachment`: Downloads one attachment of an issue. Returns it as image or embedded resource content, or as base64 with `format: "base64"`.
297
298
  - `get_issues`: Returns list of issues.
298
299
  - `count_issues`: Returns count of issues.
299
300
  - `add_issue`: Creates a new issue in the specified project.
@@ -586,6 +587,22 @@ If a response exceeds the limit, it will be truncated with a warning.
586
587
 
587
588
  > Note: This is a best-effort mitigation, not a guaranteed enforcement.
588
589
 
590
+ ### Logging
591
+
592
+ The server logs to **stderr** (stdout carries the JSON-RPC stream on the stdio transport).
593
+
594
+ | Variable | Description |
595
+ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
596
+ | `LOG_LEVEL` | `fatal`, `error`, `warn`, `info`, `debug`, `trace` or `silent`. Defaults to `error` when `NODE_ENV` is `production` — which is also the default when `NODE_ENV` is unset — and to `debug` otherwise. An unrecognised value is reported and the default is used. |
597
+
598
+ `NODE_ENV` still selects the output *format*: any value other than `production` switches to human-readable `pino-pretty` output when that package is available. Use `LOG_LEVEL`, not `NODE_ENV`, to change how much is logged, so that a deployment keeps structured JSON:
599
+
600
+ `pino-pretty` is a development dependency, so neither the published npm package nor the container image carries a copy. In those, logs are structured JSON whatever `NODE_ENV` says, and `LOG_LEVEL` is the only setting that changes the output.
601
+
602
+ ```
603
+ LOG_LEVEL=info node build/index.js --transport http
604
+ ```
605
+
589
606
  ### Full Custom Configuration Example
590
607
 
591
608
  This section demonstrates advanced configuration using multiple environment variables. These are experimental features and may not be supported across all MCP clients. This is not part of the MCP standard specification and should be used with caution.
@@ -119,7 +119,7 @@ export function createOAuthRoutes(config, store, mcpPath) {
119
119
  const declaredApplicationType = body.application_type ?? undefined;
120
120
  if (declaredApplicationType !== undefined &&
121
121
  !APPLICATION_TYPES.includes(declaredApplicationType)) {
122
- return c.json(oauthError('invalid_client_metadata', `Unsupported application_type: ${String(declaredApplicationType)}. Supported: ${APPLICATION_TYPES.join(', ')}`), 400);
122
+ return c.json(oauthError('invalid_client_metadata', `Unsupported application_type: ${JSON.stringify(declaredApplicationType)}. Supported: ${APPLICATION_TYPES.join(', ')}`), 400);
123
123
  }
124
124
  const applicationType = resolveApplicationType(declaredApplicationType, redirectUris);
125
125
  if (applicationType === 'web') {
@@ -49,20 +49,23 @@ export function customFieldFiltersToPayload(customFields) {
49
49
  break;
50
50
  }
51
51
  case 'list': {
52
- if (Array.isArray(field.value)) {
53
- const values = field.value.filter((value) => Number.isFinite(value));
54
- if (values.length > 0) {
55
- result[`${baseKey}[]`] = values;
56
- }
57
- }
58
- else if (Number.isFinite(field.value)) {
59
- result[baseKey] = field.value;
52
+ // Always an array, single value included. Backlog matches a list custom
53
+ // field only on the indexed parameters `customField_1[0]=…`, which
54
+ // `Request.toQueryString` produces from an array under a `customField_`
55
+ // key. A bare `customField_1=…` is not matched, and neither is the
56
+ // `customField_1[][0]=…` that a `[]` suffix on this key would produce.
57
+ // Neither is rejected either — the filter is silently ignored and every
58
+ // issue comes back, so the caller cannot tell a filtered query from an
59
+ // unfiltered one.
60
+ const values = (Array.isArray(field.value) ? field.value : [field.value]).filter((value) => Number.isFinite(value));
61
+ if (values.length > 0) {
62
+ result[baseKey] = values;
60
63
  }
61
64
  break;
62
65
  }
63
66
  default: {
64
67
  const exhaustiveCheck = field;
65
- throw new Error(`Unsupported custom field filter type: ${exhaustiveCheck}`);
68
+ throw new Error(`Unsupported custom field filter type: ${JSON.stringify(exhaustiveCheck)}`);
66
69
  }
67
70
  }
68
71
  }
@@ -0,0 +1,147 @@
1
+ import { z } from 'zod';
2
+ import { ErrorLike } from '../../types/result.js';
3
+ import { DynamicToolDefinition } from '../../types/tool.js';
4
+ export type ComposeDynamicOptions = {
5
+ errorHandler?: (err: unknown) => ErrorLike;
6
+ useOrganization?: boolean;
7
+ };
8
+ type DynamicInput = {
9
+ organization?: string;
10
+ } & Record<string, unknown>;
11
+ /**
12
+ * Builds the schema and handler a dynamic tool is registered with.
13
+ *
14
+ * The counterpart of `composeToolHandler` for tools that produce a
15
+ * `CallToolResult` themselves. Those cannot go through the whole pipeline:
16
+ * field picking, the token limit and `wrapWithToolResult` all assume a JSON
17
+ * value they may reshape, and reshaping is exactly what a tool returning binary
18
+ * content must not allow — a base64 payload cut at the token limit is a corrupt
19
+ * file reported as a success.
20
+ *
21
+ * The two steps that are not about reshaping still apply, and are why this
22
+ * exists rather than registering the handler directly:
23
+ *
24
+ * - `wrapWithOrganizationContext`, without which a dynamic tool ignores
25
+ * `organization` and always talks to the default space;
26
+ * - `wrapWithErrorHandling`, without which a thrown Backlog error escapes as a
27
+ * protocol error rather than an `isError` result, and never reaches the
28
+ * handler that reacts to Backlog rejecting a credential.
29
+ *
30
+ * As in `composeToolHandler`, the returned schema is a fresh object: the tool
31
+ * definition is never mutated, because one toolset group is shared across
32
+ * per-request servers.
33
+ */
34
+ export declare function composeDynamicToolHandler(tool: DynamicToolDefinition<any>, { errorHandler, useOrganization }?: ComposeDynamicOptions): {
35
+ schema: z.ZodObject<{
36
+ [x: string]: any;
37
+ organization: z.ZodOptional<z.ZodString>;
38
+ } | {
39
+ [x: string]: any;
40
+ organization?: undefined;
41
+ }, z.core.$strip>;
42
+ handler: (input: DynamicInput) => Promise<{
43
+ [x: string]: unknown;
44
+ _meta?: {
45
+ [x: string]: unknown;
46
+ "io.modelcontextprotocol/serverInfo"?: {
47
+ version: string;
48
+ websiteUrl?: string | undefined;
49
+ description?: string | undefined;
50
+ icons?: {
51
+ src: string;
52
+ mimeType?: string | undefined;
53
+ sizes?: string[] | undefined;
54
+ theme?: "dark" | "light" | undefined;
55
+ }[] | undefined;
56
+ name: string;
57
+ title?: string | undefined;
58
+ } | undefined;
59
+ } | undefined;
60
+ content: ({
61
+ type: "text";
62
+ text: string;
63
+ annotations?: {
64
+ audience?: ("assistant" | "user")[] | undefined;
65
+ priority?: number | undefined;
66
+ lastModified?: string | undefined;
67
+ } | undefined;
68
+ _meta?: {
69
+ [x: string]: unknown;
70
+ } | undefined;
71
+ } | {
72
+ type: "image";
73
+ data: string;
74
+ mimeType: string;
75
+ annotations?: {
76
+ audience?: ("assistant" | "user")[] | undefined;
77
+ priority?: number | undefined;
78
+ lastModified?: string | undefined;
79
+ } | undefined;
80
+ _meta?: {
81
+ [x: string]: unknown;
82
+ } | undefined;
83
+ } | {
84
+ type: "audio";
85
+ data: string;
86
+ mimeType: string;
87
+ annotations?: {
88
+ audience?: ("assistant" | "user")[] | undefined;
89
+ priority?: number | undefined;
90
+ lastModified?: string | undefined;
91
+ } | undefined;
92
+ _meta?: {
93
+ [x: string]: unknown;
94
+ } | undefined;
95
+ } | {
96
+ uri: string;
97
+ description?: string | undefined;
98
+ mimeType?: string | undefined;
99
+ size?: number | undefined;
100
+ annotations?: {
101
+ audience?: ("assistant" | "user")[] | undefined;
102
+ priority?: number | undefined;
103
+ lastModified?: string | undefined;
104
+ } | undefined;
105
+ _meta?: {
106
+ [x: string]: unknown;
107
+ } | undefined;
108
+ icons?: {
109
+ src: string;
110
+ mimeType?: string | undefined;
111
+ sizes?: string[] | undefined;
112
+ theme?: "dark" | "light" | undefined;
113
+ }[] | undefined;
114
+ name: string;
115
+ title?: string | undefined;
116
+ type: "resource_link";
117
+ } | {
118
+ type: "resource";
119
+ resource: {
120
+ uri: string;
121
+ mimeType?: string | undefined;
122
+ _meta?: {
123
+ [x: string]: unknown;
124
+ } | undefined;
125
+ text: string;
126
+ } | {
127
+ uri: string;
128
+ mimeType?: string | undefined;
129
+ _meta?: {
130
+ [x: string]: unknown;
131
+ } | undefined;
132
+ blob: string;
133
+ };
134
+ annotations?: {
135
+ audience?: ("assistant" | "user")[] | undefined;
136
+ priority?: number | undefined;
137
+ lastModified?: string | undefined;
138
+ } | undefined;
139
+ _meta?: {
140
+ [x: string]: unknown;
141
+ } | undefined;
142
+ })[];
143
+ structuredContent?: unknown;
144
+ isError?: boolean | undefined;
145
+ }>;
146
+ };
147
+ export {};
@@ -0,0 +1,55 @@
1
+ import { z } from 'zod';
2
+ import { wrapWithErrorHandling } from '../transformers/wrapWithErrorHandling.js';
3
+ import { wrapWithOrganizationContext } from '../transformers/wrapWithOrganizationContext.js';
4
+ import { isErrorLike } from '../../types/result.js';
5
+ /**
6
+ * Builds the schema and handler a dynamic tool is registered with.
7
+ *
8
+ * The counterpart of `composeToolHandler` for tools that produce a
9
+ * `CallToolResult` themselves. Those cannot go through the whole pipeline:
10
+ * field picking, the token limit and `wrapWithToolResult` all assume a JSON
11
+ * value they may reshape, and reshaping is exactly what a tool returning binary
12
+ * content must not allow — a base64 payload cut at the token limit is a corrupt
13
+ * file reported as a success.
14
+ *
15
+ * The two steps that are not about reshaping still apply, and are why this
16
+ * exists rather than registering the handler directly:
17
+ *
18
+ * - `wrapWithOrganizationContext`, without which a dynamic tool ignores
19
+ * `organization` and always talks to the default space;
20
+ * - `wrapWithErrorHandling`, without which a thrown Backlog error escapes as a
21
+ * protocol error rather than an `isError` result, and never reaches the
22
+ * handler that reacts to Backlog rejecting a credential.
23
+ *
24
+ * As in `composeToolHandler`, the returned schema is a fresh object: the tool
25
+ * definition is never mutated, because one toolset group is shared across
26
+ * per-request servers.
27
+ */
28
+ export function composeDynamicToolHandler(
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ tool, { errorHandler, useOrganization = false } = {}) {
31
+ // `extend` even with nothing to add, so the returned schema is always a copy.
32
+ // Handing back `tool.schema` itself would make the invariant above hold only
33
+ // when `useOrganization` is set.
34
+ const schema = tool.schema.extend(useOrganization
35
+ ? {
36
+ organization: z
37
+ .string()
38
+ .optional()
39
+ .describe('Optional organization name. Use list_organizations to inspect available organizations.'),
40
+ }
41
+ : {});
42
+ const handler = wrapWithErrorHandling(wrapWithOrganizationContext(tool.handler), errorHandler);
43
+ return {
44
+ schema,
45
+ handler: async (input) => {
46
+ const result = await handler(input);
47
+ return isErrorLike(result)
48
+ ? {
49
+ isError: true,
50
+ content: [{ type: 'text', text: result.message }],
51
+ }
52
+ : result.data;
53
+ },
54
+ };
55
+ }
@@ -16,6 +16,14 @@ export const runHttpMcpServer = async (options) => {
16
16
  const app = new Hono();
17
17
  const isLocalhostBind = LOCALHOST_BINDS.includes(host);
18
18
  const oauthEnabled = !!(oauthConfig && tokenStore);
19
+ // Registered before the Host and Origin guards so that it stays reachable
20
+ // under an allow list. A load balancer health check addresses the target
21
+ // directly and puts its address in `Host` — AWS ALB gives no way to override
22
+ // it — so a guarded `/health` can only ever answer 403, leaving an operator
23
+ // to choose between dropping the allow list and treating 403 as healthy.
24
+ // Exempting it costs nothing: the response is constant and reaches no state,
25
+ // so there is nothing here for a rebinding page to reach either.
26
+ app.get('/health', (c) => c.json({ status: 'healthy', timestamp: new Date().toISOString(), version }));
19
27
  // DNS rebinding protection. `Host` is the actual defense: a rebinding page
20
28
  // reaches us carrying its own hostname, which the allow list rejects.
21
29
  if (allowedHosts?.length) {
@@ -36,7 +44,6 @@ export const runHttpMcpServer = async (options) => {
36
44
  else if (isLocalhostBind && !allowedHosts?.length) {
37
45
  app.use('*', localhostOriginValidation());
38
46
  }
39
- app.get('/health', (c) => c.json({ status: 'healthy', timestamp: new Date().toISOString(), version }));
40
47
  if (oauthEnabled) {
41
48
  const { createOAuthRoutes } = await import('./auth/oauthRoutes.js');
42
49
  const { createBearerAuthMiddleware } = await import('./auth/bearerAuthMiddleware.js');
package/build/lib.d.ts CHANGED
@@ -14,11 +14,13 @@
14
14
  */
15
15
  export { allTools } from './tools/tools.js';
16
16
  export { composeToolHandler } from './handlers/builders/composeToolHandler.js';
17
+ export { composeDynamicToolHandler } from './handlers/builders/composeDynamicToolHandler.js';
17
18
  export { createDescriptionHelper } from './createDescriptionHelper.js';
18
19
  export { backlogErrorHandler } from './backlog/backlogErrorHandler.js';
19
20
  export { buildToolSchema } from './types/tool.js';
20
21
  export { isErrorLike } from './types/result.js';
21
22
  export type { ComposeOptions } from './handlers/builders/composeToolHandler.js';
23
+ export type { ComposeDynamicOptions } from './handlers/builders/composeDynamicToolHandler.js';
22
24
  export type { DescriptionHelper } from './createDescriptionHelper.js';
23
25
  export type { ToolDefinition, DynamicToolDefinition } from './types/tool.js';
24
26
  export type { Toolset, ToolsetGroup, DynamicToolset, DynamicToolsetGroup, } from './types/toolsets.js';
package/build/lib.js CHANGED
@@ -14,6 +14,7 @@
14
14
  */
15
15
  export { allTools } from './tools/tools.js';
16
16
  export { composeToolHandler } from './handlers/builders/composeToolHandler.js';
17
+ export { composeDynamicToolHandler } from './handlers/builders/composeDynamicToolHandler.js';
17
18
  export { createDescriptionHelper } from './createDescriptionHelper.js';
18
19
  export { backlogErrorHandler } from './backlog/backlogErrorHandler.js';
19
20
  export { buildToolSchema } from './types/tool.js';
@@ -69,8 +69,8 @@ export function loadDescriptionOverrides(options) {
69
69
  continue;
70
70
  if (outcome.status === 'unreadable') {
71
71
  // `logger.error`, not `warn`: the logger runs at level `error` unless
72
- // NODE_ENV says otherwise, so a warning here would never reach the user
73
- // this message exists for.
72
+ // LOG_LEVEL or NODE_ENV says otherwise, so a warning here would never
73
+ // reach the user this message exists for.
74
74
  logger.error({ err: outcome.error, filePath }, 'Could not read the description override file; continuing with the built-in defaults');
75
75
  return {};
76
76
  }
@@ -1,4 +1,5 @@
1
1
  import { backlogErrorHandler } from './backlog/backlogErrorHandler.js';
2
+ import { composeDynamicToolHandler } from './handlers/builders/composeDynamicToolHandler.js';
2
3
  import { composeToolHandler } from './handlers/builders/composeToolHandler.js';
3
4
  export function registerTools(server, toolsetGroup, options) {
4
5
  const { useFields, maxTokens, prefix, useOrganization } = options;
@@ -15,6 +16,23 @@ export function registerTools(server, toolsetGroup, options) {
15
16
  useOrganization,
16
17
  }),
17
18
  });
19
+ // Tools that build their own result, registered from the same toolsets so
20
+ // that `--enable-toolsets` and the prefix cover them too.
21
+ registerToolsets({
22
+ server,
23
+ toolsetGroup: {
24
+ toolsets: toolsetGroup.toolsets.map((toolset) => ({
25
+ name: toolset.name,
26
+ description: toolset.description,
27
+ enabled: toolset.enabled,
28
+ tools: toolset.dynamicTools ?? [],
29
+ })),
30
+ },
31
+ prefix,
32
+ prepareTool: (tool) => composeDynamicToolHandler(
33
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
+ tool, { errorHandler: backlogErrorHandler, useOrganization }),
35
+ });
18
36
  }
19
37
  export function registerDynamicTools(server, dynamicToolsetGroup, prefix) {
20
38
  registerToolsets({
@@ -3,7 +3,7 @@ import { Backlog } from 'backlog-js';
3
3
  import { z } from 'zod';
4
4
  import { DescriptionHelper } from '../createDescriptionHelper.js';
5
5
  import { ToolDefinition } from '../types/tool.js';
6
- declare const addDocumentSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const addDocumentSchema: (t: DescriptionHelper['t']) => {
7
7
  projectId: z.ZodNumber;
8
8
  title: z.ZodOptional<z.ZodString>;
9
9
  content: z.ZodOptional<z.ZodString>;
@@ -28,7 +28,7 @@ const addDocumentSchema = buildToolSchema((t) => ({
28
28
  }));
29
29
  export const addDocumentTool = (backlog, { t }) => {
30
30
  return {
31
- name: 'addDocument',
31
+ name: 'add_document',
32
32
  description: t('TOOL_ADD_DOCUMENT_DESCRIPTION', 'Adds a new document to the specified project.'),
33
33
  schema: z.object(addDocumentSchema(t)),
34
34
  returnsList: false,
@@ -46,6 +46,7 @@ export const addDocumentTool = (backlog, { t }) => {
46
46
  'created',
47
47
  'updatedUser',
48
48
  'updated',
49
+ 'childDocumentIds',
49
50
  ]),
50
51
  importantFields: ['id', 'projectId', 'title', 'plain', 'createdUser'],
51
52
  handler: async (params) => {
@@ -3,7 +3,7 @@ import { Backlog } from 'backlog-js';
3
3
  import { z } from 'zod';
4
4
  import { DescriptionHelper } from '../createDescriptionHelper.js';
5
5
  import { ToolDefinition } from '../types/tool.js';
6
- declare const addIssueSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const addIssueSchema: (t: DescriptionHelper['t']) => {
7
7
  projectId: z.ZodNumber;
8
8
  summary: z.ZodString;
9
9
  issueTypeId: z.ZodNumber;
@@ -3,7 +3,7 @@ import type { Entity } from 'backlog-js';
3
3
  import { Backlog } from 'backlog-js';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const addIssueCommentSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const addIssueCommentSchema: (t: DescriptionHelper['t']) => {
7
7
  issueId: z.ZodOptional<z.ZodNumber>;
8
8
  issueKey: z.ZodOptional<z.ZodString>;
9
9
  content: z.ZodString;
@@ -3,7 +3,7 @@ import type { Entity } from 'backlog-js';
3
3
  import { Backlog } from 'backlog-js';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const addProjectSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const addProjectSchema: (t: DescriptionHelper['t']) => {
7
7
  name: z.ZodString;
8
8
  key: z.ZodString;
9
9
  chartEnabled: z.ZodOptional<z.ZodBoolean>;
@@ -3,7 +3,7 @@ import type { Entity } from 'backlog-js';
3
3
  import { Backlog } from 'backlog-js';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const addPullRequestSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const addPullRequestSchema: (t: DescriptionHelper['t']) => {
7
7
  projectId: z.ZodOptional<z.ZodNumber>;
8
8
  projectKey: z.ZodOptional<z.ZodString>;
9
9
  repoId: z.ZodOptional<z.ZodNumber>;
@@ -3,7 +3,7 @@ import type { Entity } from 'backlog-js';
3
3
  import { Backlog } from 'backlog-js';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const addPullRequestCommentSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const addPullRequestCommentSchema: (t: DescriptionHelper['t']) => {
7
7
  projectId: z.ZodOptional<z.ZodNumber>;
8
8
  projectKey: z.ZodOptional<z.ZodString>;
9
9
  repoId: z.ZodOptional<z.ZodNumber>;
@@ -3,7 +3,7 @@ import type { Entity } from 'backlog-js';
3
3
  import { Backlog } from 'backlog-js';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const addRelatedIssueSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const addRelatedIssueSchema: (t: DescriptionHelper['t']) => {
7
7
  issueId: z.ZodOptional<z.ZodNumber>;
8
8
  issueKey: z.ZodOptional<z.ZodString>;
9
9
  targetIssueId: z.ZodNumber;
@@ -3,7 +3,7 @@ import type { Entity } from 'backlog-js';
3
3
  import { Backlog } from 'backlog-js';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const addVersionMilestoneSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const addVersionMilestoneSchema: (t: DescriptionHelper['t']) => {
7
7
  projectId: z.ZodOptional<z.ZodNumber>;
8
8
  projectKey: z.ZodOptional<z.ZodString>;
9
9
  name: z.ZodString;
@@ -3,7 +3,7 @@ import type { Entity } from 'backlog-js';
3
3
  import { Backlog } from 'backlog-js';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const addWatchingSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const addWatchingSchema: (t: DescriptionHelper['t']) => {
7
7
  issueIdOrKey: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
8
8
  note: z.ZodDefault<z.ZodOptional<z.ZodString>>;
9
9
  };
@@ -3,7 +3,7 @@ import type { Entity } from 'backlog-js';
3
3
  import { Backlog } from 'backlog-js';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const addWikiSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const addWikiSchema: (t: DescriptionHelper['t']) => {
7
7
  projectId: z.ZodNumber;
8
8
  name: z.ZodString;
9
9
  content: z.ZodString;
@@ -3,7 +3,7 @@ import type { Entity } from 'backlog-js';
3
3
  import { Backlog } from 'backlog-js';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const countIssuesSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const countIssuesSchema: (t: DescriptionHelper['t']) => {
7
7
  projectId: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
8
8
  issueTypeId: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
9
9
  categoryId: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
@@ -3,7 +3,7 @@ import type { Entity } from 'backlog-js';
3
3
  import { Backlog } from 'backlog-js';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const deleteIssueSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const deleteIssueSchema: (t: DescriptionHelper['t']) => {
7
7
  issueId: z.ZodOptional<z.ZodNumber>;
8
8
  issueKey: z.ZodOptional<z.ZodString>;
9
9
  };
@@ -3,7 +3,7 @@ import type { Entity } from 'backlog-js';
3
3
  import { Backlog } from 'backlog-js';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const deleteProjectSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const deleteProjectSchema: (t: DescriptionHelper['t']) => {
7
7
  projectId: z.ZodOptional<z.ZodNumber>;
8
8
  projectKey: z.ZodOptional<z.ZodString>;
9
9
  };
@@ -3,7 +3,7 @@ import type { Entity } from 'backlog-js';
3
3
  import { Backlog } from 'backlog-js';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const deleteVersionSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const deleteVersionSchema: (t: DescriptionHelper['t']) => {
7
7
  projectId: z.ZodOptional<z.ZodNumber>;
8
8
  projectKey: z.ZodOptional<z.ZodString>;
9
9
  id: z.ZodNumber;
@@ -3,7 +3,7 @@ import type { Entity } from 'backlog-js';
3
3
  import { Backlog } from 'backlog-js';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const deleteWatchingSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const deleteWatchingSchema: (t: DescriptionHelper['t']) => {
7
7
  watchId: z.ZodNumber;
8
8
  };
9
9
  export declare const deleteWatchingTool: (backlog: Backlog, { t }: DescriptionHelper) => ToolDefinition<ReturnType<typeof deleteWatchingSchema>, Entity.WatchingList.WatchingListItem>;
@@ -3,7 +3,7 @@ import type { Entity } from 'backlog-js';
3
3
  import { Backlog } from 'backlog-js';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const getCategoriesSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const getCategoriesSchema: (t: DescriptionHelper['t']) => {
7
7
  projectId: z.ZodOptional<z.ZodNumber>;
8
8
  projectKey: z.ZodOptional<z.ZodString>;
9
9
  };
@@ -3,7 +3,7 @@ import { Backlog } from 'backlog-js';
3
3
  import { z } from 'zod';
4
4
  import { ToolDefinition } from '../types/tool.js';
5
5
  import { DescriptionHelper } from '../createDescriptionHelper.js';
6
- declare const getCustomFieldsInputSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const getCustomFieldsInputSchema: (t: DescriptionHelper['t']) => {
7
7
  projectId: z.ZodOptional<z.ZodNumber>;
8
8
  projectKey: z.ZodOptional<z.ZodString>;
9
9
  };
@@ -3,7 +3,7 @@ import { Backlog } from 'backlog-js';
3
3
  import { z } from 'zod';
4
4
  import { DescriptionHelper } from '../createDescriptionHelper.js';
5
5
  import { ToolDefinition } from '../types/tool.js';
6
- declare const getDocumentSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const getDocumentSchema: (t: DescriptionHelper['t']) => {
7
7
  documentId: z.ZodString;
8
8
  };
9
9
  export declare const getDocumentTool: (backlog: Backlog, { t }: DescriptionHelper) => ToolDefinition<ReturnType<typeof getDocumentSchema>, Entity.Document.Document>;
@@ -26,6 +26,7 @@ export const getDocumentTool = (backlog, { t }) => {
26
26
  'created',
27
27
  'updatedUser',
28
28
  'updated',
29
+ 'childDocumentIds',
29
30
  ]),
30
31
  importantFields: ['id', 'title', 'createdUser'],
31
32
  handler: async ({ documentId }) => {
@@ -3,7 +3,7 @@ import { Backlog } from 'backlog-js';
3
3
  import { z } from 'zod';
4
4
  import { DescriptionHelper } from '../createDescriptionHelper.js';
5
5
  import { ToolDefinition } from '../types/tool.js';
6
- declare const getDocumentTreeSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const getDocumentTreeSchema: (t: DescriptionHelper['t']) => {
7
7
  projectIdOrKey: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
8
8
  };
9
9
  export declare const getDocumentTreeTool: (backlog: Backlog, { t }: DescriptionHelper) => ToolDefinition<ReturnType<typeof getDocumentTreeSchema>, Entity.Document.DocumentTree>;
@@ -3,7 +3,7 @@ import { Backlog } from 'backlog-js';
3
3
  import { z } from 'zod';
4
4
  import { DescriptionHelper } from '../createDescriptionHelper.js';
5
5
  import { ToolDefinition } from '../types/tool.js';
6
- declare const getDocumentsSchema: (t: DescriptionHelper["t"]) => {
6
+ declare const getDocumentsSchema: (t: DescriptionHelper['t']) => {
7
7
  projectIds: z.ZodArray<z.ZodNumber>;
8
8
  offset: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
9
9
  };
@@ -31,6 +31,7 @@ export const getDocumentsTool = (backlog, { t }) => {
31
31
  'created',
32
32
  'updatedUser',
33
33
  'updated',
34
+ 'childDocumentIds',
34
35
  ]),
35
36
  importantFields: ['id', 'projectId', 'title', 'plain'],
36
37
  handler: async ({ projectIds, offset }) => {