diff --git a/mcp/MockToolingServer.csproj b/mcp/MockToolingServer.csproj
new file mode 100644
index 00000000..fcfe71b0
--- /dev/null
+++ b/mcp/MockToolingServer.csproj
@@ -0,0 +1,25 @@
+
+
+ Exe
+ net8.0
+ enable
+ enable
+ Microsoft.Agents.A365.DevTools.MockToolingServer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mcp/MockToolingServer.sln b/mcp/MockToolingServer.sln
new file mode 100644
index 00000000..c1aa9448
--- /dev/null
+++ b/mcp/MockToolingServer.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MockToolingServer", "MockToolingServer.csproj", "{67BA49BD-228B-43D5-84C4-2438B9AEB45D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {67BA49BD-228B-43D5-84C4-2438B9AEB45D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {67BA49BD-228B-43D5-84C4-2438B9AEB45D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {67BA49BD-228B-43D5-84C4-2438B9AEB45D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {67BA49BD-228B-43D5-84C4-2438B9AEB45D}.Debug|x64.Build.0 = Debug|Any CPU
+ {67BA49BD-228B-43D5-84C4-2438B9AEB45D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {67BA49BD-228B-43D5-84C4-2438B9AEB45D}.Debug|x86.Build.0 = Debug|Any CPU
+ {67BA49BD-228B-43D5-84C4-2438B9AEB45D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {67BA49BD-228B-43D5-84C4-2438B9AEB45D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {67BA49BD-228B-43D5-84C4-2438B9AEB45D}.Release|x64.ActiveCfg = Release|Any CPU
+ {67BA49BD-228B-43D5-84C4-2438B9AEB45D}.Release|x64.Build.0 = Release|Any CPU
+ {67BA49BD-228B-43D5-84C4-2438B9AEB45D}.Release|x86.ActiveCfg = Release|Any CPU
+ {67BA49BD-228B-43D5-84C4-2438B9AEB45D}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/mcp/MockTools/FileMockToolStore.cs b/mcp/MockTools/FileMockToolStore.cs
new file mode 100644
index 00000000..024598d9
--- /dev/null
+++ b/mcp/MockTools/FileMockToolStore.cs
@@ -0,0 +1,151 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.Agents.A365.DevTools.MockToolingServer.MockTools;
+
+public class FileMockToolStore : IMockToolStore, IDisposable
+{
+ private readonly string _filePath;
+ private readonly SemaphoreSlim _lock = new(1,1);
+ private readonly FileSystemWatcher? _watcher;
+ private readonly JsonSerializerOptions _jsonOptions = new()
+ {
+ WriteIndented = true,
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ ReadCommentHandling = JsonCommentHandling.Skip,
+ AllowTrailingCommas = true
+ };
+
+ private readonly ConcurrentDictionary _cache = new(StringComparer.OrdinalIgnoreCase);
+
+ public string McpServerName { get; }
+
+ // Modified: now requires mcpServerName to determine file name (.json)
+ public FileMockToolStore(string mcpServerName, MockToolStoreOptions options)
+ {
+ if (string.IsNullOrWhiteSpace(mcpServerName)) throw new ArgumentException("mcpServerName required", nameof(mcpServerName));
+
+ McpServerName = mcpServerName;
+
+ // Sanitize server name for file system
+ var invalid = Path.GetInvalidFileNameChars();
+ var safeName = new string(mcpServerName.Select(c => invalid.Contains(c) ? '_' : c).ToArray());
+
+ _filePath = options.FilePath ?? Path.Combine(AppContext.BaseDirectory, "mocks", safeName + ".json");
+ Directory.CreateDirectory(Path.GetDirectoryName(_filePath)!);
+ if (!File.Exists(_filePath))
+ {
+ File.WriteAllText(_filePath, "[]");
+ }
+ LoadInternal();
+
+ try
+ {
+ _watcher = new FileSystemWatcher(Path.GetDirectoryName(_filePath)!)
+ {
+ Filter = Path.GetFileName(_filePath),
+ EnableRaisingEvents = true,
+ NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.FileName
+ };
+ _watcher.Changed += async (_, __) => await SafeReload();
+ _watcher.Created += async (_, __) => await SafeReload();
+ _watcher.Renamed += async (_, __) => await SafeReload();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Failed to initialize FileSystemWatcher: {ex.Message}");
+ }
+ }
+
+ private async Task SafeReload()
+ {
+ try
+ {
+ await ReloadAsync();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Failed to reload mock tools: {ex.Message}");
+ }
+ }
+
+ private void LoadInternal()
+ {
+ var json = File.ReadAllText(_filePath);
+ var list = JsonSerializer.Deserialize>(json, _jsonOptions) ?? new();
+ _cache.Clear();
+ foreach(var t in list)
+ {
+ if (!string.IsNullOrWhiteSpace(t.Name))
+ {
+ _cache[t.Name] = t;
+ }
+ }
+ }
+
+ private async Task PersistAsync()
+ {
+ var list = _cache.Values.OrderBy(v => v.Name).ToList();
+ var json = JsonSerializer.Serialize(list, _jsonOptions);
+ await File.WriteAllTextAsync(_filePath, json);
+ }
+
+ public async Task> ListAsync()
+ {
+ await Task.CompletedTask;
+ return _cache.Values.OrderBy(v => v.Name).ToList();
+ }
+
+ public async Task GetAsync(string name)
+ {
+ await Task.CompletedTask;
+ _cache.TryGetValue(name, out var def);
+ return def;
+ }
+
+ public async Task UpsertAsync(MockToolDefinition def)
+ {
+ if (string.IsNullOrWhiteSpace(def.Name)) throw new ArgumentException("Tool name required");
+ await _lock.WaitAsync();
+ try
+ {
+ _cache[def.Name] = def;
+ await PersistAsync();
+ }
+ finally
+ {
+ _lock.Release();
+ }
+ }
+
+ public async Task DeleteAsync(string name)
+ {
+ await _lock.WaitAsync();
+ try
+ {
+ var removed = _cache.TryRemove(name, out _);
+ if (removed)
+ {
+ await PersistAsync();
+ }
+ return removed;
+ }
+ finally
+ {
+ _lock.Release();
+ }
+ }
+
+ public async Task ReloadAsync()
+ {
+ await _lock.WaitAsync();
+ try { LoadInternal(); }
+ finally { _lock.Release(); }
+ }
+
+ public void Dispose()
+ {
+ _watcher?.Dispose();
+ _lock.Dispose();
+ }
+}
diff --git a/mcp/MockTools/IMockToolStore.cs b/mcp/MockTools/IMockToolStore.cs
new file mode 100644
index 00000000..26bb3e12
--- /dev/null
+++ b/mcp/MockTools/IMockToolStore.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.Agents.A365.DevTools.MockToolingServer.MockTools;
+
+public interface IMockToolStore
+{
+ string McpServerName { get; }
+ Task> ListAsync();
+ Task GetAsync(string name);
+ Task UpsertAsync(MockToolDefinition def);
+ Task DeleteAsync(string name);
+ Task ReloadAsync();
+}
diff --git a/mcp/MockTools/MockToolExecutor.cs b/mcp/MockTools/MockToolExecutor.cs
new file mode 100644
index 00000000..0e061741
--- /dev/null
+++ b/mcp/MockTools/MockToolExecutor.cs
@@ -0,0 +1,148 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.Agents.A365.DevTools.MockToolingServer.MockTools;
+
+public interface IMockToolExecutor
+{
+ Task