@robota-sdk/agent-command 3.0.0-beta.78 → 3.0.0-beta.81
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.md +11 -2
- package/dist/node/index.cjs +76 -38
- package/dist/node/index.d.cts +946 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.ts +564 -51
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +76 -38
- package/dist/node/index.js.map +1 -1
- package/package.json +34 -16
- package/src/advisor/advisor-command-module.ts +47 -0
- package/src/advisor/advisor-command.test.ts +102 -0
- package/src/advisor/advisor-command.ts +55 -0
- package/src/advisor/advisor-cost-functional.test.ts +92 -0
- package/src/advisor/index.ts +6 -0
- package/src/agent/__tests__/agent-command.test.ts +39 -8
- package/src/agent/agent-command-module.ts +14 -7
- package/src/agent/agent-command-parser.ts +1 -1
- package/src/agent/agent-command.ts +3 -3
- package/src/background/__tests__/background-command-module.test.ts +59 -52
- package/src/background/background-command-module.ts +2 -1
- package/src/background/background-command.ts +3 -3
- package/src/command-module-utils.ts +33 -0
- package/src/compact/__tests__/compact-command-module.test.ts +31 -23
- package/src/compact/compact-command-module.ts +10 -1
- package/src/compact/compact-command.ts +6 -3
- package/src/context/__tests__/context-command-module.test.ts +214 -63
- package/src/context/auto-compact-format.ts +25 -0
- package/src/context/context-breakdown.ts +295 -0
- package/src/context/context-command-module.ts +45 -14
- package/src/context/context-command.ts +34 -257
- package/src/default/__tests__/__snapshots__/model-exposure.test.ts.snap +110 -0
- package/src/default/__tests__/default-command-modules.test.ts +59 -7
- package/src/default/__tests__/model-exposure.test.ts +241 -0
- package/src/default/__tests__/org-policy-forwarding.test.ts +98 -0
- package/src/default/default-command-modules.ts +130 -18
- package/src/default/index.ts +4 -1
- package/src/devices/__tests__/devices-command-module.test.ts +212 -0
- package/src/devices/devices-command-module.ts +224 -0
- package/src/devices/devices-command-port.ts +82 -0
- package/src/devices/index.ts +11 -0
- package/src/doctor/__tests__/doctor-command-module.test.ts +145 -0
- package/src/doctor/__tests__/doctor-display-vocabulary.test.ts +38 -0
- package/src/doctor/__tests__/doctor-fixture.ts +145 -0
- package/src/doctor/__tests__/doctor-redaction.test.ts +29 -0
- package/src/doctor/__tests__/doctor-runner.test.ts +464 -0
- package/src/doctor/doctor-command-module.ts +132 -0
- package/src/doctor/doctor-extensions-probe.ts +299 -0
- package/src/doctor/doctor-node-deps.ts +89 -0
- package/src/doctor/doctor-redaction.ts +99 -0
- package/src/doctor/doctor-render.ts +70 -0
- package/src/doctor/doctor-repair.ts +136 -0
- package/src/doctor/doctor-runner.ts +122 -0
- package/src/doctor/doctor-settings-probe.ts +239 -0
- package/src/doctor/doctor-storage-probe.ts +187 -0
- package/src/doctor/doctor-types.ts +109 -0
- package/src/doctor/index.ts +32 -0
- package/src/editor/__tests__/editor-command-functional.test.ts +49 -16
- package/src/editor/editor-command-module.ts +6 -5
- package/src/editor/editor-command.ts +10 -5
- package/src/effort/effort-command-module.ts +51 -0
- package/src/effort/effort-command.test.ts +140 -0
- package/src/effort/effort-command.ts +122 -0
- package/src/effort/index.ts +6 -0
- package/src/exit/__tests__/exit-command-module.test.ts +16 -9
- package/src/exit/exit-command-module.ts +2 -1
- package/src/exit/exit-command.ts +6 -6
- package/src/fork/__tests__/fork-command.test.ts +184 -0
- package/src/fork/fork-command-module.ts +61 -0
- package/src/fork/fork-command.ts +162 -0
- package/src/fork/index.ts +8 -0
- package/src/git/__tests__/fake-git-port.ts +28 -0
- package/src/git/__tests__/git-command-module.test.ts +103 -0
- package/src/git/__tests__/git-commit.test.ts +185 -0
- package/src/git/__tests__/git-diff.test.ts +125 -0
- package/src/git/__tests__/git-process.test.ts +221 -0
- package/src/git/__tests__/git-status.test.ts +117 -0
- package/src/git/git-command-module.ts +139 -0
- package/src/git/git-commit.ts +251 -0
- package/src/git/git-diff.ts +129 -0
- package/src/git/git-process.ts +169 -0
- package/src/git/git-status.ts +134 -0
- package/src/git/index.ts +17 -0
- package/src/goal/__tests__/goal-command.test.ts +9 -4
- package/src/goal/goal-command-module.ts +2 -1
- package/src/goal/goal-command.ts +6 -9
- package/src/handoff/__tests__/handoff-command.test.ts +218 -0
- package/src/handoff/handoff-command-module.ts +48 -0
- package/src/handoff/handoff-command.ts +187 -0
- package/src/handoff/index.ts +6 -0
- package/src/help/__tests__/help-command-module.test.ts +63 -40
- package/src/help/__tests__/help-command.test.ts +46 -37
- package/src/help/help-command-module.ts +2 -1
- package/src/help/help-command.ts +3 -3
- package/src/index.ts +124 -2
- package/src/keybindings/__tests__/keybindings-command-module.test.ts +57 -0
- package/src/keybindings/index.ts +6 -0
- package/src/keybindings/keybindings-command-module.ts +74 -0
- package/src/language/__tests__/language-command-module.test.ts +33 -28
- package/src/language/language-command-module.ts +2 -1
- package/src/language/language-command.ts +8 -6
- package/src/mcp-activation/__tests__/mcp-activation-command.test.ts +630 -0
- package/src/mcp-activation/__tests__/mcp-model-view.test.ts +211 -0
- package/src/mcp-activation/index.ts +12 -0
- package/src/mcp-activation/mcp-activation-command-module.ts +92 -0
- package/src/mcp-activation/mcp-activation-command.ts +448 -0
- package/src/mcp-activation/mcp-model-notice.ts +85 -0
- package/src/mcp-activation/mcp-model-status.ts +99 -0
- package/src/memory/__tests__/memory-command-module.test.ts +167 -74
- package/src/memory/memory-command-module.ts +18 -1
- package/src/memory/memory-command.ts +38 -38
- package/src/mode/__tests__/mode-command-module.test.ts +51 -38
- package/src/mode/mode-command-module.ts +2 -1
- package/src/mode/mode-command.ts +15 -7
- package/src/output-style/__tests__/output-style-command.test.ts +96 -0
- package/src/output-style/index.ts +6 -0
- package/src/output-style/output-style-command-module.ts +143 -0
- package/src/peers/__tests__/peers-command.test.ts +241 -0
- package/src/peers/index.ts +9 -0
- package/src/peers/peers-command-module.ts +47 -0
- package/src/peers/peers-command.ts +142 -0
- package/src/permissions/__tests__/permissions-command-module.test.ts +149 -37
- package/src/permissions/permissions-command-module.ts +2 -1
- package/src/permissions/permissions-command.ts +46 -4
- package/src/plan/__tests__/plan-command.test.ts +129 -0
- package/src/plan/index.ts +9 -0
- package/src/plan/plan-command-module.ts +49 -0
- package/src/plan/plan-command.ts +92 -0
- package/src/plugin/__tests__/plugin-command-module.test.ts +46 -40
- package/src/plugin/__tests__/plugin-host-boundary.test.ts +11 -0
- package/src/plugin/plugin-command-module.ts +3 -1
- package/src/plugin/plugin-command.ts +17 -16
- package/src/preset/__tests__/host-supplied-preset-registry.test.ts +98 -0
- package/src/preset/__tests__/preset-command-module.test.ts +102 -56
- package/src/preset/preset-command-module.ts +26 -8
- package/src/preset/preset-command.ts +85 -27
- package/src/provider/__tests__/org-policy.test.ts +4 -5
- package/src/provider/__tests__/provider-command-module.test.ts +11 -14
- package/src/provider/__tests__/provider-startup-host-settings.test.ts +16 -0
- package/src/provider/__tests__/scripted-interaction.ts +7 -5
- package/src/provider/provider-command-execution.ts +4 -4
- package/src/provider/provider-command-module.ts +2 -1
- package/src/provider/provider-command-profile-lifecycle.ts +4 -4
- package/src/provider/provider-command-profile-operations.ts +3 -3
- package/src/provider/provider-command-profile.ts +1 -1
- package/src/provider/provider-command-setup.ts +4 -4
- package/src/provider/provider-startup.ts +62 -35
- package/src/remote-control/__tests__/remote-control-command-module.test.ts +36 -0
- package/src/remote-control/__tests__/remote-control-command.test.ts +216 -0
- package/src/remote-control/index.ts +8 -0
- package/src/remote-control/remote-control-command-module.ts +61 -0
- package/src/remote-control/remote-control-command.ts +138 -0
- package/src/reset/__tests__/reset-command-module.test.ts +5 -5
- package/src/reset/reset-command-module.ts +2 -1
- package/src/reset/reset-command.ts +3 -4
- package/src/rewind/__tests__/rewind-command-module.test.ts +40 -0
- package/src/rewind/rewind-command-module.ts +2 -1
- package/src/rewind/rewind-command.ts +73 -6
- package/src/sandbox/__tests__/sandbox-command.test.ts +80 -0
- package/src/sandbox/index.ts +2 -0
- package/src/sandbox/sandbox-command-module.ts +56 -0
- package/src/sandbox/sandbox-command.ts +94 -0
- package/src/schedule/__tests__/loop-command-functional.test.ts +386 -0
- package/src/schedule/__tests__/loop-command.test.ts +360 -0
- package/src/schedule/__tests__/loop-jitter.test.ts +25 -0
- package/src/schedule/__tests__/monitor-model-permission-functional.test.ts +126 -0
- package/src/schedule/__tests__/schedule-command.test.ts +143 -3
- package/src/schedule/__tests__/schedule-redos.test.ts +81 -0
- package/src/schedule/index.ts +3 -0
- package/src/schedule/loop-command.ts +333 -0
- package/src/schedule/loop-jitter.ts +41 -0
- package/src/schedule/schedule-command-module.ts +92 -14
- package/src/schedule/schedule-command.ts +98 -9
- package/src/schedule/schedule-spec-parser.ts +5 -1
- package/src/session/__tests__/rename-host-persistence.test.ts +72 -0
- package/src/session/__tests__/session-command-module.test.ts +210 -45
- package/src/session/session-command-module.ts +68 -37
- package/src/session/session-command.ts +147 -80
- package/src/settings/settings-command-module.ts +3 -2
- package/src/shell/__tests__/resolve-shell-host.test.ts +11 -0
- package/src/shell/__tests__/shell-command-functional.test.ts +23 -1
- package/src/shell/resolve-shell.ts +2 -2
- package/src/shell/shell-command-module.ts +6 -5
- package/src/shell/shell-command.ts +9 -5
- package/src/skills/__tests__/skills-command-module.test.ts +69 -28
- package/src/skills/index.ts +1 -0
- package/src/skills/skills-command-module.ts +22 -5
- package/src/skills/skills-command.ts +14 -13
- package/src/statusline/__tests__/statusline-command-module.test.ts +5 -4
- package/src/statusline/statusline-command-module.ts +2 -1
- package/src/statusline/statusline-command.ts +4 -4
- package/src/theme/__tests__/theme-command.test.ts +204 -0
- package/src/theme/index.ts +12 -0
- package/src/theme/theme-command-module.ts +211 -0
- package/src/user-local/__tests__/user-local-command.test.ts +145 -110
- package/src/user-local/user-local-command-constants.ts +1 -1
- package/src/user-local/user-local-command-module.ts +6 -5
- package/src/user-local/user-local-command.ts +12 -6
- package/src/user-local/user-local-memory-command.ts +17 -7
- package/src/plugins/default-plugin-command-adapter.ts +0 -164
- package/src/plugins/default-plugin-command-source-loader.ts +0 -31
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { SystemCommandExecutor } from '@robota-sdk/agent-framework';
|
|
4
|
+
|
|
5
|
+
import { createTestCommandHost } from '@robota-sdk/agent-framework/testing';
|
|
6
|
+
|
|
7
|
+
import { createMCPActivationCommandModule } from '../mcp-activation-command-module.js';
|
|
8
|
+
import { executeMCPActivationCommand } from '../mcp-activation-command.js';
|
|
9
|
+
import {
|
|
10
|
+
mcpUnavailableServersNotice,
|
|
11
|
+
mcpUserActionCommand,
|
|
12
|
+
mcpUserActionNotice,
|
|
13
|
+
} from '../mcp-model-notice.js';
|
|
14
|
+
|
|
15
|
+
import type {
|
|
16
|
+
ICommandMCPActivationAdapter,
|
|
17
|
+
ICommandMCPActivationSummary,
|
|
18
|
+
} from '@robota-sdk/agent-framework';
|
|
19
|
+
import type { TCommandInvocationSource } from '@robota-sdk/agent-interface-command';
|
|
20
|
+
|
|
21
|
+
function summary(overrides: Partial<ICommandMCPActivationSummary>): ICommandMCPActivationSummary {
|
|
22
|
+
return {
|
|
23
|
+
serverId: 'server-1',
|
|
24
|
+
displayName: 'Example server',
|
|
25
|
+
source: 'project',
|
|
26
|
+
status: 'pending',
|
|
27
|
+
allowed: false,
|
|
28
|
+
reason: 'SECRET-REASON-TEXT from a definition',
|
|
29
|
+
provenanceId: 'project-settings:/home/me/repo/.robota/settings.json',
|
|
30
|
+
definitionFingerprint: 'fingerprint-abc',
|
|
31
|
+
securityIdentity: 'https://user:hunter2@mcp.example.com/?token=abc',
|
|
32
|
+
...overrides,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const adapter: ICommandMCPActivationAdapter = {
|
|
37
|
+
list: () => [
|
|
38
|
+
summary({ serverId: 'github', status: 'approved', allowed: true }),
|
|
39
|
+
summary({ serverId: 'linear', status: 'pending' }),
|
|
40
|
+
summary({ serverId: 'jira', status: 'untrusted' }),
|
|
41
|
+
summary({ serverId: 'evil; rm -rf ~', status: 'stale' }),
|
|
42
|
+
summary({ serverId: 'gone', status: 'rejected' }),
|
|
43
|
+
],
|
|
44
|
+
sourceProblems: () => [
|
|
45
|
+
{ source: 'project', origin: '/home/me/repo/.robota/settings.json', reason: 'not json' },
|
|
46
|
+
],
|
|
47
|
+
approve: () => {
|
|
48
|
+
throw new Error('not used');
|
|
49
|
+
},
|
|
50
|
+
reject: () => {
|
|
51
|
+
throw new Error('not used');
|
|
52
|
+
},
|
|
53
|
+
revoke: () => {
|
|
54
|
+
throw new Error('not used');
|
|
55
|
+
},
|
|
56
|
+
oauthStatus: async () => [{ serverId: 'github', state: 'sign-in-required' }],
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function host(source: TCommandInvocationSource) {
|
|
60
|
+
return createTestCommandHost({
|
|
61
|
+
overrides: {
|
|
62
|
+
getCommandHostAdapters: () => ({ mcpActivation: adapter }),
|
|
63
|
+
getCommandInvocationSource: () => source,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
describe('/mcp status as the model sees it', () => {
|
|
69
|
+
it('names each server, its states, and the command to suggest', async () => {
|
|
70
|
+
const result = await executeMCPActivationCommand(host('model'), 'status');
|
|
71
|
+
|
|
72
|
+
expect(result.success).toBe(true);
|
|
73
|
+
expect(result.message).toBe(
|
|
74
|
+
[
|
|
75
|
+
'MCP servers:',
|
|
76
|
+
' github — approved — sign-in: sign-in-required — ask the user to run `/mcp login github`',
|
|
77
|
+
' linear — pending — ask the user to run `/mcp approve linear`',
|
|
78
|
+
' jira — untrusted — ask the user to run `robota trust`',
|
|
79
|
+
' (name not shown) — stale — ask the user to run `/mcp approve <server>`',
|
|
80
|
+
' gone — rejected',
|
|
81
|
+
'1 MCP configuration source(s) could not be read; the user can see which with `/mcp status`.',
|
|
82
|
+
].join('\n'),
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('names the terminal sign-in command in a run with no session prompt', async () => {
|
|
87
|
+
const terminal = { ...adapter, userActionSurface: 'terminal' as const };
|
|
88
|
+
const result = await executeMCPActivationCommand(
|
|
89
|
+
createTestCommandHost({
|
|
90
|
+
overrides: {
|
|
91
|
+
getCommandHostAdapters: () => ({ mcpActivation: terminal }),
|
|
92
|
+
getCommandInvocationSource: () => 'model',
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
'status',
|
|
96
|
+
);
|
|
97
|
+
expect(result.message).toContain(
|
|
98
|
+
'github — approved — sign-in: sign-in-required — ask the user to run `robota mcp login github`',
|
|
99
|
+
);
|
|
100
|
+
expect(result.message).not.toContain('/mcp login');
|
|
101
|
+
// Approval has no terminal command; it stays the session command.
|
|
102
|
+
expect(result.message).toContain('`/mcp approve linear`');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('carries no reason text, source path, fingerprint, identity or unsafe name', async () => {
|
|
106
|
+
const result = await executeMCPActivationCommand(host('model'), '');
|
|
107
|
+
const everything = JSON.stringify(result);
|
|
108
|
+
|
|
109
|
+
for (const leaked of [
|
|
110
|
+
'SECRET-REASON-TEXT',
|
|
111
|
+
'/home/me/repo',
|
|
112
|
+
'fingerprint-abc',
|
|
113
|
+
'hunter2',
|
|
114
|
+
'token=abc',
|
|
115
|
+
'mcp.example.com',
|
|
116
|
+
'rm -rf',
|
|
117
|
+
'Example server',
|
|
118
|
+
]) {
|
|
119
|
+
expect(everything).not.toContain(leaked);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('keeps the full view for the user', async () => {
|
|
124
|
+
const result = await executeMCPActivationCommand(host('user'), 'status');
|
|
125
|
+
expect(result.message).toContain('SECRET-REASON-TEXT');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('gives a caller that does not say who it is the restricted view', async () => {
|
|
129
|
+
const { getCommandInvocationSource: _unsaid, ...anonymous } = host('user');
|
|
130
|
+
const result = await executeMCPActivationCommand(anonymous, 'status');
|
|
131
|
+
expect(result.message).toContain('`/mcp approve linear`');
|
|
132
|
+
expect(result.message).not.toContain('SECRET-REASON-TEXT');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('the `/mcp login` the notices suggest', () => {
|
|
137
|
+
it('is a real verb for the user and is refused for the model before it runs', async () => {
|
|
138
|
+
// A user-typed `/mcp login` reaches the sign-in path (here: no host adapter), never "Unknown".
|
|
139
|
+
const userResult = await executeMCPActivationCommand(
|
|
140
|
+
createTestCommandHost({ overrides: { getCommandInvocationSource: () => 'user' } }),
|
|
141
|
+
'login github',
|
|
142
|
+
);
|
|
143
|
+
expect(userResult.message).not.toContain('Unknown argument');
|
|
144
|
+
expect(userResult.message).toContain('not available in this environment');
|
|
145
|
+
|
|
146
|
+
const [command] = createMCPActivationCommandModule().systemCommands ?? [];
|
|
147
|
+
expect(command?.subcommands).toContainEqual(
|
|
148
|
+
expect.objectContaining({ name: 'login', modelInvocable: false }),
|
|
149
|
+
);
|
|
150
|
+
const execute = vi.fn();
|
|
151
|
+
const executor = new SystemCommandExecutor([{ ...command!, execute }]);
|
|
152
|
+
const modelResult = await executor.executeModelInvocable(
|
|
153
|
+
'mcp',
|
|
154
|
+
createTestCommandHost(),
|
|
155
|
+
'login github',
|
|
156
|
+
);
|
|
157
|
+
expect(modelResult?.success).toBe(false);
|
|
158
|
+
expect(modelResult?.message).toContain('only the user can');
|
|
159
|
+
expect(execute).not.toHaveBeenCalled();
|
|
160
|
+
expect(mcpUserActionCommand('github', 'sign-in')).toBe('/mcp login github');
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('MCP notices for the model', () => {
|
|
165
|
+
it('names a shell-safe server and the user command', () => {
|
|
166
|
+
expect(mcpUserActionNotice('linear', 'approve')).toBe(
|
|
167
|
+
'MCP server "linear" is waiting for the user’s approval, so its tools are unavailable. You ' +
|
|
168
|
+
'cannot do this yourself; ask the user to run `/mcp approve linear`.',
|
|
169
|
+
);
|
|
170
|
+
expect(mcpUserActionNotice('github', 'sign-in')).toContain('`/mcp login github`');
|
|
171
|
+
expect(mcpUserActionNotice('jira', 'trust-workspace')).toContain(
|
|
172
|
+
'`robota trust` in a terminal, then restart the session',
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('names the terminal sign-in command where no /mcp command can be typed', () => {
|
|
177
|
+
expect(mcpUserActionNotice('github', 'sign-in', 'terminal')).toBe(
|
|
178
|
+
'MCP server "github" needs the user to sign in, so its tools are unavailable. You cannot do ' +
|
|
179
|
+
'this yourself; ask the user to run `robota mcp login github` in a terminal, then restart ' +
|
|
180
|
+
'the session.',
|
|
181
|
+
);
|
|
182
|
+
expect(mcpUserActionCommand('$(curl evil)', 'sign-in', 'terminal')).toBe(
|
|
183
|
+
'robota mcp login <server>',
|
|
184
|
+
);
|
|
185
|
+
// Only sign-in moves: approval has no terminal command.
|
|
186
|
+
expect(mcpUserActionCommand('linear', 'approve', 'terminal')).toBe('/mcp approve linear');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('stays generic for a name that is not safe to show', () => {
|
|
190
|
+
const notice = mcpUserActionNotice('$(curl evil)', 'sign-in');
|
|
191
|
+
expect(notice).not.toContain('curl');
|
|
192
|
+
expect(notice).toContain('`/mcp status` lists it');
|
|
193
|
+
expect(mcpUserActionCommand('$(curl evil)', 'approve')).toBe('/mcp approve <server>');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('lists every unavailable server, or says nothing', () => {
|
|
197
|
+
expect(mcpUnavailableServersNotice(new Map())).toBeUndefined();
|
|
198
|
+
const notice = mcpUnavailableServersNotice(
|
|
199
|
+
new Map([
|
|
200
|
+
['linear', 'approve'],
|
|
201
|
+
['github', 'sign-in'],
|
|
202
|
+
]),
|
|
203
|
+
);
|
|
204
|
+
expect(notice?.split('\n')).toEqual([
|
|
205
|
+
'MCP servers that did not start this session. Mention this only when the user needs one of ' +
|
|
206
|
+
'these servers or its tools:',
|
|
207
|
+
`- ${mcpUserActionNotice('linear', 'approve')}`,
|
|
208
|
+
`- ${mcpUserActionNotice('github', 'sign-in')}`,
|
|
209
|
+
]);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createMCPActivationCommandEntry,
|
|
3
|
+
createMCPActivationCommandModule,
|
|
4
|
+
MCPActivationCommandSource,
|
|
5
|
+
} from './mcp-activation-command-module.js';
|
|
6
|
+
export { executeMCPActivationCommand } from './mcp-activation-command.js';
|
|
7
|
+
export {
|
|
8
|
+
mcpUnavailableServersNotice,
|
|
9
|
+
mcpUserActionCommand,
|
|
10
|
+
mcpUserActionNotice,
|
|
11
|
+
} from './mcp-model-notice.js';
|
|
12
|
+
export type { TMCPUserAction, TMCPUserActionSurface } from './mcp-model-notice.js';
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { executeMCPActivationCommand } from './mcp-activation-command.js';
|
|
2
|
+
|
|
3
|
+
import type { ICommandModule, ISystemCommand } from '@robota-sdk/agent-framework';
|
|
4
|
+
import type { ICommand, ICommandSource } from '@robota-sdk/agent-interface-command';
|
|
5
|
+
|
|
6
|
+
export function createMCPActivationCommandEntry(): ICommand {
|
|
7
|
+
return {
|
|
8
|
+
name: 'mcp',
|
|
9
|
+
displayName: 'MCP activation',
|
|
10
|
+
description:
|
|
11
|
+
"Show each MCP server's trust approval and OAuth sign-in state, or change one server: " +
|
|
12
|
+
'approve, reject or revoke its trust; sign in to it (login, which connects it in this ' +
|
|
13
|
+
'session) or out of it (logout). Use when an MCP server is not approved, needs a sign-in, or ' +
|
|
14
|
+
'should stop being used. Returns the status list, or what the one action did.',
|
|
15
|
+
// Model-invocable for `status` only: when an MCP server's tools are missing or failing, the
|
|
16
|
+
// model can see why and name the command the user should run. Approving, rejecting and revoking
|
|
17
|
+
// a server are trust decisions and signing in or out is a credential action, so those stay
|
|
18
|
+
// user-only — the model's `status` view also omits everything but names and states.
|
|
19
|
+
modelDescription:
|
|
20
|
+
'Show which MCP servers are configured and why any of them is unavailable. Use it when an MCP ' +
|
|
21
|
+
'server or its tools are missing, a tool call reports that a server needs sign-in or approval, ' +
|
|
22
|
+
'or the user asks about MCP servers. Returns one line per server: its name, activation state, ' +
|
|
23
|
+
'sign-in state, and the command to suggest to the user when the server needs their approval, ' +
|
|
24
|
+
'sign-in or workspace trust. You cannot approve, sign in or sign out yourself.',
|
|
25
|
+
argumentHint:
|
|
26
|
+
'[status] | <approve|reject|revoke|logout> <server> | login <server> [--no-browser]',
|
|
27
|
+
source: 'mcp-activation',
|
|
28
|
+
modelInvocable: true,
|
|
29
|
+
// The bare `/mcp` shows the status view, so choosing it from a menu runs it.
|
|
30
|
+
runsBare: true,
|
|
31
|
+
subcommands: [
|
|
32
|
+
{
|
|
33
|
+
name: 'status',
|
|
34
|
+
description: 'Show every MCP server’s name, activation state and sign-in state',
|
|
35
|
+
source: 'mcp-activation',
|
|
36
|
+
modelInvocable: true,
|
|
37
|
+
},
|
|
38
|
+
...(['approve', 'reject', 'revoke', 'login', 'logout'] as const).map((verb) => ({
|
|
39
|
+
name: verb,
|
|
40
|
+
description: USER_ONLY_VERB_DESCRIPTION[verb],
|
|
41
|
+
argumentHint: verb === 'login' ? '<server> [--no-browser]' : '<server>',
|
|
42
|
+
source: 'mcp-activation',
|
|
43
|
+
modelInvocable: false,
|
|
44
|
+
})),
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const USER_ONLY_VERB_DESCRIPTION = {
|
|
50
|
+
approve: 'Trust and activate an MCP server',
|
|
51
|
+
reject: 'Refuse an MCP server',
|
|
52
|
+
revoke: 'Withdraw an earlier approval',
|
|
53
|
+
login: 'Sign in to an OAuth MCP server and connect it in this session',
|
|
54
|
+
logout: 'Sign out of an OAuth MCP server and revoke its tokens',
|
|
55
|
+
} as const;
|
|
56
|
+
|
|
57
|
+
function createMCPActivationSystemCommand(): ISystemCommand {
|
|
58
|
+
const entry = createMCPActivationCommandEntry();
|
|
59
|
+
return {
|
|
60
|
+
name: entry.name,
|
|
61
|
+
displayName: entry.displayName,
|
|
62
|
+
description: entry.description,
|
|
63
|
+
...(entry.modelDescription !== undefined ? { modelDescription: entry.modelDescription } : {}),
|
|
64
|
+
argumentHint: entry.argumentHint,
|
|
65
|
+
...(entry.subcommands !== undefined ? { subcommands: entry.subcommands } : {}),
|
|
66
|
+
// Approve/reject/revoke/login/logout change trust or credentials, so the command is never read-only
|
|
67
|
+
// for the user or a remote policy. The model can reach only `status` (its subcommand
|
|
68
|
+
// allowlist), a read-only view, so its call needs no prompt.
|
|
69
|
+
requiresPermission: true,
|
|
70
|
+
modelRequiresPermission: false,
|
|
71
|
+
userInvocable: true,
|
|
72
|
+
modelInvocable: true,
|
|
73
|
+
lifecycle: 'inline',
|
|
74
|
+
execute: executeMCPActivationCommand,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class MCPActivationCommandSource implements ICommandSource {
|
|
79
|
+
readonly name = 'mcp-activation';
|
|
80
|
+
|
|
81
|
+
getCommands(): ICommand[] {
|
|
82
|
+
return [createMCPActivationCommandEntry()];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function createMCPActivationCommandModule(): ICommandModule {
|
|
87
|
+
return {
|
|
88
|
+
name: 'agent-command-mcp-activation',
|
|
89
|
+
commandSources: [new MCPActivationCommandSource()],
|
|
90
|
+
systemCommands: [createMCPActivationSystemCommand()],
|
|
91
|
+
};
|
|
92
|
+
}
|