backlog-mcp-server 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +20 -10
- package/README.md +20 -10
- package/build/auth/backlogAuthContext.d.ts +2 -0
- package/build/auth/backlogOAuthClient.d.ts +10 -0
- package/build/auth/backlogOAuthConfig.d.ts +9 -0
- package/build/auth/bearerAuthMiddleware.d.ts +4 -0
- package/build/auth/oauthRoutes.d.ts +4 -0
- package/build/auth/tokenStore.d.ts +62 -0
- package/build/backlog/backlogErrorHandler.d.ts +2 -0
- package/build/backlog/customFields.d.ts +29 -0
- package/build/backlog/parseBacklogAPIError.d.ts +8 -0
- package/build/createBacklogMcpServer.d.ts +29 -0
- package/build/createBacklogMcpServer.js +9 -4
- package/build/createDescriptionHelper.d.ts +18 -0
- package/build/createDescriptionHelper.js +24 -0
- package/build/handlers/builders/composeToolHandler.d.ts +31 -0
- package/build/handlers/builders/composeToolHandler.js +21 -9
- package/build/handlers/transformers/wrapWithErrorHandling.d.ts +2 -0
- package/build/handlers/transformers/wrapWithFieldPicking.d.ts +4 -0
- package/build/handlers/transformers/wrapWithOrganizationContext.d.ts +3 -0
- package/build/handlers/transformers/wrapWithTokenLimit.d.ts +2 -0
- package/build/handlers/transformers/wrapWithToolResult.d.ts +6 -0
- package/build/httpMcpServer.d.ts +22 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +29 -11
- package/build/lib.d.ts +25 -0
- package/build/lib.js +20 -0
- package/build/loadDescriptionOverrides.d.ts +17 -0
- package/build/loadDescriptionOverrides.js +24 -0
- package/build/registerTools.d.ts +5 -0
- package/build/registerTools.js +7 -6
- package/build/tools/addDocument.d.ts +15 -0
- package/build/tools/addIssue.d.ts +30 -0
- package/build/tools/addIssueComment.d.ts +14 -0
- package/build/tools/addProject.d.ts +18 -0
- package/build/tools/addPullRequest.d.ts +20 -0
- package/build/tools/addPullRequestComment.d.ts +16 -0
- package/build/tools/addRelatedIssue.d.ts +12 -0
- package/build/tools/addVersionMilestone.d.ts +15 -0
- package/build/tools/addWatching.d.ts +11 -0
- package/build/tools/addWiki.d.ts +13 -0
- package/build/tools/countIssues.d.ts +48 -0
- package/build/tools/deleteIssue.d.ts +11 -0
- package/build/tools/deleteProject.d.ts +11 -0
- package/build/tools/deleteVersion.d.ts +12 -0
- package/build/tools/deleteWatching.d.ts +10 -0
- package/build/tools/dynamicTools/organizations.d.ts +6 -0
- package/build/tools/dynamicTools/toolsets.d.ts +15 -0
- package/build/tools/getCategories.d.ts +11 -0
- package/build/tools/getCustomFields.d.ts +12 -0
- package/build/tools/getDocument.d.ts +10 -0
- package/build/tools/getDocumentTree.d.ts +10 -0
- package/build/tools/getDocuments.d.ts +11 -0
- package/build/tools/getGitRepositories.d.ts +11 -0
- package/build/tools/getGitRepository.d.ts +13 -0
- package/build/tools/getIssue.d.ts +11 -0
- package/build/tools/getIssueComments.d.ts +18 -0
- package/build/tools/getIssueTypes.d.ts +11 -0
- package/build/tools/getIssues.d.ts +75 -0
- package/build/tools/getMyself.d.ts +7 -0
- package/build/tools/getNotifications.d.ts +16 -0
- package/build/tools/getNotificationsCount.d.ts +11 -0
- package/build/tools/getPriorities.d.ts +7 -0
- package/build/tools/getProject.d.ts +11 -0
- package/build/tools/getProjectList.d.ts +11 -0
- package/build/tools/getProjectUsers.d.ts +11 -0
- package/build/tools/getPullRequest.d.ts +14 -0
- package/build/tools/getPullRequestComments.d.ts +21 -0
- package/build/tools/getPullRequests.d.ts +19 -0
- package/build/tools/getPullRequestsCount.d.ts +17 -0
- package/build/tools/getRelatedIssues.d.ts +11 -0
- package/build/tools/getResolutions.d.ts +7 -0
- package/build/tools/getSpace.d.ts +7 -0
- package/build/tools/getSpaceActivities.d.ts +45 -0
- package/build/tools/getUserRecentUpdates.d.ts +46 -0
- package/build/tools/getUserStarsCount.d.ts +12 -0
- package/build/tools/getUsers.d.ts +7 -0
- package/build/tools/getVersionMilestoneList.d.ts +11 -0
- package/build/tools/getWatchingListCount.d.ts +10 -0
- package/build/tools/getWatchingListItems.d.ts +10 -0
- package/build/tools/getWiki.d.ts +10 -0
- package/build/tools/getWikiPages.d.ts +12 -0
- package/build/tools/getWikisCount.d.ts +11 -0
- package/build/tools/markNotificationAsRead.d.ts +13 -0
- package/build/tools/markWatchingAsRead.d.ts +13 -0
- package/build/tools/removeRelatedIssue.d.ts +12 -0
- package/build/tools/resetUnreadNotificationCount.d.ts +7 -0
- package/build/tools/shared/customFieldFiltersSchema.d.ts +21 -0
- package/build/tools/tools.d.ts +4 -0
- package/build/tools/updateIssue.d.ts +34 -0
- package/build/tools/updateIssueComment.d.ts +13 -0
- package/build/tools/updateProject.d.ts +21 -0
- package/build/tools/updatePullRequest.d.ts +20 -0
- package/build/tools/updatePullRequestComment.d.ts +16 -0
- package/build/tools/updateVersionMilestone.d.ts +17 -0
- package/build/tools/updateWatching.d.ts +11 -0
- package/build/tools/updateWiki.d.ts +13 -0
- package/build/types/mcp.d.ts +10 -0
- package/build/types/result.d.ts +10 -0
- package/build/types/tool.d.ts +24 -0
- package/build/types/toolsets.d.ts +16 -0
- package/build/types/zod/backlogOutputDefinition.d.ts +3334 -0
- package/build/types/zod/backlogOutputDefinition.js +0 -16
- package/build/utils/backlogClientRegistry.d.ts +28 -0
- package/build/utils/backlogClientRegistry.js +4 -0
- package/build/utils/backlogOrganizationContext.d.ts +2 -0
- package/build/utils/generateFieldsDescription.d.ts +5 -0
- package/build/utils/logger.d.ts +2 -0
- package/build/utils/logger.js +27 -14
- package/build/utils/resolveIdOrKey.d.ts +18 -0
- package/build/utils/runToolSafely.d.ts +6 -0
- package/build/utils/tokenCounter.d.ts +1 -0
- package/build/utils/toolRegistrar.d.ts +5 -0
- package/build/utils/toolsetUtils.d.ts +6 -0
- package/build/utils/toolsetUtils.js +0 -20
- package/build/utils/wrapServerWithToolRegistry.d.ts +6 -0
- package/package.json +12 -3
- package/build/createTranslationHelper.js +0 -28
package/README.ja.md
CHANGED
|
@@ -303,10 +303,14 @@ PROJECT-KEYプロジェクトの「repo-name」リポジトリで、ブランチ
|
|
|
303
303
|
私がウォッチしているすべてのアイテムを表示してください。
|
|
304
304
|
```
|
|
305
305
|
|
|
306
|
-
###
|
|
306
|
+
### ツール説明のオーバーライド
|
|
307
307
|
|
|
308
308
|
**ホームディレクトリ** に `.backlog-mcp-serverrc.json` ファイルを作成することで、ツールの説明をオーバーライドできます。
|
|
309
309
|
|
|
310
|
+
これらの文字列のほとんどは、モデルがどのツールを呼ぶか、引数に何を入れるかを判断するために読むツール説明・パラメータ説明です。したがってオーバーライドは、似た2つのツールを区別させたり、チームの運用ルールを添えたりしてツール選択を誘導するための手段であり、応答の言語を変えるためのものではありません。モデルは説明が何語で書かれていても、質問された言語で応答します。
|
|
311
|
+
|
|
312
|
+
一部のキーは、これとは別にバリデーションエラーのメッセージです(例:`PROJECT_ID_OR_KEY_REQUIRED`)。呼び出しが弾かれたときにツールの実行結果として返るため、モデルの応答を経由してユーザーの目に触れることがあります。
|
|
313
|
+
|
|
310
314
|
ファイルには、ツール名をキーとし、新しい説明を値とするJSONオブジェクトを含める必要があります。
|
|
311
315
|
例:
|
|
312
316
|
|
|
@@ -321,7 +325,9 @@ PROJECT-KEYプロジェクトの「repo-name」リポジトリで、ブランチ
|
|
|
321
325
|
|
|
322
326
|
1. 環境変数(例:`BACKLOG_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION`)
|
|
323
327
|
2. `.backlog-mcp-serverrc.json` 内のエントリ - サポートされる設定ファイル形式:.json、.yaml、.yml
|
|
324
|
-
3.
|
|
328
|
+
3. 組み込みのデフォルト値
|
|
329
|
+
|
|
330
|
+
空文字列または文字列以外の値は、どの段階でも無視され、組み込みのデフォルト値が使われます。
|
|
325
331
|
|
|
326
332
|
サンプル設定:
|
|
327
333
|
|
|
@@ -351,22 +357,24 @@ PROJECT-KEYプロジェクトの「repo-name」リポジトリで、ブランチ
|
|
|
351
357
|
}
|
|
352
358
|
```
|
|
353
359
|
|
|
354
|
-
###
|
|
360
|
+
### 現在の説明のエクスポート
|
|
355
361
|
|
|
356
|
-
`--export-translations`
|
|
362
|
+
`--export-descriptions` フラグを指定してバイナリを実行することで、現在の説明(オーバーライドを含む)をエクスポートできます。このフラグは以前 `--export-translations` という名前でした。旧名も当面は動作しますが、非推奨の警告を出力し、将来のリリースで削除されます。
|
|
357
363
|
|
|
358
|
-
|
|
364
|
+
ツール一覧の構築時に解決されるすべてのキーと現在の値が、行ったカスタマイズを含めて標準出力に出力されます。ツール説明・パラメータ説明はすべて含まれるため、キー名を調べる用途にはこれを使ってください。
|
|
365
|
+
|
|
366
|
+
バリデーションエラーのメッセージは含まれません。これらのキーは、実際に呼び出しが弾かれたときにはじめて解決されるためです。オーバーライド自体は同じ規則で可能ですが、キー名はソースを読んで調べる必要があります。
|
|
359
367
|
|
|
360
368
|
例:
|
|
361
369
|
|
|
362
370
|
```bash
|
|
363
|
-
docker run -i --rm ghcr.io/nulab/backlog-mcp-server node build/index.js --export-
|
|
371
|
+
docker run -i --rm ghcr.io/nulab/backlog-mcp-server node build/index.js --export-descriptions
|
|
364
372
|
```
|
|
365
373
|
|
|
366
374
|
または
|
|
367
375
|
|
|
368
376
|
```bash
|
|
369
|
-
npx github:nulab/backlog-mcp-server --export-
|
|
377
|
+
npx github:nulab/backlog-mcp-server --export-descriptions
|
|
370
378
|
```
|
|
371
379
|
|
|
372
380
|
### 環境変数の使用
|
|
@@ -537,7 +545,7 @@ pnpm test
|
|
|
537
545
|
|
|
538
546
|
サーバーはいくつかのコマンドラインオプションをサポートしています:
|
|
539
547
|
|
|
540
|
-
- `--export-
|
|
548
|
+
- `--export-descriptions`: ツール一覧の構築時に解決される説明キーと値をエクスポート。旧名は `--export-translations` で、非推奨エイリアスとして当面動作しますが、将来のリリースで削除されます
|
|
541
549
|
- `--optimize-response`: GraphQLスタイルのフィールド選択を有効にする
|
|
542
550
|
- `--max-tokens=NUMBER`: レスポンスの最大トークン制限を設定
|
|
543
551
|
- `--prefix=STRING`: すべてのツール名に付加するオプションの文字列プレフィックス(デフォルト:"")
|
|
@@ -596,7 +604,9 @@ BACKLOG_API_KEY=your-api-key
|
|
|
596
604
|
|
|
597
605
|
### ツールの使い方
|
|
598
606
|
|
|
599
|
-
|
|
607
|
+
マルチ組織用の環境変数が設定されている場合、通常のツールはすべて任意の`organization`入力フィールドを受け付けます。指定した場合、そのBacklog組織に対してツールが実行されます。
|
|
608
|
+
|
|
609
|
+
単一組織モードでは、ルーティング先が1つしかないためこのフィールドは公開されません。これにより`tools/list`のレスポンスから約8KBのスキーマが削減されます。
|
|
600
610
|
|
|
601
611
|
例:
|
|
602
612
|
|
|
@@ -614,7 +624,7 @@ BACKLOG_API_KEY=your-api-key
|
|
|
614
624
|
|
|
615
625
|
### 組織一覧の確認
|
|
616
626
|
|
|
617
|
-
|
|
627
|
+
マルチ組織モードでは、サーバーは `list_organizations` ツールを提供しており、設定済みの組織名、ドメイン、デフォルト組織かどうかを返します。単一組織モードでは登録されません。
|
|
618
628
|
|
|
619
629
|
レスポンス例:
|
|
620
630
|
|
package/README.md
CHANGED
|
@@ -418,10 +418,14 @@ Create a new pull request from branch "feature/new-feature" to "main" in the rep
|
|
|
418
418
|
Show me all items I'm watching
|
|
419
419
|
```
|
|
420
420
|
|
|
421
|
-
###
|
|
421
|
+
### Overriding Tool Descriptions
|
|
422
422
|
|
|
423
423
|
You can override the descriptions of tools by creating a `.backlog-mcp-serverrc.json` file in your **home directory**.
|
|
424
424
|
|
|
425
|
+
Almost all of these strings are the tool and parameter descriptions the model reads when it decides which tool to call and how to fill in its arguments, so overriding them is a way to steer tool selection — for example to disambiguate two similar tools, or to add a rule your team follows — rather than a way to change the language of the answers you get. The model answers in whatever language you ask in, regardless of the language these descriptions are written in.
|
|
426
|
+
|
|
427
|
+
A small number of keys are validation error messages instead (for example `PROJECT_ID_OR_KEY_REQUIRED`). Those are returned in the tool result when a call is rejected, so they can reach you by way of the model's reply.
|
|
428
|
+
|
|
425
429
|
The file should contain a JSON object with the tool names as keys and the new descriptions as values.
|
|
426
430
|
For example:
|
|
427
431
|
|
|
@@ -436,7 +440,9 @@ When the server starts, it determines the final description for each tool based
|
|
|
436
440
|
|
|
437
441
|
1. Environment variables (e.g., `BACKLOG_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION`)
|
|
438
442
|
2. Entries in `.backlog-mcp-serverrc.json` - Supported configuration file formats: .json, .yaml, .yml
|
|
439
|
-
3. Built-in
|
|
443
|
+
3. Built-in defaults
|
|
444
|
+
|
|
445
|
+
Empty or non-string values are ignored at every level, and the built-in default is used instead.
|
|
440
446
|
|
|
441
447
|
Sample config:
|
|
442
448
|
|
|
@@ -466,22 +472,24 @@ Sample config:
|
|
|
466
472
|
}
|
|
467
473
|
```
|
|
468
474
|
|
|
469
|
-
### Exporting Current
|
|
475
|
+
### Exporting Current Descriptions
|
|
470
476
|
|
|
471
|
-
You can export the current
|
|
477
|
+
You can export the current descriptions (including any overrides) by running the binary with the `--export-descriptions` flag. This flag was previously called `--export-translations`; the old name still works but prints a deprecation notice and will be removed in a future release.
|
|
472
478
|
|
|
473
|
-
This
|
|
479
|
+
This prints every key that is resolved while the tool list is built, with its current value, including any customizations you have made. That covers all tool and parameter descriptions, and it is the practical way to discover key names.
|
|
480
|
+
|
|
481
|
+
It does not cover the validation error messages, because those keys are only resolved when a call is actually rejected. They are still overridable by the same rules; you just have to read them out of the source.
|
|
474
482
|
|
|
475
483
|
Example:
|
|
476
484
|
|
|
477
485
|
```bash
|
|
478
|
-
docker run -i --rm ghcr.io/nulab/backlog-mcp-server node build/index.js --export-
|
|
486
|
+
docker run -i --rm ghcr.io/nulab/backlog-mcp-server node build/index.js --export-descriptions
|
|
479
487
|
```
|
|
480
488
|
|
|
481
489
|
or
|
|
482
490
|
|
|
483
491
|
```bash
|
|
484
|
-
npx github:nulab/backlog-mcp-server --export-
|
|
492
|
+
npx github:nulab/backlog-mcp-server --export-descriptions
|
|
485
493
|
```
|
|
486
494
|
|
|
487
495
|
### Using Environment Variables
|
|
@@ -654,7 +662,7 @@ The server supports several command line options:
|
|
|
654
662
|
- `--http-json-response`: Prefer JSON responses over SSE. Applies to `2026-07-28` clients only; the backward-compatible `2025-11-25` path is served with the SDK's default response shaping.
|
|
655
663
|
- `--http-allowed-hosts`: Comma-separated allowed `Host` hostnames (port-agnostic). Needed when binding to all interfaces, or on a loopback bind behind a reverse proxy.
|
|
656
664
|
- `--http-allowed-origins`: Comma-separated allowed `Origin` hostnames for browser-based clients. Defaults to the localhost set on a bare loopback bind, and to no `Origin` check otherwise.
|
|
657
|
-
- `--export-
|
|
665
|
+
- `--export-descriptions`: Export the description keys and values resolved when building the tool list. Was named `--export-translations`; that spelling still works as a deprecated alias and will be removed in a future release
|
|
658
666
|
- `--optimize-response`: Enable GraphQL-style field selection
|
|
659
667
|
- `--max-tokens=NUMBER`: Set maximum token limit for responses
|
|
660
668
|
- `--prefix=STRING`: Optional string prefix to prepend to all tool names (default: "")
|
|
@@ -715,7 +723,9 @@ BACKLOG_API_KEY=your-api-key
|
|
|
715
723
|
|
|
716
724
|
### Tool Usage
|
|
717
725
|
|
|
718
|
-
|
|
726
|
+
When multi-organization env vars are configured, all normal tools accept an optional `organization` input field. When provided, the tool call is routed to that Backlog organization.
|
|
727
|
+
|
|
728
|
+
In single-organization mode the field is not published, since there would be only one organization to route to. Omitting it keeps roughly 8 KB of tool schema out of every `tools/list` response.
|
|
719
729
|
|
|
720
730
|
Examples:
|
|
721
731
|
|
|
@@ -733,7 +743,7 @@ If `organization` is omitted:
|
|
|
733
743
|
|
|
734
744
|
### Organization Discovery
|
|
735
745
|
|
|
736
|
-
|
|
746
|
+
In multi-organization mode the server provides a `list_organizations` tool that returns the configured organization names, their domains, and which one is the default. It is not registered in single-organization mode.
|
|
737
747
|
|
|
738
748
|
Example response:
|
|
739
749
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BacklogOAuthConfig } from './backlogOAuthConfig.js';
|
|
2
|
+
import type { BacklogTokenData } from './tokenStore.js';
|
|
3
|
+
export declare function buildBacklogAuthorizationUrl(config: BacklogOAuthConfig, redirectUri: string, state: string): string;
|
|
4
|
+
export declare function exchangeBacklogCode(config: BacklogOAuthConfig, code: string, redirectUri: string): Promise<BacklogTokenData>;
|
|
5
|
+
export declare function refreshBacklogToken(config: BacklogOAuthConfig, refreshToken: string): Promise<BacklogTokenData>;
|
|
6
|
+
export declare function verifyBacklogToken(domain: string, accessToken: string): Promise<{
|
|
7
|
+
id: number;
|
|
8
|
+
userId: string;
|
|
9
|
+
name: string;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type BacklogOAuthConfig = {
|
|
2
|
+
clientId: string;
|
|
3
|
+
clientSecret: string;
|
|
4
|
+
backlogDomain: string;
|
|
5
|
+
serverBaseUrl: string;
|
|
6
|
+
};
|
|
7
|
+
type Environment = Record<string, string | undefined>;
|
|
8
|
+
export declare function getBacklogOAuthConfig(env?: Environment): BacklogOAuthConfig | undefined;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { MiddlewareHandler } from 'hono';
|
|
2
|
+
import type { BacklogOAuthConfig } from './backlogOAuthConfig.js';
|
|
3
|
+
import type { TokenStore } from './tokenStore.js';
|
|
4
|
+
export declare function createBearerAuthMiddleware(store: TokenStore, config: BacklogOAuthConfig, mcpPath: string): MiddlewareHandler;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { AuthInfo } from '@modelcontextprotocol/server';
|
|
2
|
+
export type BacklogTokenData = {
|
|
3
|
+
access_token: string;
|
|
4
|
+
token_type: string;
|
|
5
|
+
expires_in: number;
|
|
6
|
+
refresh_token: string;
|
|
7
|
+
};
|
|
8
|
+
export type OAuthClientInfo = {
|
|
9
|
+
client_id: string;
|
|
10
|
+
client_secret?: string;
|
|
11
|
+
client_id_issued_at: number;
|
|
12
|
+
client_secret_expires_at: number;
|
|
13
|
+
redirect_uris: string[];
|
|
14
|
+
client_name?: string;
|
|
15
|
+
token_endpoint_auth_method?: string;
|
|
16
|
+
grant_types?: string[];
|
|
17
|
+
response_types?: string[];
|
|
18
|
+
};
|
|
19
|
+
type PendingAuthorization = {
|
|
20
|
+
mcpClientId: string;
|
|
21
|
+
codeChallenge: string;
|
|
22
|
+
redirectUri: string;
|
|
23
|
+
resource?: string;
|
|
24
|
+
scopes: string[];
|
|
25
|
+
state?: string;
|
|
26
|
+
createdAt: number;
|
|
27
|
+
};
|
|
28
|
+
type AuthCodeEntry = {
|
|
29
|
+
mcpClientId: string;
|
|
30
|
+
backlogTokens: BacklogTokenData;
|
|
31
|
+
codeChallenge: string;
|
|
32
|
+
redirectUri: string;
|
|
33
|
+
resource?: string;
|
|
34
|
+
expiresAt: number;
|
|
35
|
+
};
|
|
36
|
+
export type McpTokenEntry = {
|
|
37
|
+
backlogAccessToken: string;
|
|
38
|
+
clientId: string;
|
|
39
|
+
expiresAt: number;
|
|
40
|
+
};
|
|
41
|
+
type McpRefreshEntry = {
|
|
42
|
+
backlogRefreshToken: string;
|
|
43
|
+
clientId: string;
|
|
44
|
+
expiresAt: number;
|
|
45
|
+
};
|
|
46
|
+
export type TokenStore = ReturnType<typeof createTokenStore>;
|
|
47
|
+
export declare function createTokenStore(): {
|
|
48
|
+
storePendingAuth(backlogState: string, pending: PendingAuthorization): void;
|
|
49
|
+
consumePendingAuth(backlogState: string): PendingAuthorization | undefined;
|
|
50
|
+
storeAuthCode(code: string, entry: AuthCodeEntry): void;
|
|
51
|
+
consumeAuthCode(code: string): AuthCodeEntry | undefined;
|
|
52
|
+
getClient(clientId: string): OAuthClientInfo | undefined;
|
|
53
|
+
registerClient(client: OAuthClientInfo): boolean;
|
|
54
|
+
getCachedVerification(token: string): AuthInfo | undefined;
|
|
55
|
+
cacheVerification(token: string, authInfo: AuthInfo, ttlMs: number): void;
|
|
56
|
+
storeMcpToken(mcpToken: string, entry: McpTokenEntry): void;
|
|
57
|
+
getMcpToken(mcpToken: string): McpTokenEntry | undefined;
|
|
58
|
+
storeMcpRefreshToken(mcpRefreshToken: string, entry: McpRefreshEntry): void;
|
|
59
|
+
consumeMcpRefreshToken(mcpRefreshToken: string): McpRefreshEntry | undefined;
|
|
60
|
+
cleanup(): void;
|
|
61
|
+
};
|
|
62
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type CustomFieldInput = {
|
|
2
|
+
id: number;
|
|
3
|
+
value?: string | number | string[] | number[];
|
|
4
|
+
otherValue?: string;
|
|
5
|
+
};
|
|
6
|
+
export type CustomFieldFilterInput = {
|
|
7
|
+
id: number;
|
|
8
|
+
type: 'text';
|
|
9
|
+
value: string;
|
|
10
|
+
} | {
|
|
11
|
+
id: number;
|
|
12
|
+
type: 'numeric';
|
|
13
|
+
min?: number;
|
|
14
|
+
max?: number;
|
|
15
|
+
} | {
|
|
16
|
+
id: number;
|
|
17
|
+
type: 'date';
|
|
18
|
+
min?: string;
|
|
19
|
+
max?: string;
|
|
20
|
+
} | {
|
|
21
|
+
id: number;
|
|
22
|
+
type: 'list';
|
|
23
|
+
value: number | number[];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Converts Backlog-style customFields array into proper payload format
|
|
27
|
+
*/
|
|
28
|
+
export declare function customFieldsToPayload(customFields: CustomFieldInput[] | undefined): Record<string, string | number | string[] | number[] | undefined>;
|
|
29
|
+
export declare function customFieldFiltersToPayload(customFields: CustomFieldFilterInput[] | undefined): Record<string, string | number | number[] | undefined>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type ParsedBacklogAPIError = {
|
|
2
|
+
type: 'BacklogAuthError' | 'BacklogApiError' | 'UnexpectedError' | 'UnknownError';
|
|
3
|
+
message: string;
|
|
4
|
+
status?: number;
|
|
5
|
+
code?: number;
|
|
6
|
+
url?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function parseBacklogAPIError(err: unknown): ParsedBacklogAPIError;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Backlog } from 'backlog-js';
|
|
2
|
+
import type { DescriptionHelper } from './createDescriptionHelper.js';
|
|
3
|
+
import type { MCPOptions } from './types/mcp.js';
|
|
4
|
+
import type { ToolsetGroup } from './types/toolsets.js';
|
|
5
|
+
import type { BacklogClientRegistry } from './utils/backlogClientRegistry.js';
|
|
6
|
+
import { type BacklogMCPServer } from './utils/wrapServerWithToolRegistry.js';
|
|
7
|
+
export type CreateBacklogMcpServerConfig = {
|
|
8
|
+
version: string;
|
|
9
|
+
useFields: boolean;
|
|
10
|
+
backlog: Backlog;
|
|
11
|
+
clientRegistry: BacklogClientRegistry;
|
|
12
|
+
descriptionHelper: DescriptionHelper;
|
|
13
|
+
enabledToolsets: string[];
|
|
14
|
+
mcpOption: MCPOptions;
|
|
15
|
+
dynamicToolsets: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* A toolset group to register from, instead of building one from
|
|
18
|
+
* `enabledToolsets`. Callers that produce many servers from one factory pass
|
|
19
|
+
* a single shared group: `enable_toolset` mutates it, and under the stateless
|
|
20
|
+
* HTTP model — one server per request — a group built per server would throw
|
|
21
|
+
* that mutation away the moment the request ends.
|
|
22
|
+
*/
|
|
23
|
+
toolsetGroup?: ToolsetGroup;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Builds a fresh MCP server instance with all Backlog tools registered.
|
|
27
|
+
* Used once per stdio connection; one instance per HTTP request for Streamable HTTP.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createBacklogMcpServer({ version, useFields, backlog, clientRegistry, descriptionHelper, enabledToolsets, mcpOption, dynamicToolsets, toolsetGroup: sharedToolsetGroup, }: CreateBacklogMcpServerConfig): BacklogMCPServer;
|
|
@@ -17,19 +17,24 @@ const TOOL_LIST_CACHE_HINT = {
|
|
|
17
17
|
* Builds a fresh MCP server instance with all Backlog tools registered.
|
|
18
18
|
* Used once per stdio connection; one instance per HTTP request for Streamable HTTP.
|
|
19
19
|
*/
|
|
20
|
-
export function createBacklogMcpServer({ version, useFields, backlog, clientRegistry,
|
|
20
|
+
export function createBacklogMcpServer({ version, useFields, backlog, clientRegistry, descriptionHelper, enabledToolsets, mcpOption, dynamicToolsets, toolsetGroup: sharedToolsetGroup, }) {
|
|
21
21
|
const server = wrapServerWithToolRegistry(new McpServer({
|
|
22
22
|
name: 'backlog',
|
|
23
23
|
title: useFields ? 'backlog (field selection enabled)' : 'backlog',
|
|
24
24
|
version,
|
|
25
25
|
}, dynamicToolsets ? undefined : { cacheHints: TOOL_LIST_CACHE_HINT }));
|
|
26
26
|
const toolsetGroup = sharedToolsetGroup ??
|
|
27
|
-
buildToolsetGroup(backlog,
|
|
27
|
+
buildToolsetGroup(backlog, descriptionHelper, enabledToolsets);
|
|
28
28
|
registerTools(server, toolsetGroup, mcpOption);
|
|
29
|
-
|
|
29
|
+
// `list_organizations` only has something to report when more than one space
|
|
30
|
+
// is configured; the `organization` parameter its description points at is
|
|
31
|
+
// published under the same condition.
|
|
32
|
+
if (mcpOption.useOrganization) {
|
|
33
|
+
registerDynamicTools(server, organizationTools(clientRegistry, descriptionHelper), mcpOption.prefix);
|
|
34
|
+
}
|
|
30
35
|
if (dynamicToolsets) {
|
|
31
36
|
const registrar = createToolRegistrar(server, toolsetGroup, mcpOption);
|
|
32
|
-
const dynamicToolsetGroup = dynamicTools(registrar,
|
|
37
|
+
const dynamicToolsetGroup = dynamicTools(registrar, descriptionHelper, toolsetGroup);
|
|
33
38
|
registerDynamicTools(server, dynamicToolsetGroup, mcpOption.prefix);
|
|
34
39
|
}
|
|
35
40
|
return server;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the strings every tool definition passes through `t()`. Almost all of
|
|
3
|
+
* them are tool and parameter descriptions, read by the model when it picks a
|
|
4
|
+
* tool and fills in arguments. A few are validation error messages — `deleteVersion`
|
|
5
|
+
* and `resolveIdOrField` throw them — which do surface, as the text of a failed
|
|
6
|
+
* tool result.
|
|
7
|
+
*
|
|
8
|
+
* This module intentionally imports nothing. Every tool in `src/tools/` depends on
|
|
9
|
+
* it, so anything imported here is reachable from the tool layer — and the tool
|
|
10
|
+
* layer is meant to run on non-Node runtimes too. Discovering and reading the
|
|
11
|
+
* override file needs a filesystem and a home directory, so that part lives in
|
|
12
|
+
* `loadDescriptionOverrides` and the CLI passes the result in.
|
|
13
|
+
*/
|
|
14
|
+
export interface DescriptionHelper {
|
|
15
|
+
t: (key: string, fallback: string) => string;
|
|
16
|
+
dump: () => Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
export declare function createDescriptionHelper(overrides?: Record<string, string>): DescriptionHelper;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function createDescriptionHelper(overrides = {}) {
|
|
2
|
+
const usedKeys = {};
|
|
3
|
+
function toEnvKey(key) {
|
|
4
|
+
return `BACKLOG_MCP_${key}`;
|
|
5
|
+
}
|
|
6
|
+
function t(key, fallback) {
|
|
7
|
+
const upperKey = key.toUpperCase();
|
|
8
|
+
if (usedKeys[upperKey]) {
|
|
9
|
+
return usedKeys[upperKey];
|
|
10
|
+
}
|
|
11
|
+
// Runtimes without a Node compatibility layer have no `process`, and a
|
|
12
|
+
// partial shim can have `process` without `env`.
|
|
13
|
+
const env = typeof process === 'undefined' ? undefined : process.env;
|
|
14
|
+
const fromEnv = env?.[toEnvKey(upperKey)];
|
|
15
|
+
// Priority:ENV → overrides → fallback
|
|
16
|
+
const value = fromEnv || overrides[upperKey] || fallback;
|
|
17
|
+
usedKeys[upperKey] = value;
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function dump() {
|
|
21
|
+
return { ...usedKeys };
|
|
22
|
+
}
|
|
23
|
+
return { t, dump };
|
|
24
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ErrorLike } from '../../types/result.js';
|
|
3
|
+
import { ToolDefinition } from '../../types/tool.js';
|
|
4
|
+
export interface ComposeOptions {
|
|
5
|
+
useFields: boolean;
|
|
6
|
+
errorHandler?: (err: unknown) => ErrorLike;
|
|
7
|
+
maxTokens: number;
|
|
8
|
+
/**
|
|
9
|
+
* Whether to advertise `organization`. Defaults to false: with a single
|
|
10
|
+
* Backlog space the parameter has exactly one legal value, and repeating it
|
|
11
|
+
* across every tool costs the client ~8 KB of schema for nothing.
|
|
12
|
+
*/
|
|
13
|
+
useOrganization?: boolean;
|
|
14
|
+
}
|
|
15
|
+
type ComposedInput = {
|
|
16
|
+
fields?: string;
|
|
17
|
+
organization?: string;
|
|
18
|
+
} & Record<string, unknown>;
|
|
19
|
+
/**
|
|
20
|
+
* Builds the schema and handler a tool is registered with.
|
|
21
|
+
*
|
|
22
|
+
* The returned schema is a fresh object: the tool definition is never mutated.
|
|
23
|
+
* That matters under the stateless HTTP model, where one toolset group is shared
|
|
24
|
+
* by every per-request server — an in-place extension would be re-applied on
|
|
25
|
+
* every request, against a definition other requests are reading concurrently.
|
|
26
|
+
*/
|
|
27
|
+
export declare function composeToolHandler(tool: ToolDefinition<any, any>, options: ComposeOptions): {
|
|
28
|
+
schema: z.ZodObject<any, z.core.$strip>;
|
|
29
|
+
handler: (input: ComposedInput, extra: import("@modelcontextprotocol/server").ServerContext) => Promise<import("@modelcontextprotocol/server").CallToolResult>;
|
|
30
|
+
};
|
|
31
|
+
export {};
|
|
@@ -6,25 +6,37 @@ import { wrapWithTokenLimit } from '../transformers/wrapWithTokenLimit.js';
|
|
|
6
6
|
import { wrapWithToolResult } from '../transformers/wrapWithToolResult.js';
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
import { generateFieldsDescription } from '../../utils/generateFieldsDescription.js';
|
|
9
|
+
/**
|
|
10
|
+
* Builds the schema and handler a tool is registered with.
|
|
11
|
+
*
|
|
12
|
+
* The returned schema is a fresh object: the tool definition is never mutated.
|
|
13
|
+
* That matters under the stateless HTTP model, where one toolset group is shared
|
|
14
|
+
* by every per-request server — an in-place extension would be re-applied on
|
|
15
|
+
* every request, against a definition other requests are reading concurrently.
|
|
16
|
+
*/
|
|
9
17
|
export function composeToolHandler(tool, options) {
|
|
10
|
-
const { useFields, errorHandler, maxTokens } = options;
|
|
18
|
+
const { useFields, errorHandler, maxTokens, useOrganization = false, } = options;
|
|
11
19
|
// Step 1: Add `fields` to schema if needed
|
|
12
20
|
const fieldDesc = useFields
|
|
13
21
|
? generateFieldsDescription(tool.outputSchema, tool.importantFields ?? [], tool.name)
|
|
14
22
|
: undefined;
|
|
15
|
-
|
|
23
|
+
const schema = extendSchema(tool.schema, fieldDesc, useOrganization);
|
|
16
24
|
// Step 2: Compose
|
|
17
25
|
const baseHandler = wrapWithErrorHandling(wrapWithOrganizationContext(tool.handler), errorHandler);
|
|
18
|
-
const
|
|
19
|
-
return
|
|
26
|
+
const composed = useFields ? wrapWithFieldPicking(baseHandler) : baseHandler;
|
|
27
|
+
return {
|
|
28
|
+
schema,
|
|
29
|
+
handler: wrapWithToolResult(wrapWithTokenLimit(composed, maxTokens)),
|
|
30
|
+
};
|
|
20
31
|
}
|
|
21
|
-
function extendSchema(schema, desc) {
|
|
22
|
-
const extension = {
|
|
23
|
-
|
|
32
|
+
function extendSchema(schema, desc, withOrganization = false) {
|
|
33
|
+
const extension = {};
|
|
34
|
+
if (withOrganization) {
|
|
35
|
+
extension.organization = z
|
|
24
36
|
.string()
|
|
25
37
|
.optional()
|
|
26
|
-
.describe('Optional organization name. Use list_organizations to inspect available organizations.')
|
|
27
|
-
}
|
|
38
|
+
.describe('Optional organization name. Use list_organizations to inspect available organizations.');
|
|
39
|
+
}
|
|
28
40
|
if (desc) {
|
|
29
41
|
extension.fields = z.string().describe(desc);
|
|
30
42
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { CallToolResult, ServerContext } from '@modelcontextprotocol/server';
|
|
2
|
+
import { SafeResult } from '../../types/result.js';
|
|
3
|
+
/**
|
|
4
|
+
* Convert SafeResult<T> to CallToolResult
|
|
5
|
+
*/
|
|
6
|
+
export declare function wrapWithToolResult<I, T>(fn: (input: I) => Promise<SafeResult<string | T>>): (input: I, extra: ServerContext) => Promise<CallToolResult>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Server } from 'node:http';
|
|
2
|
+
import type { BacklogOAuthConfig } from './auth/backlogOAuthConfig.js';
|
|
3
|
+
import type { TokenStore } from './auth/tokenStore.js';
|
|
4
|
+
import type { BacklogMCPServer } from './utils/wrapServerWithToolRegistry.js';
|
|
5
|
+
type RunHttpMcpServerOptions = {
|
|
6
|
+
host: string;
|
|
7
|
+
port: number;
|
|
8
|
+
path: string;
|
|
9
|
+
version: string;
|
|
10
|
+
enableJsonResponse: boolean;
|
|
11
|
+
allowedHosts?: string[];
|
|
12
|
+
allowedOrigins?: string[];
|
|
13
|
+
createServer: () => BacklogMCPServer;
|
|
14
|
+
oauthConfig?: BacklogOAuthConfig;
|
|
15
|
+
tokenStore?: TokenStore;
|
|
16
|
+
};
|
|
17
|
+
type HttpMcpServerHandle = {
|
|
18
|
+
httpServer: Server;
|
|
19
|
+
shutdown: () => Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
export declare const runHttpMcpServer: (options: RunHttpMcpServerOptions) => Promise<HttpMcpServerHandle>;
|
|
22
|
+
export {};
|
package/build/index.d.ts
ADDED