shortcutxl 0.3.50 → 0.3.51
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/dist/app/agent-session.d.ts +4 -0
- package/dist/app/agent-session.js +47 -10
- package/dist/app/approvals/types.d.ts +5 -0
- package/dist/app/auth/auth-storage.js +2 -7
- package/dist/app/extensions/slash-commands.js +1 -0
- package/dist/app/index.d.ts +2 -0
- package/dist/app/index.js +1 -0
- package/dist/app/local/tools/execute-code/observability.d.ts +4 -0
- package/dist/app/local/tools/execute-code/observability.js +57 -0
- package/dist/app/mcp/auth-status.d.ts +9 -1
- package/dist/app/mcp/auth-status.js +27 -16
- package/dist/app/mcp/catalog.d.ts +2 -1
- package/dist/app/mcp/catalog.js +15 -3
- package/dist/app/mcp/config.d.ts +14 -9
- package/dist/app/mcp/config.js +47 -61
- package/dist/app/mcp/connection.d.ts +1 -0
- package/dist/app/mcp/connection.js +100 -5
- package/dist/app/mcp/hosted-oauth-id.js +1 -4
- package/dist/app/mcp/hosted-oauth-provider.js +1 -1
- package/dist/app/mcp/index.js +24 -6
- package/dist/app/mcp/install.d.ts +21 -0
- package/dist/app/mcp/install.js +68 -0
- package/dist/app/mcp/oauth-discovery.d.ts +1 -0
- package/dist/app/mcp/oauth-discovery.js +45 -5
- package/dist/app/mcp/types.d.ts +2 -0
- package/dist/app/modes/action/agent.d.ts +3 -3
- package/dist/app/modes/action/agent.js +4 -9
- package/dist/app/modes/action/prompt.js +5 -5
- package/dist/app/modes/ask/agent.js +2 -1
- package/dist/app/modes/plan/agent.js +2 -1
- package/dist/app/modes/switch-mode.d.ts +8 -0
- package/dist/app/modes/switch-mode.js +9 -5
- package/dist/app/prompts/com-api-reference.json +215 -146
- package/dist/app/providers/model-resolver.d.ts +12 -0
- package/dist/app/providers/model-resolver.js +30 -0
- package/dist/app/providers/shortcut-stream.js +5 -1
- package/dist/app/session/permission-context.d.ts +3 -0
- package/dist/app/session/permission-context.js +4 -0
- package/dist/app/session/tool-registry.js +13 -2
- package/dist/app/settings-manager.js +3 -3
- package/dist/app/sync/skills-download.d.ts +16 -0
- package/dist/app/sync/skills-download.js +65 -6
- package/dist/app/sync/skills-status.d.ts +44 -0
- package/dist/app/sync/skills-status.js +279 -0
- package/dist/app/sync/skills-upload.d.ts +15 -0
- package/dist/app/sync/skills-upload.js +62 -6
- package/dist/app/tools/get-tool-info.js +1 -0
- package/dist/app/tools/index.d.ts +1 -0
- package/dist/app/tools/index.js +1 -0
- package/dist/app/tools/session-settings/get-session-settings.d.ts +11 -0
- package/dist/app/tools/session-settings/get-session-settings.js +36 -0
- package/dist/app/tools/session-settings/index.d.ts +3 -0
- package/dist/app/tools/session-settings/index.js +3 -0
- package/dist/app/tools/session-settings/update-session-settings.d.ts +15 -0
- package/dist/app/tools/session-settings/update-session-settings.js +77 -0
- package/dist/app/tools/switch-mode.d.ts +4 -3
- package/dist/app/tools/switch-mode.js +26 -8
- package/dist/app/tools/task/manager.js +0 -2
- package/dist/app/tools/task/send-message.js +10 -3
- package/dist/app/tools/task/task.js +4 -1
- package/dist/cli/args.js +3 -1
- package/dist/cli/mcp-commands.d.ts +2 -0
- package/dist/cli/mcp-commands.js +358 -0
- package/dist/cli.js +1083 -1061
- package/dist/config.js +2 -1
- package/dist/main.js +50 -58
- package/dist/model-ids.d.ts +4 -0
- package/dist/model-ids.js +3 -0
- package/dist/shared/files/lock-utils.d.ts +10 -0
- package/dist/shared/files/lock-utils.js +32 -0
- package/dist/shared/logging/agent-log.d.ts +1 -0
- package/dist/shared/logging/agent-log.js +4 -1
- package/dist/shared/platform/open.js +12 -5
- package/dist/shell/approvals/approval.d.ts +3 -19
- package/dist/shell/approvals/approval.js +2 -59
- package/dist/shell/approvals/excel-approval.d.ts +14 -3
- package/dist/shell/approvals/excel-approval.js +77 -7
- package/dist/shell/approvals/switch-mode-approval.d.ts +10 -0
- package/dist/shell/approvals/switch-mode-approval.js +35 -0
- package/dist/shell/approvals/task-spawn-approval.d.ts +6 -7
- package/dist/shell/approvals/task-spawn-approval.js +7 -29
- package/dist/shell/commands/mcp-command.d.ts +7 -1
- package/dist/shell/commands/mcp-command.js +93 -69
- package/dist/shell/components/interactive-shell-layout.d.ts +13 -2
- package/dist/shell/components/line-selection-scroll-container.d.ts +43 -0
- package/dist/shell/components/line-selection-scroll-container.js +245 -0
- package/dist/shell/components/primitives/footer.js +40 -11
- package/dist/shell/components/primitives/grid-table.d.ts +8 -1
- package/dist/shell/components/primitives/grid-table.js +45 -7
- package/dist/shell/export-html/canonical-trace-html.js +1 -1
- package/dist/shell/export-html/template.css +1124 -1110
- package/dist/shell/export-html/template.js +15 -11
- package/dist/shell/extension-ui.d.ts +1 -0
- package/dist/shell/index.d.ts +1 -0
- package/dist/shell/index.js +1 -0
- package/dist/shell/interactive/interactive-actions.d.ts +1 -0
- package/dist/shell/interactive/interactive-actions.js +1 -0
- package/dist/shell/interactive/interactive-mode-options.d.ts +8 -0
- package/dist/shell/interactive/interactive-mode.d.ts +18 -0
- package/dist/shell/interactive/interactive-mode.js +317 -11
- package/dist/shell/interactive/skills-workflow.d.ts +10 -2
- package/dist/shell/interactive/skills-workflow.js +66 -2
- package/dist/shell/keybindings.d.ts +1 -1
- package/dist/shell/keybindings.js +2 -0
- package/dist/shell/presentation/slash-command-rendering.js +2 -0
- package/dist/shell/session-client.d.ts +8 -0
- package/dist/shell/session-client.js +11 -1
- package/dist/shell/tools/presentation/registry.js +2 -2
- package/dist/shell/tools/presentation/renderers/tool-meta.d.ts +2 -2
- package/dist/shell/tools/presentation/renderers/tool-meta.js +4 -3
- package/dist/startup/interactive-commands.d.ts +8 -0
- package/dist/startup/interactive-commands.js +30 -0
- package/dist/tool-names.d.ts +2 -0
- package/dist/tool-names.js +2 -0
- package/dist/tui/terminal.js +2 -2
- package/package.json +1 -1
- package/skills/mcp/SKILL.md +94 -186
- package/user-docs/dist/index.html +13 -10
- package/user-docs/dist/shortcutxl-docs.pdf +0 -0
- package/xll/ShortcutXL.xll +0 -0
- package/xll/python/Lib/site-packages/httpx-0.28.1.dist-info/RECORD +1 -1
- package/xll/python/Lib/site-packages/pip-26.1.1.dist-info/RECORD +3 -3
- package/xll/python/Scripts/httpx.exe +0 -0
- package/xll/python/Scripts/pip.exe +0 -0
- package/xll/python/Scripts/pip3.12.exe +0 -0
- package/xll/python/Scripts/pip3.exe +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.51]
|
|
4
|
+
|
|
5
|
+
- **TUI rendering and text selection** - Improved TUI rendering, scrolling. Fixed text highlighting and selection for copy pastes.
|
|
6
|
+
- **Agentic MCP setup** - Agents can now help set up MCP servers through `shortcut mcp add/list/get/login/logout/remove`, with clearer login, logs, and OAuth settings.
|
|
7
|
+
- **User-requested session settings** - When asked, agents can inspect available models and thinking levels or apply your requested session change.
|
|
8
|
+
- **Skill sync clarity** - Skill sync now shows startup differences plus per-file upload/download changes, source, and visibility.
|
|
9
|
+
- **Fast mode toggle** - Fast Mode now shows in the footer and can be toggled with `alt+f` or `/fast`, with clearer availability warnings.
|
|
10
|
+
- **Scrollable approvals** - Large approval and permission dialogs now scroll properly, including spreadsheet change tables in small terminals.
|
|
11
|
+
|
|
3
12
|
## [0.3.50]
|
|
4
13
|
|
|
5
14
|
- **MCP-enabled task agents** - General task agents can now use connected MCP servers, giving delegated work access to the same external tools and resources as the main CLI.
|
|
@@ -261,6 +261,10 @@ export declare class AgentSession {
|
|
|
261
261
|
get model(): Model<any> | undefined;
|
|
262
262
|
/** Current thinking level */
|
|
263
263
|
get thinkingLevel(): ThinkingLevel;
|
|
264
|
+
get isFastModeEnabled(): boolean;
|
|
265
|
+
get isFastModeAvailable(): boolean;
|
|
266
|
+
setFastModeEnabled(enabled: boolean): void;
|
|
267
|
+
toggleFastMode(): boolean;
|
|
264
268
|
/** Whether agent is currently streaming a response */
|
|
265
269
|
get isStreaming(): boolean;
|
|
266
270
|
/** Current effective system prompt (includes any per-turn extension modifications) */
|
|
@@ -16,6 +16,7 @@ import { isContextOverflow } from '@mariozechner/pi-ai';
|
|
|
16
16
|
import { SessionState } from '../core/session-state.js';
|
|
17
17
|
import { createCompactionActions, createErrorRecoveryActions, triggerCompactionIfNeeded } from '../core/session/compaction-bridge.js';
|
|
18
18
|
import { SessionCompaction } from '../core/session/session-compaction.js';
|
|
19
|
+
import { isShortcutFastModeModel } from '../model-ids.js';
|
|
19
20
|
import { formatFileUploadsContext, mergeFileUploads } from './file-uploads.js';
|
|
20
21
|
import { expandPromptTemplate } from './resources/prompt-template-expansion.js';
|
|
21
22
|
import { parseSkillBlock } from './resources/skill-block.js';
|
|
@@ -26,6 +27,7 @@ import { emitExtensionEvent } from './session/extension-emitter.js';
|
|
|
26
27
|
import { ExtensionLifecycle } from './session/extension-lifecycle.js';
|
|
27
28
|
import { ModelManager } from './session/model-manager.js';
|
|
28
29
|
import { resolvePendingRefreshOverride } from './session/pending-refresh-overrides.js';
|
|
30
|
+
import { formatRuntimePermissionBypassContext } from './session/permission-context.js';
|
|
29
31
|
import { PersistenceHandler } from './session/persistence-handler.js';
|
|
30
32
|
import { assertNotExtensionCommand, buildMessageContent, normalizeUserContent, tryExtensionCommand } from './session/prompt-pipeline.js';
|
|
31
33
|
import { QueueTracker } from './session/queue-tracker.js';
|
|
@@ -582,6 +584,24 @@ export class AgentSession {
|
|
|
582
584
|
get thinkingLevel() {
|
|
583
585
|
return this.agent.state.thinkingLevel;
|
|
584
586
|
}
|
|
587
|
+
get isFastModeEnabled() {
|
|
588
|
+
return this.isFastModeAvailable && this.agent.speed === 'fast';
|
|
589
|
+
}
|
|
590
|
+
get isFastModeAvailable() {
|
|
591
|
+
return isShortcutFastModeModel(this.model);
|
|
592
|
+
}
|
|
593
|
+
setFastModeEnabled(enabled) {
|
|
594
|
+
this.agent.speed = enabled && this.isFastModeAvailable ? 'fast' : undefined;
|
|
595
|
+
}
|
|
596
|
+
toggleFastMode() {
|
|
597
|
+
if (!this.isFastModeAvailable) {
|
|
598
|
+
this.agent.speed = undefined;
|
|
599
|
+
return false;
|
|
600
|
+
}
|
|
601
|
+
const enabled = !this.isFastModeEnabled;
|
|
602
|
+
this.setFastModeEnabled(enabled);
|
|
603
|
+
return enabled;
|
|
604
|
+
}
|
|
585
605
|
/** Whether agent is currently streaming a response */
|
|
586
606
|
get isStreaming() {
|
|
587
607
|
return this.agent.state.isStreaming;
|
|
@@ -792,18 +812,30 @@ export class AgentSession {
|
|
|
792
812
|
timestamp: Date.now()
|
|
793
813
|
});
|
|
794
814
|
this.lastPrePromptDetails = undefined;
|
|
815
|
+
const prePromptContextParts = [formatRuntimePermissionBypassContext(this.permissionPolicy)];
|
|
816
|
+
let prePromptDetails;
|
|
795
817
|
if (this._prePromptContext) {
|
|
796
818
|
const ctx = await this._prePromptContext();
|
|
797
819
|
if (ctx) {
|
|
798
820
|
this.lastPrePromptDetails = ctx.details;
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
821
|
+
prePromptDetails = ctx.details;
|
|
822
|
+
prePromptContextParts.unshift(ctx.text);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
messages.push({
|
|
826
|
+
role: 'custom',
|
|
827
|
+
customType: 'prePromptContext',
|
|
828
|
+
content: prePromptContextParts.join('\n\n'),
|
|
829
|
+
display: false,
|
|
830
|
+
details: prePromptDetails,
|
|
831
|
+
timestamp: Date.now()
|
|
832
|
+
});
|
|
833
|
+
for (const message of messages) {
|
|
834
|
+
if (message.role === 'custom') {
|
|
835
|
+
this.sessionManager.appendCustomMessageEntry(message.customType, message.content, message.display, message.details);
|
|
836
|
+
}
|
|
837
|
+
else if (message.role === 'user') {
|
|
838
|
+
this.sessionManager.appendMessage(message);
|
|
807
839
|
}
|
|
808
840
|
}
|
|
809
841
|
// Inject any pending "nextTurn" messages as context alongside the user message
|
|
@@ -1149,11 +1181,16 @@ export class AgentSession {
|
|
|
1149
1181
|
// =========================================================================
|
|
1150
1182
|
/** Switch the session's active LLM to `model`, persisting the change. */
|
|
1151
1183
|
async setModel(model) {
|
|
1152
|
-
|
|
1184
|
+
await this._models.setModel(model);
|
|
1185
|
+
if (!this.isFastModeAvailable)
|
|
1186
|
+
this.setFastModeEnabled(false);
|
|
1153
1187
|
}
|
|
1154
1188
|
/** Cycle to the next configured model and report the switch result. */
|
|
1155
1189
|
async cycleModel(direction = 'forward') {
|
|
1156
|
-
|
|
1190
|
+
const result = await this._models.cycleModel(direction);
|
|
1191
|
+
if (!this.isFastModeAvailable)
|
|
1192
|
+
this.setFastModeEnabled(false);
|
|
1193
|
+
return result;
|
|
1157
1194
|
}
|
|
1158
1195
|
/** Set the model's reasoning/thinking level for subsequent turns. */
|
|
1159
1196
|
setThinkingLevel(level) {
|
|
@@ -66,4 +66,9 @@ export interface TaskSpawnApprovalData {
|
|
|
66
66
|
/** Launch mode requested by the tool call before user approval overrides. */
|
|
67
67
|
requestedRunInBackground: boolean;
|
|
68
68
|
}
|
|
69
|
+
export interface SwitchModeApprovalData {
|
|
70
|
+
currentMode: string;
|
|
71
|
+
targetMode: string;
|
|
72
|
+
reason?: string;
|
|
73
|
+
}
|
|
69
74
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -10,12 +10,7 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'f
|
|
|
10
10
|
import { dirname, join } from 'path';
|
|
11
11
|
import lockfile from 'proper-lockfile';
|
|
12
12
|
import { getAgentDir } from '../../config.js';
|
|
13
|
-
|
|
14
|
-
* Shared lock options so sync and async locks always use the same lock path.
|
|
15
|
-
* realpath: false ensures both create the same .lock directory on Windows,
|
|
16
|
-
* where fs.realpath can normalize paths differently.
|
|
17
|
-
*/
|
|
18
|
-
const LOCK_BASE_OPTIONS = { realpath: false };
|
|
13
|
+
import { LOCK_BASE_OPTIONS, lockSyncWithRetries } from '../../shared/files/lock-utils.js';
|
|
19
14
|
export class FileAuthStorageBackend {
|
|
20
15
|
authPath;
|
|
21
16
|
constructor(authPath = join(getAgentDir(), 'auth.json')) {
|
|
@@ -38,7 +33,7 @@ export class FileAuthStorageBackend {
|
|
|
38
33
|
this.ensureFileExists();
|
|
39
34
|
let release;
|
|
40
35
|
try {
|
|
41
|
-
release =
|
|
36
|
+
release = lockSyncWithRetries(this.authPath);
|
|
42
37
|
const current = existsSync(this.authPath) ? readFileSync(this.authPath, 'utf-8') : undefined;
|
|
43
38
|
const { result, next } = fn(current);
|
|
44
39
|
if (next !== undefined) {
|
|
@@ -15,6 +15,7 @@ const ALL_BUILTIN_SLASH_COMMANDS = [
|
|
|
15
15
|
// Model
|
|
16
16
|
{ name: 'model', description: 'Select model' },
|
|
17
17
|
{ name: 'thinking', description: 'Select thinking level' },
|
|
18
|
+
{ name: 'fast', description: 'Toggle fast mode for subsequent turns' },
|
|
18
19
|
{ name: 'hide-thinking', description: 'Toggle visibility of thinking blocks' },
|
|
19
20
|
// Checkpointing
|
|
20
21
|
{ name: 'resume', description: 'Resume a different session' },
|
package/dist/app/index.d.ts
CHANGED
|
@@ -48,6 +48,8 @@ export { readSkillProposalReviewSnapshot, type SkillProposalReviewSnapshot } fro
|
|
|
48
48
|
export type { StartupInfoEntry } from './startup-info.js';
|
|
49
49
|
export { downloadSkillsWithApproval } from './sync/skills-download.js';
|
|
50
50
|
export type { SkillDownloadApprovalHandler, SkillDownloadApprovalRequest, SkillsDownloadResult } from './sync/skills-download.js';
|
|
51
|
+
export { checkSkillsSyncStatus } from './sync/skills-status.js';
|
|
52
|
+
export type { SkillSyncStatusItem, SkillsSyncStatus } from './sync/skills-status.js';
|
|
51
53
|
export { uploadSkills } from './sync/skills-upload.js';
|
|
52
54
|
export type { SkillUploadApprovalRequest, SkillsUploadResult } from './sync/skills-upload.js';
|
|
53
55
|
export * from './tools/index.js';
|
package/dist/app/index.js
CHANGED
|
@@ -37,6 +37,7 @@ export { default as skillProposalsExtension } from './skill-proposals/extension.
|
|
|
37
37
|
export { getSkillProposalRoots } from './skill-proposals/proposal-fs.js';
|
|
38
38
|
export { readSkillProposalReviewSnapshot } from './skill-proposals/review.js';
|
|
39
39
|
export { downloadSkillsWithApproval } from './sync/skills-download.js';
|
|
40
|
+
export { checkSkillsSyncStatus } from './sync/skills-status.js';
|
|
40
41
|
export { uploadSkills } from './sync/skills-upload.js';
|
|
41
42
|
export * from './tools/index.js';
|
|
42
43
|
export { buildWorkbookPromptContext, fetchWorkbookConnectionState, formatWorkbookScopePromptContext } from './workbook-context/workbook-summary.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ShortcutClientLogger } from '../../../observability/index.js';
|
|
2
|
+
import type { ExcelExecApprovalObservability } from './executor.js';
|
|
3
|
+
export declare function createExecuteCodeApprovalObservability(clientLogger: ShortcutClientLogger): ExcelExecApprovalObservability;
|
|
4
|
+
//# sourceMappingURL=observability.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const TOOL_NAME = 'execute_code';
|
|
2
|
+
export function createExecuteCodeApprovalObservability(clientLogger) {
|
|
3
|
+
return {
|
|
4
|
+
onRequested: (request) => {
|
|
5
|
+
clientLogger.info({
|
|
6
|
+
module: 'tool',
|
|
7
|
+
event: 'tool_approval_requested',
|
|
8
|
+
data: {
|
|
9
|
+
tool_name: TOOL_NAME,
|
|
10
|
+
approval_kind: request.payload.kind
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
},
|
|
14
|
+
onAccepted: (request, choice) => {
|
|
15
|
+
clientLogger.info({
|
|
16
|
+
module: 'tool',
|
|
17
|
+
event: 'tool_approval_accepted',
|
|
18
|
+
data: {
|
|
19
|
+
tool_name: TOOL_NAME,
|
|
20
|
+
approval_kind: request.payload.kind,
|
|
21
|
+
accept_all: choice === 'Accept All'
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
onRejected: (request) => {
|
|
26
|
+
clientLogger.warn({
|
|
27
|
+
module: 'tool',
|
|
28
|
+
event: 'tool_approval_rejected',
|
|
29
|
+
data: {
|
|
30
|
+
tool_name: TOOL_NAME,
|
|
31
|
+
approval_kind: request.payload.kind
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
onReverted: (request) => {
|
|
36
|
+
clientLogger.info({
|
|
37
|
+
module: 'tool',
|
|
38
|
+
event: 'tool_approval_reverted',
|
|
39
|
+
data: {
|
|
40
|
+
tool_name: TOOL_NAME,
|
|
41
|
+
approval_kind: request.payload.kind
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
onRevertFailed: (request) => {
|
|
46
|
+
clientLogger.error({
|
|
47
|
+
module: 'tool',
|
|
48
|
+
event: 'tool_approval_revert_failed',
|
|
49
|
+
data: {
|
|
50
|
+
tool_name: TOOL_NAME,
|
|
51
|
+
approval_kind: request.payload.kind
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=observability.js.map
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { AuthStorage } from '../auth/auth-storage.js';
|
|
2
|
+
import type { McpOAuthStore } from './oauth-state-store.js';
|
|
2
3
|
import { type McpAuthStatus, type McpServerConfig } from './types.js';
|
|
3
|
-
export
|
|
4
|
+
export type McpAuthDiscoveryMode = 'never' | 'cached' | 'network';
|
|
5
|
+
export interface ResolveMcpAuthStatusOptions {
|
|
6
|
+
authStorage?: AuthStorage;
|
|
7
|
+
mcpOAuthStore?: McpOAuthStore;
|
|
8
|
+
discovery?: McpAuthDiscoveryMode;
|
|
9
|
+
}
|
|
10
|
+
export declare function resolveMcpAuthStatus(serverName: string, config: McpServerConfig, options?: ResolveMcpAuthStatusOptions): Promise<McpAuthStatus>;
|
|
11
|
+
export declare function getMcpAuthStatus(serverName: string, config: McpServerConfig, authStorage?: AuthStorage, mcpOAuthStore?: McpOAuthStore): Promise<McpAuthStatus>;
|
|
4
12
|
//# sourceMappingURL=auth-status.d.ts.map
|
|
@@ -1,33 +1,44 @@
|
|
|
1
1
|
import { getSecret } from '../connectors/secret-store.js';
|
|
2
2
|
import { getHostedMcpOAuthProviderId } from './hosted-oauth-id.js';
|
|
3
|
+
import { resolveHostedMcpOAuthTimeoutMs, supportsHostedMcpOAuth } from './oauth-discovery.js';
|
|
3
4
|
import { isHostedServerConfig, isStdioServerConfig } from './types.js';
|
|
4
5
|
function hasConfiguredHeaders(headers) {
|
|
5
6
|
return !!headers && Object.keys(headers).length > 0;
|
|
6
7
|
}
|
|
7
|
-
export async function
|
|
8
|
+
export async function resolveMcpAuthStatus(serverName, config, options = {}) {
|
|
8
9
|
if (isStdioServerConfig(config)) {
|
|
9
10
|
return 'unsupported';
|
|
10
11
|
}
|
|
11
12
|
const staticHeaders = { ...(config.headers ?? {}) };
|
|
12
13
|
const secretHeaders = config.connection ? getSecret(config.connection) : undefined;
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (credential?.type === 'oauth') {
|
|
14
|
+
if (hasConfiguredHeaders(staticHeaders) || hasConfiguredHeaders(secretHeaders)) {
|
|
15
|
+
return 'configured';
|
|
16
|
+
}
|
|
17
|
+
if (isHostedServerConfig(config)) {
|
|
18
|
+
const discovery = options.discovery ?? 'cached';
|
|
19
|
+
const providerId = getHostedMcpOAuthProviderId(serverName, config);
|
|
20
|
+
if (options.authStorage?.get(providerId)?.type === 'oauth') {
|
|
21
21
|
return 'oauth';
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
if (config.oauth ||
|
|
24
|
+
(discovery !== 'never' && options.mcpOAuthStore?.getDiscoveryState(providerId))) {
|
|
25
|
+
return 'not_logged_in';
|
|
26
|
+
}
|
|
27
|
+
if (discovery === 'network' &&
|
|
28
|
+
(await supportsHostedMcpOAuth(serverName, config, options.mcpOAuthStore, {
|
|
29
|
+
timeoutMs: resolveHostedMcpOAuthTimeoutMs(config)
|
|
30
|
+
}).catch(() => false))) {
|
|
31
|
+
return 'not_logged_in';
|
|
32
|
+
}
|
|
33
|
+
return 'unsupported';
|
|
30
34
|
}
|
|
31
35
|
return 'unsupported';
|
|
32
36
|
}
|
|
37
|
+
export async function getMcpAuthStatus(serverName, config, authStorage, mcpOAuthStore) {
|
|
38
|
+
return resolveMcpAuthStatus(serverName, config, {
|
|
39
|
+
authStorage,
|
|
40
|
+
mcpOAuthStore,
|
|
41
|
+
discovery: 'cached'
|
|
42
|
+
});
|
|
43
|
+
}
|
|
33
44
|
//# sourceMappingURL=auth-status.js.map
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Use mcp_describe(server) to get full tool details.
|
|
9
9
|
*/
|
|
10
|
-
import type { McpAuthStatus, McpConnection, McpServerStatus } from './types.js';
|
|
10
|
+
import type { McpAuthStatus, McpConnection, McpFailureKind, McpServerStatus } from './types.js';
|
|
11
11
|
/**
|
|
12
12
|
* Build a compact tool catalog — just server names and tool names.
|
|
13
13
|
* Detailed descriptions are available via mcp_describe.
|
|
@@ -21,6 +21,7 @@ export declare function buildStatusLine(entries: Array<{
|
|
|
21
21
|
name: string;
|
|
22
22
|
connected?: McpConnection;
|
|
23
23
|
failure?: string;
|
|
24
|
+
failureKind?: McpFailureKind;
|
|
24
25
|
authStatus: McpAuthStatus;
|
|
25
26
|
transport: McpServerStatus['transport'];
|
|
26
27
|
}>): string;
|
package/dist/app/mcp/catalog.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Use mcp_describe(server) to get full tool details.
|
|
9
9
|
*/
|
|
10
|
+
import { getAgentLogPath } from '../../shared/logging/agent-log.js';
|
|
10
11
|
/**
|
|
11
12
|
* Build a compact tool catalog — just server names and tool names.
|
|
12
13
|
* Detailed descriptions are available via mcp_describe.
|
|
@@ -41,7 +42,7 @@ function formatAuthSuffix(authStatus) {
|
|
|
41
42
|
case 'oauth':
|
|
42
43
|
return 'oauth';
|
|
43
44
|
case 'not_logged_in':
|
|
44
|
-
return 'login
|
|
45
|
+
return 'needs login';
|
|
45
46
|
case 'unsupported':
|
|
46
47
|
default:
|
|
47
48
|
return 'no auth';
|
|
@@ -49,6 +50,7 @@ function formatAuthSuffix(authStatus) {
|
|
|
49
50
|
}
|
|
50
51
|
export function buildStatusLine(entries) {
|
|
51
52
|
const parts = [];
|
|
53
|
+
let hasFailure = false;
|
|
52
54
|
for (const entry of entries) {
|
|
53
55
|
const authSuffix = entry.transport === 'streamable_http' && entry.authStatus !== 'unsupported'
|
|
54
56
|
? `, ${formatAuthSuffix(entry.authStatus)}`
|
|
@@ -57,12 +59,22 @@ export function buildStatusLine(entries) {
|
|
|
57
59
|
parts.push(`${entry.name} (${entry.connected.tools.length} tools${authSuffix})`);
|
|
58
60
|
}
|
|
59
61
|
else if (entry.failure) {
|
|
60
|
-
|
|
62
|
+
hasFailure = true;
|
|
63
|
+
if (entry.failureKind === 'auth_required') {
|
|
64
|
+
parts.push(`${entry.name} (needs login - run /login -> MCP: ${entry.name})`);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
parts.push(`${entry.name} (failed)`);
|
|
68
|
+
}
|
|
61
69
|
}
|
|
62
70
|
else {
|
|
63
71
|
parts.push(`${entry.name} (${formatAuthSuffix(entry.authStatus)})`);
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
|
-
|
|
74
|
+
if (parts.length === 0) {
|
|
75
|
+
return '';
|
|
76
|
+
}
|
|
77
|
+
const logSuffix = hasFailure ? `; logs: ${getAgentLogPath()}` : '';
|
|
78
|
+
return `[MCP] ${parts.join(', ')}${logSuffix}`;
|
|
67
79
|
}
|
|
68
80
|
//# sourceMappingURL=catalog.js.map
|
package/dist/app/mcp/config.d.ts
CHANGED
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Load
|
|
2
|
+
* Load MCP server config from the global agent directory.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Project servers override global servers with the same name.
|
|
4
|
+
* MCPs are user-level integrations for ShortcutXL spreadsheet sessions, not
|
|
5
|
+
* project-local coding config.
|
|
8
6
|
*/
|
|
9
|
-
import { type McpServerConfig } from './types.js';
|
|
7
|
+
import { type McpConfig, type McpServerConfig } from './types.js';
|
|
8
|
+
export declare function getMcpConfigPath(globalConfigDir: string): string;
|
|
9
|
+
export declare function readMcpConfigFile(path: string): McpConfig;
|
|
10
|
+
export declare function writeMcpConfigFile(path: string, config: McpConfig): void;
|
|
11
|
+
export declare function upsertMcpServerConfig(path: string, name: string, config: McpServerConfig, options?: {
|
|
12
|
+
force?: boolean;
|
|
13
|
+
}): void;
|
|
14
|
+
export declare function removeMcpServerConfig(path: string, name: string): boolean;
|
|
15
|
+
export declare function validateServerConfig(name: string, cfg: unknown): cfg is McpServerConfig;
|
|
16
|
+
export declare function normalizeServerConfig(name: string, cfg: McpServerConfig): McpServerConfig;
|
|
10
17
|
interface LoadConfigOptions {
|
|
11
18
|
cwd: string;
|
|
12
19
|
globalConfigDir: string;
|
|
13
20
|
}
|
|
14
21
|
/**
|
|
15
|
-
* Load
|
|
16
|
-
* Project-local servers override global servers with the same name.
|
|
17
|
-
* Warns on secrets found in project-local config.
|
|
22
|
+
* Load MCP server configs from the global agent path only.
|
|
18
23
|
*/
|
|
19
24
|
export declare function loadMcpConfig(options: LoadConfigOptions): Record<string, McpServerConfig>;
|
|
20
25
|
export {};
|
package/dist/app/mcp/config.js
CHANGED
|
@@ -1,40 +1,61 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Load
|
|
2
|
+
* Load MCP server config from the global agent directory.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Project servers override global servers with the same name.
|
|
4
|
+
* MCPs are user-level integrations for ShortcutXL spreadsheet sessions, not
|
|
5
|
+
* project-local coding config.
|
|
8
6
|
*/
|
|
9
|
-
import { existsSync, readFileSync } from 'fs';
|
|
10
|
-
import { join } from 'path';
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
8
|
+
import { dirname, join } from 'path';
|
|
11
9
|
import { log } from '../../shared/logging/agent-log.js';
|
|
12
10
|
const TAG = 'mcp';
|
|
13
11
|
const MCP_CONFIG_FILENAME = 'mcp.json';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
function looksLikeSecret(value) {
|
|
17
|
-
return SECRET_PATTERNS.some((p) => p.test(value));
|
|
12
|
+
export function getMcpConfigPath(globalConfigDir) {
|
|
13
|
+
return join(globalConfigDir, MCP_CONFIG_FILENAME);
|
|
18
14
|
}
|
|
19
|
-
function
|
|
15
|
+
export function readMcpConfigFile(path) {
|
|
20
16
|
if (!existsSync(path))
|
|
21
|
-
return
|
|
17
|
+
return { mcpServers: {} };
|
|
22
18
|
try {
|
|
23
19
|
const raw = readFileSync(path, 'utf-8');
|
|
24
20
|
const parsed = JSON.parse(raw);
|
|
25
21
|
if (!parsed || typeof parsed !== 'object' || !parsed.mcpServers) {
|
|
26
22
|
log.warn(TAG, `MCP config at ${path} missing "mcpServers" key — skipping.`);
|
|
27
|
-
return
|
|
23
|
+
return { mcpServers: {} };
|
|
24
|
+
}
|
|
25
|
+
if (typeof parsed.mcpServers !== 'object' || Array.isArray(parsed.mcpServers)) {
|
|
26
|
+
log.warn(TAG, `MCP config at ${path} has invalid "mcpServers" shape — skipping.`);
|
|
27
|
+
return { mcpServers: {} };
|
|
28
28
|
}
|
|
29
|
-
return parsed;
|
|
29
|
+
return { mcpServers: parsed.mcpServers };
|
|
30
30
|
}
|
|
31
31
|
catch (err) {
|
|
32
32
|
const msg = err instanceof SyntaxError ? err.message : String(err);
|
|
33
33
|
log.warn(TAG, `Failed to parse ${path}: ${msg}`);
|
|
34
|
-
return
|
|
34
|
+
return { mcpServers: {} };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function writeMcpConfigFile(path, config) {
|
|
38
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
39
|
+
writeFileSync(path, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
40
|
+
}
|
|
41
|
+
export function upsertMcpServerConfig(path, name, config, options = {}) {
|
|
42
|
+
const existing = readMcpConfigFile(path);
|
|
43
|
+
if (!options.force && Object.prototype.hasOwnProperty.call(existing.mcpServers, name)) {
|
|
44
|
+
throw new Error(`MCP server '${name}' already exists in ${path}. Choose a different name or rerun with --force to replace it.`);
|
|
45
|
+
}
|
|
46
|
+
existing.mcpServers[name] = config;
|
|
47
|
+
writeMcpConfigFile(path, existing);
|
|
48
|
+
}
|
|
49
|
+
export function removeMcpServerConfig(path, name) {
|
|
50
|
+
const config = readMcpConfigFile(path);
|
|
51
|
+
if (!Object.prototype.hasOwnProperty.call(config.mcpServers, name)) {
|
|
52
|
+
return false;
|
|
35
53
|
}
|
|
54
|
+
delete config.mcpServers[name];
|
|
55
|
+
writeMcpConfigFile(path, config);
|
|
56
|
+
return true;
|
|
36
57
|
}
|
|
37
|
-
function validateServerConfig(name, cfg) {
|
|
58
|
+
export function validateServerConfig(name, cfg) {
|
|
38
59
|
if (!cfg || typeof cfg !== 'object') {
|
|
39
60
|
log.warn(TAG, `MCP server '${name}': config must be an object — skipping.`);
|
|
40
61
|
return false;
|
|
@@ -67,7 +88,7 @@ function normalizeBoolean(serverName, fieldName, value) {
|
|
|
67
88
|
}
|
|
68
89
|
return value;
|
|
69
90
|
}
|
|
70
|
-
function normalizeServerConfig(name, cfg) {
|
|
91
|
+
export function normalizeServerConfig(name, cfg) {
|
|
71
92
|
const { timeout: legacyTimeout, startupTimeoutMs: rawStartupTimeoutMs, toolCallTimeoutMs: rawToolCallTimeoutMs, resetToolCallTimeoutOnProgress: rawResetToolCallTimeoutOnProgress, maxToolCallTotalTimeoutMs: rawMaxToolCallTotalTimeoutMs, ...rest } = cfg;
|
|
72
93
|
const startupTimeoutMs = normalizePositiveInt(name, 'startupTimeoutMs', rawStartupTimeoutMs) ??
|
|
73
94
|
normalizePositiveInt(name, 'timeout', legacyTimeout);
|
|
@@ -84,53 +105,18 @@ function normalizeServerConfig(name, cfg) {
|
|
|
84
105
|
};
|
|
85
106
|
return normalized;
|
|
86
107
|
}
|
|
87
|
-
function warnOnSecrets(path, config) {
|
|
88
|
-
for (const [name, server] of Object.entries(config.mcpServers)) {
|
|
89
|
-
const entries = [];
|
|
90
|
-
if ('env' in server) {
|
|
91
|
-
entries.push(...Object.entries(server.env ?? {}));
|
|
92
|
-
}
|
|
93
|
-
if ('headers' in server) {
|
|
94
|
-
entries.push(...Object.entries(server.headers ?? {}));
|
|
95
|
-
}
|
|
96
|
-
for (const [key, value] of entries) {
|
|
97
|
-
if (typeof value === 'string' && looksLikeSecret(value)) {
|
|
98
|
-
log.warn(TAG, `MCP server '${name}': value '${key}' in ${path} looks like a secret. ` +
|
|
99
|
-
`Consider moving it to the global config (~/.shortcut/agent/mcp.json) or using shell environment variables.`);
|
|
100
|
-
return; // One warning per load is enough.
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
108
|
/**
|
|
106
|
-
* Load
|
|
107
|
-
* Project-local servers override global servers with the same name.
|
|
108
|
-
* Warns on secrets found in project-local config.
|
|
109
|
+
* Load MCP server configs from the global agent path only.
|
|
109
110
|
*/
|
|
110
111
|
export function loadMcpConfig(options) {
|
|
111
|
-
const globalPath =
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
// Merge: project overrides global.
|
|
119
|
-
const merged = {};
|
|
120
|
-
if (globalConfig) {
|
|
121
|
-
for (const [name, cfg] of Object.entries(globalConfig.mcpServers)) {
|
|
122
|
-
if (validateServerConfig(name, cfg)) {
|
|
123
|
-
merged[name] = normalizeServerConfig(name, cfg);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if (projectConfig) {
|
|
128
|
-
for (const [name, cfg] of Object.entries(projectConfig.mcpServers)) {
|
|
129
|
-
if (validateServerConfig(name, cfg)) {
|
|
130
|
-
merged[name] = normalizeServerConfig(name, cfg); // Override global
|
|
131
|
-
}
|
|
112
|
+
const globalPath = getMcpConfigPath(options.globalConfigDir);
|
|
113
|
+
const globalConfig = readMcpConfigFile(globalPath);
|
|
114
|
+
const servers = {};
|
|
115
|
+
for (const [name, cfg] of Object.entries(globalConfig.mcpServers)) {
|
|
116
|
+
if (validateServerConfig(name, cfg)) {
|
|
117
|
+
servers[name] = normalizeServerConfig(name, cfg);
|
|
132
118
|
}
|
|
133
119
|
}
|
|
134
|
-
return
|
|
120
|
+
return servers;
|
|
135
121
|
}
|
|
136
122
|
//# sourceMappingURL=config.js.map
|
|
@@ -10,6 +10,7 @@ export declare class McpConnectionError extends Error {
|
|
|
10
10
|
}
|
|
11
11
|
export declare function resolveStartupTimeoutMs(config: McpServerConfig): number;
|
|
12
12
|
export declare function buildRequestPolicy(config: McpServerConfig): McpRequestPolicy;
|
|
13
|
+
export declare function createHostedMcpLoggingFetch(serverName: string, fetchImpl?: typeof fetch): typeof fetch;
|
|
13
14
|
/**
|
|
14
15
|
* Connect a server, list tools, and return a live MCP connection.
|
|
15
16
|
*/
|