backlog-mcp-server 0.16.0 → 0.18.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 +15 -32
- package/README.md +20 -32
- package/build/auth/oauthRoutes.js +53 -1
- package/build/createBacklogMcpServer.d.ts +4 -6
- package/build/createBacklogMcpServer.js +4 -12
- package/build/handlers/builders/composeToolHandler.d.ts +1 -1
- package/build/handlers/builders/composeToolHandler.js +15 -9
- package/build/handlers/transformers/wrapWithFieldPicking.d.ts +14 -1
- package/build/handlers/transformers/wrapWithFieldPicking.js +30 -36
- package/build/index.js +30 -16
- package/build/lib.d.ts +1 -1
- package/build/loadDescriptionOverrides.d.ts +0 -13
- package/build/loadDescriptionOverrides.js +73 -11
- package/build/reportUnknownOverrideKeys.d.ts +22 -0
- package/build/reportUnknownOverrideKeys.js +42 -0
- package/build/tools/addDocument.d.ts +2 -2
- package/build/tools/addDocument.js +17 -2
- package/build/tools/addIssue.d.ts +2 -2
- package/build/tools/addIssue.js +32 -2
- package/build/tools/addIssueComment.d.ts +2 -2
- package/build/tools/addIssueComment.js +14 -2
- package/build/tools/addProject.d.ts +2 -2
- package/build/tools/addProject.js +22 -2
- package/build/tools/addPullRequest.d.ts +2 -2
- package/build/tools/addPullRequest.js +26 -2
- package/build/tools/addPullRequestComment.d.ts +2 -2
- package/build/tools/addPullRequestComment.js +12 -2
- package/build/tools/addRelatedIssue.d.ts +2 -2
- package/build/tools/addRelatedIssue.js +33 -2
- package/build/tools/addVersionMilestone.d.ts +2 -2
- package/build/tools/addVersionMilestone.js +12 -2
- package/build/tools/addWatching.d.ts +2 -2
- package/build/tools/addWatching.js +12 -2
- package/build/tools/addWiki.d.ts +2 -2
- package/build/tools/addWiki.js +16 -2
- package/build/tools/countIssues.d.ts +2 -2
- package/build/tools/countIssues.js +3 -2
- package/build/tools/deleteIssue.d.ts +2 -2
- package/build/tools/deleteIssue.js +32 -2
- package/build/tools/deleteProject.d.ts +2 -2
- package/build/tools/deleteProject.js +22 -2
- package/build/tools/deleteVersion.d.ts +2 -2
- package/build/tools/deleteVersion.js +12 -2
- package/build/tools/deleteWatching.d.ts +2 -2
- package/build/tools/deleteWatching.js +12 -2
- package/build/tools/getCategories.d.ts +2 -2
- package/build/tools/getCategories.js +8 -2
- package/build/tools/getCustomFields.d.ts +2 -2
- package/build/tools/getCustomFields.js +22 -2
- package/build/tools/getDocument.d.ts +2 -2
- package/build/tools/getDocument.js +17 -2
- package/build/tools/getDocumentTree.d.ts +2 -2
- package/build/tools/getDocumentTree.js +7 -2
- package/build/tools/getDocuments.d.ts +2 -2
- package/build/tools/getDocuments.js +17 -2
- package/build/tools/getGitRepositories.d.ts +2 -2
- package/build/tools/getGitRepositories.js +17 -2
- package/build/tools/getGitRepository.d.ts +2 -2
- package/build/tools/getGitRepository.js +17 -2
- package/build/tools/getIssue.d.ts +2 -2
- package/build/tools/getIssue.js +32 -2
- package/build/tools/getIssueComments.d.ts +2 -2
- package/build/tools/getIssueComments.js +14 -2
- package/build/tools/getIssueTypes.d.ts +2 -2
- package/build/tools/getIssueTypes.js +11 -2
- package/build/tools/getIssues.d.ts +10 -10
- package/build/tools/getIssues.js +32 -2
- package/build/tools/getMyself.d.ts +2 -2
- package/build/tools/getMyself.js +11 -2
- package/build/tools/getNotifications.d.ts +2 -2
- package/build/tools/getNotifications.js +15 -2
- package/build/tools/getNotificationsCount.d.ts +2 -2
- package/build/tools/getNotificationsCount.js +5 -2
- package/build/tools/getPriorities.d.ts +2 -2
- package/build/tools/getPriorities.js +3 -2
- package/build/tools/getProject.d.ts +2 -2
- package/build/tools/getProject.js +22 -2
- package/build/tools/getProjectList.d.ts +2 -2
- package/build/tools/getProjectList.js +22 -2
- package/build/tools/getProjectUsers.d.ts +2 -2
- package/build/tools/getProjectUsers.js +11 -2
- package/build/tools/getPullRequest.d.ts +2 -2
- package/build/tools/getPullRequest.js +26 -2
- package/build/tools/getPullRequestComments.d.ts +2 -2
- package/build/tools/getPullRequestComments.js +12 -2
- package/build/tools/getPullRequests.d.ts +2 -2
- package/build/tools/getPullRequests.js +26 -2
- package/build/tools/getPullRequestsCount.d.ts +2 -2
- package/build/tools/getPullRequestsCount.js +5 -2
- package/build/tools/getRelatedIssues.d.ts +2 -2
- package/build/tools/getRelatedIssues.js +33 -2
- package/build/tools/getResolutions.d.ts +2 -2
- package/build/tools/getResolutions.js +3 -2
- package/build/tools/getSpace.d.ts +2 -2
- package/build/tools/getSpace.js +13 -2
- package/build/tools/getSpaceActivities.d.ts +2 -2
- package/build/tools/getSpaceActivities.js +12 -2
- package/build/tools/getUserRecentUpdates.d.ts +2 -2
- package/build/tools/getUserRecentUpdates.js +12 -2
- package/build/tools/getUserStarsCount.d.ts +2 -2
- package/build/tools/getUserStarsCount.js +3 -2
- package/build/tools/getUsers.d.ts +2 -2
- package/build/tools/getUsers.js +11 -2
- package/build/tools/getVersionMilestoneList.d.ts +2 -2
- package/build/tools/getVersionMilestoneList.js +12 -2
- package/build/tools/getWatchingListCount.d.ts +2 -2
- package/build/tools/getWatchingListCount.js +5 -2
- package/build/tools/getWatchingListItems.d.ts +2 -2
- package/build/tools/getWatchingListItems.js +12 -2
- package/build/tools/getWiki.d.ts +2 -2
- package/build/tools/getWiki.js +16 -2
- package/build/tools/getWikiPages.d.ts +2 -2
- package/build/tools/getWikiPages.js +12 -2
- package/build/tools/getWikisCount.d.ts +2 -2
- package/build/tools/getWikisCount.js +3 -2
- package/build/tools/markNotificationAsRead.d.ts +6 -1
- package/build/tools/markNotificationAsRead.js +6 -1
- package/build/tools/markWatchingAsRead.d.ts +6 -1
- package/build/tools/markWatchingAsRead.js +6 -1
- package/build/tools/removeRelatedIssue.d.ts +2 -2
- package/build/tools/removeRelatedIssue.js +33 -2
- package/build/tools/resetUnreadNotificationCount.d.ts +2 -2
- package/build/tools/resetUnreadNotificationCount.js +5 -2
- package/build/tools/updateIssue.d.ts +2 -2
- package/build/tools/updateIssue.js +32 -2
- package/build/tools/updateIssueComment.d.ts +2 -2
- package/build/tools/updateIssueComment.js +14 -2
- package/build/tools/updateProject.d.ts +2 -2
- package/build/tools/updateProject.js +22 -2
- package/build/tools/updatePullRequest.d.ts +2 -2
- package/build/tools/updatePullRequest.js +26 -2
- package/build/tools/updatePullRequestComment.d.ts +2 -2
- package/build/tools/updatePullRequestComment.js +12 -2
- package/build/tools/updateVersionMilestone.d.ts +2 -2
- package/build/tools/updateVersionMilestone.js +12 -2
- package/build/tools/updateWatching.d.ts +2 -2
- package/build/tools/updateWatching.js +12 -2
- package/build/tools/updateWiki.d.ts +2 -2
- package/build/tools/updateWiki.js +16 -2
- package/build/types/outputFields.d.ts +26 -0
- package/build/types/outputFields.js +3 -0
- package/build/types/tool.d.ts +21 -8
- package/build/types/zod/activityType.d.ts +38 -0
- package/build/types/zod/activityType.js +38 -0
- package/build/utils/fieldSelection.d.ts +22 -0
- package/build/utils/fieldSelection.js +40 -0
- package/build/utils/toolsetUtils.d.ts +1 -3
- package/build/utils/toolsetUtils.js +0 -12
- package/package.json +4 -4
- package/build/tools/dynamicTools/toolsets.d.ts +0 -15
- package/build/tools/dynamicTools/toolsets.js +0 -103
- package/build/types/zod/backlogOutputDefinition.d.ts +0 -3334
- package/build/types/zod/backlogOutputDefinition.js +0 -483
- package/build/utils/generateFieldsDescription.d.ts +0 -5
- package/build/utils/generateFieldsDescription.js +0 -50
- package/build/utils/toolRegistrar.d.ts +0 -5
- package/build/utils/toolRegistrar.js +0 -12
package/README.ja.md
CHANGED
|
@@ -16,7 +16,7 @@ Backlog API とやり取りするための Model Context Protocol(MCP)サー
|
|
|
16
16
|
- Wikiページサポート
|
|
17
17
|
- Gitリポジトリとプルリクエストツール
|
|
18
18
|
- 通知ツール
|
|
19
|
-
-
|
|
19
|
+
- 最適化されたレスポンスのためのフィールド選択
|
|
20
20
|
- 大規模なレスポンスに対するトークン制限
|
|
21
21
|
|
|
22
22
|
## 利用開始
|
|
@@ -188,6 +188,14 @@ OAuth有効時、サーバーは以下のOAuthエンドポイントを自動的
|
|
|
188
188
|
|
|
189
189
|
MCP認可仕様に対応するMCPクライアントは、これらのエンドポイントを自動的に使用します。
|
|
190
190
|
|
|
191
|
+
`POST /register` は登録できる redirect URI を制限します。ループバック URI
|
|
192
|
+
(`http://localhost`、`http://127.0.0.1`、`http://[::1]`)はユーザーのマシン上で動く
|
|
193
|
+
アプリが認可コードを受け取るための手段で、`"application_type": "native"` を宣言した
|
|
194
|
+
クライアント、または宣言がない場合は redirect URI が**すべて**ループバックである
|
|
195
|
+
クライアントから受け付けます。`"application_type": "web"` を宣言したクライアントや、
|
|
196
|
+
宣言なしでリモートの `https:` URI とループバック URI を混在させた登録は
|
|
197
|
+
`invalid_client_metadata` で拒否されます。
|
|
198
|
+
|
|
191
199
|
> **制約事項:**
|
|
192
200
|
>
|
|
193
201
|
> - OAuthモードは現在、単一のBacklog組織のみをサポートしています。複数組織設定との併用はできません。
|
|
@@ -233,26 +241,6 @@ ENABLE_TOOLSETS="space,project,issue"
|
|
|
233
241
|
|
|
234
242
|
> 🧩 ヒント: `project` ツールセットは、他の多くのツールがエントリポイントとしてプロジェクトデータに依存しているため、強く推奨されます。
|
|
235
243
|
|
|
236
|
-
### 動的なツールセット検出(実験的)
|
|
237
|
-
|
|
238
|
-
MCPサーバーをAIエージェントと共に使用している場合、実行時にツールセットの動的な検出を有効にすることができます:
|
|
239
|
-
|
|
240
|
-
CLI経由での有効化:
|
|
241
|
-
|
|
242
|
-
```
|
|
243
|
-
--dynamic-toolsets
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
または環境変数経由:
|
|
247
|
-
|
|
248
|
-
```
|
|
249
|
-
-e DYNAMIC_TOOLSETS=1 \
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
動的ツールセットを有効にすると、LLMはツールインターフェースを介してオンデマンドでツールセットを一覧表示およびアクティブ化できるようになります。
|
|
253
|
-
|
|
254
|
-
> **HTTPでのスコープ:** MCP `2026-07-28` にはプロトコルセッションがないため、アクティブ化したツールセットはクライアント単位ではなく**サーバープロセス単位**で保持されます。HTTPトランスポートでは接続中の全クライアントが1つのツールセット状態を共有し、プロセス再起動でリセットされます。共有されるのはツールの*可視性*のみで、認可は共有されません(各呼び出しは呼び出し元自身の認証情報で認証されます)。
|
|
255
|
-
|
|
256
244
|
## 利用可能なツール
|
|
257
245
|
|
|
258
246
|
以下のような Backlog 機能に対応するツールを提供しています:
|
|
@@ -437,7 +425,7 @@ PREFIX="backlog_"
|
|
|
437
425
|
|
|
438
426
|
### レスポンスの最適化とトークン制限
|
|
439
427
|
|
|
440
|
-
####
|
|
428
|
+
#### フィールド選択
|
|
441
429
|
|
|
442
430
|
```
|
|
443
431
|
--optimize-response
|
|
@@ -449,17 +437,13 @@ PREFIX="backlog_"
|
|
|
449
437
|
OPTIMIZE_RESPONSE=1
|
|
450
438
|
```
|
|
451
439
|
|
|
452
|
-
|
|
440
|
+
**一覧を返すツール**に `fields` パラメータ(省略可)が追加されます。値はそのツールの結果のトップレベルのフィールド名のリストで、enum として公開されるため、そのツールに存在しない名前は無視されずに拒否されます。単体のレコードを返すツールには追加されません(パラメータは毎セッション費用がかかる一方、1 件のレコードから削れる量はごくわずかなためです)。
|
|
453
441
|
|
|
454
442
|
```
|
|
455
|
-
get_project(projectIdOrKey: "PROJECT-KEY", fields: "
|
|
443
|
+
get_project(projectIdOrKey: "PROJECT-KEY", fields: ["name", "key", "description"])
|
|
456
444
|
```
|
|
457
445
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
```
|
|
461
|
-
get_project(projectIdOrKey: "PROJECT-KEY", fields: "{ name key description }")
|
|
462
|
-
```
|
|
446
|
+
`fields` を省略すると結果全体が返ります。選択は 1 階層のみで、オブジェクトや配列のフィールドを指定した場合はそのまま丸ごと返ります。
|
|
463
447
|
|
|
464
448
|
利点:
|
|
465
449
|
|
|
@@ -518,8 +502,7 @@ MAX_TOKENS=10000
|
|
|
518
502
|
"MAX_TOKENS": "10000",
|
|
519
503
|
"OPTIMIZE_RESPONSE": "1",
|
|
520
504
|
"PREFIX": "backlog_",
|
|
521
|
-
"ENABLE_TOOLSETS": "space,project,issue"
|
|
522
|
-
"ENABLE_DYNAMIC_TOOLSETS": "1"
|
|
505
|
+
"ENABLE_TOOLSETS": "space,project,issue"
|
|
523
506
|
}
|
|
524
507
|
}
|
|
525
508
|
}
|
|
@@ -546,7 +529,7 @@ pnpm test
|
|
|
546
529
|
サーバーはいくつかのコマンドラインオプションをサポートしています:
|
|
547
530
|
|
|
548
531
|
- `--export-descriptions`: ツール一覧の構築時に解決される説明キーと値をエクスポート。旧名は `--export-translations` で、非推奨エイリアスとして当面動作しますが、将来のリリースで削除されます
|
|
549
|
-
- `--optimize-response`:
|
|
532
|
+
- `--optimize-response`: 各ツールに、返す結果フィールドを選ぶ `fields` パラメータを追加する
|
|
550
533
|
- `--max-tokens=NUMBER`: レスポンスの最大トークン制限を設定
|
|
551
534
|
- `--prefix=STRING`: すべてのツール名に付加するオプションの文字列プレフィックス(デフォルト:"")
|
|
552
535
|
- `--enable-toolsets <toolsets...>`: 有効にするツールセットを指定します(カンマ区切りまたは複数の引数)。デフォルトは "all" です。
|
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ A Model Context Protocol (MCP) server for interacting with the Backlog API. This
|
|
|
17
17
|
- Wiki page support
|
|
18
18
|
- Git repository and pull request tools
|
|
19
19
|
- Notification tools
|
|
20
|
-
-
|
|
20
|
+
- Field selection for optimized responses
|
|
21
21
|
- Token limiting for large responses
|
|
22
22
|
|
|
23
23
|
## Getting Started
|
|
@@ -215,6 +215,14 @@ The server automatically exposes the following OAuth endpoints when OAuth is ena
|
|
|
215
215
|
|
|
216
216
|
MCP clients that support the MCP authorization specification will use these endpoints automatically.
|
|
217
217
|
|
|
218
|
+
`POST /register` restricts which redirect URIs a client may register. A loopback
|
|
219
|
+
URI (`http://localhost`, `http://127.0.0.1`, `http://[::1]`) is how an app running
|
|
220
|
+
on the user's machine receives the authorization code, and is accepted from a
|
|
221
|
+
client that declares `"application_type": "native"` — or, when the field is
|
|
222
|
+
absent, from one whose redirect URIs are _all_ loopback. A client declaring
|
|
223
|
+
`"application_type": "web"`, or mixing a remote `https:` URI with a loopback one
|
|
224
|
+
without declaring itself, is rejected with `invalid_client_metadata`.
|
|
225
|
+
|
|
218
226
|
> **Limitations:**
|
|
219
227
|
>
|
|
220
228
|
> - OAuth mode currently supports a single Backlog organization. It is not compatible with the multi-organization configuration.
|
|
@@ -260,26 +268,6 @@ Using selective toolsets can be helpful if the toolset list is too large for you
|
|
|
260
268
|
|
|
261
269
|
> 🧩 Tip: `project` toolset is highly recommended, as many other tools rely on project data as an entry point.
|
|
262
270
|
|
|
263
|
-
### Dynamic Toolset Discovery (Experimental)
|
|
264
|
-
|
|
265
|
-
If you're using the MCP server with AI agents, you can enable dynamic discovery of toolsets at runtime:
|
|
266
|
-
|
|
267
|
-
Enabling via CLI:
|
|
268
|
-
|
|
269
|
-
```
|
|
270
|
-
--dynamic-toolsets
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
Or via environment variable::
|
|
274
|
-
|
|
275
|
-
```
|
|
276
|
-
-e DYNAMIC_TOOLSETS=1 \
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
With dynamic toolsets enabled, the LLM will be able to list and activate toolsets on demand via tool interface.
|
|
280
|
-
|
|
281
|
-
> **Scope over HTTP:** MCP `2026-07-28` has no protocol sessions, so an activated toolset is remembered per **server process**, not per client. On the HTTP transport every connected client shares one toolset state, and it resets when the process restarts. Tool _visibility_ is shared; authorization is not — every call is still authenticated with the caller's own credentials.
|
|
282
|
-
|
|
283
271
|
## Available Tools
|
|
284
272
|
|
|
285
273
|
### Toolset: `space`
|
|
@@ -549,7 +537,7 @@ This is especially useful if you're using multiple MCP servers or tools in the s
|
|
|
549
537
|
|
|
550
538
|
### Response Optimization & Token Limits
|
|
551
539
|
|
|
552
|
-
#### Field Selection
|
|
540
|
+
#### Field Selection
|
|
553
541
|
|
|
554
542
|
```
|
|
555
543
|
--optimize-response
|
|
@@ -561,17 +549,18 @@ Or environment variable:
|
|
|
561
549
|
OPTIMIZE_RESPONSE=1
|
|
562
550
|
```
|
|
563
551
|
|
|
564
|
-
|
|
552
|
+
Tools that return a **list** then take an optional `fields` parameter: a list of
|
|
553
|
+
top-level field names from that tool's own result, published as an enum so a name
|
|
554
|
+
the tool does not have is rejected rather than ignored. Tools that return a single
|
|
555
|
+
record do not get it — the parameter costs schema on every session, and one record
|
|
556
|
+
has almost nothing to trim.
|
|
565
557
|
|
|
566
558
|
```
|
|
567
|
-
get_project(projectIdOrKey: "PROJECT-KEY", fields: "
|
|
559
|
+
get_project(projectIdOrKey: "PROJECT-KEY", fields: ["name", "key", "description"])
|
|
568
560
|
```
|
|
569
561
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
```
|
|
573
|
-
get_project(projectIdOrKey: "PROJECT-KEY", fields: "{ name key description }")
|
|
574
|
-
```
|
|
562
|
+
Omitting `fields` returns the whole result. Selection is one level deep: naming an
|
|
563
|
+
object or array field returns it whole.
|
|
575
564
|
|
|
576
565
|
Benefits:
|
|
577
566
|
|
|
@@ -630,8 +619,7 @@ This section demonstrates advanced configuration using multiple environment vari
|
|
|
630
619
|
"MAX_TOKENS": "10000",
|
|
631
620
|
"OPTIMIZE_RESPONSE": "1",
|
|
632
621
|
"PREFIX": "backlog_",
|
|
633
|
-
"ENABLE_TOOLSETS": "space,project,issue"
|
|
634
|
-
"ENABLE_DYNAMIC_TOOLSETS": "1"
|
|
622
|
+
"ENABLE_TOOLSETS": "space,project,issue"
|
|
635
623
|
}
|
|
636
624
|
}
|
|
637
625
|
}
|
|
@@ -663,7 +651,7 @@ The server supports several command line options:
|
|
|
663
651
|
- `--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.
|
|
664
652
|
- `--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.
|
|
665
653
|
- `--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
|
|
666
|
-
- `--optimize-response`:
|
|
654
|
+
- `--optimize-response`: Add a `fields` parameter to each tool for selecting which result fields to return
|
|
667
655
|
- `--max-tokens=NUMBER`: Set maximum token limit for responses
|
|
668
656
|
- `--prefix=STRING`: Optional string prefix to prepend to all tool names (default: "")
|
|
669
657
|
- `--enable-toolsets <toolsets...>`: Specify which toolsets to enable (comma-separated or multiple arguments). Defaults to "all".
|
|
@@ -15,17 +15,54 @@ function verifyPkce(codeVerifier, codeChallenge) {
|
|
|
15
15
|
function oauthError(code, description) {
|
|
16
16
|
return { error: code, error_description: description };
|
|
17
17
|
}
|
|
18
|
+
function isLoopbackRedirectUri(uri) {
|
|
19
|
+
try {
|
|
20
|
+
const parsed = new URL(uri);
|
|
21
|
+
return (parsed.protocol === 'http:' && LOCALHOST_HOSTS.includes(parsed.hostname));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
18
27
|
function isValidRedirectUri(uri) {
|
|
19
28
|
try {
|
|
20
29
|
const parsed = new URL(uri);
|
|
21
30
|
if (parsed.protocol === 'https:')
|
|
22
31
|
return true;
|
|
23
|
-
return (
|
|
32
|
+
return isLoopbackRedirectUri(uri);
|
|
24
33
|
}
|
|
25
34
|
catch {
|
|
26
35
|
return false;
|
|
27
36
|
}
|
|
28
37
|
}
|
|
38
|
+
/** RFC 7591 defines these two, and defaults to `web` when the field is absent. */
|
|
39
|
+
const APPLICATION_TYPES = ['web', 'native'];
|
|
40
|
+
/**
|
|
41
|
+
* Decides which application type a registration is held to.
|
|
42
|
+
*
|
|
43
|
+
* A loopback redirect URI is the standard way for an app on the user's machine
|
|
44
|
+
* to receive the authorization code (RFC 8252), and the wrong thing entirely for
|
|
45
|
+
* an app that has a domain of its own: nobody owns `http://localhost`, so
|
|
46
|
+
* whichever process is listening on that port receives the code.
|
|
47
|
+
*
|
|
48
|
+
* RFC 7591 defaults the field to `web`, which would reject every local MCP
|
|
49
|
+
* client that does not declare itself — and they do not, because nothing has
|
|
50
|
+
* ever checked. So an undeclared registration is judged by what it asks for: a
|
|
51
|
+
* set of redirect URIs that is entirely loopback can only be a native client,
|
|
52
|
+
* and is treated as one.
|
|
53
|
+
*
|
|
54
|
+
* The inference deliberately requires *all* of them. A registration mixing a
|
|
55
|
+
* remote https URI with a loopback one is the shape this check exists to stop:
|
|
56
|
+
* something that can serve a redirect on its own domain has no need to also
|
|
57
|
+
* collect codes on the user's machine. Mixing is only accepted from a client
|
|
58
|
+
* that declares `native` outright, which RFC 8252 does allow (a native app may
|
|
59
|
+
* claim an https URI as well as a loopback one).
|
|
60
|
+
*/
|
|
61
|
+
function resolveApplicationType(declared, redirectUris) {
|
|
62
|
+
if (declared)
|
|
63
|
+
return declared;
|
|
64
|
+
return redirectUris.every(isLoopbackRedirectUri) ? 'native' : 'web';
|
|
65
|
+
}
|
|
29
66
|
export function createOAuthRoutes(config, store, mcpPath) {
|
|
30
67
|
const app = new Hono();
|
|
31
68
|
const { serverBaseUrl } = config;
|
|
@@ -76,6 +113,21 @@ export function createOAuthRoutes(config, store, mcpPath) {
|
|
|
76
113
|
return c.json(oauthError('invalid_client_metadata', `redirect_uri must use https or http://localhost: ${uri}`), 400);
|
|
77
114
|
}
|
|
78
115
|
}
|
|
116
|
+
// `null` counts as not declared. Serialisers that emit null for an absent
|
|
117
|
+
// optional field are common, and rejecting it would break clients this
|
|
118
|
+
// change is meant to leave alone.
|
|
119
|
+
const declaredApplicationType = body.application_type ?? undefined;
|
|
120
|
+
if (declaredApplicationType !== undefined &&
|
|
121
|
+
!APPLICATION_TYPES.includes(declaredApplicationType)) {
|
|
122
|
+
return c.json(oauthError('invalid_client_metadata', `Unsupported application_type: ${String(declaredApplicationType)}. Supported: ${APPLICATION_TYPES.join(', ')}`), 400);
|
|
123
|
+
}
|
|
124
|
+
const applicationType = resolveApplicationType(declaredApplicationType, redirectUris);
|
|
125
|
+
if (applicationType === 'web') {
|
|
126
|
+
const loopback = redirectUris.filter(isLoopbackRedirectUri);
|
|
127
|
+
if (loopback.length > 0) {
|
|
128
|
+
return c.json(oauthError('invalid_client_metadata', `application_type "web" may not use a loopback redirect_uri: ${loopback.join(', ')}. Declare application_type "native" if this client runs on the user's machine.`), 400);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
79
131
|
const authMethod = typeof body.token_endpoint_auth_method === 'string'
|
|
80
132
|
? body.token_endpoint_auth_method
|
|
81
133
|
: 'client_secret_post';
|
|
@@ -12,13 +12,11 @@ export type CreateBacklogMcpServerConfig = {
|
|
|
12
12
|
descriptionHelper: DescriptionHelper;
|
|
13
13
|
enabledToolsets: string[];
|
|
14
14
|
mcpOption: MCPOptions;
|
|
15
|
-
dynamicToolsets: boolean;
|
|
16
15
|
/**
|
|
17
16
|
* A toolset group to register from, instead of building one from
|
|
18
|
-
* `enabledToolsets`. Callers that produce many servers from one factory pass
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* that mutation away the moment the request ends.
|
|
17
|
+
* `enabledToolsets`. Callers that produce many servers from one factory pass a
|
|
18
|
+
* single shared group so the tool tree is built once rather than per request.
|
|
19
|
+
* Nothing mutates it.
|
|
22
20
|
*/
|
|
23
21
|
toolsetGroup?: ToolsetGroup;
|
|
24
22
|
};
|
|
@@ -26,4 +24,4 @@ export type CreateBacklogMcpServerConfig = {
|
|
|
26
24
|
* Builds a fresh MCP server instance with all Backlog tools registered.
|
|
27
25
|
* Used once per stdio connection; one instance per HTTP request for Streamable HTTP.
|
|
28
26
|
*/
|
|
29
|
-
export declare function createBacklogMcpServer({ version, useFields, backlog, clientRegistry, descriptionHelper, enabledToolsets, mcpOption,
|
|
27
|
+
export declare function createBacklogMcpServer({ version, useFields, backlog, clientRegistry, descriptionHelper, enabledToolsets, mcpOption, toolsetGroup: sharedToolsetGroup, }: CreateBacklogMcpServerConfig): BacklogMCPServer;
|
|
@@ -3,13 +3,10 @@
|
|
|
3
3
|
import { McpServer } from '@modelcontextprotocol/server';
|
|
4
4
|
import { registerDynamicTools, registerTools } from './registerTools.js';
|
|
5
5
|
import { organizationTools } from './tools/dynamicTools/organizations.js';
|
|
6
|
-
import { dynamicTools } from './tools/dynamicTools/toolsets.js';
|
|
7
|
-
import { createToolRegistrar } from './utils/toolRegistrar.js';
|
|
8
6
|
import { buildToolsetGroup } from './utils/toolsetUtils.js';
|
|
9
7
|
import { wrapServerWithToolRegistry, } from './utils/wrapServerWithToolRegistry.js';
|
|
10
|
-
// The tool list is fixed for the process lifetime
|
|
11
|
-
// and environment
|
|
12
|
-
// grow at runtime, so no hint is published in that case.
|
|
8
|
+
// The tool list is fixed for the process lifetime: it only depends on CLI flags
|
|
9
|
+
// and environment, so clients may cache it.
|
|
13
10
|
const TOOL_LIST_CACHE_HINT = {
|
|
14
11
|
'tools/list': { ttlMs: 5 * 60 * 1000, cacheScope: 'public' },
|
|
15
12
|
};
|
|
@@ -17,12 +14,12 @@ const TOOL_LIST_CACHE_HINT = {
|
|
|
17
14
|
* Builds a fresh MCP server instance with all Backlog tools registered.
|
|
18
15
|
* Used once per stdio connection; one instance per HTTP request for Streamable HTTP.
|
|
19
16
|
*/
|
|
20
|
-
export function createBacklogMcpServer({ version, useFields, backlog, clientRegistry, descriptionHelper, enabledToolsets, mcpOption,
|
|
17
|
+
export function createBacklogMcpServer({ version, useFields, backlog, clientRegistry, descriptionHelper, enabledToolsets, mcpOption, toolsetGroup: sharedToolsetGroup, }) {
|
|
21
18
|
const server = wrapServerWithToolRegistry(new McpServer({
|
|
22
19
|
name: 'backlog',
|
|
23
20
|
title: useFields ? 'backlog (field selection enabled)' : 'backlog',
|
|
24
21
|
version,
|
|
25
|
-
},
|
|
22
|
+
}, { cacheHints: TOOL_LIST_CACHE_HINT }));
|
|
26
23
|
const toolsetGroup = sharedToolsetGroup ??
|
|
27
24
|
buildToolsetGroup(backlog, descriptionHelper, enabledToolsets);
|
|
28
25
|
registerTools(server, toolsetGroup, mcpOption);
|
|
@@ -32,10 +29,5 @@ export function createBacklogMcpServer({ version, useFields, backlog, clientRegi
|
|
|
32
29
|
if (mcpOption.useOrganization) {
|
|
33
30
|
registerDynamicTools(server, organizationTools(clientRegistry, descriptionHelper), mcpOption.prefix);
|
|
34
31
|
}
|
|
35
|
-
if (dynamicToolsets) {
|
|
36
|
-
const registrar = createToolRegistrar(server, toolsetGroup, mcpOption);
|
|
37
|
-
const dynamicToolsetGroup = dynamicTools(registrar, descriptionHelper, toolsetGroup);
|
|
38
|
-
registerDynamicTools(server, dynamicToolsetGroup, mcpOption.prefix);
|
|
39
|
-
}
|
|
40
32
|
return server;
|
|
41
33
|
}
|
|
@@ -5,7 +5,7 @@ import { wrapWithOrganizationContext } from '../transformers/wrapWithOrganizatio
|
|
|
5
5
|
import { wrapWithTokenLimit } from '../transformers/wrapWithTokenLimit.js';
|
|
6
6
|
import { wrapWithToolResult } from '../transformers/wrapWithToolResult.js';
|
|
7
7
|
import { z } from 'zod';
|
|
8
|
-
import {
|
|
8
|
+
import { fieldSelectionSchema } from '../../utils/fieldSelection.js';
|
|
9
9
|
/**
|
|
10
10
|
* Builds the schema and handler a tool is registered with.
|
|
11
11
|
*
|
|
@@ -16,20 +16,24 @@ import { generateFieldsDescription } from '../../utils/generateFieldsDescription
|
|
|
16
16
|
*/
|
|
17
17
|
export function composeToolHandler(tool, options) {
|
|
18
18
|
const { useFields, errorHandler, maxTokens, useOrganization = false, } = options;
|
|
19
|
-
// Step 1: Add `fields` to schema if needed
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
// Step 1: Add `fields` to schema if needed.
|
|
20
|
+
//
|
|
21
|
+
// Only on tools that return a list. That is where a response grows without
|
|
22
|
+
// bound and trimming it pays for the schema every client downloads; a tool
|
|
23
|
+
// returning one record saves a few hundred bytes at best.
|
|
24
|
+
const fields = useFields && tool.returnsList
|
|
25
|
+
? fieldSelectionSchema(tool.outputFields, tool.importantFields ?? [])
|
|
22
26
|
: undefined;
|
|
23
|
-
const schema = extendSchema(tool.schema,
|
|
27
|
+
const schema = extendSchema(tool.schema, fields, useOrganization);
|
|
24
28
|
// Step 2: Compose
|
|
25
29
|
const baseHandler = wrapWithErrorHandling(wrapWithOrganizationContext(tool.handler), errorHandler);
|
|
26
|
-
const composed = useFields ? wrapWithFieldPicking(baseHandler) : baseHandler;
|
|
30
|
+
const composed = useFields && fields ? wrapWithFieldPicking(baseHandler) : baseHandler;
|
|
27
31
|
return {
|
|
28
32
|
schema,
|
|
29
33
|
handler: wrapWithToolResult(wrapWithTokenLimit(composed, maxTokens)),
|
|
30
34
|
};
|
|
31
35
|
}
|
|
32
|
-
function extendSchema(schema,
|
|
36
|
+
function extendSchema(schema, fields, withOrganization = false) {
|
|
33
37
|
const extension = {};
|
|
34
38
|
if (withOrganization) {
|
|
35
39
|
extension.organization = z
|
|
@@ -37,8 +41,10 @@ function extendSchema(schema, desc, withOrganization = false) {
|
|
|
37
41
|
.optional()
|
|
38
42
|
.describe('Optional organization name. Use list_organizations to inspect available organizations.');
|
|
39
43
|
}
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
// Optional, as `wrapWithFieldPicking` assumes: it returns the whole result
|
|
45
|
+
// when `fields` is absent.
|
|
46
|
+
if (fields) {
|
|
47
|
+
extension.fields = fields;
|
|
42
48
|
}
|
|
43
49
|
// zod v4 reworked the ZodObject shape generics, so `extend()`'s result no
|
|
44
50
|
// longer overlaps the declared return type enough for a direct cast. The
|
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import { SafeResult } from '../../types/result.js';
|
|
2
|
+
/**
|
|
3
|
+
* Narrows a successful result to the fields the caller asked for.
|
|
4
|
+
*
|
|
5
|
+
* `fields` is a list of top-level names, validated against the tool's own enum
|
|
6
|
+
* before it reaches here, so an unknown name is rejected by the protocol rather
|
|
7
|
+
* than dropped in silence. A missing or empty list returns everything.
|
|
8
|
+
*
|
|
9
|
+
* One level deep on purpose. This used to accept a GraphQL selection set and
|
|
10
|
+
* parse it with the `graphql` package: ~1 MB for a single `parse` call, a grammar
|
|
11
|
+
* that accepted aliases, fragments, arguments and directives and then honoured
|
|
12
|
+
* none of them, and a descent into an array field that returned `{}` and lost the
|
|
13
|
+
* data outright.
|
|
14
|
+
*/
|
|
2
15
|
export declare function wrapWithFieldPicking<I extends {
|
|
3
|
-
fields?: string;
|
|
16
|
+
fields?: string[];
|
|
4
17
|
}, O>(fn: (input: I) => Promise<SafeResult<O>>): (input: I) => Promise<SafeResult<O>>;
|
|
@@ -1,55 +1,49 @@
|
|
|
1
|
-
import { parse } from 'graphql';
|
|
2
1
|
import { isErrorLike } from '../../types/result.js';
|
|
2
|
+
/**
|
|
3
|
+
* Narrows a successful result to the fields the caller asked for.
|
|
4
|
+
*
|
|
5
|
+
* `fields` is a list of top-level names, validated against the tool's own enum
|
|
6
|
+
* before it reaches here, so an unknown name is rejected by the protocol rather
|
|
7
|
+
* than dropped in silence. A missing or empty list returns everything.
|
|
8
|
+
*
|
|
9
|
+
* One level deep on purpose. This used to accept a GraphQL selection set and
|
|
10
|
+
* parse it with the `graphql` package: ~1 MB for a single `parse` call, a grammar
|
|
11
|
+
* that accepted aliases, fragments, arguments and directives and then honoured
|
|
12
|
+
* none of them, and a descent into an array field that returned `{}` and lost the
|
|
13
|
+
* data outright.
|
|
14
|
+
*/
|
|
3
15
|
export function wrapWithFieldPicking(fn) {
|
|
4
16
|
return async (input) => {
|
|
5
17
|
const { fields, ...rest } = input;
|
|
6
18
|
const result = await fn(rest);
|
|
7
|
-
if (!fields || isErrorLike(result)) {
|
|
19
|
+
if (!fields || fields.length === 0 || isErrorLike(result)) {
|
|
8
20
|
return result;
|
|
9
21
|
}
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
if (Array.isArray(resultData)) {
|
|
22
|
+
const data = result.data;
|
|
23
|
+
if (Array.isArray(data)) {
|
|
13
24
|
return {
|
|
14
25
|
kind: 'ok',
|
|
15
|
-
data:
|
|
26
|
+
data: data.map((item) => pick(item, fields)),
|
|
16
27
|
};
|
|
17
28
|
}
|
|
18
|
-
|
|
29
|
+
if (typeof data === 'object' && data !== null) {
|
|
19
30
|
return {
|
|
20
31
|
kind: 'ok',
|
|
21
|
-
data:
|
|
32
|
+
data: pick(data, fields),
|
|
22
33
|
};
|
|
23
34
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
35
|
+
// A scalar has no fields to narrow.
|
|
36
|
+
return result;
|
|
27
37
|
};
|
|
28
38
|
}
|
|
29
|
-
function
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
function pickFieldsFromData(data, selectionSet) {
|
|
39
|
-
const result = {};
|
|
40
|
-
for (const selection of selectionSet.selections) {
|
|
41
|
-
if (selection.kind === 'Field') {
|
|
42
|
-
const key = selection.name.value;
|
|
43
|
-
if (data != null && key in data) {
|
|
44
|
-
const value = data[key];
|
|
45
|
-
if (selection.selectionSet && value != null) {
|
|
46
|
-
result[key] = pickFieldsFromData(data[key], selection.selectionSet);
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
result[key] = data[key];
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
39
|
+
function pick(value, fields) {
|
|
40
|
+
if (typeof value !== 'object' || value === null)
|
|
41
|
+
return value;
|
|
42
|
+
const source = value;
|
|
43
|
+
const picked = {};
|
|
44
|
+
for (const field of fields) {
|
|
45
|
+
if (field in source)
|
|
46
|
+
picked[field] = source[field];
|
|
53
47
|
}
|
|
54
|
-
return
|
|
48
|
+
return picked;
|
|
55
49
|
}
|
package/build/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { createDescriptionHelper } from './createDescriptionHelper.js';
|
|
|
11
11
|
import { loadDescriptionOverrides } from './loadDescriptionOverrides.js';
|
|
12
12
|
import { createBacklogMcpServer } from './createBacklogMcpServer.js';
|
|
13
13
|
import { runHttpMcpServer } from './httpMcpServer.js';
|
|
14
|
+
import { reportUnknownOverrideKeys } from './reportUnknownOverrideKeys.js';
|
|
14
15
|
import { createBacklogClientRegistry, createOAuthBacklogClientRegistry, } from './utils/backlogClientRegistry.js';
|
|
15
16
|
import { logger } from './utils/logger.js';
|
|
16
17
|
import { buildToolsetGroup } from './utils/toolsetUtils.js';
|
|
@@ -119,11 +120,6 @@ Available toolsets:
|
|
|
119
120
|
- git: Tools for managing Git repositories and pull requests
|
|
120
121
|
- notifications: Tools for managing user notifications`,
|
|
121
122
|
default: env.get('ENABLE_TOOLSETS').default('all').asArray(','),
|
|
122
|
-
})
|
|
123
|
-
.option('dynamic-toolsets', {
|
|
124
|
-
type: 'boolean',
|
|
125
|
-
describe: 'Enable dynamic toolsets such as enable_toolset, list_available_toolsets, etc.',
|
|
126
|
-
default: env.get('ENABLE_DYNAMIC_TOOLSETS').default('false').asBool(),
|
|
127
123
|
})
|
|
128
124
|
.parseSync();
|
|
129
125
|
// The alias resolves both spellings to the same argv key, so which one was typed
|
|
@@ -133,6 +129,22 @@ Available toolsets:
|
|
|
133
129
|
if (hideBin(process.argv).some((arg) => arg.split('=')[0] === '--export-translations')) {
|
|
134
130
|
process.stderr.write('--export-translations is deprecated and will be removed in a future release. Use --export-descriptions.\n');
|
|
135
131
|
}
|
|
132
|
+
// Dynamic toolsets are gone. yargs ignores the unknown flag, so without this the
|
|
133
|
+
// server would start with a quietly different tool list: the flag used to drop
|
|
134
|
+
// `all` from the enabled toolsets, so a setup that passed only this one went from
|
|
135
|
+
// no toolsets plus three meta-tools to every toolset enabled.
|
|
136
|
+
//
|
|
137
|
+
// Only worth saying to someone who had it switched on. A setting left at `false`
|
|
138
|
+
// asked for what it now gets, so a notice claiming the tool list changed would be
|
|
139
|
+
// wrong.
|
|
140
|
+
const asksForDynamicToolsets = (value) => value !== undefined &&
|
|
141
|
+
!['', '0', 'false', 'no'].includes(value.toLowerCase());
|
|
142
|
+
const dynamicToolsetsFlag = hideBin(process.argv).find((arg) => arg.split('=')[0] === '--dynamic-toolsets');
|
|
143
|
+
if ((dynamicToolsetsFlag !== undefined &&
|
|
144
|
+
asksForDynamicToolsets(dynamicToolsetsFlag.split('=')[1] ?? 'true')) ||
|
|
145
|
+
asksForDynamicToolsets(process.env.ENABLE_DYNAMIC_TOOLSETS)) {
|
|
146
|
+
process.stderr.write('Dynamic toolsets have been removed, and --dynamic-toolsets / ENABLE_DYNAMIC_TOOLSETS no longer do anything. Every toolset is enabled unless you narrow it with --enable-toolsets or ENABLE_TOOLSETS.\n');
|
|
147
|
+
}
|
|
136
148
|
const clientRegistry = oauthConfig
|
|
137
149
|
? createOAuthBacklogClientRegistry(oauthConfig.backlogDomain)
|
|
138
150
|
: createBacklogClientRegistry();
|
|
@@ -144,24 +156,28 @@ if (tokenStore) {
|
|
|
144
156
|
cleanupTimer.unref();
|
|
145
157
|
}
|
|
146
158
|
const useFields = argv.optimizeResponse;
|
|
147
|
-
const
|
|
159
|
+
const descriptionOverrides = loadDescriptionOverrides();
|
|
160
|
+
const descriptionHelper = createDescriptionHelper(descriptionOverrides);
|
|
148
161
|
const maxTokens = argv.maxTokens;
|
|
149
162
|
const prefix = argv.prefix;
|
|
150
|
-
const enabledToolsets = argv.
|
|
151
|
-
? argv.enableToolsets.filter((a) => a !== 'all')
|
|
152
|
-
: argv.enableToolsets;
|
|
163
|
+
const enabledToolsets = argv.enableToolsets;
|
|
153
164
|
const mcpOption = {
|
|
154
165
|
useFields: useFields,
|
|
155
166
|
maxTokens,
|
|
156
167
|
prefix,
|
|
157
168
|
useOrganization: clientRegistry.isMultiOrganization,
|
|
158
169
|
};
|
|
159
|
-
// Built once and shared by every server the factory produces.
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
// Sharing it makes toolset state process-wide, which is the only scope left now
|
|
163
|
-
// that the protocol has no sessions.
|
|
170
|
+
// Built once and shared by every server the factory produces. Nothing mutates
|
|
171
|
+
// it, so this is purely to avoid rebuilding the whole tool tree per request under
|
|
172
|
+
// the stateless HTTP model.
|
|
164
173
|
const sharedToolsetGroup = buildToolsetGroup(backlog, descriptionHelper, enabledToolsets);
|
|
174
|
+
reportUnknownOverrideKeys({
|
|
175
|
+
overrides: descriptionOverrides,
|
|
176
|
+
version,
|
|
177
|
+
backlog,
|
|
178
|
+
clientRegistry,
|
|
179
|
+
mcpOption,
|
|
180
|
+
});
|
|
165
181
|
// Factory: creates a fresh MCP server with all tools registered.
|
|
166
182
|
// Used once per stdio connection; one fresh instance per HTTP request.
|
|
167
183
|
const createServer = () => createBacklogMcpServer({
|
|
@@ -172,7 +188,6 @@ const createServer = () => createBacklogMcpServer({
|
|
|
172
188
|
descriptionHelper,
|
|
173
189
|
enabledToolsets,
|
|
174
190
|
mcpOption,
|
|
175
|
-
dynamicToolsets: argv.dynamicToolsets,
|
|
176
191
|
toolsetGroup: sharedToolsetGroup,
|
|
177
192
|
});
|
|
178
193
|
if (argv.exportDescriptions) {
|
|
@@ -187,7 +202,6 @@ if (argv.exportDescriptions) {
|
|
|
187
202
|
descriptionHelper,
|
|
188
203
|
enabledToolsets: ['all'],
|
|
189
204
|
mcpOption,
|
|
190
|
-
dynamicToolsets: true,
|
|
191
205
|
});
|
|
192
206
|
const data = descriptionHelper.dump();
|
|
193
207
|
// eslint-disable-next-line no-console
|
package/build/lib.d.ts
CHANGED
|
@@ -20,6 +20,6 @@ export { buildToolSchema } from './types/tool.js';
|
|
|
20
20
|
export { isErrorLike } from './types/result.js';
|
|
21
21
|
export type { ComposeOptions } from './handlers/builders/composeToolHandler.js';
|
|
22
22
|
export type { DescriptionHelper } from './createDescriptionHelper.js';
|
|
23
|
-
export type { ToolDefinition, DynamicToolDefinition
|
|
23
|
+
export type { ToolDefinition, DynamicToolDefinition } from './types/tool.js';
|
|
24
24
|
export type { Toolset, ToolsetGroup, DynamicToolset, DynamicToolsetGroup, } from './types/toolsets.js';
|
|
25
25
|
export type { ErrorLike, SafeResult } from './types/result.js';
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reads description overrides from a `.backlog-mcp-serverrc` file (`.json`,
|
|
3
|
-
* `.yaml` or `.yml`) in the user's home directory.
|
|
4
|
-
*
|
|
5
|
-
* Node-only, and kept separate from `createDescriptionHelper` for that reason:
|
|
6
|
-
* cosmiconfig walks the filesystem and the default search path is the home
|
|
7
|
-
* directory. The CLI calls this and hands the result to the helper.
|
|
8
|
-
*
|
|
9
|
-
* The file is user-authored, so its contents are unknown: anything that is not a
|
|
10
|
-
* string is dropped here rather than passed on. Most overrides end up in a tool
|
|
11
|
-
* description, and a number or an array there would produce an invalid
|
|
12
|
-
* `tools/list` payload.
|
|
13
|
-
*/
|
|
14
1
|
export declare function loadDescriptionOverrides(options?: {
|
|
15
2
|
configName?: string;
|
|
16
3
|
searchDir?: string;
|