@@ -46,6 +46,8 @@ import { IPreferencesEditorModel, IPreferencesService, ISetting, ISettingsEditor
46
46
import { DefaultSettingsEditorModel , SettingsEditorModel , WorkspaceConfigurationEditorModel } from '../../../services/preferences/common/preferencesModels.js' ;
47
47
import { IUserDataProfileService } from '../../../services/userDataProfile/common/userDataProfile.js' ;
48
48
import { EXPERIMENTAL_INDICATOR_DESCRIPTION , PREVIEW_INDICATOR_DESCRIPTION } from '../common/preferences.js' ;
49
+ import { mcpConfigurationSection } from '../../mcp/common/mcpConfiguration.js' ;
50
+ import { McpCommandIds } from '../../mcp/common/mcpCommandIds.js' ;
49
51
50
52
export interface IPreferencesRenderer extends IDisposable {
51
53
render ( ) : void ;
@@ -63,6 +65,7 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend
63
65
private associatedPreferencesModel ! : IPreferencesEditorModel < ISetting > ;
64
66
65
67
private unsupportedSettingsRenderer : UnsupportedSettingsRenderer ;
68
+ private mcpSettingsRenderer : McpSettingsRenderer ;
66
69
67
70
constructor ( protected editor : ICodeEditor , readonly preferencesModel : SettingsEditorModel ,
68
71
@IPreferencesService protected preferencesService : IPreferencesService ,
@@ -75,11 +78,13 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend
75
78
this . _register ( this . editSettingActionRenderer . onUpdateSetting ( ( { key, value, source } ) => this . updatePreference ( key , value , source ) ) ) ;
76
79
this . _register ( this . editor . getModel ( ) ! . onDidChangeContent ( ( ) => this . modelChangeDelayer . trigger ( ( ) => this . onModelChanged ( ) ) ) ) ;
77
80
this . unsupportedSettingsRenderer = this . _register ( instantiationService . createInstance ( UnsupportedSettingsRenderer , editor , preferencesModel ) ) ;
81
+ this . mcpSettingsRenderer = this . _register ( instantiationService . createInstance ( McpSettingsRenderer , editor , preferencesModel ) ) ;
78
82
}
79
83
80
84
render ( ) : void {
81
85
this . editSettingActionRenderer . render ( this . preferencesModel . settingsGroups , this . associatedPreferencesModel ) ;
82
86
this . unsupportedSettingsRenderer . render ( ) ;
87
+ this . mcpSettingsRenderer . render ( ) ;
83
88
}
84
89
85
90
updatePreference ( key : string , value : any , source : IIndexedSetting ) : void {
@@ -784,6 +789,126 @@ class UnsupportedSettingsRenderer extends Disposable implements languages.CodeAc
784
789
785
790
}
786
791
792
+ class McpSettingsRenderer extends Disposable implements languages . CodeActionProvider {
793
+
794
+ private renderingDelayer : Delayer < void > = new Delayer < void > ( 200 ) ;
795
+ private readonly codeActions = new ResourceMap < [ Range , languages . CodeAction [ ] ] [ ] > ( uri => this . uriIdentityService . extUri . getComparisonKey ( uri ) ) ;
796
+
797
+ constructor (
798
+ private readonly editor : ICodeEditor ,
799
+ private readonly settingsEditorModel : SettingsEditorModel ,
800
+ @IMarkerService private readonly markerService : IMarkerService ,
801
+ @IUriIdentityService private readonly uriIdentityService : IUriIdentityService ,
802
+ @ILanguageFeaturesService languageFeaturesService : ILanguageFeaturesService ,
803
+ ) {
804
+ super ( ) ;
805
+ this . _register ( this . editor . getModel ( ) ! . onDidChangeContent ( ( ) => this . delayedRender ( ) ) ) ;
806
+ this . _register ( languageFeaturesService . codeActionProvider . register ( { pattern : settingsEditorModel . uri . path } , this ) ) ;
807
+ }
808
+
809
+ private delayedRender ( ) : void {
810
+ this . renderingDelayer . trigger ( ( ) => this . render ( ) ) ;
811
+ }
812
+
813
+ public render ( ) : void {
814
+ this . codeActions . clear ( ) ;
815
+ const markerData : IMarkerData [ ] = this . generateMarkerData ( ) ;
816
+ if ( markerData . length ) {
817
+ this . markerService . changeOne ( 'McpSettingsRenderer' , this . settingsEditorModel . uri , markerData ) ;
818
+ } else {
819
+ this . markerService . remove ( 'McpSettingsRenderer' , [ this . settingsEditorModel . uri ] ) ;
820
+ }
821
+ }
822
+
823
+ async provideCodeActions ( model : ITextModel , range : Range | Selection , context : languages . CodeActionContext , token : CancellationToken ) : Promise < languages . CodeActionList > {
824
+ const actions : languages . CodeAction [ ] = [ ] ;
825
+ const codeActionsByRange = this . codeActions . get ( model . uri ) ;
826
+ if ( codeActionsByRange ) {
827
+ for ( const [ codeActionsRange , codeActions ] of codeActionsByRange ) {
828
+ if ( codeActionsRange . containsRange ( range ) ) {
829
+ actions . push ( ...codeActions ) ;
830
+ }
831
+ }
832
+ }
833
+ return {
834
+ actions,
835
+ dispose : ( ) => { }
836
+ } ;
837
+ }
838
+
839
+ private generateMarkerData ( ) : IMarkerData [ ] {
840
+ const markerData : IMarkerData [ ] = [ ] ;
841
+
842
+ // Only check for MCP configuration in user local and user remote settings
843
+ if ( this . settingsEditorModel . configurationTarget !== ConfigurationTarget . USER_LOCAL &&
844
+ this . settingsEditorModel . configurationTarget !== ConfigurationTarget . USER_REMOTE ) {
845
+ return markerData ;
846
+ }
847
+
848
+ for ( const settingsGroup of this . settingsEditorModel . settingsGroups ) {
849
+ for ( const section of settingsGroup . sections ) {
850
+ for ( const setting of section . settings ) {
851
+ if ( setting . key === mcpConfigurationSection ) {
852
+ const marker = this . generateMcpConfigurationMarker ( setting ) ;
853
+ markerData . push ( marker ) ;
854
+ const codeActions = this . generateMcpConfigurationCodeActions ( [ marker ] ) ;
855
+ this . addCodeActions ( setting . range , codeActions ) ;
856
+ }
857
+ }
858
+ }
859
+ }
860
+ return markerData ;
861
+ }
862
+
863
+ private generateMcpConfigurationMarker ( setting : ISetting ) : IMarkerData {
864
+ const isRemote = this . settingsEditorModel . configurationTarget === ConfigurationTarget . USER_REMOTE ;
865
+ const message = isRemote
866
+ ? nls . localize ( 'mcp.renderer.remoteConfigFound' , 'MCP servers should not be configured in remote user settings. Use the dedicated MCP configuration instead.' )
867
+ : nls . localize ( 'mcp.renderer.userConfigFound' , 'MCP servers should not be configured in user settings. Use the dedicated MCP configuration instead.' ) ;
868
+
869
+ return {
870
+ severity : MarkerSeverity . Warning ,
871
+ ...setting . range ,
872
+ message
873
+ } ;
874
+ }
875
+
876
+ private generateMcpConfigurationCodeActions ( diagnostics : IMarkerData [ ] ) : languages . CodeAction [ ] {
877
+ const isRemote = this . settingsEditorModel . configurationTarget === ConfigurationTarget . USER_REMOTE ;
878
+ const openConfigLabel = isRemote
879
+ ? nls . localize ( 'mcp.renderer.openRemoteConfig' , 'Open Remote User MCP Configuration' )
880
+ : nls . localize ( 'mcp.renderer.openUserConfig' , 'Open User MCP Configuration' ) ;
881
+
882
+ const commandId = isRemote ? McpCommandIds . OpenRemoteUserMcp : McpCommandIds . OpenUserMcp ;
883
+
884
+ return [ {
885
+ title : openConfigLabel ,
886
+ command : {
887
+ id : commandId ,
888
+ title : openConfigLabel
889
+ } ,
890
+ diagnostics,
891
+ kind : CodeActionKind . QuickFix . value
892
+ } ] ;
893
+ }
894
+
895
+ private addCodeActions ( range : IRange , codeActions : languages . CodeAction [ ] ) : void {
896
+ let actions = this . codeActions . get ( this . settingsEditorModel . uri ) ;
897
+ if ( ! actions ) {
898
+ actions = [ ] ;
899
+ this . codeActions . set ( this . settingsEditorModel . uri , actions ) ;
900
+ }
901
+ actions . push ( [ Range . lift ( range ) , codeActions ] ) ;
902
+ }
903
+
904
+ public override dispose ( ) : void {
905
+ this . markerService . remove ( 'McpSettingsRenderer' , [ this . settingsEditorModel . uri ] ) ;
906
+ this . codeActions . clear ( ) ;
907
+ super . dispose ( ) ;
908
+ }
909
+
910
+ }
911
+
787
912
class WorkspaceConfigurationRenderer extends Disposable {
788
913
private static readonly supportedKeys = [ 'folders' , 'tasks' , 'launch' , 'extensions' , 'settings' , 'remoteAuthority' , 'transient' ] ;
789
914
0 commit comments