@sanity/cli 8.26.0 → 8.27.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.
Files changed (40) hide show
  1. package/dist/actions/context/mcpEndpoint.js +165 -0
  2. package/dist/actions/context/mcpEndpoint.js.map +1 -0
  3. package/dist/actions/context/resolveOrganizationId.js +8 -1
  4. package/dist/actions/context/resolveOrganizationId.js.map +1 -1
  5. package/dist/commands/context/create.js +8 -22
  6. package/dist/commands/context/create.js.map +1 -1
  7. package/dist/commands/context/delete.js +2 -2
  8. package/dist/commands/context/delete.js.map +1 -1
  9. package/dist/commands/context/issues/list.js +0 -5
  10. package/dist/commands/context/issues/list.js.map +1 -1
  11. package/dist/commands/context/list.js +6 -14
  12. package/dist/commands/context/list.js.map +1 -1
  13. package/dist/commands/context/mcp/create.js +152 -0
  14. package/dist/commands/context/mcp/create.js.map +1 -0
  15. package/dist/commands/context/mcp/delete.js +82 -0
  16. package/dist/commands/context/mcp/delete.js.map +1 -0
  17. package/dist/commands/context/mcp/get.js +42 -0
  18. package/dist/commands/context/mcp/get.js.map +1 -0
  19. package/dist/commands/context/mcp/list.js +87 -0
  20. package/dist/commands/context/mcp/list.js.map +1 -0
  21. package/dist/commands/context/mcp/status.js +98 -0
  22. package/dist/commands/context/mcp/status.js.map +1 -0
  23. package/dist/commands/context/mcp/update.js +225 -0
  24. package/dist/commands/context/mcp/update.js.map +1 -0
  25. package/dist/commands/context/query.js +6 -14
  26. package/dist/commands/context/query.js.map +1 -1
  27. package/dist/commands/context/update.js +2 -2
  28. package/dist/commands/context/update.js.map +1 -1
  29. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +6 -0
  30. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
  31. package/dist/prompts/promptForText.js +12 -0
  32. package/dist/prompts/promptForText.js.map +1 -0
  33. package/dist/services/context.js +107 -6
  34. package/dist/services/context.js.map +1 -1
  35. package/dist/util/sharedFlags.js.map +1 -1
  36. package/dist/util/telemetry/cliCommandIdentity.js +6 -0
  37. package/dist/util/telemetry/cliCommandIdentity.js.map +1 -1
  38. package/oclif.config.js +1 -1
  39. package/oclif.manifest.json +2527 -1977
  40. package/package.json +2 -2
@@ -0,0 +1,152 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
3
+ import { getErrorMessage } from '@sanity/cli-core/errors';
4
+ import { requiredWhenUnattended } from '@sanity/cli-core/flags';
5
+ import { spinner } from '@sanity/cli-core/ux';
6
+ import { datasetSourceId, editSources, formatMcpEndpoint, parseEndpointName, parseNonEmpty } from '../../../actions/context/mcpEndpoint.js';
7
+ import { resolveOrganizationId } from '../../../actions/context/resolveOrganizationId.js';
8
+ import { promptForText } from '../../../prompts/promptForText.js';
9
+ import { createMcpEndpoint } from '../../../services/context.js';
10
+ import { getDatasetFlag, getOrganizationFlag, getProjectIdFlag } from '../../../util/sharedFlags.js';
11
+ import { defineCommandTelemetry } from '../../../util/telemetry/commandTelemetry.js';
12
+ const createMcpDebug = subdebug('context:mcp:create');
13
+ const SOURCE_FLAGS = [
14
+ 'knowledge-base',
15
+ 'dataset'
16
+ ];
17
+ const flags = {
18
+ ...getOrganizationFlag({
19
+ description: 'Organization to create the MCP endpoint in',
20
+ semantics: 'override'
21
+ }),
22
+ ...getDatasetFlag({
23
+ atLeastOne: SOURCE_FLAGS,
24
+ description: 'Dataset the endpoint serves',
25
+ semantics: 'specify'
26
+ }),
27
+ ...getProjectIdFlag({
28
+ dependsOn: [
29
+ 'dataset'
30
+ ],
31
+ description: 'Project the dataset belongs to',
32
+ semantics: 'override'
33
+ }),
34
+ 'groq-filter': Flags.string({
35
+ dependsOn: [
36
+ 'dataset'
37
+ ],
38
+ description: 'GROQ filter that limits which dataset documents the endpoint serves',
39
+ parse: parseNonEmpty('groq-filter')
40
+ }),
41
+ instructions: Flags.string({
42
+ description: 'Instructions the endpoint serves to connecting agents',
43
+ parse: parseNonEmpty('instructions')
44
+ }),
45
+ 'knowledge-base': Flags.string({
46
+ atLeastOne: SOURCE_FLAGS,
47
+ description: 'Knowledge base the endpoint serves; repeat the flag to serve several',
48
+ helpValue: '<id>',
49
+ multiple: true,
50
+ parse: parseNonEmpty('knowledge-base')
51
+ }),
52
+ perspective: Flags.custom({
53
+ dependsOn: [
54
+ 'dataset'
55
+ ],
56
+ description: 'Perspective agents query the dataset with',
57
+ options: [
58
+ 'published',
59
+ 'drafts',
60
+ 'raw'
61
+ ]
62
+ })(),
63
+ 'semantic-search': Flags.boolean({
64
+ allowNo: true,
65
+ dependsOn: [
66
+ 'dataset'
67
+ ],
68
+ description: 'Let agents use semantic search on the dataset (--no-semantic-search to turn it off)'
69
+ }),
70
+ title: requiredWhenUnattended(Flags.string({
71
+ description: 'MCP endpoint title',
72
+ parse: parseNonEmpty('title')
73
+ })),
74
+ workspace: Flags.string({
75
+ dependsOn: [
76
+ 'dataset'
77
+ ],
78
+ description: 'Studio workspace whose schema agents use, instead of the first workspace',
79
+ parse: parseNonEmpty('workspace')
80
+ })
81
+ };
82
+ export class CreateMcpEndpointCommand extends SanityCommand {
83
+ static args = {
84
+ name: Args.string({
85
+ description: 'Permanent MCP endpoint name used in its URL, made of lowercase letters, digits and hyphens',
86
+ parse: parseEndpointName,
87
+ required: true
88
+ })
89
+ };
90
+ static description = 'Create a Context MCP endpoint that serves knowledge bases and a dataset to agents';
91
+ static examples = [
92
+ {
93
+ command: '<%= config.bin %> <%= command.id %> support --title "Support" --knowledge-base kb-abc123 --knowledge-base kb-def456',
94
+ description: 'Create an MCP endpoint that serves two knowledge bases'
95
+ },
96
+ {
97
+ command: '<%= config.bin %> <%= command.id %> catalog --title "Catalog" --project abc123 --dataset production --perspective drafts --groq-filter \'_type in ["product"]\'',
98
+ description: 'Create an MCP endpoint that serves the products in a dataset'
99
+ }
100
+ ];
101
+ static flags = flags;
102
+ static telemetry = defineCommandTelemetry(flags, {
103
+ redact: [
104
+ 'title',
105
+ 'instructions',
106
+ 'groq-filter'
107
+ ]
108
+ });
109
+ async run() {
110
+ const { name } = this.args;
111
+ const { dataset, 'groq-filter': groqFilter, instructions, 'knowledge-base': knowledgeBaseIds = [], organization, perspective, project, 'semantic-search': semanticSearch, title: titleFlag, workspace } = this.flags;
112
+ const cliConfig = await this.tryGetCliConfig();
113
+ const datasetId = datasetSourceId(dataset, project ?? cliConfig.api?.projectId);
114
+ const organizationId = await resolveOrganizationId({
115
+ configuredOrganizationId: cliConfig.app?.organizationId,
116
+ flagOrganizationId: organization,
117
+ unattended: this.isUnattended()
118
+ });
119
+ const title = titleFlag || await promptForText('MCP endpoint title:');
120
+ const spin = spinner('Creating MCP endpoint').start();
121
+ try {
122
+ const endpoint = await createMcpEndpoint(organizationId, {
123
+ groqFilter,
124
+ instructions,
125
+ name,
126
+ sources: editSources([], {
127
+ addKnowledgeBases: knowledgeBaseIds,
128
+ dataset: datasetId,
129
+ removeKnowledgeBases: [],
130
+ settings: {
131
+ perspective,
132
+ semanticSearch,
133
+ workspace
134
+ }
135
+ }).sources,
136
+ title
137
+ });
138
+ spin.succeed('MCP endpoint created');
139
+ for (const line of formatMcpEndpoint(endpoint)){
140
+ this.log(line);
141
+ }
142
+ } catch (error) {
143
+ spin.fail();
144
+ createMcpDebug('Error creating MCP endpoint', error);
145
+ this.error(`Failed to create MCP endpoint: ${getErrorMessage(error)}`, {
146
+ exit: exitCodes.RUNTIME_ERROR
147
+ });
148
+ }
149
+ }
150
+ }
151
+
152
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/context/mcp/create.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {requiredWhenUnattended} from '@sanity/cli-core/flags'\nimport {spinner} from '@sanity/cli-core/ux'\n\nimport {\n datasetSourceId,\n editSources,\n formatMcpEndpoint,\n parseEndpointName,\n parseNonEmpty,\n} from '../../../actions/context/mcpEndpoint.js'\nimport {resolveOrganizationId} from '../../../actions/context/resolveOrganizationId.js'\nimport {promptForText} from '../../../prompts/promptForText.js'\nimport {createMcpEndpoint} from '../../../services/context.js'\nimport {getDatasetFlag, getOrganizationFlag, getProjectIdFlag} from '../../../util/sharedFlags.js'\nimport {defineCommandTelemetry} from '../../../util/telemetry/commandTelemetry.js'\n\nconst createMcpDebug = subdebug('context:mcp:create')\n\nconst SOURCE_FLAGS = ['knowledge-base', 'dataset']\n\nconst flags = {\n ...getOrganizationFlag({\n description: 'Organization to create the MCP endpoint in',\n semantics: 'override',\n }),\n ...getDatasetFlag({\n atLeastOne: SOURCE_FLAGS,\n description: 'Dataset the endpoint serves',\n semantics: 'specify',\n }),\n ...getProjectIdFlag({\n dependsOn: ['dataset'],\n description: 'Project the dataset belongs to',\n semantics: 'override',\n }),\n 'groq-filter': Flags.string({\n dependsOn: ['dataset'],\n description: 'GROQ filter that limits which dataset documents the endpoint serves',\n parse: parseNonEmpty('groq-filter'),\n }),\n instructions: Flags.string({\n description: 'Instructions the endpoint serves to connecting agents',\n parse: parseNonEmpty('instructions'),\n }),\n 'knowledge-base': Flags.string({\n atLeastOne: SOURCE_FLAGS,\n description: 'Knowledge base the endpoint serves; repeat the flag to serve several',\n helpValue: '<id>',\n multiple: true,\n parse: parseNonEmpty('knowledge-base'),\n }),\n perspective: Flags.custom<'drafts' | 'published' | 'raw'>({\n dependsOn: ['dataset'],\n description: 'Perspective agents query the dataset with',\n options: ['published', 'drafts', 'raw'],\n })(),\n 'semantic-search': Flags.boolean({\n allowNo: true,\n dependsOn: ['dataset'],\n description:\n 'Let agents use semantic search on the dataset (--no-semantic-search to turn it off)',\n }),\n title: requiredWhenUnattended(\n Flags.string({\n description: 'MCP endpoint title',\n parse: parseNonEmpty('title'),\n }),\n ),\n workspace: Flags.string({\n dependsOn: ['dataset'],\n description: 'Studio workspace whose schema agents use, instead of the first workspace',\n parse: parseNonEmpty('workspace'),\n }),\n} satisfies FlagInput\n\nexport class CreateMcpEndpointCommand extends SanityCommand<typeof CreateMcpEndpointCommand> {\n static override args = {\n name: Args.string({\n description:\n 'Permanent MCP endpoint name used in its URL, made of lowercase letters, digits and hyphens',\n parse: parseEndpointName,\n required: true,\n }),\n }\n\n static override description =\n 'Create a Context MCP endpoint that serves knowledge bases and a dataset to agents'\n\n static override examples = [\n {\n command:\n '<%= config.bin %> <%= command.id %> support --title \"Support\" --knowledge-base kb-abc123 --knowledge-base kb-def456',\n description: 'Create an MCP endpoint that serves two knowledge bases',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> catalog --title \"Catalog\" --project abc123 --dataset production --perspective drafts --groq-filter \\'_type in [\"product\"]\\'',\n description: 'Create an MCP endpoint that serves the products in a dataset',\n },\n ]\n\n static override flags = flags\n\n static telemetry = defineCommandTelemetry(flags, {\n redact: ['title', 'instructions', 'groq-filter'],\n })\n\n public async run(): Promise<void> {\n const {name} = this.args\n const {\n dataset,\n 'groq-filter': groqFilter,\n instructions,\n 'knowledge-base': knowledgeBaseIds = [],\n organization,\n perspective,\n project,\n 'semantic-search': semanticSearch,\n title: titleFlag,\n workspace,\n } = this.flags\n\n const cliConfig = await this.tryGetCliConfig()\n const datasetId = datasetSourceId(dataset, project ?? cliConfig.api?.projectId)\n\n const organizationId = await resolveOrganizationId({\n configuredOrganizationId: cliConfig.app?.organizationId,\n flagOrganizationId: organization,\n unattended: this.isUnattended(),\n })\n\n const title = titleFlag || (await promptForText('MCP endpoint title:'))\n\n const spin = spinner('Creating MCP endpoint').start()\n try {\n const endpoint = await createMcpEndpoint(organizationId, {\n groqFilter,\n instructions,\n name,\n sources: editSources([], {\n addKnowledgeBases: knowledgeBaseIds,\n dataset: datasetId,\n removeKnowledgeBases: [],\n settings: {perspective, semanticSearch, workspace},\n }).sources,\n title,\n })\n spin.succeed('MCP endpoint created')\n for (const line of formatMcpEndpoint(endpoint)) {\n this.log(line)\n }\n } catch (error) {\n spin.fail()\n createMcpDebug('Error creating MCP endpoint', error)\n this.error(`Failed to create MCP endpoint: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","requiredWhenUnattended","spinner","datasetSourceId","editSources","formatMcpEndpoint","parseEndpointName","parseNonEmpty","resolveOrganizationId","promptForText","createMcpEndpoint","getDatasetFlag","getOrganizationFlag","getProjectIdFlag","defineCommandTelemetry","createMcpDebug","SOURCE_FLAGS","flags","description","semantics","atLeastOne","dependsOn","string","parse","instructions","helpValue","multiple","perspective","custom","options","boolean","allowNo","title","workspace","CreateMcpEndpointCommand","args","name","required","examples","command","telemetry","redact","run","dataset","groqFilter","knowledgeBaseIds","organization","project","semanticSearch","titleFlag","cliConfig","tryGetCliConfig","datasetId","api","projectId","organizationId","configuredOrganizationId","app","flagOrganizationId","unattended","isUnattended","spin","start","endpoint","sources","addKnowledgeBases","removeKnowledgeBases","settings","succeed","line","log","error","fail","exit","RUNTIME_ERROR"],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
3
+ import { getErrorMessage } from '@sanity/cli-core/errors';
4
+ import { confirm } from '@sanity/cli-core/ux';
5
+ import { isHttpError } from '@sanity/client';
6
+ import { findMcpEndpoint, mcpEndpointNameArg, mcpEndpointOrganizationFlag } from '../../../actions/context/mcpEndpoint.js';
7
+ import { resolveOrganizationId } from '../../../actions/context/resolveOrganizationId.js';
8
+ import { deleteMcpEndpoint, describeBlueprintManager, getMcpEndpointBlueprintOwnership } from '../../../services/context.js';
9
+ const deleteMcpDebug = subdebug('context:mcp:delete');
10
+ export class DeleteMcpEndpointCommand extends SanityCommand {
11
+ static args = {
12
+ name: mcpEndpointNameArg
13
+ };
14
+ static description = 'Delete a Context MCP endpoint, which disconnects any agents using it';
15
+ static examples = [
16
+ {
17
+ command: '<%= config.bin %> <%= command.id %> support',
18
+ description: 'Delete an MCP endpoint after confirmation'
19
+ },
20
+ {
21
+ command: '<%= config.bin %> <%= command.id %> support --yes',
22
+ description: 'Delete an MCP endpoint without confirmation prompt'
23
+ }
24
+ ];
25
+ static flags = {
26
+ ...mcpEndpointOrganizationFlag,
27
+ yes: Flags.boolean({
28
+ char: 'y',
29
+ default: false,
30
+ description: 'Skip confirmation prompt (unattended mode)'
31
+ })
32
+ };
33
+ async run() {
34
+ const { name } = this.args;
35
+ const { organization, yes: skipConfirmation } = this.flags;
36
+ const organizationId = await resolveOrganizationId({
37
+ configuredOrganizationId: (await this.tryGetCliConfig()).app?.organizationId,
38
+ flagOrganizationId: organization,
39
+ // Not `isUnattended()`: `--yes` skips the confirmation, not the organization picker
40
+ unattended: !this.resolveIsInteractive()
41
+ });
42
+ const endpoint = await findMcpEndpoint(organizationId, name);
43
+ const ownership = await getMcpEndpointBlueprintOwnership(organizationId, endpoint);
44
+ if (ownership.managed) {
45
+ const manager = describeBlueprintManager(ownership.stackName);
46
+ this.error(`${manager} manages MCP endpoint "${name}", so the next deploy would recreate it. To delete it, remove it from the blueprint and run \`sanity blueprints deploy\`.`, {
47
+ exit: exitCodes.USAGE_ERROR
48
+ });
49
+ }
50
+ if (this.isUnattended() && !skipConfirmation) {
51
+ this.error('Deletion requires confirmation. Pass `--yes` to delete the MCP endpoint.', {
52
+ exit: exitCodes.USAGE_ERROR
53
+ });
54
+ }
55
+ if (!skipConfirmation) {
56
+ const confirmed = await confirm({
57
+ default: false,
58
+ message: `Delete MCP endpoint "${name}"? Agents connected through it lose access.`
59
+ });
60
+ if (!confirmed) {
61
+ this.log('MCP endpoint not deleted');
62
+ this.exit(exitCodes.USER_ABORT);
63
+ }
64
+ }
65
+ try {
66
+ await deleteMcpEndpoint(organizationId, endpoint.name);
67
+ this.log('MCP endpoint deleted');
68
+ } catch (error) {
69
+ deleteMcpDebug('Error deleting MCP endpoint', error);
70
+ if (isHttpError(error) && error.statusCode === 404) {
71
+ this.error(`MCP endpoint "${name}" not found`, {
72
+ exit: exitCodes.RUNTIME_ERROR
73
+ });
74
+ }
75
+ this.error(`Failed to delete MCP endpoint: ${getErrorMessage(error)}`, {
76
+ exit: exitCodes.RUNTIME_ERROR
77
+ });
78
+ }
79
+ }
80
+ }
81
+
82
+ //# sourceMappingURL=delete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/context/mcp/delete.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {confirm} from '@sanity/cli-core/ux'\nimport {isHttpError} from '@sanity/client'\n\nimport {\n findMcpEndpoint,\n mcpEndpointNameArg,\n mcpEndpointOrganizationFlag,\n} from '../../../actions/context/mcpEndpoint.js'\nimport {resolveOrganizationId} from '../../../actions/context/resolveOrganizationId.js'\nimport {\n deleteMcpEndpoint,\n describeBlueprintManager,\n getMcpEndpointBlueprintOwnership,\n} from '../../../services/context.js'\n\nconst deleteMcpDebug = subdebug('context:mcp:delete')\n\nexport class DeleteMcpEndpointCommand extends SanityCommand<typeof DeleteMcpEndpointCommand> {\n static override args = {\n name: mcpEndpointNameArg,\n }\n\n static override description =\n 'Delete a Context MCP endpoint, which disconnects any agents using it'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> support',\n description: 'Delete an MCP endpoint after confirmation',\n },\n {\n command: '<%= config.bin %> <%= command.id %> support --yes',\n description: 'Delete an MCP endpoint without confirmation prompt',\n },\n ]\n\n static override flags = {\n ...mcpEndpointOrganizationFlag,\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description: 'Skip confirmation prompt (unattended mode)',\n }),\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {name} = this.args\n const {organization, yes: skipConfirmation} = this.flags\n\n const organizationId = await resolveOrganizationId({\n configuredOrganizationId: (await this.tryGetCliConfig()).app?.organizationId,\n flagOrganizationId: organization,\n // Not `isUnattended()`: `--yes` skips the confirmation, not the organization picker\n unattended: !this.resolveIsInteractive(),\n })\n\n const endpoint = await findMcpEndpoint(organizationId, name)\n\n const ownership = await getMcpEndpointBlueprintOwnership(organizationId, endpoint)\n if (ownership.managed) {\n const manager = describeBlueprintManager(ownership.stackName)\n this.error(\n `${manager} manages MCP endpoint \"${name}\", so the next deploy would recreate it. To delete it, remove it from the blueprint and run \\`sanity blueprints deploy\\`.`,\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n if (this.isUnattended() && !skipConfirmation) {\n this.error('Deletion requires confirmation. Pass `--yes` to delete the MCP endpoint.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n if (!skipConfirmation) {\n const confirmed = await confirm({\n default: false,\n message: `Delete MCP endpoint \"${name}\"? Agents connected through it lose access.`,\n })\n\n if (!confirmed) {\n this.log('MCP endpoint not deleted')\n this.exit(exitCodes.USER_ABORT)\n }\n }\n\n try {\n await deleteMcpEndpoint(organizationId, endpoint.name)\n this.log('MCP endpoint deleted')\n } catch (error) {\n deleteMcpDebug('Error deleting MCP endpoint', error)\n if (isHttpError(error) && error.statusCode === 404) {\n this.error(`MCP endpoint \"${name}\" not found`, {exit: exitCodes.RUNTIME_ERROR})\n }\n this.error(`Failed to delete MCP endpoint: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","confirm","isHttpError","findMcpEndpoint","mcpEndpointNameArg","mcpEndpointOrganizationFlag","resolveOrganizationId","deleteMcpEndpoint","describeBlueprintManager","getMcpEndpointBlueprintOwnership","deleteMcpDebug","DeleteMcpEndpointCommand","args","name","description","examples","command","flags","yes","boolean","char","default","run","organization","skipConfirmation","organizationId","configuredOrganizationId","tryGetCliConfig","app","flagOrganizationId","unattended","resolveIsInteractive","endpoint","ownership","managed","manager","stackName","error","exit","USAGE_ERROR","isUnattended","confirmed","message","log","USER_ABORT","statusCode","RUNTIME_ERROR"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AAEjC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SACEC,eAAe,EACfC,kBAAkB,EAClBC,2BAA2B,QACtB,0CAAyC;AAChD,SAAQC,qBAAqB,QAAO,oDAAmD;AACvF,SACEC,iBAAiB,EACjBC,wBAAwB,EACxBC,gCAAgC,QAC3B,+BAA8B;AAErC,MAAMC,iBAAiBX,SAAS;AAEhC,OAAO,MAAMY,iCAAiCb;IAC5C,OAAgBc,OAAO;QACrBC,MAAMT;IACR,EAAC;IAED,OAAgBU,cACd,uEAAsE;IAExE,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;KACD,CAAA;IAED,OAAgBG,QAAQ;QACtB,GAAGZ,2BAA2B;QAC9Ba,KAAKtB,MAAMuB,OAAO,CAAC;YACjBC,MAAM;YACNC,SAAS;YACTP,aAAa;QACf;IACF,EAAqB;IAErB,MAAaQ,MAAqB;QAChC,MAAM,EAACT,IAAI,EAAC,GAAG,IAAI,CAACD,IAAI;QACxB,MAAM,EAACW,YAAY,EAAEL,KAAKM,gBAAgB,EAAC,GAAG,IAAI,CAACP,KAAK;QAExD,MAAMQ,iBAAiB,MAAMnB,sBAAsB;YACjDoB,0BAA0B,AAAC,CAAA,MAAM,IAAI,CAACC,eAAe,EAAC,EAAGC,GAAG,EAAEH;YAC9DI,oBAAoBN;YACpB,oFAAoF;YACpFO,YAAY,CAAC,IAAI,CAACC,oBAAoB;QACxC;QAEA,MAAMC,WAAW,MAAM7B,gBAAgBsB,gBAAgBZ;QAEvD,MAAMoB,YAAY,MAAMxB,iCAAiCgB,gBAAgBO;QACzE,IAAIC,UAAUC,OAAO,EAAE;YACrB,MAAMC,UAAU3B,yBAAyByB,UAAUG,SAAS;YAC5D,IAAI,CAACC,KAAK,CACR,GAAGF,QAAQ,uBAAuB,EAAEtB,KAAK,yHAAyH,CAAC,EACnK;gBAACyB,MAAMzC,UAAU0C,WAAW;YAAA;QAEhC;QAEA,IAAI,IAAI,CAACC,YAAY,MAAM,CAAChB,kBAAkB;YAC5C,IAAI,CAACa,KAAK,CAAC,4EAA4E;gBACrFC,MAAMzC,UAAU0C,WAAW;YAC7B;QACF;QAEA,IAAI,CAACf,kBAAkB;YACrB,MAAMiB,YAAY,MAAMxC,QAAQ;gBAC9BoB,SAAS;gBACTqB,SAAS,CAAC,qBAAqB,EAAE7B,KAAK,2CAA2C,CAAC;YACpF;YAEA,IAAI,CAAC4B,WAAW;gBACd,IAAI,CAACE,GAAG,CAAC;gBACT,IAAI,CAACL,IAAI,CAACzC,UAAU+C,UAAU;YAChC;QACF;QAEA,IAAI;YACF,MAAMrC,kBAAkBkB,gBAAgBO,SAASnB,IAAI;YACrD,IAAI,CAAC8B,GAAG,CAAC;QACX,EAAE,OAAON,OAAO;YACd3B,eAAe,+BAA+B2B;YAC9C,IAAInC,YAAYmC,UAAUA,MAAMQ,UAAU,KAAK,KAAK;gBAClD,IAAI,CAACR,KAAK,CAAC,CAAC,cAAc,EAAExB,KAAK,WAAW,CAAC,EAAE;oBAACyB,MAAMzC,UAAUiD,aAAa;gBAAA;YAC/E;YACA,IAAI,CAACT,KAAK,CAAC,CAAC,+BAA+B,EAAErC,gBAAgBqC,QAAQ,EAAE;gBACrEC,MAAMzC,UAAUiD,aAAa;YAC/B;QACF;IACF;AACF"}
@@ -0,0 +1,42 @@
1
+ import { SanityCommand } from '@sanity/cli-core';
2
+ import { findMcpEndpoint, formatMcpEndpoint, mcpEndpointNameArg, mcpEndpointOrganizationFlag } from '../../../actions/context/mcpEndpoint.js';
3
+ import { resolveOrganizationId } from '../../../actions/context/resolveOrganizationId.js';
4
+ export class GetMcpEndpointCommand extends SanityCommand {
5
+ static args = {
6
+ name: mcpEndpointNameArg
7
+ };
8
+ static description = 'Get details of a Context MCP endpoint, including its URL';
9
+ static enableJsonFlag = true;
10
+ static examples = [
11
+ {
12
+ command: '<%= config.bin %> <%= command.id %> support',
13
+ description: 'Get details of the "support" MCP endpoint'
14
+ },
15
+ {
16
+ command: '<%= config.bin %> <%= command.id %> support --organization org-abc123 --json',
17
+ description: 'Output an MCP endpoint in a specific organization as JSON'
18
+ }
19
+ ];
20
+ static flags = {
21
+ ...mcpEndpointOrganizationFlag
22
+ };
23
+ async run() {
24
+ const { name } = this.args;
25
+ const { json, organization } = this.flags;
26
+ const organizationId = await resolveOrganizationId({
27
+ configuredOrganizationId: (await this.tryGetCliConfig()).app?.organizationId,
28
+ flagOrganizationId: organization,
29
+ unattended: this.isUnattended()
30
+ });
31
+ const endpoint = await findMcpEndpoint(organizationId, name);
32
+ if (json) {
33
+ this.logJson(endpoint);
34
+ return;
35
+ }
36
+ for (const line of formatMcpEndpoint(endpoint)){
37
+ this.log(line);
38
+ }
39
+ }
40
+ }
41
+
42
+ //# sourceMappingURL=get.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/context/mcp/get.ts"],"sourcesContent":["import {type FlagInput} from '@oclif/core/interfaces'\nimport {SanityCommand} from '@sanity/cli-core'\n\nimport {\n findMcpEndpoint,\n formatMcpEndpoint,\n mcpEndpointNameArg,\n mcpEndpointOrganizationFlag,\n} from '../../../actions/context/mcpEndpoint.js'\nimport {resolveOrganizationId} from '../../../actions/context/resolveOrganizationId.js'\n\nexport class GetMcpEndpointCommand extends SanityCommand<typeof GetMcpEndpointCommand> {\n static override args = {\n name: mcpEndpointNameArg,\n }\n\n static override description = 'Get details of a Context MCP endpoint, including its URL'\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> support',\n description: 'Get details of the \"support\" MCP endpoint',\n },\n {\n command: '<%= config.bin %> <%= command.id %> support --organization org-abc123 --json',\n description: 'Output an MCP endpoint in a specific organization as JSON',\n },\n ]\n\n static override flags = {\n ...mcpEndpointOrganizationFlag,\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {name} = this.args\n const {json, organization} = this.flags\n\n const organizationId = await resolveOrganizationId({\n configuredOrganizationId: (await this.tryGetCliConfig()).app?.organizationId,\n flagOrganizationId: organization,\n unattended: this.isUnattended(),\n })\n\n const endpoint = await findMcpEndpoint(organizationId, name)\n\n if (json) {\n this.logJson(endpoint)\n return\n }\n\n for (const line of formatMcpEndpoint(endpoint)) {\n this.log(line)\n }\n }\n}\n"],"names":["SanityCommand","findMcpEndpoint","formatMcpEndpoint","mcpEndpointNameArg","mcpEndpointOrganizationFlag","resolveOrganizationId","GetMcpEndpointCommand","args","name","description","enableJsonFlag","examples","command","flags","run","json","organization","organizationId","configuredOrganizationId","tryGetCliConfig","app","flagOrganizationId","unattended","isUnattended","endpoint","logJson","line","log"],"mappings":"AACA,SAAQA,aAAa,QAAO,mBAAkB;AAE9C,SACEC,eAAe,EACfC,iBAAiB,EACjBC,kBAAkB,EAClBC,2BAA2B,QACtB,0CAAyC;AAChD,SAAQC,qBAAqB,QAAO,oDAAmD;AAEvF,OAAO,MAAMC,8BAA8BN;IACzC,OAAgBO,OAAO;QACrBC,MAAML;IACR,EAAC;IAED,OAAgBM,cAAc,2DAA0D;IAExF,OAAgBC,iBAAiB,KAAI;IAErC,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IAED,OAAgBI,QAAQ;QACtB,GAAGT,2BAA2B;IAChC,EAAqB;IAErB,MAAaU,MAAqB;QAChC,MAAM,EAACN,IAAI,EAAC,GAAG,IAAI,CAACD,IAAI;QACxB,MAAM,EAACQ,IAAI,EAAEC,YAAY,EAAC,GAAG,IAAI,CAACH,KAAK;QAEvC,MAAMI,iBAAiB,MAAMZ,sBAAsB;YACjDa,0BAA0B,AAAC,CAAA,MAAM,IAAI,CAACC,eAAe,EAAC,EAAGC,GAAG,EAAEH;YAC9DI,oBAAoBL;YACpBM,YAAY,IAAI,CAACC,YAAY;QAC/B;QAEA,MAAMC,WAAW,MAAMvB,gBAAgBgB,gBAAgBT;QAEvD,IAAIO,MAAM;YACR,IAAI,CAACU,OAAO,CAACD;YACb;QACF;QAEA,KAAK,MAAME,QAAQxB,kBAAkBsB,UAAW;YAC9C,IAAI,CAACG,GAAG,CAACD;QACX;IACF;AACF"}
@@ -0,0 +1,87 @@
1
+ import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
2
+ import { getErrorMessage } from '@sanity/cli-core/errors';
3
+ import { resolveOrganizationId } from '../../../actions/context/resolveOrganizationId.js';
4
+ import { listMcpEndpoints } from '../../../services/context.js';
5
+ import { Table } from '../../../util/responsiveTable.js';
6
+ import { getOrganizationFlag } from '../../../util/sharedFlags.js';
7
+ const listMcpDebug = subdebug('context:mcp:list');
8
+ export class ListMcpEndpointsCommand extends SanityCommand {
9
+ static description = 'List Context MCP endpoints in an organization';
10
+ static enableJsonFlag = true;
11
+ static examples = [
12
+ {
13
+ command: '<%= config.bin %> <%= command.id %>',
14
+ description: 'List MCP endpoints, selecting the organization interactively'
15
+ },
16
+ {
17
+ command: '<%= config.bin %> <%= command.id %> --organization org-abc123 --json',
18
+ description: 'Output the MCP endpoints of a specific organization as JSON'
19
+ }
20
+ ];
21
+ static flags = {
22
+ ...getOrganizationFlag({
23
+ description: 'Organization to list MCP endpoints for',
24
+ semantics: 'override'
25
+ })
26
+ };
27
+ async run() {
28
+ const { json, organization } = this.flags;
29
+ const organizationId = await resolveOrganizationId({
30
+ configuredOrganizationId: (await this.tryGetCliConfig()).app?.organizationId,
31
+ flagOrganizationId: organization,
32
+ unattended: this.isUnattended()
33
+ });
34
+ let endpoints;
35
+ try {
36
+ endpoints = await listMcpEndpoints(organizationId);
37
+ } catch (error) {
38
+ listMcpDebug('Error listing MCP endpoints', error);
39
+ this.error(`Failed to list MCP endpoints: ${getErrorMessage(error)}`, {
40
+ exit: exitCodes.RUNTIME_ERROR
41
+ });
42
+ }
43
+ if (json) {
44
+ this.logJson(endpoints);
45
+ return;
46
+ }
47
+ if (endpoints.length === 0) {
48
+ this.log('No MCP endpoints found');
49
+ return;
50
+ }
51
+ const table = new Table({
52
+ columns: [
53
+ {
54
+ alignment: 'left',
55
+ name: 'name',
56
+ title: 'Name'
57
+ },
58
+ {
59
+ alignment: 'left',
60
+ name: 'title',
61
+ title: 'Title'
62
+ },
63
+ {
64
+ alignment: 'left',
65
+ name: 'sources',
66
+ title: 'Sources'
67
+ },
68
+ {
69
+ alignment: 'left',
70
+ name: 'url',
71
+ title: 'URL'
72
+ }
73
+ ]
74
+ });
75
+ for (const { name, sources, title, url } of endpoints){
76
+ table.addRow({
77
+ name,
78
+ sources: sources.map((source)=>source.id).join(', '),
79
+ title,
80
+ url
81
+ });
82
+ }
83
+ this.log(table.render());
84
+ }
85
+ }
86
+
87
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/context/mcp/list.ts"],"sourcesContent":["import {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\n\nimport {resolveOrganizationId} from '../../../actions/context/resolveOrganizationId.js'\nimport {listMcpEndpoints} from '../../../services/context.js'\nimport {Table} from '../../../util/responsiveTable.js'\nimport {getOrganizationFlag} from '../../../util/sharedFlags.js'\n\nconst listMcpDebug = subdebug('context:mcp:list')\n\nexport class ListMcpEndpointsCommand extends SanityCommand<typeof ListMcpEndpointsCommand> {\n static override description = 'List Context MCP endpoints in an organization'\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List MCP endpoints, selecting the organization interactively',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --organization org-abc123 --json',\n description: 'Output the MCP endpoints of a specific organization as JSON',\n },\n ]\n\n static override flags = {\n ...getOrganizationFlag({\n description: 'Organization to list MCP endpoints for',\n semantics: 'override',\n }),\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {json, organization} = this.flags\n\n const organizationId = await resolveOrganizationId({\n configuredOrganizationId: (await this.tryGetCliConfig()).app?.organizationId,\n flagOrganizationId: organization,\n unattended: this.isUnattended(),\n })\n\n let endpoints\n try {\n endpoints = await listMcpEndpoints(organizationId)\n } catch (error) {\n listMcpDebug('Error listing MCP endpoints', error)\n this.error(`Failed to list MCP endpoints: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (json) {\n this.logJson(endpoints)\n return\n }\n\n if (endpoints.length === 0) {\n this.log('No MCP endpoints found')\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'name', title: 'Name'},\n {alignment: 'left', name: 'title', title: 'Title'},\n {alignment: 'left', name: 'sources', title: 'Sources'},\n {alignment: 'left', name: 'url', title: 'URL'},\n ],\n })\n\n for (const {name, sources, title, url} of endpoints) {\n table.addRow({name, sources: sources.map((source) => source.id).join(', '), title, url})\n }\n\n this.log(table.render())\n }\n}\n"],"names":["exitCodes","SanityCommand","subdebug","getErrorMessage","resolveOrganizationId","listMcpEndpoints","Table","getOrganizationFlag","listMcpDebug","ListMcpEndpointsCommand","description","enableJsonFlag","examples","command","flags","semantics","run","json","organization","organizationId","configuredOrganizationId","tryGetCliConfig","app","flagOrganizationId","unattended","isUnattended","endpoints","error","exit","RUNTIME_ERROR","logJson","length","log","table","columns","alignment","name","title","sources","url","addRow","map","source","id","join","render"],"mappings":"AACA,SAAQA,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,QAAO,0BAAyB;AAEvD,SAAQC,qBAAqB,QAAO,oDAAmD;AACvF,SAAQC,gBAAgB,QAAO,+BAA8B;AAC7D,SAAQC,KAAK,QAAO,mCAAkC;AACtD,SAAQC,mBAAmB,QAAO,+BAA8B;AAEhE,MAAMC,eAAeN,SAAS;AAE9B,OAAO,MAAMO,gCAAgCR;IAC3C,OAAgBS,cAAc,gDAA+C;IAE7E,OAAgBC,iBAAiB,KAAI;IAErC,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IAED,OAAgBI,QAAQ;QACtB,GAAGP,oBAAoB;YACrBG,aAAa;YACbK,WAAW;QACb,EAAE;IACJ,EAAqB;IAErB,MAAaC,MAAqB;QAChC,MAAM,EAACC,IAAI,EAAEC,YAAY,EAAC,GAAG,IAAI,CAACJ,KAAK;QAEvC,MAAMK,iBAAiB,MAAMf,sBAAsB;YACjDgB,0BAA0B,AAAC,CAAA,MAAM,IAAI,CAACC,eAAe,EAAC,EAAGC,GAAG,EAAEH;YAC9DI,oBAAoBL;YACpBM,YAAY,IAAI,CAACC,YAAY;QAC/B;QAEA,IAAIC;QACJ,IAAI;YACFA,YAAY,MAAMrB,iBAAiBc;QACrC,EAAE,OAAOQ,OAAO;YACdnB,aAAa,+BAA+BmB;YAC5C,IAAI,CAACA,KAAK,CAAC,CAAC,8BAA8B,EAAExB,gBAAgBwB,QAAQ,EAAE;gBACpEC,MAAM5B,UAAU6B,aAAa;YAC/B;QACF;QAEA,IAAIZ,MAAM;YACR,IAAI,CAACa,OAAO,CAACJ;YACb;QACF;QAEA,IAAIA,UAAUK,MAAM,KAAK,GAAG;YAC1B,IAAI,CAACC,GAAG,CAAC;YACT;QACF;QAEA,MAAMC,QAAQ,IAAI3B,MAAM;YACtB4B,SAAS;gBACP;oBAACC,WAAW;oBAAQC,MAAM;oBAAQC,OAAO;gBAAM;gBAC/C;oBAACF,WAAW;oBAAQC,MAAM;oBAASC,OAAO;gBAAO;gBACjD;oBAACF,WAAW;oBAAQC,MAAM;oBAAWC,OAAO;gBAAS;gBACrD;oBAACF,WAAW;oBAAQC,MAAM;oBAAOC,OAAO;gBAAK;aAC9C;QACH;QAEA,KAAK,MAAM,EAACD,IAAI,EAAEE,OAAO,EAAED,KAAK,EAAEE,GAAG,EAAC,IAAIb,UAAW;YACnDO,MAAMO,MAAM,CAAC;gBAACJ;gBAAME,SAASA,QAAQG,GAAG,CAAC,CAACC,SAAWA,OAAOC,EAAE,EAAEC,IAAI,CAAC;gBAAOP;gBAAOE;YAAG;QACxF;QAEA,IAAI,CAACP,GAAG,CAACC,MAAMY,MAAM;IACvB;AACF"}
@@ -0,0 +1,98 @@
1
+ import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
2
+ import { getErrorMessage } from '@sanity/cli-core/errors';
3
+ import { isHttpError } from '@sanity/client';
4
+ import { findMcpEndpoint, mcpEndpointNameArg, mcpEndpointOrganizationFlag } from '../../../actions/context/mcpEndpoint.js';
5
+ import { resolveOrganizationId } from '../../../actions/context/resolveOrganizationId.js';
6
+ import { formatKeyValue } from '../../../actions/debug/output.js';
7
+ import { getMcpEndpointStatus } from '../../../services/context.js';
8
+ const statusMcpDebug = subdebug('context:mcp:status');
9
+ export class McpEndpointStatusCommand extends SanityCommand {
10
+ static args = {
11
+ name: mcpEndpointNameArg
12
+ };
13
+ static description = 'Check whether an agent connecting with your credentials can use a Context MCP endpoint';
14
+ static enableJsonFlag = true;
15
+ static examples = [
16
+ {
17
+ command: '<%= config.bin %> <%= command.id %> support',
18
+ description: 'Check the "support" MCP endpoint'
19
+ }
20
+ ];
21
+ static flags = {
22
+ ...mcpEndpointOrganizationFlag
23
+ };
24
+ async run() {
25
+ const { name } = this.args;
26
+ const { json, organization } = this.flags;
27
+ const organizationId = await resolveOrganizationId({
28
+ configuredOrganizationId: (await this.tryGetCliConfig()).app?.organizationId,
29
+ flagOrganizationId: organization,
30
+ unattended: this.isUnattended()
31
+ });
32
+ const endpoint = await findMcpEndpoint(organizationId, name);
33
+ let status;
34
+ try {
35
+ // The status route only takes names, not IDs
36
+ status = await getMcpEndpointStatus(organizationId, endpoint.name);
37
+ } catch (error) {
38
+ statusMcpDebug('Error checking MCP endpoint', error);
39
+ if (isHttpError(error) && error.statusCode === 404) {
40
+ this.error(`MCP endpoint "${name}" not found`, {
41
+ exit: exitCodes.RUNTIME_ERROR
42
+ });
43
+ }
44
+ this.error(`Failed to check MCP endpoint: ${getErrorMessage(error)}`, {
45
+ exit: exitCodes.RUNTIME_ERROR
46
+ });
47
+ }
48
+ if (json) {
49
+ this.logJson(status);
50
+ } else {
51
+ this.printStatus(status);
52
+ }
53
+ // Like `sanity doctor`: fail when agents can't use the endpoint. Semantic search never affects `requiredOk`
54
+ if (!status.requiredOk) this.exit(exitCodes.RUNTIME_ERROR);
55
+ }
56
+ printStatus(status) {
57
+ const { knowledgeBases, schema, semanticSearch, studio } = status.checks;
58
+ const padTo = 15 // "Knowledge bases" and "Semantic search" are the longest keys
59
+ ;
60
+ this.log(formatKeyValue('Studio', formatCheck(studio, (check)=>`workspace "${check.workspace}"`), {
61
+ padTo
62
+ }));
63
+ this.log(formatKeyValue('Schema', formatCheck(schema, (check)=>`${check.documentTypes} document types, ${check.perspective} perspective`), {
64
+ padTo
65
+ }));
66
+ this.log(formatKeyValue('Knowledge bases', formatCheck(knowledgeBases, (check)=>check.resolved.join(', ')), {
67
+ padTo
68
+ }));
69
+ this.log(formatKeyValue('Semantic search', formatSemanticSearch(semanticSearch), {
70
+ padTo
71
+ }));
72
+ this.log(formatKeyValue('Ready', status.requiredOk ? 'yes' : 'no', {
73
+ padTo
74
+ }));
75
+ }
76
+ }
77
+ /**
78
+ * A check is `null` when it doesn't apply to the endpoint
79
+ */ function formatCheck(check, describe) {
80
+ if (!check) return '-';
81
+ return isPassed(check) ? `ok (${describe(check)})` : `failed: ${check.error}`;
82
+ }
83
+ function isPassed(check) {
84
+ return check.ok;
85
+ }
86
+ const INACTIVE_REASONS = {
87
+ datasetEmbeddingsNotReady: 'embeddings are still being prepared',
88
+ turnedOff: 'turned off',
89
+ usageLimitReached: 'embeddings usage limit reached'
90
+ };
91
+ function formatSemanticSearch(check) {
92
+ if (!check) return '-';
93
+ if (!isPassed(check)) return `failed: ${check.error}`;
94
+ if (check.active) return 'active';
95
+ return check.inactiveReason ? `inactive (${INACTIVE_REASONS[check.inactiveReason]})` : 'inactive';
96
+ }
97
+
98
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/context/mcp/status.ts"],"sourcesContent":["import {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {type Context, isHttpError} from '@sanity/client'\n\nimport {\n findMcpEndpoint,\n mcpEndpointNameArg,\n mcpEndpointOrganizationFlag,\n} from '../../../actions/context/mcpEndpoint.js'\nimport {resolveOrganizationId} from '../../../actions/context/resolveOrganizationId.js'\nimport {formatKeyValue} from '../../../actions/debug/output.js'\nimport {getMcpEndpointStatus} from '../../../services/context.js'\n\nconst statusMcpDebug = subdebug('context:mcp:status')\n\nexport class McpEndpointStatusCommand extends SanityCommand<typeof McpEndpointStatusCommand> {\n static override args = {\n name: mcpEndpointNameArg,\n }\n\n static override description =\n 'Check whether an agent connecting with your credentials can use a Context MCP endpoint'\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> support',\n description: 'Check the \"support\" MCP endpoint',\n },\n ]\n\n static override flags = {\n ...mcpEndpointOrganizationFlag,\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {name} = this.args\n const {json, organization} = this.flags\n\n const organizationId = await resolveOrganizationId({\n configuredOrganizationId: (await this.tryGetCliConfig()).app?.organizationId,\n flagOrganizationId: organization,\n unattended: this.isUnattended(),\n })\n\n const endpoint = await findMcpEndpoint(organizationId, name)\n\n let status\n try {\n // The status route only takes names, not IDs\n status = await getMcpEndpointStatus(organizationId, endpoint.name)\n } catch (error) {\n statusMcpDebug('Error checking MCP endpoint', error)\n if (isHttpError(error) && error.statusCode === 404) {\n this.error(`MCP endpoint \"${name}\" not found`, {exit: exitCodes.RUNTIME_ERROR})\n }\n this.error(`Failed to check MCP endpoint: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (json) {\n this.logJson(status)\n } else {\n this.printStatus(status)\n }\n\n // Like `sanity doctor`: fail when agents can't use the endpoint. Semantic search never affects `requiredOk`\n if (!status.requiredOk) this.exit(exitCodes.RUNTIME_ERROR)\n }\n\n private printStatus(status: Context.McpEndpointStatus): void {\n const {knowledgeBases, schema, semanticSearch, studio} = status.checks\n const padTo = 15 // \"Knowledge bases\" and \"Semantic search\" are the longest keys\n this.log(\n formatKeyValue(\n 'Studio',\n formatCheck(studio, (check) => `workspace \"${check.workspace}\"`),\n {padTo},\n ),\n )\n this.log(\n formatKeyValue(\n 'Schema',\n formatCheck(\n schema,\n (check) => `${check.documentTypes} document types, ${check.perspective} perspective`,\n ),\n {padTo},\n ),\n )\n this.log(\n formatKeyValue(\n 'Knowledge bases',\n formatCheck(knowledgeBases, (check) => check.resolved.join(', ')),\n {padTo},\n ),\n )\n this.log(formatKeyValue('Semantic search', formatSemanticSearch(semanticSearch), {padTo}))\n this.log(formatKeyValue('Ready', status.requiredOk ? 'yes' : 'no', {padTo}))\n }\n}\n\n/**\n * A check is `null` when it doesn't apply to the endpoint\n */\nfunction formatCheck<Check extends {error?: string; ok: boolean}>(\n check: Check | null,\n describe: (check: Extract<Check, {ok: true}>) => string,\n): string {\n if (!check) return '-'\n return isPassed(check) ? `ok (${describe(check)})` : `failed: ${check.error}`\n}\n\nfunction isPassed<Check extends {ok: boolean}>(check: Check): check is Extract<Check, {ok: true}> {\n return check.ok\n}\n\ntype SemanticSearchCheck = Context.McpEndpointStatus['checks']['semanticSearch']\n\nconst INACTIVE_REASONS = {\n datasetEmbeddingsNotReady: 'embeddings are still being prepared',\n turnedOff: 'turned off',\n usageLimitReached: 'embeddings usage limit reached',\n} satisfies Record<NonNullable<Extract<SemanticSearchCheck, {ok: true}>['inactiveReason']>, string>\n\nfunction formatSemanticSearch(check: SemanticSearchCheck): string {\n if (!check) return '-'\n if (!isPassed(check)) return `failed: ${check.error}`\n if (check.active) return 'active'\n return check.inactiveReason ? `inactive (${INACTIVE_REASONS[check.inactiveReason]})` : 'inactive'\n}\n"],"names":["exitCodes","SanityCommand","subdebug","getErrorMessage","isHttpError","findMcpEndpoint","mcpEndpointNameArg","mcpEndpointOrganizationFlag","resolveOrganizationId","formatKeyValue","getMcpEndpointStatus","statusMcpDebug","McpEndpointStatusCommand","args","name","description","enableJsonFlag","examples","command","flags","run","json","organization","organizationId","configuredOrganizationId","tryGetCliConfig","app","flagOrganizationId","unattended","isUnattended","endpoint","status","error","statusCode","exit","RUNTIME_ERROR","logJson","printStatus","requiredOk","knowledgeBases","schema","semanticSearch","studio","checks","padTo","log","formatCheck","check","workspace","documentTypes","perspective","resolved","join","formatSemanticSearch","describe","isPassed","ok","INACTIVE_REASONS","datasetEmbeddingsNotReady","turnedOff","usageLimitReached","active","inactiveReason"],"mappings":"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"}