-
-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathRpcClient.cs
More file actions
159 lines (136 loc) · 7.4 KB
/
RpcClient.cs
File metadata and controls
159 lines (136 loc) · 7.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO.Pipes;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using CodingWithCalvin.MCPServer.Shared;
using CodingWithCalvin.MCPServer.Shared.Models;
using ModelContextProtocol.Server;
using StreamJsonRpc;
namespace CodingWithCalvin.MCPServer.Server;
public class RpcClient : IVisualStudioRpc, IServerRpc, IDisposable
{
private readonly CancellationTokenSource _shutdownCts;
private NamedPipeClientStream? _pipeClient;
private JsonRpc? _jsonRpc;
private IVisualStudioRpc? _proxy;
private bool _disposed;
private List<ToolInfo>? _cachedTools;
public bool IsConnected => _pipeClient?.IsConnected ?? false;
public RpcClient(CancellationTokenSource shutdownCts)
{
_shutdownCts = shutdownCts;
}
public async Task ConnectAsync(string pipeName, int timeoutMs = 10000)
{
_pipeClient = new NamedPipeClientStream(
".",
pipeName,
PipeDirection.InOut,
PipeOptions.Asynchronous);
await _pipeClient.ConnectAsync(timeoutMs);
_jsonRpc = JsonRpc.Attach(_pipeClient, this);
_proxy = _jsonRpc.Attach<IVisualStudioRpc>();
}
public void Dispose()
{
if (_disposed)
{
return;
}
_disposed = true;
_jsonRpc?.Dispose();
_pipeClient?.Dispose();
}
private IVisualStudioRpc Proxy => _proxy ?? throw new InvalidOperationException("Not connected to Visual Studio");
public Task<List<ToolInfo>> GetAvailableToolsAsync()
{
if (_cachedTools != null)
{
return Task.FromResult(_cachedTools);
}
var tools = new List<ToolInfo>();
var toolTypes = new[] { typeof(Tools.SolutionTools), typeof(Tools.DocumentTools), typeof(Tools.BuildTools), typeof(Tools.NavigationTools), typeof(Tools.DebuggerTools), typeof(Tools.DiagnosticsTools) };
foreach (var toolType in toolTypes)
{
var category = toolType.Name.Replace("Tools", "");
foreach (var method in toolType.GetMethods(BindingFlags.Public | BindingFlags.Instance))
{
var toolAttr = method.GetCustomAttribute<McpServerToolAttribute>();
if (toolAttr == null)
{
continue;
}
var descAttr = method.GetCustomAttribute<DescriptionAttribute>();
tools.Add(new ToolInfo
{
Name = toolAttr.Name ?? method.Name,
Description = descAttr?.Description ?? string.Empty,
Category = category
});
}
}
_cachedTools = tools;
return Task.FromResult(tools);
}
public Task ShutdownAsync()
{
Console.Error.WriteLine("Shutdown requested via RPC");
_shutdownCts.Cancel();
return Task.CompletedTask;
}
public Task<SolutionInfo?> GetSolutionInfoAsync() => Proxy.GetSolutionInfoAsync();
public Task<bool> OpenSolutionAsync(string path) => Proxy.OpenSolutionAsync(path);
public Task CloseSolutionAsync(bool saveFirst) => Proxy.CloseSolutionAsync(saveFirst);
public Task<List<ProjectInfo>> GetProjectsAsync() => Proxy.GetProjectsAsync();
public Task<List<DocumentInfo>> GetOpenDocumentsAsync() => Proxy.GetOpenDocumentsAsync();
public Task<DocumentInfo?> GetActiveDocumentAsync() => Proxy.GetActiveDocumentAsync();
public Task<bool> OpenDocumentAsync(string path) => Proxy.OpenDocumentAsync(path);
public Task<bool> CloseDocumentAsync(string path, bool save) => Proxy.CloseDocumentAsync(path, save);
public Task<bool> SaveDocumentAsync(string path) => Proxy.SaveDocumentAsync(path);
public Task<string?> ReadDocumentAsync(string path) => Proxy.ReadDocumentAsync(path);
public Task<bool> WriteDocumentAsync(string path, string content) => Proxy.WriteDocumentAsync(path, content);
public Task<SelectionInfo?> GetSelectionAsync() => Proxy.GetSelectionAsync();
public Task<bool> SetSelectionAsync(string path, int startLine, int startColumn, int endLine, int endColumn)
=> Proxy.SetSelectionAsync(path, startLine, startColumn, endLine, endColumn);
public Task<bool> InsertTextAsync(string text) => Proxy.InsertTextAsync(text);
public Task<int> ReplaceTextAsync(string oldText, string newText) => Proxy.ReplaceTextAsync(oldText, newText);
public Task<bool> GoToLineAsync(int line) => Proxy.GoToLineAsync(line);
public Task<List<FindResult>> FindAsync(string searchText, bool matchCase, bool wholeWord)
=> Proxy.FindAsync(searchText, matchCase, wholeWord);
public Task<bool> BuildSolutionAsync() => Proxy.BuildSolutionAsync();
public Task<bool> BuildProjectAsync(string projectName) => Proxy.BuildProjectAsync(projectName);
public Task<bool> CleanSolutionAsync() => Proxy.CleanSolutionAsync();
public Task<bool> CancelBuildAsync() => Proxy.CancelBuildAsync();
public Task<BuildStatus> GetBuildStatusAsync() => Proxy.GetBuildStatusAsync();
public Task<List<SymbolInfo>> GetDocumentSymbolsAsync(string path) => Proxy.GetDocumentSymbolsAsync(path);
public Task<WorkspaceSymbolResult> SearchWorkspaceSymbolsAsync(string query, int maxResults = 100)
=> Proxy.SearchWorkspaceSymbolsAsync(query, maxResults);
public Task<DefinitionResult> GoToDefinitionAsync(string path, int line, int column)
=> Proxy.GoToDefinitionAsync(path, line, column);
public Task<ReferencesResult> FindReferencesAsync(string path, int line, int column, int maxResults = 100)
=> Proxy.FindReferencesAsync(path, line, column, maxResults);
public Task<DebuggerStatus> GetDebuggerStatusAsync() => Proxy.GetDebuggerStatusAsync();
public Task<bool> DebugLaunchAsync() => Proxy.DebugLaunchAsync();
public Task<bool> DebugLaunchWithoutDebuggingAsync() => Proxy.DebugLaunchWithoutDebuggingAsync();
public Task<bool> DebugContinueAsync() => Proxy.DebugContinueAsync();
public Task<bool> DebugBreakAsync() => Proxy.DebugBreakAsync();
public Task<bool> DebugStopAsync() => Proxy.DebugStopAsync();
public Task<bool> DebugStepOverAsync() => Proxy.DebugStepOverAsync();
public Task<bool> DebugStepIntoAsync() => Proxy.DebugStepIntoAsync();
public Task<bool> DebugStepOutAsync() => Proxy.DebugStepOutAsync();
public Task<bool> DebugAddBreakpointAsync(string file, int line) => Proxy.DebugAddBreakpointAsync(file, line);
public Task<bool> DebugRemoveBreakpointAsync(string file, int line) => Proxy.DebugRemoveBreakpointAsync(file, line);
public Task<List<BreakpointInfo>> DebugGetBreakpointsAsync() => Proxy.DebugGetBreakpointsAsync();
public Task<List<Shared.Models.LocalVariableInfo>> DebugGetLocalsAsync() => Proxy.DebugGetLocalsAsync();
public Task<List<CallStackFrameInfo>> DebugGetCallStackAsync() => Proxy.DebugGetCallStackAsync();
public Task<ErrorListResult> GetErrorListAsync(string? severity = null, int maxResults = 100)
=> Proxy.GetErrorListAsync(severity, maxResults);
public Task<OutputReadResult> ReadOutputPaneAsync(string paneIdentifier) => Proxy.ReadOutputPaneAsync(paneIdentifier);
public Task<bool> WriteOutputPaneAsync(string paneIdentifier, string message, bool activate = false)
=> Proxy.WriteOutputPaneAsync(paneIdentifier, message, activate);
public Task<List<OutputPaneInfo>> GetOutputPanesAsync() => Proxy.GetOutputPanesAsync();
}