#
tokens: 32986/50000 15/164 files (page 3/3)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 3 of 3. Use http://codebase.md/nulab/backlog-mcp-server?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .clineigonre
├── .clinerules
│   └── commit-conventional-format.md
├── .env.example
├── .github
│   └── workflows
│       ├── ci.yml
│       └── release.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .release-it.json
├── .tool-versions
├── CHANGELOG.md
├── Dockerfile
├── eslint.config.js
├── jest.config.js
├── LICENSE
├── memory-bank
│   ├── activeContext.md
│   ├── productContext.md
│   ├── progress.md
│   ├── projectbrief.md
│   ├── systemPatterns.md
│   ├── techContext.md
│   └── URLlist.md
├── package-lock.json
├── package.json
├── README.ja.md
├── README.md
├── scripts
│   └── replace-version.js
├── src
│   ├── backlog
│   │   ├── backlogErrorHandler.ts
│   │   ├── customFields.test.ts
│   │   ├── customFields.ts
│   │   └── parseBacklogAPIError.ts
│   ├── createTranslationHelper.test.ts
│   ├── createTranslationHelper.ts
│   ├── handlers
│   │   ├── builders
│   │   │   ├── composeToolHandler.test.ts
│   │   │   └── composeToolHandler.ts
│   │   └── transformers
│   │       ├── wrapWithErrorHandling.test.ts
│   │       ├── wrapWithErrorHandling.ts
│   │       ├── wrapWithFieldPicking.test.ts
│   │       ├── wrapWithFieldPicking.ts
│   │       ├── wrapWithTokenLimit.test.ts
│   │       ├── wrapWithTokenLimit.ts
│   │       ├── wrapWithToolResult.test.ts
│   │       └── wrapWithToolResult.ts
│   ├── index.ts
│   ├── registerTools.test.ts
│   ├── registerTools.ts
│   ├── tools
│   │   ├── addIssue.test.ts
│   │   ├── addIssue.ts
│   │   ├── addIssueComment.test.ts
│   │   ├── addIssueComment.ts
│   │   ├── addProject.test.ts
│   │   ├── addProject.ts
│   │   ├── addPullRequest.test.ts
│   │   ├── addPullRequest.ts
│   │   ├── addPullRequestComment.test.ts
│   │   ├── addPullRequestComment.ts
│   │   ├── addVersionMilestone.test.ts
│   │   ├── addVersionMilestone.ts
│   │   ├── addWiki.test.ts
│   │   ├── addWiki.ts
│   │   ├── countIssues.test.ts
│   │   ├── countIssues.ts
│   │   ├── deleteIssue.test.ts
│   │   ├── deleteIssue.ts
│   │   ├── deleteProject.test.ts
│   │   ├── deleteProject.ts
│   │   ├── deleteVersion.test.ts
│   │   ├── deleteVersion.ts
│   │   ├── dynamicTools
│   │   │   ├── toolsets.test.ts
│   │   │   └── toolsets.ts
│   │   ├── getCategories.test.ts
│   │   ├── getCategories.ts
│   │   ├── getCustomFields.test.ts
│   │   ├── getCustomFields.ts
│   │   ├── getDocument.test.ts
│   │   ├── getDocument.ts
│   │   ├── getDocuments.test.ts
│   │   ├── getDocuments.ts
│   │   ├── getDocumentTree.test.ts
│   │   ├── getDocumentTree.ts
│   │   ├── getGitRepositories.test.ts
│   │   ├── getGitRepositories.ts
│   │   ├── getGitRepository.test.ts
│   │   ├── getGitRepository.ts
│   │   ├── getIssue.test.ts
│   │   ├── getIssue.ts
│   │   ├── getIssueComments.test.ts
│   │   ├── getIssueComments.ts
│   │   ├── getIssues.test.ts
│   │   ├── getIssues.ts
│   │   ├── getIssueTypes.test.ts
│   │   ├── getIssueTypes.ts
│   │   ├── getMyself.test.ts
│   │   ├── getMyself.ts
│   │   ├── getNotifications.test.ts
│   │   ├── getNotifications.ts
│   │   ├── getNotificationsCount.test.ts
│   │   ├── getNotificationsCount.ts
│   │   ├── getPriorities.test.ts
│   │   ├── getPriorities.ts
│   │   ├── getProject.test.ts
│   │   ├── getProject.ts
│   │   ├── getProjectList.test.ts
│   │   ├── getProjectList.ts
│   │   ├── getPullRequest.test.ts
│   │   ├── getPullRequest.ts
│   │   ├── getPullRequestComments.test.ts
│   │   ├── getPullRequestComments.ts
│   │   ├── getPullRequests.test.ts
│   │   ├── getPullRequests.ts
│   │   ├── getPullRequestsCount.test.ts
│   │   ├── getPullRequestsCount.ts
│   │   ├── getResolutions.test.ts
│   │   ├── getResolutions.ts
│   │   ├── getSpace.test.ts
│   │   ├── getSpace.ts
│   │   ├── getUsers.test.ts
│   │   ├── getUsers.ts
│   │   ├── getVersionMilestoneList.test.ts
│   │   ├── getVersionMilestoneList.ts
│   │   ├── getWatchingListCount.test.ts
│   │   ├── getWatchingListCount.ts
│   │   ├── getWatchingListItems.test.ts
│   │   ├── getWatchingListItems.ts
│   │   ├── getWiki.test.ts
│   │   ├── getWiki.ts
│   │   ├── getWikiPages.test.ts
│   │   ├── getWikiPages.ts
│   │   ├── getWikisCount.test.ts
│   │   ├── getWikisCount.ts
│   │   ├── markNotificationAsRead.test.ts
│   │   ├── markNotificationAsRead.ts
│   │   ├── resetUnreadNotificationCount.test.ts
│   │   ├── resetUnreadNotificationCount.ts
│   │   ├── tools.ts
│   │   ├── updateIssue.test.ts
│   │   ├── updateIssue.ts
│   │   ├── updateProject.test.ts
│   │   ├── updateProject.ts
│   │   ├── updatePullRequest.test.ts
│   │   ├── updatePullRequest.ts
│   │   ├── updatePullRequestComment.test.ts
│   │   ├── updatePullRequestComment.ts
│   │   ├── updateVersionMilestone.test.ts
│   │   └── updateVersionMilestone.ts
│   ├── types
│   │   ├── mcp.ts
│   │   ├── result.ts
│   │   ├── tool.ts
│   │   ├── toolsets.ts
│   │   └── zod
│   │       └── backlogOutputDefinition.ts
│   ├── utils
│   │   ├── generateFieldsDescription.test.ts
│   │   ├── generateFieldsDescription.ts
│   │   ├── logger.ts
│   │   ├── resolveIdOrKey.test.ts
│   │   ├── resolveIdOrKey.ts
│   │   ├── runToolSafely.test.ts
│   │   ├── runToolSafely.ts
│   │   ├── tokenCounter.test.ts
│   │   ├── tokenCounter.ts
│   │   ├── toolRegistrar.test.ts
│   │   ├── toolRegistrar.ts
│   │   ├── toolsetUtils.test.ts
│   │   ├── toolsetUtils.ts
│   │   ├── wrapServerWithToolRegistry.test.ts
│   │   └── wrapServerWithToolRegistry.ts
│   └── version.template.ts
├── translationConfig
│   └── .backlog-mcp-serverrc.json.example
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/src/tools/updateIssue.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { updateIssueTool } from './updateIssue.js';
  2 | import { jest, describe, it, expect } from '@jest/globals';
  3 | import type { Backlog } from 'backlog-js';
  4 | import { createTranslationHelper } from '../createTranslationHelper.js';
  5 | 
  6 | describe('updateIssueTool', () => {
  7 |   const mockBacklog: Partial<Backlog> = {
  8 |     patchIssue: jest.fn<() => Promise<any>>().mockResolvedValue({
  9 |       id: 1,
 10 |       projectId: 100,
 11 |       issueKey: 'TEST-1',
 12 |       keyId: 1,
 13 |       issueType: {
 14 |         id: 2,
 15 |         projectId: 100,
 16 |         name: 'Bug',
 17 |         color: '#990000',
 18 |         displayOrder: 0,
 19 |       },
 20 |       summary: 'Updated Issue',
 21 |       description: 'This is an updated issue',
 22 |       priority: {
 23 |         id: 2,
 24 |         name: 'High',
 25 |       },
 26 |       status: {
 27 |         id: 2,
 28 |         name: 'In Progress',
 29 |         projectId: 100,
 30 |         color: '#ff9900',
 31 |         displayOrder: 1,
 32 |       },
 33 |       assignee: {
 34 |         id: 5,
 35 |         userId: 'user',
 36 |         name: 'Test User',
 37 |         roleType: 1,
 38 |         lang: 'en',
 39 |         mailAddress: '[email protected]',
 40 |         lastLoginTime: '2023-01-01T00:00:00Z',
 41 |       },
 42 |       startDate: '2023-01-01',
 43 |       dueDate: '2023-01-31',
 44 |       estimatedHours: 15,
 45 |       actualHours: 8,
 46 |       createdUser: {
 47 |         id: 1,
 48 |         userId: 'admin',
 49 |         name: 'Admin User',
 50 |         roleType: 1,
 51 |         lang: 'en',
 52 |         mailAddress: '[email protected]',
 53 |         lastLoginTime: '2023-01-01T00:00:00Z',
 54 |       },
 55 |       created: '2023-01-01T00:00:00Z',
 56 |       updatedUser: {
 57 |         id: 1,
 58 |         userId: 'admin',
 59 |         name: 'Admin User',
 60 |         roleType: 1,
 61 |         lang: 'en',
 62 |         mailAddress: '[email protected]',
 63 |         lastLoginTime: '2023-01-01T00:00:00Z',
 64 |       },
 65 |       updated: '2023-01-02T00:00:00Z',
 66 |     }),
 67 |   };
 68 | 
 69 |   const mockTranslationHelper = createTranslationHelper();
 70 |   const tool = updateIssueTool(mockBacklog as Backlog, mockTranslationHelper);
 71 | 
 72 |   it('returns updated issue', async () => {
 73 |     const result = await tool.handler({
 74 |       issueKey: 'TEST-1',
 75 |       summary: 'Updated Issue',
 76 |       description: 'This is an updated issue',
 77 |     });
 78 | 
 79 |     expect(result).toHaveProperty('summary', 'Updated Issue');
 80 |     expect(result).toHaveProperty('description', 'This is an updated issue');
 81 |     expect(result).toHaveProperty('issueKey', 'TEST-1');
 82 |   });
 83 | 
 84 |   it('calls backlog.patchIssue with correct params when using issue key', async () => {
 85 |     await tool.handler({
 86 |       issueKey: 'TEST-1',
 87 |       summary: 'Updated Issue',
 88 |       priorityId: 2,
 89 |       statusId: 2,
 90 |     });
 91 | 
 92 |     expect(mockBacklog.patchIssue).toHaveBeenCalledWith('TEST-1', {
 93 |       priorityId: 2,
 94 |       statusId: 2,
 95 |       summary: 'Updated Issue',
 96 |     });
 97 |   });
 98 | 
 99 |   it('calls backlog.patchIssue with correct params when using issue ID', async () => {
100 |     await tool.handler({
101 |       issueId: 1,
102 |       estimatedHours: 15,
103 |       actualHours: 8,
104 |       comment: 'Updated the estimated and actual hours',
105 |     });
106 | 
107 |     expect(mockBacklog.patchIssue).toHaveBeenCalledWith(1, {
108 |       // Expect number
109 |       estimatedHours: 15,
110 |       actualHours: 8,
111 |       comment: 'Updated the estimated and actual hours',
112 |     });
113 |   });
114 | 
115 |   it('throws an error if neither issueId nor issueKey is provided', async () => {
116 |     await expect(tool.handler({})).rejects.toThrow(Error);
117 |   });
118 | 
119 |   it('transforms customFields to proper customField_{id} format', async () => {
120 |     await tool.handler({
121 |       issueKey: 'TEST-1',
122 |       summary: 'Custom Field Test',
123 |       issueTypeId: 2,
124 |       priorityId: 3,
125 |       customFields: [
126 |         { id: 123, value: 'テキスト' },
127 |         { id: 456, value: 42 },
128 |         { id: 789, value: ['OptionA', 'OptionB'], otherValue: '詳細説明' },
129 |       ],
130 |     });
131 | 
132 |     expect(mockBacklog.patchIssue).toHaveBeenCalledWith(
133 |       'TEST-1',
134 |       expect.objectContaining({
135 |         summary: 'Custom Field Test',
136 |         issueTypeId: 2,
137 |         priorityId: 3,
138 |         customField_123: 'テキスト',
139 |         customField_456: 42,
140 |         customField_789: ['OptionA', 'OptionB'],
141 |         customField_789_otherValue: '詳細説明',
142 |       })
143 |     );
144 |   });
145 | });
146 | 
```

--------------------------------------------------------------------------------
/src/tools/countIssues.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { z } from 'zod';
  2 | import { Backlog } from 'backlog-js';
  3 | import { buildToolSchema, ToolDefinition } from '../types/tool.js';
  4 | import { TranslationHelper } from '../createTranslationHelper.js';
  5 | import { IssueCountSchema } from '../types/zod/backlogOutputDefinition.js';
  6 | import { customFieldsToPayload } from '../backlog/customFields.js';
  7 | 
  8 | const countIssuesSchema = buildToolSchema((t) => ({
  9 |   projectId: z
 10 |     .array(z.number())
 11 |     .optional()
 12 |     .describe(t('TOOL_COUNT_ISSUES_PROJECT_ID', 'Project IDs')),
 13 |   issueTypeId: z
 14 |     .array(z.number())
 15 |     .optional()
 16 |     .describe(t('TOOL_COUNT_ISSUES_ISSUE_TYPE_ID', 'Issue type IDs')),
 17 |   categoryId: z
 18 |     .array(z.number())
 19 |     .optional()
 20 |     .describe(t('TOOL_COUNT_ISSUES_CATEGORY_ID', 'Category IDs')),
 21 |   versionId: z
 22 |     .array(z.number())
 23 |     .optional()
 24 |     .describe(t('TOOL_COUNT_ISSUES_VERSION_ID', 'Version IDs')),
 25 |   milestoneId: z
 26 |     .array(z.number())
 27 |     .optional()
 28 |     .describe(t('TOOL_COUNT_ISSUES_MILESTONE_ID', 'Milestone IDs')),
 29 |   statusId: z
 30 |     .array(z.number())
 31 |     .optional()
 32 |     .describe(t('TOOL_COUNT_ISSUES_STATUS_ID', 'Status IDs')),
 33 |   priorityId: z
 34 |     .array(z.number())
 35 |     .optional()
 36 |     .describe(t('TOOL_COUNT_ISSUES_PRIORITY_ID', 'Priority IDs')),
 37 |   assigneeId: z
 38 |     .array(z.number())
 39 |     .optional()
 40 |     .describe(t('TOOL_COUNT_ISSUES_ASSIGNEE_ID', 'Assignee user IDs')),
 41 |   createdUserId: z
 42 |     .array(z.number())
 43 |     .optional()
 44 |     .describe(t('TOOL_COUNT_ISSUES_CREATED_USER_ID', 'Created user IDs')),
 45 |   resolutionId: z
 46 |     .array(z.number())
 47 |     .optional()
 48 |     .describe(t('TOOL_COUNT_ISSUES_RESOLUTION_ID', 'Resolution IDs')),
 49 |   parentIssueId: z
 50 |     .array(z.number())
 51 |     .optional()
 52 |     .describe(t('TOOL_COUNT_ISSUES_PARENT_ISSUE_ID', 'Parent issue IDs')),
 53 |   keyword: z
 54 |     .string()
 55 |     .optional()
 56 |     .describe(
 57 |       t('TOOL_COUNT_ISSUES_KEYWORD', 'Keyword to search for in issues')
 58 |     ),
 59 |   startDateSince: z
 60 |     .string()
 61 |     .optional()
 62 |     .describe(
 63 |       t('TOOL_COUNT_ISSUES_START_DATE_SINCE', 'Start date since (yyyy-MM-dd)')
 64 |     ),
 65 |   startDateUntil: z
 66 |     .string()
 67 |     .optional()
 68 |     .describe(
 69 |       t('TOOL_COUNT_ISSUES_START_DATE_UNTIL', 'Start date until (yyyy-MM-dd)')
 70 |     ),
 71 |   dueDateSince: z
 72 |     .string()
 73 |     .optional()
 74 |     .describe(
 75 |       t('TOOL_COUNT_ISSUES_DUE_DATE_SINCE', 'Due date since (yyyy-MM-dd)')
 76 |     ),
 77 |   dueDateUntil: z
 78 |     .string()
 79 |     .optional()
 80 |     .describe(
 81 |       t('TOOL_COUNT_ISSUES_DUE_DATE_UNTIL', 'Due date until (yyyy-MM-dd)')
 82 |     ),
 83 |   createdSince: z
 84 |     .string()
 85 |     .optional()
 86 |     .describe(
 87 |       t('TOOL_COUNT_ISSUES_CREATED_SINCE', 'Created since (yyyy-MM-dd)')
 88 |     ),
 89 |   createdUntil: z
 90 |     .string()
 91 |     .optional()
 92 |     .describe(
 93 |       t('TOOL_COUNT_ISSUES_CREATED_UNTIL', 'Created until (yyyy-MM-dd)')
 94 |     ),
 95 |   updatedSince: z
 96 |     .string()
 97 |     .optional()
 98 |     .describe(
 99 |       t('TOOL_COUNT_ISSUES_UPDATED_SINCE', 'Updated since (yyyy-MM-dd)')
100 |     ),
101 |   updatedUntil: z
102 |     .string()
103 |     .optional()
104 |     .describe(
105 |       t('TOOL_COUNT_ISSUES_UPDATED_UNTIL', 'Updated until (yyyy-MM-dd)')
106 |     ),
107 |   customFields: z
108 |     .array(
109 |       z.object({
110 |         id: z
111 |           .number()
112 |           .describe(t('TOOL_COUNT_ISSUES_CUSTOM_FIELD_ID', 'Custom field ID')),
113 |         value: z
114 |           .union([z.string(), z.number(), z.array(z.string())])
115 |           .describe(
116 |             t('TOOL_COUNT_ISSUES_CUSTOM_FIELD_VALUE', 'Custom field value')
117 |           ),
118 |       })
119 |     )
120 |     .optional()
121 |     .describe(t('TOOL_COUNT_ISSUES_CUSTOM_FIELDS', 'Custom fields')),
122 | }));
123 | 
124 | export const countIssuesTool = (
125 |   backlog: Backlog,
126 |   { t }: TranslationHelper
127 | ): ToolDefinition<
128 |   ReturnType<typeof countIssuesSchema>,
129 |   (typeof IssueCountSchema)['shape']
130 | > => {
131 |   return {
132 |     name: 'count_issues',
133 |     description: t('TOOL_COUNT_ISSUES_DESCRIPTION', 'Returns count of issues'),
134 |     schema: z.object(countIssuesSchema(t)),
135 |     outputSchema: IssueCountSchema,
136 |     handler: async ({ customFields, ...rest }) => {
137 |       return backlog.getIssuesCount({
138 |         ...rest,
139 |         ...customFieldsToPayload(customFields),
140 |       });
141 |     },
142 |   };
143 | };
144 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | // Copyright (c) 2025 Nulab inc.
  3 | // Licensed under the MIT License.
  4 | 
  5 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
  6 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
  7 | import * as backlogjs from 'backlog-js';
  8 | import dotenv from 'dotenv';
  9 | import { default as env } from 'env-var';
 10 | import yargs from 'yargs';
 11 | import { hideBin } from 'yargs/helpers';
 12 | import { createTranslationHelper } from './createTranslationHelper.js';
 13 | import { registerDyamicTools, registerTools } from './registerTools.js';
 14 | import { dynamicTools } from './tools/dynamicTools/toolsets.js';
 15 | import { logger } from './utils/logger.js';
 16 | import { createToolRegistrar } from './utils/toolRegistrar.js';
 17 | import { buildToolsetGroup } from './utils/toolsetUtils.js';
 18 | import { wrapServerWithToolRegistry } from './utils/wrapServerWithToolRegistry.js';
 19 | import { VERSION } from './version.js';
 20 | 
 21 | dotenv.config();
 22 | 
 23 | const domain = env.get('BACKLOG_DOMAIN').required().asString();
 24 | 
 25 | const apiKey = env.get('BACKLOG_API_KEY').required().asString();
 26 | 
 27 | const backlog = new backlogjs.Backlog({ host: domain, apiKey: apiKey });
 28 | 
 29 | const argv = yargs(hideBin(process.argv))
 30 |   .option('max-tokens', {
 31 |     type: 'number',
 32 |     describe: 'Maximum number of tokens allowed in the response',
 33 |     default: env.get('MAX_TOKENS').default('50000').asIntPositive(),
 34 |   })
 35 |   .option('optimize-response', {
 36 |     type: 'boolean',
 37 |     describe:
 38 |       'Enable GraphQL-style response optimization to include only requested fields',
 39 |     default: env.get('OPTIMIZE_RESPONSE').default('false').asBool(),
 40 |   })
 41 |   .option('prefix', {
 42 |     type: 'string',
 43 |     describe: 'Optional string prefix to prepend to all generated outputs',
 44 |     default: env.get('PREFIX').default('').asString(),
 45 |   })
 46 |   .option('export-translations', {
 47 |     type: 'boolean',
 48 |     describe: 'Export translations and exit',
 49 |     default: false,
 50 |   })
 51 |   .option('enable-toolsets', {
 52 |     type: 'array',
 53 |     describe: `Specify which toolsets to enable. Defaults to 'all'.
 54 | Available toolsets:
 55 |   - space:       Tools for managing Backlog space settings and general information
 56 |   - project:     Tools for managing projects, categories, custom fields, and issue types
 57 |   - issue:       Tools for managing issues and their comments
 58 |   - wiki:        Tools for managing wiki pages
 59 |   - git:         Tools for managing Git repositories and pull requests
 60 |   - notifications: Tools for managing user notifications`,
 61 |     default: env.get('ENABLE_TOOLSETS').default('all').asArray(','),
 62 |   })
 63 |   .option('dynamic-toolsets', {
 64 |     type: 'boolean',
 65 |     describe:
 66 |       'Enable dynamic toolsets such as enable_toolset, list_available_toolsets, etc.',
 67 |     default: env.get('ENABLE_DYNAMIC_TOOLSETS').default('false').asBool(),
 68 |   })
 69 |   .parseSync();
 70 | 
 71 | const useFields = argv.optimizeResponse;
 72 | 
 73 | const server = wrapServerWithToolRegistry(
 74 |   new McpServer({
 75 |     name: 'backlog',
 76 |     description: useFields
 77 |       ? `You can include only the fields you need using GraphQL-style syntax.
 78 | Start with the example above and customize freely.`
 79 |       : undefined,
 80 |     version: VERSION,
 81 |   })
 82 | );
 83 | 
 84 | const transHelper = createTranslationHelper();
 85 | 
 86 | const maxTokens = argv.maxTokens;
 87 | const prefix = argv.prefix;
 88 | let enabledToolsets = argv.enableToolsets as string[];
 89 | 
 90 | // If dynamic toolsets are enabled, remove "all" to allow for selective enabling via commands
 91 | if (argv.dynamicToolsets) {
 92 |   enabledToolsets = enabledToolsets.filter((a) => a != 'all');
 93 | }
 94 | 
 95 | const mcpOption = { useFields: useFields, maxTokens, prefix };
 96 | const toolsetGroup = buildToolsetGroup(backlog, transHelper, enabledToolsets);
 97 | 
 98 | // Register all tools
 99 | registerTools(server, toolsetGroup, mcpOption);
100 | 
101 | // Register dynamic tool management tools if enabled
102 | if (argv.dynamicToolsets) {
103 |   const registrar = createToolRegistrar(server, toolsetGroup, mcpOption);
104 |   const dynamicToolsetGroup = dynamicTools(
105 |     registrar,
106 |     transHelper,
107 |     toolsetGroup
108 |   );
109 | 
110 |   registerDyamicTools(server, dynamicToolsetGroup, prefix);
111 | }
112 | 
113 | if (argv.exportTranslations) {
114 |   const data = transHelper.dump();
115 |   // eslint-disable-next-line no-console
116 |   console.log(JSON.stringify(data, null, 2));
117 |   process.exit(0);
118 | }
119 | 
120 | async function main() {
121 |   const transport = new StdioServerTransport();
122 |   await server.connect(transport);
123 |   logger.info('Backlog MCP Server running on stdio');
124 | }
125 | 
126 | main().catch((error) => {
127 |   logger.error({ err: error }, 'Fatal error in main()');
128 |   process.exit(1);
129 | });
130 | 
```

--------------------------------------------------------------------------------
/src/tools/updateIssue.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { z } from 'zod';
  2 | import { Backlog } from 'backlog-js';
  3 | import { buildToolSchema, ToolDefinition } from '../types/tool.js';
  4 | import { TranslationHelper } from '../createTranslationHelper.js';
  5 | import { IssueSchema } from '../types/zod/backlogOutputDefinition.js';
  6 | import { resolveIdOrKey } from '../utils/resolveIdOrKey.js';
  7 | import { customFieldsToPayload } from '../backlog/customFields.js';
  8 | 
  9 | const updateIssueSchema = buildToolSchema((t) => ({
 10 |   issueId: z
 11 |     .number()
 12 |     .optional()
 13 |     .describe(
 14 |       t(
 15 |         'TOOL_UPDATE_ISSUE_ISSUE_ID',
 16 |         'The numeric ID of the issue (e.g., 12345)'
 17 |       )
 18 |     ),
 19 |   issueKey: z
 20 |     .string()
 21 |     .optional()
 22 |     .describe(
 23 |       t(
 24 |         'TOOL_UPDATE_ISSUE_ISSUE_KEY',
 25 |         "The key of the issue (e.g., 'PROJ-123')"
 26 |       )
 27 |     ),
 28 |   summary: z
 29 |     .string()
 30 |     .optional()
 31 |     .describe(t('TOOL_UPDATE_ISSUE_SUMMARY', 'Summary of the issue')),
 32 |   issueTypeId: z
 33 |     .number()
 34 |     .optional()
 35 |     .describe(t('TOOL_UPDATE_ISSUE_ISSUE_TYPE_ID', 'Issue type ID')),
 36 |   priorityId: z
 37 |     .number()
 38 |     .optional()
 39 |     .describe(t('TOOL_UPDATE_ISSUE_PRIORITY_ID', 'Priority ID')),
 40 |   description: z
 41 |     .string()
 42 |     .optional()
 43 |     .describe(
 44 |       t('TOOL_UPDATE_ISSUE_DESCRIPTION', 'Detailed description of the issue')
 45 |     ),
 46 |   startDate: z
 47 |     .string()
 48 |     .optional()
 49 |     .describe(
 50 |       t('TOOL_UPDATE_ISSUE_START_DATE', 'Scheduled start date (yyyy-MM-dd)')
 51 |     ),
 52 |   dueDate: z
 53 |     .string()
 54 |     .optional()
 55 |     .describe(
 56 |       t('TOOL_UPDATE_ISSUE_DUE_DATE', 'Scheduled due date (yyyy-MM-dd)')
 57 |     ),
 58 |   estimatedHours: z
 59 |     .number()
 60 |     .optional()
 61 |     .describe(t('TOOL_UPDATE_ISSUE_ESTIMATED_HOURS', 'Estimated work hours')),
 62 |   actualHours: z
 63 |     .number()
 64 |     .optional()
 65 |     .describe(t('TOOL_UPDATE_ISSUE_ACTUAL_HOURS', 'Actual work hours')),
 66 |   categoryId: z
 67 |     .array(z.number())
 68 |     .optional()
 69 |     .describe(t('TOOL_UPDATE_ISSUE_CATEGORY_ID', 'Category IDs')),
 70 |   versionId: z
 71 |     .array(z.number())
 72 |     .optional()
 73 |     .describe(t('TOOL_UPDATE_ISSUE_VERSION_ID', 'Version IDs')),
 74 |   milestoneId: z
 75 |     .array(z.number())
 76 |     .optional()
 77 |     .describe(t('TOOL_UPDATE_ISSUE_MILESTONE_ID', 'Milestone IDs')),
 78 |   statusId: z
 79 |     .number()
 80 |     .optional()
 81 |     .describe(t('TOOL_UPDATE_ISSUE_STATUS_ID', 'Status ID')),
 82 |   resolutionId: z
 83 |     .number()
 84 |     .optional()
 85 |     .describe(t('TOOL_UPDATE_ISSUE_RESOLUTION_ID', 'Resolution ID')),
 86 |   assigneeId: z
 87 |     .number()
 88 |     .optional()
 89 |     .describe(t('TOOL_UPDATE_ISSUE_ASSIGNEE_ID', 'User ID of the assignee')),
 90 |   notifiedUserId: z
 91 |     .array(z.number())
 92 |     .optional()
 93 |     .describe(t('TOOL_UPDATE_ISSUE_NOTIFIED_USER_ID', 'User IDs to notify')),
 94 |   attachmentId: z
 95 |     .array(z.number())
 96 |     .optional()
 97 |     .describe(t('TOOL_UPDATE_ISSUE_ATTACHMENT_ID', 'Attachment IDs')),
 98 |   comment: z
 99 |     .string()
100 |     .optional()
101 |     .describe(
102 |       t('TOOL_UPDATE_ISSUE_COMMENT', 'Comment to add when updating the issue')
103 |     ),
104 |   customFields: z
105 |     .array(
106 |       z.object({
107 |         id: z
108 |           .number()
109 |           .describe(
110 |             t(
111 |               'TOOL_UPDATE_ISSUE_CUSTOM_FIELD_ID',
112 |               'The ID of the custom field (e.g., 12345)'
113 |             )
114 |           ),
115 |         value: z.union([z.string().max(255), z.number(), z.array(z.string())]),
116 |         otherValue: z
117 |           .string()
118 |           .optional()
119 |           .describe(
120 |             t(
121 |               'TOOL_UPDATE_ISSUE_CUSTOM_FIELD_OTHER_VALUE',
122 |               'Other value for list type fields'
123 |             )
124 |           ),
125 |       })
126 |     )
127 |     .optional()
128 |     .describe(
129 |       t(
130 |         'TOOL_UPDATE_ISSUE_CUSTOM_FIELDS',
131 |         'List of custom fields to set on the issue'
132 |       )
133 |     ),
134 | }));
135 | 
136 | export const updateIssueTool = (
137 |   backlog: Backlog,
138 |   { t }: TranslationHelper
139 | ): ToolDefinition<
140 |   ReturnType<typeof updateIssueSchema>,
141 |   (typeof IssueSchema)['shape']
142 | > => {
143 |   return {
144 |     name: 'update_issue',
145 |     description: t(
146 |       'TOOL_UPDATE_ISSUE_DESCRIPTION',
147 |       'Updates an existing issue'
148 |     ),
149 |     schema: z.object(updateIssueSchema(t)),
150 |     outputSchema: IssueSchema,
151 |     handler: async ({ issueId, issueKey, customFields, ...params }) => {
152 |       const result = resolveIdOrKey('issue', { id: issueId, key: issueKey }, t);
153 |       if (!result.ok) {
154 |         throw result.error;
155 |       }
156 |       const customFieldPayload = customFieldsToPayload(customFields);
157 | 
158 |       const finalPayload = {
159 |         ...params,
160 |         ...customFieldPayload,
161 |       };
162 |       return backlog.patchIssue(result.value, finalPayload);
163 |     },
164 |   };
165 | };
166 | 
```

--------------------------------------------------------------------------------
/src/tools/updatePullRequest.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { updatePullRequestTool } from './updatePullRequest.js';
  2 | import { jest, describe, it, expect } from '@jest/globals';
  3 | import type { Backlog } from 'backlog-js';
  4 | import { createTranslationHelper } from '../createTranslationHelper.js';
  5 | 
  6 | describe('updatePullRequestTool', () => {
  7 |   const mockBacklog: Partial<Backlog> = {
  8 |     patchPullRequest: jest.fn<() => Promise<any>>().mockResolvedValue({
  9 |       id: 1,
 10 |       projectId: 100,
 11 |       repositoryId: 200,
 12 |       number: 1,
 13 |       summary: 'Updated PR title',
 14 |       description: 'Updated PR description',
 15 |       base: 'main',
 16 |       branch: 'fix/login-bug',
 17 |       status: {
 18 |         id: 2,
 19 |         name: 'Closed',
 20 |       },
 21 |       assignee: {
 22 |         id: 2,
 23 |         userId: 'user2',
 24 |         name: 'User Two',
 25 |       },
 26 |       issue: {
 27 |         id: 1001,
 28 |         issueKey: 'TEST-2',
 29 |         summary: 'Another issue',
 30 |       },
 31 |       baseCommit: 'abc123',
 32 |       branchCommit: 'def456',
 33 |       closeAt: '2023-01-02T00:00:00Z',
 34 |       mergeAt: '2023-01-02T00:00:00Z',
 35 |       createdUser: {
 36 |         id: 1,
 37 |         userId: 'user1',
 38 |         name: 'User One',
 39 |       },
 40 |       created: '2023-01-01T00:00:00Z',
 41 |       updatedUser: {
 42 |         id: 2,
 43 |         userId: 'user2',
 44 |         name: 'User Two',
 45 |       },
 46 |       updated: '2023-01-02T00:00:00Z',
 47 |     }),
 48 |   };
 49 | 
 50 |   const mockTranslationHelper = createTranslationHelper();
 51 |   const tool = updatePullRequestTool(
 52 |     mockBacklog as Backlog,
 53 |     mockTranslationHelper
 54 |   );
 55 | 
 56 |   it('returns updated pull request', async () => {
 57 |     const result = await tool.handler({
 58 |       projectKey: 'TEST',
 59 |       repoName: 'test-repo', // Changed
 60 |       number: 1,
 61 |       summary: 'Updated PR title',
 62 |       description: 'Updated PR description',
 63 |       statusId: 2,
 64 |     });
 65 | 
 66 |     if (Array.isArray(result)) {
 67 |       throw new Error('Unexpected array result');
 68 |     }
 69 | 
 70 |     expect(result).toHaveProperty('summary', 'Updated PR title');
 71 |     expect(result).toHaveProperty('description', 'Updated PR description');
 72 |     expect(result.status).toHaveProperty('name', 'Closed');
 73 |   });
 74 | 
 75 |   it('calls backlog.patchPullRequest with correct params when using repoName', async () => {
 76 |     const params = {
 77 |       projectKey: 'TEST',
 78 |       repoName: 'test-repo', // Changed
 79 |       number: 1,
 80 |       summary: 'Updated PR title',
 81 |       description: 'Updated PR description',
 82 |       issueId: 1001,
 83 |       assigneeId: 2,
 84 |       statusId: 2,
 85 |     };
 86 | 
 87 |     await tool.handler(params);
 88 | 
 89 |     expect(mockBacklog.patchPullRequest).toHaveBeenCalledWith(
 90 |       'TEST',
 91 |       'test-repo',
 92 |       1,
 93 |       {
 94 |         summary: 'Updated PR title',
 95 |         description: 'Updated PR description',
 96 |         issueId: 1001,
 97 |         assigneeId: 2,
 98 |         statusId: 2,
 99 |       }
100 |     );
101 |   });
102 | 
103 |   it('calls backlog.patchPullRequest with correct params when using projectId and repoName', async () => {
104 |     const params = {
105 |       projectId: 100,
106 |       repoName: 'test-repo', // Changed
107 |       number: 1,
108 |       summary: 'Updated PR title via projectId',
109 |     };
110 | 
111 |     await tool.handler(params);
112 | 
113 |     expect(mockBacklog.patchPullRequest).toHaveBeenCalledWith(
114 |       100,
115 |       'test-repo',
116 |       1,
117 |       {
118 |         summary: 'Updated PR title via projectId',
119 |         description: undefined,
120 |         issueId: undefined,
121 |         assigneeId: undefined,
122 |         statusId: undefined,
123 |         notifiedUserId: undefined,
124 |       }
125 |     );
126 |   });
127 | 
128 |   it('calls backlog.patchPullRequest with correct params when using projectId and repoId', async () => {
129 |     const params = {
130 |       projectId: 100,
131 |       repoId: 200, // Added repoId
132 |       number: 1,
133 |       summary: 'Updated PR title via repoId',
134 |     };
135 | 
136 |     await tool.handler(params);
137 | 
138 |     expect(mockBacklog.patchPullRequest).toHaveBeenCalledWith(100, '200', 1, {
139 |       summary: 'Updated PR title via repoId',
140 |       description: undefined,
141 |       issueId: undefined,
142 |       assigneeId: undefined,
143 |       statusId: undefined,
144 |       notifiedUserId: undefined,
145 |     });
146 |   });
147 | 
148 |   it('throws an error if neither projectId nor projectKey is provided', async () => {
149 |     const params = {
150 |       // projectId and projectKey are missing
151 |       repoName: 'test-repo', // Changed
152 |       number: 1,
153 |       summary: 'Test Summary',
154 |     };
155 | 
156 |     await expect(tool.handler(params as any)).rejects.toThrow(Error);
157 |   });
158 | 
159 |   it('throws an error if neither repoId nor repoName is provided', async () => {
160 |     const params = {
161 |       projectKey: 'TEST',
162 |       // repoId and repoName are missing
163 |       number: 1,
164 |       summary: 'Test Summary',
165 |     };
166 | 
167 |     await expect(tool.handler(params as any)).rejects.toThrow(Error);
168 |   });
169 | });
170 | 
```

--------------------------------------------------------------------------------
/memory-bank/techContext.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Technical Context
  2 | 
  3 | ## Technologies Used
  4 | 
  5 | ### Languages and Runtime
  6 | - **TypeScript**: Static typing for improved safety and development efficiency
  7 | - **Node.js**: Server-side JavaScript runtime (v22 or higher recommended)
  8 | 
  9 | ### Key Libraries
 10 | - **@modelcontextprotocol/sdk**: Implementation of MCP (Model Context Protocol) server
 11 | - **backlog-js**: Client library to simplify communication with Backlog API
 12 | - **zod**: Provides schema validation and type safety
 13 | - **cosmiconfig**: Configuration file loading and management
 14 | - **dotenv**: Environment variable management
 15 | - **graphql**: Used for field selection parsing and processing
 16 | 
 17 | ### Development Tools
 18 | - **Jest**: Testing framework
 19 | - **ESLint**: Code quality and style validation
 20 | - **Prettier**: Code formatting
 21 | - **release-it**: Release management automation
 22 | 
 23 | ### Containerization
 24 | - **Docker**: Application containerization with multi-stage builds
 25 | - **GitHub Container Registry**: Container image distribution
 26 | 
 27 | ## Development Environment Setup
 28 | 
 29 | ### Prerequisites
 30 | - Node.js v22 or higher (recommended)
 31 | - npm or yarn
 32 | - Git
 33 | 
 34 | ### Installation Steps
 35 | ```bash
 36 | # Clone the repository
 37 | git clone https://github.com/nulab/backlog-mcp-server.git
 38 | cd backlog-mcp-server
 39 | 
 40 | # Install dependencies
 41 | npm install
 42 | 
 43 | # Build
 44 | npm run build
 45 | ```
 46 | 
 47 | ### Environment Variables
 48 | Create a `.env` file during development with the following variables:
 49 | ```
 50 | BACKLOG_DOMAIN=your-domain.backlog.com
 51 | BACKLOG_API_KEY=your-api-key
 52 | ```
 53 | 
 54 | ## Technical Constraints
 55 | 
 56 | ### Backlog API
 57 | - Be mindful of API rate limits
 58 | - Some APIs require specific permissions
 59 | - API keys are issued per user and operate with that user's permissions
 60 | - Large responses may need pagination or token limiting
 61 | 
 62 | ### MCP Protocol
 63 | - Communicates through standard input/output (stdio)
 64 | - Tool inputs and outputs must follow specific formats
 65 | - Requires support for asynchronous processing
 66 | - Response size should be managed to avoid token limit issues
 67 | 
 68 | ### Containerization
 69 | - Multi-stage builds used to maintain lightweight container images
 70 | - Supports cross-architecture builds (amd64, arm64)
 71 | - Environment variables must be properly passed to containers
 72 | 
 73 | ## Build and Deploy
 74 | 
 75 | ### Build Process
 76 | ```mermaid
 77 | graph TD
 78 |     Clone[Clone repository] --> Install[Install dependencies]
 79 |     Install --> Lint[Lint check]
 80 |     Lint --> Test[Run tests]
 81 |     Test --> Build[TypeScript build]
 82 |     Build --> Docker[Docker image build]
 83 |     Docker --> Push[Push to registry]
 84 | ```
 85 | 
 86 | ### CI/CD
 87 | - Automation using GitHub Actions
 88 | - Testing and validation for each pull request
 89 | - Automatic release on tag push
 90 | - Building and publishing multi-architecture Docker images
 91 | 
 92 | ### Deployment Options
 93 | 1. **Docker**:
 94 |    ```bash
 95 |    docker run -i --rm \
 96 |      -e BACKLOG_DOMAIN=your-domain.backlog.com \
 97 |      -e BACKLOG_API_KEY=your-api-key \
 98 |      -v /path/to/.backlog-mcp-serverrc.json:/root/.backlog-mcp-serverrc.json:ro \
 99 |      ghcr.io/nulab/backlog-mcp-server
100 |    ```
101 | 
102 | 2. **Node.js**:
103 |    ```bash
104 |    BACKLOG_DOMAIN=your-domain.backlog.com \
105 |    BACKLOG_API_KEY=your-api-key \
106 |    node build/index.js
107 |    ```
108 | 
109 | ## Test Strategy
110 | 
111 | ### Unit Tests
112 | - Testing framework using Jest
113 | - Using mocks to isolate Backlog API dependencies
114 | - Creating test files corresponding to each tool
115 | 
116 | ### Running Tests
117 | ```bash
118 | # Run all tests
119 | npm test
120 | 
121 | # Run specific tests
122 | npm test -- -t "getSpace"
123 | ```
124 | 
125 | ## Performance Considerations
126 | 
127 | - Minimizing API requests
128 | - Appropriate error handling and retry strategies
129 | - Pagination handling when dealing with large amounts of data
130 | - Token limiting for large responses
131 | - Field selection to reduce response size
132 | - Streaming large responses in chunks
133 | 
134 | ## Security Considerations
135 | 
136 | - Secure management of API keys
137 | - Injection of sensitive information through environment variables
138 | - Principle of least privilege in containers
139 | - Input validation to prevent injection attacks
140 | - GraphQL field selection validation to prevent injection
141 | 
142 | ## Multi-language Support
143 | 
144 | - Multi-language support through translation files
145 | - Translation overrides through environment variables
146 | - Translation customization through configuration files
147 | - Fallback to default language (English)
148 | - Translation key tracking for consistency
149 | 
150 | ## Response Optimization
151 | 
152 | ### Field Selection
153 | - GraphQL-style field selection syntax
154 | - Allows clients to request only needed fields
155 | - Reduces response size and processing time
156 | - Example: `{ id name description }`
157 | 
158 | ### Token Limiting
159 | - Configurable maximum token limit (default: 50,000)
160 | - Can be set via environment variable or CLI argument
161 | - Automatically truncates large responses
162 | - Streaming implementation for efficient processing
163 | 
164 | ### Error Handling
165 | - Categorized error types (authentication, API, unexpected, unknown)
166 | - Consistent error response format
167 | - Detailed error messages for debugging
168 | - Backlog API-specific error parsing
169 | 
```

--------------------------------------------------------------------------------
/src/tools/getPullRequests.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { getPullRequestsTool } from './getPullRequests.js';
  2 | import { jest, describe, it, expect } from '@jest/globals';
  3 | import type { Backlog } from 'backlog-js';
  4 | import { createTranslationHelper } from '../createTranslationHelper.js';
  5 | 
  6 | describe('getPullRequestsTool', () => {
  7 |   const mockBacklog: Partial<Backlog> = {
  8 |     getPullRequests: jest.fn<() => Promise<any>>().mockResolvedValue([
  9 |       {
 10 |         id: 1,
 11 |         projectId: 100,
 12 |         repositoryId: 200,
 13 |         number: 1,
 14 |         summary: 'Fix bug in login',
 15 |         description: 'This PR fixes a bug in the login process',
 16 |         base: 'main',
 17 |         branch: 'fix/login-bug',
 18 |         status: {
 19 |           id: 1,
 20 |           name: 'Open',
 21 |         },
 22 |         assignee: {
 23 |           id: 1,
 24 |           userId: 'user1',
 25 |           name: 'User One',
 26 |         },
 27 |         issue: {
 28 |           id: 1000,
 29 |           issueKey: 'TEST-1',
 30 |           summary: 'Login bug',
 31 |         },
 32 |         baseCommit: 'abc123',
 33 |         branchCommit: 'def456',
 34 |         closeAt: null,
 35 |         mergeAt: null,
 36 |         createdUser: {
 37 |           id: 1,
 38 |           userId: 'user1',
 39 |           name: 'User One',
 40 |         },
 41 |         created: '2023-01-01T00:00:00Z',
 42 |         updatedUser: {
 43 |           id: 1,
 44 |           userId: 'user1',
 45 |           name: 'User One',
 46 |         },
 47 |         updated: '2023-01-01T00:00:00Z',
 48 |       },
 49 |       {
 50 |         id: 2,
 51 |         projectId: 100,
 52 |         repositoryId: 200,
 53 |         number: 2,
 54 |         summary: 'Add new feature',
 55 |         description: 'This PR adds a new feature',
 56 |         base: 'main',
 57 |         branch: 'feature/new-feature',
 58 |         status: {
 59 |           id: 1,
 60 |           name: 'Open',
 61 |         },
 62 |         assignee: {
 63 |           id: 2,
 64 |           userId: 'user2',
 65 |           name: 'User Two',
 66 |         },
 67 |         issue: {
 68 |           id: 1001,
 69 |           issueKey: 'TEST-2',
 70 |           summary: 'New feature',
 71 |         },
 72 |         baseCommit: 'ghi789',
 73 |         branchCommit: 'jkl012',
 74 |         closeAt: null,
 75 |         mergeAt: null,
 76 |         createdUser: {
 77 |           id: 2,
 78 |           userId: 'user2',
 79 |           name: 'User Two',
 80 |         },
 81 |         created: '2023-01-02T00:00:00Z',
 82 |         updatedUser: {
 83 |           id: 2,
 84 |           userId: 'user2',
 85 |           name: 'User Two',
 86 |         },
 87 |         updated: '2023-01-02T00:00:00Z',
 88 |       },
 89 |     ]),
 90 |   };
 91 | 
 92 |   const mockTranslationHelper = createTranslationHelper();
 93 |   const tool = getPullRequestsTool(
 94 |     mockBacklog as Backlog,
 95 |     mockTranslationHelper
 96 |   );
 97 | 
 98 |   it('returns pull requests list as formatted JSON text', async () => {
 99 |     const result = await tool.handler({
100 |       projectKey: 'TEST',
101 |       repoName: 'test-repo',
102 |     });
103 | 
104 |     if (!Array.isArray(result)) {
105 |       throw new Error('Unexpected non array result');
106 |     }
107 |     expect(result).toHaveLength(2);
108 |     expect(result[0].summary).toEqual('Fix bug in login');
109 |     expect(result[1].summary).toEqual('Add new feature');
110 |   });
111 | 
112 |   it('calls backlog.getPullRequests with correct params when using repoName', async () => {
113 |     const params = {
114 |       projectKey: 'TEST',
115 |       repoName: 'test-repo', // Changed
116 |       statusId: [1, 2],
117 |       assigneeId: [1],
118 |       count: 20,
119 |     };
120 | 
121 |     await tool.handler(params);
122 | 
123 |     expect(mockBacklog.getPullRequests).toHaveBeenCalledWith(
124 |       'TEST',
125 |       'test-repo',
126 |       {
127 |         statusId: [1, 2],
128 |         assigneeId: [1],
129 |         count: 20,
130 |       }
131 |     );
132 |   });
133 | 
134 |   it('calls backlog.getPullRequests with correct params when using projectId and repoName', async () => {
135 |     const params = {
136 |       projectId: 100,
137 |       repoName: 'test-repo', // Changed
138 |       statusId: [1],
139 |     };
140 | 
141 |     await tool.handler(params);
142 | 
143 |     expect(mockBacklog.getPullRequests).toHaveBeenCalledWith(100, 'test-repo', {
144 |       statusId: [1],
145 |       assigneeId: undefined,
146 |       count: undefined,
147 |       createdUserId: undefined,
148 |       issueId: undefined,
149 |       offset: undefined,
150 |     });
151 |   });
152 | 
153 |   it('calls backlog.getPullRequests with correct params when using projectId and repoId', async () => {
154 |     const params = {
155 |       projectId: 100,
156 |       repoId: 200, // Added repoId
157 |       statusId: [1],
158 |     };
159 | 
160 |     await tool.handler(params);
161 | 
162 |     expect(mockBacklog.getPullRequests).toHaveBeenCalledWith(100, '200', {
163 |       statusId: [1],
164 |       assigneeId: undefined,
165 |       count: undefined,
166 |       createdUserId: undefined,
167 |       issueId: undefined,
168 |       offset: undefined,
169 |     });
170 |   });
171 | 
172 |   it('throws an error if neither projectId nor projectKey is provided', async () => {
173 |     const params = {
174 |       // projectId and projectKey are missing
175 |       repoName: 'test-repo',
176 |     };
177 | 
178 |     await expect(tool.handler(params as any)).rejects.toThrow(Error);
179 |   });
180 | 
181 |   it('throws an error if neither repoId nor repoName is provided', async () => {
182 |     const params = {
183 |       projectKey: 'TEST',
184 |       // repoId and repoName are missing
185 |     };
186 | 
187 |     await expect(tool.handler(params as any)).rejects.toThrow(Error);
188 |   });
189 | });
190 | 
```

--------------------------------------------------------------------------------
/src/tools/getIssues.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { z } from 'zod';
  2 | import { Backlog } from 'backlog-js';
  3 | import { buildToolSchema, ToolDefinition } from '../types/tool.js';
  4 | import { TranslationHelper } from '../createTranslationHelper.js';
  5 | import { IssueSchema } from '../types/zod/backlogOutputDefinition.js';
  6 | import { customFieldsToPayload } from '../backlog/customFields.js';
  7 | 
  8 | const getIssuesSchema = buildToolSchema((t) => ({
  9 |   projectId: z
 10 |     .array(z.number())
 11 |     .optional()
 12 |     .describe(t('TOOL_GET_ISSUES_PROJECT_ID', 'Project IDs')),
 13 |   issueTypeId: z
 14 |     .array(z.number())
 15 |     .optional()
 16 |     .describe(t('TOOL_GET_ISSUES_ISSUE_TYPE_ID', 'Issue type IDs')),
 17 |   categoryId: z
 18 |     .array(z.number())
 19 |     .optional()
 20 |     .describe(t('TOOL_GET_ISSUES_CATEGORY_ID', 'Category IDs')),
 21 |   versionId: z
 22 |     .array(z.number())
 23 |     .optional()
 24 |     .describe(t('TOOL_GET_ISSUES_VERSION_ID', 'Version IDs')),
 25 |   milestoneId: z
 26 |     .array(z.number())
 27 |     .optional()
 28 |     .describe(t('TOOL_GET_ISSUES_MILESTONE_ID', 'Milestone IDs')),
 29 |   statusId: z
 30 |     .array(z.number())
 31 |     .optional()
 32 |     .describe(t('TOOL_GET_ISSUES_STATUS_ID', 'Status IDs')),
 33 |   priorityId: z
 34 |     .array(z.number())
 35 |     .optional()
 36 |     .describe(t('TOOL_GET_ISSUES_PRIORITY_ID', 'Priority IDs')),
 37 |   assigneeId: z
 38 |     .array(z.number())
 39 |     .optional()
 40 |     .describe(t('TOOL_GET_ISSUES_ASSIGNEE_ID', 'Assignee user IDs')),
 41 |   createdUserId: z
 42 |     .array(z.number())
 43 |     .optional()
 44 |     .describe(t('TOOL_GET_ISSUES_CREATED_USER_ID', 'Created user IDs')),
 45 |   resolutionId: z
 46 |     .array(z.number())
 47 |     .optional()
 48 |     .describe(t('TOOL_GET_ISSUES_RESOLUTION_ID', 'Resolution IDs')),
 49 |   parentIssueId: z
 50 |     .array(z.number())
 51 |     .optional()
 52 |     .describe(t('TOOL_GET_ISSUES_PARENT_ISSUE_ID', 'Parent issue IDs')),
 53 |   keyword: z
 54 |     .string()
 55 |     .optional()
 56 |     .describe(t('TOOL_GET_ISSUES_KEYWORD', 'Keyword to search for in issues')),
 57 |   startDateSince: z
 58 |     .string()
 59 |     .optional()
 60 |     .describe(
 61 |       t('TOOL_GET_ISSUES_START_DATE_SINCE', 'Start date since (yyyy-MM-dd)')
 62 |     ),
 63 |   startDateUntil: z
 64 |     .string()
 65 |     .optional()
 66 |     .describe(
 67 |       t('TOOL_GET_ISSUES_START_DATE_UNTIL', 'Start date until (yyyy-MM-dd)')
 68 |     ),
 69 |   dueDateSince: z
 70 |     .string()
 71 |     .optional()
 72 |     .describe(
 73 |       t('TOOL_GET_ISSUES_DUE_DATE_SINCE', 'Due date since (yyyy-MM-dd)')
 74 |     ),
 75 |   dueDateUntil: z
 76 |     .string()
 77 |     .optional()
 78 |     .describe(
 79 |       t('TOOL_GET_ISSUES_DUE_DATE_UNTIL', 'Due date until (yyyy-MM-dd)')
 80 |     ),
 81 |   createdSince: z
 82 |     .string()
 83 |     .optional()
 84 |     .describe(t('TOOL_GET_ISSUES_CREATED_SINCE', 'Created since (yyyy-MM-dd)')),
 85 |   createdUntil: z
 86 |     .string()
 87 |     .optional()
 88 |     .describe(t('TOOL_GET_ISSUES_CREATED_UNTIL', 'Created until (yyyy-MM-dd)')),
 89 |   updatedSince: z
 90 |     .string()
 91 |     .optional()
 92 |     .describe(t('TOOL_GET_ISSUES_UPDATED_SINCE', 'Updated since (yyyy-MM-dd)')),
 93 |   updatedUntil: z
 94 |     .string()
 95 |     .optional()
 96 |     .describe(t('TOOL_GET_ISSUES_UPDATED_UNTIL', 'Updated until (yyyy-MM-dd)')),
 97 |   sort: z
 98 |     .enum([
 99 |       'issueType',
100 |       'category',
101 |       'version',
102 |       'milestone',
103 |       'summary',
104 |       'status',
105 |       'priority',
106 |       'attachment',
107 |       'sharedFile',
108 |       'created',
109 |       'createdUser',
110 |       'updated',
111 |       'updatedUser',
112 |       'assignee',
113 |       'startDate',
114 |       'dueDate',
115 |       'estimatedHours',
116 |       'actualHours',
117 |       'childIssue',
118 |     ])
119 |     .optional()
120 |     .describe(t('TOOL_GET_ISSUES_SORT', 'Sort field')),
121 |   order: z
122 |     .enum(['asc', 'desc'])
123 |     .optional()
124 |     .describe(t('TOOL_GET_ISSUES_ORDER', 'Sort order')),
125 |   offset: z
126 |     .number()
127 |     .optional()
128 |     .describe(t('TOOL_GET_ISSUES_OFFSET', 'Offset for pagination')),
129 |   count: z
130 |     .number()
131 |     .optional()
132 |     .describe(t('TOOL_GET_ISSUES_COUNT', 'Number of issues to retrieve')),
133 |   customFields: z
134 |     .array(
135 |       z.object({
136 |         id: z
137 |           .number()
138 |           .describe(t('TOOL_GET_ISSUES_CUSTOM_FIELD_ID', 'Custom field ID')),
139 |         value: z
140 |           .union([z.string(), z.number(), z.array(z.string())])
141 |           .describe(
142 |             t('TOOL_GET_ISSUES_CUSTOM_FIELD_VALUE', 'Custom field value')
143 |           ),
144 |       })
145 |     )
146 |     .optional()
147 |     .describe(t('TOOL_GET_ISSUES_CUSTOM_FIELDS', 'Custom fields')),
148 | }));
149 | 
150 | export const getIssuesTool = (
151 |   backlog: Backlog,
152 |   { t }: TranslationHelper
153 | ): ToolDefinition<
154 |   ReturnType<typeof getIssuesSchema>,
155 |   (typeof IssueSchema)['shape']
156 | > => {
157 |   return {
158 |     name: 'get_issues',
159 |     description: t('TOOL_GET_ISSUES_DESCRIPTION', 'Returns list of issues'),
160 |     schema: z.object(getIssuesSchema(t)),
161 |     importantFields: [
162 |       'projectId',
163 |       'issueKey',
164 |       'keyId',
165 |       'summary',
166 |       'description',
167 |       'issueType',
168 |     ],
169 |     outputSchema: IssueSchema,
170 |     handler: async ({ customFields, ...rest }) => {
171 |       return backlog.getIssues({
172 |         ...rest,
173 |         ...customFieldsToPayload(customFields),
174 |       });
175 |     },
176 |   };
177 | };
178 | 
```

--------------------------------------------------------------------------------
/src/tools/addPullRequest.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { addPullRequestTool } from './addPullRequest.js';
  2 | import { jest, describe, it, expect } from '@jest/globals';
  3 | import type { Backlog } from 'backlog-js';
  4 | import { createTranslationHelper } from '../createTranslationHelper.js';
  5 | 
  6 | describe('addPullRequestTool', () => {
  7 |   const mockBacklog: Partial<Backlog> = {
  8 |     postPullRequest: jest.fn<() => Promise<any>>().mockResolvedValue({
  9 |       id: 1,
 10 |       projectId: 100,
 11 |       repositoryId: 200,
 12 |       number: 1,
 13 |       summary: 'Fix bug in login',
 14 |       description: 'This PR fixes a bug in the login process',
 15 |       base: 'main',
 16 |       branch: 'fix/login-bug',
 17 |       status: {
 18 |         id: 1,
 19 |         name: 'Open',
 20 |       },
 21 |       assignee: {
 22 |         id: 1,
 23 |         userId: 'user1',
 24 |         name: 'User One',
 25 |       },
 26 |       issue: {
 27 |         id: 1000,
 28 |         issueKey: 'TEST-1',
 29 |         summary: 'Login bug',
 30 |       },
 31 |       baseCommit: 'abc123',
 32 |       branchCommit: 'def456',
 33 |       closeAt: null,
 34 |       mergeAt: null,
 35 |       createdUser: {
 36 |         id: 1,
 37 |         userId: 'user1',
 38 |         name: 'User One',
 39 |       },
 40 |       created: '2023-01-01T00:00:00Z',
 41 |       updatedUser: {
 42 |         id: 1,
 43 |         userId: 'user1',
 44 |         name: 'User One',
 45 |       },
 46 |       updated: '2023-01-01T00:00:00Z',
 47 |     }),
 48 |   };
 49 | 
 50 |   const mockTranslationHelper = createTranslationHelper();
 51 |   const tool = addPullRequestTool(
 52 |     mockBacklog as Backlog,
 53 |     mockTranslationHelper
 54 |   );
 55 | 
 56 |   it('returns created pull request as formatted JSON text', async () => {
 57 |     const result = await tool.handler({
 58 |       projectKey: 'TEST',
 59 |       repoName: 'test-repo', // Changed
 60 |       summary: 'Fix bug in login',
 61 |       description: 'This PR fixes a bug in the login process',
 62 |       base: 'main',
 63 |       branch: 'fix/login-bug',
 64 |       issueId: 1000,
 65 |       assigneeId: 1,
 66 |     });
 67 | 
 68 |     if (Array.isArray(result)) {
 69 |       throw new Error('Unexpected array result');
 70 |     }
 71 |     expect(result.summary).toEqual('Fix bug in login');
 72 |     expect(result.description).toEqual(
 73 |       'This PR fixes a bug in the login process'
 74 |     );
 75 |   });
 76 | 
 77 |   it('calls backlog.postPullRequest with correct params when using repoName', async () => {
 78 |     const params = {
 79 |       projectKey: 'TEST',
 80 |       repoName: 'test-repo', // Changed
 81 |       summary: 'Fix bug in login',
 82 |       description: 'This PR fixes a bug in the login process',
 83 |       base: 'main',
 84 |       branch: 'fix/login-bug',
 85 |       issueId: 1000,
 86 |       assigneeId: 1,
 87 |       notifiedUserId: [2, 3],
 88 |     };
 89 | 
 90 |     await tool.handler(params);
 91 | 
 92 |     expect(mockBacklog.postPullRequest).toHaveBeenCalledWith(
 93 |       'TEST',
 94 |       'test-repo',
 95 |       {
 96 |         summary: 'Fix bug in login',
 97 |         description: 'This PR fixes a bug in the login process',
 98 |         base: 'main',
 99 |         branch: 'fix/login-bug',
100 |         issueId: 1000,
101 |         assigneeId: 1,
102 |         notifiedUserId: [2, 3],
103 |       }
104 |     );
105 |   });
106 | 
107 |   it('calls backlog.postPullRequest with correct params when using projectId and repoName', async () => {
108 |     const params = {
109 |       projectId: 1,
110 |       repoName: 'test-repo', // Changed
111 |       summary: 'Summary for projectId and repoName',
112 |       description: 'Description for projectId and repoName',
113 |       base: 'main',
114 |       branch: 'feature/new',
115 |     };
116 | 
117 |     await tool.handler(params as any);
118 | 
119 |     expect(mockBacklog.postPullRequest).toHaveBeenCalledWith(1, 'test-repo', {
120 |       summary: 'Summary for projectId and repoName',
121 |       description: 'Description for projectId and repoName',
122 |       base: 'main',
123 |       branch: 'feature/new',
124 |       issueId: undefined,
125 |       assigneeId: undefined,
126 |       notifiedUserId: undefined,
127 |     });
128 |   });
129 | 
130 |   it('calls backlog.postPullRequest with correct params when using projectId and repoId', async () => {
131 |     const params = {
132 |       projectId: 1,
133 |       repoId: 200, // Added repoId
134 |       summary: 'Summary for projectId and repoId',
135 |       description: 'Description for projectId and repoId',
136 |       base: 'main',
137 |       branch: 'feature/new-id',
138 |     };
139 | 
140 |     await tool.handler(params as any);
141 | 
142 |     expect(mockBacklog.postPullRequest).toHaveBeenCalledWith(1, '200', {
143 |       summary: 'Summary for projectId and repoId',
144 |       description: 'Description for projectId and repoId',
145 |       base: 'main',
146 |       branch: 'feature/new-id',
147 |       issueId: undefined,
148 |       assigneeId: undefined,
149 |       notifiedUserId: undefined,
150 |     });
151 |   });
152 | 
153 |   it('throws an error if neither projectId nor projectKey is provided', async () => {
154 |     const params = {
155 |       repoName: 'test-repo',
156 |       summary: 'Summary',
157 |       description: 'Description',
158 |       base: 'main',
159 |       branch: 'branch',
160 |     };
161 | 
162 |     await expect(tool.handler(params as any)).rejects.toThrow(Error);
163 |   });
164 | 
165 |   it('throws an error if neither repoId nor repoName is provided', async () => {
166 |     const params = {
167 |       projectKey: 'TEST',
168 |       summary: 'Summary',
169 |       description: 'Description',
170 |       base: 'main',
171 |       branch: 'branch',
172 |     };
173 | 
174 |     await expect(tool.handler(params as any)).rejects.toThrow(Error);
175 |   });
176 | });
177 | 
```

--------------------------------------------------------------------------------
/memory-bank/activeContext.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Active Context
  2 | 
  3 | ## Current Work Focus
  4 | 
  5 | Currently, the Backlog MCP Server implements tools corresponding to the following feature categories:
  6 | 
  7 | 1. **Space-related Tools**
  8 |    - Retrieving space information
  9 |    - Retrieving user lists
 10 |    - Retrieving information about oneself
 11 |    - Retrieving priority lists
 12 |    - Retrieving resolution lists
 13 |    - Retrieving issue type lists
 14 | 
 15 | 2. **Project-related Tools**
 16 |    - Retrieving project lists
 17 |    - Creating projects
 18 |    - Retrieving project information
 19 |    - Updating projects
 20 |    - Deleting projects
 21 | 
 22 | 3. **Issue-related Tools**
 23 |    - Retrieving issue information
 24 |    - Retrieving issue lists
 25 |    - Retrieving issue counts
 26 |    - Creating issues
 27 |    - Updating issues
 28 |    - Deleting issues
 29 | 
 30 | 4. **Comment-related Tools**
 31 |    - Retrieving issue comment lists
 32 |    - Adding issue comments
 33 | 
 34 | 5. **Wiki-related Tools**
 35 |    - Retrieving Wiki page lists
 36 |    - Retrieving Wiki page counts
 37 |    - Retrieving Wiki information
 38 |    - Creating Wiki pages
 39 | 
 40 | 6. **Category-related Tools**
 41 |    - Retrieving category lists
 42 | 
 43 | 7. **Notification-related Tools**
 44 |    - Retrieving notification lists
 45 |    - Retrieving notification counts
 46 |    - Resetting unread notification counts
 47 |    - Marking notifications as read
 48 | 
 49 | 8. **Git Repository-related Tools**
 50 |    - Retrieving Git repository lists
 51 |    - Retrieving Git repository information
 52 | 
 53 | 9. **Pull Request-related Tools**
 54 |    - Retrieving pull request lists
 55 |    - Retrieving pull request counts
 56 |    - Retrieving pull request information
 57 |    - Creating pull requests
 58 |    - Updating pull requests
 59 |    - Retrieving pull request comment lists
 60 |    - Adding pull request comments
 61 |    - Updating pull request comments
 62 | 
 63 | 10. **Watch-related Tools**
 64 |     - Retrieving watched item lists
 65 |     - Retrieving watch counts
 66 | 
 67 | ## Recent Changes
 68 | 
 69 | 1. **Token Limiting Implementation**
 70 |    - Added token limiting functionality to prevent large responses from exceeding token limits
 71 |    - Implemented streaming for large responses with automatic truncation
 72 |    - Added configurable maximum token limit via environment variables or CLI arguments
 73 | 
 74 | 2. **Field Picking Optimization**
 75 |    - Added GraphQL-style field selection to allow clients to request only specific fields
 76 |    - Implemented field picking transformer to optimize response size
 77 |    - Added field description generation for better documentation
 78 | 
 79 | 3. **Error Handling Improvements**
 80 |    - Enhanced Backlog API error parsing and handling
 81 |    - Added more descriptive error messages for different error types
 82 |    - Implemented unified error handling system
 83 | 
 84 | 4. **Documentation Updates**
 85 |    - Updated README with new features and usage examples
 86 |    - Added Japanese translation of documentation
 87 |    - Improved installation and configuration instructions
 88 | 
 89 | 5. **Build and Infrastructure**
 90 |    - Updated Docker configuration to use Node.js 22
 91 |    - Improved multi-stage Docker build for smaller image size
 92 |    - Updated dependencies to latest versions
 93 | 
 94 | ## Active Decisions and Considerations
 95 | 
 96 | 1. **Response Optimization**
 97 |    - Implementing GraphQL-style field selection to reduce response size
 98 |    - Adding token limiting to prevent large responses from causing issues
 99 |    - Balancing between comprehensive data and performance
100 | 
101 | 2. **API Endpoint Coverage**
102 |    - Prioritizing implementation of API endpoints listed in URLlist.md
103 |    - Gradually adding unimplemented endpoints
104 |    - Focusing on most commonly used endpoints first
105 | 
106 | 3. **Test Strategy**
107 |    - Creating unit tests corresponding to each tool
108 |    - Using mocks to isolate Backlog API dependencies
109 |    - Focusing on validating input parameters and output format
110 |    - Testing error handling and edge cases
111 | 
112 | 4. **Multi-language Support**
113 |    - Using English as the default language, with support for other languages like Japanese
114 |    - Providing customization possibilities through translation files
115 |    - Supporting translation overrides through environment variables
116 |    - Maintaining consistent translation keys across the system
117 | 
118 | 5. **Deployment Options**
119 |    - Prioritizing easy deployment via Docker
120 |    - Also supporting direct Node.js execution
121 |    - Customization through mounted configuration files
122 |    - Supporting environment variable configuration for flexibility
123 | 
124 | ## Important Patterns and Design Principles
125 | 
126 | 1. **Tool Implementation Consistency**
127 |    - Each tool has the same structure (name, description, schema, handler)
128 |    - Input validation using Zod schemas
129 |    - Unified response format
130 |    - Consistent error handling
131 | 
132 | 2. **Handler Composition Pattern**
133 |    - Using function composition for tool handlers
134 |    - Applying transformers in a specific order (error handling → field picking → token limiting → result formatting)
135 |    - Separation of concerns through transformer functions
136 | 
137 | 3. **Translation System**
138 |    - Key-based translation system
139 |    - Priority: environment variables → configuration file → default value
140 |    - Tracking of all translation keys used
141 |    - Support for multiple languages through configuration
142 | 
143 | 4. **Error Handling**
144 |    - Appropriate handling of API errors and meaningful error messages
145 |    - Clear reporting of input validation errors
146 |    - Categorization of errors (authentication, API, unexpected, unknown)
147 |    - Consistent error response format
148 | 
149 | 5. **Testability**
150 |    - Ease of testing through dependency injection
151 |    - Isolation of external dependencies using mocks
152 |    - Unit tests for each component and transformer
153 | 
154 | ## Learnings and Project Insights
155 | 
156 | 1. **MCP Integration Best Practices**
157 |    - Importance of tool naming conventions and parameters
158 |    - Improved usability through appropriate descriptions
159 |    - Importance of schema validation
160 |    - Balancing between comprehensive data and token limits
161 | 
162 | 2. **Response Optimization Techniques**
163 |    - GraphQL-style field selection for targeted data retrieval
164 |    - Token counting and limiting for large responses
165 |    - Streaming large responses in chunks
166 |    - Balancing between data completeness and performance
167 | 
168 | 3. **Backlog API-specific Considerations**
169 |    - Flexibility to support both IDs and keys
170 |    - Permission requirements for some API endpoints
171 |    - Handling differences in response formats
172 |    - Error handling for various API response scenarios
173 | 
174 | 4. **Multi-language Support Challenges**
175 |    - Management and consistency of translation keys
176 |    - Importance of default values
177 |    - Maintainability of translation files
178 |    - Balancing between translation flexibility and complexity
179 | 
```

--------------------------------------------------------------------------------
/src/tools/tools.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { Backlog } from 'backlog-js';
  2 | import { TranslationHelper } from '../createTranslationHelper.js';
  3 | import { ToolsetGroup } from '../types/toolsets.js';
  4 | import { addIssueTool } from './addIssue.js';
  5 | import { addIssueCommentTool } from './addIssueComment.js';
  6 | import { addProjectTool } from './addProject.js';
  7 | import { addPullRequestTool } from './addPullRequest.js';
  8 | import { addPullRequestCommentTool } from './addPullRequestComment.js';
  9 | import { addWikiTool } from './addWiki.js';
 10 | import { countIssuesTool } from './countIssues.js';
 11 | import { deleteIssueTool } from './deleteIssue.js';
 12 | import { deleteProjectTool } from './deleteProject.js';
 13 | import { getCategoriesTool } from './getCategories.js';
 14 | import { getCustomFieldsTool } from './getCustomFields.js';
 15 | import { getGitRepositoriesTool } from './getGitRepositories.js';
 16 | import { getGitRepositoryTool } from './getGitRepository.js';
 17 | import { getIssueTool } from './getIssue.js';
 18 | import { getIssueCommentsTool } from './getIssueComments.js';
 19 | import { getIssuesTool } from './getIssues.js';
 20 | import { getIssueTypesTool } from './getIssueTypes.js';
 21 | import { getMyselfTool } from './getMyself.js';
 22 | import { getNotificationsTool } from './getNotifications.js';
 23 | import { getNotificationsCountTool } from './getNotificationsCount.js';
 24 | import { getPrioritiesTool } from './getPriorities.js';
 25 | import { getProjectTool } from './getProject.js';
 26 | import { getProjectListTool } from './getProjectList.js';
 27 | import { getPullRequestTool } from './getPullRequest.js';
 28 | import { getPullRequestCommentsTool } from './getPullRequestComments.js';
 29 | import { getPullRequestsTool } from './getPullRequests.js';
 30 | import { getPullRequestsCountTool } from './getPullRequestsCount.js';
 31 | import { getResolutionsTool } from './getResolutions.js';
 32 | import { getSpaceTool } from './getSpace.js';
 33 | import { getUsersTool } from './getUsers.js';
 34 | import { getWatchingListCountTool } from './getWatchingListCount.js';
 35 | import { getWatchingListItemsTool } from './getWatchingListItems.js';
 36 | import { getWikiTool } from './getWiki.js';
 37 | import { getWikiPagesTool } from './getWikiPages.js';
 38 | import { getWikisCountTool } from './getWikisCount.js';
 39 | import { markNotificationAsReadTool } from './markNotificationAsRead.js';
 40 | import { resetUnreadNotificationCountTool } from './resetUnreadNotificationCount.js';
 41 | import { updateIssueTool } from './updateIssue.js';
 42 | import { updateProjectTool } from './updateProject.js';
 43 | import { updatePullRequestTool } from './updatePullRequest.js';
 44 | import { updatePullRequestCommentTool } from './updatePullRequestComment.js';
 45 | import { getDocumentTool } from './getDocument.js';
 46 | import { getDocumentsTool } from './getDocuments.js';
 47 | import { getDocumentTreeTool } from './getDocumentTree.js';
 48 | import { getVersionMilestoneListTool } from './getVersionMilestoneList.js';
 49 | import { addVersionMilestoneTool } from './addVersionMilestone.js';
 50 | import { updateVersionMilestoneTool } from './updateVersionMilestone.js';
 51 | import { deleteVersionTool } from './deleteVersion.js';
 52 | 
 53 | export const allTools = (
 54 |   backlog: Backlog,
 55 |   helper: TranslationHelper
 56 | ): ToolsetGroup => {
 57 |   return {
 58 |     toolsets: [
 59 |       {
 60 |         name: 'space',
 61 |         description:
 62 |           'Tools for managing Backlog space settings and general information.',
 63 |         enabled: false,
 64 |         tools: [
 65 |           getSpaceTool(backlog, helper),
 66 |           getUsersTool(backlog, helper),
 67 |           getMyselfTool(backlog, helper),
 68 |         ],
 69 |       },
 70 |       {
 71 |         name: 'project',
 72 |         description:
 73 |           'Tools for managing projects, categories, custom fields, and issue types.',
 74 |         enabled: false,
 75 |         tools: [
 76 |           getProjectListTool(backlog, helper),
 77 |           addProjectTool(backlog, helper),
 78 |           getProjectTool(backlog, helper),
 79 |           updateProjectTool(backlog, helper),
 80 |           deleteProjectTool(backlog, helper),
 81 |         ],
 82 |       },
 83 |       {
 84 |         name: 'issue',
 85 |         description: 'Tools for managing issues and their comments.',
 86 |         enabled: false,
 87 |         tools: [
 88 |           getIssueTool(backlog, helper),
 89 |           getIssuesTool(backlog, helper),
 90 |           countIssuesTool(backlog, helper),
 91 |           addIssueTool(backlog, helper),
 92 |           updateIssueTool(backlog, helper),
 93 |           deleteIssueTool(backlog, helper),
 94 |           getIssueCommentsTool(backlog, helper),
 95 |           addIssueCommentTool(backlog, helper),
 96 |           getPrioritiesTool(backlog, helper),
 97 |           getCategoriesTool(backlog, helper),
 98 |           getCustomFieldsTool(backlog, helper),
 99 |           getIssueTypesTool(backlog, helper),
100 |           getResolutionsTool(backlog, helper),
101 |           getWatchingListItemsTool(backlog, helper),
102 |           getWatchingListCountTool(backlog, helper),
103 |           getVersionMilestoneListTool(backlog, helper),
104 |           addVersionMilestoneTool(backlog, helper),
105 |           updateVersionMilestoneTool(backlog, helper),
106 |           deleteVersionTool(backlog, helper),
107 |         ],
108 |       },
109 |       {
110 |         name: 'wiki',
111 |         description: 'Tools for managing wiki pages.',
112 |         enabled: false,
113 |         tools: [
114 |           getWikiPagesTool(backlog, helper),
115 |           getWikisCountTool(backlog, helper),
116 |           getWikiTool(backlog, helper),
117 |           addWikiTool(backlog, helper),
118 |         ],
119 |       },
120 |       {
121 |         name: 'git',
122 |         description: 'Tools for managing Git repositories and pull requests.',
123 |         enabled: false,
124 |         tools: [
125 |           getGitRepositoriesTool(backlog, helper),
126 |           getGitRepositoryTool(backlog, helper),
127 |           getPullRequestsTool(backlog, helper),
128 |           getPullRequestsCountTool(backlog, helper),
129 |           getPullRequestTool(backlog, helper),
130 |           addPullRequestTool(backlog, helper),
131 |           updatePullRequestTool(backlog, helper),
132 |           getPullRequestCommentsTool(backlog, helper),
133 |           addPullRequestCommentTool(backlog, helper),
134 |           updatePullRequestCommentTool(backlog, helper),
135 |         ],
136 |       },
137 |       {
138 |         name: 'document',
139 |         description: 'Tools for managing documents.',
140 |         enabled: false,
141 |         tools: [
142 |           getDocumentsTool(backlog, helper),
143 |           getDocumentTreeTool(backlog, helper),
144 |           getDocumentTool(backlog, helper),
145 |         ],
146 |       },
147 |       {
148 |         name: 'notifications',
149 |         description: 'Tools for managing user notifications.',
150 |         enabled: false,
151 |         tools: [
152 |           getNotificationsTool(backlog, helper),
153 |           getNotificationsCountTool(backlog, helper),
154 |           resetUnreadNotificationCountTool(backlog, helper),
155 |           markNotificationAsReadTool(backlog, helper),
156 |         ],
157 |       },
158 |     ],
159 |   };
160 | };
161 | 
```

--------------------------------------------------------------------------------
/memory-bank/systemPatterns.md:
--------------------------------------------------------------------------------

```markdown
  1 | # System Patterns
  2 | 
  3 | ## Architecture Overview
  4 | 
  5 | The Backlog MCP Server functions as a bridge between Claude and the Backlog API using the Model Context Protocol (MCP). The system consists of the following main components:
  6 | 
  7 | ```mermaid
  8 | graph TD
  9 |     Claude[Claude AI] <--> MCP[MCP Protocol]
 10 |     MCP <--> Server[Backlog MCP Server]
 11 |     Server <--> BacklogAPI[Backlog API]
 12 |     Server <--> Config[Configuration Files]
 13 | ```
 14 | 
 15 | ## Main Components
 16 | 
 17 | ### 1. MCP Server
 18 | - Implements an MCP server using `@modelcontextprotocol/sdk`
 19 | - Communicates with Claude etc through standard input/output (stdio)
 20 | - Manages tool registration and execution
 21 | 
 22 | ### 2. Tool Definition System
 23 | - Defines tools corresponding to each Backlog API endpoint
 24 | - Validates input parameters using Zod schemas
 25 | - Returns data in a unified response format
 26 | 
 27 | ### 3. Translation System
 28 | - Translation helper for multi-language support
 29 | - Loads translations from configuration files or environment variables
 30 | - Ensures descriptions are always displayed with fallback functionality
 31 | 
 32 | ### 4. Backlog API Client
 33 | - Communicates with the Backlog API using the `backlog-js` library
 34 | - Retrieves authentication information from environment variables
 35 | - Each tool uses the API client to perform operations
 36 | 
 37 | ## Design Patterns
 38 | 
 39 | ### 1. Factory Pattern
 40 | - The `allTools` function receives a Backlog client and translation helper, generating instances of all tools
 41 | - Each tool has its own definition and implementation while providing a unified interface
 42 | 
 43 | ### 2. Dependency Injection
 44 | - Backlog client and translation helper are injected into tools
 45 | - Mock objects can be injected during testing for easier unit testing
 46 | - Options for field picking and token limiting are injected into handlers
 47 | 
 48 | ### 3. Adapter Pattern
 49 | - Converts Backlog API responses to MCP tool output format
 50 | - Adapts diverse response formats from different API endpoints to a unified format
 51 | 
 52 | ### 4. Strategy Pattern
 53 | - Translation system selects appropriate translations from different sources (environment variables, configuration files, default values)
 54 | - Provides optimal translations based on priority
 55 | 
 56 | ### 5. Decorator Pattern
 57 | - Tool handlers are wrapped with various transformers (error handling, field picking, token limiting, result formatting)
 58 | - Each transformer adds specific functionality while maintaining the same interface
 59 | - Transformers can be composed in different orders based on requirements
 60 | 
 61 | ### 6. Pipeline Pattern
 62 | - Response processing follows a clear pipeline: handler → error handling → field picking → token limiting → result formatting
 63 | - Each step in the pipeline processes the data and passes it to the next step
 64 | 
 65 | ## Important Implementation Paths
 66 | 
 67 | ### Tool Registration Flow
 68 | ```mermaid
 69 | sequenceDiagram
 70 |     participant Main as index.ts
 71 |     participant Register as registerTools.ts
 72 |     participant Tools as tools.ts
 73 |     participant Tool as Individual Tools
 74 |     participant Compose as composeToolHandler.ts
 75 | 
 76 |     Main->>Register: registerTools(server, backlog, helper, options)
 77 |     Register->>Tools: allTools(backlog, helper)
 78 |     Tools->>Tool: Tool factory function
 79 |     Tool-->>Tools: Tool instance
 80 |     Tools-->>Register: Array of tools
 81 |     Register->>Register: Duplicate check
 82 |     Register->>Compose: composeToolHandler(tool, options)
 83 |     Compose-->>Register: Composed handler
 84 |     Register->>Main: Register tools with server
 85 | ```
 86 | 
 87 | ### Request Processing Flow
 88 | ```mermaid
 89 | sequenceDiagram
 90 |     participant Claude as Claude
 91 |     participant Server as MCP Server
 92 |     participant Handler as Composed Handler
 93 |     participant ErrorHandler as Error Handler
 94 |     participant FieldPicker as Field Picker
 95 |     participant TokenLimiter as Token Limiter
 96 |     participant ResultFormatter as Result Formatter
 97 |     participant Tool as Tool Handler
 98 |     participant Backlog as Backlog API
 99 | 
100 |     Claude->>Server: Tool request with fields
101 |     Server->>Handler: Call with input
102 |     Handler->>ErrorHandler: Safe execution
103 |     ErrorHandler->>Tool: Execute tool handler
104 |     Tool->>Backlog: API call
105 |     Backlog-->>Tool: API response
106 |     Tool-->>ErrorHandler: Raw result
107 |     alt Field picking enabled
108 |         ErrorHandler->>FieldPicker: Result with fields
109 |         FieldPicker->>FieldPicker: Parse GraphQL fields
110 |         FieldPicker->>FieldPicker: Pick requested fields
111 |         FieldPicker->>TokenLimiter: Filtered result
112 |     else Field picking disabled
113 |         ErrorHandler->>TokenLimiter: Full result
114 |     end
115 |     TokenLimiter->>TokenLimiter: Count tokens
116 |     TokenLimiter->>TokenLimiter: Stream if large
117 |     TokenLimiter->>TokenLimiter: Truncate if over limit
118 |     TokenLimiter->>ResultFormatter: Limited result
119 |     ResultFormatter->>Server: Formatted response
120 |     Server-->>Claude: Tool response
121 | ```
122 | 
123 | ### Translation Resolution Flow
124 | ```mermaid
125 | sequenceDiagram
126 |     participant Tool as Tool
127 |     participant Helper as TranslationHelper
128 |     participant Env as Environment Variables
129 |     participant Config as Configuration File
130 | 
131 |     Tool->>Helper: t(key, fallback)
132 |     Helper->>Env: Check environment variables
133 |     alt Exists in environment variables
134 |         Env-->>Helper: Translation value
135 |     else Does not exist in environment variables
136 |         Helper->>Config: Check configuration file
137 |         alt Exists in configuration file
138 |             Config-->>Helper: Translation value
139 |         else Does not exist in configuration file
140 |             Helper-->>Helper: Use fallback value
141 |         end
142 |     end
143 |     Helper-->>Tool: Resolved translation
144 | ```
145 | 
146 | ### Token Limiting Flow
147 | ```mermaid
148 | sequenceDiagram
149 |     participant Handler as Tool Handler
150 |     participant Limiter as Token Limiter
151 |     participant Counter as Token Counter
152 |     participant Streamer as Content Streamer
153 | 
154 |     Handler->>Limiter: Large response
155 |     Limiter->>Limiter: Convert to stream
156 |     Limiter->>Streamer: Stream chunks
157 |     loop For each chunk
158 |         Streamer->>Counter: Count tokens
159 |         Counter-->>Streamer: Token count
160 |         alt Under limit
161 |             Streamer->>Streamer: Add chunk
162 |             Streamer->>Streamer: Update total count
163 |         else Over limit
164 |             Streamer->>Streamer: Add truncation message
165 |             Streamer->>Streamer: Break loop
166 |         end
167 |     end
168 |     Streamer-->>Limiter: Limited content
169 |     Limiter-->>Handler: Formatted result
170 | ```
171 | 
172 | ## Component Relationships
173 | 
174 | ### Tool Structure
175 | Each tool has the following structure:
176 | - **Name**: Identifier representing the API endpoint
177 | - **Description**: Description of the tool's functionality (translatable)
178 | - **Schema**: Definition of input parameters (Zod)
179 | - **OutputSchema**: Definition of output structure (Zod, for field picking)
180 | - **ImportantFields**: List of fields that are most commonly needed (for examples)
181 | - **Handler**: Function that performs the actual processing
182 | 
183 | ### Handler Composition Structure
184 | ```mermaid
185 | graph TD
186 |     RawHandler[Raw Tool Handler] --> ErrorHandler[Error Handler]
187 |     ErrorHandler --> FieldPicker[Field Picker]
188 |     FieldPicker --> TokenLimiter[Token Limiter]
189 |     TokenLimiter --> ResultFormatter[Result Formatter]
190 |     ResultFormatter --> FinalHandler[Final Handler]
191 | ```
192 | 
193 | ### File Structure
194 | ```
195 | src/
196 | ├── index.ts              # Entry point
197 | ├── registerTools.ts      # Tool registration logic
198 | ├── createTranslationHelper.ts # Translation helper
199 | ├── backlog/
200 | │   ├── backlogErrorHandler.ts # Backlog-specific error handling
201 | │   └── parseBacklogAPIError.ts # Error parsing utilities
202 | ├── handlers/
203 | │   ├── builders/
204 | │   │   └── composeToolHandler.ts # Handler composition
205 | │   └── transformers/
206 | │       ├── wrapWithErrorHandling.ts # Error handling transformer
207 | │       ├── wrapWithFieldPicking.ts # Field picking transformer
208 | │       ├── wrapWithTokenLimit.ts # Token limiting transformer
209 | │       └── wrapWithToolResult.ts # Result formatting transformer
210 | ├── tools/
211 | │   ├── tools.ts          # Exports all tools
212 | │   ├── getSpace.ts       # Individual tool implementation
213 | │   ├── getSpace.test.ts  # Corresponding test
214 | │   └── ...               # Other tools
215 | ├── types/
216 | │   ├── mcp.ts            # MCP-related types
217 | │   ├── result.ts         # Result types
218 | │   ├── tool.ts           # Tool definition types
219 | │   └── zod/              # Zod schema definitions
220 | └── utils/
221 |     ├── contentStreamingWithTokenLimit.ts # Token limiting utilities
222 |     ├── generateFieldsDescription.ts # Field description generation
223 |     ├── runToolSafely.ts  # Safe tool execution
224 |     └── tokenCounter.ts   # Token counting utilities
225 | ```
226 | 
227 | ## Test Strategy
228 | 
229 | - Create unit tests corresponding to each tool
230 | - Use mocks to eliminate external dependencies on the Backlog API
231 | - Focus on validating input parameters and output format
232 | - Use translation helper mocks to test translation functionality
233 | 
```

--------------------------------------------------------------------------------
/memory-bank/URLlist.md:
--------------------------------------------------------------------------------

```markdown
  1 | https://developer.nulab.com/docs/backlog/api/2/get-space/
  2 | https://developer.nulab.com/docs/backlog/api/2/get-recent-updates/
  3 | https://developer.nulab.com/docs/backlog/api/2/get-space-logo/
  4 | https://developer.nulab.com/docs/backlog/api/2/get-space-notification/
  5 | https://developer.nulab.com/docs/backlog/api/2/update-space-notification/
  6 | https://developer.nulab.com/docs/backlog/api/2/get-space-disk-usage/
  7 | https://developer.nulab.com/docs/backlog/api/2/post-attachment-file/
  8 | https://developer.nulab.com/docs/backlog/api/2/get-user-list/
  9 | https://developer.nulab.com/docs/backlog/api/2/get-user/
 10 | https://developer.nulab.com/docs/backlog/api/2/add-user/
 11 | https://developer.nulab.com/docs/backlog/api/2/update-user/
 12 | https://developer.nulab.com/docs/backlog/api/2/delete-user/
 13 | https://developer.nulab.com/docs/backlog/api/2/get-own-user/
 14 | https://developer.nulab.com/docs/backlog/api/2/get-user-icon/
 15 | https://developer.nulab.com/docs/backlog/api/2/get-user-recent-updates/
 16 | https://developer.nulab.com/docs/backlog/api/2/get-received-star-list/
 17 | https://developer.nulab.com/docs/backlog/api/2/count-user-received-stars/
 18 | https://developer.nulab.com/docs/backlog/api/2/get-list-of-recently-viewed-issues/
 19 | https://developer.nulab.com/docs/backlog/api/2/get-list-of-recently-viewed-projects/
 20 | https://developer.nulab.com/docs/backlog/api/2/get-list-of-recently-viewed-wikis/
 21 | https://developer.nulab.com/docs/backlog/api/2/get-resolution-list/
 22 | https://developer.nulab.com/docs/backlog/api/2/get-priority-list/
 23 | https://developer.nulab.com/docs/backlog/api/2/get-project-list/
 24 | https://developer.nulab.com/docs/backlog/api/2/add-project/
 25 | https://developer.nulab.com/docs/backlog/api/2/get-project/
 26 | https://developer.nulab.com/docs/backlog/api/2/update-project/
 27 | https://developer.nulab.com/docs/backlog/api/2/delete-project/
 28 | https://developer.nulab.com/docs/backlog/api/2/get-project-icon/
 29 | https://developer.nulab.com/docs/backlog/api/2/get-project-recent-updates/
 30 | https://developer.nulab.com/docs/backlog/api/2/add-project-user/
 31 | https://developer.nulab.com/docs/backlog/api/2/get-project-user-list/
 32 | https://developer.nulab.com/docs/backlog/api/2/delete-project-user/
 33 | https://developer.nulab.com/docs/backlog/api/2/add-project-administrator/
 34 | https://developer.nulab.com/docs/backlog/api/2/get-list-of-project-administrators/
 35 | https://developer.nulab.com/docs/backlog/api/2/delete-project-administrator/
 36 | https://developer.nulab.com/docs/backlog/api/2/add-status/
 37 | https://developer.nulab.com/docs/backlog/api/2/update-status/
 38 | https://developer.nulab.com/docs/backlog/api/2/delete-status/
 39 | https://developer.nulab.com/docs/backlog/api/2/update-order-of-status/
 40 | https://developer.nulab.com/docs/backlog/api/2/get-issue-type-list/
 41 | https://developer.nulab.com/docs/backlog/api/2/add-issue-type/
 42 | https://developer.nulab.com/docs/backlog/api/2/update-issue-type/
 43 | https://developer.nulab.com/docs/backlog/api/2/delete-issue-type/
 44 | https://developer.nulab.com/docs/backlog/api/2/get-category-list/
 45 | https://developer.nulab.com/docs/backlog/api/2/add-category/
 46 | https://developer.nulab.com/docs/backlog/api/2/update-category/
 47 | https://developer.nulab.com/docs/backlog/api/2/delete-category/
 48 | https://developer.nulab.com/docs/backlog/api/2/get-version-milestone-list/
 49 | https://developer.nulab.com/docs/backlog/api/2/add-version-milestone/
 50 | https://developer.nulab.com/docs/backlog/api/2/update-version-milestone/
 51 | https://developer.nulab.com/docs/backlog/api/2/delete-version/
 52 | https://developer.nulab.com/docs/backlog/api/2/get-custom-field-list/
 53 | https://developer.nulab.com/docs/backlog/api/2/add-custom-field/
 54 | https://developer.nulab.com/docs/backlog/api/2/update-custom-field/
 55 | https://developer.nulab.com/docs/backlog/api/2/delete-custom-field/
 56 | https://developer.nulab.com/docs/backlog/api/2/add-list-item-for-list-type-custom-field/
 57 | https://developer.nulab.com/docs/backlog/api/2/update-list-item-for-list-type-custom-field/
 58 | https://developer.nulab.com/docs/backlog/api/2/delete-list-item-for-list-type-custom-field/
 59 | https://developer.nulab.com/docs/backlog/api/2/get-list-of-shared-files/
 60 | https://developer.nulab.com/docs/backlog/api/2/get-file/
 61 | https://developer.nulab.com/docs/backlog/api/2/get-project-disk-usage/
 62 | https://developer.nulab.com/docs/backlog/api/2/get-list-of-webhooks/
 63 | https://developer.nulab.com/docs/backlog/api/2/add-webhook/
 64 | https://developer.nulab.com/docs/backlog/api/2/get-webhook/
 65 | https://developer.nulab.com/docs/backlog/api/2/update-webhook/
 66 | https://developer.nulab.com/docs/backlog/api/2/delete-webhook/
 67 | https://developer.nulab.com/docs/backlog/api/2/get-issue-list/
 68 | https://developer.nulab.com/docs/backlog/api/2/count-issue/
 69 | https://developer.nulab.com/docs/backlog/api/2/add-issue/
 70 | https://developer.nulab.com/docs/backlog/api/2/update-issue/
 71 | https://developer.nulab.com/docs/backlog/api/2/get-issue/
 72 | https://developer.nulab.com/docs/backlog/api/2/get-comment-list/
 73 | https://developer.nulab.com/docs/backlog/api/2/add-comment/
 74 | https://developer.nulab.com/docs/backlog/api/2/count-comment/
 75 | https://developer.nulab.com/docs/backlog/api/2/get-comment/
 76 | https://developer.nulab.com/docs/backlog/api/2/delete-comment/
 77 | https://developer.nulab.com/docs/backlog/api/2/update-comment/
 78 | https://developer.nulab.com/docs/backlog/api/2/get-list-of-comment-notifications/
 79 | https://developer.nulab.com/docs/backlog/api/2/add-comment-notification/
 80 | https://developer.nulab.com/docs/backlog/api/2/get-list-of-issue-attachments/
 81 | https://developer.nulab.com/docs/backlog/api/2/get-issue-attachment/
 82 | https://developer.nulab.com/docs/backlog/api/2/delete-issue-attachment/
 83 | https://developer.nulab.com/docs/backlog/api/2/get-issue-participant-list/
 84 | https://developer.nulab.com/docs/backlog/api/2/get-list-of-linked-shared-files/
 85 | https://developer.nulab.com/docs/backlog/api/2/link-shared-files-to-issue/
 86 | https://developer.nulab.com/docs/backlog/api/2/remove-link-to-shared-file-from-issue/
 87 | https://developer.nulab.com/docs/backlog/api/2/get-wiki-page-list/
 88 | https://developer.nulab.com/docs/backlog/api/2/count-wiki-page/
 89 | https://developer.nulab.com/docs/backlog/api/2/get-wiki-page-tag-list/
 90 | https://developer.nulab.com/docs/backlog/api/2/add-wiki-page/
 91 | https://developer.nulab.com/docs/backlog/api/2/get-wiki-page/
 92 | https://developer.nulab.com/docs/backlog/api/2/update-wiki-page/
 93 | https://developer.nulab.com/docs/backlog/api/2/delete-wiki-page/
 94 | https://developer.nulab.com/docs/backlog/api/2/get-list-of-wiki-attachments/
 95 | https://developer.nulab.com/docs/backlog/api/2/attach-file-to-wiki/
 96 | https://developer.nulab.com/docs/backlog/api/2/get-wiki-page-attachment/
 97 | https://developer.nulab.com/docs/backlog/api/2/remove-wiki-attachment/
 98 | https://developer.nulab.com/docs/backlog/api/2/get-list-of-shared-files-on-wiki/
 99 | https://developer.nulab.com/docs/backlog/api/2/link-shared-files-to-wiki/
100 | https://developer.nulab.com/docs/backlog/api/2/remove-link-to-shared-file-from-wiki/
101 | https://developer.nulab.com/docs/backlog/api/2/get-wiki-page-history/
102 | https://developer.nulab.com/docs/backlog/api/2/get-wiki-page-star/
103 | https://developer.nulab.com/docs/backlog/api/2/add-star/
104 | https://developer.nulab.com/docs/backlog/api/2/get-notification/
105 | https://developer.nulab.com/docs/backlog/api/2/count-notification/
106 | https://developer.nulab.com/docs/backlog/api/2/reset-unread-notification-count/
107 | https://developer.nulab.com/docs/backlog/api/2/read-notification/
108 | https://developer.nulab.com/docs/backlog/api/2/get-list-of-git-repositories/
109 | https://developer.nulab.com/docs/backlog/api/2/get-git-repository/
110 | https://developer.nulab.com/docs/backlog/api/2/get-pull-request-list/
111 | https://developer.nulab.com/docs/backlog/api/2/get-number-of-pull-requests/
112 | https://developer.nulab.com/docs/backlog/api/2/add-pull-request/
113 | https://developer.nulab.com/docs/backlog/api/2/get-pull-request/
114 | https://developer.nulab.com/docs/backlog/api/2/update-pull-request/
115 | https://developer.nulab.com/docs/backlog/api/2/get-pull-request-comment/
116 | https://developer.nulab.com/docs/backlog/api/2/add-pull-request-comment/
117 | https://developer.nulab.com/docs/backlog/api/2/get-number-of-pull-request-comments/
118 | https://developer.nulab.com/docs/backlog/api/2/update-pull-request-comment-information/
119 | https://developer.nulab.com/docs/backlog/api/2/get-list-of-pull-request-attachment/
120 | https://developer.nulab.com/docs/backlog/api/2/download-pull-request-attachment/
121 | https://developer.nulab.com/docs/backlog/api/2/delete-pull-request-attachments/
122 | https://developer.nulab.com/docs/backlog/api/2/get-watching-list
123 | https://developer.nulab.com/docs/backlog/api/2/count-watching
124 | https://developer.nulab.com/docs/backlog/api/2/get-watching
125 | https://developer.nulab.com/docs/backlog/api/2/add-watching
126 | https://developer.nulab.com/docs/backlog/api/2/update-watching
127 | https://developer.nulab.com/docs/backlog/api/2/delete-watching
128 | https://developer.nulab.com/docs/backlog/api/2/mark-watching-as-read
129 | https://developer.nulab.com/docs/backlog/api/2/get-licence
130 | https://developer.nulab.com/docs/backlog/api/2/get-list-of-teams/
131 | https://developer.nulab.com/docs/backlog/api/2/add-team/
132 | https://developer.nulab.com/docs/backlog/api/2/get-team/
133 | https://developer.nulab.com/docs/backlog/api/2/update-team/
134 | https://developer.nulab.com/docs/backlog/api/2/delete-team/
135 | https://developer.nulab.com/docs/backlog/api/2/get-team-icon/
136 | https://developer.nulab.com/docs/backlog/api/2/get-project-team-list/
137 | https://developer.nulab.com/docs/backlog/api/2/add-project-team/
138 | https://developer.nulab.com/docs/backlog/api/2/delete-project-team/
139 | https://developer.nulab.com/docs/backlog/api/2/get-rate-limit/
```

--------------------------------------------------------------------------------
/memory-bank/progress.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Progress Status
  2 | 
  3 | ## Implemented Features
  4 | 
  5 | ### Space-related
  6 | - ✅ Retrieving space information (`get_space`)
  7 | - ✅ Retrieving user lists (`get_users`)
  8 | - ✅ Retrieving information about oneself (`get_myself`)
  9 | - ✅ Retrieving priority lists (`get_priorities`)
 10 | - ✅ Retrieving resolution lists (`get_resolutions`)
 11 | - ✅ Retrieving issue type lists (`get_issue_types`)
 12 | 
 13 | ### Project-related
 14 | - ✅ Retrieving project lists (`get_project_list`)
 15 | - ✅ Creating projects (`add_project`)
 16 | - ✅ Retrieving project information (`get_project`)
 17 | - ✅ Updating projects (`update_project`)
 18 | - ✅ Deleting projects (`delete_project`)
 19 | 
 20 | ### Issue-related
 21 | - ✅ Retrieving issue information (`get_issue`)
 22 | - ✅ Retrieving issue lists (`get_issues`)
 23 | - ✅ Retrieving issue counts (`count_issues`)
 24 | - ✅ Creating issues (`add_issue`)
 25 | - ✅ Updating issues (`update_issue`)
 26 | - ✅ Deleting issues (`delete_issue`)
 27 | 
 28 | ### Comment-related
 29 | - ✅ Retrieving issue comment lists (`get_issue_comments`)
 30 | - ✅ Adding issue comments (`add_issue_comment`)
 31 | 
 32 | ### Wiki-related
 33 | - ✅ Retrieving Wiki page lists (`get_wiki_pages`)
 34 | - ✅ Retrieving Wiki page counts (`get_wikis_count`)
 35 | - ✅ Retrieving Wiki information (`get_wiki`)
 36 | 
 37 | ### Category-related
 38 | - ✅ Retrieving category lists (`get_categories`)
 39 | 
 40 | ### Notification-related
 41 | - ✅ Retrieving notification lists (`get_notifications`)
 42 | - ✅ Retrieving notification counts (`count_notifications`)
 43 | - ✅ Resetting unread notification counts (`reset_unread_notification_count`)
 44 | - ✅ Marking notifications as read (`mark_notification_as_read`)
 45 | 
 46 | ### Git Repository-related
 47 | - ✅ Retrieving Git repository lists (`get_git_repositories`)
 48 | - ✅ Retrieving Git repository information (`get_git_repository`)
 49 | 
 50 | ### Pull Request-related
 51 | - ✅ Retrieving pull request lists (`get_pull_requests`)
 52 | - ✅ Retrieving pull request counts (`get_pull_requests_count`)
 53 | - ✅ Retrieving pull request information (`get_pull_request`)
 54 | - ✅ Creating pull requests (`add_pull_request`)
 55 | - ✅ Updating pull requests (`update_pull_request`)
 56 | - ✅ Retrieving pull request comment lists (`get_pull_request_comments`)
 57 | - ✅ Adding pull request comments (`add_pull_request_comment`)
 58 | - ✅ Updating pull request comments (`update_pull_request_comment`)
 59 | 
 60 | ### Version/Milestone-related
 61 | - ✅ Retrieving version/milestone lists (`get_version_milestone_list`)
 62 | - ✅ Adding versions/milestones (`add_version_milestone`)
 63 | - ✅ Updating versions/milestones (`update_version_milestone`)
 64 | - ✅ Deleting versions (`delete_version`)
 65 | 
 66 | ### Watch-related
 67 | - ✅ Retrieving watched item lists (`get_watching_list_items`)
 68 | - ✅ Retrieving watch counts (`get_watching_list_count`)
 69 | 
 70 | ### Infrastructure
 71 | - ✅ MCP server implementation
 72 | - ✅ Tool registration system
 73 | - ✅ Translation system
 74 | - ✅ Docker containerization
 75 | - ✅ CI/CD pipeline
 76 | 
 77 | ## Unimplemented Features
 78 | 
 79 | ### Watch-related
 80 | - ❌ Retrieving watches (`get_watching`)
 81 | - ❌ Adding watches (`add_watching`)
 82 | - ❌ Updating watches (`update_watching`)
 83 | - ❌ Deleting watches (`delete_watching`)
 84 | - ❌ Marking watches as read (`mark_watching_as_read`)
 85 | 
 86 | ### Attachment-related
 87 | - ❌ Uploading attachments (`post_attachment_file`)
 88 | - ❌ Retrieving issue attachment lists (`get_list_of_issue_attachments`)
 89 | - ❌ Retrieving issue attachments (`get_issue_attachment`)
 90 | - ❌ Deleting issue attachments (`delete_issue_attachment`)
 91 | - ❌ Retrieving pull request attachment lists (`get_list_of_pull_request_attachment`)
 92 | - ❌ Downloading pull request attachments (`download_pull_request_attachment`)
 93 | - ❌ Deleting pull request attachments (`delete_pull_request_attachments`)
 94 | 
 95 | ### Star-related
 96 | - ❌ Adding stars (`add_star`)
 97 | - ❌ Retrieving received star lists (`get_received_star_list`)
 98 | - ❌ Retrieving user received star counts (`count_user_received_stars`)
 99 | - ❌ Retrieving Wiki page stars (`get_wiki_page_star`)
100 | 
101 | ### Shared File-related
102 | - ❌ Retrieving shared file lists (`get_list_of_shared_files`)
103 | - ❌ Retrieving files (`get_file`)
104 | - ❌ Retrieving issue shared file lists (`get_list_of_linked_shared_files`)
105 | - ❌ Linking shared files to issues (`link_shared_files_to_issue`)
106 | - ❌ Removing shared file links from issues (`remove_link_to_shared_file_from_issue`)
107 | - ❌ Retrieving Wiki shared file lists (`get_list_of_shared_files_on_wiki`)
108 | - ❌ Linking shared files to Wikis (`link_shared_files_to_wiki`)
109 | - ❌ Removing shared file links from Wikis (`remove_link_to_shared_file_from_wiki`)
110 | 
111 | ### Other Features
112 | - ❌ Retrieving recent updates (`get_recent_updates`)
113 | - ❌ Retrieving space logos (`get_space_logo`)
114 | - ❌ Retrieving space notifications (`get_space_notification`)
115 | - ❌ Updating space notifications (`update_space_notification`)
116 | - ❌ Retrieving space disk usage (`get_space_disk_usage`)
117 | - ❌ Retrieving user icons (`get_user_icon`)
118 | - ❌ Retrieving user recent updates (`get_user_recent_updates`)
119 | - ❌ Retrieving recently viewed issue lists (`get_list_of_recently_viewed_issues`)
120 | - ❌ Retrieving recently viewed project lists (`get_list_of_recently_viewed_projects`)
121 | - ❌ Retrieving recently viewed Wiki lists (`get_list_of_recently_viewed_wikis`)
122 | - ❌ Retrieving project icons (`get_project_icon`)
123 | - ❌ Retrieving project recent updates (`get_project_recent_updates`)
124 | - ❌ Adding project users (`add_project_user`)
125 | - ❌ Retrieving project user lists (`get_project_user_list`)
126 | - ❌ Deleting project users (`delete_project_user`)
127 | - ❌ Adding project administrators (`add_project_administrator`)
128 | - ❌ Retrieving project administrator lists (`get_list_of_project_administrators`)
129 | - ❌ Deleting project administrators (`delete_project_administrator`)
130 | - ❌ Adding statuses (`add_status`)
131 | - ❌ Updating statuses (`update_status`)
132 | - ❌ Deleting statuses (`delete_status`)
133 | - ❌ Updating status orders (`update_order_of_status`)
134 | - ❌ Adding issue types (`add_issue_type`)
135 | - ❌ Updating issue types (`update_issue_type`)
136 | - ❌ Deleting issue types (`delete_issue_type`)
137 | - ❌ Adding categories (`add_category`)
138 | - ❌ Updating categories (`update_category`)
139 | - ❌ Deleting categories (`delete_category`)
140 | - ❌ Retrieving custom field lists (`get_custom_field_list`)
141 | - ❌ Adding custom fields (`add_custom_field`)
142 | - ❌ Updating custom fields (`update_custom_field`)
143 | - ❌ Deleting custom fields (`delete_custom_field`)
144 | - ❌ Adding list items for list type custom fields (`add_list_item_for_list_type_custom_field`)
145 | - ❌ Updating list items for list type custom fields (`update_list_item_for_list_type_custom_field`)
146 | - ❌ Deleting list items for list type custom fields (`delete_list_item_for_list_type_custom_field`)
147 | - ❌ Retrieving project disk usage (`get_project_disk_usage`)
148 | - ❌ Retrieving webhook lists (`get_list_of_webhooks`)
149 | - ❌ Adding webhooks (`add_webhook`)
150 | - ❌ Retrieving webhooks (`get_webhook`)
151 | - ❌ Updating webhooks (`update_webhook`)
152 | - ❌ Deleting webhooks (`delete_webhook`)
153 | - ❌ Retrieving comment counts (`count_comment`)
154 | - ❌ Retrieving comments (`get_comment`)
155 | - ❌ Deleting comments (`delete_comment`)
156 | - ❌ Updating comments (`update_comment`)
157 | - ❌ Retrieving comment notification lists (`get_list_of_comment_notifications`)
158 | - ❌ Adding comment notifications (`add_comment_notification`)
159 | - ❌ Retrieving issue participant lists (`get_issue_participant_list`)
160 | - ❌ Retrieving Wiki page tag lists (`get_wiki_page_tag_list`)
161 | - ❌ Adding Wiki pages (`add_wiki_page`)
162 | - ❌ Updating Wiki pages (`update_wiki_page`)
163 | - ❌ Deleting Wiki pages (`delete_wiki_page`)
164 | - ❌ Retrieving Wiki attachment lists (`get_list_of_wiki_attachments`)
165 | - ❌ Attaching files to Wikis (`attach_file_to_wiki`)
166 | - ❌ Retrieving Wiki page attachments (`get_wiki_page_attachment`)
167 | - ❌ Removing Wiki attachments (`remove_wiki_attachment`)
168 | - ❌ Retrieving Wiki page history (`get_wiki_page_history`)
169 | - ❌ Retrieving licenses (`get_licence`)
170 | - ❌ Retrieving team lists (`get_list_of_teams`)
171 | - ❌ Adding teams (`add_team`)
172 | - ❌ Retrieving teams (`get_team`)
173 | - ❌ Updating teams (`update_team`)
174 | - ❌ Deleting teams (`delete_team`)
175 | - ❌ Retrieving team icons (`get_team_icon`)
176 | - ❌ Retrieving project team lists (`get_project_team_list`)
177 | - ❌ Adding project teams (`add_project_team`)
178 | - ❌ Deleting project teams (`delete_project_team`)
179 | - ❌ Retrieving rate limits (`get_rate_limit`)
180 | 
181 | ## Current Status
182 | 
183 | Currently, the Backlog MCP Server has comprehensive functionality implemented, covering API endpoints in the following categories:
184 | 
185 | - Space information
186 | - Project management
187 | - Issue management
188 | - Comment management
189 | - Wiki management
190 | - Notification management
191 | - Git repository management
192 | - Pull request management
193 | - Watch management (partial)
194 | 
195 | This allows access to Backlog's main features from Claude, with optimizations for response size and token limits.
196 | 
197 | ## Recent Improvements
198 | 
199 | 1. **Response Optimization**
200 |    - Added GraphQL-style field selection to reduce response size
201 |    - Implemented token limiting to prevent large responses from exceeding limits
202 |    - Added streaming for large responses with automatic truncation
203 | 
204 | 2. **Error Handling**
205 |    - Enhanced error handling with categorized error types
206 |    - Improved error messages for better debugging
207 |    - Added Backlog API-specific error parsing
208 | 
209 | 3. **Documentation**
210 |    - Updated README with new features and usage examples
211 |    - Added Japanese translation of documentation
212 |    - Improved installation and configuration instructions
213 | 
214 | ## Future Plans
215 | 
216 | 1. **High Priority Unimplemented Features**
217 |    - Remaining watch-related features
218 |    - Attachment-related features
219 |    - Star-related features
220 | 
221 | 2. **Medium-term Goals**
222 |    - Custom field-related features
223 |    - Webhook-related features
224 |    - Further performance optimizations
225 | 
226 | 3. **Long-term Goals**
227 |    - Cover all Backlog API endpoints
228 |    - Advanced pagination handling
229 |    - More sophisticated error handling and recovery
230 |    - Enhanced field selection capabilities
231 | 
232 | ## Known Issues
233 | 
234 | 1. **Large Data Processing**
235 |    - While token limiting helps, pagination handling for retrieving large numbers of issues or comments could be further optimized
236 |    - Some complex nested objects may not be optimally handled by field selection
237 | 
238 | 2. **Error Handling Edge Cases**
239 |    - Some rare API error scenarios may not be handled specifically
240 |    - Error messages for certain edge cases could be improved
241 | 
242 | 3. **Permission Checking**
243 |    - Some API endpoints may be restricted by user permissions, but pre-checking is insufficient
244 |    - Better feedback for permission-related errors would be helpful
245 | 
246 | ## Evolution of Project Decisions
247 | 
248 | 1. **Tool Naming Conventions**
249 |    - Initially used Backlog API endpoint names directly, but changed to more intuitive names
250 |    - Example: `getIssue` → `get_issue`
251 | 
252 | 2. **Response Format**
253 |    - Initially returned Backlog API responses directly, but changed to a more structured format
254 |    - Returning as JSON strings made it easier for Claude to parse
255 |    - Added field selection to allow clients to request only needed fields
256 | 
257 | 3. **Multi-language Support**
258 |    - Initially only supported English, but added multi-language support through configuration files
259 |    - Provided Japanese translation files to improve usability in Japanese environments
260 |    - Implemented translation key tracking for consistency
261 | 
262 | 4. **Handler Architecture**
263 |    - Initially had simple handlers, but evolved to a composed handler pattern
264 |    - Added transformers for error handling, field picking, token limiting, and result formatting
265 |    - Implemented a pipeline pattern for response processing
266 | 
267 | 5. **Response Size Management**
268 |    - Initially returned full responses, but added field selection for targeted data retrieval
269 |    - Implemented token counting and limiting for large responses
270 |    - Added streaming for efficient processing of large responses
271 | 
```

--------------------------------------------------------------------------------
/src/types/zod/backlogOutputDefinition.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { z } from 'zod';
  2 | 
  3 | export const TextFormattingRuleSchema = z.enum(['backlog', 'markdown']);
  4 | 
  5 | export const RoleTypeSchema = z.union([
  6 |   z.nativeEnum({
  7 |     Admin: 1,
  8 |     User: 2,
  9 |     Reporter: 3,
 10 |     Viewer: 4,
 11 |     GuestReporter: 5,
 12 |     GuestViewer: 6,
 13 |   }),
 14 |   z.nativeEnum({
 15 |     Admin: 1,
 16 |     MemberOrGuest: 2,
 17 |     MemberOrGuestForAddIssues: 3,
 18 |     MemberOrGuestForViewIssues: 4,
 19 |   }),
 20 | ]);
 21 | 
 22 | export const LanguageSchema = z.union([
 23 |   z.literal('en'),
 24 |   z.literal('ja'),
 25 |   z.null(),
 26 | ]);
 27 | 
 28 | export const ActivityTypeSchema = z.nativeEnum({
 29 |   Undefined: -1,
 30 |   IssueCreated: 1,
 31 |   IssueUpdated: 2,
 32 |   IssueCommented: 3,
 33 |   IssueDeleted: 4,
 34 |   WikiCreated: 5,
 35 |   WikiUpdated: 6,
 36 |   WikiDeleted: 7,
 37 |   FileAdded: 8,
 38 |   FileUpdated: 9,
 39 |   FileDeleted: 10,
 40 |   SvnCommitted: 11,
 41 |   GitPushed: 12,
 42 |   GitRepositoryCreated: 13,
 43 |   IssueMultiUpdated: 14,
 44 |   ProjectUserAdded: 15,
 45 |   ProjectUserRemoved: 16,
 46 |   NotifyAdded: 17,
 47 |   PullRequestAdded: 18,
 48 |   PullRequestUpdated: 19,
 49 |   PullRequestCommented: 20,
 50 |   PullRequestMerged: 21,
 51 |   MilestoneCreated: 22,
 52 |   MilestoneUpdated: 23,
 53 |   MilestoneDeleted: 24,
 54 |   ProjectGroupAdded: 25,
 55 |   ProjectGroupDeleted: 26,
 56 | });
 57 | 
 58 | export const IssueTypeColorSchema = z.enum([
 59 |   '#e30000',
 60 |   '#990000',
 61 |   '#934981',
 62 |   '#814fbc',
 63 |   '#2779ca',
 64 |   '#007e9a',
 65 |   '#7ea800',
 66 |   '#ff9200',
 67 |   '#ff3265',
 68 |   '#666665',
 69 | ]);
 70 | 
 71 | export const ProjectStatusColorSchema = z.enum([
 72 |   '#ea2c00',
 73 |   '#e87758',
 74 |   '#e07b9a',
 75 |   '#868cb7',
 76 |   '#3b9dbd',
 77 |   '#4caf93',
 78 |   '#b0be3c',
 79 |   '#eda62a',
 80 |   '#f42858',
 81 |   '#393939',
 82 | ]);
 83 | 
 84 | export const CustomFieldTypeSchema = z.nativeEnum({
 85 |   Text: 1,
 86 |   TextArea: 2,
 87 |   Numeric: 3,
 88 |   Date: 4,
 89 |   SingleList: 5,
 90 |   MultipleList: 6,
 91 |   CheckBox: 7,
 92 |   Radio: 8,
 93 | });
 94 | 
 95 | export const WebhookActivityIdSchema = z.number();
 96 | 
 97 | export const UserSchema = z.object({
 98 |   id: z.number(),
 99 |   userId: z.string(),
100 |   name: z.string(),
101 |   roleType: RoleTypeSchema,
102 |   lang: LanguageSchema,
103 |   mailAddress: z.string(),
104 |   lastLoginTime: z.string(),
105 | });
106 | export const ProjectStatusSchema = z.object({
107 |   id: z.number(),
108 |   projectId: z.number(),
109 |   name: z.string(),
110 |   color: ProjectStatusColorSchema,
111 |   displayOrder: z.number(),
112 | });
113 | 
114 | export const CategorySchema = z.object({
115 |   id: z.number(),
116 |   projectId: z.number(),
117 |   name: z.string(),
118 |   displayOrder: z.number(),
119 | });
120 | 
121 | export const IssueFileInfoSchema = z.object({
122 |   id: z.number(),
123 |   name: z.string(),
124 |   size: z.number(),
125 |   createdUser: UserSchema,
126 |   created: z.string(),
127 | });
128 | 
129 | export const StarSchema = z.object({
130 |   id: z.number(),
131 |   comment: z.string().optional(),
132 |   url: z.string(),
133 |   title: z.string(),
134 |   presenter: UserSchema,
135 |   created: z.string(),
136 | });
137 | 
138 | export const IssueTypeSchema = z.object({
139 |   id: z.number(),
140 |   projectId: z.number(),
141 |   name: z.string(),
142 |   color: IssueTypeColorSchema,
143 |   displayOrder: z.number(),
144 |   templateSummary: z.string().optional(),
145 |   templateDescription: z.string().optional(),
146 | });
147 | 
148 | export const ResolutionSchema = z.object({
149 |   id: z.number(),
150 |   name: z.string(),
151 | });
152 | 
153 | export const PrioritySchema = z.object({
154 |   id: z.number(),
155 |   name: z.string(),
156 | });
157 | 
158 | export const VersionSchema = z.object({
159 |   id: z.number(),
160 |   projectId: z.number(),
161 |   name: z.string(),
162 |   description: z.string().optional(),
163 |   startDate: z.string().optional(),
164 |   releaseDueDate: z.string().optional(),
165 |   archived: z.boolean(),
166 |   displayOrder: z.number(),
167 | });
168 | 
169 | export const CustomFieldSchema = z.object({
170 |   id: z.number(),
171 |   projectId: z.number(),
172 |   typeId: CustomFieldTypeSchema,
173 |   name: z.string(),
174 |   description: z.string(),
175 |   required: z.boolean(),
176 |   applicableIssueTypes: z.array(z.number()),
177 | });
178 | 
179 | export const SharedFileSchema = z.object({
180 |   id: z.number(),
181 |   projectId: z.number(),
182 |   type: z.string(),
183 |   dir: z.string(),
184 |   name: z.string(),
185 |   size: z.number(),
186 |   createdUser: UserSchema,
187 |   created: z.string(),
188 |   updatedUser: UserSchema,
189 |   updated: z.string(),
190 | });
191 | 
192 | export const IssueSchema = z.object({
193 |   id: z.number(),
194 |   projectId: z.number(),
195 |   issueKey: z.string(),
196 |   keyId: z.number(),
197 |   issueType: IssueTypeSchema,
198 |   summary: z.string(),
199 |   description: z.string(),
200 |   resolution: ResolutionSchema.optional(),
201 |   priority: PrioritySchema,
202 |   status: ProjectStatusSchema,
203 |   assignee: UserSchema.optional(),
204 |   category: z.array(CategorySchema),
205 |   versions: z.array(VersionSchema),
206 |   milestone: z.array(VersionSchema),
207 |   startDate: z.string().optional(),
208 |   dueDate: z.string().optional(),
209 |   estimatedHours: z.number().optional(),
210 |   actualHours: z.number().optional(),
211 |   parentIssueId: z.number().optional(),
212 |   createdUser: UserSchema,
213 |   created: z.string(),
214 |   updatedUser: UserSchema,
215 |   updated: z.string(),
216 |   customFields: z.array(CustomFieldSchema),
217 |   attachments: z.array(IssueFileInfoSchema),
218 |   sharedFiles: z.array(SharedFileSchema),
219 |   stars: z.array(StarSchema),
220 | });
221 | 
222 | export const ProjectSchema = z.object({
223 |   id: z.number(),
224 |   projectKey: z.string(),
225 |   name: z.string(),
226 |   chartEnabled: z.boolean(),
227 |   useResolvedForChart: z.boolean(),
228 |   subtaskingEnabled: z.boolean(),
229 |   projectLeaderCanEditProjectLeader: z.boolean(),
230 |   useWiki: z.boolean(),
231 |   useFileSharing: z.boolean(),
232 |   useWikiTreeView: z.boolean(),
233 |   useOriginalImageSizeAtWiki: z.boolean(),
234 |   useSubversion: z.boolean(),
235 |   useGit: z.boolean(),
236 |   textFormattingRule: TextFormattingRuleSchema,
237 |   archived: z.boolean(),
238 |   displayOrder: z.number(),
239 |   useDevAttributes: z.boolean(),
240 | });
241 | 
242 | export const AttachmentInfoSchema = z.object({
243 |   id: z.number(),
244 |   type: z.string(),
245 | });
246 | export const AttributeInfoSchema = z.object({
247 |   id: z.number(),
248 |   typeId: z.number(),
249 | });
250 | 
251 | export const NotificationInfoSchema = z.object({
252 |   type: z.string(),
253 | });
254 | 
255 | export const IssueChangeLogSchema = z.object({
256 |   field: z.string(),
257 |   newValue: z.string(),
258 |   originalValue: z.string(),
259 |   attachmentInfo: AttachmentInfoSchema,
260 |   attributeInfo: AttributeInfoSchema,
261 |   notificationInfo: NotificationInfoSchema,
262 | });
263 | 
264 | export const CommentNotificationSchema = z.object({
265 |   id: z.number(),
266 |   alreadyRead: z.boolean(),
267 |   reason: z.number(),
268 |   user: UserSchema,
269 |   resourceAlreadyRead: z.boolean(),
270 | });
271 | 
272 | export const IssueCommentSchema = z.object({
273 |   id: z.number(),
274 |   projectId: z.number(),
275 |   issueId: z.number(),
276 |   content: z.string(),
277 |   changeLog: z.array(IssueChangeLogSchema),
278 |   createdUser: UserSchema,
279 |   created: z.string(),
280 |   updated: z.string(),
281 |   stars: z.array(StarSchema),
282 |   notifications: z.array(CommentNotificationSchema),
283 | });
284 | 
285 | export const PullRequestStatusSchema = z.object({
286 |   id: z.number(),
287 |   name: z.string(),
288 | });
289 | 
290 | export const PullRequestFileInfoSchema = z.object({
291 |   id: z.number(),
292 |   name: z.string(),
293 |   size: z.number(),
294 |   createdUser: UserSchema,
295 |   created: z.string(),
296 | });
297 | 
298 | export const ChangeLogSchema = z.object({
299 |   field: z.string(),
300 |   newValue: z.string(),
301 |   originalValue: z.string(),
302 | });
303 | 
304 | export const PullRequestChangeLogSchema = ChangeLogSchema;
305 | 
306 | export const PullRequestSchema = z.object({
307 |   id: z.number(),
308 |   projectId: z.number(),
309 |   repositoryId: z.number(),
310 |   number: z.number(),
311 |   summary: z.string(),
312 |   description: z.string(),
313 |   base: z.string(),
314 |   branch: z.string(),
315 |   status: PullRequestStatusSchema,
316 |   assignee: UserSchema.optional(),
317 |   issue: IssueSchema,
318 |   baseCommit: z.string().optional(),
319 |   branchCommit: z.string().optional(),
320 |   mergeCommit: z.string().optional(),
321 |   closeAt: z.string().optional(),
322 |   mergeAt: z.string().optional(),
323 |   createdUser: UserSchema,
324 |   created: z.string(),
325 |   updatedUser: UserSchema,
326 |   updated: z.string(),
327 |   attachments: z.array(PullRequestFileInfoSchema),
328 |   stars: z.array(StarSchema),
329 | });
330 | 
331 | export const PullRequestCommentSchema = z.object({
332 |   id: z.number(),
333 |   content: z.string(),
334 |   changeLog: z.array(PullRequestChangeLogSchema),
335 |   createdUser: UserSchema,
336 |   created: z.string(),
337 |   updated: z.string(),
338 |   stars: z.array(StarSchema),
339 |   notifications: z.array(CommentNotificationSchema),
340 | });
341 | 
342 | export const WikiFileInfoSchema = z.object({
343 |   id: z.number(),
344 |   name: z.string(),
345 |   size: z.number(),
346 |   createdUser: UserSchema,
347 |   created: z.string(),
348 | });
349 | 
350 | export const TagSchema = z.object({
351 |   id: z.number(),
352 |   name: z.string(),
353 | });
354 | 
355 | export const WikiSchema = z.object({
356 |   id: z.number(),
357 |   projectId: z.number(),
358 |   name: z.string(),
359 |   content: z.string(),
360 |   tags: z.array(TagSchema),
361 |   attachments: z.array(WikiFileInfoSchema),
362 |   sharedFiles: z.array(SharedFileSchema),
363 |   stars: z.array(StarSchema),
364 |   createdUser: UserSchema,
365 |   created: z.string(),
366 |   updatedUser: UserSchema,
367 |   updated: z.string(),
368 | });
369 | 
370 | export const IssueCountSchema = z.object({
371 |   count: z.number(),
372 | });
373 | 
374 | export const WatchingListItemSchema = z.object({
375 |   id: z.number(),
376 |   resourceAlreadyRead: z.boolean(),
377 |   note: z.string(),
378 |   type: z.string(),
379 |   issue: IssueSchema,
380 |   lastContentUpdated: z.string(),
381 |   created: z.string(),
382 |   updated: z.string(),
383 | });
384 | 
385 | export const GitRepositorySchema = z.object({
386 |   id: z.number(),
387 |   projectId: z.number(),
388 |   name: z.string(),
389 |   description: z.string(),
390 |   hookUrl: z.string().optional(),
391 |   httpUrl: z.string(),
392 |   sshUrl: z.string(),
393 |   displayOrder: z.number(),
394 |   pushedAt: z.string().optional(),
395 |   createdUser: UserSchema,
396 |   created: z.string(),
397 |   updatedUser: UserSchema,
398 |   updated: z.string(),
399 | });
400 | 
401 | export const NotificationSchema = z.object({
402 |   id: z.number(),
403 |   alreadyRead: z.boolean(),
404 |   reason: z.number(),
405 |   resourceAlreadyRead: z.boolean(),
406 |   project: ProjectSchema.optional(),
407 |   issue: IssueSchema.optional(),
408 |   comment: IssueCommentSchema.optional(),
409 |   pullRequest: PullRequestSchema.optional(),
410 |   pullRequestComment: PullRequestCommentSchema.optional(),
411 |   sender: UserSchema,
412 |   created: z.string(),
413 | });
414 | 
415 | export const NotificationCountSchema = z.object({
416 |   count: z.number(),
417 | });
418 | 
419 | export const PullRequestCountSchema = z.object({
420 |   count: z.number(),
421 | });
422 | 
423 | export const SpaceSchema = z.object({
424 |   spaceKey: z.string(),
425 |   name: z.string(),
426 |   ownerId: z.number(),
427 |   lang: z.string(),
428 |   timezone: z.string(),
429 |   reportSendTime: z.string(),
430 |   textFormattingRule: TextFormattingRuleSchema,
431 |   created: z.string(),
432 |   updated: z.string(),
433 | });
434 | 
435 | export const WatchingListCountSchema = z.object({
436 |   count: z.number(),
437 | });
438 | 
439 | export const WikiListItemSchema = z.object({
440 |   id: z.number(),
441 |   projectId: z.number(),
442 |   name: z.string(),
443 |   tags: z.array(TagSchema),
444 |   createdUser: UserSchema,
445 |   created: z.string(),
446 |   updatedUser: UserSchema,
447 |   updated: z.string(),
448 | });
449 | 
450 | export const WikiCountSchema = z.object({
451 |   count: z.number(),
452 | });
453 | 
454 | export const DocumentSchema = z.object({
455 |   id: z.number(),
456 |   projectId: z.number(),
457 |   name: z.string(),
458 |   content: z.string(),
459 |   createdUser: UserSchema,
460 |   created: z.string(),
461 |   updatedUser: UserSchema,
462 |   updated: z.string(),
463 | });
464 | 
465 | export const DocumentAttachmentSchema = z.object({
466 |   filename: z.string(),
467 |   body: z.any(),
468 |   url: z.string(),
469 | });
470 | 
471 | export const DocumentTagSchema = z.object({
472 |   id: z.number(),
473 |   name: z.string(),
474 | });
475 | 
476 | export const DocumentFileInfoSchema = z.object({
477 |   id: z.number(),
478 |   name: z.string(),
479 |   size: z.number(),
480 |   createdUser: UserSchema,
481 |   created: z.string(),
482 | });
483 | 
484 | export const DocumentItemSchema = z.object({
485 |   id: z.string(),
486 |   projectId: z.number(),
487 |   title: z.string(),
488 |   plain: z.string(),
489 |   json: z.string(),
490 |   statusId: z.number(),
491 |   emoji: z.string().nullable(),
492 |   attachments: z.array(DocumentFileInfoSchema),
493 |   tags: z.array(DocumentTagSchema),
494 |   createdUser: UserSchema,
495 |   created: z.string(),
496 |   updatedUser: UserSchema,
497 |   updated: z.string(),
498 | });
499 | 
500 | export type DocumentTreeNode = {
501 |   id: string;
502 |   name?: string;
503 |   children: DocumentTreeNode[];
504 |   statusId?: number;
505 |   emoji?: string;
506 |   emojiType?: string;
507 |   updated?: string;
508 | };
509 | 
510 | export const DocumentTreeNodeSchema: z.ZodType<DocumentTreeNode> = z.lazy(() =>
511 |   z.object({
512 |     id: z.string(),
513 |     name: z.string().optional(),
514 |     children: z.array(DocumentTreeNodeSchema),
515 |     statusId: z.number().optional(),
516 |     emoji: z.string().optional(),
517 |     emojiType: z.string().optional(),
518 |     updated: z.string().optional(),
519 |   })
520 | );
521 | 
522 | export const ActiveTrashTreeSchema = z.object({
523 |   id: z.string(),
524 |   children: z.array(DocumentTreeNodeSchema),
525 | });
526 | 
527 | export const DocumentTreeFullSchema: z.ZodRawShape = {
528 |   projectId: z.number(),
529 |   activeTree: ActiveTrashTreeSchema.optional(),
530 |   trashTree: ActiveTrashTreeSchema.optional(),
531 | };
532 | 
533 | export const DocumentTreeFullSchemaZ = z.object(DocumentTreeFullSchema);
534 | 
```
Page 3/3FirstPrevNextLast