pi-mcp-adapter 2.13.0 → 2.14.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/CHANGELOG.md +9 -0
- package/README.md +6 -0
- package/init.ts +6 -5
- package/json-schema-validator.ts +55 -0
- package/package.json +2 -1
- package/proxy-modes.ts +3 -3
- package/server-manager.ts +96 -72
- package/types.ts +2 -0
- package/utils.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.14.0] - 2026-07-25
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Added the global `settings.showStatusIcon` opt-out for plain `MCP: ...` status and connection text while keeping the plug icon enabled by default. Thanks @vaultboy001 for issue #216.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Deferred implicit OAuth credential-store access until an HTTP server actually challenges for authentication, so unauthenticated remote Streamable HTTP servers work in headless environments. Thanks @vdom-1 for issue #218.
|
|
17
|
+
- Accepted draft-07 tool output schemas alongside JSON Schema 2020-12 while preserving structured-content validation. Thanks Daniel Marbach (@danielmarbach) for issue #217.
|
|
18
|
+
|
|
10
19
|
## [2.13.0] - 2026-07-25
|
|
11
20
|
|
|
12
21
|
### Added
|
package/README.md
CHANGED
|
@@ -237,6 +237,7 @@ When any enabled server uses `eager` or `keep-alive`, initialization also starts
|
|
|
237
237
|
"toolPrefix": "server",
|
|
238
238
|
"idleTimeout": 10,
|
|
239
239
|
"requestTimeoutMs": 30000,
|
|
240
|
+
"showStatusIcon": true,
|
|
240
241
|
"hostConfigDiscovery": "off",
|
|
241
242
|
"oauthDir": ".pi/mcp-oauth",
|
|
242
243
|
"trace": {
|
|
@@ -255,6 +256,7 @@ When any enabled server uses `eager` or `keep-alive`, initialization also starts
|
|
|
255
256
|
| `toolPrefix` | `"server"` (default), `"short"` (strips `-mcp` suffix), `"none"`, or `"mcp"` (prefixes with `mcp__`, using server-mode normalization) |
|
|
256
257
|
| `idleTimeout` | Global idle timeout in minutes (default: 10, 0 to disable) |
|
|
257
258
|
| `requestTimeoutMs` | Global request timeout in milliseconds for live MCP calls (if omitted or `<= 0`, the MCP SDK default timeout is used) |
|
|
259
|
+
| `showStatusIcon` | Show the plug icon in MCP status and connection text (default: `true`). Set to `false` for plain `MCP: ...` text. |
|
|
258
260
|
| `hostConfigDiscovery` | Host-specific config policy: `"off"` (default), `"prompt"` (detect/report only), or `"on"` (explicitly load detected host configs as the lowest-precedence fallback) |
|
|
259
261
|
| `oauthDir` | Legacy OAuth `tokens.json` import directory for this MCP config. Relative paths resolve from the active project cwd. `MCP_OAUTH_DIR` still wins when set. Persistent OAuth credentials are stored in the OS credential store, not this directory. |
|
|
260
262
|
| `directTools` | Global default for all servers (default: false). Per-server overrides this. |
|
|
@@ -522,6 +524,10 @@ If `settings.autoAuth` is `true`, `mcp({ connect: ... })`, `mcp({ tool: ... })`,
|
|
|
522
524
|
|
|
523
525
|
In interactive sessions, you can also authenticate from `/mcp` with `ctrl+a` or Enter on a server that needs auth. In remote/headless sessions, use the proxy tool's `auth-start` and `auth-complete` actions to copy the authorization URL locally and paste the redirect URL back into Pi. `/mcp-auth` without a server only opens a picker in the interactive UI.
|
|
524
526
|
|
|
527
|
+
### MCP output schemas
|
|
528
|
+
|
|
529
|
+
Advertised tool `outputSchema` values support JSON Schema draft-07 and 2020-12. Unstamped schemas use the SDK's 2020-12 default. Returned `structuredContent` is validated against the advertised schema for both proxy and direct-tool calls.
|
|
530
|
+
|
|
525
531
|
## How It Works
|
|
526
532
|
|
|
527
533
|
- One `mcp` tool in context (~200 tokens) instead of hundreds
|
package/init.ts
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
import { McpServerManager } from "./server-manager.ts";
|
|
23
23
|
import { buildToolMetadata, totalToolCount } from "./tool-metadata.ts";
|
|
24
24
|
import { UiResourceHandler } from "./ui-resource-handler.ts";
|
|
25
|
-
import { openUrl, parallelLimit, sanitizeTerminalText } from "./utils.ts";
|
|
25
|
+
import { formatMcpStatus, openUrl, parallelLimit, sanitizeTerminalText } from "./utils.ts";
|
|
26
26
|
import { logger } from "./logger.ts";
|
|
27
27
|
import { throwIfAborted } from "./abort.ts";
|
|
28
28
|
import { getAuthStorageOptions } from "./mcp-auth.ts";
|
|
@@ -261,7 +261,7 @@ export async function initializeMcp(
|
|
|
261
261
|
});
|
|
262
262
|
|
|
263
263
|
if (ui && startupServers.length > 0) {
|
|
264
|
-
ui.setStatus("mcp",
|
|
264
|
+
ui.setStatus("mcp", formatMcpStatus(state.config, `connecting to ${startupServers.length} servers...`));
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
const results = await parallelLimit(startupServers, 10, async ([name, definition]) => {
|
|
@@ -523,10 +523,11 @@ export function updateStatusBar(state: McpExtensionState): void {
|
|
|
523
523
|
const definition = state.config.mcpServers[name];
|
|
524
524
|
return connection.status === "connected" && definition !== undefined && !isServerDisabled(definition);
|
|
525
525
|
}).length;
|
|
526
|
-
let status =
|
|
526
|
+
let status = `${enabledCount} ${enabledCount === 1 ? "server" : "servers"} enabled`;
|
|
527
527
|
if (connectedCount > 0) status += ` (${connectedCount} connected)`;
|
|
528
528
|
if (disabledCount > 0) status += ` (${disabledCount} disabled)`;
|
|
529
|
-
|
|
529
|
+
const formattedStatus = formatMcpStatus(state.config, status);
|
|
530
|
+
ui.setStatus("mcp", ui.theme ? ui.theme.fg("accent", formattedStatus) : formattedStatus);
|
|
530
531
|
}
|
|
531
532
|
|
|
532
533
|
export function getFailureAgeSeconds(state: McpExtensionState, serverName: string): number | null {
|
|
@@ -563,7 +564,7 @@ export async function lazyConnect(state: McpExtensionState, serverName: string,
|
|
|
563
564
|
|
|
564
565
|
try {
|
|
565
566
|
if (state.ui) {
|
|
566
|
-
state.ui.setStatus("mcp",
|
|
567
|
+
state.ui.setStatus("mcp", formatMcpStatus(state.config, `connecting to ${serverName}...`));
|
|
567
568
|
}
|
|
568
569
|
const newConnection = await state.manager.connect(serverName, definition, ownedSignal);
|
|
569
570
|
if (newConnection.status === "needs-auth") {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Ajv,
|
|
3
|
+
AjvJsonSchemaValidator,
|
|
4
|
+
addFormats,
|
|
5
|
+
} from "@modelcontextprotocol/client/validators/ajv";
|
|
6
|
+
import type {
|
|
7
|
+
JsonSchemaType,
|
|
8
|
+
JsonSchemaValidator,
|
|
9
|
+
jsonSchemaValidator as JsonSchemaValidatorProvider,
|
|
10
|
+
} from "@modelcontextprotocol/client";
|
|
11
|
+
|
|
12
|
+
type SchemaDialect =
|
|
13
|
+
| { status: "unstamped" }
|
|
14
|
+
| { status: "stamped"; uri: string };
|
|
15
|
+
|
|
16
|
+
const DRAFT_07_SCHEMA_URIS: ReadonlySet<string> = new Set([
|
|
17
|
+
"http://json-schema.org/draft-07/schema",
|
|
18
|
+
"https://json-schema.org/draft-07/schema",
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
function schemaDialect(schema: JsonSchemaType): SchemaDialect {
|
|
22
|
+
if (!("$schema" in schema) || typeof schema.$schema !== "string") {
|
|
23
|
+
return { status: "unstamped" };
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
status: "stamped",
|
|
27
|
+
uri: schema.$schema.endsWith("#") ? schema.$schema.slice(0, -1) : schema.$schema,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createJsonSchemaValidator(): JsonSchemaValidatorProvider {
|
|
32
|
+
const defaultValidator = new AjvJsonSchemaValidator();
|
|
33
|
+
let draft07Validator: AjvJsonSchemaValidator | undefined;
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
getValidator<T>(schema: JsonSchemaType): JsonSchemaValidator<T> {
|
|
37
|
+
const dialect = schemaDialect(schema);
|
|
38
|
+
if (dialect.status !== "stamped" || !DRAFT_07_SCHEMA_URIS.has(dialect.uri)) {
|
|
39
|
+
return defaultValidator.getValidator<T>(schema);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
draft07Validator ??= (() => {
|
|
43
|
+
const ajv = new Ajv({
|
|
44
|
+
strict: false,
|
|
45
|
+
validateFormats: true,
|
|
46
|
+
validateSchema: false,
|
|
47
|
+
allErrors: true,
|
|
48
|
+
});
|
|
49
|
+
addFormats(ajv);
|
|
50
|
+
return new AjvJsonSchemaValidator(ajv);
|
|
51
|
+
})();
|
|
52
|
+
return draft07Validator.getValidator<T>(schema);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mcp-adapter",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"description": "MCP (Model Context Protocol) adapter extension for Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./index.ts",
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"ui-stream-types.ts",
|
|
76
76
|
"config.ts",
|
|
77
77
|
"server-manager.ts",
|
|
78
|
+
"json-schema-validator.ts",
|
|
78
79
|
"session-recovery.ts",
|
|
79
80
|
"sampling-handler.ts",
|
|
80
81
|
"elicitation-handler.ts",
|
package/proxy-modes.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { reconstructPromptMetadata } from "./metadata-cache.ts";
|
|
|
12
12
|
import { resolveMcpResultContent, transformMcpContent } from "./tool-registrar.ts";
|
|
13
13
|
import { guardMcpOutput, guardedMcpDetails, resolveMcpOutputGuardOptions } from "./mcp-output-guard.ts";
|
|
14
14
|
import { maybeStartUiSession, summarizeUiSessionResult, type UiSessionRuntime } from "./ui-session.ts";
|
|
15
|
-
import { formatAuthRequiredMessage, resolveServerUrl, truncateAtWord } from "./utils.ts";
|
|
15
|
+
import { formatAuthRequiredMessage, formatMcpStatus, resolveServerUrl, truncateAtWord } from "./utils.ts";
|
|
16
16
|
import { authenticate, completeAuthFromInput, startAuth, supportsOAuth } from "./mcp-auth-flow.ts";
|
|
17
17
|
import { SessionRecoveryAuthRequiredError, withSessionRecovery } from "./session-recovery.ts";
|
|
18
18
|
|
|
@@ -663,7 +663,7 @@ export async function executeConnect(state: McpExtensionState, serverName: strin
|
|
|
663
663
|
|
|
664
664
|
try {
|
|
665
665
|
if (state.ui) {
|
|
666
|
-
state.ui.setStatus("mcp",
|
|
666
|
+
state.ui.setStatus("mcp", formatMcpStatus(state.config, `connecting to ${serverName}...`));
|
|
667
667
|
}
|
|
668
668
|
const currentConnection = state.manager.getConnection(serverName);
|
|
669
669
|
let connection = currentConnection?.status === "connected"
|
|
@@ -948,7 +948,7 @@ export async function executeCall(
|
|
|
948
948
|
|
|
949
949
|
try {
|
|
950
950
|
if (state.ui) {
|
|
951
|
-
state.ui.setStatus("mcp",
|
|
951
|
+
state.ui.setStatus("mcp", formatMcpStatus(state.config, `connecting to ${serverName}...`));
|
|
952
952
|
}
|
|
953
953
|
connection = await state.manager.connect(serverName, definition, ownedSignal);
|
|
954
954
|
if (connection.status === "needs-auth") {
|
package/server-manager.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
Client,
|
|
3
3
|
SSEClientTransport,
|
|
4
4
|
StreamableHTTPClientTransport,
|
|
5
|
+
SdkHttpError,
|
|
5
6
|
UnauthorizedError,
|
|
6
7
|
type RequestOptions,
|
|
7
8
|
type ReadResourceResult,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
} from "./types.ts";
|
|
21
22
|
import { SERVER_STREAM_RESULT_PATCH_METHOD, serverStreamResultPatchNotificationSchema } from "./types.ts";
|
|
22
23
|
import { resolveNpxBinary } from "./npx-resolver.ts";
|
|
24
|
+
import { createJsonSchemaValidator } from "./json-schema-validator.ts";
|
|
23
25
|
import { logger } from "./logger.ts";
|
|
24
26
|
import { McpOAuthProvider } from "./mcp-oauth-provider.ts";
|
|
25
27
|
import { extractOAuthConfig, supportsOAuth, type McpOAuthRuntime } from "./mcp-auth-flow.ts";
|
|
@@ -50,6 +52,16 @@ const MCP_CLIENT_OPTIONS = {
|
|
|
50
52
|
};
|
|
51
53
|
const abortCleanupPromises = new WeakMap<object, Promise<void>>();
|
|
52
54
|
|
|
55
|
+
type HttpAuthProviderState =
|
|
56
|
+
| { status: "disabled" }
|
|
57
|
+
| { status: "implicit-deferred" }
|
|
58
|
+
| { status: "explicit"; provider: McpOAuthProvider }
|
|
59
|
+
| { status: "implicit-challenged"; provider: McpOAuthProvider };
|
|
60
|
+
|
|
61
|
+
function isUnauthorizedHttpError(error: unknown): boolean {
|
|
62
|
+
return error instanceof UnauthorizedError || (error instanceof SdkHttpError && error.status === 401);
|
|
63
|
+
}
|
|
64
|
+
|
|
53
65
|
function boundedStderrChunk(chunk: Buffer | string): Buffer {
|
|
54
66
|
if (Buffer.isBuffer(chunk)) {
|
|
55
67
|
const start = Math.max(0, chunk.byteLength - MAX_CAPTURED_STDERR_BYTES);
|
|
@@ -406,7 +418,7 @@ export class McpServerManager {
|
|
|
406
418
|
|
|
407
419
|
// Check for UnauthorizedError - server requires OAuth. A cleanup failure
|
|
408
420
|
// remains a setup failure rather than being hidden behind needs-auth.
|
|
409
|
-
if (error
|
|
421
|
+
if (isUnauthorizedHttpError(error) && supportsOAuth(definition) && cleanupFailures.length === 0) {
|
|
410
422
|
return {
|
|
411
423
|
client,
|
|
412
424
|
transport,
|
|
@@ -484,6 +496,7 @@ export class McpServerManager {
|
|
|
484
496
|
{ name: `pi-mcp-${serverName}`, version: "1.0.0" },
|
|
485
497
|
{
|
|
486
498
|
...MCP_CLIENT_OPTIONS,
|
|
499
|
+
jsonSchemaValidator: createJsonSchemaValidator(),
|
|
487
500
|
...(Object.keys(capabilities).length > 0 ? { capabilities } : {}),
|
|
488
501
|
listChanged: {
|
|
489
502
|
tools: {
|
|
@@ -630,88 +643,99 @@ export class McpServerManager {
|
|
|
630
643
|
|
|
631
644
|
// Create request init with headers (Authorization now included for bearer auth)
|
|
632
645
|
const requestInit = Object.keys(headers).length > 0 ? { headers } : undefined;
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
serverUrl,
|
|
641
|
-
oauthConfig,
|
|
642
|
-
{
|
|
643
|
-
onRedirect: async (_authUrl) => {
|
|
644
|
-
// URL is captured by startAuth, no need to log
|
|
645
|
-
},
|
|
646
|
+
const createAuthProvider = (): McpOAuthProvider => new McpOAuthProvider(
|
|
647
|
+
serverName,
|
|
648
|
+
serverUrl,
|
|
649
|
+
extractOAuthConfig(definition),
|
|
650
|
+
{
|
|
651
|
+
onRedirect: async (_authUrl) => {
|
|
652
|
+
// URL is captured by startAuth, no need to log
|
|
646
653
|
},
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
// Try StreamableHTTP first (modern MCP servers)
|
|
653
|
-
const streamableTransport = new StreamableHTTPClientTransport(url, {
|
|
654
|
-
requestInit,
|
|
655
|
-
authProvider,
|
|
656
|
-
});
|
|
657
|
-
const probeTransport = traceObserver
|
|
658
|
-
? wrapTransportWithMcpTrace(streamableTransport, serverName, "streamable-http", traceObserver)
|
|
659
|
-
: streamableTransport;
|
|
660
|
-
|
|
661
|
-
const testClient = new Client(
|
|
662
|
-
{ name: "pi-mcp-probe", version: "2.1.2" },
|
|
663
|
-
MCP_CLIENT_OPTIONS,
|
|
654
|
+
},
|
|
655
|
+
this.authStorageOptions,
|
|
656
|
+
this.oauthRuntime?.signal,
|
|
664
657
|
);
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
658
|
+
|
|
659
|
+
// Explicit OAuth must check the secure credential store before connecting.
|
|
660
|
+
let authState: HttpAuthProviderState = supportsOAuth(definition)
|
|
661
|
+
? definition.auth === undefined
|
|
662
|
+
? { status: "implicit-deferred" }
|
|
663
|
+
: { status: "explicit", provider: createAuthProvider() }
|
|
664
|
+
: { status: "disabled" };
|
|
665
|
+
|
|
666
|
+
// Try StreamableHTTP first (modern MCP servers). For implicit OAuth, defer
|
|
667
|
+
// creating the provider until the server proves that authentication is needed.
|
|
668
|
+
for (;;) {
|
|
669
|
+
const authProvider = "provider" in authState ? authState.provider : undefined;
|
|
670
|
+
const streamableTransport = new StreamableHTTPClientTransport(url, {
|
|
671
|
+
requestInit,
|
|
672
|
+
authProvider,
|
|
673
|
+
});
|
|
674
|
+
const probeTransport = traceObserver
|
|
675
|
+
? wrapTransportWithMcpTrace(streamableTransport, serverName, "streamable-http", traceObserver)
|
|
676
|
+
: streamableTransport;
|
|
677
|
+
const testClient = new Client(
|
|
678
|
+
{ name: "pi-mcp-probe", version: "2.1.2" },
|
|
679
|
+
MCP_CLIENT_OPTIONS,
|
|
672
680
|
);
|
|
673
|
-
probeCleanupAttempted =
|
|
681
|
+
let probeCleanupAttempted = false;
|
|
674
682
|
try {
|
|
675
|
-
await
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
return new StreamableHTTPClientTransport(url, { requestInit, authProvider });
|
|
682
|
-
} catch (error) {
|
|
683
|
-
if (error instanceof AggregateError && (
|
|
684
|
-
error.message === "MCP connection abort cleanup failed" ||
|
|
685
|
-
error.message === "MCP HTTP probe cleanup failed"
|
|
686
|
-
)) {
|
|
687
|
-
throw error;
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
// StreamableHTTP failed, close through the SDK client and try SSE fallback.
|
|
691
|
-
// If connectClientWithAbort already owned the close, await that operation
|
|
692
|
-
// instead of closing the same transport again.
|
|
693
|
-
if (!probeCleanupAttempted) {
|
|
683
|
+
await this.connectClientWithAbort(
|
|
684
|
+
testClient,
|
|
685
|
+
probeTransport,
|
|
686
|
+
this.buildRequestOptions(definition, requestSignal),
|
|
687
|
+
signal,
|
|
688
|
+
);
|
|
694
689
|
probeCleanupAttempted = true;
|
|
695
690
|
try {
|
|
696
|
-
await
|
|
691
|
+
await testClient.close();
|
|
697
692
|
} catch (cleanupError) {
|
|
698
|
-
throw new AggregateError([
|
|
693
|
+
throw new AggregateError([cleanupError], "MCP HTTP probe cleanup failed");
|
|
699
694
|
}
|
|
700
|
-
}
|
|
701
695
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
696
|
+
// StreamableHTTP works - create fresh transport for actual use
|
|
697
|
+
return new StreamableHTTPClientTransport(url, { requestInit, authProvider });
|
|
698
|
+
} catch (error) {
|
|
699
|
+
if (error instanceof AggregateError && (
|
|
700
|
+
error.message === "MCP connection abort cleanup failed" ||
|
|
701
|
+
error.message === "MCP HTTP probe cleanup failed"
|
|
702
|
+
)) {
|
|
703
|
+
throw error;
|
|
704
|
+
}
|
|
707
705
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
706
|
+
// StreamableHTTP failed, close through the SDK client and try SSE fallback.
|
|
707
|
+
// If connectClientWithAbort already owned the close, await that operation
|
|
708
|
+
// instead of closing the same transport twice.
|
|
709
|
+
if (!probeCleanupAttempted) {
|
|
710
|
+
probeCleanupAttempted = true;
|
|
711
|
+
try {
|
|
712
|
+
await (abortCleanupPromises.get(probeTransport) ?? testClient.close());
|
|
713
|
+
} catch (cleanupError) {
|
|
714
|
+
throw new AggregateError([error, cleanupError], "MCP HTTP probe cleanup failed");
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Host cancellation is not transport capability evidence; do not fall
|
|
719
|
+
// through to SSE when the caller is trying to cancel the connect.
|
|
720
|
+
if (signal?.aborted) {
|
|
721
|
+
throwIfAborted(signal);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// An implicit URL-only server gets a provider only after a real auth
|
|
725
|
+
// challenge. This keeps unauthenticated servers independent of storage.
|
|
726
|
+
if (authState.status === "implicit-deferred" && isUnauthorizedHttpError(error)) {
|
|
727
|
+
authState = { status: "implicit-challenged", provider: createAuthProvider() };
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// If this was an UnauthorizedError, don't try SSE - the server needs auth
|
|
732
|
+
if (isUnauthorizedHttpError(error)) {
|
|
733
|
+
throw error;
|
|
734
|
+
}
|
|
712
735
|
|
|
713
|
-
|
|
714
|
-
|
|
736
|
+
// SSE is the legacy transport
|
|
737
|
+
return new SSEClientTransport(url, { requestInit, authProvider });
|
|
738
|
+
}
|
|
715
739
|
}
|
|
716
740
|
}
|
|
717
741
|
|
package/types.ts
CHANGED
|
@@ -395,6 +395,8 @@ export interface McpTraceSettings {
|
|
|
395
395
|
|
|
396
396
|
export interface McpSettings {
|
|
397
397
|
toolPrefix?: ToolPrefix;
|
|
398
|
+
/** Show the plug prefix in MCP status and connection text (default: true). Set to false to disable it. */
|
|
399
|
+
showStatusIcon?: boolean;
|
|
398
400
|
/** Discover detected host-specific MCP configs only when explicitly enabled. */
|
|
399
401
|
hostConfigDiscovery?: HostConfigDiscovery;
|
|
400
402
|
idleTimeout?: number; // minutes, default 10, 0 to disable
|
package/utils.ts
CHANGED
|
@@ -221,6 +221,10 @@ export function formatAuthRequiredMessage(
|
|
|
221
221
|
return template ? template.replaceAll("${server}", serverName) : defaultMessage;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
export function formatMcpStatus(config: Pick<McpConfig, "settings">, message: string): string {
|
|
225
|
+
return `${config.settings?.showStatusIcon === false ? "MCP: " : "🔌 MCP: "}${message}`;
|
|
226
|
+
}
|
|
227
|
+
|
|
224
228
|
/**
|
|
225
229
|
* Extract the adapter-owned UI stream mode from tool metadata.
|
|
226
230
|
*/
|