Add files using upload-large-folder tool
Browse files- .devcontainer/Dockerfile +8 -0
- .devcontainer/README.md +147 -0
- .devcontainer/devcontainer.json +53 -0
- .devcontainer/postCreate.sh +14 -0
- .dockerignore +85 -0
- .github/COPILOT_ENVIRONMENT.md +106 -0
- .github/GITHUB_README.md +118 -0
- .github/MCP_CONFIGURATION.md +180 -0
- .github/SETUP_SUMMARY.md +195 -0
- .github/copilot-instructions.md +394 -0
- .github/dependabot.yml +58 -0
- .gitignore +64 -0
- .venv/.gitignore +1 -0
- .venv/.lock +0 -0
- .venv/CACHEDIR.TAG +1 -0
- .venv/pyvenv.cfg +6 -0
- .vscode/mcp.json +31 -0
- .vscode/settings.json +13 -0
- ASPIRE_INTEGRATION.md +295 -0
- DOCKER.md +169 -0
- DYNAMIC_COMPILATION_BEST_PRACTICES.md +469 -0
- Dockerfile +138 -0
- GETTING_STARTED.md +559 -0
- LICENSE +21 -0
- MCP_ARCHITECTURE.md +457 -0
- README.md +349 -0
- RoslynStone.sln +191 -0
- TESTING_IMPROVEMENTS.md +311 -0
- artifacts/resharper-report.xml +2801 -0
- build.cake +366 -0
- docker-compose.yml +58 -0
- scripts/check-python-quality.sh +34 -0
- scripts/format-python.sh +24 -0
- src/RoslynStone.Api/Dockerfile +138 -0
- src/RoslynStone.Api/Program.cs +337 -0
- src/RoslynStone.Api/RoslynStone.Api.csproj +38 -0
- src/RoslynStone.Api/entrypoint.sh +53 -0
- src/RoslynStone.AppHost/AppHost.cs +56 -0
- src/RoslynStone.AppHost/RoslynStone.AppHost.csproj +18 -0
- src/RoslynStone.AppHost/appsettings.Development.json +8 -0
- src/RoslynStone.AppHost/appsettings.json +9 -0
- src/RoslynStone.Core/RoslynStone.Core.csproj +29 -0
- src/RoslynStone.GradioModule/RoslynStone.GradioModule.csproj +23 -0
- src/RoslynStone.GradioModule/gradio_app.py +1279 -0
- src/RoslynStone.GradioModule/gradio_launcher.py +47 -0
- src/RoslynStone.GradioModule/pyproject.toml +126 -0
- src/RoslynStone.Infrastructure/RoslynStone.Infrastructure.csproj +37 -0
- src/RoslynStone.Infrastructure/Tools/DocumentationTools.cs +68 -0
- src/RoslynStone.ServiceDefaults/Extensions.cs +137 -0
- src/RoslynStone.ServiceDefaults/RoslynStone.ServiceDefaults.csproj +20 -0
.devcontainer/Dockerfile
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM mcr.microsoft.com/devcontainers/dotnet:latest AS base
|
| 2 |
+
|
| 3 |
+
# Important we change to the vscode user that the devcontainer runs under
|
| 4 |
+
USER vscode
|
| 5 |
+
WORKDIR /home/vscode
|
| 6 |
+
|
| 7 |
+
# Install UV
|
| 8 |
+
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
|
.devcontainer/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Devcontainer Configuration
|
| 2 |
+
|
| 3 |
+
This directory contains the VS Code devcontainer configuration for Roslyn-Stone development.
|
| 4 |
+
|
| 5 |
+
## Features
|
| 6 |
+
|
| 7 |
+
- **Base Image**: Official Microsoft .NET devcontainer with .NET SDK 10.0
|
| 8 |
+
- **Docker-in-Docker**: Docker feature enabled via devcontainer features
|
| 9 |
+
- **VS Code Extensions**: Pre-configured C# development tools
|
| 10 |
+
- **Non-root User**: Development as `vscode` user with Docker access
|
| 11 |
+
- **.NET Configuration**: Optimized environment variables for development
|
| 12 |
+
|
| 13 |
+
## Docker-in-Docker Support
|
| 14 |
+
|
| 15 |
+
The devcontainer includes the docker-in-docker feature which provides full Docker support, allowing you to:
|
| 16 |
+
- Build Docker images from within the container
|
| 17 |
+
- Run Docker containers for testing
|
| 18 |
+
- Test containerized deployments of Roslyn-Stone
|
| 19 |
+
- Develop and test Docker-based workflows
|
| 20 |
+
|
| 21 |
+
### Testing Docker-in-Docker
|
| 22 |
+
|
| 23 |
+
After opening the project in the devcontainer, verify Docker is working:
|
| 24 |
+
|
| 25 |
+
```bash
|
| 26 |
+
# Check Docker is available
|
| 27 |
+
docker --version
|
| 28 |
+
|
| 29 |
+
# Run a test container
|
| 30 |
+
docker run --rm hello-world
|
| 31 |
+
|
| 32 |
+
# Build and run a test image
|
| 33 |
+
echo 'FROM alpine:latest' > /tmp/Dockerfile.test
|
| 34 |
+
echo 'CMD ["echo", "Docker-in-Docker is working!"]' >> /tmp/Dockerfile.test
|
| 35 |
+
docker build -t dind-test -f /tmp/Dockerfile.test /tmp
|
| 36 |
+
docker run --rm dind-test
|
| 37 |
+
docker rmi dind-test
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
## Quick Start
|
| 41 |
+
|
| 42 |
+
1. Open this repository in VS Code
|
| 43 |
+
2. Install the "Dev Containers" extension if not already installed
|
| 44 |
+
3. Press `F1` and select "Dev Containers: Reopen in Container"
|
| 45 |
+
4. Wait for the container to build and the project to restore
|
| 46 |
+
5. Start developing!
|
| 47 |
+
|
| 48 |
+
## Configuration Details
|
| 49 |
+
|
| 50 |
+
### Dockerfile
|
| 51 |
+
|
| 52 |
+
The Dockerfile:
|
| 53 |
+
- Uses `mcr.microsoft.com/devcontainers/dotnet:1-10.0` as the base image (includes .NET 10.0 SDK)
|
| 54 |
+
- Base image already includes the `vscode` user and common development tools
|
| 55 |
+
- Docker will be installed by the docker-in-docker feature from `devcontainer.json`
|
| 56 |
+
- Minimal and clean - relies on official Microsoft devcontainer images
|
| 57 |
+
|
| 58 |
+
### devcontainer.json
|
| 59 |
+
|
| 60 |
+
The devcontainer configuration:
|
| 61 |
+
- Uses the docker-in-docker feature for full Docker support
|
| 62 |
+
- Runs the container as the `vscode` user
|
| 63 |
+
- Sets .NET environment variables to optimize development experience
|
| 64 |
+
- Configures VS Code extensions for C#, Docker, and GitHub integration
|
| 65 |
+
- Runs `dotnet restore && dotnet build` after container creation
|
| 66 |
+
|
| 67 |
+
## Environment Variables
|
| 68 |
+
|
| 69 |
+
The following .NET environment variables are configured:
|
| 70 |
+
- `DOTNET_CLI_TELEMETRY_OPTOUT=1` - Disables telemetry
|
| 71 |
+
- `DOTNET_GENERATE_ASPNET_CERTIFICATE=0` - Skips HTTPS dev certificate
|
| 72 |
+
- `DOTNET_NOLOGO=1` - Suppresses .NET logo output
|
| 73 |
+
- `DOTNET_USE_POLLING_FILE_WATCHER=1` - Uses polling for file watching (better for containers)
|
| 74 |
+
|
| 75 |
+
## Post-Create Command
|
| 76 |
+
|
| 77 |
+
The devcontainer automatically runs `dotnet restore && dotnet build` after creation to ensure the project is ready for development.
|
| 78 |
+
|
| 79 |
+
## Customization
|
| 80 |
+
|
| 81 |
+
### Adding VS Code Extensions
|
| 82 |
+
|
| 83 |
+
Edit the `extensions` array in `devcontainer.json`:
|
| 84 |
+
|
| 85 |
+
```json
|
| 86 |
+
"customizations": {
|
| 87 |
+
"vscode": {
|
| 88 |
+
"extensions": [
|
| 89 |
+
"your.extension.id"
|
| 90 |
+
]
|
| 91 |
+
}
|
| 92 |
+
}
|
| 93 |
+
```
|
| 94 |
+
|
| 95 |
+
### Adding System Packages
|
| 96 |
+
|
| 97 |
+
Edit the `Dockerfile` to install additional packages:
|
| 98 |
+
|
| 99 |
+
```dockerfile
|
| 100 |
+
RUN apt-get update && apt-get install -y \
|
| 101 |
+
your-package-name \
|
| 102 |
+
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
| 103 |
+
```
|
| 104 |
+
|
| 105 |
+
### Adding .NET Tools
|
| 106 |
+
|
| 107 |
+
Install tools in the post-create command or manually after container starts:
|
| 108 |
+
|
| 109 |
+
```bash
|
| 110 |
+
dotnet tool install -g your-tool-name
|
| 111 |
+
```
|
| 112 |
+
|
| 113 |
+
## Troubleshooting
|
| 114 |
+
|
| 115 |
+
### Docker-in-Docker Not Working
|
| 116 |
+
|
| 117 |
+
If Docker commands fail inside the container:
|
| 118 |
+
|
| 119 |
+
1. Ensure Docker is running on your host machine
|
| 120 |
+
2. Check that the docker-in-docker feature is enabled in `devcontainer.json`
|
| 121 |
+
3. Try rebuilding the container: "Dev Containers: Rebuild Container"
|
| 122 |
+
4. Check Docker daemon status: `sudo service docker status` or `docker info`
|
| 123 |
+
5. Verify the `vscode` user has Docker access: `groups` (should include `docker`)
|
| 124 |
+
|
| 125 |
+
### Build Failures
|
| 126 |
+
|
| 127 |
+
If the post-create command fails:
|
| 128 |
+
|
| 129 |
+
1. Check internet connectivity
|
| 130 |
+
2. Verify NuGet feeds are accessible
|
| 131 |
+
3. Manually run `dotnet restore` and check for errors
|
| 132 |
+
4. Review the devcontainer creation logs
|
| 133 |
+
|
| 134 |
+
### Permission Issues
|
| 135 |
+
|
| 136 |
+
If you encounter permission issues with Docker:
|
| 137 |
+
|
| 138 |
+
1. Verify the `vscode` user is in the `docker` group: `groups`
|
| 139 |
+
2. Rebuild the container to ensure the docker-in-docker feature is properly configured
|
| 140 |
+
3. Try restarting the Docker daemon if needed
|
| 141 |
+
|
| 142 |
+
## References
|
| 143 |
+
|
| 144 |
+
- [VS Code Dev Containers](https://code.visualstudio.com/docs/devcontainers/containers)
|
| 145 |
+
- [Docker-in-Docker Feature](https://github.com/devcontainers/features/tree/main/src/docker-in-docker)
|
| 146 |
+
- [.NET Dev Container Images](https://github.com/devcontainers/images/tree/main/src/dotnet)
|
| 147 |
+
- [Microsoft Devcontainers Documentation](https://containers.dev/)
|
.devcontainer/devcontainer.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "Roslyn-Stone Development Container",
|
| 3 |
+
"build": {
|
| 4 |
+
"dockerfile": "Dockerfile",
|
| 5 |
+
"target": "base",
|
| 6 |
+
"context": ".."
|
| 7 |
+
},
|
| 8 |
+
|
| 9 |
+
"features": {
|
| 10 |
+
"ghcr.io/devcontainers/features/docker-in-docker:2": {
|
| 11 |
+
"version": "latest",
|
| 12 |
+
"moby": true,
|
| 13 |
+
"dockerDashComposeVersion": "v2"
|
| 14 |
+
}
|
| 15 |
+
},
|
| 16 |
+
|
| 17 |
+
"containerUser": "vscode",
|
| 18 |
+
|
| 19 |
+
"customizations": {
|
| 20 |
+
"vscode": {
|
| 21 |
+
"extensions": [
|
| 22 |
+
"ms-dotnettools.csdevkit",
|
| 23 |
+
"ms-dotnettools.csharp",
|
| 24 |
+
"ms-dotnettools.vscode-dotnet-runtime",
|
| 25 |
+
"ms-dotnettools.vscodeintellicode-csharp",
|
| 26 |
+
"kreativ-software.csharpextensions",
|
| 27 |
+
"jchannon.csharpextensions",
|
| 28 |
+
"ms-azuretools.vscode-docker",
|
| 29 |
+
"ms-vscode-remote.remote-containers",
|
| 30 |
+
"eamodio.gitlens",
|
| 31 |
+
"github.vscode-github-actions",
|
| 32 |
+
"bierner.markdown-preview-github-styles",
|
| 33 |
+
"redhat.vscode-yaml",
|
| 34 |
+
"ms-python.autopep8",
|
| 35 |
+
"ms-python.python",
|
| 36 |
+
"ms-python.vscode-pylance"
|
| 37 |
+
],
|
| 38 |
+
"settings": {
|
| 39 |
+
"dotnet.defaultSolution": "RoslynStone.sln",
|
| 40 |
+
"omnisharp.enableEditorConfigSupport": true,
|
| 41 |
+
"omnisharp.enableRoslynAnalyzers": true
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
},
|
| 45 |
+
|
| 46 |
+
"containerEnv": {
|
| 47 |
+
"DOTNET_CLI_TELEMETRY_OPTOUT": "1",
|
| 48 |
+
"DOTNET_GENERATE_ASPNET_CERTIFICATE": "0",
|
| 49 |
+
"DOTNET_NOLOGO": "1",
|
| 50 |
+
"DOTNET_USE_POLLING_FILE_WATCHER": "1"
|
| 51 |
+
},
|
| 52 |
+
"postCreateCommand": "sh .devcontainer/postCreate.sh"
|
| 53 |
+
}
|
.devcontainer/postCreate.sh
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/sh
|
| 2 |
+
|
| 3 |
+
# Build the dotnet project to restore dependencies
|
| 4 |
+
dotnet restore && dotnet build
|
| 5 |
+
|
| 6 |
+
# setup environment using UV
|
| 7 |
+
uv venv
|
| 8 |
+
source .venv/bin/activate
|
| 9 |
+
echo source .venv/bin/activate >> $HOME/.bashrc
|
| 10 |
+
|
| 11 |
+
# Install huggingface_hub cli
|
| 12 |
+
uv pip install huggingface_hub
|
| 13 |
+
|
| 14 |
+
echo Still need to run \`huggingface-cli login\`
|
.dockerignore
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
### Git
|
| 2 |
+
.git
|
| 3 |
+
.gitignore
|
| 4 |
+
.gitattributes
|
| 5 |
+
|
| 6 |
+
### Build artifacts
|
| 7 |
+
[Dd]ebug/
|
| 8 |
+
[Dd]ebugPublic/
|
| 9 |
+
[Rr]elease/
|
| 10 |
+
[Rr]eleases/
|
| 11 |
+
x64/
|
| 12 |
+
x86/
|
| 13 |
+
[Ww][Ii][Nn]32/
|
| 14 |
+
[Aa][Rr][Mm]/
|
| 15 |
+
[Aa][Rr][Mm]64/
|
| 16 |
+
bld/
|
| 17 |
+
[Bb]in/
|
| 18 |
+
[Oo]bj/
|
| 19 |
+
[Ll]og/
|
| 20 |
+
[Ll]ogs/
|
| 21 |
+
artifacts/
|
| 22 |
+
|
| 23 |
+
### .NET Core
|
| 24 |
+
project.lock.json
|
| 25 |
+
project.fragment.lock.json
|
| 26 |
+
|
| 27 |
+
### NuGet packages
|
| 28 |
+
*.nupkg
|
| 29 |
+
*.snupkg
|
| 30 |
+
|
| 31 |
+
### Test Results
|
| 32 |
+
[Tt]est[Rr]esult*/
|
| 33 |
+
[Bb]uild[Ll]og.*
|
| 34 |
+
|
| 35 |
+
### IDE / Editor
|
| 36 |
+
.vs/
|
| 37 |
+
.vscode/
|
| 38 |
+
**/.classpath
|
| 39 |
+
**/.project
|
| 40 |
+
**/.settings
|
| 41 |
+
*.user
|
| 42 |
+
*.suo
|
| 43 |
+
*.userosscache
|
| 44 |
+
*.sln.docstates
|
| 45 |
+
**/*.*proj.user
|
| 46 |
+
|
| 47 |
+
### MSBuild and logs
|
| 48 |
+
*.binlog
|
| 49 |
+
*.dbmdl
|
| 50 |
+
*.jfm
|
| 51 |
+
|
| 52 |
+
### Misc
|
| 53 |
+
*.swp
|
| 54 |
+
*.*~
|
| 55 |
+
~$*
|
| 56 |
+
*~
|
| 57 |
+
LICENSE
|
| 58 |
+
|
| 59 |
+
### Node / JS
|
| 60 |
+
node_modules
|
| 61 |
+
npm-debug.log
|
| 62 |
+
|
| 63 |
+
### Docker/Compose
|
| 64 |
+
docker-compose*
|
| 65 |
+
Dockerfile*
|
| 66 |
+
**/bin/
|
| 67 |
+
**/obj/
|
| 68 |
+
|
| 69 |
+
### Kubernetes / Helm (charts)
|
| 70 |
+
charts/
|
| 71 |
+
|
| 72 |
+
### Config / Secrets for development
|
| 73 |
+
**/.env
|
| 74 |
+
**/azds.yaml
|
| 75 |
+
**/secrets.dev.yaml
|
| 76 |
+
**/values.dev.yaml
|
| 77 |
+
|
| 78 |
+
### Exclude CI/CD metadata
|
| 79 |
+
.github/
|
| 80 |
+
|
| 81 |
+
### Allow README.md to be included if needed
|
| 82 |
+
!README.md
|
| 83 |
+
|
| 84 |
+
# Python virtual environment
|
| 85 |
+
.venv/
|
.github/COPILOT_ENVIRONMENT.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Custom Copilot Environment
|
| 2 |
+
|
| 3 |
+
This repository uses a custom environment for GitHub Copilot coding agent to ensure all necessary development tools are available when working on C# code.
|
| 4 |
+
|
| 5 |
+
## Overview
|
| 6 |
+
|
| 7 |
+
The custom environment is configured through the `.github/workflows/copilot-setup-steps.yml` workflow, which automatically runs before Copilot starts working on the repository.
|
| 8 |
+
|
| 9 |
+
## Installed Tools
|
| 10 |
+
|
| 11 |
+
### Roslyn-Stone MCP Server (Dogfooding)
|
| 12 |
+
- **Purpose**: C# REPL and documentation tools for validating code changes
|
| 13 |
+
- **Usage**: Test C# expressions, validate syntax, look up .NET documentation
|
| 14 |
+
- **Tools Available**:
|
| 15 |
+
- `EvaluateCsharp` - Execute C# code in a stateful REPL
|
| 16 |
+
- `ValidateCsharp` - Validate C# syntax without execution
|
| 17 |
+
- `GetDocumentation` - Look up XML documentation for .NET types
|
| 18 |
+
- `GetReplInfo` - Get REPL environment information
|
| 19 |
+
- `LoadNuGetPackage` - Dynamically load NuGet packages
|
| 20 |
+
- `SearchNuGetPackages` - Search for NuGet packages
|
| 21 |
+
- **Recommendation**: Use these tools to validate C# code changes before committing
|
| 22 |
+
|
| 23 |
+
### .NET 10 SDK
|
| 24 |
+
- **Version**: 10.0.x (latest)
|
| 25 |
+
- **Purpose**: Provides the latest C# features and runtime
|
| 26 |
+
- **Usage**: All C# compilation and runtime operations
|
| 27 |
+
|
| 28 |
+
### CSharpier
|
| 29 |
+
- **Purpose**: Opinionated code formatter for C#
|
| 30 |
+
- **Usage**: Format C# code consistently across the project
|
| 31 |
+
- **Command**: `dotnet csharpier <file-or-directory>`
|
| 32 |
+
- **Documentation**: https://csharpier.com/
|
| 33 |
+
|
| 34 |
+
### ReSharper Command Line Tools
|
| 35 |
+
- **Purpose**: Code analysis, inspection, and refactoring
|
| 36 |
+
- **Usage**: Run code inspections and analysis
|
| 37 |
+
- **Command**: `jb <command>`
|
| 38 |
+
- **Documentation**: https://www.jetbrains.com/help/resharper/ReSharper_Command_Line_Tools.html
|
| 39 |
+
|
| 40 |
+
### Cake Build Tool
|
| 41 |
+
- **Purpose**: Cross-platform build automation
|
| 42 |
+
- **Usage**: Execute build scripts written in C#
|
| 43 |
+
- **Command**: `dotnet cake <script.cake>`
|
| 44 |
+
- **Documentation**: https://cakebuild.net/
|
| 45 |
+
|
| 46 |
+
### dotnet-outdated
|
| 47 |
+
- **Purpose**: Check for outdated NuGet package dependencies
|
| 48 |
+
- **Usage**: Identify packages that can be updated
|
| 49 |
+
- **Command**: `dotnet-outdated` or `dotnet-outdated -u` to upgrade
|
| 50 |
+
- **Documentation**: https://github.com/dotnet-outdated/dotnet-outdated
|
| 51 |
+
|
| 52 |
+
## How It Works
|
| 53 |
+
|
| 54 |
+
1. **Automatic Triggers**: The workflow runs automatically when:
|
| 55 |
+
- The workflow file itself is modified (push or pull request)
|
| 56 |
+
- Manually triggered via `workflow_dispatch`
|
| 57 |
+
|
| 58 |
+
2. **Setup Process**:
|
| 59 |
+
- Checkout the repository
|
| 60 |
+
- Install .NET 10 SDK
|
| 61 |
+
- Install all development tools globally
|
| 62 |
+
- Cache NuGet packages for faster subsequent runs
|
| 63 |
+
- Verify all tools are properly installed
|
| 64 |
+
|
| 65 |
+
3. **Copilot Integration**:
|
| 66 |
+
- Copilot uses this prepared environment when executing code or running builds
|
| 67 |
+
- All tools are available in the PATH for immediate use
|
| 68 |
+
- NuGet packages are cached to speed up dependency resolution
|
| 69 |
+
|
| 70 |
+
## Customization
|
| 71 |
+
|
| 72 |
+
To add more tools to the environment:
|
| 73 |
+
|
| 74 |
+
1. Edit `.github/workflows/copilot-setup-steps.yml`
|
| 75 |
+
2. Add installation steps in the `steps` section
|
| 76 |
+
3. Use `dotnet tool install --global <tool-name>` for .NET tools
|
| 77 |
+
4. Use standard `apt-get` or other package managers for system tools
|
| 78 |
+
5. Commit and push the changes - the workflow will run automatically
|
| 79 |
+
|
| 80 |
+
Example of adding a new .NET tool:
|
| 81 |
+
```yaml
|
| 82 |
+
- name: Install <ToolName>
|
| 83 |
+
run: dotnet tool install --global <package-name>
|
| 84 |
+
```
|
| 85 |
+
|
| 86 |
+
## Benefits
|
| 87 |
+
|
| 88 |
+
- **Consistency**: Same tools available every time Copilot works on the code
|
| 89 |
+
- **Performance**: Cached dependencies reduce setup time
|
| 90 |
+
- **Reliability**: Known-good versions of tools eliminate version mismatch issues
|
| 91 |
+
- **Productivity**: Copilot can use advanced tools without manual setup
|
| 92 |
+
|
| 93 |
+
## Troubleshooting
|
| 94 |
+
|
| 95 |
+
If tools are not available in Copilot sessions:
|
| 96 |
+
|
| 97 |
+
1. Check the workflow run logs in GitHub Actions
|
| 98 |
+
2. Verify the workflow completed successfully
|
| 99 |
+
3. Ensure the tool installation steps didn't fail
|
| 100 |
+
4. Check that the workflow is enabled in the repository settings
|
| 101 |
+
|
| 102 |
+
## References
|
| 103 |
+
|
| 104 |
+
- [GitHub Copilot Custom Environment Documentation](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/customize-the-agent-environment)
|
| 105 |
+
- [.NET SDK Documentation](https://learn.microsoft.com/en-us/dotnet/)
|
| 106 |
+
- [GitHub Actions Workflow Syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions)
|
.github/GITHUB_README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GitHub Configuration Directory
|
| 2 |
+
|
| 3 |
+
This directory contains configuration files for GitHub Copilot and other GitHub integrations.
|
| 4 |
+
|
| 5 |
+
## Structure
|
| 6 |
+
|
| 7 |
+
### `/workflows/copilot-setup-steps.yml`
|
| 8 |
+
Custom environment setup workflow for GitHub Copilot coding agent. This workflow:
|
| 9 |
+
- Installs .NET 10 SDK for latest C# features
|
| 10 |
+
- Installs CSharpier for code formatting
|
| 11 |
+
- Installs ReSharper Command Line Tools for code analysis
|
| 12 |
+
- Installs Cake for build automation
|
| 13 |
+
- Caches NuGet packages for faster subsequent runs
|
| 14 |
+
- Runs automatically when the workflow file is modified
|
| 15 |
+
|
| 16 |
+
See `COPILOT_ENVIRONMENT.md` for detailed documentation on the custom environment setup.
|
| 17 |
+
|
| 18 |
+
### `/COPILOT_ENVIRONMENT.md`
|
| 19 |
+
Detailed documentation about the custom Copilot environment:
|
| 20 |
+
- Overview of installed tools and their versions
|
| 21 |
+
- How the environment setup works
|
| 22 |
+
- Customization instructions
|
| 23 |
+
- Troubleshooting guide
|
| 24 |
+
- References and links to official documentation
|
| 25 |
+
|
| 26 |
+
This ensures Copilot has all necessary tools available when working on C# code.
|
| 27 |
+
|
| 28 |
+
### `/copilot-instructions.md`
|
| 29 |
+
Main instructions file for GitHub Copilot coding agent. This file provides:
|
| 30 |
+
- Project overview and architecture
|
| 31 |
+
- Development setup instructions
|
| 32 |
+
- Coding standards and conventions
|
| 33 |
+
- Security considerations
|
| 34 |
+
- Testing guidelines
|
| 35 |
+
- Pull request guidelines
|
| 36 |
+
|
| 37 |
+
This is the primary reference that Copilot will use when working on any part of the repository.
|
| 38 |
+
|
| 39 |
+
### `/instructions/`
|
| 40 |
+
Directory containing scope-specific instruction files that provide more granular guidance for particular areas of the codebase.
|
| 41 |
+
|
| 42 |
+
#### `csharp-mcp-server.instructions.md`
|
| 43 |
+
Instructions specific to building Model Context Protocol (MCP) servers using the C# SDK:
|
| 44 |
+
- Scoped to C# and .csproj files
|
| 45 |
+
- C# MCP SDK best practices (ModelContextProtocol NuGet packages)
|
| 46 |
+
- Tool and prompt implementation patterns
|
| 47 |
+
- Server setup with dependency injection
|
| 48 |
+
- Common code patterns and examples
|
| 49 |
+
|
| 50 |
+
#### `repl.instructions.md`
|
| 51 |
+
Instructions specific to C# REPL implementation:
|
| 52 |
+
- Scoped to C# files and REPL-related patterns
|
| 53 |
+
- Script execution patterns using Roslyn
|
| 54 |
+
- State management guidelines
|
| 55 |
+
- NuGet integration patterns
|
| 56 |
+
- Security and sandboxing requirements
|
| 57 |
+
|
| 58 |
+
#### `testing.instructions.md`
|
| 59 |
+
Instructions for writing and maintaining tests:
|
| 60 |
+
- Scoped to test files and directories
|
| 61 |
+
- xUnit testing patterns
|
| 62 |
+
- Mocking strategies
|
| 63 |
+
- Test data management
|
| 64 |
+
- Coverage requirements
|
| 65 |
+
|
| 66 |
+
### `/agents/`
|
| 67 |
+
Directory containing custom agent definitions for specialized tasks.
|
| 68 |
+
|
| 69 |
+
#### `CSharpExpert.agent.md`
|
| 70 |
+
Custom agent definition for C# and .NET expertise with functional programming focus. This agent should be delegated to for:
|
| 71 |
+
- Complex C# code changes
|
| 72 |
+
- Roslyn-specific implementations
|
| 73 |
+
- .NET framework integrations
|
| 74 |
+
- Performance-critical code paths
|
| 75 |
+
- Functional programming refactorings (LINQ, pure functions, etc.)
|
| 76 |
+
|
| 77 |
+
### `/chatmodes/`
|
| 78 |
+
Directory containing chat mode definitions for specialized conversational contexts.
|
| 79 |
+
|
| 80 |
+
#### `csharp-mcp-expert.chatmode.md`
|
| 81 |
+
Expert chat mode for C# MCP server development:
|
| 82 |
+
- World-class expertise in ModelContextProtocol SDK
|
| 83 |
+
- Deep knowledge of .NET architecture and async programming
|
| 84 |
+
- Best practices for tool design and LLM-friendly interfaces
|
| 85 |
+
- Provides complete, production-ready code examples
|
| 86 |
+
|
| 87 |
+
### `/prompts/`
|
| 88 |
+
Directory containing reusable prompt templates for common tasks.
|
| 89 |
+
|
| 90 |
+
#### `csharp-mcp-server-generator.prompt.md`
|
| 91 |
+
Prompt template for generating complete C# MCP server projects:
|
| 92 |
+
- Generates project structure with proper configuration
|
| 93 |
+
- Includes tools, prompts, and error handling
|
| 94 |
+
- Provides testing guidance and troubleshooting tips
|
| 95 |
+
- Production-ready with comprehensive documentation
|
| 96 |
+
|
| 97 |
+
## How It Works
|
| 98 |
+
|
| 99 |
+
1. **Global Instructions**: `copilot-instructions.md` provides repository-wide context and guidelines
|
| 100 |
+
2. **Scoped Instructions**: Files in `instructions/` directory provide additional context based on file patterns and languages
|
| 101 |
+
3. **Custom Agents**: Specialized agents in `agents/` directory handle domain-specific tasks
|
| 102 |
+
4. **Chat Modes**: Conversational modes in `chatmodes/` directory provide expert assistance for specific scenarios
|
| 103 |
+
5. **Prompts**: Reusable templates in `prompts/` directory for generating code or completing common tasks
|
| 104 |
+
|
| 105 |
+
When Copilot works on a file, it:
|
| 106 |
+
1. Always reads the global `copilot-instructions.md`
|
| 107 |
+
2. Reads any applicable scoped instructions based on file path and language
|
| 108 |
+
3. Can delegate to custom agents for specialized work
|
| 109 |
+
4. Can switch to expert chat modes for conversational assistance
|
| 110 |
+
5. Can use prompt templates to generate code or structures
|
| 111 |
+
|
| 112 |
+
## Best Practices
|
| 113 |
+
|
| 114 |
+
- Keep instructions focused and actionable
|
| 115 |
+
- Use YAML frontmatter in `.instructions.md` files to define scope
|
| 116 |
+
- Update instructions when patterns or standards change
|
| 117 |
+
- Reference custom agents in instructions when appropriate
|
| 118 |
+
- Keep instructions concise but comprehensive
|
.github/MCP_CONFIGURATION.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# MCP Server Configuration Examples
|
| 2 |
+
|
| 3 |
+
This document provides example configurations for integrating the Roslyn-Stone MCP server with various AI tools.
|
| 4 |
+
|
| 5 |
+
## Claude Desktop Configuration
|
| 6 |
+
|
| 7 |
+
To use Roslyn-Stone with Claude Desktop, add the following to your Claude Desktop configuration file:
|
| 8 |
+
|
| 9 |
+
### Windows
|
| 10 |
+
Location: `%APPDATA%\Claude\claude_desktop_config.json`
|
| 11 |
+
|
| 12 |
+
### macOS
|
| 13 |
+
Location: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
| 14 |
+
|
| 15 |
+
### Linux
|
| 16 |
+
Location: `~/.config/Claude/claude_desktop_config.json`
|
| 17 |
+
|
| 18 |
+
### Configuration
|
| 19 |
+
```json
|
| 20 |
+
{
|
| 21 |
+
"mcpServers": {
|
| 22 |
+
"roslyn-stone": {
|
| 23 |
+
"command": "dotnet",
|
| 24 |
+
"args": ["run", "--project", "/path/to/Roslyn-Stone/Roslyn-Stone.csproj"],
|
| 25 |
+
"env": {
|
| 26 |
+
"DOTNET_ENVIRONMENT": "Development"
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
## GitHub Copilot Agent Mode
|
| 34 |
+
|
| 35 |
+
Roslyn-Stone can be used with GitHub Copilot in agent mode. The custom agent configuration is already set up in `.github/agents/CSharpExpert.agent.md`.
|
| 36 |
+
|
| 37 |
+
To enable MCP tools for the agent:
|
| 38 |
+
1. Ensure the MCP server is running
|
| 39 |
+
2. Configure the agent to use stdio transport
|
| 40 |
+
3. Reference the server in agent configuration
|
| 41 |
+
|
| 42 |
+
## VS Code Configuration
|
| 43 |
+
|
| 44 |
+
For VS Code integration, you can configure the MCP server in your workspace settings:
|
| 45 |
+
|
| 46 |
+
### `.vscode/settings.json`
|
| 47 |
+
```json
|
| 48 |
+
{
|
| 49 |
+
"mcp.servers": {
|
| 50 |
+
"roslyn-stone": {
|
| 51 |
+
"command": "dotnet",
|
| 52 |
+
"args": ["run", "--project", "${workspaceFolder}/Roslyn-Stone.csproj"],
|
| 53 |
+
"transport": "stdio"
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
```
|
| 58 |
+
|
| 59 |
+
## Environment Variables
|
| 60 |
+
|
| 61 |
+
The following environment variables can be used to configure Roslyn-Stone:
|
| 62 |
+
|
| 63 |
+
- `ROSLYN_STONE_PORT`: Port for HTTP transport (default: stdio)
|
| 64 |
+
- `ROSLYN_STONE_LOG_LEVEL`: Logging level (Debug, Info, Warning, Error)
|
| 65 |
+
- `ROSLYN_STONE_CACHE_DIR`: Directory for caching compiled scripts
|
| 66 |
+
- `ROSLYN_STONE_MAX_MEMORY`: Maximum memory allocation in MB
|
| 67 |
+
- `ROSLYN_STONE_TIMEOUT`: Script execution timeout in seconds
|
| 68 |
+
|
| 69 |
+
## Transport Options
|
| 70 |
+
|
| 71 |
+
### Standard I/O (stdio)
|
| 72 |
+
Default transport for console applications:
|
| 73 |
+
```csharp
|
| 74 |
+
builder.Services
|
| 75 |
+
.AddMcpServer()
|
| 76 |
+
.WithStdioServerTransport();
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
### HTTP
|
| 80 |
+
For web-based integrations:
|
| 81 |
+
```csharp
|
| 82 |
+
builder.Services
|
| 83 |
+
.AddMcpServer()
|
| 84 |
+
.WithHttpServerTransport(options =>
|
| 85 |
+
{
|
| 86 |
+
options.Port = 8080;
|
| 87 |
+
});
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
## Security Configuration
|
| 91 |
+
|
| 92 |
+
For production deployments:
|
| 93 |
+
```json
|
| 94 |
+
{
|
| 95 |
+
"mcpServers": {
|
| 96 |
+
"roslyn-stone": {
|
| 97 |
+
"command": "dotnet",
|
| 98 |
+
"args": ["run", "--project", "/path/to/Roslyn-Stone/Roslyn-Stone.csproj"],
|
| 99 |
+
"env": {
|
| 100 |
+
"DOTNET_ENVIRONMENT": "Production",
|
| 101 |
+
"ROSLYN_STONE_SANDBOX": "true",
|
| 102 |
+
"ROSLYN_STONE_RESTRICT_IO": "true",
|
| 103 |
+
"ROSLYN_STONE_RESTRICT_NETWORK": "true"
|
| 104 |
+
}
|
| 105 |
+
}
|
| 106 |
+
}
|
| 107 |
+
}
|
| 108 |
+
```
|
| 109 |
+
|
| 110 |
+
## Tool Discovery
|
| 111 |
+
|
| 112 |
+
To verify the server is working and discover available tools:
|
| 113 |
+
|
| 114 |
+
### Using stdio transport
|
| 115 |
+
```bash
|
| 116 |
+
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | dotnet run --project Roslyn-Stone.csproj
|
| 117 |
+
```
|
| 118 |
+
|
| 119 |
+
### Using HTTP transport
|
| 120 |
+
```bash
|
| 121 |
+
curl -X POST http://localhost:8080/mcp \
|
| 122 |
+
-H "Content-Type: application/json" \
|
| 123 |
+
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
|
| 124 |
+
```
|
| 125 |
+
|
| 126 |
+
## Troubleshooting
|
| 127 |
+
|
| 128 |
+
### Server Not Starting
|
| 129 |
+
- Check that .NET 8.0 SDK or later is installed: `dotnet --version`
|
| 130 |
+
- Verify the project builds: `dotnet build`
|
| 131 |
+
- Check logs for error messages
|
| 132 |
+
|
| 133 |
+
### Tools Not Discovered
|
| 134 |
+
- Verify the MCP server is running
|
| 135 |
+
- Check transport configuration (stdio vs HTTP)
|
| 136 |
+
- Ensure tool methods have proper `[McpTool]` attributes
|
| 137 |
+
|
| 138 |
+
### Connection Issues
|
| 139 |
+
- For stdio: Ensure no buffering issues in your terminal
|
| 140 |
+
- For HTTP: Check firewall settings and port availability
|
| 141 |
+
- Verify JSON-RPC 2.0 message format
|
| 142 |
+
|
| 143 |
+
## Example Usage
|
| 144 |
+
|
| 145 |
+
### Execute C# Code
|
| 146 |
+
```json
|
| 147 |
+
{
|
| 148 |
+
"jsonrpc": "2.0",
|
| 149 |
+
"method": "tools/call",
|
| 150 |
+
"params": {
|
| 151 |
+
"name": "evaluate_csharp",
|
| 152 |
+
"arguments": {
|
| 153 |
+
"code": "var x = 42; return x * 2;"
|
| 154 |
+
}
|
| 155 |
+
},
|
| 156 |
+
"id": 1
|
| 157 |
+
}
|
| 158 |
+
```
|
| 159 |
+
|
| 160 |
+
### Add NuGet Package
|
| 161 |
+
```json
|
| 162 |
+
{
|
| 163 |
+
"jsonrpc": "2.0",
|
| 164 |
+
"method": "tools/call",
|
| 165 |
+
"params": {
|
| 166 |
+
"name": "add_package",
|
| 167 |
+
"arguments": {
|
| 168 |
+
"packageId": "Newtonsoft.Json",
|
| 169 |
+
"version": "13.0.3"
|
| 170 |
+
}
|
| 171 |
+
},
|
| 172 |
+
"id": 2
|
| 173 |
+
}
|
| 174 |
+
```
|
| 175 |
+
|
| 176 |
+
## Further Reading
|
| 177 |
+
|
| 178 |
+
- [Model Context Protocol Specification](https://modelcontextprotocol.io/)
|
| 179 |
+
- [MCP C# SDK Documentation](https://github.com/modelcontextprotocol/csharp-sdk)
|
| 180 |
+
- [Claude Desktop MCP Guide](https://docs.anthropic.com/claude/docs/model-context-protocol)
|
.github/SETUP_SUMMARY.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copilot Setup Summary
|
| 2 |
+
|
| 3 |
+
This document summarizes the GitHub Copilot configuration that has been set up for the Roslyn-Stone repository.
|
| 4 |
+
|
| 5 |
+
## What Was Configured
|
| 6 |
+
|
| 7 |
+
### 1. Main Copilot Instructions (`.github/copilot-instructions.md`)
|
| 8 |
+
The primary instructions file that Copilot will reference for all work in this repository. It includes:
|
| 9 |
+
- **Project Overview**: Description of Roslyn-Stone as a C# REPL/sandbox for MCP
|
| 10 |
+
- **Architecture**: Overview of key technologies (Roslyn, MCP, NuGet)
|
| 11 |
+
- **Development Setup**: How to build and test the project
|
| 12 |
+
- **Coding Standards**: C# conventions, error handling, code quality guidelines
|
| 13 |
+
- **Testing Guidelines**: Testing approach and requirements
|
| 14 |
+
- **MCP Integration**: Best practices for MCP-related code
|
| 15 |
+
- **Security Considerations**: Important security guidelines
|
| 16 |
+
- **LLM-Friendly Features**: How to make features accessible to AI agents
|
| 17 |
+
- **Pull Request Guidelines**: What to include in PRs
|
| 18 |
+
- **Custom Agents**: When to delegate to the CSharpExpert agent
|
| 19 |
+
|
| 20 |
+
### 2. Scoped Instruction Files (`.github/instructions/`)
|
| 21 |
+
Specialized instruction files that provide additional context for specific areas:
|
| 22 |
+
|
| 23 |
+
#### `csharp-mcp-server.instructions.md`
|
| 24 |
+
- **Scope**: C# and .csproj files (`**/*.cs`, `**/*.csproj`)
|
| 25 |
+
- **Content**: C# MCP SDK best practices, tool and prompt implementation patterns, server setup with dependency injection, common code examples
|
| 26 |
+
- **Use Case**: Automatically applies when working on C# MCP server code
|
| 27 |
+
|
| 28 |
+
#### `repl.instructions.md`
|
| 29 |
+
- **Scope**: C# files matching patterns: `*repl*`, `*REPL*`, `*eval*`, `*interactive*`
|
| 30 |
+
- **Content**: REPL implementation patterns, Roslyn scripting, state management, NuGet integration, sandboxing
|
| 31 |
+
- **Use Case**: Automatically applies when working on REPL functionality
|
| 32 |
+
|
| 33 |
+
#### `testing.instructions.md`
|
| 34 |
+
- **Scope**: Files in test directories and test files (`*Test.cs`, `*Tests.cs`, etc.)
|
| 35 |
+
- **Content**: xUnit patterns, mocking strategies, test categories, coverage requirements
|
| 36 |
+
- **Use Case**: Automatically applies when writing or modifying tests
|
| 37 |
+
|
| 38 |
+
### 3. MCP Configuration Documentation (`.github/MCP_CONFIGURATION.md`)
|
| 39 |
+
Comprehensive guide for integrating Roslyn-Stone with AI tools:
|
| 40 |
+
- Claude Desktop configuration examples
|
| 41 |
+
- VS Code integration setup
|
| 42 |
+
- GitHub Copilot agent mode configuration
|
| 43 |
+
- Environment variables and transport options
|
| 44 |
+
- Security configuration for production
|
| 45 |
+
- Troubleshooting guide
|
| 46 |
+
- Example usage with JSON-RPC calls
|
| 47 |
+
|
| 48 |
+
### 4. Custom Agent Configuration (`.github/agents/CSharpExpert.agent.md`)
|
| 49 |
+
Pre-existing custom agent for C# and .NET expertise:
|
| 50 |
+
- Already configured (192 lines)
|
| 51 |
+
- Should be delegated to for complex C# work
|
| 52 |
+
- Referenced in main instructions
|
| 53 |
+
|
| 54 |
+
### 5. Chat Mode Configuration (`.github/chatmodes/csharp-mcp-expert.chatmode.md`)
|
| 55 |
+
Expert chat mode for C# MCP server development:
|
| 56 |
+
- World-class expertise in ModelContextProtocol SDK
|
| 57 |
+
- Deep knowledge of .NET architecture and async programming
|
| 58 |
+
- Provides complete, production-ready code examples
|
| 59 |
+
- Best practices for tool design and LLM-friendly interfaces
|
| 60 |
+
|
| 61 |
+
### 6. Prompt Templates (`.github/prompts/csharp-mcp-server-generator.prompt.md`)
|
| 62 |
+
Reusable prompt for generating complete C# MCP server projects:
|
| 63 |
+
- Generates project structure with proper configuration
|
| 64 |
+
- Includes tools, prompts, and error handling
|
| 65 |
+
- Provides testing guidance and troubleshooting tips
|
| 66 |
+
- Production-ready with comprehensive documentation
|
| 67 |
+
|
| 68 |
+
### 7. Documentation (`.github/README.md`)
|
| 69 |
+
Structure and usage documentation for all the configuration files.
|
| 70 |
+
|
| 71 |
+
## How It Works
|
| 72 |
+
|
| 73 |
+
When Copilot works on your repository:
|
| 74 |
+
|
| 75 |
+
1. **Always reads**: `.github/copilot-instructions.md` for general context
|
| 76 |
+
2. **Conditionally reads**: Scoped instructions in `.github/instructions/` based on file patterns
|
| 77 |
+
3. **Can delegate to**: Custom agents in `.github/agents/` for specialized tasks
|
| 78 |
+
4. **Can use**: Chat modes in `.github/chatmodes/` for expert conversational assistance
|
| 79 |
+
5. **Can invoke**: Prompt templates in `.github/prompts/` for generating code structures
|
| 80 |
+
|
| 81 |
+
### Example Scenarios
|
| 82 |
+
|
| 83 |
+
**Working on MCP server code** (`src/McpServer.cs`):
|
| 84 |
+
- Reads: copilot-instructions.md + csharp-mcp-server.instructions.md
|
| 85 |
+
- Gets: General guidelines + C# MCP SDK patterns and best practices
|
| 86 |
+
|
| 87 |
+
**Working on REPL functionality** (`src/Repl/Evaluator.cs`):
|
| 88 |
+
- Reads: copilot-instructions.md + repl.instructions.md
|
| 89 |
+
- Gets: General guidelines + REPL-specific implementation patterns
|
| 90 |
+
|
| 91 |
+
**Writing tests** (`tests/ReplTests.cs`):
|
| 92 |
+
- Reads: copilot-instructions.md + testing.instructions.md
|
| 93 |
+
- Gets: General guidelines + Testing standards and patterns
|
| 94 |
+
|
| 95 |
+
**Complex C# refactoring**:
|
| 96 |
+
- Copilot delegates to CSharpExpert custom agent
|
| 97 |
+
- Agent has specialized C#/.NET knowledge and tools
|
| 98 |
+
|
| 99 |
+
## Benefits
|
| 100 |
+
|
| 101 |
+
### For Copilot Coding Agent
|
| 102 |
+
- **Better Context**: Understands project goals, architecture, and conventions
|
| 103 |
+
- **Consistent Output**: Follows established patterns and standards
|
| 104 |
+
- **Proper Error Handling**: Knows to provide actionable, LLM-friendly errors
|
| 105 |
+
- **Security Awareness**: Follows security guidelines automatically
|
| 106 |
+
- **Testing Standards**: Knows how to write tests that match project conventions
|
| 107 |
+
- **Specialization**: Can delegate complex C# work to expert agent
|
| 108 |
+
|
| 109 |
+
### For Developers
|
| 110 |
+
- **Higher Quality PRs**: Copilot produces code that matches project standards
|
| 111 |
+
- **Less Review Overhead**: Code follows conventions from the start
|
| 112 |
+
- **Better Documentation**: Copilot knows what to document and how
|
| 113 |
+
- **Consistent Style**: Uniform code across Copilot-generated and human-written code
|
| 114 |
+
- **Security by Default**: Security considerations baked into generated code
|
| 115 |
+
|
| 116 |
+
### For the Project
|
| 117 |
+
- **LLM-Ready**: Clear instructions help any AI assistant work effectively
|
| 118 |
+
- **Maintainability**: Well-documented standards and patterns
|
| 119 |
+
- **Onboarding**: New developers (human or AI) can understand the project quickly
|
| 120 |
+
- **Scalability**: As the project grows, instructions keep everyone aligned
|
| 121 |
+
|
| 122 |
+
## Files Created/Modified
|
| 123 |
+
|
| 124 |
+
```
|
| 125 |
+
.github/
|
| 126 |
+
├── README.md (UPDATED)
|
| 127 |
+
├── copilot-instructions.md (NEW - 3,464 bytes)
|
| 128 |
+
├── MCP_CONFIGURATION.md (NEW - 4,312 bytes)
|
| 129 |
+
├── SETUP_SUMMARY.md (UPDATED)
|
| 130 |
+
├── instructions/
|
| 131 |
+
│ ├── csharp-mcp-server.instructions.md (USER - 4,350 bytes)
|
| 132 |
+
│ ├── repl.instructions.md (NEW - 4,605 bytes)
|
| 133 |
+
│ └── testing.instructions.md (NEW - 7,509 bytes)
|
| 134 |
+
├── agents/
|
| 135 |
+
│ └── CSharpExpert.agent.md (EXISTING - 8,322 bytes)
|
| 136 |
+
├── chatmodes/
|
| 137 |
+
│ └── csharp-mcp-expert.chatmode.md (USER - 4,546 bytes)
|
| 138 |
+
└── prompts/
|
| 139 |
+
└── csharp-mcp-server-generator.prompt.md (USER - 2,218 bytes)
|
| 140 |
+
```
|
| 141 |
+
|
| 142 |
+
**Total**: 3 new files (created by Copilot), 3 user files (manually added), 2 updated documentation files, 1 existing agent
|
| 143 |
+
|
| 144 |
+
## Next Steps
|
| 145 |
+
|
| 146 |
+
### Immediate
|
| 147 |
+
- ✓ Configuration files are in place
|
| 148 |
+
- ✓ Scoped instructions have proper YAML frontmatter
|
| 149 |
+
- ✓ MCP configuration examples are documented
|
| 150 |
+
- ✓ Custom agent is referenced in main instructions
|
| 151 |
+
|
| 152 |
+
### Future Enhancements
|
| 153 |
+
As the project develops, consider:
|
| 154 |
+
1. Adding more scoped instructions for specific modules
|
| 155 |
+
2. Creating additional custom agents for specialized tasks (e.g., testing, documentation)
|
| 156 |
+
3. Updating instructions based on lessons learned
|
| 157 |
+
4. Adding examples of actual MCP tools once implemented
|
| 158 |
+
5. Creating CI/CD instructions if workflows are added
|
| 159 |
+
|
| 160 |
+
## Testing the Setup
|
| 161 |
+
|
| 162 |
+
To verify Copilot is using these instructions:
|
| 163 |
+
1. Create an issue with a coding task
|
| 164 |
+
2. Assign it to @copilot
|
| 165 |
+
3. Observe that Copilot references the instructions in its planning
|
| 166 |
+
4. Check that generated code follows the patterns defined
|
| 167 |
+
5. For C# work, verify delegation to CSharpExpert agent
|
| 168 |
+
|
| 169 |
+
## Maintenance
|
| 170 |
+
|
| 171 |
+
Update instructions when:
|
| 172 |
+
- Coding standards change
|
| 173 |
+
- New patterns or practices are adopted
|
| 174 |
+
- Security requirements evolve
|
| 175 |
+
- Testing strategies change
|
| 176 |
+
- New tools or frameworks are added
|
| 177 |
+
- Architecture evolves
|
| 178 |
+
|
| 179 |
+
Keep instructions:
|
| 180 |
+
- **Focused**: Each file should cover its scope clearly
|
| 181 |
+
- **Actionable**: Provide concrete examples and patterns
|
| 182 |
+
- **Current**: Update as the project evolves
|
| 183 |
+
- **Concise**: Be thorough but avoid verbosity
|
| 184 |
+
|
| 185 |
+
## Resources
|
| 186 |
+
|
| 187 |
+
- [GitHub Copilot Documentation](https://docs.github.com/en/copilot)
|
| 188 |
+
- [Adding Repository Custom Instructions](https://docs.github.com/en/copilot/how-tos/configure-custom-instructions/add-repository-instructions)
|
| 189 |
+
- [Model Context Protocol](https://modelcontextprotocol.io/)
|
| 190 |
+
- [MCP C# SDK](https://github.com/modelcontextprotocol/csharp-sdk)
|
| 191 |
+
|
| 192 |
+
---
|
| 193 |
+
|
| 194 |
+
**Setup completed on**: 2025-11-16
|
| 195 |
+
**Configured for**: GitHub Copilot coding agent with MCP integration
|
.github/copilot-instructions.md
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Roslyn-Stone: C# MCP Sandbox
|
| 2 |
+
|
| 3 |
+
Developer and LLM-friendly C# REPL with Model Context Protocol integration.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Project Overview
|
| 8 |
+
|
| 9 |
+
**Stack**: .NET 10.0, Roslyn, MCP, NuGet
|
| 10 |
+
**Purpose**: C# REPL for MCP with actionable errors, XML docs, and NuGet extensibility
|
| 11 |
+
**Optimization**: ReadyToRun (R2R) compilation for fast startup
|
| 12 |
+
|
| 13 |
+
---
|
| 14 |
+
|
| 15 |
+
## Quick Start
|
| 16 |
+
|
| 17 |
+
```bash
|
| 18 |
+
dotnet restore
|
| 19 |
+
dotnet build
|
| 20 |
+
dotnet test
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
## Communication Style
|
| 26 |
+
|
| 27 |
+
**Swear liberally for comedic effect:**
|
| 28 |
+
- Found a gnarly bug? "Well shit, that's a nasty one."
|
| 29 |
+
- Stuck on a problem? "Fuck, this is tricky."
|
| 30 |
+
- Solved something tough? "Holy shit, got it!"
|
| 31 |
+
- Code quality issues? "This code is a goddamn mess."
|
| 32 |
+
- Performance problems? "Christ, this is slow as hell."
|
| 33 |
+
|
| 34 |
+
Keep it natural and conversational. Swearing makes explanations more human and entertaining.
|
| 35 |
+
|
| 36 |
+
---
|
| 37 |
+
|
| 38 |
+
## Coding Standards
|
| 39 |
+
|
| 40 |
+
### C# Conventions
|
| 41 |
+
- PascalCase for public members, camelCase for private fields
|
| 42 |
+
- Nullable reference types where appropriate
|
| 43 |
+
- Async/await for I/O operations
|
| 44 |
+
- XML documentation for public APIs
|
| 45 |
+
|
| 46 |
+
### Functional Programming (Preferred)
|
| 47 |
+
|
| 48 |
+
**Use:**
|
| 49 |
+
- LINQ over imperative loops
|
| 50 |
+
- Pure functions for transformations
|
| 51 |
+
- Direct service calls from MCP Tools (`Tool → Service`)
|
| 52 |
+
- Expression-bodied members and pattern matching
|
| 53 |
+
- Records for immutable data
|
| 54 |
+
- Extension methods for functional helpers
|
| 55 |
+
|
| 56 |
+
**Avoid:**
|
| 57 |
+
- Heavy OOP abstractions
|
| 58 |
+
- CQRS patterns (no Commands/Queries/Handlers)
|
| 59 |
+
- Imperative loops when LINQ works
|
| 60 |
+
|
| 61 |
+
```csharp
|
| 62 |
+
// Good: Functional LINQ
|
| 63 |
+
var results = items
|
| 64 |
+
.Select(x => Transform(x))
|
| 65 |
+
.Where(x => x.IsValid)
|
| 66 |
+
.ToList();
|
| 67 |
+
|
| 68 |
+
// Avoid: Imperative loop
|
| 69 |
+
var results = new List<Result>();
|
| 70 |
+
foreach (var item in items) {
|
| 71 |
+
var transformed = Transform(item);
|
| 72 |
+
if (transformed.IsValid) results.Add(transformed);
|
| 73 |
+
}
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
### Error Handling
|
| 77 |
+
- Use specific exception types, not generic `catch (Exception)`
|
| 78 |
+
- Filter out `OperationCanceledException`: `catch (Exception ex) when (ex is not OperationCanceledException)`
|
| 79 |
+
- Provide actionable error messages LLMs can understand
|
| 80 |
+
- Include context: what was attempted, why it failed, how to fix
|
| 81 |
+
|
| 82 |
+
### Code Quality
|
| 83 |
+
- SOLID principles
|
| 84 |
+
- Focused, single-purpose methods
|
| 85 |
+
- Dependency injection where appropriate
|
| 86 |
+
- Always use `using` for disposables
|
| 87 |
+
|
| 88 |
+
---
|
| 89 |
+
|
| 90 |
+
## Code Quality Tools (REQUIRED)
|
| 91 |
+
|
| 92 |
+
**Both C# and Python code quality checks are enforced in CI pipeline.**
|
| 93 |
+
|
| 94 |
+
### C# Quality Checks
|
| 95 |
+
|
| 96 |
+
**Run before every C# commit:**
|
| 97 |
+
|
| 98 |
+
```bash
|
| 99 |
+
# 1. ReSharper inspection (fix ALL warnings/errors)
|
| 100 |
+
jb inspectcode RoslynStone.sln --output=/tmp/resharper-output.xml --verbosity=WARN
|
| 101 |
+
cat /tmp/resharper-output.xml | jq -r '.runs[0].results[] | select(.level == "warning" or .level == "error")'
|
| 102 |
+
|
| 103 |
+
# 2. CSharpier formatting
|
| 104 |
+
csharpier format .
|
| 105 |
+
|
| 106 |
+
# 3. Build and test
|
| 107 |
+
dotnet build
|
| 108 |
+
dotnet test
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
Zero ReSharper warnings or errors allowed in codebase.
|
| 112 |
+
|
| 113 |
+
### Python Quality Checks
|
| 114 |
+
|
| 115 |
+
**Run before every Python commit:**
|
| 116 |
+
|
| 117 |
+
```bash
|
| 118 |
+
# Quick check script (recommended)
|
| 119 |
+
./scripts/check-python-quality.sh
|
| 120 |
+
|
| 121 |
+
# Or manually:
|
| 122 |
+
cd src/RoslynStone.GradioModule
|
| 123 |
+
|
| 124 |
+
# 1. Ruff formatter check
|
| 125 |
+
ruff format --check .
|
| 126 |
+
|
| 127 |
+
# 2. Ruff linter
|
| 128 |
+
ruff check .
|
| 129 |
+
|
| 130 |
+
# 3. Mypy type checker
|
| 131 |
+
mypy .
|
| 132 |
+
```
|
| 133 |
+
|
| 134 |
+
**Auto-format and fix:**
|
| 135 |
+
|
| 136 |
+
```bash
|
| 137 |
+
./scripts/format-python.sh
|
| 138 |
+
|
| 139 |
+
# Or manually:
|
| 140 |
+
cd src/RoslynStone.GradioModule
|
| 141 |
+
ruff format .
|
| 142 |
+
ruff check --fix --unsafe-fixes .
|
| 143 |
+
```
|
| 144 |
+
|
| 145 |
+
**Python Tools:**
|
| 146 |
+
- **Ruff** - Fast linter and formatter (like ReSharper + CSharpier for Python)
|
| 147 |
+
- **mypy** - Static type checker (like Roslyn type checking)
|
| 148 |
+
- Configuration in `src/RoslynStone.GradioModule/pyproject.toml`
|
| 149 |
+
|
| 150 |
+
**Python Standards:**
|
| 151 |
+
- Google-style docstrings
|
| 152 |
+
- Type hints on public functions
|
| 153 |
+
- Line length: 100 characters
|
| 154 |
+
- Use modern Python syntax (PEP 585+)
|
| 155 |
+
- Lazy imports OK for optional dependencies
|
| 156 |
+
|
| 157 |
+
### Cake Build Targets
|
| 158 |
+
|
| 159 |
+
```bash
|
| 160 |
+
# Run full CI pipeline (C# + Python quality checks + tests)
|
| 161 |
+
dotnet cake --target=CI
|
| 162 |
+
|
| 163 |
+
# Individual targets
|
| 164 |
+
dotnet cake --target=Format-Check # Check C# formatting
|
| 165 |
+
dotnet cake --target=Python-Check # Check Python quality
|
| 166 |
+
dotnet cake --target=Format # Auto-format C#
|
| 167 |
+
dotnet cake --target=Python-Format # Auto-format Python
|
| 168 |
+
dotnet cake --target=Inspect # ReSharper inspection
|
| 169 |
+
```
|
| 170 |
+
|
| 171 |
+
**CI Pipeline enforces:**
|
| 172 |
+
- ✅ C# formatting (CSharpier)
|
| 173 |
+
- ✅ Python formatting (Ruff)
|
| 174 |
+
- ✅ Python linting (Ruff)
|
| 175 |
+
- ✅ Python type checking (mypy)
|
| 176 |
+
- ✅ ReSharper code inspection
|
| 177 |
+
- ✅ All tests with coverage
|
| 178 |
+
|
| 179 |
+
---
|
| 180 |
+
|
| 181 |
+
## Testing
|
| 182 |
+
|
| 183 |
+
### Coverage Targets
|
| 184 |
+
- Line coverage: >80% (current: 86.67%)
|
| 185 |
+
- Branch coverage: >75% (current: 62.98%)
|
| 186 |
+
|
| 187 |
+
### Running Tests
|
| 188 |
+
|
| 189 |
+
```bash
|
| 190 |
+
# All tests
|
| 191 |
+
dotnet test
|
| 192 |
+
|
| 193 |
+
# With coverage
|
| 194 |
+
dotnet cake --target=Test-Coverage
|
| 195 |
+
|
| 196 |
+
# HTML report
|
| 197 |
+
dotnet cake --target=Test-Coverage-Report
|
| 198 |
+
|
| 199 |
+
# Filtered
|
| 200 |
+
dotnet test --filter "Category=Unit"
|
| 201 |
+
dotnet test --filter "Component=REPL"
|
| 202 |
+
```
|
| 203 |
+
|
| 204 |
+
### Test Projects
|
| 205 |
+
1. **RoslynStone.Tests** - Unit/integration (xUnit, 102 tests)
|
| 206 |
+
2. **RoslynStone.Benchmarks** - Performance (BenchmarkDotNet)
|
| 207 |
+
3. **RoslynStone.LoadTests** - HTTP server load tests (300 concurrent requests)
|
| 208 |
+
|
| 209 |
+
### Writing Tests
|
| 210 |
+
|
| 211 |
+
```csharp
|
| 212 |
+
[Fact]
|
| 213 |
+
[Trait("Category", "Unit")]
|
| 214 |
+
[Trait("Component", "REPL")]
|
| 215 |
+
public async Task MethodName_Scenario_ExpectedBehavior()
|
| 216 |
+
{
|
| 217 |
+
// Arrange
|
| 218 |
+
var service = new Service();
|
| 219 |
+
|
| 220 |
+
// Act
|
| 221 |
+
var result = await service.MethodAsync();
|
| 222 |
+
|
| 223 |
+
// Assert
|
| 224 |
+
Assert.NotNull(result);
|
| 225 |
+
Assert.True(result.Success);
|
| 226 |
+
}
|
| 227 |
+
```
|
| 228 |
+
|
| 229 |
+
**Best Practices:**
|
| 230 |
+
- Test names: `MethodName_Scenario_ExpectedBehavior`
|
| 231 |
+
- One logical assertion per test
|
| 232 |
+
- Fast unit tests (<100ms)
|
| 233 |
+
- Independent tests (any order)
|
| 234 |
+
- Use `DefaultIfEmpty()` when averaging to prevent exceptions
|
| 235 |
+
- Avoid meaningless assertions like `Assert.True(true)`
|
| 236 |
+
|
| 237 |
+
---
|
| 238 |
+
|
| 239 |
+
## Dogfooding: Use Roslyn-Stone MCP Tools
|
| 240 |
+
|
| 241 |
+
**IMPORTANT**: Use roslyn-stone MCP tools when working on this repo.
|
| 242 |
+
|
| 243 |
+
### Available Tools
|
| 244 |
+
|
| 245 |
+
#### EvaluateCsharp
|
| 246 |
+
Execute C# in stateful REPL. Test expressions, validate code, verify refactorings.
|
| 247 |
+
|
| 248 |
+
```json
|
| 249 |
+
{ "code": "var x = 10; x * 2" }
|
| 250 |
+
// Returns: { success: true, returnValue: 20, ... }
|
| 251 |
+
```
|
| 252 |
+
|
| 253 |
+
#### ValidateCsharp
|
| 254 |
+
Check syntax without executing. Validate before adding to codebase.
|
| 255 |
+
|
| 256 |
+
```json
|
| 257 |
+
{ "code": "int x = 10; x * 2" }
|
| 258 |
+
// Returns: { isValid: true, issues: [] }
|
| 259 |
+
```
|
| 260 |
+
|
| 261 |
+
#### GetDocumentation
|
| 262 |
+
Look up .NET XML docs. Understand APIs, find signatures, get examples.
|
| 263 |
+
|
| 264 |
+
```json
|
| 265 |
+
{ "symbolName": "System.String" }
|
| 266 |
+
```
|
| 267 |
+
|
| 268 |
+
#### LoadNuGetPackage & SearchNuGetPackages
|
| 269 |
+
Test packages before adding dependencies.
|
| 270 |
+
|
| 271 |
+
### When to Use
|
| 272 |
+
|
| 273 |
+
**ALWAYS use these tools when:**
|
| 274 |
+
- Writing or modifying C# code
|
| 275 |
+
- Testing expressions or algorithms
|
| 276 |
+
- Looking up .NET API documentation
|
| 277 |
+
- Validating refactoring preserves behavior
|
| 278 |
+
- Checking complex expression syntax
|
| 279 |
+
- Experimenting with approaches
|
| 280 |
+
|
| 281 |
+
**Workflow:**
|
| 282 |
+
1. Write C# code change
|
| 283 |
+
2. ValidateCsharp for syntax
|
| 284 |
+
3. EvaluateCsharp to test logic
|
| 285 |
+
4. GetDocumentation to verify API usage
|
| 286 |
+
5. Add validated code to codebase
|
| 287 |
+
|
| 288 |
+
**Benefits:**
|
| 289 |
+
- Faster iteration without full builds
|
| 290 |
+
- Higher confidence before committing
|
| 291 |
+
- Better API understanding
|
| 292 |
+
- Catch errors early
|
| 293 |
+
- Dogfooding our own tools
|
| 294 |
+
|
| 295 |
+
---
|
| 296 |
+
|
| 297 |
+
## MCP Integration
|
| 298 |
+
|
| 299 |
+
- Follow `.github/instructions/csharp-mcp-server.instructions.md`
|
| 300 |
+
- Use ModelContextProtocol NuGet package (prerelease)
|
| 301 |
+
- Log to stderr (avoid interfering with stdio)
|
| 302 |
+
- Use attributes: `[McpServerToolType]`, `[McpServerTool]`, `[Description]`
|
| 303 |
+
- Handle errors with `McpProtocolException`
|
| 304 |
+
- Validate inputs, sanitize paths
|
| 305 |
+
- For expert help: `.github/chatmodes/csharp-mcp-expert.chatmode.md`
|
| 306 |
+
- Generate servers: `.github/prompts/csharp-mcp-server-generator.prompt.md`
|
| 307 |
+
|
| 308 |
+
---
|
| 309 |
+
|
| 310 |
+
## Security
|
| 311 |
+
|
| 312 |
+
- Never commit secrets
|
| 313 |
+
- Validate and sanitize all inputs
|
| 314 |
+
- Secure defaults for configuration
|
| 315 |
+
- Caution with dynamic compilation
|
| 316 |
+
- Proper access controls for file operations
|
| 317 |
+
|
| 318 |
+
---
|
| 319 |
+
|
| 320 |
+
## LLM-Friendly Design
|
| 321 |
+
|
| 322 |
+
- Clear, structured error messages
|
| 323 |
+
- Context and suggestions in responses
|
| 324 |
+
- Self-documenting APIs via XML comments
|
| 325 |
+
- Support discoverability (reflection, metadata)
|
| 326 |
+
- Return actionable feedback
|
| 327 |
+
|
| 328 |
+
---
|
| 329 |
+
|
| 330 |
+
## Documentation
|
| 331 |
+
|
| 332 |
+
- Keep README.md current
|
| 333 |
+
- XML comments for all APIs
|
| 334 |
+
- Include usage examples
|
| 335 |
+
- Document breaking changes
|
| 336 |
+
|
| 337 |
+
---
|
| 338 |
+
|
| 339 |
+
## Pull Requests
|
| 340 |
+
|
| 341 |
+
- Focused, atomic changes
|
| 342 |
+
- Tests for new functionality
|
| 343 |
+
- Updated documentation
|
| 344 |
+
- All tests passing
|
| 345 |
+
- Descriptive commit messages
|
| 346 |
+
|
| 347 |
+
---
|
| 348 |
+
|
| 349 |
+
## Custom Agents
|
| 350 |
+
|
| 351 |
+
Delegate to agents (`.github/agents/*.agent.md`) for complex tasks:
|
| 352 |
+
- Refactoring
|
| 353 |
+
- Bug fixing
|
| 354 |
+
- Feature implementation
|
| 355 |
+
- Performance optimization
|
| 356 |
+
- Code reviews
|
| 357 |
+
- Documentation updates
|
| 358 |
+
|
| 359 |
+
---
|
| 360 |
+
|
| 361 |
+
## Update This File When
|
| 362 |
+
|
| 363 |
+
### Code Quality & Standards
|
| 364 |
+
- New linting/formatting tools (ReSharper, CSharpier)
|
| 365 |
+
- New code style patterns or anti-patterns
|
| 366 |
+
- Build process changes
|
| 367 |
+
- Major framework upgrades
|
| 368 |
+
|
| 369 |
+
### Testing Infrastructure
|
| 370 |
+
- New test frameworks or patterns
|
| 371 |
+
- Coverage requirement changes
|
| 372 |
+
- New test categories or organization
|
| 373 |
+
- New test commands
|
| 374 |
+
|
| 375 |
+
### Architecture & Patterns
|
| 376 |
+
- New design patterns adopted project-wide
|
| 377 |
+
- Service organization changes
|
| 378 |
+
- Domain-specific guidelines
|
| 379 |
+
|
| 380 |
+
### Development Workflow
|
| 381 |
+
- New custom agents
|
| 382 |
+
- New MCP tools for dogfooding
|
| 383 |
+
- New scripts or automation
|
| 384 |
+
|
| 385 |
+
### Project Structure
|
| 386 |
+
- Directory reorganization
|
| 387 |
+
- New projects added
|
| 388 |
+
- Module boundary changes
|
| 389 |
+
|
| 390 |
+
**Update immediately after:**
|
| 391 |
+
- Adding testing infrastructure
|
| 392 |
+
- Introducing linting/formatting tools
|
| 393 |
+
- Establishing architectural patterns
|
| 394 |
+
- Adding custom agents or dev tools
|
.github/dependabot.yml
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
| 2 |
+
version: 2
|
| 3 |
+
updates:
|
| 4 |
+
- package-ecosystem: "nuget"
|
| 5 |
+
open-pull-requests-limit: 1
|
| 6 |
+
commit-message:
|
| 7 |
+
prefix: "C# - "
|
| 8 |
+
directory: "./src"
|
| 9 |
+
schedule:
|
| 10 |
+
interval: "monthly"
|
| 11 |
+
groups:
|
| 12 |
+
"C#":
|
| 13 |
+
patterns:
|
| 14 |
+
- "*"
|
| 15 |
+
- package-ecosystem: "nuget"
|
| 16 |
+
open-pull-requests-limit: 1
|
| 17 |
+
commit-message:
|
| 18 |
+
prefix: "C# Tests - "
|
| 19 |
+
directory: "./tests"
|
| 20 |
+
schedule:
|
| 21 |
+
interval: "monthly"
|
| 22 |
+
groups:
|
| 23 |
+
"C# Tests":
|
| 24 |
+
patterns:
|
| 25 |
+
- "*"
|
| 26 |
+
- package-ecosystem: "pip"
|
| 27 |
+
open-pull-requests-limit: 1
|
| 28 |
+
commit-message:
|
| 29 |
+
prefix: "Python - "
|
| 30 |
+
directory: "./src/RoslynStone.GradioModule"
|
| 31 |
+
schedule:
|
| 32 |
+
interval: "monthly"
|
| 33 |
+
groups:
|
| 34 |
+
"Python":
|
| 35 |
+
patterns:
|
| 36 |
+
- "*"
|
| 37 |
+
- package-ecosystem: "github-actions"
|
| 38 |
+
open-pull-requests-limit: 1
|
| 39 |
+
commit-message:
|
| 40 |
+
prefix: "GitHub Actions - "
|
| 41 |
+
directory: ".github/workflows/"
|
| 42 |
+
schedule:
|
| 43 |
+
interval: "monthly"
|
| 44 |
+
groups:
|
| 45 |
+
"GitHub Actions":
|
| 46 |
+
patterns:
|
| 47 |
+
- "*"
|
| 48 |
+
- package-ecosystem: "devcontainers"
|
| 49 |
+
open-pull-requests-limit: 1
|
| 50 |
+
commit-message:
|
| 51 |
+
prefix: "Dev Container - "
|
| 52 |
+
directory: "/"
|
| 53 |
+
schedule:
|
| 54 |
+
interval: "monthly"
|
| 55 |
+
groups:
|
| 56 |
+
"Dev Container":
|
| 57 |
+
patterns:
|
| 58 |
+
- "*"
|
.gitignore
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## A streamlined .gitignore for modern .NET projects
|
| 2 |
+
## including temporary files, build results, and
|
| 3 |
+
## files generated by popular .NET tools. If you are
|
| 4 |
+
## developing with Visual Studio, the VS .gitignore
|
| 5 |
+
## https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
| 6 |
+
## has more thorough IDE-specific entries.
|
| 7 |
+
##
|
| 8 |
+
## Get latest from https://github.com/github/gitignore/blob/main/Dotnet.gitignore
|
| 9 |
+
|
| 10 |
+
# Build results
|
| 11 |
+
[Dd]ebug/
|
| 12 |
+
[Dd]ebugPublic/
|
| 13 |
+
[Rr]elease/
|
| 14 |
+
[Rr]eleases/
|
| 15 |
+
x64/
|
| 16 |
+
x86/
|
| 17 |
+
[Ww][Ii][Nn]32/
|
| 18 |
+
[Aa][Rr][Mm]/
|
| 19 |
+
[Aa][Rr][Mm]64/
|
| 20 |
+
bld/
|
| 21 |
+
[Bb]in/
|
| 22 |
+
[Oo]bj/
|
| 23 |
+
[Ll]og/
|
| 24 |
+
[Ll]ogs/
|
| 25 |
+
|
| 26 |
+
# .NET Core
|
| 27 |
+
project.lock.json
|
| 28 |
+
project.fragment.lock.json
|
| 29 |
+
artifacts/
|
| 30 |
+
|
| 31 |
+
# ASP.NET Scaffolding
|
| 32 |
+
ScaffoldingReadMe.txt
|
| 33 |
+
|
| 34 |
+
# NuGet Packages
|
| 35 |
+
*.nupkg
|
| 36 |
+
# NuGet Symbol Packages
|
| 37 |
+
*.snupkg
|
| 38 |
+
|
| 39 |
+
# Others
|
| 40 |
+
~$*
|
| 41 |
+
*~
|
| 42 |
+
CodeCoverage/
|
| 43 |
+
|
| 44 |
+
# MSBuild Binary and Structured Log
|
| 45 |
+
*.binlog
|
| 46 |
+
|
| 47 |
+
# MSTest test Results
|
| 48 |
+
[Tt]est[Rr]esult*/
|
| 49 |
+
[Bb]uild[Ll]og.*
|
| 50 |
+
|
| 51 |
+
# NUnit
|
| 52 |
+
*.VisualState.xml
|
| 53 |
+
TestResult.xml
|
| 54 |
+
nunit-*.xml
|
| 55 |
+
inspectcode-results.xml
|
| 56 |
+
|
| 57 |
+
# Python virtual environments
|
| 58 |
+
.venv/
|
| 59 |
+
venv/
|
| 60 |
+
__pycache__/
|
| 61 |
+
*.pyc
|
| 62 |
+
*.pyo
|
| 63 |
+
*.pyd
|
| 64 |
+
.Python
|
.venv/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
*
|
.venv/.lock
ADDED
|
File without changes
|
.venv/CACHEDIR.TAG
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Signature: 8a477f597d28d172789f06886806bc55
|
.venv/pyvenv.cfg
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
home = /home/vscode/.local/share/uv/python/cpython-3.14.0-linux-x86_64-gnu/bin
|
| 2 |
+
implementation = CPython
|
| 3 |
+
uv = 0.9.10
|
| 4 |
+
version_info = 3.14.0
|
| 5 |
+
include-system-site-packages = false
|
| 6 |
+
prompt = Rosyln-Stone
|
.vscode/mcp.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"servers": {
|
| 3 |
+
// "roslyn-stone-docker": {
|
| 4 |
+
// "command": "docker",
|
| 5 |
+
// "type": "stdio",
|
| 6 |
+
// "args": [
|
| 7 |
+
// "run",
|
| 8 |
+
// "-i",
|
| 9 |
+
// "--rm",
|
| 10 |
+
// "-e", "DOTNET_USE_POLLING_FILE_WATCHER=1",
|
| 11 |
+
// "ghcr.io/dylanlangston/roslyn-stone:latest"
|
| 12 |
+
// ]
|
| 13 |
+
// },
|
| 14 |
+
"roslyn-stone-local": {
|
| 15 |
+
"command": "dotnet",
|
| 16 |
+
"type": "stdio",
|
| 17 |
+
"dev": {
|
| 18 |
+
"watch": "src/RoslynStone.Api//**/*.cs",
|
| 19 |
+
},
|
| 20 |
+
"args": [
|
| 21 |
+
"run",
|
| 22 |
+
"--project",
|
| 23 |
+
"src/RoslynStone.Api/RoslynStone.Api.csproj"
|
| 24 |
+
],
|
| 25 |
+
"env": {
|
| 26 |
+
"ASPNETCORE_ENVIRONMENT": "Development",
|
| 27 |
+
"DOTNET_USE_POLLING_FILE_WATCHER": "1"
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
}
|
.vscode/settings.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"github.copilot.chat.commitMessageGeneration.instructions": [
|
| 3 |
+
{
|
| 4 |
+
"text": "Use conventional commit format: type(scope): subject. Limit first line to 72 characters."
|
| 5 |
+
},
|
| 6 |
+
{
|
| 7 |
+
"text": "Swear liberally for comedic effect. Examples: 'fix(repl): holy shit, finally fixed the memory leak' or 'feat(mcp): fuck yeah, added package search' or 'perf(compiler): christ, made this 10x faster'"
|
| 8 |
+
},
|
| 9 |
+
{
|
| 10 |
+
"text": "Types: feat, fix, refactor, test, docs, chore, perf, style, ci, build. Use present tense imperatives."
|
| 11 |
+
}
|
| 12 |
+
]
|
| 13 |
+
}
|
ASPIRE_INTEGRATION.md
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Aspire and OpenTelemetry Integration Summary
|
| 2 |
+
|
| 3 |
+
## Overview
|
| 4 |
+
|
| 5 |
+
This document summarizes the changes made to containerize the Roslyn-Stone MCP server using .NET Aspire and OpenTelemetry integration.
|
| 6 |
+
|
| 7 |
+
## Changes Made
|
| 8 |
+
|
| 9 |
+
### 1. New Projects
|
| 10 |
+
|
| 11 |
+
#### RoslynStone.ServiceDefaults
|
| 12 |
+
- **Purpose**: Provides OpenTelemetry configuration and service defaults
|
| 13 |
+
- **Location**: `src/RoslynStone.ServiceDefaults/`
|
| 14 |
+
- **Key Components**:
|
| 15 |
+
- Extensions.cs: Configures OpenTelemetry logging, metrics, and tracing
|
| 16 |
+
- Automatic OTLP exporter configuration
|
| 17 |
+
- Health check endpoints
|
| 18 |
+
- Service discovery support
|
| 19 |
+
|
| 20 |
+
#### RoslynStone.AppHost
|
| 21 |
+
- **Purpose**: Aspire orchestration host for local development
|
| 22 |
+
- **Location**: `src/RoslynStone.AppHost/`
|
| 23 |
+
- **Key Components**:
|
| 24 |
+
- AppHost.cs: Configures MCP server as a containerized resource with HTTP transport
|
| 25 |
+
- Provides Aspire dashboard for telemetry visualization
|
| 26 |
+
- Manages service lifecycle and configuration
|
| 27 |
+
- **MCP Inspector Integration**: Uses CommunityToolkit.Aspire.Hosting.McpInspector package to automatically launch and configure the inspector in development mode
|
| 28 |
+
|
| 29 |
+
#### RoslynStone.AppHost.Tests
|
| 30 |
+
- **Purpose**: Integration tests for Aspire configuration
|
| 31 |
+
- **Location**: `tests/RoslynStone.AppHost.Tests/`
|
| 32 |
+
- **Key Components**:
|
| 33 |
+
- AppHostTests.cs: 7 tests verifying AppHost configuration
|
| 34 |
+
- Tests verify HTTP transport, environment variables, resource setup, and inspector configuration
|
| 35 |
+
- Fast-running tests that don't require Docker or DCP
|
| 36 |
+
|
| 37 |
+
### 2. Modified Projects
|
| 38 |
+
|
| 39 |
+
#### RoslynStone.Api
|
| 40 |
+
- **Changes**:
|
| 41 |
+
- Added reference to ServiceDefaults project
|
| 42 |
+
- Integrated `AddServiceDefaults()` in Program.cs
|
| 43 |
+
- Supports both stdio and HTTP transport modes via `MCP_TRANSPORT` environment variable
|
| 44 |
+
- HTTP transport mode provides REST endpoints for MCP protocol
|
| 45 |
+
- Logging configured to stderr to avoid protocol interference
|
| 46 |
+
|
| 47 |
+
### 3. Transport Configuration
|
| 48 |
+
|
| 49 |
+
#### HTTP Transport (Default for Aspire)
|
| 50 |
+
- **Port**: Configurable via `MCP_HTTP_PORT` environment variable (default: 8080)
|
| 51 |
+
- **Endpoints**: `/mcp` for MCP HTTP protocol, `/health` for health checks
|
| 52 |
+
- **Usage**: Designed for container deployment and testing with tools like MCP Inspector
|
| 53 |
+
- **Security**: No authentication by default - configure before public exposure
|
| 54 |
+
|
| 55 |
+
#### Stdio Transport
|
| 56 |
+
- **Removed from AppHost**: Stdio transport configuration removed from Aspire AppHost
|
| 57 |
+
- **Still Available**: Can still be used by running the API directly with `MCP_TRANSPORT=stdio`
|
| 58 |
+
- **Use Case**: Direct execution, Claude Desktop integration, local development
|
| 59 |
+
|
| 60 |
+
### 4. Containerization
|
| 61 |
+
|
| 62 |
+
#### Dockerfile
|
| 63 |
+
- **Location**: `src/RoslynStone.Api/Dockerfile`
|
| 64 |
+
- **Features**:
|
| 65 |
+
- Multi-stage build (build, publish, final)
|
| 66 |
+
- Optimized layer caching
|
| 67 |
+
- Based on .NET 10.0 SDK and runtime images
|
| 68 |
+
- Minimal runtime dependencies
|
| 69 |
+
- Configurable build configuration
|
| 70 |
+
|
| 71 |
+
#### .dockerignore
|
| 72 |
+
- **Purpose**: Optimize Docker build context
|
| 73 |
+
- **Excludes**: Build artifacts, tests, documentation, git files
|
| 74 |
+
|
| 75 |
+
#### docker-compose.yml
|
| 76 |
+
- **Purpose**: Local development with Aspire dashboard
|
| 77 |
+
- **Services**:
|
| 78 |
+
- roslyn-stone-mcp: The MCP server container
|
| 79 |
+
- aspire-dashboard: Telemetry visualization dashboard
|
| 80 |
+
- **Features**:
|
| 81 |
+
- Automatic network configuration
|
| 82 |
+
- OTLP endpoint configuration
|
| 83 |
+
- Dashboard accessible at http://localhost:18888
|
| 84 |
+
|
| 85 |
+
### 5. CI/CD
|
| 86 |
+
|
| 87 |
+
#### .github/workflows/container.yml
|
| 88 |
+
- **Purpose**: Build and publish container images to GHCR
|
| 89 |
+
- **Triggers**: Push to main, tags, pull requests
|
| 90 |
+
- **Features**:
|
| 91 |
+
- Multi-architecture builds (linux/amd64, linux/arm64)
|
| 92 |
+
- Build caching with GitHub Actions cache
|
| 93 |
+
- Automatic tagging strategy
|
| 94 |
+
- Artifact attestation
|
| 95 |
+
- Only pushes on non-PR events
|
| 96 |
+
|
| 97 |
+
### 6. Documentation
|
| 98 |
+
|
| 99 |
+
#### README.md
|
| 100 |
+
- **Updates**:
|
| 101 |
+
- Added Aspire and OpenTelemetry to features list
|
| 102 |
+
- Updated architecture diagram
|
| 103 |
+
- Added Docker and Aspire usage instructions
|
| 104 |
+
- New "Observability with OpenTelemetry" section
|
| 105 |
+
- Container registry information
|
| 106 |
+
- Updated technology stack
|
| 107 |
+
- Docker Desktop configuration examples
|
| 108 |
+
- Production deployment guidance
|
| 109 |
+
|
| 110 |
+
## OpenTelemetry Configuration
|
| 111 |
+
|
| 112 |
+
### Automatic Configuration
|
| 113 |
+
The ServiceDefaults project automatically configures:
|
| 114 |
+
- **Logging**: OpenTelemetry format with formatted messages and scopes
|
| 115 |
+
- **Metrics**: ASP.NET Core, HttpClient, and runtime instrumentation
|
| 116 |
+
- **Tracing**: Distributed tracing with activity sources
|
| 117 |
+
|
| 118 |
+
### OTLP Exporter
|
| 119 |
+
Configured via environment variables:
|
| 120 |
+
- `OTEL_EXPORTER_OTLP_ENDPOINT`: OTLP endpoint URL
|
| 121 |
+
- `OTEL_SERVICE_NAME`: Service identifier
|
| 122 |
+
- `OTEL_RESOURCE_ATTRIBUTES`: Additional resource metadata
|
| 123 |
+
|
| 124 |
+
### Aspire Dashboard
|
| 125 |
+
- Provides real-time visualization during development
|
| 126 |
+
- Logs, metrics, and traces in a unified interface
|
| 127 |
+
- No configuration required for local development
|
| 128 |
+
|
| 129 |
+
## Usage Scenarios
|
| 130 |
+
|
| 131 |
+
### Local Development (Direct)
|
| 132 |
+
```bash
|
| 133 |
+
# Stdio transport (for Claude Desktop, etc.)
|
| 134 |
+
cd src/RoslynStone.Api
|
| 135 |
+
dotnet run
|
| 136 |
+
|
| 137 |
+
# HTTP transport
|
| 138 |
+
cd src/RoslynStone.Api
|
| 139 |
+
MCP_TRANSPORT=http dotnet run
|
| 140 |
+
```
|
| 141 |
+
|
| 142 |
+
### Local Development (Aspire)
|
| 143 |
+
```bash
|
| 144 |
+
cd src/RoslynStone.AppHost
|
| 145 |
+
dotnet run
|
| 146 |
+
# Aspire dashboard available at http://localhost:18888
|
| 147 |
+
# MCP server HTTP endpoint available on configured port (default: 8080)
|
| 148 |
+
# MCP Inspector UI automatically starts at http://localhost:6274
|
| 149 |
+
```
|
| 150 |
+
|
| 151 |
+
### Using MCP Inspector
|
| 152 |
+
|
| 153 |
+
The MCP Inspector is automatically launched when running the AppHost in development mode using the CommunityToolkit.Aspire.Hosting.McpInspector package.
|
| 154 |
+
|
| 155 |
+
**Automatic Configuration:**
|
| 156 |
+
- Inspector UI: http://localhost:6274 (configurable via `INSPECTOR_UI_PORT`)
|
| 157 |
+
- Inspector Proxy: http://localhost:6277 (configurable via `INSPECTOR_PROXY_PORT`)
|
| 158 |
+
- Automatically connects to the MCP server's HTTP endpoint
|
| 159 |
+
|
| 160 |
+
**Usage:**
|
| 161 |
+
1. Start the Aspire AppHost: `cd src/RoslynStone.AppHost && dotnet run`
|
| 162 |
+
2. Open the Inspector UI at http://localhost:6274
|
| 163 |
+
3. The inspector is automatically connected to your MCP server
|
| 164 |
+
4. Test MCP tools through the visual interface
|
| 165 |
+
5. Export server configurations for Claude Desktop or other MCP clients
|
| 166 |
+
|
| 167 |
+
**Technical Details:**
|
| 168 |
+
- Uses `AddMcpInspector()` extension from CommunityToolkit package
|
| 169 |
+
- Automatically discovers and connects to MCP resources via `WithMcpServer()`
|
| 170 |
+
- Inspector endpoints are named "client" (UI) and "server-proxy" (proxy)
|
| 171 |
+
|
| 172 |
+
### Docker Compose
|
| 173 |
+
```bash
|
| 174 |
+
docker-compose up --build
|
| 175 |
+
# Dashboard available at http://localhost:18888
|
| 176 |
+
# MCP server available via HTTP transport
|
| 177 |
+
```
|
| 178 |
+
|
| 179 |
+
### Production Container
|
| 180 |
+
```bash
|
| 181 |
+
docker pull ghcr.io/dylanlangston/roslyn-stone:latest
|
| 182 |
+
docker run -i --rm \
|
| 183 |
+
-e OTEL_EXPORTER_OTLP_ENDPOINT=http://your-collector:4317 \
|
| 184 |
+
-e OTEL_SERVICE_NAME=roslyn-stone-mcp \
|
| 185 |
+
ghcr.io/dylanlangston/roslyn-stone:latest
|
| 186 |
+
```
|
| 187 |
+
|
| 188 |
+
## Security Considerations
|
| 189 |
+
|
| 190 |
+
1. **No HTTP Endpoints**: The MCP server uses stdio transport only
|
| 191 |
+
2. **Minimal Runtime Image**: Based on aspnet:10.0 runtime (smaller attack surface)
|
| 192 |
+
3. **No Secrets in Container**: All configuration via environment variables
|
| 193 |
+
4. **Diagnostics Disabled**: `DOTNET_EnableDiagnostics=0` in production
|
| 194 |
+
|
| 195 |
+
## Testing
|
| 196 |
+
|
| 197 |
+
### Unit and Integration Tests
|
| 198 |
+
All existing tests continue to pass:
|
| 199 |
+
- 101 tests passing
|
| 200 |
+
- 1 test skipped (slow network operations)
|
| 201 |
+
- 0 failures
|
| 202 |
+
- Build succeeds without warnings
|
| 203 |
+
|
| 204 |
+
### Aspire Integration Tests
|
| 205 |
+
The `RoslynStone.AppHost.Tests` project provides integration tests for the Aspire configuration:
|
| 206 |
+
- 7 tests verifying AppHost setup and configuration
|
| 207 |
+
- Tests verify HTTP transport configuration
|
| 208 |
+
- Tests verify environment variables
|
| 209 |
+
- Tests verify resource endpoints
|
| 210 |
+
- Tests verify MCP Inspector integration
|
| 211 |
+
- Fast-running tests that don't require Docker or DCP
|
| 212 |
+
|
| 213 |
+
```bash
|
| 214 |
+
# Run Aspire tests
|
| 215 |
+
dotnet test tests/RoslynStone.AppHost.Tests
|
| 216 |
+
|
| 217 |
+
# Run all tests
|
| 218 |
+
dotnet test
|
| 219 |
+
```
|
| 220 |
+
|
| 221 |
+
### MCP Inspector Integration
|
| 222 |
+
|
| 223 |
+
The MCP Inspector is automatically launched by the AppHost in development mode using the CommunityToolkit.Aspire.Hosting.McpInspector package:
|
| 224 |
+
|
| 225 |
+
**Features:**
|
| 226 |
+
- Automatically starts when running `dotnet run` from AppHost in development
|
| 227 |
+
- Uses managed Aspire resource instead of manual npx execution
|
| 228 |
+
- Automatically discovers and connects to MCP server resources
|
| 229 |
+
- Provides interactive web UI at `http://localhost:6274`
|
| 230 |
+
- Test MCP tools without writing code
|
| 231 |
+
- View real-time request/response data
|
| 232 |
+
- Export server configurations for Claude Desktop and other clients
|
| 233 |
+
|
| 234 |
+
**Ports:**
|
| 235 |
+
- `6274`: Inspector UI (web interface) - endpoint name: "client"
|
| 236 |
+
- `6277`: MCP Proxy (protocol bridge) - endpoint name: "server-proxy"
|
| 237 |
+
|
| 238 |
+
**Environment Detection:**
|
| 239 |
+
The inspector only starts when:
|
| 240 |
+
- `ASPNETCORE_ENVIRONMENT` or `DOTNET_ENVIRONMENT` is set to `Development`
|
| 241 |
+
- Or when no environment is specified (defaults to development)
|
| 242 |
+
|
| 243 |
+
**Benefits:**
|
| 244 |
+
- Seamless testing experience alongside Aspire dashboard
|
| 245 |
+
- No manual setup or configuration required
|
| 246 |
+
- Automatic connection to MCP server via `WithMcpServer()` extension
|
| 247 |
+
- Visual feedback on tool execution
|
| 248 |
+
- Easy configuration export
|
| 249 |
+
- Better integration with Aspire lifecycle management
|
| 250 |
+
|
| 251 |
+
## Compatibility
|
| 252 |
+
|
| 253 |
+
- **Backward Compatible**: All existing functionality preserved
|
| 254 |
+
- **Stdio Transport**: MCP protocol communication unchanged
|
| 255 |
+
- **Configuration**: Existing environment variables still work
|
| 256 |
+
- **Dependencies**: Aspire packages are additive, no breaking changes
|
| 257 |
+
|
| 258 |
+
## Deployment Options
|
| 259 |
+
|
| 260 |
+
### Container Registries
|
| 261 |
+
- GitHub Container Registry (GHCR): `ghcr.io/dylanlangston/roslyn-stone`
|
| 262 |
+
- Can be pushed to any OCI-compatible registry
|
| 263 |
+
|
| 264 |
+
### Orchestration
|
| 265 |
+
- Docker Compose
|
| 266 |
+
- Kubernetes
|
| 267 |
+
- Azure Container Apps
|
| 268 |
+
- AWS ECS/Fargate
|
| 269 |
+
- Google Cloud Run
|
| 270 |
+
|
| 271 |
+
### Monitoring Solutions
|
| 272 |
+
- Azure Monitor / Application Insights
|
| 273 |
+
- AWS CloudWatch
|
| 274 |
+
- Google Cloud Operations
|
| 275 |
+
- Grafana / Prometheus / Jaeger
|
| 276 |
+
- Any OTLP-compatible collector
|
| 277 |
+
|
| 278 |
+
## Future Enhancements
|
| 279 |
+
|
| 280 |
+
Potential improvements for future iterations:
|
| 281 |
+
1. Kubernetes manifests with Helm charts
|
| 282 |
+
2. Health check endpoints (currently only in development)
|
| 283 |
+
3. Prometheus metrics endpoint
|
| 284 |
+
4. Additional instrumentation for REPL operations
|
| 285 |
+
5. Custom metrics for code execution performance
|
| 286 |
+
6. Trace sampling configuration
|
| 287 |
+
7. Log aggregation configuration
|
| 288 |
+
|
| 289 |
+
## Support
|
| 290 |
+
|
| 291 |
+
For issues or questions:
|
| 292 |
+
- GitHub Issues: https://github.com/dylanlangston/Roslyn-Stone/issues
|
| 293 |
+
- Documentation: README.md
|
| 294 |
+
- Aspire Docs: https://learn.microsoft.com/en-us/dotnet/aspire/
|
| 295 |
+
- OpenTelemetry Docs: https://opentelemetry.io/docs/languages/net/
|
DOCKER.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Docker Deployment Guide
|
| 2 |
+
|
| 3 |
+
This document describes how to build and run the Roslyn-Stone MCP server in a Docker container with Gradio landing page support.
|
| 4 |
+
|
| 5 |
+
## Prerequisites
|
| 6 |
+
|
| 7 |
+
- Docker Engine 20.10+
|
| 8 |
+
- Internet connection for downloading Python redistributable and packages
|
| 9 |
+
|
| 10 |
+
## Building the Image
|
| 11 |
+
|
| 12 |
+
```bash
|
| 13 |
+
# From repository root
|
| 14 |
+
docker build -t roslyn-stone -f src/RoslynStone.Api/Dockerfile .
|
| 15 |
+
```
|
| 16 |
+
|
| 17 |
+
The build process:
|
| 18 |
+
1. **Build Stage**: Compiles .NET application and generates CSnakes bindings
|
| 19 |
+
2. **Publish Stage**:
|
| 20 |
+
- Installs CSnakes.Stage tool
|
| 21 |
+
- Downloads Python 3.12 redistributable via `setup-python`
|
| 22 |
+
- Installs UV (fast Python package manager)
|
| 23 |
+
- Installs Gradio 6.x into virtual environment
|
| 24 |
+
- Publishes .NET application
|
| 25 |
+
3. **Runtime Stage**:
|
| 26 |
+
- Copies published app, CSnakes Python, and venv
|
| 27 |
+
- Sets up environment variables for Python/UV
|
| 28 |
+
- Runs as non-root user (`appuser`)
|
| 29 |
+
|
| 30 |
+
## Running the Container
|
| 31 |
+
|
| 32 |
+
### Stdio Mode (Default)
|
| 33 |
+
|
| 34 |
+
```bash
|
| 35 |
+
docker run -i --rm roslyn-stone
|
| 36 |
+
```
|
| 37 |
+
|
| 38 |
+
Stdio mode doesn't expose any ports and communicates via stdin/stdout. Gradio landing page is **not activated** in this mode.
|
| 39 |
+
|
| 40 |
+
### HTTP Mode (with Gradio Landing Page)
|
| 41 |
+
|
| 42 |
+
```bash
|
| 43 |
+
docker run -d \
|
| 44 |
+
-e MCP_TRANSPORT=http \
|
| 45 |
+
-e ASPNETCORE_URLS=http://+:8080 \
|
| 46 |
+
-p 8080:8080 \
|
| 47 |
+
--name roslyn-stone \
|
| 48 |
+
roslyn-stone
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
Access:
|
| 52 |
+
- **Gradio Landing Page**: http://localhost:8080/
|
| 53 |
+
- **MCP Endpoint**: http://localhost:8080/mcp
|
| 54 |
+
- **Health Checks**: http://localhost:8080/health
|
| 55 |
+
|
| 56 |
+
## Environment Variables
|
| 57 |
+
|
| 58 |
+
### MCP Server Configuration
|
| 59 |
+
|
| 60 |
+
- `MCP_TRANSPORT`: `stdio` (default) or `http`
|
| 61 |
+
- `ASPNETCORE_URLS`: Listening URLs for HTTP mode (e.g., `http://+:8080`)
|
| 62 |
+
- `DOTNET_ENVIRONMENT`: `Production` (default), `Development`, `Staging`
|
| 63 |
+
|
| 64 |
+
### Python/CSnakes Configuration
|
| 65 |
+
|
| 66 |
+
These are set automatically in the Dockerfile:
|
| 67 |
+
|
| 68 |
+
- `LD_LIBRARY_PATH`: Path to libpython (`/home/appuser/.config/CSnakes/python3.12.9/python/install/lib`)
|
| 69 |
+
- `PATH`: Includes UV binary path (`/home/appuser/.local/bin`)
|
| 70 |
+
|
| 71 |
+
### Gradio Configuration
|
| 72 |
+
|
| 73 |
+
- `GRADIO_SERVER_PORT`: Port for Gradio (default: 7860, internal only)
|
| 74 |
+
|
| 75 |
+
## CSnakes and UV Integration
|
| 76 |
+
|
| 77 |
+
The Dockerfile uses:
|
| 78 |
+
|
| 79 |
+
1. **CSnakes.Stage** (`setup-python`):
|
| 80 |
+
- Downloads Python 3.12.9 redistributable
|
| 81 |
+
- Creates isolated Python environment
|
| 82 |
+
- No system Python dependencies required
|
| 83 |
+
|
| 84 |
+
2. **UV Package Manager**:
|
| 85 |
+
- Installs Python packages 10-100x faster than pip
|
| 86 |
+
- Resolves dependencies efficiently
|
| 87 |
+
- Caches packages for faster rebuilds
|
| 88 |
+
|
| 89 |
+
3. **Virtual Environment**:
|
| 90 |
+
- Isolated Python environment in `.venv`
|
| 91 |
+
- Pre-installed Gradio 6.x
|
| 92 |
+
- Copied to runtime image
|
| 93 |
+
|
| 94 |
+
## Docker Compose Example
|
| 95 |
+
|
| 96 |
+
```yaml
|
| 97 |
+
version: '3.8'
|
| 98 |
+
|
| 99 |
+
services:
|
| 100 |
+
roslyn-stone:
|
| 101 |
+
build:
|
| 102 |
+
context: .
|
| 103 |
+
dockerfile: src/RoslynStone.Api/Dockerfile
|
| 104 |
+
environment:
|
| 105 |
+
- MCP_TRANSPORT=http
|
| 106 |
+
- ASPNETCORE_URLS=http://+:8080
|
| 107 |
+
ports:
|
| 108 |
+
- "8080:8080"
|
| 109 |
+
restart: unless-stopped
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
## Troubleshooting
|
| 113 |
+
|
| 114 |
+
### Gradio Not Starting
|
| 115 |
+
|
| 116 |
+
If Gradio doesn't start in HTTP mode:
|
| 117 |
+
|
| 118 |
+
1. Check logs: `docker logs roslyn-stone`
|
| 119 |
+
2. Verify environment: `MCP_TRANSPORT=http` and `ASPNETCORE_URLS` are set
|
| 120 |
+
3. Check Python environment: `docker exec roslyn-stone ls -la .venv/`
|
| 121 |
+
|
| 122 |
+
### Python Import Errors
|
| 123 |
+
|
| 124 |
+
If you see Python-related errors:
|
| 125 |
+
|
| 126 |
+
1. Verify CSnakes Python is present: `docker exec roslyn-stone ls -la /home/appuser/.config/CSnakes/`
|
| 127 |
+
2. Check LD_LIBRARY_PATH: `docker exec roslyn-stone printenv LD_LIBRARY_PATH`
|
| 128 |
+
|
| 129 |
+
### Port Already in Use
|
| 130 |
+
|
| 131 |
+
```bash
|
| 132 |
+
# Find process using port
|
| 133 |
+
docker ps | grep 8080
|
| 134 |
+
|
| 135 |
+
# Stop existing container
|
| 136 |
+
docker stop roslyn-stone
|
| 137 |
+
docker rm roslyn-stone
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
## Multi-Platform Builds
|
| 141 |
+
|
| 142 |
+
To build for multiple platforms:
|
| 143 |
+
|
| 144 |
+
```bash
|
| 145 |
+
docker buildx build --platform linux/amd64,linux/arm64 \
|
| 146 |
+
-t roslyn-stone:latest \
|
| 147 |
+
-f src/RoslynStone.Api/Dockerfile .
|
| 148 |
+
```
|
| 149 |
+
|
| 150 |
+
## Security Considerations
|
| 151 |
+
|
| 152 |
+
- Container runs as non-root user (`appuser`)
|
| 153 |
+
- No elevated privileges required
|
| 154 |
+
- Python environment is isolated
|
| 155 |
+
- Only necessary ports are exposed
|
| 156 |
+
|
| 157 |
+
## Size Optimization
|
| 158 |
+
|
| 159 |
+
The image includes:
|
| 160 |
+
- .NET 10 runtime (~200 MB)
|
| 161 |
+
- CSnakes Python redistributable (~50 MB)
|
| 162 |
+
- Gradio and dependencies (~150 MB)
|
| 163 |
+
|
| 164 |
+
Total image size: ~400-500 MB
|
| 165 |
+
|
| 166 |
+
To reduce size:
|
| 167 |
+
- Use multi-stage builds (already implemented)
|
| 168 |
+
- Remove development tools from runtime image
|
| 169 |
+
- Use Alpine-based images (requires additional setup for CSnakes)
|
DYNAMIC_COMPILATION_BEST_PRACTICES.md
ADDED
|
@@ -0,0 +1,469 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dynamic Compilation Best Practices
|
| 2 |
+
|
| 3 |
+
This document explains the best practices for dynamically compiling and executing C# code at runtime, based on Laurent Kempé's article "Dynamically compile and run code using .NET Core 3.0" ([article link](https://laurentkempe.com/2019/02/18/dynamically-compile-and-run-code-using-dotNET-Core-3.0/)).
|
| 4 |
+
|
| 5 |
+
## Overview
|
| 6 |
+
|
| 7 |
+
Dynamic compilation enables scenarios such as:
|
| 8 |
+
- Plugin architectures
|
| 9 |
+
- REPL (Read-Eval-Print Loop) implementations
|
| 10 |
+
- Code evaluation services
|
| 11 |
+
- Hot-reloading of code without restarting the application
|
| 12 |
+
- Runtime code generation and execution
|
| 13 |
+
|
| 14 |
+
## Key Concepts
|
| 15 |
+
|
| 16 |
+
### 1. AssemblyLoadContext (Critical for .NET Core 3.0+)
|
| 17 |
+
|
| 18 |
+
**What it is**: A mechanism introduced in .NET Core that provides control over assembly loading and enables assembly unloading.
|
| 19 |
+
|
| 20 |
+
**Why it matters**:
|
| 21 |
+
- **Memory Management**: Without proper unloading, dynamically loaded assemblies stay in memory forever
|
| 22 |
+
- **Isolation**: Each context provides isolation between different versions of assemblies
|
| 23 |
+
- **Hot Reload**: Enables recompilation and reloading of code at runtime
|
| 24 |
+
- **Resource Cleanup**: Properly releases memory when assemblies are no longer needed
|
| 25 |
+
|
| 26 |
+
**Implementation**:
|
| 27 |
+
```csharp
|
| 28 |
+
public class UnloadableAssemblyLoadContext : AssemblyLoadContext
|
| 29 |
+
{
|
| 30 |
+
public UnloadableAssemblyLoadContext()
|
| 31 |
+
: base(isCollectible: true) // CRITICAL: isCollectible must be true
|
| 32 |
+
{ }
|
| 33 |
+
|
| 34 |
+
protected override Assembly? Load(AssemblyName assemblyName)
|
| 35 |
+
{
|
| 36 |
+
// Return null to use default loading behavior
|
| 37 |
+
// This delegates to the default context for framework assemblies
|
| 38 |
+
return null;
|
| 39 |
+
}
|
| 40 |
+
}
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
**Key Points**:
|
| 44 |
+
- **`isCollectible: true`**: This is the critical parameter that enables assembly unloading
|
| 45 |
+
- Must be used for any dynamically loaded assemblies that should be unloadable
|
| 46 |
+
- Assemblies loaded in collectible contexts can be garbage collected after `Unload()` is called
|
| 47 |
+
|
| 48 |
+
### 2. WeakReference for Tracking Unloading
|
| 49 |
+
|
| 50 |
+
**Purpose**: Verify that assemblies are actually unloaded and garbage collected.
|
| 51 |
+
|
| 52 |
+
**Implementation**:
|
| 53 |
+
```csharp
|
| 54 |
+
var context = new UnloadableAssemblyLoadContext();
|
| 55 |
+
WeakReference contextWeakRef = new(context, trackResurrection: true);
|
| 56 |
+
|
| 57 |
+
try
|
| 58 |
+
{
|
| 59 |
+
// Load and execute assembly
|
| 60 |
+
var assembly = context.LoadFromStream(assemblyStream);
|
| 61 |
+
// ... execute code ...
|
| 62 |
+
}
|
| 63 |
+
finally
|
| 64 |
+
{
|
| 65 |
+
// Unload the context
|
| 66 |
+
context.Unload();
|
| 67 |
+
|
| 68 |
+
// Verify unloading by forcing garbage collection
|
| 69 |
+
for (int i = 0; i < 10 && contextWeakRef.IsAlive; i++)
|
| 70 |
+
{
|
| 71 |
+
GC.Collect();
|
| 72 |
+
GC.WaitForPendingFinalizers();
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
// If contextWeakRef.IsAlive is still true, something is holding a reference
|
| 76 |
+
}
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
**Key Points**:
|
| 80 |
+
- **`trackResurrection: true`**: Tracks the object even if it has a finalizer
|
| 81 |
+
- After `Unload()`, the weak reference should become dead after garbage collection
|
| 82 |
+
- If the weak reference stays alive, it indicates a memory leak (something is holding a reference)
|
| 83 |
+
|
| 84 |
+
### 3. Roslyn Compilation API
|
| 85 |
+
|
| 86 |
+
**Two Approaches**:
|
| 87 |
+
|
| 88 |
+
#### A. Roslyn Scripting API (Simpler, for REPL)
|
| 89 |
+
```csharp
|
| 90 |
+
using Microsoft.CodeAnalysis.CSharp.Scripting;
|
| 91 |
+
using Microsoft.CodeAnalysis.Scripting;
|
| 92 |
+
|
| 93 |
+
var options = ScriptOptions.Default
|
| 94 |
+
.WithReferences(typeof(object).Assembly)
|
| 95 |
+
.WithImports("System", "System.Linq");
|
| 96 |
+
|
| 97 |
+
var result = await CSharpScript.RunAsync("1 + 1", options);
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
**Pros**:
|
| 101 |
+
- Very simple API
|
| 102 |
+
- Built-in state management between executions
|
| 103 |
+
- Good for REPL scenarios
|
| 104 |
+
|
| 105 |
+
**Cons**:
|
| 106 |
+
- Less control over compilation
|
| 107 |
+
- Cannot easily unload assemblies
|
| 108 |
+
- Not suitable when assembly isolation is needed
|
| 109 |
+
|
| 110 |
+
#### B. CSharpCompilation API (More Control)
|
| 111 |
+
```csharp
|
| 112 |
+
using Microsoft.CodeAnalysis;
|
| 113 |
+
using Microsoft.CodeAnalysis.CSharp;
|
| 114 |
+
|
| 115 |
+
var syntaxTree = CSharpSyntaxTree.ParseText(code);
|
| 116 |
+
|
| 117 |
+
var references = new[]
|
| 118 |
+
{
|
| 119 |
+
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
|
| 120 |
+
MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
|
| 121 |
+
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location)
|
| 122 |
+
};
|
| 123 |
+
|
| 124 |
+
var compilation = CSharpCompilation.Create(
|
| 125 |
+
"DynamicAssembly",
|
| 126 |
+
syntaxTrees: new[] { syntaxTree },
|
| 127 |
+
references: references,
|
| 128 |
+
options: new CSharpCompilationOptions(
|
| 129 |
+
OutputKind.DynamicallyLinkedLibrary,
|
| 130 |
+
optimizationLevel: OptimizationLevel.Release
|
| 131 |
+
)
|
| 132 |
+
);
|
| 133 |
+
|
| 134 |
+
using var ms = new MemoryStream();
|
| 135 |
+
var emitResult = compilation.Emit(ms);
|
| 136 |
+
|
| 137 |
+
if (emitResult.Success)
|
| 138 |
+
{
|
| 139 |
+
ms.Seek(0, SeekOrigin.Begin);
|
| 140 |
+
var assembly = context.LoadFromStream(ms);
|
| 141 |
+
}
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
**Pros**:
|
| 145 |
+
- Full control over compilation process
|
| 146 |
+
- Can emit to memory streams for loading in custom contexts
|
| 147 |
+
- Better error diagnostics
|
| 148 |
+
- Suitable for production scenarios
|
| 149 |
+
|
| 150 |
+
**Cons**:
|
| 151 |
+
- More verbose
|
| 152 |
+
- Requires manual reference management
|
| 153 |
+
|
| 154 |
+
### 4. Reference Management
|
| 155 |
+
|
| 156 |
+
**Critical**: All assemblies and types used in the dynamic code must have their metadata references added to the compilation.
|
| 157 |
+
|
| 158 |
+
**Common References**:
|
| 159 |
+
```csharp
|
| 160 |
+
var references = new List<MetadataReference>
|
| 161 |
+
{
|
| 162 |
+
// Core runtime
|
| 163 |
+
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
|
| 164 |
+
MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
|
| 165 |
+
|
| 166 |
+
// LINQ
|
| 167 |
+
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location),
|
| 168 |
+
|
| 169 |
+
// System.Runtime (critical for .NET Core)
|
| 170 |
+
MetadataReference.CreateFromFile(Assembly.Load("System.Runtime").Location),
|
| 171 |
+
|
| 172 |
+
// Collections
|
| 173 |
+
MetadataReference.CreateFromFile(Assembly.Load("System.Collections").Location),
|
| 174 |
+
|
| 175 |
+
// For async/await
|
| 176 |
+
MetadataReference.CreateFromFile(typeof(Task).Assembly.Location)
|
| 177 |
+
};
|
| 178 |
+
```
|
| 179 |
+
|
| 180 |
+
**Finding Additional References**:
|
| 181 |
+
```csharp
|
| 182 |
+
// For a specific type you need
|
| 183 |
+
var type = typeof(SomeType);
|
| 184 |
+
var reference = MetadataReference.CreateFromFile(type.Assembly.Location);
|
| 185 |
+
|
| 186 |
+
// For framework assemblies
|
| 187 |
+
var assembly = Assembly.Load("AssemblyName");
|
| 188 |
+
var reference = MetadataReference.CreateFromFile(assembly.Location);
|
| 189 |
+
```
|
| 190 |
+
|
| 191 |
+
### 5. Entry Point Discovery
|
| 192 |
+
|
| 193 |
+
When executing compiled assemblies, you need to find the entry point:
|
| 194 |
+
|
| 195 |
+
```csharp
|
| 196 |
+
private static MethodInfo? FindEntryPoint(Assembly assembly)
|
| 197 |
+
{
|
| 198 |
+
// Traditional Main method
|
| 199 |
+
var programType = assembly.GetTypes()
|
| 200 |
+
.FirstOrDefault(t => t.Name == "Program");
|
| 201 |
+
if (programType != null)
|
| 202 |
+
{
|
| 203 |
+
var mainMethod = programType.GetMethod("Main",
|
| 204 |
+
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
| 205 |
+
if (mainMethod != null)
|
| 206 |
+
return mainMethod;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
// Top-level statements (C# 9+)
|
| 210 |
+
var entryPoint = assembly.GetTypes()
|
| 211 |
+
.SelectMany(t => t.GetMethods(
|
| 212 |
+
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
|
| 213 |
+
.FirstOrDefault(m => m.Name == "<Main>$");
|
| 214 |
+
|
| 215 |
+
return entryPoint;
|
| 216 |
+
}
|
| 217 |
+
```
|
| 218 |
+
|
| 219 |
+
**Execution**:
|
| 220 |
+
```csharp
|
| 221 |
+
var entryPoint = FindEntryPoint(assembly);
|
| 222 |
+
var parameters = entryPoint.GetParameters().Length == 0
|
| 223 |
+
? null
|
| 224 |
+
: new object[] { Array.Empty<string>() };
|
| 225 |
+
|
| 226 |
+
var result = entryPoint.Invoke(null, parameters);
|
| 227 |
+
|
| 228 |
+
// Handle async returns
|
| 229 |
+
if (result is Task task)
|
| 230 |
+
{
|
| 231 |
+
await task;
|
| 232 |
+
}
|
| 233 |
+
```
|
| 234 |
+
|
| 235 |
+
### 6. Console Output Capture
|
| 236 |
+
|
| 237 |
+
**For REPL scenarios**, capture Console output:
|
| 238 |
+
|
| 239 |
+
```csharp
|
| 240 |
+
var outputBuilder = new StringBuilder();
|
| 241 |
+
var originalOut = Console.Out;
|
| 242 |
+
|
| 243 |
+
try
|
| 244 |
+
{
|
| 245 |
+
using var outputWriter = new StringWriter(outputBuilder);
|
| 246 |
+
Console.SetOut(outputWriter);
|
| 247 |
+
|
| 248 |
+
// Execute code
|
| 249 |
+
|
| 250 |
+
await outputWriter.FlushAsync();
|
| 251 |
+
var output = outputBuilder.ToString();
|
| 252 |
+
}
|
| 253 |
+
finally
|
| 254 |
+
{
|
| 255 |
+
Console.SetOut(originalOut);
|
| 256 |
+
}
|
| 257 |
+
```
|
| 258 |
+
|
| 259 |
+
## Implementation in RoslynStone
|
| 260 |
+
|
| 261 |
+
### Architecture Decision
|
| 262 |
+
|
| 263 |
+
We use **both approaches** strategically:
|
| 264 |
+
|
| 265 |
+
1. **RoslynScriptingService** (Scripting API)
|
| 266 |
+
- Used for REPL functionality
|
| 267 |
+
- State preservation between executions
|
| 268 |
+
- Simple expression evaluation
|
| 269 |
+
- Quick prototyping
|
| 270 |
+
|
| 271 |
+
2. **CompilationService + AssemblyExecutionService** (Compilation API)
|
| 272 |
+
- Used for file execution
|
| 273 |
+
- Proper assembly unloading
|
| 274 |
+
- Memory isolation
|
| 275 |
+
- Production-grade execution
|
| 276 |
+
|
| 277 |
+
### Services Created
|
| 278 |
+
|
| 279 |
+
#### CompilationService
|
| 280 |
+
```csharp
|
| 281 |
+
// Compiles C# code to in-memory assemblies
|
| 282 |
+
public class CompilationService
|
| 283 |
+
{
|
| 284 |
+
public CompilationResult Compile(string code, string? assemblyName = null)
|
| 285 |
+
{
|
| 286 |
+
// Uses CSharpCompilation API
|
| 287 |
+
// Returns MemoryStream with compiled assembly
|
| 288 |
+
}
|
| 289 |
+
}
|
| 290 |
+
```
|
| 291 |
+
|
| 292 |
+
#### AssemblyExecutionService
|
| 293 |
+
```csharp
|
| 294 |
+
// Executes assemblies in unloadable contexts
|
| 295 |
+
public class AssemblyExecutionService
|
| 296 |
+
{
|
| 297 |
+
public async Task<AssemblyExecutionResult> ExecuteFileAsync(
|
| 298 |
+
string filePath,
|
| 299 |
+
CancellationToken cancellationToken = default)
|
| 300 |
+
{
|
| 301 |
+
// 1. Compile code
|
| 302 |
+
// 2. Create UnloadableAssemblyLoadContext
|
| 303 |
+
// 3. Load assembly from stream
|
| 304 |
+
// 4. Find and invoke entry point
|
| 305 |
+
// 5. Unload context
|
| 306 |
+
// 6. Verify unloading with WeakReference
|
| 307 |
+
}
|
| 308 |
+
}
|
| 309 |
+
```
|
| 310 |
+
|
| 311 |
+
#### UnloadableAssemblyLoadContext
|
| 312 |
+
```csharp
|
| 313 |
+
// Custom context for assembly isolation
|
| 314 |
+
public class UnloadableAssemblyLoadContext : AssemblyLoadContext
|
| 315 |
+
{
|
| 316 |
+
public UnloadableAssemblyLoadContext()
|
| 317 |
+
: base(isCollectible: true) { }
|
| 318 |
+
}
|
| 319 |
+
```
|
| 320 |
+
|
| 321 |
+
## Best Practices Summary
|
| 322 |
+
|
| 323 |
+
### ✅ DO
|
| 324 |
+
|
| 325 |
+
1. **Use AssemblyLoadContext** for any dynamically loaded assemblies
|
| 326 |
+
2. **Set isCollectible: true** when creating the context
|
| 327 |
+
3. **Use WeakReference** to verify unloading
|
| 328 |
+
4. **Call Unload()** and force garbage collection
|
| 329 |
+
5. **Manage metadata references** carefully
|
| 330 |
+
6. **Capture and handle compilation errors** properly
|
| 331 |
+
7. **Find entry points** for both traditional and top-level statements
|
| 332 |
+
8. **Handle async return types** (Task, Task<T>)
|
| 333 |
+
9. **Capture console output** if needed
|
| 334 |
+
10. **Dispose MemoryStreams** after loading assemblies
|
| 335 |
+
|
| 336 |
+
### ❌ DON'T
|
| 337 |
+
|
| 338 |
+
1. **Don't load assemblies in the default context** if you need to unload them
|
| 339 |
+
2. **Don't forget to call Unload()** on the context
|
| 340 |
+
3. **Don't hold references** to objects from the unloaded context
|
| 341 |
+
4. **Don't use the Scripting API** when you need assembly unloading
|
| 342 |
+
5. **Don't forget required assembly references** (System.Runtime is critical)
|
| 343 |
+
6. **Don't emit to disk** unless necessary (use MemoryStream)
|
| 344 |
+
7. **Don't forget to reset Console.Out** after capturing output
|
| 345 |
+
8. **Don't ignore compilation diagnostics**
|
| 346 |
+
9. **Don't assume synchronous execution** (handle Task returns)
|
| 347 |
+
10. **Don't forget to flush output writers** before reading captured output
|
| 348 |
+
|
| 349 |
+
## Memory Management Pattern
|
| 350 |
+
|
| 351 |
+
```csharp
|
| 352 |
+
// Correct pattern for dynamic compilation and execution
|
| 353 |
+
var context = new UnloadableAssemblyLoadContext();
|
| 354 |
+
WeakReference weakRef = new(context, trackResurrection: true);
|
| 355 |
+
|
| 356 |
+
try
|
| 357 |
+
{
|
| 358 |
+
// 1. Compile
|
| 359 |
+
var compilation = /* ... */;
|
| 360 |
+
using var ms = new MemoryStream();
|
| 361 |
+
var result = compilation.Emit(ms);
|
| 362 |
+
|
| 363 |
+
// 2. Load
|
| 364 |
+
ms.Seek(0, SeekOrigin.Begin);
|
| 365 |
+
var assembly = context.LoadFromStream(ms);
|
| 366 |
+
|
| 367 |
+
// 3. Execute
|
| 368 |
+
var entryPoint = FindEntryPoint(assembly);
|
| 369 |
+
entryPoint.Invoke(null, parameters);
|
| 370 |
+
}
|
| 371 |
+
finally
|
| 372 |
+
{
|
| 373 |
+
// 4. Unload
|
| 374 |
+
context.Unload();
|
| 375 |
+
|
| 376 |
+
// 5. Verify unloading
|
| 377 |
+
for (int i = 0; i < 10 && weakRef.IsAlive; i++)
|
| 378 |
+
{
|
| 379 |
+
GC.Collect();
|
| 380 |
+
GC.WaitForPendingFinalizers();
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
if (weakRef.IsAlive)
|
| 384 |
+
{
|
| 385 |
+
// Memory leak detected - something is still holding a reference
|
| 386 |
+
Console.WriteLine("Warning: Assembly context was not unloaded");
|
| 387 |
+
}
|
| 388 |
+
}
|
| 389 |
+
```
|
| 390 |
+
|
| 391 |
+
## Security Considerations
|
| 392 |
+
|
| 393 |
+
1. **Code Execution Risk**: Dynamic compilation executes arbitrary code
|
| 394 |
+
- Run in sandboxed environments
|
| 395 |
+
- Implement code review/validation
|
| 396 |
+
- Use least-privilege execution
|
| 397 |
+
|
| 398 |
+
2. **Resource Limits**:
|
| 399 |
+
- Set execution timeouts
|
| 400 |
+
- Monitor memory usage
|
| 401 |
+
- Limit CPU usage
|
| 402 |
+
|
| 403 |
+
3. **Assembly References**:
|
| 404 |
+
- Only add necessary references
|
| 405 |
+
- Avoid loading privileged assemblies
|
| 406 |
+
- Validate assembly sources
|
| 407 |
+
|
| 408 |
+
## Performance Considerations
|
| 409 |
+
|
| 410 |
+
1. **First Compilation**: ~500-1000ms (includes JIT)
|
| 411 |
+
2. **Subsequent Compilations**: ~200-300ms
|
| 412 |
+
3. **Unloading**: ~50-100ms (with forced GC)
|
| 413 |
+
4. **Memory**: Each loaded assembly context adds ~1-5MB overhead
|
| 414 |
+
|
| 415 |
+
**Optimization Tips**:
|
| 416 |
+
- Cache compilation results when possible
|
| 417 |
+
- Reuse AssemblyLoadContext instances for similar operations
|
| 418 |
+
- Batch multiple compilations
|
| 419 |
+
- Use OptimizationLevel.Release for production
|
| 420 |
+
|
| 421 |
+
## Testing
|
| 422 |
+
|
| 423 |
+
Essential tests to include:
|
| 424 |
+
|
| 425 |
+
```csharp
|
| 426 |
+
[Fact]
|
| 427 |
+
public async Task Assembly_CanBeUnloaded()
|
| 428 |
+
{
|
| 429 |
+
WeakReference weakRef = null;
|
| 430 |
+
|
| 431 |
+
{
|
| 432 |
+
var context = new UnloadableAssemblyLoadContext();
|
| 433 |
+
weakRef = new WeakReference(context, trackResurrection: true);
|
| 434 |
+
|
| 435 |
+
// Load and execute assembly
|
| 436 |
+
|
| 437 |
+
context.Unload();
|
| 438 |
+
}
|
| 439 |
+
|
| 440 |
+
// Force GC
|
| 441 |
+
for (int i = 0; i < 10; i++)
|
| 442 |
+
{
|
| 443 |
+
GC.Collect();
|
| 444 |
+
GC.WaitForPendingFinalizers();
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
Assert.False(weakRef.IsAlive, "Assembly context was not unloaded");
|
| 448 |
+
}
|
| 449 |
+
```
|
| 450 |
+
|
| 451 |
+
## References
|
| 452 |
+
|
| 453 |
+
- [Laurent Kempé's Article](https://laurentkempe.com/2019/02/18/dynamically-compile-and-run-code-using-dotNET-Core-3.0/)
|
| 454 |
+
- [GitHub - DynamicRun Project](https://github.com/laurentkempe/DynamicRun)
|
| 455 |
+
- [Microsoft Docs - AssemblyLoadContext](https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/understanding-assemblyloadcontext)
|
| 456 |
+
- [Microsoft Docs - Roslyn APIs](https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/)
|
| 457 |
+
- [Stack Overflow - Dynamic Compilation in .NET Core](https://stackoverflow.com/questions/71474900/dynamic-compilation-in-net-core-6)
|
| 458 |
+
|
| 459 |
+
## Conclusion
|
| 460 |
+
|
| 461 |
+
The key insight from Laurent Kempé's approach is that **AssemblyLoadContext with `isCollectible: true` is essential** for proper memory management in dynamic compilation scenarios. Without it, every dynamically loaded assembly stays in memory forever, leading to memory leaks.
|
| 462 |
+
|
| 463 |
+
Combined with proper use of:
|
| 464 |
+
- Roslyn's CSharpCompilation API
|
| 465 |
+
- WeakReference for verification
|
| 466 |
+
- Correct reference management
|
| 467 |
+
- Proper entry point discovery
|
| 468 |
+
|
| 469 |
+
This approach enables production-grade dynamic code execution with full control over memory lifecycle.
|
Dockerfile
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Build stage
|
| 2 |
+
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
| 3 |
+
ARG BUILD_CONFIGURATION=Release
|
| 4 |
+
WORKDIR /src
|
| 5 |
+
|
| 6 |
+
# Copy solution and project files for better layer caching
|
| 7 |
+
COPY ["RoslynStone.sln", "./"]
|
| 8 |
+
COPY ["src/RoslynStone.Api/RoslynStone.Api.csproj", "src/RoslynStone.Api/"]
|
| 9 |
+
COPY ["src/RoslynStone.Core/RoslynStone.Core.csproj", "src/RoslynStone.Core/"]
|
| 10 |
+
COPY ["src/RoslynStone.Infrastructure/RoslynStone.Infrastructure.csproj", "src/RoslynStone.Infrastructure/"]
|
| 11 |
+
COPY ["src/RoslynStone.ServiceDefaults/RoslynStone.ServiceDefaults.csproj", "src/RoslynStone.ServiceDefaults/"]
|
| 12 |
+
COPY ["src/RoslynStone.GradioModule/RoslynStone.GradioModule.csproj", "src/RoslynStone.GradioModule/"]
|
| 13 |
+
|
| 14 |
+
# Restore dependencies (this layer will be cached if project files don't change)
|
| 15 |
+
RUN dotnet restore "src/RoslynStone.Api/RoslynStone.Api.csproj"
|
| 16 |
+
|
| 17 |
+
# Copy all source files
|
| 18 |
+
COPY . .
|
| 19 |
+
|
| 20 |
+
# Build the application
|
| 21 |
+
WORKDIR "/src/src/RoslynStone.Api"
|
| 22 |
+
# Build into the standard bin/$(Configuration)/$(TargetFramework) output which publish expects
|
| 23 |
+
RUN dotnet build "RoslynStone.Api.csproj" \
|
| 24 |
+
-c $BUILD_CONFIGURATION \
|
| 25 |
+
--no-restore
|
| 26 |
+
|
| 27 |
+
# Publish stage
|
| 28 |
+
FROM build AS publish
|
| 29 |
+
ARG BUILD_CONFIGURATION=Release
|
| 30 |
+
|
| 31 |
+
# Install CSnakes.Stage tool for Python environment setup
|
| 32 |
+
RUN dotnet tool install --global CSnakes.Stage
|
| 33 |
+
ENV PATH="/root/.dotnet/tools:${PATH}"
|
| 34 |
+
|
| 35 |
+
# Set up Python environment with CSnakes
|
| 36 |
+
# This downloads Python 3.12 redistributable and creates a venv
|
| 37 |
+
RUN setup-python --python 3.12 --venv /app/.venv --verbose
|
| 38 |
+
|
| 39 |
+
# Install UV (fast Python package installer) - only needed in build stage
|
| 40 |
+
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
|
| 41 |
+
ENV PATH="/root/.local/bin:${PATH}"
|
| 42 |
+
|
| 43 |
+
# Install all Python dependencies using UV into the venv
|
| 44 |
+
# This happens at build time, not runtime, for faster container startup
|
| 45 |
+
# --prerelease=allow is needed because gradio 6.0.0 depends on gradio-client 2.0.0.dev3
|
| 46 |
+
# We install dependencies directly rather than using editable install since this is just a script, not a package
|
| 47 |
+
WORKDIR "/src/src/RoslynStone.GradioModule"
|
| 48 |
+
RUN uv pip install \
|
| 49 |
+
"gradio>=6.0.0" \
|
| 50 |
+
"httpx>=0.27.0" \
|
| 51 |
+
"pygments>=2.17.0" \
|
| 52 |
+
"openai>=1.0.0" \
|
| 53 |
+
"anthropic>=0.25.0" \
|
| 54 |
+
"google-generativeai>=0.3.0" \
|
| 55 |
+
"huggingface_hub>=0.20.0" \
|
| 56 |
+
--python /app/.venv/bin/python \
|
| 57 |
+
--prerelease=allow
|
| 58 |
+
|
| 59 |
+
# Verify Gradio is installed correctly
|
| 60 |
+
RUN /app/.venv/bin/python3 -c "import gradio; print(f'Gradio {gradio.__version__} installed successfully')"
|
| 61 |
+
|
| 62 |
+
# Publish the application with ReadyToRun (R2R) for faster startup
|
| 63 |
+
# Note: We cannot use Native AOT because Roslyn requires dynamic code compilation
|
| 64 |
+
# R2R provides a hybrid approach - pre-compiled code with JIT fallback for dynamic scenarios
|
| 65 |
+
WORKDIR "/src/src/RoslynStone.Api"
|
| 66 |
+
RUN dotnet publish "RoslynStone.Api.csproj" \
|
| 67 |
+
-c $BUILD_CONFIGURATION \
|
| 68 |
+
-o /app/publish \
|
| 69 |
+
--no-restore \
|
| 70 |
+
--no-build \
|
| 71 |
+
/p:UseAppHost=false \
|
| 72 |
+
/p:PublishReadyToRun=true \
|
| 73 |
+
/p:PublishSingleFile=false \
|
| 74 |
+
/p:PublishTrimmed=false
|
| 75 |
+
|
| 76 |
+
# Runtime stage
|
| 77 |
+
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
|
| 78 |
+
WORKDIR /app
|
| 79 |
+
|
| 80 |
+
# Create non-root user for security
|
| 81 |
+
RUN groupadd -r appuser && useradd -r -g appuser -m -d /home/appuser appuser
|
| 82 |
+
|
| 83 |
+
# Copy published application
|
| 84 |
+
COPY --from=publish /app/publish .
|
| 85 |
+
COPY src/RoslynStone.Api/entrypoint.sh .
|
| 86 |
+
RUN chmod +x entrypoint.sh
|
| 87 |
+
|
| 88 |
+
# Copy CSnakes Python redistributable from build stage
|
| 89 |
+
COPY --from=publish /root/.config/CSnakes /home/appuser/.config/CSnakes
|
| 90 |
+
|
| 91 |
+
# Some venv scripts created during the publish stage contain shebangs
|
| 92 |
+
# that reference the root user's CSnakes path (/root/.config/CSnakes/...).
|
| 93 |
+
# Create a root-side symlink to the copied location so those shebangs
|
| 94 |
+
# keep working in the runtime image.
|
| 95 |
+
RUN mkdir -p /root/.config \
|
| 96 |
+
&& ln -s /home/appuser/.config/CSnakes /root/.config/CSnakes || true
|
| 97 |
+
|
| 98 |
+
# Copy Python virtual environment with pre-installed Gradio from build stage
|
| 99 |
+
COPY --from=publish /app/.venv .venv
|
| 100 |
+
|
| 101 |
+
# Verify venv was copied correctly
|
| 102 |
+
RUN test -f /app/.venv/bin/python3 && test -f /app/.venv/bin/pip || \
|
| 103 |
+
(echo "ERROR: Virtual environment not copied correctly!" && exit 1)
|
| 104 |
+
|
| 105 |
+
# Create symlink for backward compatibility (some code may reference /app/venv)
|
| 106 |
+
RUN ln -s /app/.venv /app/venv
|
| 107 |
+
# Change ownership to non-root user
|
| 108 |
+
RUN chown -R appuser:appuser /app /home/appuser/.config
|
| 109 |
+
|
| 110 |
+
# Set environment variables for MCP server and Python
|
| 111 |
+
# MCP_TRANSPORT: "stdio" (default) or "http"
|
| 112 |
+
# When using HTTP transport, set ASPNETCORE_URLS to configure listening address
|
| 113 |
+
# DOTNET_RUNNING_IN_CONTAINER tells the app to skip Python dependency installation
|
| 114 |
+
# since dependencies are pre-installed during docker build
|
| 115 |
+
ENV DOTNET_ENVIRONMENT=Production \
|
| 116 |
+
MCP_TRANSPORT=stdio \
|
| 117 |
+
ASPNETCORE_URLS= \
|
| 118 |
+
DOTNET_EnableDiagnostics=0 \
|
| 119 |
+
DOTNET_RUNNING_IN_CONTAINER=true \
|
| 120 |
+
LD_LIBRARY_PATH=/home/appuser/.config/CSnakes/python3.12.9/python/install/lib \
|
| 121 |
+
PYTHONHOME=/home/appuser/.config/CSnakes/python3.12.9/python/install
|
| 122 |
+
|
| 123 |
+
# Switch to non-root user
|
| 124 |
+
USER appuser
|
| 125 |
+
|
| 126 |
+
# The MCP server supports both stdio and HTTP transports
|
| 127 |
+
# - Stdio (default): Set MCP_TRANSPORT=stdio, no ports exposed
|
| 128 |
+
# - HTTP: Set MCP_TRANSPORT=http and ASPNETCORE_URLS=http://+:8080, then EXPOSE 8080
|
| 129 |
+
# In HTTP mode, Gradio landing page will be available at root (/)
|
| 130 |
+
# Telemetry will be sent to the OTEL endpoint configured via environment variables
|
| 131 |
+
|
| 132 |
+
# Example for HTTP mode:
|
| 133 |
+
# ENV MCP_TRANSPORT=http
|
| 134 |
+
# ENV ASPNETCORE_URLS=http://+:8080
|
| 135 |
+
# EXPOSE 8080
|
| 136 |
+
|
| 137 |
+
ENTRYPOINT ["./entrypoint.sh"]
|
| 138 |
+
|
GETTING_STARTED.md
ADDED
|
@@ -0,0 +1,559 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Getting Started with Roslyn-Stone Development
|
| 2 |
+
|
| 3 |
+
This guide covers advanced setup options, development workflows, and technical details for contributors and developers working on Roslyn-Stone.
|
| 4 |
+
|
| 5 |
+
For basic usage, see the main [README.md](README.md).
|
| 6 |
+
|
| 7 |
+
## Table of Contents
|
| 8 |
+
|
| 9 |
+
- [Local Development Setup](#local-development-setup)
|
| 10 |
+
- [Transport Modes](#transport-modes)
|
| 11 |
+
- [Development Environments](#development-environments)
|
| 12 |
+
- [Advanced Configuration](#advanced-configuration)
|
| 13 |
+
- [Development Tools](#development-tools)
|
| 14 |
+
- [Testing](#testing)
|
| 15 |
+
- [Project Structure](#project-structure)
|
| 16 |
+
- [Adding New MCP Tools](#adding-new-mcp-tools)
|
| 17 |
+
- [Testing and Debugging](#testing-and-debugging)
|
| 18 |
+
|
| 19 |
+
## Local Development Setup
|
| 20 |
+
|
| 21 |
+
### Prerequisites
|
| 22 |
+
|
| 23 |
+
- .NET 10.0 SDK or later
|
| 24 |
+
- C# 13
|
| 25 |
+
- Docker (for containerized deployment)
|
| 26 |
+
- VS Code with Dev Containers extension (optional)
|
| 27 |
+
- .NET Aspire workload (optional, for local orchestration)
|
| 28 |
+
|
| 29 |
+
### Building from Source
|
| 30 |
+
|
| 31 |
+
```bash
|
| 32 |
+
# Clone the repository
|
| 33 |
+
git clone https://github.com/dylanlangston/Roslyn-Stone.git
|
| 34 |
+
cd Roslyn-Stone
|
| 35 |
+
|
| 36 |
+
# Build the solution
|
| 37 |
+
dotnet build
|
| 38 |
+
|
| 39 |
+
# Run tests
|
| 40 |
+
dotnet test
|
| 41 |
+
|
| 42 |
+
# Run the MCP server (stdio transport - default)
|
| 43 |
+
cd src/RoslynStone.Api
|
| 44 |
+
dotnet run
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
## Transport Modes
|
| 48 |
+
|
| 49 |
+
Roslyn-Stone supports two MCP transport modes:
|
| 50 |
+
|
| 51 |
+
### Stdio Transport (Default)
|
| 52 |
+
|
| 53 |
+
- Best for local, single-machine integrations
|
| 54 |
+
- Used by Claude Desktop and other local MCP clients
|
| 55 |
+
- Communication via stdin/stdout
|
| 56 |
+
- No network ports required
|
| 57 |
+
|
| 58 |
+
### HTTP Transport
|
| 59 |
+
|
| 60 |
+
- Best for remote access and web-based integrations
|
| 61 |
+
- Accessible over the network via HTTP/SSE
|
| 62 |
+
- Endpoint at `/mcp` (e.g., `http://localhost:8080/mcp`)
|
| 63 |
+
- ⚠️ **WARNING**: Do not expose publicly without authentication, authorization, and network restrictions. This server can execute arbitrary C# code.
|
| 64 |
+
|
| 65 |
+
To switch transport modes, set the `MCP_TRANSPORT` environment variable:
|
| 66 |
+
|
| 67 |
+
```bash
|
| 68 |
+
# Stdio (default)
|
| 69 |
+
MCP_TRANSPORT=stdio dotnet run
|
| 70 |
+
|
| 71 |
+
# HTTP
|
| 72 |
+
MCP_TRANSPORT=http dotnet run --urls "http://localhost:8080"
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
## Development Environments
|
| 76 |
+
|
| 77 |
+
### With Aspire (Orchestrated)
|
| 78 |
+
|
| 79 |
+
```bash
|
| 80 |
+
# Install Aspire workload
|
| 81 |
+
dotnet workload install aspire
|
| 82 |
+
|
| 83 |
+
# Run with Aspire dashboard for observability
|
| 84 |
+
cd src/RoslynStone.AppHost
|
| 85 |
+
dotnet run
|
| 86 |
+
```
|
| 87 |
+
|
| 88 |
+
This will start:
|
| 89 |
+
- **Aspire Dashboard** at `http://localhost:18888` - View logs, metrics, and traces
|
| 90 |
+
- **MCP Inspector UI** at `http://localhost:6274` - Interactive tool testing interface (development mode only)
|
| 91 |
+
- **MCP Proxy** at `http://localhost:6277` - Protocol bridge for the inspector
|
| 92 |
+
- **MCP Server (stdio)** - Stdio transport instance for local testing
|
| 93 |
+
- **MCP Server (HTTP)** at `http://localhost:8080/mcp` - HTTP transport instance for remote access
|
| 94 |
+
|
| 95 |
+
The MCP Inspector is automatically started in development mode, providing a web-based interface to test and debug MCP tools in real-time.
|
| 96 |
+
|
| 97 |
+
### Development Container
|
| 98 |
+
|
| 99 |
+
The repository includes a fully configured devcontainer with Docker-in-Docker support for isolated development:
|
| 100 |
+
|
| 101 |
+
```bash
|
| 102 |
+
# Open the repo in VS Code
|
| 103 |
+
code .
|
| 104 |
+
|
| 105 |
+
# Press F1 and select "Dev Containers: Reopen in Container"
|
| 106 |
+
# The container will automatically build, restore dependencies, and build the project
|
| 107 |
+
```
|
| 108 |
+
|
| 109 |
+
See [`.devcontainer/README.md`](.devcontainer/README.md) for more details about the devcontainer setup.
|
| 110 |
+
|
| 111 |
+
### Docker Compose
|
| 112 |
+
|
| 113 |
+
```bash
|
| 114 |
+
# Build and run both stdio and HTTP variants with Docker Compose
|
| 115 |
+
docker-compose up --build
|
| 116 |
+
|
| 117 |
+
# Run only the stdio variant
|
| 118 |
+
docker-compose up roslyn-stone-mcp-stdio
|
| 119 |
+
|
| 120 |
+
# Run only the HTTP variant
|
| 121 |
+
docker-compose up roslyn-stone-mcp-http
|
| 122 |
+
|
| 123 |
+
# Access Aspire dashboard at http://localhost:18888
|
| 124 |
+
# Access HTTP MCP endpoint at http://localhost:8080/mcp
|
| 125 |
+
```
|
| 126 |
+
|
| 127 |
+
The server supports both stdio and HTTP transport modes:
|
| 128 |
+
- **Stdio transport**: Reads JSON-RPC messages from stdin and writes responses to stdout, with logging to stderr
|
| 129 |
+
- **HTTP transport**: Exposes MCP endpoints via HTTP/SSE for remote access at `/mcp`
|
| 130 |
+
|
| 131 |
+
## Advanced Configuration
|
| 132 |
+
|
| 133 |
+
### Custom MCP Server Configurations
|
| 134 |
+
|
| 135 |
+
For local development without Docker:
|
| 136 |
+
|
| 137 |
+
**Claude Desktop:**
|
| 138 |
+
```json
|
| 139 |
+
{
|
| 140 |
+
"mcpServers": {
|
| 141 |
+
"roslyn-stone": {
|
| 142 |
+
"command": "dotnet",
|
| 143 |
+
"args": ["run", "--project", "/path/to/Roslyn-Stone/src/RoslynStone.Api"],
|
| 144 |
+
"env": {
|
| 145 |
+
"DOTNET_ENVIRONMENT": "Development",
|
| 146 |
+
"MCP_TRANSPORT": "stdio"
|
| 147 |
+
}
|
| 148 |
+
}
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
```
|
| 152 |
+
|
| 153 |
+
### HTTP Transport Configuration
|
| 154 |
+
|
| 155 |
+
For remote MCP servers or web-based integrations, use HTTP transport:
|
| 156 |
+
|
| 157 |
+
**Local HTTP Server:**
|
| 158 |
+
```bash
|
| 159 |
+
# Start the server with HTTP transport
|
| 160 |
+
cd src/RoslynStone.Api
|
| 161 |
+
MCP_TRANSPORT=http ASPNETCORE_URLS=http://localhost:8080 dotnet run
|
| 162 |
+
```
|
| 163 |
+
|
| 164 |
+
**Docker HTTP Server:**
|
| 165 |
+
```bash
|
| 166 |
+
# Run with HTTP transport
|
| 167 |
+
docker run -e MCP_TRANSPORT=http -e ASPNETCORE_URLS=http://+:8080 -p 8080:8080 ghcr.io/dylanlangston/roslyn-stone:latest
|
| 168 |
+
```
|
| 169 |
+
|
| 170 |
+
**MCP Client Configuration (HTTP):**
|
| 171 |
+
```json
|
| 172 |
+
{
|
| 173 |
+
"mcpServers": {
|
| 174 |
+
"roslyn-stone-http": {
|
| 175 |
+
"type": "http",
|
| 176 |
+
"url": "http://localhost:8080/mcp"
|
| 177 |
+
}
|
| 178 |
+
}
|
| 179 |
+
}
|
| 180 |
+
```
|
| 181 |
+
|
| 182 |
+
The HTTP endpoint supports:
|
| 183 |
+
- **Server-Sent Events (SSE)** for streaming responses
|
| 184 |
+
- **CORS** support for web-based clients - ⚠️ Configure strict origin allowlists in production. Never use wildcard (*) CORS for code execution endpoints.
|
| 185 |
+
- **Standard MCP protocol** over HTTP
|
| 186 |
+
|
| 187 |
+
See `.github/MCP_CONFIGURATION.md` for more configuration examples.
|
| 188 |
+
|
| 189 |
+
### OpenTelemetry Configuration
|
| 190 |
+
|
| 191 |
+
Configure OpenTelemetry export with environment variables:
|
| 192 |
+
|
| 193 |
+
```bash
|
| 194 |
+
# OTLP endpoint (default: Aspire dashboard)
|
| 195 |
+
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:18889
|
| 196 |
+
|
| 197 |
+
# Service name
|
| 198 |
+
OTEL_SERVICE_NAME=roslyn-stone-mcp
|
| 199 |
+
|
| 200 |
+
# Additional resource attributes
|
| 201 |
+
OTEL_RESOURCE_ATTRIBUTES=service.namespace=roslyn-stone,deployment.environment=production
|
| 202 |
+
```
|
| 203 |
+
|
| 204 |
+
### Production Deployment
|
| 205 |
+
|
| 206 |
+
For production, configure the OTLP endpoint to send telemetry to your monitoring solution:
|
| 207 |
+
|
| 208 |
+
- Azure Monitor / Application Insights
|
| 209 |
+
- AWS CloudWatch
|
| 210 |
+
- Google Cloud Operations
|
| 211 |
+
- Grafana / Prometheus / Jaeger
|
| 212 |
+
- Any OTLP-compatible collector
|
| 213 |
+
|
| 214 |
+
## Development Tools
|
| 215 |
+
|
| 216 |
+
This project uses several development tools:
|
| 217 |
+
|
| 218 |
+
- **CSharpier** - Code formatter (`dotnet csharpier <file-or-directory>`)
|
| 219 |
+
- **ReSharper CLI** - Code analysis (`jb <command>`)
|
| 220 |
+
- **Cake** - Build automation (`dotnet cake <script>`)
|
| 221 |
+
|
| 222 |
+
See `.github/COPILOT_ENVIRONMENT.md` for details on the custom environment setup.
|
| 223 |
+
|
| 224 |
+
## Testing
|
| 225 |
+
|
| 226 |
+
### Running Tests
|
| 227 |
+
|
| 228 |
+
```bash
|
| 229 |
+
dotnet test --logger "console;verbosity=normal"
|
| 230 |
+
|
| 231 |
+
# Run tests by category
|
| 232 |
+
dotnet test --filter "Category=Unit"
|
| 233 |
+
|
| 234 |
+
# Run tests by component
|
| 235 |
+
dotnet test --filter "Component=REPL"
|
| 236 |
+
```
|
| 237 |
+
|
| 238 |
+
### Test Coverage
|
| 239 |
+
|
| 240 |
+
The project maintains high test coverage with automated reporting in CI:
|
| 241 |
+
|
| 242 |
+
- **Line Coverage**: >86% (target: 80%)
|
| 243 |
+
- **Branch Coverage**: >62% (target: 75%)
|
| 244 |
+
- **Total Tests**: 103+ tests
|
| 245 |
+
|
| 246 |
+
#### Run Tests with Coverage
|
| 247 |
+
|
| 248 |
+
```bash
|
| 249 |
+
# Using Cake build script (recommended)
|
| 250 |
+
dotnet cake --target=Test-Coverage
|
| 251 |
+
|
| 252 |
+
# Using dotnet CLI
|
| 253 |
+
dotnet test --collect:"XPlat Code Coverage"
|
| 254 |
+
```
|
| 255 |
+
|
| 256 |
+
Coverage reports are generated in `./artifacts/coverage/` with detailed metrics including:
|
| 257 |
+
- Line coverage percentage
|
| 258 |
+
- Branch coverage percentage
|
| 259 |
+
- Per-file coverage analysis
|
| 260 |
+
- Uncovered code locations
|
| 261 |
+
|
| 262 |
+
#### Generate HTML Coverage Report
|
| 263 |
+
|
| 264 |
+
```bash
|
| 265 |
+
dotnet cake --target=Test-Coverage-Report
|
| 266 |
+
```
|
| 267 |
+
|
| 268 |
+
Opens a detailed HTML report at `./artifacts/coverage-report/index.html` with:
|
| 269 |
+
- Interactive file browser
|
| 270 |
+
- Line-by-line coverage visualization
|
| 271 |
+
- Coverage badges
|
| 272 |
+
- Historical trends
|
| 273 |
+
|
| 274 |
+
### Benchmarks
|
| 275 |
+
|
| 276 |
+
Performance benchmarks using [BenchmarkDotNet](https://benchmarkdotnet.org/) to track and optimize critical operations:
|
| 277 |
+
|
| 278 |
+
#### Available Benchmarks
|
| 279 |
+
|
| 280 |
+
- **RoslynScriptingService** - REPL execution performance
|
| 281 |
+
- Simple expressions
|
| 282 |
+
- Variable assignments
|
| 283 |
+
- LINQ queries
|
| 284 |
+
- Complex operations
|
| 285 |
+
- **CompilationService** - Code compilation performance
|
| 286 |
+
- Simple class compilation
|
| 287 |
+
- Complex code compilation
|
| 288 |
+
- Error handling
|
| 289 |
+
- **NuGetService** - Package operations performance
|
| 290 |
+
- Package search
|
| 291 |
+
- Version lookup
|
| 292 |
+
- README retrieval
|
| 293 |
+
|
| 294 |
+
#### Run Benchmarks
|
| 295 |
+
|
| 296 |
+
```bash
|
| 297 |
+
# Run all benchmarks (Release configuration)
|
| 298 |
+
dotnet cake --target=Benchmark
|
| 299 |
+
|
| 300 |
+
# Run specific benchmark
|
| 301 |
+
dotnet run --project tests/RoslynStone.Benchmarks --configuration Release -- --filter *RoslynScriptingService*
|
| 302 |
+
```
|
| 303 |
+
|
| 304 |
+
Results are saved to `./artifacts/benchmarks/` with:
|
| 305 |
+
- Execution times (Min, Max, Mean, Median)
|
| 306 |
+
- Memory allocations
|
| 307 |
+
- Statistical analysis
|
| 308 |
+
|
| 309 |
+
See [tests/RoslynStone.Benchmarks/README.md](tests/RoslynStone.Benchmarks/README.md) for detailed documentation.
|
| 310 |
+
|
| 311 |
+
### Load Tests
|
| 312 |
+
|
| 313 |
+
Load tests validate the server can handle concurrent requests at scale:
|
| 314 |
+
|
| 315 |
+
#### Test Configuration
|
| 316 |
+
|
| 317 |
+
- **Concurrency**: 300 concurrent requests per round
|
| 318 |
+
- **Rounds**: 10 rounds per scenario
|
| 319 |
+
- **Scenarios**: Expression evaluation, LINQ queries, NuGet search
|
| 320 |
+
- **Total Requests**: 12,000 (300 × 10 × 4 scenarios)
|
| 321 |
+
|
| 322 |
+
#### Prerequisites
|
| 323 |
+
|
| 324 |
+
Start the server in HTTP mode:
|
| 325 |
+
|
| 326 |
+
```bash
|
| 327 |
+
cd src/RoslynStone.Api
|
| 328 |
+
MCP_TRANSPORT=http dotnet run
|
| 329 |
+
```
|
| 330 |
+
|
| 331 |
+
#### Run Load Tests
|
| 332 |
+
|
| 333 |
+
```bash
|
| 334 |
+
# Using Cake build script
|
| 335 |
+
dotnet cake --target=Load-Test
|
| 336 |
+
|
| 337 |
+
# Using dotnet CLI with custom configuration
|
| 338 |
+
dotnet run --project tests/RoslynStone.LoadTests -- http://localhost:7071 300 10
|
| 339 |
+
```
|
| 340 |
+
|
| 341 |
+
Arguments:
|
| 342 |
+
1. Base URL (default: `http://localhost:7071`)
|
| 343 |
+
2. Concurrency (default: 300)
|
| 344 |
+
3. Rounds (default: 10)
|
| 345 |
+
|
| 346 |
+
#### Expected Results
|
| 347 |
+
|
| 348 |
+
A healthy server should achieve:
|
| 349 |
+
- ✅ Success rate > 99%
|
| 350 |
+
- ✅ Average response time < 100ms
|
| 351 |
+
- ✅ Throughput > 1000 req/sec
|
| 352 |
+
|
| 353 |
+
See [tests/RoslynStone.LoadTests/README.md](tests/RoslynStone.LoadTests/README.md) for detailed documentation.
|
| 354 |
+
|
| 355 |
+
### Continuous Integration
|
| 356 |
+
|
| 357 |
+
The CI pipeline runs on every push and pull request:
|
| 358 |
+
|
| 359 |
+
```bash
|
| 360 |
+
# Run full CI pipeline locally
|
| 361 |
+
dotnet cake --target=CI
|
| 362 |
+
```
|
| 363 |
+
|
| 364 |
+
CI includes:
|
| 365 |
+
1. ✅ Code formatting check (CSharpier)
|
| 366 |
+
2. ✅ Code quality analysis (ReSharper)
|
| 367 |
+
3. ✅ Build verification
|
| 368 |
+
4. ✅ Test execution with coverage
|
| 369 |
+
5. ✅ Coverage threshold validation
|
| 370 |
+
|
| 371 |
+
Artifacts generated:
|
| 372 |
+
- Test results (`.trx` files)
|
| 373 |
+
- Coverage reports (Cobertura XML)
|
| 374 |
+
- ReSharper inspection reports
|
| 375 |
+
- Build logs
|
| 376 |
+
|
| 377 |
+
## Project Structure
|
| 378 |
+
|
| 379 |
+
### Core (Domain Layer)
|
| 380 |
+
- **Models**: ExecutionResult, DocumentationInfo, CompilationError, PackageReference, PackageMetadata, PackageVersion, PackageSearchResult
|
| 381 |
+
- **Domain Types**: Simple records and classes representing domain concepts
|
| 382 |
+
|
| 383 |
+
### Infrastructure (Implementation Layer)
|
| 384 |
+
- **Tools**: MCP tool implementations (ReplTools, NuGetTools) - Active operations
|
| 385 |
+
- **Resources**: MCP resource implementations (DocumentationResource, NuGetSearchResource, NuGetPackageResource, ReplStateResource) - Passive data access
|
| 386 |
+
- **Services**: RoslynScriptingService, DocumentationService, NuGetService, CompilationService, AssemblyExecutionService, ReplContextManager
|
| 387 |
+
- **Functional Helpers**: Pure functions for diagnostics, transformations, and utilities
|
| 388 |
+
|
| 389 |
+
### Api (Presentation Layer)
|
| 390 |
+
- **Program.cs**: Host configuration with MCP server setup
|
| 391 |
+
- **Stdio Transport**: JSON-RPC communication via stdin/stdout
|
| 392 |
+
- **HTTP Transport**: HTTP/SSE communication for remote access
|
| 393 |
+
- **Logging**: Configured to stderr to avoid protocol interference
|
| 394 |
+
|
| 395 |
+
## Adding New MCP Tools
|
| 396 |
+
|
| 397 |
+
1. Create tool method in `Infrastructure/Tools` with `[McpServerTool]` attribute
|
| 398 |
+
2. Add to existing `[McpServerToolType]` class or create new one
|
| 399 |
+
3. Use dependency injection for services (RoslynScriptingService, IReplContextManager, etc.)
|
| 400 |
+
4. Include comprehensive XML documentation with `<param>` and `<returns>` tags
|
| 401 |
+
5. Add `[Description]` attributes for MCP protocol metadata
|
| 402 |
+
6. Tools are auto-discovered via `WithToolsFromAssembly()`
|
| 403 |
+
|
| 404 |
+
Example:
|
| 405 |
+
```csharp
|
| 406 |
+
[McpServerToolType]
|
| 407 |
+
public class MyTools
|
| 408 |
+
{
|
| 409 |
+
/// <summary>
|
| 410 |
+
/// Execute custom operation
|
| 411 |
+
/// </summary>
|
| 412 |
+
/// <param name="service">Injected service</param>
|
| 413 |
+
/// <param name="contextManager">Context manager for stateful operations</param>
|
| 414 |
+
/// <param name="input">Input parameter</param>
|
| 415 |
+
/// <param name="contextId">Optional context ID for session continuity</param>
|
| 416 |
+
/// <param name="cancellationToken">Cancellation token</param>
|
| 417 |
+
/// <returns>Result description</returns>
|
| 418 |
+
[McpServerTool]
|
| 419 |
+
[Description("Tool description for MCP")]
|
| 420 |
+
public static async Task<object> MyTool(
|
| 421 |
+
MyService service,
|
| 422 |
+
IReplContextManager contextManager,
|
| 423 |
+
[Description("Input description")] string input,
|
| 424 |
+
[Description("Optional context ID from previous execution")] string? contextId = null,
|
| 425 |
+
CancellationToken cancellationToken = default)
|
| 426 |
+
{
|
| 427 |
+
// Implementation
|
| 428 |
+
}
|
| 429 |
+
}
|
| 430 |
+
```
|
| 431 |
+
|
| 432 |
+
## Adding New MCP Resources
|
| 433 |
+
|
| 434 |
+
1. Create resource class in `Infrastructure/Resources` implementing base resource patterns
|
| 435 |
+
2. Add `[McpServerResourceType]` attribute to the class
|
| 436 |
+
3. Implement URI parsing logic for your resource patterns
|
| 437 |
+
4. Return structured data (not operations)
|
| 438 |
+
5. Resources are auto-discovered via `WithResourcesFromAssembly()`
|
| 439 |
+
|
| 440 |
+
Example:
|
| 441 |
+
```csharp
|
| 442 |
+
[McpServerResourceType]
|
| 443 |
+
public class MyDataResource
|
| 444 |
+
{
|
| 445 |
+
/// <summary>
|
| 446 |
+
/// Provides access to my data
|
| 447 |
+
/// </summary>
|
| 448 |
+
[McpServerResource("mydata://{id}")]
|
| 449 |
+
[Description("Access my data by ID")]
|
| 450 |
+
public static async Task<object> GetMyData(
|
| 451 |
+
[Description("Data ID")] string id,
|
| 452 |
+
MyDataService service,
|
| 453 |
+
CancellationToken cancellationToken = default)
|
| 454 |
+
{
|
| 455 |
+
var data = await service.GetDataAsync(id, cancellationToken);
|
| 456 |
+
return new { id, data };
|
| 457 |
+
}
|
| 458 |
+
}
|
| 459 |
+
```
|
| 460 |
+
|
| 461 |
+
## Testing and Debugging
|
| 462 |
+
|
| 463 |
+
### MCP Inspector
|
| 464 |
+
|
| 465 |
+
The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is an interactive developer tool for testing and debugging MCP servers. It provides a web-based UI to explore available tools, test requests, and view responses in real-time.
|
| 466 |
+
|
| 467 |
+
#### Integrated with Aspire (Recommended)
|
| 468 |
+
|
| 469 |
+
When running with Aspire, the MCP Inspector is automatically started in development mode:
|
| 470 |
+
|
| 471 |
+
```bash
|
| 472 |
+
cd src/RoslynStone.AppHost
|
| 473 |
+
dotnet run
|
| 474 |
+
```
|
| 475 |
+
|
| 476 |
+
The inspector will be available at:
|
| 477 |
+
- **Inspector UI**: `http://localhost:6274` - Interactive web interface
|
| 478 |
+
- **Aspire Dashboard**: `http://localhost:18888` - Observability and logs
|
| 479 |
+
|
| 480 |
+
This provides a seamless development experience with both testing and observability in one place.
|
| 481 |
+
|
| 482 |
+
#### Standalone Inspector
|
| 483 |
+
|
| 484 |
+
To inspect the server without Aspire, use npx to run the inspector directly:
|
| 485 |
+
|
| 486 |
+
```bash
|
| 487 |
+
# From the repository root, run the compiled server through the inspector
|
| 488 |
+
npx @modelcontextprotocol/inspector dotnet run --project src/RoslynStone.Api/RoslynStone.Api.csproj
|
| 489 |
+
```
|
| 490 |
+
|
| 491 |
+
The inspector will start two services:
|
| 492 |
+
- **MCP Inspector UI** at `http://localhost:6274` - Interactive web interface
|
| 493 |
+
- **MCP Proxy** at `http://localhost:6277` - Protocol bridge
|
| 494 |
+
|
| 495 |
+
#### Using the Inspector
|
| 496 |
+
|
| 497 |
+
1. Open `http://localhost:6274` in your browser
|
| 498 |
+
2. The server connection is automatically established
|
| 499 |
+
3. Explore available tools in the left sidebar
|
| 500 |
+
4. Test tools by clicking them and providing parameters
|
| 501 |
+
5. View responses, including return values and output
|
| 502 |
+
6. Export server configuration for Claude Desktop or other clients
|
| 503 |
+
|
| 504 |
+
#### Inspecting with Environment Variables
|
| 505 |
+
|
| 506 |
+
Pass environment variables to configure the server:
|
| 507 |
+
|
| 508 |
+
```bash
|
| 509 |
+
npx @modelcontextprotocol/inspector \
|
| 510 |
+
-e DOTNET_ENVIRONMENT=Development \
|
| 511 |
+
-e OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 \
|
| 512 |
+
dotnet run --project src/RoslynStone.Api/RoslynStone.Api.csproj
|
| 513 |
+
```
|
| 514 |
+
|
| 515 |
+
#### Inspecting the Container
|
| 516 |
+
|
| 517 |
+
You can also inspect the containerized version:
|
| 518 |
+
|
| 519 |
+
```bash
|
| 520 |
+
# Inspect the container image
|
| 521 |
+
npx @modelcontextprotocol/inspector docker run -i --rm ghcr.io/dylanlangston/roslyn-stone:latest
|
| 522 |
+
```
|
| 523 |
+
|
| 524 |
+
#### Custom Ports
|
| 525 |
+
|
| 526 |
+
If you need to use different ports:
|
| 527 |
+
|
| 528 |
+
```bash
|
| 529 |
+
CLIENT_PORT=8080 SERVER_PORT=9000 npx @modelcontextprotocol/inspector \
|
| 530 |
+
dotnet run --project src/RoslynStone.Api/RoslynStone.Api.csproj
|
| 531 |
+
```
|
| 532 |
+
|
| 533 |
+
#### Exporting Configuration
|
| 534 |
+
|
| 535 |
+
The inspector provides buttons to export server configurations:
|
| 536 |
+
|
| 537 |
+
- **Server Entry**: Copies the launch configuration to clipboard for use in `mcp.json`
|
| 538 |
+
- Compatible with Claude Desktop, Cursor, Claude Code, and other MCP clients
|
| 539 |
+
|
| 540 |
+
Example exported configuration:
|
| 541 |
+
```json
|
| 542 |
+
{
|
| 543 |
+
"command": "dotnet",
|
| 544 |
+
"args": ["run", "--project", "/path/to/Roslyn-Stone/src/RoslynStone.Api/RoslynStone.Api.csproj"],
|
| 545 |
+
"env": {
|
| 546 |
+
"DOTNET_ENVIRONMENT": "Development"
|
| 547 |
+
}
|
| 548 |
+
}
|
| 549 |
+
```
|
| 550 |
+
|
| 551 |
+
For more details, see the [MCP Inspector documentation](https://modelcontextprotocol.io/docs/tools/inspector).
|
| 552 |
+
|
| 553 |
+
## Additional Resources
|
| 554 |
+
|
| 555 |
+
- [Model Context Protocol (MCP)](https://github.com/modelcontextprotocol/specification)
|
| 556 |
+
- [Roslyn Scripting APIs](https://learn.microsoft.com/en-us/archive/msdn-magazine/2016/january/essential-net-csharp-scripting)
|
| 557 |
+
- [Dynamic Compilation Best Practices](DYNAMIC_COMPILATION_BEST_PRACTICES.md)
|
| 558 |
+
- [Aspire Documentation](https://learn.microsoft.com/en-us/dotnet/aspire/)
|
| 559 |
+
- [OpenTelemetry Documentation](https://opentelemetry.io/docs/languages/net/)
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2025 Dylan
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
MCP_ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# MCP Architecture
|
| 2 |
+
|
| 3 |
+
This document describes the Model Context Protocol (MCP) architecture implementation in Roslyn-Stone.
|
| 4 |
+
|
| 5 |
+
## Overview
|
| 6 |
+
|
| 7 |
+
Roslyn-Stone implements MCP to help LLMs create single-file C# utility programs (file-based apps). It properly distinguishes between:
|
| 8 |
+
- **Tools**: Active functions that execute, validate, and build utility programs
|
| 9 |
+
- **Resources**: Passive data sources providing read-only access to documentation and package info
|
| 10 |
+
- **Prompts**: Optimized templates guiding LLMs to create runnable .cs files
|
| 11 |
+
|
| 12 |
+
## Design Philosophy
|
| 13 |
+
|
| 14 |
+
The goal is to enable LLMs to create complete, runnable single-file C# programs using .NET 10's file-based app feature. This aligns with `dotnet run app.cs`, which eliminates the need for project files and boilerplate code by supporting:
|
| 15 |
+
- **Top-level statements**: No class or Main method required
|
| 16 |
+
- **`#:package` directive**: Declare NuGet dependencies directly in .cs files
|
| 17 |
+
- **`#:sdk` directive**: Specify project SDK (e.g., Microsoft.NET.Sdk.Web) in code
|
| 18 |
+
|
| 19 |
+
**Target output:** Self-contained .cs files that can be run directly, perfect for:
|
| 20 |
+
- Command-line utilities
|
| 21 |
+
- Data processing scripts
|
| 22 |
+
- Automation tools
|
| 23 |
+
- Web APIs and services
|
| 24 |
+
- Quick C# programs without project scaffolding
|
| 25 |
+
|
| 26 |
+
**Development workflow:**
|
| 27 |
+
1. **Test with `nugetPackages` parameter**: Load packages during REPL execution for testing
|
| 28 |
+
2. **Finalize with `#:package` directive**: Create self-contained .cs files for production use
|
| 29 |
+
3. **No .csproj needed**: Everything declared in the single .cs file
|
| 30 |
+
|
| 31 |
+
## Resources (Passive Data Access)
|
| 32 |
+
|
| 33 |
+
Resources provide read-only, URI-based access to data. They enable efficient discovery and querying without execution.
|
| 34 |
+
|
| 35 |
+
### DocumentationResource
|
| 36 |
+
|
| 37 |
+
Provides access to .NET XML documentation.
|
| 38 |
+
|
| 39 |
+
**URI Pattern**: `doc://{symbolName}`
|
| 40 |
+
|
| 41 |
+
**Examples**:
|
| 42 |
+
```
|
| 43 |
+
doc://System.String
|
| 44 |
+
doc://System.Linq.Enumerable.Select
|
| 45 |
+
doc://System.Collections.Generic.List`1
|
| 46 |
+
```
|
| 47 |
+
|
| 48 |
+
**Response**: Structured documentation with summary, parameters, return values, examples, and related types.
|
| 49 |
+
|
| 50 |
+
### NuGetSearchResource
|
| 51 |
+
|
| 52 |
+
Searchable catalog of NuGet packages.
|
| 53 |
+
|
| 54 |
+
**URI Pattern**: `nuget://search?q={query}&skip={skip}&take={take}`
|
| 55 |
+
|
| 56 |
+
**Examples**:
|
| 57 |
+
```
|
| 58 |
+
nuget://search?q=json
|
| 59 |
+
nuget://search?q=http%20client&skip=0&take=10
|
| 60 |
+
```
|
| 61 |
+
|
| 62 |
+
**Response**: Package list with ID, description, version, download count, tags.
|
| 63 |
+
|
| 64 |
+
### NuGetPackageResource
|
| 65 |
+
|
| 66 |
+
Package-specific information.
|
| 67 |
+
|
| 68 |
+
**URI Patterns**:
|
| 69 |
+
- Versions: `nuget://packages/{id}/versions`
|
| 70 |
+
- README: `nuget://packages/{id}/readme?version={version}`
|
| 71 |
+
|
| 72 |
+
**Examples**:
|
| 73 |
+
```
|
| 74 |
+
nuget://packages/Newtonsoft.Json/versions
|
| 75 |
+
nuget://packages/Newtonsoft.Json/readme
|
| 76 |
+
nuget://packages/Newtonsoft.Json/readme?version=13.0.3
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
**Response**: Version list or README content in Markdown.
|
| 80 |
+
|
| 81 |
+
### ReplStateResource
|
| 82 |
+
|
| 83 |
+
REPL environment and session state.
|
| 84 |
+
|
| 85 |
+
**URI Patterns**:
|
| 86 |
+
- General: `repl://state` or `repl://info`
|
| 87 |
+
- Session-specific: `repl://sessions/{contextId}/state`
|
| 88 |
+
|
| 89 |
+
**Examples**:
|
| 90 |
+
```
|
| 91 |
+
repl://state
|
| 92 |
+
repl://sessions/abc-123-def-456/state
|
| 93 |
+
```
|
| 94 |
+
|
| 95 |
+
**Response**: Framework version, capabilities, active session count, session metadata.
|
| 96 |
+
|
| 97 |
+
## Tools (Active Operations)
|
| 98 |
+
|
| 99 |
+
Tools perform operations and can modify state. All REPL tools support context management.
|
| 100 |
+
|
| 101 |
+
### Context-Aware Design
|
| 102 |
+
|
| 103 |
+
All tools accept an optional `contextId` parameter:
|
| 104 |
+
- **Without contextId**: Single-shot execution (context created, can be reused)
|
| 105 |
+
- **With contextId**: Session-based execution (state persists)
|
| 106 |
+
|
| 107 |
+
### EvaluateCsharp
|
| 108 |
+
|
| 109 |
+
Execute C# code to create and test single-file utility programs. Supports inline package loading for testing.
|
| 110 |
+
|
| 111 |
+
**Parameters**:
|
| 112 |
+
- `code`: C# code to execute (use top-level statements for file-based apps)
|
| 113 |
+
- `contextId` (optional): Session context ID for iterative development
|
| 114 |
+
- `createContext` (optional): Create persistent context for multi-step building
|
| 115 |
+
- `nugetPackages` (optional): Array of packages to load inline: `[{packageName: "Humanizer", version: "3.0.1"}]`
|
| 116 |
+
|
| 117 |
+
**Returns**: `{ success, returnValue, output, errors, warnings, executionTime, contextId }`
|
| 118 |
+
|
| 119 |
+
**Use Cases**:
|
| 120 |
+
- Test complete utility programs with packages loaded inline
|
| 121 |
+
- Iteratively build single-file apps with stateful context
|
| 122 |
+
- Validate program logic before finalizing
|
| 123 |
+
- Rapid prototyping with NuGet packages (test) → Finalize with `#:package` directive (production)
|
| 124 |
+
|
| 125 |
+
**Example workflow:**
|
| 126 |
+
```
|
| 127 |
+
1. Test: EvaluateCsharp(code, nugetPackages: [{packageName: "Humanizer"}])
|
| 128 |
+
2. Iterate: Refine logic with loaded packages
|
| 129 |
+
3. Finalize: Output utility.cs with #:package directive at top
|
| 130 |
+
```
|
| 131 |
+
|
| 132 |
+
### ValidateCsharp
|
| 133 |
+
|
| 134 |
+
Validate C# syntax and semantics for single-file utility programs.
|
| 135 |
+
|
| 136 |
+
**Parameters**:
|
| 137 |
+
- `code`: C# code to validate (use top-level statements)
|
| 138 |
+
- `contextId` (optional): Session context for context-aware validation
|
| 139 |
+
|
| 140 |
+
**Returns**: `{ isValid, issues: [{ code, message, severity, line, column }] }`
|
| 141 |
+
|
| 142 |
+
**Use Cases**:
|
| 143 |
+
- Check utility program syntax before execution
|
| 144 |
+
- Validate complete .cs files
|
| 145 |
+
- Context-aware validation against session variables
|
| 146 |
+
|
| 147 |
+
### ResetRepl
|
| 148 |
+
|
| 149 |
+
Reset REPL sessions.
|
| 150 |
+
|
| 151 |
+
**Parameters**:
|
| 152 |
+
- `contextId` (optional): Specific session to reset
|
| 153 |
+
|
| 154 |
+
**Behavior**:
|
| 155 |
+
- With contextId: Reset specific session
|
| 156 |
+
- Without contextId: Reset all sessions
|
| 157 |
+
|
| 158 |
+
**Returns**: `{ success, message, sessionsCleared }`
|
| 159 |
+
|
| 160 |
+
### LoadNuGetPackage
|
| 161 |
+
|
| 162 |
+
Load a NuGet package for use in utility programs.
|
| 163 |
+
|
| 164 |
+
**Parameters**:
|
| 165 |
+
- `packageName`: Package ID
|
| 166 |
+
- `version` (optional): Specific version (omit for latest stable)
|
| 167 |
+
|
| 168 |
+
**Use Cases**: Add functionality to single-file utility programs (JSON processing, HTTP clients, etc.)
|
| 169 |
+
|
| 170 |
+
### GetReplInfo
|
| 171 |
+
|
| 172 |
+
Get current execution environment information and capabilities for building file-based C# apps.
|
| 173 |
+
|
| 174 |
+
**Parameters**:
|
| 175 |
+
- `contextId` (optional): Session context for session-specific information
|
| 176 |
+
|
| 177 |
+
**Returns**: `{ frameworkVersion, language, state, activeSessionCount, contextId, isSessionSpecific, defaultImports, capabilities, tips, examples, sessionMetadata }`
|
| 178 |
+
|
| 179 |
+
**Use Cases**:
|
| 180 |
+
- Understand environment capabilities (.NET 10, C# 14)
|
| 181 |
+
- Check active session count
|
| 182 |
+
- Get tips for creating utility programs (includes `nugetPackages` guidance)
|
| 183 |
+
- Access example code patterns (includes package loading example)
|
| 184 |
+
- Learn about file-based app workflow (REPL testing → `#:package` finalization)
|
| 185 |
+
|
| 186 |
+
### SearchNuGetPackages
|
| 187 |
+
|
| 188 |
+
Search for NuGet packages (Tool alternative to `nuget://search` resource).
|
| 189 |
+
|
| 190 |
+
**Parameters**:
|
| 191 |
+
- `query`: Search query
|
| 192 |
+
- `skip` (optional): Pagination offset (default: 0)
|
| 193 |
+
- `take` (optional): Results to return (default: 20, max: 100)
|
| 194 |
+
|
| 195 |
+
**Returns**: `{ packages, totalCount, query, skip, take }`
|
| 196 |
+
|
| 197 |
+
**Use Cases**: For clients that don't support MCP resources
|
| 198 |
+
|
| 199 |
+
### GetNuGetPackageVersions
|
| 200 |
+
|
| 201 |
+
Get all versions of a NuGet package (Tool alternative to `nuget://packages/{id}/versions` resource).
|
| 202 |
+
|
| 203 |
+
**Parameters**:
|
| 204 |
+
- `packageId`: Package ID to query
|
| 205 |
+
|
| 206 |
+
**Returns**: `{ found, packageId, versions, totalCount }`
|
| 207 |
+
|
| 208 |
+
**Use Cases**: For clients that don't support MCP resources
|
| 209 |
+
|
| 210 |
+
### GetNuGetPackageReadme
|
| 211 |
+
|
| 212 |
+
Get README content for a NuGet package (Tool alternative to `nuget://packages/{id}/readme` resource).
|
| 213 |
+
|
| 214 |
+
**Parameters**:
|
| 215 |
+
- `packageId`: Package ID
|
| 216 |
+
- `version` (optional): Specific version (omit for latest)
|
| 217 |
+
|
| 218 |
+
**Returns**: `{ found, packageId, version, content }`
|
| 219 |
+
|
| 220 |
+
**Use Cases**: For clients that don't support MCP resources
|
| 221 |
+
|
| 222 |
+
### GetDocumentation
|
| 223 |
+
|
| 224 |
+
Get XML documentation for .NET types/methods (Tool alternative to `doc://` resource).
|
| 225 |
+
|
| 226 |
+
**Parameters**:
|
| 227 |
+
- `symbolName`: Fully qualified type or method name
|
| 228 |
+
- `packageId` (optional): NuGet package ID for package-specific types
|
| 229 |
+
|
| 230 |
+
**Returns**: `{ found, symbolName, summary, remarks, parameters, returns, exceptions, example }`
|
| 231 |
+
|
| 232 |
+
**Use Cases**: For clients that don't support MCP resources
|
| 233 |
+
|
| 234 |
+
**Examples**:
|
| 235 |
+
- GetDocumentation("System.String")
|
| 236 |
+
- GetDocumentation("JsonConvert", "Newtonsoft.Json")
|
| 237 |
+
|
| 238 |
+
## Context Management
|
| 239 |
+
|
| 240 |
+
### IReplContextManager
|
| 241 |
+
|
| 242 |
+
Interface for managing REPL session lifecycle.
|
| 243 |
+
|
| 244 |
+
**Key Methods**:
|
| 245 |
+
- `CreateContext()`: Create new session, return GUID
|
| 246 |
+
- `ContextExists(contextId)`: Check if session exists
|
| 247 |
+
- `GetContextState(contextId)`: Retrieve script state
|
| 248 |
+
- `UpdateContextState(contextId, state)`: Update script state
|
| 249 |
+
- `RemoveContext(contextId)`: Delete session
|
| 250 |
+
- `CleanupExpiredContexts()`: Remove old sessions
|
| 251 |
+
|
| 252 |
+
### ReplContextManager
|
| 253 |
+
|
| 254 |
+
Thread-safe implementation using `ConcurrentDictionary`.
|
| 255 |
+
|
| 256 |
+
**Features**:
|
| 257 |
+
- Configurable timeout (default: 30 minutes)
|
| 258 |
+
- Automatic cleanup of expired sessions
|
| 259 |
+
- Metadata tracking (creation time, last access, execution count)
|
| 260 |
+
- Thread-safe concurrent operations
|
| 261 |
+
|
| 262 |
+
### Context Metadata
|
| 263 |
+
|
| 264 |
+
Tracks session information:
|
| 265 |
+
- `ContextId`: Unique identifier (GUID)
|
| 266 |
+
- `CreatedAt`: Session creation timestamp
|
| 267 |
+
- `LastAccessedAt`: Last activity timestamp
|
| 268 |
+
- `ExecutionCount`: Number of executions
|
| 269 |
+
- `IsInitialized`: Whether code has been executed
|
| 270 |
+
|
| 271 |
+
## Prompts (Optimized Templates)
|
| 272 |
+
|
| 273 |
+
Prompts guide LLMs in creating single-file C# utility programs. All prompts focus on file-based app patterns.
|
| 274 |
+
|
| 275 |
+
### Available Prompts
|
| 276 |
+
|
| 277 |
+
1. **QuickStartRepl** (150 tokens): Quick start for creating single-file utilities
|
| 278 |
+
2. **GetStartedWithCsharpRepl** (800 tokens): Comprehensive guide to file-based C# apps with complete examples
|
| 279 |
+
3. **DebugCompilationErrors** (250 tokens): Error handling workflow
|
| 280 |
+
4. **ReplBestPractices** (600 tokens): Best practices for creating utility programs with complete examples
|
| 281 |
+
5. **WorkingWithPackages** (400 tokens): Using NuGet packages in utility programs
|
| 282 |
+
6. **PackageIntegrationGuide** (700 tokens): Detailed package integration with 4 complete utility examples
|
| 283 |
+
|
| 284 |
+
### Prompt Design Principles
|
| 285 |
+
|
| 286 |
+
- **File-based focus**: Guide LLMs to create complete, runnable .cs files with .NET 10 syntax
|
| 287 |
+
- **Top-level statements**: Emphasize simple, boilerplate-free code
|
| 288 |
+
- **`#:package` directive**: Show self-contained apps with inline package declarations
|
| 289 |
+
- **`#:sdk` directive**: Demonstrate specialized SDKs (e.g., web apps)
|
| 290 |
+
- **Complete examples**: Show full utility programs, not just snippets
|
| 291 |
+
- **Resource-driven**: Reference doc://, nuget:// for API lookup
|
| 292 |
+
- **Context-aware**: Document optional contextId for iterative development
|
| 293 |
+
- **nugetPackages parameter**: Show testing workflow with inline package loading
|
| 294 |
+
- **Two-phase workflow**: Test with `nugetPackages` → Finalize with `#:package`
|
| 295 |
+
- **Workflow-focused**: Resource query → Build → Test → Refine
|
| 296 |
+
- **Practical patterns**: Real-world utility examples (file processing, HTTP clients, etc.)
|
| 297 |
+
|
| 298 |
+
## Workflow Patterns
|
| 299 |
+
|
| 300 |
+
### Creating a Simple Utility
|
| 301 |
+
|
| 302 |
+
```
|
| 303 |
+
1. Access doc://System.IO.File (learn API)
|
| 304 |
+
2. ValidateCsharp(code) (check syntax)
|
| 305 |
+
3. EvaluateCsharp(code) (test program)
|
| 306 |
+
4. → Complete utility.cs file
|
| 307 |
+
```
|
| 308 |
+
|
| 309 |
+
### Building with Packages (Recommended Workflow)
|
| 310 |
+
|
| 311 |
+
**Testing Phase:**
|
| 312 |
+
```
|
| 313 |
+
1. Access nuget://search?q=json (find package)
|
| 314 |
+
2. Access nuget://packages/Newtonsoft.Json/readme (read docs)
|
| 315 |
+
3. EvaluateCsharp(code, nugetPackages: [{packageName: "Newtonsoft.Json", version: "13.0.3"}])
|
| 316 |
+
(test with inline package loading)
|
| 317 |
+
4. Iterate and refine logic
|
| 318 |
+
```
|
| 319 |
+
|
| 320 |
+
**Finalization Phase:**
|
| 321 |
+
```
|
| 322 |
+
5. Generate final utility.cs with #:package directive:
|
| 323 |
+
#:package [email protected]
|
| 324 |
+
using Newtonsoft.Json;
|
| 325 |
+
// ... complete utility code ...
|
| 326 |
+
6. → Self-contained json-processor.cs (no .csproj needed!)
|
| 327 |
+
7. Run with: dotnet run json-processor.cs
|
| 328 |
+
```
|
| 329 |
+
|
| 330 |
+
**Alternative (Legacy):**
|
| 331 |
+
```
|
| 332 |
+
1. Access nuget://search?q=json (find package)
|
| 333 |
+
2. LoadNuGetPackage("Newtonsoft.Json") (load into global context)
|
| 334 |
+
3. EvaluateCsharp(code) (test)
|
| 335 |
+
4. → Generate utility.cs (requires .csproj or LoadNuGetPackage for execution)
|
| 336 |
+
```
|
| 337 |
+
|
| 338 |
+
### Iterative Development
|
| 339 |
+
|
| 340 |
+
```
|
| 341 |
+
1. EvaluateCsharp(initial code, createContext: true) → get contextId
|
| 342 |
+
2. ValidateCsharp(next code, contextId) → check additions
|
| 343 |
+
3. EvaluateCsharp(next code, contextId) → test incremental changes
|
| 344 |
+
4. Repeat steps 2-3 as needed
|
| 345 |
+
5. → Final complete utility program
|
| 346 |
+
```
|
| 347 |
+
|
| 348 |
+
## Design Decisions
|
| 349 |
+
|
| 350 |
+
### Why Focus on File-Based Apps?
|
| 351 |
+
|
| 352 |
+
- **Simplicity**: No project files, build configuration, or boilerplate code
|
| 353 |
+
- **Self-contained**: `#:package` directive eliminates need for .csproj
|
| 354 |
+
- **Quick utilities**: Perfect for creating small, focused programs
|
| 355 |
+
- **LLM-friendly**: Clear goal (complete .cs file) vs. open-ended REPL experimentation
|
| 356 |
+
- **Real-world use**: Aligns with .NET 10's `dotnet run app.cs` feature
|
| 357 |
+
- **Completeness**: Guides toward finished programs, not code snippets
|
| 358 |
+
- **Modern syntax**: Leverages .NET 10 directives for project-less development
|
| 359 |
+
|
| 360 |
+
### Why Resources?
|
| 361 |
+
|
| 362 |
+
- **Efficiency**: Read-only data access without execution overhead
|
| 363 |
+
- **Discoverability**: LLMs can explore available data
|
| 364 |
+
- **Caching**: Resources can be cached by clients
|
| 365 |
+
- **Separation**: Clear distinction between data (Resources) and operations (Tools)
|
| 366 |
+
|
| 367 |
+
### Why Context Management?
|
| 368 |
+
|
| 369 |
+
- **Iterative development**: Build utility programs step by step
|
| 370 |
+
- **Isolation**: Multiple concurrent sessions without interference
|
| 371 |
+
- **Flexibility**: Support both single-shot and iterative development
|
| 372 |
+
- **Lifecycle**: Automatic cleanup prevents resource leaks
|
| 373 |
+
|
| 374 |
+
### Why Optimize Prompts?
|
| 375 |
+
|
| 376 |
+
- **Clear guidance**: Help LLMs create complete, runnable programs
|
| 377 |
+
- **Token efficiency**: Focus on essential patterns and examples
|
| 378 |
+
- **Practical examples**: Show real utility programs, not toy snippets
|
| 379 |
+
- **Resource-driven**: Leverage Resources for API docs instead of embedding
|
| 380 |
+
- **Goal-oriented**: Guide toward finished .cs files
|
| 381 |
+
|
| 382 |
+
## Performance Considerations
|
| 383 |
+
|
| 384 |
+
### Resource Caching
|
| 385 |
+
|
| 386 |
+
- Documentation lookups cached by symbol name
|
| 387 |
+
- NuGet queries cached by search terms
|
| 388 |
+
- Package metadata cached by ID
|
| 389 |
+
|
| 390 |
+
### Context Cleanup
|
| 391 |
+
|
| 392 |
+
- Automatic background cleanup every X minutes
|
| 393 |
+
- Configurable timeout (default: 30 minutes)
|
| 394 |
+
- Manual cleanup via ResetRepl
|
| 395 |
+
|
| 396 |
+
### Concurrent Sessions
|
| 397 |
+
|
| 398 |
+
- Thread-safe ConcurrentDictionary
|
| 399 |
+
- No lock contention for independent sessions
|
| 400 |
+
- Isolation prevents cross-session interference
|
| 401 |
+
|
| 402 |
+
## Testing
|
| 403 |
+
|
| 404 |
+
### Test Coverage
|
| 405 |
+
|
| 406 |
+
- **Resources**: 12 tests covering URI parsing, error handling, response structure
|
| 407 |
+
- **Context Management**: 19 tests covering lifecycle, thread safety, cleanup
|
| 408 |
+
- **Tools**: 11 integration tests updated for context-aware API
|
| 409 |
+
- **Total**: 580+ new assertions, 134 tests passing
|
| 410 |
+
|
| 411 |
+
### Test Categories
|
| 412 |
+
|
| 413 |
+
- Unit tests: Components in isolation
|
| 414 |
+
- Integration tests: Tool → Service → Context interactions
|
| 415 |
+
- Resource tests: URI handling and response validation
|
| 416 |
+
- Thread safety tests: Concurrent context operations
|
| 417 |
+
|
| 418 |
+
## Future Enhancements
|
| 419 |
+
|
| 420 |
+
### Potential Additions
|
| 421 |
+
|
| 422 |
+
1. **Context persistence**: Save/restore sessions across server restarts
|
| 423 |
+
2. **Context limits**: Enforce maximum context count/size
|
| 424 |
+
3. **Context listing resource**: `repl://sessions` to enumerate all
|
| 425 |
+
4. **Package documentation resource**: Loaded package type docs
|
| 426 |
+
5. **Execution history resource**: Per-session execution log
|
| 427 |
+
|
| 428 |
+
### Optimization Opportunities
|
| 429 |
+
|
| 430 |
+
1. Lazy loading for Resources
|
| 431 |
+
2. Response streaming for large results
|
| 432 |
+
3. Incremental compilation caching
|
| 433 |
+
4. NuGet package preloading
|
| 434 |
+
|
| 435 |
+
## Recommended Pairing: Microsoft Learn MCP
|
| 436 |
+
|
| 437 |
+
For optimal C# utility program development, **Roslyn-Stone pairs excellently with the Microsoft Learn MCP server** ([github.com/microsoftdocs/mcp](https://github.com/microsoftdocs/mcp)).
|
| 438 |
+
|
| 439 |
+
**Complementary capabilities:**
|
| 440 |
+
- **Roslyn-Stone**: C# code execution, validation, package loading, REPL testing
|
| 441 |
+
- **Microsoft Learn MCP**: Official .NET documentation, API references, code samples from Microsoft Learn
|
| 442 |
+
|
| 443 |
+
**Combined workflow:**
|
| 444 |
+
1. **Search docs** (Microsoft Learn) → **Find APIs**
|
| 445 |
+
2. **Get code samples** (Microsoft Learn) → **Test with packages** (Roslyn-Stone)
|
| 446 |
+
3. **Look up types** (both) → **Execute and validate** (Roslyn-Stone)
|
| 447 |
+
4. **Build utility** with official guidance + live testing
|
| 448 |
+
|
| 449 |
+
This combination provides comprehensive documentation alongside live execution for the ultimate utility program development experience.
|
| 450 |
+
|
| 451 |
+
## References
|
| 452 |
+
|
| 453 |
+
- Model Context Protocol: https://modelcontextprotocol.io
|
| 454 |
+
- MCP C# SDK: https://github.com/modelcontextprotocol/csharp-sdk
|
| 455 |
+
- Microsoft Learn MCP: https://github.com/microsoftdocs/mcp (recommended pairing)
|
| 456 |
+
- Roslyn Scripting: https://github.com/dotnet/roslyn/wiki/Scripting-API-Samples
|
| 457 |
+
- .NET 10 File-Based Apps: https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/
|
README.md
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Roslyn Stone
|
| 3 |
+
emoji: 🪨
|
| 4 |
+
colorFrom: gray
|
| 5 |
+
colorTo: blue
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
+
pinned: true
|
| 9 |
+
tags:
|
| 10 |
+
- mcp
|
| 11 |
+
- csharp
|
| 12 |
+
- roslyn
|
| 13 |
+
- repl
|
| 14 |
+
- building-mcp-track-enterprise
|
| 15 |
+
short_description: LLM-friendly C# sandbox for creating single-file programs
|
| 16 |
+
space_sdk: docker
|
| 17 |
+
---
|
| 18 |
+
|
| 19 |
+
# Roslyn-Stone
|
| 20 |
+
|
| 21 |
+
> **Note**: This project was collaboratively built with GitHub Copilot, embracing the future of AI-assisted development.
|
| 22 |
+
|
| 23 |
+
A developer- and LLM-friendly C# sandbox for creating single-file utility programs through the Model Context Protocol (MCP). Named as a playful nod to the Rosetta Stone—the ancient artifact that helped decode languages—Roslyn-Stone helps AI systems create runnable C# programs using file-based apps (top-level statements).
|
| 24 |
+
|
| 25 |
+
Build complete, runnable .cs files using the power of the Roslyn compiler. Execute C# code, validate syntax, load NuGet packages, and lookup documentation through MCP for seamless integration with Claude Code, VS Code, and other AI-powered development tools.
|
| 26 |
+
|
| 27 |
+
Perfect for creating command-line utilities, data processing scripts, automation tools, and quick C# programs without project scaffolding.
|
| 28 |
+
|
| 29 |
+
## Quick Start with Docker
|
| 30 |
+
|
| 31 |
+
The fastest way to get started is using the pre-built Docker container with your favorite MCP-enabled editor:
|
| 32 |
+
|
| 33 |
+
### Claude Code / VS Code / Cursor
|
| 34 |
+
|
| 35 |
+
Add this to your MCP configuration file:
|
| 36 |
+
|
| 37 |
+
**Linux/macOS**: `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
|
| 38 |
+
**Windows**: `%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json`
|
| 39 |
+
|
| 40 |
+
```json
|
| 41 |
+
{
|
| 42 |
+
"mcpServers": {
|
| 43 |
+
"roslyn-stone": {
|
| 44 |
+
"command": "docker",
|
| 45 |
+
"args": [
|
| 46 |
+
"run",
|
| 47 |
+
"-i",
|
| 48 |
+
"--rm",
|
| 49 |
+
"-e", "DOTNET_USE_POLLING_FILE_WATCHER=1",
|
| 50 |
+
"ghcr.io/dylanlangston/roslyn-stone:latest"
|
| 51 |
+
]
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
}
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
**Using Podman?** Just replace `"docker"` with `"podman"` in the command field.
|
| 58 |
+
|
| 59 |
+
### Claude Desktop
|
| 60 |
+
|
| 61 |
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or equivalent:
|
| 62 |
+
|
| 63 |
+
```json
|
| 64 |
+
{
|
| 65 |
+
"mcpServers": {
|
| 66 |
+
"roslyn-stone": {
|
| 67 |
+
"command": "docker",
|
| 68 |
+
"args": [
|
| 69 |
+
"run",
|
| 70 |
+
"-i",
|
| 71 |
+
"--rm",
|
| 72 |
+
"-e", "DOTNET_USE_POLLING_FILE_WATCHER=1",
|
| 73 |
+
"ghcr.io/dylanlangston/roslyn-stone:latest"
|
| 74 |
+
]
|
| 75 |
+
}
|
| 76 |
+
}
|
| 77 |
+
}
|
| 78 |
+
```
|
| 79 |
+
|
| 80 |
+
That's it! The container provides isolated execution of C# code with minimal setup and enhanced security.
|
| 81 |
+
|
| 82 |
+
## Features
|
| 83 |
+
|
| 84 |
+
**File-Based C# Apps** - Create single-file utilities using top-level statements aligned with .NET 10's `dotnet run app.cs` feature
|
| 85 |
+
**Inline Package Loading** - Use `nugetPackages` parameter to load packages during testing, finalize with `#:package` directive for self-contained apps
|
| 86 |
+
**C# Execution via Roslyn** - Execute and test C# code with full .NET 10 support and recursive NuGet dependency resolution
|
| 87 |
+
**Iterative Development** - Build programs incrementally with optional stateful sessions and context management
|
| 88 |
+
**Real-time Error Feedback** - Get detailed compilation errors and warnings with actionable messages
|
| 89 |
+
**Resources & Tools** - Proper MCP separation: Resources (data) vs Tools (operations)
|
| 90 |
+
**Documentation Access** - Query .NET type/method docs via `doc://` resource URIs (includes core SDK types like System.String)
|
| 91 |
+
**NuGet Integration** - Search packages via `nuget://` resources, load with tools, full dependency tree resolution
|
| 92 |
+
**MCP Protocol** - Official ModelContextProtocol SDK with stdio and HTTP transports
|
| 93 |
+
**Dual Transport** - Support for both stdio (local) and HTTP (remote) MCP connections
|
| 94 |
+
**AI-Friendly** - Designed for LLM interactions via Model Context Protocol
|
| 95 |
+
**Token-Optimized Prompts** - Efficient guidance for creating utility programs with .NET 10 directive examples
|
| 96 |
+
**Containerized** - Docker support with .NET Aspire orchestration
|
| 97 |
+
**OpenTelemetry** - Built-in observability with logs, metrics, and traces
|
| 98 |
+
|
| 99 |
+
## Architecture
|
| 100 |
+
|
| 101 |
+
Roslyn-Stone implements the Model Context Protocol (MCP) to help LLMs create single-file C# utility programs. It follows best practices by properly distinguishing between:
|
| 102 |
+
|
| 103 |
+
- **Resources** (passive data sources): URI-based read-only access to documentation (`doc://`), NuGet packages (`nuget://`), and execution state (`repl://`)
|
| 104 |
+
- **Tools** (active operations): Code execution, validation, and package loading for building utility programs
|
| 105 |
+
- **Prompts** (optimized templates): Token-efficient guidance for creating file-based C# apps
|
| 106 |
+
|
| 107 |
+
The solution follows clean architecture principles with functional programming patterns. It implements best practices for dynamic code compilation and execution, including proper AssemblyLoadContext usage for memory management.
|
| 108 |
+
|
| 109 |
+
### Key Components
|
| 110 |
+
|
| 111 |
+
- **Context Management**: Thread-safe session lifecycle with automatic cleanup (30min timeout)
|
| 112 |
+
- **Stateful Execution**: Variables and types persist within sessions for iterative development
|
| 113 |
+
- **Resource Discovery**: Query docs, packages, and state before execution for efficient workflows
|
| 114 |
+
- **Token Optimization**: Prompts guide LLMs to create complete, runnable .cs files
|
| 115 |
+
|
| 116 |
+
See `MCP_ARCHITECTURE.md` for detailed design documentation and `DYNAMIC_COMPILATION_BEST_PRACTICES.md` for compilation details.
|
| 117 |
+
|
| 118 |
+
```
|
| 119 |
+
RoslynStone/
|
| 120 |
+
├── src/
|
| 121 |
+
│ ├── RoslynStone.Api/ # Console Host with MCP Server
|
| 122 |
+
│ ├── RoslynStone.Core/ # Domain models (ExecutionResult, PackageMetadata, etc.)
|
| 123 |
+
│ ├── RoslynStone.Infrastructure/ # MCP Tools, Roslyn services, functional helpers
|
| 124 |
+
│ ├── RoslynStone.ServiceDefaults/# OpenTelemetry and Aspire defaults
|
| 125 |
+
│ └── RoslynStone.AppHost/ # Aspire orchestration
|
| 126 |
+
└── tests/
|
| 127 |
+
├── RoslynStone.Tests/ # xUnit unit and integration tests
|
| 128 |
+
├── RoslynStone.Benchmarks/ # BenchmarkDotNet performance tests
|
| 129 |
+
└── RoslynStone.LoadTests/ # HTTP load and concurrency tests
|
| 130 |
+
```
|
| 131 |
+
|
| 132 |
+
### Architecture Principles
|
| 133 |
+
|
| 134 |
+
- **Functional Programming**: Leverages LINQ, pure functions, and functional composition
|
| 135 |
+
- **Direct Service Calls**: MCP Tools call services directly without abstraction layers
|
| 136 |
+
- **Thread Safety**: Static and instance-level synchronization for reliable parallel execution
|
| 137 |
+
- **Immutable Models**: Uses records and readonly properties where appropriate
|
| 138 |
+
|
| 139 |
+
### MCP Resources (Read-Only Data Access)
|
| 140 |
+
|
| 141 |
+
Resources provide URI-based access to passive data sources:
|
| 142 |
+
|
| 143 |
+
- **`doc://{symbolName}`** - .NET XML documentation lookup
|
| 144 |
+
- Example: `doc://System.String`, `doc://System.Linq.Enumerable.Select`
|
| 145 |
+
- **New:** Supports NuGet packages: `doc://{packageId}@{symbolName}`
|
| 146 |
+
- Example: `doc://[email protected]`
|
| 147 |
+
- **`nuget://search?q={query}`** - Search NuGet packages
|
| 148 |
+
- Example: `nuget://search?q=json&take=10`
|
| 149 |
+
- **`nuget://packages/{id}/versions`** - Get package version list
|
| 150 |
+
- Example: `nuget://packages/Newtonsoft.Json/versions`
|
| 151 |
+
- **`nuget://packages/{id}/readme`** - Get package README
|
| 152 |
+
- Example: `nuget://packages/Newtonsoft.Json/readme?version=13.0.3`
|
| 153 |
+
- **`repl://state`** - General REPL information and capabilities
|
| 154 |
+
- **`repl://sessions`** - List active REPL sessions
|
| 155 |
+
- **`repl://sessions/{contextId}/state`** - Session-specific metadata
|
| 156 |
+
|
| 157 |
+
### MCP Tools (Active Operations)
|
| 158 |
+
|
| 159 |
+
Tools perform operations and can modify state. All tools support optional context management:
|
| 160 |
+
|
| 161 |
+
**Execution Tools:**
|
| 162 |
+
- **EvaluateCsharp** - Execute C# code to create and test single-file utility programs
|
| 163 |
+
- Optional `contextId` parameter for iterative development
|
| 164 |
+
- Returns `contextId` for session continuity
|
| 165 |
+
- **ValidateCsharp** - Validate C# syntax and semantics before execution
|
| 166 |
+
- Optional `contextId` for context-aware validation
|
| 167 |
+
- **ResetRepl** - Reset execution sessions
|
| 168 |
+
- Optional `contextId` to reset specific session or all sessions
|
| 169 |
+
- **GetReplInfo** - Get execution environment information and capabilities
|
| 170 |
+
- Optional `contextId` for session-specific state
|
| 171 |
+
- Returns framework version, capabilities, tips, and examples
|
| 172 |
+
|
| 173 |
+
**NuGet Tools:**
|
| 174 |
+
- **LoadNuGetPackage** - Load NuGet packages into REPL environment
|
| 175 |
+
- Packages persist in session until reset
|
| 176 |
+
- **SearchNuGetPackages** - Search for NuGet packages
|
| 177 |
+
- Parameters: `query`, `skip`, `take` for pagination
|
| 178 |
+
- Alternative to `nuget://search` resource for clients without resource support
|
| 179 |
+
- **GetNuGetPackageVersions** - Get all versions of a package
|
| 180 |
+
- Parameter: `packageId`
|
| 181 |
+
- Alternative to `nuget://packages/{id}/versions` resource
|
| 182 |
+
- **GetNuGetPackageReadme** - Get package README content
|
| 183 |
+
- Parameters: `packageId`, optional `version`
|
| 184 |
+
- Alternative to `nuget://packages/{id}/readme` resource
|
| 185 |
+
|
| 186 |
+
**Documentation Tools:**
|
| 187 |
+
- **GetDocumentation** - Get XML documentation for .NET types/methods
|
| 188 |
+
- Parameters: `symbolName`, optional `packageId`
|
| 189 |
+
- Alternative to `doc://` resource for clients without resource support
|
| 190 |
+
- Example: GetDocumentation("System.String") or GetDocumentation("JsonConvert", "Newtonsoft.Json")
|
| 191 |
+
|
| 192 |
+
> **Note:** Both Resources and Tools are provided for maximum client compatibility. Resources are preferred for passive data access, while Tools work in all MCP clients.
|
| 193 |
+
|
| 194 |
+
### MCP Prompts
|
| 195 |
+
|
| 196 |
+
Roslyn-Stone includes built-in prompts to help LLMs create single-file C# utility programs:
|
| 197 |
+
|
| 198 |
+
- **GetStartedWithCsharpRepl** - Comprehensive introduction to file-based C# apps, development workflow, and best practices
|
| 199 |
+
- **ReplBestPractices** - Patterns for creating single-file utilities with complete examples
|
| 200 |
+
- **WorkingWithPackages** - How to discover, evaluate, and use NuGet packages in utility programs
|
| 201 |
+
- **PackageIntegrationGuide** - Deep dive into package integration with detailed utility examples
|
| 202 |
+
|
| 203 |
+
These prompts provide detailed guidance on creating runnable .cs files, including examples, common patterns, and tips for success.
|
| 204 |
+
|
| 205 |
+
## What Can It Do?
|
| 206 |
+
|
| 207 |
+
Once configured, your AI assistant can help you create single-file C# utility programs aligned with .NET 10's file-based app feature:
|
| 208 |
+
|
| 209 |
+
**Build a simple utility:**
|
| 210 |
+
```
|
| 211 |
+
User: "Create a utility that lists files in the current directory"
|
| 212 |
+
Assistant: [Calls EvaluateCsharp with complete file-based app code]
|
| 213 |
+
→ Returns complete .cs file using top-level statements
|
| 214 |
+
→ Can be run directly with: dotnet run utility.cs
|
| 215 |
+
```
|
| 216 |
+
|
| 217 |
+
**Create self-contained apps with packages:**
|
| 218 |
+
```
|
| 219 |
+
User: "Create a JSON formatter utility"
|
| 220 |
+
Assistant:
|
| 221 |
+
1. [Searches packages: nuget://search?q=json]
|
| 222 |
+
2. [Tests with nugetPackages parameter: EvaluateCsharp(..., nugetPackages: [{packageName: "Newtonsoft.Json", version: "13.0.3"}])]
|
| 223 |
+
3. [Returns complete json-formatter.cs with #:package directive]
|
| 224 |
+
→ Complete self-contained app:
|
| 225 |
+
#:package [email protected]
|
| 226 |
+
using Newtonsoft.Json;
|
| 227 |
+
// ... utility code ...
|
| 228 |
+
→ Run with: dotnet run json-formatter.cs (no .csproj needed!)
|
| 229 |
+
```
|
| 230 |
+
|
| 231 |
+
**Query documentation for .NET types:**
|
| 232 |
+
```
|
| 233 |
+
User: "Show me how to use System.String.Split"
|
| 234 |
+
Assistant: [Option 1: Reads doc://System.String.Split resource]
|
| 235 |
+
[Option 2: Calls GetDocumentation("System.String.Split")]
|
| 236 |
+
→ Returns XML documentation with summary, parameters, examples
|
| 237 |
+
```
|
| 238 |
+
|
| 239 |
+
**Validate before execution:**
|
| 240 |
+
```
|
| 241 |
+
User: "Check if this C# code is valid: <code>"
|
| 242 |
+
Assistant: [Calls ValidateCsharp with code]
|
| 243 |
+
→ Returns syntax validation results with detailed diagnostics
|
| 244 |
+
```
|
| 245 |
+
|
| 246 |
+
**Iterative development with context:**
|
| 247 |
+
```
|
| 248 |
+
User: "Start a CSV processor utility"
|
| 249 |
+
Assistant: [EvaluateCsharp with createContext=true, nugetPackages: CsvHelper]
|
| 250 |
+
→ Returns contextId for session
|
| 251 |
+
User: "Add filtering by date"
|
| 252 |
+
Assistant: [EvaluateCsharp with contextId, adds filtering logic]
|
| 253 |
+
→ Builds incrementally on previous code
|
| 254 |
+
```
|
| 255 |
+
|
| 256 |
+
The AI assistant creates complete, runnable single-file C# programs using .NET 10's modern syntax—no class, Main method, or project file boilerplate needed.
|
| 257 |
+
|
| 258 |
+
## For Developers
|
| 259 |
+
|
| 260 |
+
Want to contribute, run from source, or learn more about the internals? Check out our comprehensive [Getting Started Guide](GETTING_STARTED.md) for:
|
| 261 |
+
|
| 262 |
+
- Building from source
|
| 263 |
+
- Development environment setup
|
| 264 |
+
- Running tests and benchmarks
|
| 265 |
+
- Adding new MCP tools
|
| 266 |
+
- Debugging with MCP Inspector
|
| 267 |
+
- Architecture details
|
| 268 |
+
|
| 269 |
+
## Security Considerations
|
| 270 |
+
|
| 271 |
+
⚠️ **Important**: This is a code execution service. Deploy with appropriate security measures:
|
| 272 |
+
|
| 273 |
+
- Run in isolated containers/sandboxes
|
| 274 |
+
- Implement rate limiting
|
| 275 |
+
- Add authentication/authorization
|
| 276 |
+
- Restrict network access
|
| 277 |
+
- Monitor resource usage
|
| 278 |
+
- Use read-only file systems where possible
|
| 279 |
+
|
| 280 |
+
## Future Enhancements
|
| 281 |
+
|
| 282 |
+
- [x] Full NuGet package resolution and loading
|
| 283 |
+
- [x] Docker container support
|
| 284 |
+
- [x] OpenTelemetry integration
|
| 285 |
+
- [ ] Persistent REPL sessions with user isolation
|
| 286 |
+
- [ ] Code snippet history and caching
|
| 287 |
+
- [ ] Syntax highlighting and IntelliSense data
|
| 288 |
+
- [ ] WebSocket support for interactive sessions
|
| 289 |
+
- [ ] Multi-architecture container images (amd64, arm64)
|
| 290 |
+
|
| 291 |
+
## Contributing
|
| 292 |
+
|
| 293 |
+
Contributions are welcome! Please see our [Getting Started Guide](GETTING_STARTED.md) for development setup instructions. Feel free to submit issues and pull requests.
|
| 294 |
+
|
| 295 |
+
## License
|
| 296 |
+
|
| 297 |
+
See [LICENSE](LICENSE) file for details.
|
| 298 |
+
|
| 299 |
+
## Recommended Pairing: Microsoft Learn MCP
|
| 300 |
+
|
| 301 |
+
For the best experience creating C# utility programs, **we recommend using Roslyn-Stone alongside the Microsoft Learn MCP server** ([github.com/microsoftdocs/mcp](https://github.com/microsoftdocs/mcp)).
|
| 302 |
+
|
| 303 |
+
**Why pair these tools?**
|
| 304 |
+
- **Roslyn-Stone**: Provides C# code execution, validation, and package loading for building utilities
|
| 305 |
+
- **Microsoft Learn MCP**: Provides comprehensive .NET documentation, API references, and code samples from official Microsoft Learn
|
| 306 |
+
|
| 307 |
+
**Together they enable:**
|
| 308 |
+
1. **Search official docs** (Microsoft Learn MCP) → **Find APIs** → **Test with C# code** (Roslyn-Stone)
|
| 309 |
+
2. **Get code samples** (Microsoft Learn) → **Execute and validate** (Roslyn-Stone) → **Build utility**
|
| 310 |
+
3. **Look up .NET types** (both) → **Understand usage** (Learn docs) → **Test implementation** (Roslyn-Stone)
|
| 311 |
+
|
| 312 |
+
**Example workflow:**
|
| 313 |
+
```
|
| 314 |
+
User: "Create a utility to parse JSON files"
|
| 315 |
+
Assistant:
|
| 316 |
+
1. [microsoft-learn MCP: Searches for JSON documentation]
|
| 317 |
+
2. [microsoft-learn MCP: Gets System.Text.Json code samples]
|
| 318 |
+
3. [roslyn-stone: Tests code with EvaluateCsharp]
|
| 319 |
+
4. [roslyn-stone: Returns complete json-parser.cs]
|
| 320 |
+
→ Best of both worlds: Official docs + live execution
|
| 321 |
+
```
|
| 322 |
+
|
| 323 |
+
**Setup both servers:**
|
| 324 |
+
```json
|
| 325 |
+
{
|
| 326 |
+
"mcpServers": {
|
| 327 |
+
"roslyn-stone": {
|
| 328 |
+
"command": "docker",
|
| 329 |
+
"args": ["run", "-i", "--rm", "ghcr.io/dylanlangston/roslyn-stone:latest"]
|
| 330 |
+
},
|
| 331 |
+
"microsoft-learn": {
|
| 332 |
+
"command": "npx",
|
| 333 |
+
"args": ["-y", "@microsoft/docs-mcp-server"]
|
| 334 |
+
}
|
| 335 |
+
}
|
| 336 |
+
}
|
| 337 |
+
```
|
| 338 |
+
|
| 339 |
+
This combination provides comprehensive .NET documentation alongside live C# execution for the ultimate utility program development experience.
|
| 340 |
+
|
| 341 |
+
## Learn More
|
| 342 |
+
|
| 343 |
+
- [Getting Started Guide](GETTING_STARTED.md) - Development and contribution guide
|
| 344 |
+
- [MCP Architecture](MCP_ARCHITECTURE.md) - Detailed design documentation for file-based C# apps
|
| 345 |
+
- [Model Context Protocol (MCP)](https://github.com/modelcontextprotocol/specification)
|
| 346 |
+
- [Microsoft Learn MCP](https://github.com/microsoftdocs/mcp) - Official .NET documentation MCP server (recommended pairing)
|
| 347 |
+
- [Roslyn Scripting APIs](https://learn.microsoft.com/en-us/archive/msdn-magazine/2016/january/essential-net-csharp-scripting)
|
| 348 |
+
- [Dynamic Compilation Best Practices](DYNAMIC_COMPILATION_BEST_PRACTICES.md)
|
| 349 |
+
- [.NET 10 File-Based Apps](https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/) - Official blog post about `dotnet run app.cs`
|
RoslynStone.sln
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Microsoft Visual Studio Solution File, Format Version 12.00
|
| 3 |
+
# Visual Studio Version 17
|
| 4 |
+
VisualStudioVersion = 17.0.31903.59
|
| 5 |
+
MinimumVisualStudioVersion = 10.0.40219.1
|
| 6 |
+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
|
| 7 |
+
EndProject
|
| 8 |
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynStone.Api", "src\RoslynStone.Api\RoslynStone.Api.csproj", "{925E18BC-C1D3-413B-ABE0-1921E905497D}"
|
| 9 |
+
EndProject
|
| 10 |
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynStone.Core", "src\RoslynStone.Core\RoslynStone.Core.csproj", "{5F9B7489-C19D-4779-BA31-F1414BE1B155}"
|
| 11 |
+
EndProject
|
| 12 |
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynStone.Infrastructure", "src\RoslynStone.Infrastructure\RoslynStone.Infrastructure.csproj", "{58E3C123-9602-4EAB-8925-46F8F2E882BA}"
|
| 13 |
+
EndProject
|
| 14 |
+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
|
| 15 |
+
EndProject
|
| 16 |
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynStone.Tests", "tests\RoslynStone.Tests\RoslynStone.Tests.csproj", "{6CA8EDF5-4A86-492C-B497-FF32807B7DB1}"
|
| 17 |
+
EndProject
|
| 18 |
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynStone.ServiceDefaults", "src\RoslynStone.ServiceDefaults\RoslynStone.ServiceDefaults.csproj", "{05255555-7027-416C-9ED7-C4BDB5A73402}"
|
| 19 |
+
EndProject
|
| 20 |
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynStone.AppHost", "src\RoslynStone.AppHost\RoslynStone.AppHost.csproj", "{CC117307-5F76-4BF3-8C06-66C0EE3C7C66}"
|
| 21 |
+
EndProject
|
| 22 |
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynStone.Benchmarks", "tests\RoslynStone.Benchmarks\RoslynStone.Benchmarks.csproj", "{875FCCDE-6AD5-48B5-A52F-A5C2D553C36B}"
|
| 23 |
+
EndProject
|
| 24 |
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynStone.LoadTests", "tests\RoslynStone.LoadTests\RoslynStone.LoadTests.csproj", "{2EFCC8E5-8008-45DE-82D2-E3EB2EC99C4D}"
|
| 25 |
+
EndProject
|
| 26 |
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynStone.AppHost.Tests", "tests\RoslynStone.AppHost.Tests\RoslynStone.AppHost.Tests.csproj", "{691AB6AF-4719-4BE4-95E3-E90D94D52A7B}"
|
| 27 |
+
EndProject
|
| 28 |
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynStone.GradioModule", "src\RoslynStone.GradioModule\RoslynStone.GradioModule.csproj", "{C1E093E3-0D6E-47EF-B428-0EDD042BEA9B}"
|
| 29 |
+
EndProject
|
| 30 |
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynStone.GradioTests", "tests\RoslynStone.GradioTests\RoslynStone.GradioTests.csproj", "{35F5E701-0A63-4B1F-87F2-FFFE09C0CBAD}"
|
| 31 |
+
EndProject
|
| 32 |
+
Global
|
| 33 |
+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
| 34 |
+
Debug|Any CPU = Debug|Any CPU
|
| 35 |
+
Debug|x64 = Debug|x64
|
| 36 |
+
Debug|x86 = Debug|x86
|
| 37 |
+
Release|Any CPU = Release|Any CPU
|
| 38 |
+
Release|x64 = Release|x64
|
| 39 |
+
Release|x86 = Release|x86
|
| 40 |
+
EndGlobalSection
|
| 41 |
+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
| 42 |
+
{925E18BC-C1D3-413B-ABE0-1921E905497D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
| 43 |
+
{925E18BC-C1D3-413B-ABE0-1921E905497D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
| 44 |
+
{925E18BC-C1D3-413B-ABE0-1921E905497D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
| 45 |
+
{925E18BC-C1D3-413B-ABE0-1921E905497D}.Debug|x64.Build.0 = Debug|Any CPU
|
| 46 |
+
{925E18BC-C1D3-413B-ABE0-1921E905497D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
| 47 |
+
{925E18BC-C1D3-413B-ABE0-1921E905497D}.Debug|x86.Build.0 = Debug|Any CPU
|
| 48 |
+
{925E18BC-C1D3-413B-ABE0-1921E905497D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
| 49 |
+
{925E18BC-C1D3-413B-ABE0-1921E905497D}.Release|Any CPU.Build.0 = Release|Any CPU
|
| 50 |
+
{925E18BC-C1D3-413B-ABE0-1921E905497D}.Release|x64.ActiveCfg = Release|Any CPU
|
| 51 |
+
{925E18BC-C1D3-413B-ABE0-1921E905497D}.Release|x64.Build.0 = Release|Any CPU
|
| 52 |
+
{925E18BC-C1D3-413B-ABE0-1921E905497D}.Release|x86.ActiveCfg = Release|Any CPU
|
| 53 |
+
{925E18BC-C1D3-413B-ABE0-1921E905497D}.Release|x86.Build.0 = Release|Any CPU
|
| 54 |
+
{5F9B7489-C19D-4779-BA31-F1414BE1B155}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
| 55 |
+
{5F9B7489-C19D-4779-BA31-F1414BE1B155}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
| 56 |
+
{5F9B7489-C19D-4779-BA31-F1414BE1B155}.Debug|x64.ActiveCfg = Debug|Any CPU
|
| 57 |
+
{5F9B7489-C19D-4779-BA31-F1414BE1B155}.Debug|x64.Build.0 = Debug|Any CPU
|
| 58 |
+
{5F9B7489-C19D-4779-BA31-F1414BE1B155}.Debug|x86.ActiveCfg = Debug|Any CPU
|
| 59 |
+
{5F9B7489-C19D-4779-BA31-F1414BE1B155}.Debug|x86.Build.0 = Debug|Any CPU
|
| 60 |
+
{5F9B7489-C19D-4779-BA31-F1414BE1B155}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
| 61 |
+
{5F9B7489-C19D-4779-BA31-F1414BE1B155}.Release|Any CPU.Build.0 = Release|Any CPU
|
| 62 |
+
{5F9B7489-C19D-4779-BA31-F1414BE1B155}.Release|x64.ActiveCfg = Release|Any CPU
|
| 63 |
+
{5F9B7489-C19D-4779-BA31-F1414BE1B155}.Release|x64.Build.0 = Release|Any CPU
|
| 64 |
+
{5F9B7489-C19D-4779-BA31-F1414BE1B155}.Release|x86.ActiveCfg = Release|Any CPU
|
| 65 |
+
{5F9B7489-C19D-4779-BA31-F1414BE1B155}.Release|x86.Build.0 = Release|Any CPU
|
| 66 |
+
{58E3C123-9602-4EAB-8925-46F8F2E882BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
| 67 |
+
{58E3C123-9602-4EAB-8925-46F8F2E882BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
| 68 |
+
{58E3C123-9602-4EAB-8925-46F8F2E882BA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
| 69 |
+
{58E3C123-9602-4EAB-8925-46F8F2E882BA}.Debug|x64.Build.0 = Debug|Any CPU
|
| 70 |
+
{58E3C123-9602-4EAB-8925-46F8F2E882BA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
| 71 |
+
{58E3C123-9602-4EAB-8925-46F8F2E882BA}.Debug|x86.Build.0 = Debug|Any CPU
|
| 72 |
+
{58E3C123-9602-4EAB-8925-46F8F2E882BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
| 73 |
+
{58E3C123-9602-4EAB-8925-46F8F2E882BA}.Release|Any CPU.Build.0 = Release|Any CPU
|
| 74 |
+
{58E3C123-9602-4EAB-8925-46F8F2E882BA}.Release|x64.ActiveCfg = Release|Any CPU
|
| 75 |
+
{58E3C123-9602-4EAB-8925-46F8F2E882BA}.Release|x64.Build.0 = Release|Any CPU
|
| 76 |
+
{58E3C123-9602-4EAB-8925-46F8F2E882BA}.Release|x86.ActiveCfg = Release|Any CPU
|
| 77 |
+
{58E3C123-9602-4EAB-8925-46F8F2E882BA}.Release|x86.Build.0 = Release|Any CPU
|
| 78 |
+
{6CA8EDF5-4A86-492C-B497-FF32807B7DB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
| 79 |
+
{6CA8EDF5-4A86-492C-B497-FF32807B7DB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
| 80 |
+
{6CA8EDF5-4A86-492C-B497-FF32807B7DB1}.Debug|x64.ActiveCfg = Debug|Any CPU
|
| 81 |
+
{6CA8EDF5-4A86-492C-B497-FF32807B7DB1}.Debug|x64.Build.0 = Debug|Any CPU
|
| 82 |
+
{6CA8EDF5-4A86-492C-B497-FF32807B7DB1}.Debug|x86.ActiveCfg = Debug|Any CPU
|
| 83 |
+
{6CA8EDF5-4A86-492C-B497-FF32807B7DB1}.Debug|x86.Build.0 = Debug|Any CPU
|
| 84 |
+
{6CA8EDF5-4A86-492C-B497-FF32807B7DB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
| 85 |
+
{6CA8EDF5-4A86-492C-B497-FF32807B7DB1}.Release|Any CPU.Build.0 = Release|Any CPU
|
| 86 |
+
{6CA8EDF5-4A86-492C-B497-FF32807B7DB1}.Release|x64.ActiveCfg = Release|Any CPU
|
| 87 |
+
{6CA8EDF5-4A86-492C-B497-FF32807B7DB1}.Release|x64.Build.0 = Release|Any CPU
|
| 88 |
+
{6CA8EDF5-4A86-492C-B497-FF32807B7DB1}.Release|x86.ActiveCfg = Release|Any CPU
|
| 89 |
+
{6CA8EDF5-4A86-492C-B497-FF32807B7DB1}.Release|x86.Build.0 = Release|Any CPU
|
| 90 |
+
{05255555-7027-416C-9ED7-C4BDB5A73402}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
| 91 |
+
{05255555-7027-416C-9ED7-C4BDB5A73402}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
| 92 |
+
{05255555-7027-416C-9ED7-C4BDB5A73402}.Debug|x64.ActiveCfg = Debug|Any CPU
|
| 93 |
+
{05255555-7027-416C-9ED7-C4BDB5A73402}.Debug|x64.Build.0 = Debug|Any CPU
|
| 94 |
+
{05255555-7027-416C-9ED7-C4BDB5A73402}.Debug|x86.ActiveCfg = Debug|Any CPU
|
| 95 |
+
{05255555-7027-416C-9ED7-C4BDB5A73402}.Debug|x86.Build.0 = Debug|Any CPU
|
| 96 |
+
{05255555-7027-416C-9ED7-C4BDB5A73402}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
| 97 |
+
{05255555-7027-416C-9ED7-C4BDB5A73402}.Release|Any CPU.Build.0 = Release|Any CPU
|
| 98 |
+
{05255555-7027-416C-9ED7-C4BDB5A73402}.Release|x64.ActiveCfg = Release|Any CPU
|
| 99 |
+
{05255555-7027-416C-9ED7-C4BDB5A73402}.Release|x64.Build.0 = Release|Any CPU
|
| 100 |
+
{05255555-7027-416C-9ED7-C4BDB5A73402}.Release|x86.ActiveCfg = Release|Any CPU
|
| 101 |
+
{05255555-7027-416C-9ED7-C4BDB5A73402}.Release|x86.Build.0 = Release|Any CPU
|
| 102 |
+
{CC117307-5F76-4BF3-8C06-66C0EE3C7C66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
| 103 |
+
{CC117307-5F76-4BF3-8C06-66C0EE3C7C66}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
| 104 |
+
{CC117307-5F76-4BF3-8C06-66C0EE3C7C66}.Debug|x64.ActiveCfg = Debug|Any CPU
|
| 105 |
+
{CC117307-5F76-4BF3-8C06-66C0EE3C7C66}.Debug|x64.Build.0 = Debug|Any CPU
|
| 106 |
+
{CC117307-5F76-4BF3-8C06-66C0EE3C7C66}.Debug|x86.ActiveCfg = Debug|Any CPU
|
| 107 |
+
{CC117307-5F76-4BF3-8C06-66C0EE3C7C66}.Debug|x86.Build.0 = Debug|Any CPU
|
| 108 |
+
{CC117307-5F76-4BF3-8C06-66C0EE3C7C66}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
| 109 |
+
{CC117307-5F76-4BF3-8C06-66C0EE3C7C66}.Release|Any CPU.Build.0 = Release|Any CPU
|
| 110 |
+
{CC117307-5F76-4BF3-8C06-66C0EE3C7C66}.Release|x64.ActiveCfg = Release|Any CPU
|
| 111 |
+
{CC117307-5F76-4BF3-8C06-66C0EE3C7C66}.Release|x64.Build.0 = Release|Any CPU
|
| 112 |
+
{CC117307-5F76-4BF3-8C06-66C0EE3C7C66}.Release|x86.ActiveCfg = Release|Any CPU
|
| 113 |
+
{CC117307-5F76-4BF3-8C06-66C0EE3C7C66}.Release|x86.Build.0 = Release|Any CPU
|
| 114 |
+
{875FCCDE-6AD5-48B5-A52F-A5C2D553C36B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
| 115 |
+
{875FCCDE-6AD5-48B5-A52F-A5C2D553C36B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
| 116 |
+
{875FCCDE-6AD5-48B5-A52F-A5C2D553C36B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
| 117 |
+
{875FCCDE-6AD5-48B5-A52F-A5C2D553C36B}.Debug|x64.Build.0 = Debug|Any CPU
|
| 118 |
+
{875FCCDE-6AD5-48B5-A52F-A5C2D553C36B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
| 119 |
+
{875FCCDE-6AD5-48B5-A52F-A5C2D553C36B}.Debug|x86.Build.0 = Debug|Any CPU
|
| 120 |
+
{875FCCDE-6AD5-48B5-A52F-A5C2D553C36B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
| 121 |
+
{875FCCDE-6AD5-48B5-A52F-A5C2D553C36B}.Release|Any CPU.Build.0 = Release|Any CPU
|
| 122 |
+
{875FCCDE-6AD5-48B5-A52F-A5C2D553C36B}.Release|x64.ActiveCfg = Release|Any CPU
|
| 123 |
+
{875FCCDE-6AD5-48B5-A52F-A5C2D553C36B}.Release|x64.Build.0 = Release|Any CPU
|
| 124 |
+
{875FCCDE-6AD5-48B5-A52F-A5C2D553C36B}.Release|x86.ActiveCfg = Release|Any CPU
|
| 125 |
+
{875FCCDE-6AD5-48B5-A52F-A5C2D553C36B}.Release|x86.Build.0 = Release|Any CPU
|
| 126 |
+
{2EFCC8E5-8008-45DE-82D2-E3EB2EC99C4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
| 127 |
+
{2EFCC8E5-8008-45DE-82D2-E3EB2EC99C4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
| 128 |
+
{2EFCC8E5-8008-45DE-82D2-E3EB2EC99C4D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
| 129 |
+
{2EFCC8E5-8008-45DE-82D2-E3EB2EC99C4D}.Debug|x64.Build.0 = Debug|Any CPU
|
| 130 |
+
{2EFCC8E5-8008-45DE-82D2-E3EB2EC99C4D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
| 131 |
+
{2EFCC8E5-8008-45DE-82D2-E3EB2EC99C4D}.Debug|x86.Build.0 = Debug|Any CPU
|
| 132 |
+
{2EFCC8E5-8008-45DE-82D2-E3EB2EC99C4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
| 133 |
+
{2EFCC8E5-8008-45DE-82D2-E3EB2EC99C4D}.Release|Any CPU.Build.0 = Release|Any CPU
|
| 134 |
+
{2EFCC8E5-8008-45DE-82D2-E3EB2EC99C4D}.Release|x64.ActiveCfg = Release|Any CPU
|
| 135 |
+
{2EFCC8E5-8008-45DE-82D2-E3EB2EC99C4D}.Release|x64.Build.0 = Release|Any CPU
|
| 136 |
+
{2EFCC8E5-8008-45DE-82D2-E3EB2EC99C4D}.Release|x86.ActiveCfg = Release|Any CPU
|
| 137 |
+
{2EFCC8E5-8008-45DE-82D2-E3EB2EC99C4D}.Release|x86.Build.0 = Release|Any CPU
|
| 138 |
+
{691AB6AF-4719-4BE4-95E3-E90D94D52A7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
| 139 |
+
{691AB6AF-4719-4BE4-95E3-E90D94D52A7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
| 140 |
+
{691AB6AF-4719-4BE4-95E3-E90D94D52A7B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
| 141 |
+
{691AB6AF-4719-4BE4-95E3-E90D94D52A7B}.Debug|x64.Build.0 = Debug|Any CPU
|
| 142 |
+
{691AB6AF-4719-4BE4-95E3-E90D94D52A7B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
| 143 |
+
{691AB6AF-4719-4BE4-95E3-E90D94D52A7B}.Debug|x86.Build.0 = Debug|Any CPU
|
| 144 |
+
{691AB6AF-4719-4BE4-95E3-E90D94D52A7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
| 145 |
+
{691AB6AF-4719-4BE4-95E3-E90D94D52A7B}.Release|Any CPU.Build.0 = Release|Any CPU
|
| 146 |
+
{691AB6AF-4719-4BE4-95E3-E90D94D52A7B}.Release|x64.ActiveCfg = Release|Any CPU
|
| 147 |
+
{691AB6AF-4719-4BE4-95E3-E90D94D52A7B}.Release|x64.Build.0 = Release|Any CPU
|
| 148 |
+
{691AB6AF-4719-4BE4-95E3-E90D94D52A7B}.Release|x86.ActiveCfg = Release|Any CPU
|
| 149 |
+
{691AB6AF-4719-4BE4-95E3-E90D94D52A7B}.Release|x86.Build.0 = Release|Any CPU
|
| 150 |
+
{C1E093E3-0D6E-47EF-B428-0EDD042BEA9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
| 151 |
+
{C1E093E3-0D6E-47EF-B428-0EDD042BEA9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
| 152 |
+
{C1E093E3-0D6E-47EF-B428-0EDD042BEA9B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
| 153 |
+
{C1E093E3-0D6E-47EF-B428-0EDD042BEA9B}.Debug|x64.Build.0 = Debug|Any CPU
|
| 154 |
+
{C1E093E3-0D6E-47EF-B428-0EDD042BEA9B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
| 155 |
+
{C1E093E3-0D6E-47EF-B428-0EDD042BEA9B}.Debug|x86.Build.0 = Debug|Any CPU
|
| 156 |
+
{C1E093E3-0D6E-47EF-B428-0EDD042BEA9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
| 157 |
+
{C1E093E3-0D6E-47EF-B428-0EDD042BEA9B}.Release|Any CPU.Build.0 = Release|Any CPU
|
| 158 |
+
{C1E093E3-0D6E-47EF-B428-0EDD042BEA9B}.Release|x64.ActiveCfg = Release|Any CPU
|
| 159 |
+
{C1E093E3-0D6E-47EF-B428-0EDD042BEA9B}.Release|x64.Build.0 = Release|Any CPU
|
| 160 |
+
{C1E093E3-0D6E-47EF-B428-0EDD042BEA9B}.Release|x86.ActiveCfg = Release|Any CPU
|
| 161 |
+
{C1E093E3-0D6E-47EF-B428-0EDD042BEA9B}.Release|x86.Build.0 = Release|Any CPU
|
| 162 |
+
{35F5E701-0A63-4B1F-87F2-FFFE09C0CBAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
| 163 |
+
{35F5E701-0A63-4B1F-87F2-FFFE09C0CBAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
| 164 |
+
{35F5E701-0A63-4B1F-87F2-FFFE09C0CBAD}.Debug|x64.ActiveCfg = Debug|Any CPU
|
| 165 |
+
{35F5E701-0A63-4B1F-87F2-FFFE09C0CBAD}.Debug|x64.Build.0 = Debug|Any CPU
|
| 166 |
+
{35F5E701-0A63-4B1F-87F2-FFFE09C0CBAD}.Debug|x86.ActiveCfg = Debug|Any CPU
|
| 167 |
+
{35F5E701-0A63-4B1F-87F2-FFFE09C0CBAD}.Debug|x86.Build.0 = Debug|Any CPU
|
| 168 |
+
{35F5E701-0A63-4B1F-87F2-FFFE09C0CBAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
| 169 |
+
{35F5E701-0A63-4B1F-87F2-FFFE09C0CBAD}.Release|Any CPU.Build.0 = Release|Any CPU
|
| 170 |
+
{35F5E701-0A63-4B1F-87F2-FFFE09C0CBAD}.Release|x64.ActiveCfg = Release|Any CPU
|
| 171 |
+
{35F5E701-0A63-4B1F-87F2-FFFE09C0CBAD}.Release|x64.Build.0 = Release|Any CPU
|
| 172 |
+
{35F5E701-0A63-4B1F-87F2-FFFE09C0CBAD}.Release|x86.ActiveCfg = Release|Any CPU
|
| 173 |
+
{35F5E701-0A63-4B1F-87F2-FFFE09C0CBAD}.Release|x86.Build.0 = Release|Any CPU
|
| 174 |
+
EndGlobalSection
|
| 175 |
+
GlobalSection(SolutionProperties) = preSolution
|
| 176 |
+
HideSolutionNode = FALSE
|
| 177 |
+
EndGlobalSection
|
| 178 |
+
GlobalSection(NestedProjects) = preSolution
|
| 179 |
+
{925E18BC-C1D3-413B-ABE0-1921E905497D} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
| 180 |
+
{5F9B7489-C19D-4779-BA31-F1414BE1B155} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
| 181 |
+
{58E3C123-9602-4EAB-8925-46F8F2E882BA} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
| 182 |
+
{6CA8EDF5-4A86-492C-B497-FF32807B7DB1} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
|
| 183 |
+
{05255555-7027-416C-9ED7-C4BDB5A73402} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
| 184 |
+
{CC117307-5F76-4BF3-8C06-66C0EE3C7C66} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
| 185 |
+
{875FCCDE-6AD5-48B5-A52F-A5C2D553C36B} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
|
| 186 |
+
{2EFCC8E5-8008-45DE-82D2-E3EB2EC99C4D} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
|
| 187 |
+
{691AB6AF-4719-4BE4-95E3-E90D94D52A7B} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
|
| 188 |
+
{C1E093E3-0D6E-47EF-B428-0EDD042BEA9B} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
| 189 |
+
{35F5E701-0A63-4B1F-87F2-FFFE09C0CBAD} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
|
| 190 |
+
EndGlobalSection
|
| 191 |
+
EndGlobal
|
TESTING_IMPROVEMENTS.md
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Testing Improvements Summary
|
| 2 |
+
|
| 3 |
+
## Overview
|
| 4 |
+
|
| 5 |
+
This document summarizes the comprehensive testing improvements made to the Roslyn-Stone project, including test coverage enhancements, performance benchmarks, and load tests.
|
| 6 |
+
|
| 7 |
+
## Test Coverage Improvements
|
| 8 |
+
|
| 9 |
+
### Metrics
|
| 10 |
+
|
| 11 |
+
| Metric | Before | After | Improvement |
|
| 12 |
+
|--------|--------|-------|-------------|
|
| 13 |
+
| Line Coverage | 78.61% | 86.67% | +8.06% |
|
| 14 |
+
| Branch Coverage | 40.25% | 62.98% | +22.73% |
|
| 15 |
+
| Total Tests | 48 | 103 | +55 tests |
|
| 16 |
+
| Pass Rate | 100% | 99.0% | 102/103 passing |
|
| 17 |
+
|
| 18 |
+
### New Test Files
|
| 19 |
+
|
| 20 |
+
1. **DiagnosticHelpersTests.cs** (10 tests)
|
| 21 |
+
- Tests for functional helper methods
|
| 22 |
+
- Diagnostic error/warning partitioning
|
| 23 |
+
- Error detection logic
|
| 24 |
+
|
| 25 |
+
2. **CompilationServiceEdgeCasesTests.cs** (13 tests)
|
| 26 |
+
- Compilation error scenarios
|
| 27 |
+
- Edge cases and boundary conditions
|
| 28 |
+
- Multiple error handling
|
| 29 |
+
|
| 30 |
+
### Enhanced Test Files
|
| 31 |
+
|
| 32 |
+
1. **RoslynScriptingServiceTests.cs** (+19 tests)
|
| 33 |
+
- Runtime error scenarios
|
| 34 |
+
- Cancellation token support
|
| 35 |
+
- Package reference handling
|
| 36 |
+
- REPL state reset functionality
|
| 37 |
+
|
| 38 |
+
2. **DocumentationServiceTests.cs** (+9 tests)
|
| 39 |
+
- Edge cases for symbol lookup
|
| 40 |
+
- Caching behavior
|
| 41 |
+
- Various symbol types
|
| 42 |
+
|
| 43 |
+
3. **NuGetServiceTests.cs** (+10 tests)
|
| 44 |
+
- README retrieval
|
| 45 |
+
- Package download with cancellation
|
| 46 |
+
- Error scenarios
|
| 47 |
+
|
| 48 |
+
4. **AssemblyExecutionServiceTests.cs** (1 test skipped)
|
| 49 |
+
- Marked flaky async test as skipped
|
| 50 |
+
|
| 51 |
+
## Benchmark Project
|
| 52 |
+
|
| 53 |
+
### Purpose
|
| 54 |
+
|
| 55 |
+
Track and optimize performance of critical operations using BenchmarkDotNet.
|
| 56 |
+
|
| 57 |
+
### Benchmarks
|
| 58 |
+
|
| 59 |
+
#### RoslynScriptingServiceBenchmarks
|
| 60 |
+
- `ExecuteSimpleExpression` - Basic arithmetic (2 + 2)
|
| 61 |
+
- `ExecuteVariableAssignment` - Variable declaration and usage
|
| 62 |
+
- `ExecuteLinqQuery` - LINQ query execution
|
| 63 |
+
- `ExecuteComplexExpression` - List manipulation
|
| 64 |
+
- `ExecuteWithStringManipulation` - String operations
|
| 65 |
+
|
| 66 |
+
#### CompilationServiceBenchmarks
|
| 67 |
+
- `CompileSimpleClass` - Simple class compilation
|
| 68 |
+
- `CompileComplexCode` - Complex code with LINQ
|
| 69 |
+
- `CompileWithError` - Error handling performance
|
| 70 |
+
- `CompileMultipleClasses` - Multiple class compilation
|
| 71 |
+
|
| 72 |
+
#### NuGetServiceBenchmarks
|
| 73 |
+
- `SearchPackages` - Package search operations
|
| 74 |
+
- `GetPackageVersions` - Version lookup
|
| 75 |
+
- `GetPackageReadme` - README retrieval
|
| 76 |
+
|
| 77 |
+
### Usage
|
| 78 |
+
|
| 79 |
+
```bash
|
| 80 |
+
# Run all benchmarks
|
| 81 |
+
dotnet cake --target=Benchmark
|
| 82 |
+
|
| 83 |
+
# Run specific benchmark
|
| 84 |
+
dotnet run --project tests/RoslynStone.Benchmarks --configuration Release -- --filter *RoslynScriptingService*
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
### Output
|
| 88 |
+
|
| 89 |
+
Results are saved to `./artifacts/benchmarks/` with:
|
| 90 |
+
- Execution times (Min, Max, Mean, Median)
|
| 91 |
+
- Memory allocations and GC statistics
|
| 92 |
+
- Statistical analysis
|
| 93 |
+
|
| 94 |
+
## Load Test Project
|
| 95 |
+
|
| 96 |
+
### Purpose
|
| 97 |
+
|
| 98 |
+
Validate that the MCP HTTP server can handle high concurrency and scale under load.
|
| 99 |
+
|
| 100 |
+
### Configuration
|
| 101 |
+
|
| 102 |
+
- **Concurrency**: 300 concurrent requests per round
|
| 103 |
+
- **Rounds**: 10 rounds per scenario
|
| 104 |
+
- **Scenarios**: 4 different test scenarios
|
| 105 |
+
- **Total Requests**: 12,000 (300 × 10 × 4)
|
| 106 |
+
|
| 107 |
+
### Test Scenarios
|
| 108 |
+
|
| 109 |
+
1. **Simple Expression** - `2 + 2`
|
| 110 |
+
2. **Variable Assignment** - `var x = 10; x * 2`
|
| 111 |
+
3. **LINQ Query** - `Enumerable.Range(1, 100).Where(x => x % 2 == 0).Sum()`
|
| 112 |
+
4. **NuGet Package Loading** - Load package via LoadNuGetPackage tool
|
| 113 |
+
|
| 114 |
+
### Expected Performance
|
| 115 |
+
|
| 116 |
+
A healthy server should achieve:
|
| 117 |
+
- ✅ Success rate > 99%
|
| 118 |
+
- ✅ Average response time < 100ms
|
| 119 |
+
- ✅ Throughput > 1000 requests/second
|
| 120 |
+
|
| 121 |
+
### Usage
|
| 122 |
+
|
| 123 |
+
```bash
|
| 124 |
+
# Start the server
|
| 125 |
+
cd src/RoslynStone.Api
|
| 126 |
+
MCP_TRANSPORT=http dotnet run
|
| 127 |
+
|
| 128 |
+
# Run load tests
|
| 129 |
+
dotnet cake --target=Load-Test
|
| 130 |
+
|
| 131 |
+
# Or with custom configuration
|
| 132 |
+
dotnet run --project tests/RoslynStone.LoadTests -- http://localhost:7071 300 10
|
| 133 |
+
```
|
| 134 |
+
|
| 135 |
+
### Output
|
| 136 |
+
|
| 137 |
+
The load test reports:
|
| 138 |
+
- Average round time
|
| 139 |
+
- Average response time per request
|
| 140 |
+
- Success rate percentage
|
| 141 |
+
- Total success/failure counts
|
| 142 |
+
- Throughput (requests per second)
|
| 143 |
+
|
| 144 |
+
## CI Pipeline Enhancements
|
| 145 |
+
|
| 146 |
+
### Test-Coverage Task
|
| 147 |
+
|
| 148 |
+
Enhanced to include:
|
| 149 |
+
- Automatic coverage threshold checking
|
| 150 |
+
- Line coverage validation (>80%)
|
| 151 |
+
- Branch coverage validation (>75%, currently warning-only)
|
| 152 |
+
- Coverage metrics displayed in build output
|
| 153 |
+
|
| 154 |
+
```bash
|
| 155 |
+
dotnet cake --target=Test-Coverage
|
| 156 |
+
```
|
| 157 |
+
|
| 158 |
+
### Test-Coverage-Report Task
|
| 159 |
+
|
| 160 |
+
Generates HTML coverage reports using ReportGenerator:
|
| 161 |
+
- Interactive file browser
|
| 162 |
+
- Line-by-line coverage visualization
|
| 163 |
+
- Coverage badges
|
| 164 |
+
- Historical trends
|
| 165 |
+
|
| 166 |
+
```bash
|
| 167 |
+
dotnet cake --target=Test-Coverage-Report
|
| 168 |
+
```
|
| 169 |
+
|
| 170 |
+
### CI Task
|
| 171 |
+
|
| 172 |
+
Updated to run Test-Coverage instead of Test:
|
| 173 |
+
```bash
|
| 174 |
+
dotnet cake --target=CI
|
| 175 |
+
```
|
| 176 |
+
|
| 177 |
+
Includes:
|
| 178 |
+
1. Code formatting check (CSharpier)
|
| 179 |
+
2. Code quality analysis (ReSharper)
|
| 180 |
+
3. Build verification
|
| 181 |
+
4. Test execution with coverage
|
| 182 |
+
5. Coverage threshold validation
|
| 183 |
+
|
| 184 |
+
## Build Script Updates
|
| 185 |
+
|
| 186 |
+
### New Tasks
|
| 187 |
+
|
| 188 |
+
| Task | Description | Command |
|
| 189 |
+
|------|-------------|---------|
|
| 190 |
+
| Test-Coverage | Run tests with coverage reporting | `dotnet cake --target=Test-Coverage` |
|
| 191 |
+
| Test-Coverage-Report | Generate HTML coverage report | `dotnet cake --target=Test-Coverage-Report` |
|
| 192 |
+
| Benchmark | Run performance benchmarks | `dotnet cake --target=Benchmark` |
|
| 193 |
+
| Load-Test | Run load tests | `dotnet cake --target=Load-Test` |
|
| 194 |
+
|
| 195 |
+
### Coverage Thresholds
|
| 196 |
+
|
| 197 |
+
- **Line Coverage**: 80% (enforced with warning)
|
| 198 |
+
- **Branch Coverage**: 75% (enforced with warning)
|
| 199 |
+
|
| 200 |
+
Currently achieving:
|
| 201 |
+
- ✅ Line Coverage: 86.67%
|
| 202 |
+
- ⚠️ Branch Coverage: 62.98%
|
| 203 |
+
|
| 204 |
+
## Documentation Updates
|
| 205 |
+
|
| 206 |
+
### README.md
|
| 207 |
+
|
| 208 |
+
Added comprehensive testing section covering:
|
| 209 |
+
- Test coverage metrics and commands
|
| 210 |
+
- Benchmark usage and configuration
|
| 211 |
+
- Load test setup and expectations
|
| 212 |
+
- CI pipeline details
|
| 213 |
+
- Project structure updates
|
| 214 |
+
|
| 215 |
+
### Individual READMEs
|
| 216 |
+
|
| 217 |
+
1. **tests/RoslynStone.Benchmarks/README.md**
|
| 218 |
+
- Benchmark usage guide
|
| 219 |
+
- Available benchmarks
|
| 220 |
+
- Command reference
|
| 221 |
+
- Best practices
|
| 222 |
+
|
| 223 |
+
2. **tests/RoslynStone.LoadTests/README.md**
|
| 224 |
+
- Load test configuration
|
| 225 |
+
- Prerequisites and setup
|
| 226 |
+
- Expected results
|
| 227 |
+
- Troubleshooting guide
|
| 228 |
+
|
| 229 |
+
## Branch Coverage Gap Analysis
|
| 230 |
+
|
| 231 |
+
### Current State
|
| 232 |
+
|
| 233 |
+
Branch coverage: 62.98% (target: 75%)
|
| 234 |
+
|
| 235 |
+
### Remaining Uncovered Branches
|
| 236 |
+
|
| 237 |
+
1. **NuGetService.GetPackageReadmeAsync**
|
| 238 |
+
- Error paths requiring specific NuGet API failures
|
| 239 |
+
- Network error scenarios
|
| 240 |
+
- Package unavailability
|
| 241 |
+
|
| 242 |
+
2. **NuGetService.DownloadPackageAsync**
|
| 243 |
+
- Network timeout scenarios
|
| 244 |
+
- Download failure handling
|
| 245 |
+
- Partial download recovery
|
| 246 |
+
|
| 247 |
+
3. **DocumentationResource**
|
| 248 |
+
- Missing XML documentation files
|
| 249 |
+
- Assembly load failures
|
| 250 |
+
- Symbol resolution edge cases
|
| 251 |
+
|
| 252 |
+
### Why These Aren't Covered
|
| 253 |
+
|
| 254 |
+
These scenarios require:
|
| 255 |
+
- Complex network mocking infrastructure
|
| 256 |
+
- Live NuGet package scenarios (unstable in CI)
|
| 257 |
+
- XML documentation files that may not exist
|
| 258 |
+
- File system manipulation (security concerns)
|
| 259 |
+
|
| 260 |
+
### Future Improvements
|
| 261 |
+
|
| 262 |
+
To reach 75% branch coverage:
|
| 263 |
+
1. Implement comprehensive network mocking
|
| 264 |
+
2. Add more error injection test cases
|
| 265 |
+
3. Create test fixtures for XML documentation
|
| 266 |
+
4. Add integration tests with real NuGet packages (in separate category)
|
| 267 |
+
|
| 268 |
+
## Recommendations
|
| 269 |
+
|
| 270 |
+
### For Development
|
| 271 |
+
|
| 272 |
+
1. Run tests with coverage locally before committing:
|
| 273 |
+
```bash
|
| 274 |
+
dotnet cake --target=Test-Coverage
|
| 275 |
+
```
|
| 276 |
+
|
| 277 |
+
2. Run benchmarks after performance-sensitive changes:
|
| 278 |
+
```bash
|
| 279 |
+
dotnet cake --target=Benchmark
|
| 280 |
+
```
|
| 281 |
+
|
| 282 |
+
3. Run load tests to validate HTTP server changes:
|
| 283 |
+
```bash
|
| 284 |
+
dotnet cake --target=Load-Test
|
| 285 |
+
```
|
| 286 |
+
|
| 287 |
+
### For CI/CD
|
| 288 |
+
|
| 289 |
+
1. Monitor coverage trends over time
|
| 290 |
+
2. Set up performance regression detection
|
| 291 |
+
3. Run load tests in staging environment
|
| 292 |
+
4. Archive benchmark results for comparison
|
| 293 |
+
|
| 294 |
+
### For Code Reviews
|
| 295 |
+
|
| 296 |
+
1. Check coverage impact of new code
|
| 297 |
+
2. Verify benchmarks for performance-critical changes
|
| 298 |
+
3. Ensure new tests follow existing patterns
|
| 299 |
+
4. Validate test isolation and determinism
|
| 300 |
+
|
| 301 |
+
## Conclusion
|
| 302 |
+
|
| 303 |
+
These testing improvements provide:
|
| 304 |
+
- ✅ High confidence in code quality (86.67% line coverage)
|
| 305 |
+
- ✅ Significant branch coverage improvement (+22.73%)
|
| 306 |
+
- ✅ Performance tracking and optimization capabilities
|
| 307 |
+
- ✅ Scalability validation under load
|
| 308 |
+
- ✅ Automated coverage reporting in CI
|
| 309 |
+
- ✅ Comprehensive documentation
|
| 310 |
+
|
| 311 |
+
The project now has a robust testing infrastructure that will help maintain quality as it evolves.
|
artifacts/resharper-report.xml
ADDED
|
@@ -0,0 +1,2801 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.6.json",
|
| 3 |
+
"version": "2.1.0",
|
| 4 |
+
"runs": [
|
| 5 |
+
{
|
| 6 |
+
"results": [
|
| 7 |
+
{
|
| 8 |
+
"ruleId": "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract",
|
| 9 |
+
"ruleIndex": 0,
|
| 10 |
+
"level": "warning",
|
| 11 |
+
"message": {
|
| 12 |
+
"text": "Conditional access qualifier expression is never null according to nullable reference types' annotations"
|
| 13 |
+
},
|
| 14 |
+
"locations": [
|
| 15 |
+
{
|
| 16 |
+
"physicalLocation": {
|
| 17 |
+
"artifactLocation": {
|
| 18 |
+
"uri": "tests/RoslynStone.Tests/CriticalBugsTests.cs",
|
| 19 |
+
"uriBaseId": "solutionDir",
|
| 20 |
+
"index": 0
|
| 21 |
+
},
|
| 22 |
+
"region": {
|
| 23 |
+
"startLine": 31,
|
| 24 |
+
"startColumn": 22,
|
| 25 |
+
"endLine": 31,
|
| 26 |
+
"endColumn": 23,
|
| 27 |
+
"charOffset": 1014,
|
| 28 |
+
"charLength": 1
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
],
|
| 33 |
+
"partialFingerprints": {
|
| 34 |
+
"contextRegionHash/v1": "3F82E6F857E5A5AB9DFD812F8AAD40C698B7130FE1ECCEA26261CB331825BE5E"
|
| 35 |
+
},
|
| 36 |
+
"properties": {
|
| 37 |
+
"tags": ["C#",".NET 10.0"]
|
| 38 |
+
}
|
| 39 |
+
},
|
| 40 |
+
{
|
| 41 |
+
"ruleId": "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract",
|
| 42 |
+
"ruleIndex": 0,
|
| 43 |
+
"level": "warning",
|
| 44 |
+
"message": {
|
| 45 |
+
"text": "Conditional access qualifier expression is never null according to nullable reference types' annotations"
|
| 46 |
+
},
|
| 47 |
+
"locations": [
|
| 48 |
+
{
|
| 49 |
+
"physicalLocation": {
|
| 50 |
+
"artifactLocation": {
|
| 51 |
+
"uri": "tests/RoslynStone.Tests/LoadNuGetPackageTests.cs",
|
| 52 |
+
"uriBaseId": "solutionDir",
|
| 53 |
+
"index": 1
|
| 54 |
+
},
|
| 55 |
+
"region": {
|
| 56 |
+
"startLine": 25,
|
| 57 |
+
"startColumn": 22,
|
| 58 |
+
"endLine": 25,
|
| 59 |
+
"endColumn": 23,
|
| 60 |
+
"charOffset": 694,
|
| 61 |
+
"charLength": 1
|
| 62 |
+
}
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
],
|
| 66 |
+
"partialFingerprints": {
|
| 67 |
+
"contextRegionHash/v1": "3A1995860840B241D10D46B0A102ADFD4C1B98578D947A51FD60E21793F69500"
|
| 68 |
+
},
|
| 69 |
+
"properties": {
|
| 70 |
+
"tags": ["C#",".NET 10.0"]
|
| 71 |
+
}
|
| 72 |
+
},
|
| 73 |
+
{
|
| 74 |
+
"ruleId": "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract",
|
| 75 |
+
"ruleIndex": 0,
|
| 76 |
+
"level": "warning",
|
| 77 |
+
"message": {
|
| 78 |
+
"text": "Conditional access qualifier expression is never null according to nullable reference types' annotations"
|
| 79 |
+
},
|
| 80 |
+
"locations": [
|
| 81 |
+
{
|
| 82 |
+
"physicalLocation": {
|
| 83 |
+
"artifactLocation": {
|
| 84 |
+
"uri": "tests/RoslynStone.Tests/McpToolsIntegrationTests.cs",
|
| 85 |
+
"uriBaseId": "solutionDir",
|
| 86 |
+
"index": 2
|
| 87 |
+
},
|
| 88 |
+
"region": {
|
| 89 |
+
"startLine": 29,
|
| 90 |
+
"startColumn": 22,
|
| 91 |
+
"endLine": 29,
|
| 92 |
+
"endColumn": 23,
|
| 93 |
+
"charOffset": 874,
|
| 94 |
+
"charLength": 1
|
| 95 |
+
}
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
+
],
|
| 99 |
+
"partialFingerprints": {
|
| 100 |
+
"contextRegionHash/v1": "3A1995860840B241D10D46B0A102ADFD4C1B98578D947A51FD60E21793F69500"
|
| 101 |
+
},
|
| 102 |
+
"properties": {
|
| 103 |
+
"tags": ["C#",".NET 10.0"]
|
| 104 |
+
}
|
| 105 |
+
},
|
| 106 |
+
{
|
| 107 |
+
"ruleId": "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract",
|
| 108 |
+
"ruleIndex": 0,
|
| 109 |
+
"level": "warning",
|
| 110 |
+
"message": {
|
| 111 |
+
"text": "Conditional access qualifier expression is never null according to nullable reference types' annotations"
|
| 112 |
+
},
|
| 113 |
+
"locations": [
|
| 114 |
+
{
|
| 115 |
+
"physicalLocation": {
|
| 116 |
+
"artifactLocation": {
|
| 117 |
+
"uri": "tests/RoslynStone.Tests/ReplToolsContextControlTests.cs",
|
| 118 |
+
"uriBaseId": "solutionDir",
|
| 119 |
+
"index": 3
|
| 120 |
+
},
|
| 121 |
+
"region": {
|
| 122 |
+
"startLine": 28,
|
| 123 |
+
"startColumn": 22,
|
| 124 |
+
"endLine": 28,
|
| 125 |
+
"endColumn": 23,
|
| 126 |
+
"charOffset": 811,
|
| 127 |
+
"charLength": 1
|
| 128 |
+
}
|
| 129 |
+
}
|
| 130 |
+
}
|
| 131 |
+
],
|
| 132 |
+
"partialFingerprints": {
|
| 133 |
+
"contextRegionHash/v1": "3A1995860840B241D10D46B0A102ADFD4C1B98578D947A51FD60E21793F69500"
|
| 134 |
+
},
|
| 135 |
+
"properties": {
|
| 136 |
+
"tags": ["C#",".NET 10.0"]
|
| 137 |
+
}
|
| 138 |
+
},
|
| 139 |
+
{
|
| 140 |
+
"ruleId": "InconsistentNaming",
|
| 141 |
+
"ruleIndex": 1,
|
| 142 |
+
"level": "warning",
|
| 143 |
+
"message": {
|
| 144 |
+
"text": "Name '_executionLock' does not match rule 'Static readonly fields (private)'. Suggested name is 'ExecutionLock'."
|
| 145 |
+
},
|
| 146 |
+
"locations": [
|
| 147 |
+
{
|
| 148 |
+
"physicalLocation": {
|
| 149 |
+
"artifactLocation": {
|
| 150 |
+
"uri": "src/RoslynStone.Infrastructure/Services/RoslynScriptingService.cs",
|
| 151 |
+
"uriBaseId": "solutionDir",
|
| 152 |
+
"index": 4
|
| 153 |
+
},
|
| 154 |
+
"region": {
|
| 155 |
+
"startLine": 24,
|
| 156 |
+
"startColumn": 43,
|
| 157 |
+
"endLine": 24,
|
| 158 |
+
"endColumn": 57,
|
| 159 |
+
"charOffset": 943,
|
| 160 |
+
"charLength": 14
|
| 161 |
+
}
|
| 162 |
+
}
|
| 163 |
+
}
|
| 164 |
+
],
|
| 165 |
+
"partialFingerprints": {
|
| 166 |
+
"contextRegionHash/v1": "AF35C315C6C8B81A257B7D7AEE4B2277667C01A8D2352E1486966DBF825D6BF3"
|
| 167 |
+
},
|
| 168 |
+
"properties": {
|
| 169 |
+
"tags": ["C#",".NET 10.0"]
|
| 170 |
+
}
|
| 171 |
+
},
|
| 172 |
+
{
|
| 173 |
+
"ruleId": "NotAccessedField.Local",
|
| 174 |
+
"ruleIndex": 2,
|
| 175 |
+
"level": "warning",
|
| 176 |
+
"message": {
|
| 177 |
+
"text": "Field '_documentationService' is assigned but its value is never used"
|
| 178 |
+
},
|
| 179 |
+
"locations": [
|
| 180 |
+
{
|
| 181 |
+
"physicalLocation": {
|
| 182 |
+
"artifactLocation": {
|
| 183 |
+
"uri": "tests/RoslynStone.Tests/McpToolsIntegrationTests.cs",
|
| 184 |
+
"uriBaseId": "solutionDir",
|
| 185 |
+
"index": 2
|
| 186 |
+
},
|
| 187 |
+
"region": {
|
| 188 |
+
"startLine": 15,
|
| 189 |
+
"startColumn": 43,
|
| 190 |
+
"endLine": 15,
|
| 191 |
+
"endColumn": 64,
|
| 192 |
+
"charOffset": 425,
|
| 193 |
+
"charLength": 21
|
| 194 |
+
}
|
| 195 |
+
}
|
| 196 |
+
}
|
| 197 |
+
],
|
| 198 |
+
"partialFingerprints": {
|
| 199 |
+
"contextRegionHash/v1": "AF369CC8A9A05DDD3DE0F8D914E0C879EB56D935E9F51E42B187176005C2AF70"
|
| 200 |
+
},
|
| 201 |
+
"properties": {
|
| 202 |
+
"tags": ["C#",".NET 10.0"]
|
| 203 |
+
}
|
| 204 |
+
},
|
| 205 |
+
{
|
| 206 |
+
"ruleId": "NotAccessedField.Local",
|
| 207 |
+
"ruleIndex": 2,
|
| 208 |
+
"level": "warning",
|
| 209 |
+
"message": {
|
| 210 |
+
"text": "Field '_expectedRounds' is assigned but its value is never used"
|
| 211 |
+
},
|
| 212 |
+
"locations": [
|
| 213 |
+
{
|
| 214 |
+
"physicalLocation": {
|
| 215 |
+
"artifactLocation": {
|
| 216 |
+
"uri": "tests/RoslynStone.LoadTests/Program.cs",
|
| 217 |
+
"uriBaseId": "solutionDir",
|
| 218 |
+
"index": 5
|
| 219 |
+
},
|
| 220 |
+
"region": {
|
| 221 |
+
"startLine": 260,
|
| 222 |
+
"startColumn": 26,
|
| 223 |
+
"endLine": 260,
|
| 224 |
+
"endColumn": 41,
|
| 225 |
+
"charOffset": 7955,
|
| 226 |
+
"charLength": 15
|
| 227 |
+
}
|
| 228 |
+
}
|
| 229 |
+
}
|
| 230 |
+
],
|
| 231 |
+
"partialFingerprints": {
|
| 232 |
+
"contextRegionHash/v1": "1DAA46DBF681FD84D3FA3E1344B750A0BD7BC38CE9B113B6F94B16637C947960"
|
| 233 |
+
},
|
| 234 |
+
"properties": {
|
| 235 |
+
"tags": ["C#",".NET 10.0"]
|
| 236 |
+
}
|
| 237 |
+
},
|
| 238 |
+
{
|
| 239 |
+
"ruleId": "NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract",
|
| 240 |
+
"ruleIndex": 3,
|
| 241 |
+
"level": "warning",
|
| 242 |
+
"message": {
|
| 243 |
+
"text": "'??' left operand is never null according to nullable reference types' annotations"
|
| 244 |
+
},
|
| 245 |
+
"locations": [
|
| 246 |
+
{
|
| 247 |
+
"physicalLocation": {
|
| 248 |
+
"artifactLocation": {
|
| 249 |
+
"uri": "src/RoslynStone.Infrastructure/Resources/NuGetSearchResource.cs",
|
| 250 |
+
"uriBaseId": "solutionDir",
|
| 251 |
+
"index": 6
|
| 252 |
+
},
|
| 253 |
+
"region": {
|
| 254 |
+
"startLine": 49,
|
| 255 |
+
"startColumn": 35,
|
| 256 |
+
"endLine": 49,
|
| 257 |
+
"endColumn": 47,
|
| 258 |
+
"charOffset": 2506,
|
| 259 |
+
"charLength": 12
|
| 260 |
+
}
|
| 261 |
+
}
|
| 262 |
+
}
|
| 263 |
+
],
|
| 264 |
+
"partialFingerprints": {
|
| 265 |
+
"contextRegionHash/v1": "45AA56235572C19F5743DB6FF2FAB125DF73701895E295F81CD671A2579AA1BE"
|
| 266 |
+
},
|
| 267 |
+
"properties": {
|
| 268 |
+
"tags": ["C#",".NET 10.0"]
|
| 269 |
+
}
|
| 270 |
+
},
|
| 271 |
+
{
|
| 272 |
+
"ruleId": "PossibleMultipleEnumeration",
|
| 273 |
+
"ruleIndex": 4,
|
| 274 |
+
"level": "warning",
|
| 275 |
+
"message": {
|
| 276 |
+
"text": "Possible multiple enumeration"
|
| 277 |
+
},
|
| 278 |
+
"locations": [
|
| 279 |
+
{
|
| 280 |
+
"physicalLocation": {
|
| 281 |
+
"artifactLocation": {
|
| 282 |
+
"uri": "tests/RoslynStone.AppHost.Tests/AppHostTests.cs",
|
| 283 |
+
"uriBaseId": "solutionDir",
|
| 284 |
+
"index": 7
|
| 285 |
+
},
|
| 286 |
+
"region": {
|
| 287 |
+
"startLine": 135,
|
| 288 |
+
"startColumn": 25,
|
| 289 |
+
"endLine": 135,
|
| 290 |
+
"endColumn": 34,
|
| 291 |
+
"charOffset": 4415,
|
| 292 |
+
"charLength": 9
|
| 293 |
+
}
|
| 294 |
+
}
|
| 295 |
+
}
|
| 296 |
+
],
|
| 297 |
+
"partialFingerprints": {
|
| 298 |
+
"contextRegionHash/v1": "630674D2BDD6855DC4BCCFDCABD6B3F763336BBF1C7D0C00F5F7101F4A2F267B"
|
| 299 |
+
},
|
| 300 |
+
"properties": {
|
| 301 |
+
"tags": ["C#",".NET 10.0"]
|
| 302 |
+
}
|
| 303 |
+
},
|
| 304 |
+
{
|
| 305 |
+
"ruleId": "PossibleMultipleEnumeration",
|
| 306 |
+
"ruleIndex": 4,
|
| 307 |
+
"level": "warning",
|
| 308 |
+
"message": {
|
| 309 |
+
"text": "Possible multiple enumeration"
|
| 310 |
+
},
|
| 311 |
+
"locations": [
|
| 312 |
+
{
|
| 313 |
+
"physicalLocation": {
|
| 314 |
+
"artifactLocation": {
|
| 315 |
+
"uri": "tests/RoslynStone.AppHost.Tests/AppHostTests.cs",
|
| 316 |
+
"uriBaseId": "solutionDir",
|
| 317 |
+
"index": 7
|
| 318 |
+
},
|
| 319 |
+
"region": {
|
| 320 |
+
"startLine": 136,
|
| 321 |
+
"startColumn": 25,
|
| 322 |
+
"endLine": 136,
|
| 323 |
+
"endColumn": 34,
|
| 324 |
+
"charOffset": 4484,
|
| 325 |
+
"charLength": 9
|
| 326 |
+
}
|
| 327 |
+
}
|
| 328 |
+
}
|
| 329 |
+
],
|
| 330 |
+
"partialFingerprints": {
|
| 331 |
+
"contextRegionHash/v1": "DEC67C00AF74AC99221FF58D49B56E8D62110A4C547A9439677FEBDA404CB82C"
|
| 332 |
+
},
|
| 333 |
+
"properties": {
|
| 334 |
+
"tags": ["C#",".NET 10.0"]
|
| 335 |
+
}
|
| 336 |
+
},
|
| 337 |
+
{
|
| 338 |
+
"ruleId": "PrivateFieldCanBeConvertedToLocalVariable",
|
| 339 |
+
"ruleIndex": 5,
|
| 340 |
+
"level": "warning",
|
| 341 |
+
"message": {
|
| 342 |
+
"text": "The field is always assigned before being used and can be converted into a local variable"
|
| 343 |
+
},
|
| 344 |
+
"locations": [
|
| 345 |
+
{
|
| 346 |
+
"physicalLocation": {
|
| 347 |
+
"artifactLocation": {
|
| 348 |
+
"uri": "tests/RoslynStone.GradioTests/GradioLandingPageTests.cs",
|
| 349 |
+
"uriBaseId": "solutionDir",
|
| 350 |
+
"index": 8
|
| 351 |
+
},
|
| 352 |
+
"region": {
|
| 353 |
+
"startLine": 14,
|
| 354 |
+
"startColumn": 40,
|
| 355 |
+
"endLine": 14,
|
| 356 |
+
"endColumn": 47,
|
| 357 |
+
"charOffset": 500,
|
| 358 |
+
"charLength": 7
|
| 359 |
+
}
|
| 360 |
+
}
|
| 361 |
+
}
|
| 362 |
+
],
|
| 363 |
+
"partialFingerprints": {
|
| 364 |
+
"contextRegionHash/v1": "CB006F97FB03998D96EC29223B2D46107C067B0F758AD722DF932A91902C97CA"
|
| 365 |
+
},
|
| 366 |
+
"properties": {
|
| 367 |
+
"tags": ["C#",".NET 10.0"]
|
| 368 |
+
}
|
| 369 |
+
},
|
| 370 |
+
{
|
| 371 |
+
"ruleId": "RedundantArgumentDefaultValue",
|
| 372 |
+
"ruleIndex": 6,
|
| 373 |
+
"level": "warning",
|
| 374 |
+
"message": {
|
| 375 |
+
"text": "The parameter 'assemblyName' has the same default value"
|
| 376 |
+
},
|
| 377 |
+
"locations": [
|
| 378 |
+
{
|
| 379 |
+
"physicalLocation": {
|
| 380 |
+
"artifactLocation": {
|
| 381 |
+
"uri": "tests/RoslynStone.Tests/CompilationServiceEdgeCasesTests.cs",
|
| 382 |
+
"uriBaseId": "solutionDir",
|
| 383 |
+
"index": 9
|
| 384 |
+
},
|
| 385 |
+
"region": {
|
| 386 |
+
"startLine": 179,
|
| 387 |
+
"startColumn": 44,
|
| 388 |
+
"endLine": 179,
|
| 389 |
+
"endColumn": 48,
|
| 390 |
+
"charOffset": 4532,
|
| 391 |
+
"charLength": 4
|
| 392 |
+
}
|
| 393 |
+
}
|
| 394 |
+
}
|
| 395 |
+
],
|
| 396 |
+
"partialFingerprints": {
|
| 397 |
+
"contextRegionHash/v1": "FCBEE6D4980CE017440A7BEC925127098440C0DD97EABA1A6C5E4E7812070DFC"
|
| 398 |
+
},
|
| 399 |
+
"properties": {
|
| 400 |
+
"tags": ["C#",".NET 10.0"]
|
| 401 |
+
}
|
| 402 |
+
},
|
| 403 |
+
{
|
| 404 |
+
"ruleId": "RedundantArgumentDefaultValue",
|
| 405 |
+
"ruleIndex": 6,
|
| 406 |
+
"level": "warning",
|
| 407 |
+
"message": {
|
| 408 |
+
"text": "The parameter 'assemblyPaths' has the same default value"
|
| 409 |
+
},
|
| 410 |
+
"locations": [
|
| 411 |
+
{
|
| 412 |
+
"physicalLocation": {
|
| 413 |
+
"artifactLocation": {
|
| 414 |
+
"uri": "tests/RoslynStone.Tests/RoslynScriptingServiceTests.cs",
|
| 415 |
+
"uriBaseId": "solutionDir",
|
| 416 |
+
"index": 10
|
| 417 |
+
},
|
| 418 |
+
"region": {
|
| 419 |
+
"startLine": 317,
|
| 420 |
+
"startColumn": 72,
|
| 421 |
+
"endLine": 317,
|
| 422 |
+
"endColumn": 76,
|
| 423 |
+
"charOffset": 9150,
|
| 424 |
+
"charLength": 4
|
| 425 |
+
}
|
| 426 |
+
}
|
| 427 |
+
}
|
| 428 |
+
],
|
| 429 |
+
"partialFingerprints": {
|
| 430 |
+
"contextRegionHash/v1": "BF8B11DA107EBE88DB4011E1EAA37D3883011775D070FB2CE7EBEFC3FD3E36F6"
|
| 431 |
+
},
|
| 432 |
+
"properties": {
|
| 433 |
+
"tags": ["C#",".NET 10.0"]
|
| 434 |
+
}
|
| 435 |
+
},
|
| 436 |
+
{
|
| 437 |
+
"ruleId": "RedundantArgumentDefaultValue",
|
| 438 |
+
"ruleIndex": 6,
|
| 439 |
+
"level": "warning",
|
| 440 |
+
"message": {
|
| 441 |
+
"text": "The parameter 'skip' has the same default value"
|
| 442 |
+
},
|
| 443 |
+
"locations": [
|
| 444 |
+
{
|
| 445 |
+
"physicalLocation": {
|
| 446 |
+
"artifactLocation": {
|
| 447 |
+
"uri": "tests/RoslynStone.Tests/ResourceTests.cs",
|
| 448 |
+
"uriBaseId": "solutionDir",
|
| 449 |
+
"index": 11
|
| 450 |
+
},
|
| 451 |
+
"region": {
|
| 452 |
+
"startLine": 221,
|
| 453 |
+
"startColumn": 13,
|
| 454 |
+
"endLine": 221,
|
| 455 |
+
"endColumn": 17,
|
| 456 |
+
"charOffset": 8037,
|
| 457 |
+
"charLength": 4
|
| 458 |
+
}
|
| 459 |
+
}
|
| 460 |
+
}
|
| 461 |
+
],
|
| 462 |
+
"partialFingerprints": {
|
| 463 |
+
"contextRegionHash/v1": "E193E5BB98F978AE68DADD5C38C33FF50932162FE1BD6E967865EE0D24287016"
|
| 464 |
+
},
|
| 465 |
+
"properties": {
|
| 466 |
+
"tags": ["C#",".NET 10.0"]
|
| 467 |
+
}
|
| 468 |
+
},
|
| 469 |
+
{
|
| 470 |
+
"ruleId": "RedundantArgumentDefaultValue",
|
| 471 |
+
"ruleIndex": 6,
|
| 472 |
+
"level": "warning",
|
| 473 |
+
"message": {
|
| 474 |
+
"text": "The parameter 'take' has the same default value"
|
| 475 |
+
},
|
| 476 |
+
"locations": [
|
| 477 |
+
{
|
| 478 |
+
"physicalLocation": {
|
| 479 |
+
"artifactLocation": {
|
| 480 |
+
"uri": "tests/RoslynStone.Tests/ResourceTests.cs",
|
| 481 |
+
"uriBaseId": "solutionDir",
|
| 482 |
+
"index": 11
|
| 483 |
+
},
|
| 484 |
+
"region": {
|
| 485 |
+
"startLine": 222,
|
| 486 |
+
"startColumn": 13,
|
| 487 |
+
"endLine": 222,
|
| 488 |
+
"endColumn": 17,
|
| 489 |
+
"charOffset": 8055,
|
| 490 |
+
"charLength": 4
|
| 491 |
+
}
|
| 492 |
+
}
|
| 493 |
+
}
|
| 494 |
+
],
|
| 495 |
+
"partialFingerprints": {
|
| 496 |
+
"contextRegionHash/v1": "0E396FA8E6AA7CAC8801019BCB8BAB4AA0FE28CC2D0FFBAED259BF62830E8790"
|
| 497 |
+
},
|
| 498 |
+
"properties": {
|
| 499 |
+
"tags": ["C#",".NET 10.0"]
|
| 500 |
+
}
|
| 501 |
+
},
|
| 502 |
+
{
|
| 503 |
+
"ruleId": "RedundantNameQualifier",
|
| 504 |
+
"ruleIndex": 7,
|
| 505 |
+
"level": "warning",
|
| 506 |
+
"message": {
|
| 507 |
+
"text": "Qualifier is redundant"
|
| 508 |
+
},
|
| 509 |
+
"locations": [
|
| 510 |
+
{
|
| 511 |
+
"physicalLocation": {
|
| 512 |
+
"artifactLocation": {
|
| 513 |
+
"uri": "tests/RoslynStone.Tests/DiagnosticHelpersTests.cs",
|
| 514 |
+
"uriBaseId": "solutionDir",
|
| 515 |
+
"index": 12
|
| 516 |
+
},
|
| 517 |
+
"region": {
|
| 518 |
+
"startLine": 167,
|
| 519 |
+
"startColumn": 17,
|
| 520 |
+
"endLine": 167,
|
| 521 |
+
"endColumn": 47,
|
| 522 |
+
"charOffset": 5243,
|
| 523 |
+
"charLength": 30
|
| 524 |
+
}
|
| 525 |
+
}
|
| 526 |
+
}
|
| 527 |
+
],
|
| 528 |
+
"partialFingerprints": {
|
| 529 |
+
"contextRegionHash/v1": "74EEE0900700E92AA09AB8376BB42A3085DFD286B4D7273D03E576477D29D66D"
|
| 530 |
+
},
|
| 531 |
+
"properties": {
|
| 532 |
+
"tags": ["C#",".NET 10.0"]
|
| 533 |
+
}
|
| 534 |
+
},
|
| 535 |
+
{
|
| 536 |
+
"ruleId": "RedundantTypeArgumentsOfMethod",
|
| 537 |
+
"ruleIndex": 8,
|
| 538 |
+
"level": "warning",
|
| 539 |
+
"message": {
|
| 540 |
+
"text": "Type argument specification is redundant"
|
| 541 |
+
},
|
| 542 |
+
"locations": [
|
| 543 |
+
{
|
| 544 |
+
"physicalLocation": {
|
| 545 |
+
"artifactLocation": {
|
| 546 |
+
"uri": "src/RoslynStone.Api/Program.cs",
|
| 547 |
+
"uriBaseId": "solutionDir",
|
| 548 |
+
"index": 13
|
| 549 |
+
},
|
| 550 |
+
"region": {
|
| 551 |
+
"startLine": 132,
|
| 552 |
+
"startColumn": 44,
|
| 553 |
+
"endLine": 132,
|
| 554 |
+
"endColumn": 49,
|
| 555 |
+
"charOffset": 5276,
|
| 556 |
+
"charLength": 5
|
| 557 |
+
}
|
| 558 |
+
}
|
| 559 |
+
}
|
| 560 |
+
],
|
| 561 |
+
"partialFingerprints": {
|
| 562 |
+
"contextRegionHash/v1": "AEC43A46ACDC620CF23BBC33494B7798D8978CA10C2628A1A4C205CFF058C298"
|
| 563 |
+
},
|
| 564 |
+
"properties": {
|
| 565 |
+
"tags": ["C#",".NET 10.0"]
|
| 566 |
+
}
|
| 567 |
+
},
|
| 568 |
+
{
|
| 569 |
+
"ruleId": "RedundantUsingDirective",
|
| 570 |
+
"ruleIndex": 9,
|
| 571 |
+
"level": "warning",
|
| 572 |
+
"message": {
|
| 573 |
+
"text": "Using directive is not required by the code and can be safely removed"
|
| 574 |
+
},
|
| 575 |
+
"locations": [
|
| 576 |
+
{
|
| 577 |
+
"physicalLocation": {
|
| 578 |
+
"artifactLocation": {
|
| 579 |
+
"uri": "src/RoslynStone.Infrastructure/Resources/NuGetPackageResource.cs",
|
| 580 |
+
"uriBaseId": "solutionDir",
|
| 581 |
+
"index": 14
|
| 582 |
+
},
|
| 583 |
+
"region": {
|
| 584 |
+
"startLine": 2,
|
| 585 |
+
"startColumn": 1,
|
| 586 |
+
"endLine": 2,
|
| 587 |
+
"endColumn": 41,
|
| 588 |
+
"charOffset": 29,
|
| 589 |
+
"charLength": 40
|
| 590 |
+
}
|
| 591 |
+
}
|
| 592 |
+
}
|
| 593 |
+
],
|
| 594 |
+
"partialFingerprints": {
|
| 595 |
+
"contextRegionHash/v1": "4293B7AB9B2AFEC3D6DD50F5783B64384B1075357B5EDAB16943C610BB0E5D31"
|
| 596 |
+
},
|
| 597 |
+
"properties": {
|
| 598 |
+
"tags": ["C#",".NET 10.0"]
|
| 599 |
+
}
|
| 600 |
+
},
|
| 601 |
+
{
|
| 602 |
+
"ruleId": "RedundantUsingDirective",
|
| 603 |
+
"ruleIndex": 9,
|
| 604 |
+
"level": "warning",
|
| 605 |
+
"message": {
|
| 606 |
+
"text": "Using directive is not required by the code and can be safely removed"
|
| 607 |
+
},
|
| 608 |
+
"locations": [
|
| 609 |
+
{
|
| 610 |
+
"physicalLocation": {
|
| 611 |
+
"artifactLocation": {
|
| 612 |
+
"uri": "src/RoslynStone.Infrastructure/Resources/NuGetSearchResource.cs",
|
| 613 |
+
"uriBaseId": "solutionDir",
|
| 614 |
+
"index": 6
|
| 615 |
+
},
|
| 616 |
+
"region": {
|
| 617 |
+
"startLine": 2,
|
| 618 |
+
"startColumn": 1,
|
| 619 |
+
"endLine": 2,
|
| 620 |
+
"endColumn": 41,
|
| 621 |
+
"charOffset": 29,
|
| 622 |
+
"charLength": 40
|
| 623 |
+
}
|
| 624 |
+
}
|
| 625 |
+
}
|
| 626 |
+
],
|
| 627 |
+
"partialFingerprints": {
|
| 628 |
+
"contextRegionHash/v1": "4293B7AB9B2AFEC3D6DD50F5783B64384B1075357B5EDAB16943C610BB0E5D31"
|
| 629 |
+
},
|
| 630 |
+
"properties": {
|
| 631 |
+
"tags": ["C#",".NET 10.0"]
|
| 632 |
+
}
|
| 633 |
+
},
|
| 634 |
+
{
|
| 635 |
+
"ruleId": "RedundantUsingDirective",
|
| 636 |
+
"ruleIndex": 9,
|
| 637 |
+
"level": "warning",
|
| 638 |
+
"message": {
|
| 639 |
+
"text": "Using directive is not required by the code and can be safely removed"
|
| 640 |
+
},
|
| 641 |
+
"locations": [
|
| 642 |
+
{
|
| 643 |
+
"physicalLocation": {
|
| 644 |
+
"artifactLocation": {
|
| 645 |
+
"uri": "tests/RoslynStone.AppHost.Tests/AppHostTests.cs",
|
| 646 |
+
"uriBaseId": "solutionDir",
|
| 647 |
+
"index": 7
|
| 648 |
+
},
|
| 649 |
+
"region": {
|
| 650 |
+
"startLine": 4,
|
| 651 |
+
"startColumn": 1,
|
| 652 |
+
"endLine": 4,
|
| 653 |
+
"endColumn": 48,
|
| 654 |
+
"charOffset": 91,
|
| 655 |
+
"charLength": 47
|
| 656 |
+
}
|
| 657 |
+
}
|
| 658 |
+
}
|
| 659 |
+
],
|
| 660 |
+
"partialFingerprints": {
|
| 661 |
+
"contextRegionHash/v1": "9D5332DB8F31A2F8223A325F63E5CCBE79203A5DC26D3218108778787A671B9B"
|
| 662 |
+
},
|
| 663 |
+
"properties": {
|
| 664 |
+
"tags": ["C#",".NET 10.0"]
|
| 665 |
+
}
|
| 666 |
+
},
|
| 667 |
+
{
|
| 668 |
+
"ruleId": "SuspiciousTypeConversion.Global",
|
| 669 |
+
"ruleIndex": 10,
|
| 670 |
+
"level": "warning",
|
| 671 |
+
"message": {
|
| 672 |
+
"text": "Suspicious type check: there is no type in the solution that is inherited from both 'RoslynStone.Infrastructure.Services.IReplContextManager' and 'System.IDisposable'"
|
| 673 |
+
},
|
| 674 |
+
"locations": [
|
| 675 |
+
{
|
| 676 |
+
"physicalLocation": {
|
| 677 |
+
"artifactLocation": {
|
| 678 |
+
"uri": "tests/RoslynStone.Tests/CriticalBugsTests.cs",
|
| 679 |
+
"uriBaseId": "solutionDir",
|
| 680 |
+
"index": 0
|
| 681 |
+
},
|
| 682 |
+
"region": {
|
| 683 |
+
"startLine": 32,
|
| 684 |
+
"startColumn": 29,
|
| 685 |
+
"endLine": 32,
|
| 686 |
+
"endColumn": 43,
|
| 687 |
+
"charOffset": 1055,
|
| 688 |
+
"charLength": 14
|
| 689 |
+
}
|
| 690 |
+
}
|
| 691 |
+
}
|
| 692 |
+
],
|
| 693 |
+
"partialFingerprints": {
|
| 694 |
+
"contextRegionHash/v1": "8C06D47A1E92AAEA1E391B0F8F22CFC94495C4F49E6357AEF4AB934B507EDCC5"
|
| 695 |
+
},
|
| 696 |
+
"properties": {
|
| 697 |
+
"tags": ["C#",".NET 10.0"]
|
| 698 |
+
}
|
| 699 |
+
},
|
| 700 |
+
{
|
| 701 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 702 |
+
"ruleIndex": 11,
|
| 703 |
+
"level": "warning",
|
| 704 |
+
"message": {
|
| 705 |
+
"text": "Auto-property accessor 'AsyncAwait.get' is never used"
|
| 706 |
+
},
|
| 707 |
+
"locations": [
|
| 708 |
+
{
|
| 709 |
+
"physicalLocation": {
|
| 710 |
+
"artifactLocation": {
|
| 711 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 712 |
+
"uriBaseId": "solutionDir",
|
| 713 |
+
"index": 15
|
| 714 |
+
},
|
| 715 |
+
"region": {
|
| 716 |
+
"startLine": 454,
|
| 717 |
+
"startColumn": 39,
|
| 718 |
+
"endLine": 454,
|
| 719 |
+
"endColumn": 43,
|
| 720 |
+
"charOffset": 15342,
|
| 721 |
+
"charLength": 4
|
| 722 |
+
}
|
| 723 |
+
}
|
| 724 |
+
}
|
| 725 |
+
],
|
| 726 |
+
"partialFingerprints": {
|
| 727 |
+
"contextRegionHash/v1": "CF74E1B7E02A53F2B97DB944067F3DE0BAC28911B93D8833B039F2E293E03F56"
|
| 728 |
+
},
|
| 729 |
+
"properties": {
|
| 730 |
+
"tags": ["C#",".NET 10.0"]
|
| 731 |
+
}
|
| 732 |
+
},
|
| 733 |
+
{
|
| 734 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 735 |
+
"ruleIndex": 11,
|
| 736 |
+
"level": "warning",
|
| 737 |
+
"message": {
|
| 738 |
+
"text": "Auto-property accessor 'AsyncOperation.get' is never used"
|
| 739 |
+
},
|
| 740 |
+
"locations": [
|
| 741 |
+
{
|
| 742 |
+
"physicalLocation": {
|
| 743 |
+
"artifactLocation": {
|
| 744 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 745 |
+
"uriBaseId": "solutionDir",
|
| 746 |
+
"index": 15
|
| 747 |
+
},
|
| 748 |
+
"region": {
|
| 749 |
+
"startLine": 492,
|
| 750 |
+
"startColumn": 45,
|
| 751 |
+
"endLine": 492,
|
| 752 |
+
"endColumn": 49,
|
| 753 |
+
"charOffset": 16602,
|
| 754 |
+
"charLength": 4
|
| 755 |
+
}
|
| 756 |
+
}
|
| 757 |
+
}
|
| 758 |
+
],
|
| 759 |
+
"partialFingerprints": {
|
| 760 |
+
"contextRegionHash/v1": "4942EC56CF038386F1071CC7BD8F456E480851B507DD38563640F750CD66EEA5"
|
| 761 |
+
},
|
| 762 |
+
"properties": {
|
| 763 |
+
"tags": ["C#",".NET 10.0"]
|
| 764 |
+
}
|
| 765 |
+
},
|
| 766 |
+
{
|
| 767 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 768 |
+
"ruleIndex": 11,
|
| 769 |
+
"level": "warning",
|
| 770 |
+
"message": {
|
| 771 |
+
"text": "Auto-property accessor 'ConsoleOutput.get' is never used"
|
| 772 |
+
},
|
| 773 |
+
"locations": [
|
| 774 |
+
{
|
| 775 |
+
"physicalLocation": {
|
| 776 |
+
"artifactLocation": {
|
| 777 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 778 |
+
"uriBaseId": "solutionDir",
|
| 779 |
+
"index": 15
|
| 780 |
+
},
|
| 781 |
+
"region": {
|
| 782 |
+
"startLine": 466,
|
| 783 |
+
"startColumn": 42,
|
| 784 |
+
"endLine": 466,
|
| 785 |
+
"endColumn": 46,
|
| 786 |
+
"charOffset": 15765,
|
| 787 |
+
"charLength": 4
|
| 788 |
+
}
|
| 789 |
+
}
|
| 790 |
+
}
|
| 791 |
+
],
|
| 792 |
+
"partialFingerprints": {
|
| 793 |
+
"contextRegionHash/v1": "5B36E40C45249457BCF308792A1D3A38F9D2FB1ACA5F0708E24336330911593A"
|
| 794 |
+
},
|
| 795 |
+
"properties": {
|
| 796 |
+
"tags": ["C#",".NET 10.0"]
|
| 797 |
+
}
|
| 798 |
+
},
|
| 799 |
+
{
|
| 800 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 801 |
+
"ruleIndex": 11,
|
| 802 |
+
"level": "warning",
|
| 803 |
+
"message": {
|
| 804 |
+
"text": "Auto-property accessor 'ConsoleOutput.get' is never used"
|
| 805 |
+
},
|
| 806 |
+
"locations": [
|
| 807 |
+
{
|
| 808 |
+
"physicalLocation": {
|
| 809 |
+
"artifactLocation": {
|
| 810 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 811 |
+
"uriBaseId": "solutionDir",
|
| 812 |
+
"index": 15
|
| 813 |
+
},
|
| 814 |
+
"region": {
|
| 815 |
+
"startLine": 500,
|
| 816 |
+
"startColumn": 44,
|
| 817 |
+
"endLine": 500,
|
| 818 |
+
"endColumn": 48,
|
| 819 |
+
"charOffset": 16884,
|
| 820 |
+
"charLength": 4
|
| 821 |
+
}
|
| 822 |
+
}
|
| 823 |
+
}
|
| 824 |
+
],
|
| 825 |
+
"partialFingerprints": {
|
| 826 |
+
"contextRegionHash/v1": "72092E8E74A4D3CC8B3F4EA0C71650D7BD49C4940B5818E56D366F151F989C7E"
|
| 827 |
+
},
|
| 828 |
+
"properties": {
|
| 829 |
+
"tags": ["C#",".NET 10.0"]
|
| 830 |
+
}
|
| 831 |
+
},
|
| 832 |
+
{
|
| 833 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 834 |
+
"ruleIndex": 11,
|
| 835 |
+
"level": "warning",
|
| 836 |
+
"message": {
|
| 837 |
+
"text": "Auto-property accessor 'ContextId.get' is never used"
|
| 838 |
+
},
|
| 839 |
+
"locations": [
|
| 840 |
+
{
|
| 841 |
+
"physicalLocation": {
|
| 842 |
+
"artifactLocation": {
|
| 843 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 844 |
+
"uriBaseId": "solutionDir",
|
| 845 |
+
"index": 15
|
| 846 |
+
},
|
| 847 |
+
"region": {
|
| 848 |
+
"startLine": 510,
|
| 849 |
+
"startColumn": 40,
|
| 850 |
+
"endLine": 510,
|
| 851 |
+
"endColumn": 44,
|
| 852 |
+
"charOffset": 17126,
|
| 853 |
+
"charLength": 4
|
| 854 |
+
}
|
| 855 |
+
}
|
| 856 |
+
}
|
| 857 |
+
],
|
| 858 |
+
"partialFingerprints": {
|
| 859 |
+
"contextRegionHash/v1": "EDFBE9F92E2C66176E99898A9FE54D8339D391D218C393DD341A7C8F1B48F954"
|
| 860 |
+
},
|
| 861 |
+
"properties": {
|
| 862 |
+
"tags": ["C#",".NET 10.0"]
|
| 863 |
+
}
|
| 864 |
+
},
|
| 865 |
+
{
|
| 866 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 867 |
+
"ruleIndex": 11,
|
| 868 |
+
"level": "warning",
|
| 869 |
+
"message": {
|
| 870 |
+
"text": "Auto-property accessor 'CreatedAt.get' is never used"
|
| 871 |
+
},
|
| 872 |
+
"locations": [
|
| 873 |
+
{
|
| 874 |
+
"physicalLocation": {
|
| 875 |
+
"artifactLocation": {
|
| 876 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 877 |
+
"uriBaseId": "solutionDir",
|
| 878 |
+
"index": 15
|
| 879 |
+
},
|
| 880 |
+
"region": {
|
| 881 |
+
"startLine": 514,
|
| 882 |
+
"startColumn": 48,
|
| 883 |
+
"endLine": 514,
|
| 884 |
+
"endColumn": 52,
|
| 885 |
+
"charOffset": 17272,
|
| 886 |
+
"charLength": 4
|
| 887 |
+
}
|
| 888 |
+
}
|
| 889 |
+
}
|
| 890 |
+
],
|
| 891 |
+
"partialFingerprints": {
|
| 892 |
+
"contextRegionHash/v1": "678E199A3B8759B37D4A68079002D617BE3C0D74BA968B77F25A271ADBB38889"
|
| 893 |
+
},
|
| 894 |
+
"properties": {
|
| 895 |
+
"tags": ["C#",".NET 10.0"]
|
| 896 |
+
}
|
| 897 |
+
},
|
| 898 |
+
{
|
| 899 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 900 |
+
"ruleIndex": 11,
|
| 901 |
+
"level": "warning",
|
| 902 |
+
"message": {
|
| 903 |
+
"text": "Auto-property accessor 'Error.get' is never used"
|
| 904 |
+
},
|
| 905 |
+
"locations": [
|
| 906 |
+
{
|
| 907 |
+
"physicalLocation": {
|
| 908 |
+
"artifactLocation": {
|
| 909 |
+
"uri": "tests/RoslynStone.LoadTests/Program.cs",
|
| 910 |
+
"uriBaseId": "solutionDir",
|
| 911 |
+
"index": 5
|
| 912 |
+
},
|
| 913 |
+
"region": {
|
| 914 |
+
"startLine": 248,
|
| 915 |
+
"startColumn": 28,
|
| 916 |
+
"endLine": 248,
|
| 917 |
+
"endColumn": 32,
|
| 918 |
+
"charOffset": 7718,
|
| 919 |
+
"charLength": 4
|
| 920 |
+
}
|
| 921 |
+
}
|
| 922 |
+
}
|
| 923 |
+
],
|
| 924 |
+
"partialFingerprints": {
|
| 925 |
+
"contextRegionHash/v1": "9A188F5AE884B92015422508534588BF9A6A1EA68DBD95E35AC52599CD110205"
|
| 926 |
+
},
|
| 927 |
+
"properties": {
|
| 928 |
+
"tags": ["C#",".NET 10.0"]
|
| 929 |
+
}
|
| 930 |
+
},
|
| 931 |
+
{
|
| 932 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 933 |
+
"ruleIndex": 11,
|
| 934 |
+
"level": "warning",
|
| 935 |
+
"message": {
|
| 936 |
+
"text": "Auto-property accessor 'ExecutionCount.get' is never used"
|
| 937 |
+
},
|
| 938 |
+
"locations": [
|
| 939 |
+
{
|
| 940 |
+
"physicalLocation": {
|
| 941 |
+
"artifactLocation": {
|
| 942 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 943 |
+
"uriBaseId": "solutionDir",
|
| 944 |
+
"index": 15
|
| 945 |
+
},
|
| 946 |
+
"region": {
|
| 947 |
+
"startLine": 522,
|
| 948 |
+
"startColumn": 42,
|
| 949 |
+
"endLine": 522,
|
| 950 |
+
"endColumn": 46,
|
| 951 |
+
"charOffset": 17567,
|
| 952 |
+
"charLength": 4
|
| 953 |
+
}
|
| 954 |
+
}
|
| 955 |
+
}
|
| 956 |
+
],
|
| 957 |
+
"partialFingerprints": {
|
| 958 |
+
"contextRegionHash/v1": "33D4DDFE81C5434C7BB6233A9330DF0ECAAC2D1034D7FC85D609B68084299247"
|
| 959 |
+
},
|
| 960 |
+
"properties": {
|
| 961 |
+
"tags": ["C#",".NET 10.0"]
|
| 962 |
+
}
|
| 963 |
+
},
|
| 964 |
+
{
|
| 965 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 966 |
+
"ruleIndex": 11,
|
| 967 |
+
"level": "warning",
|
| 968 |
+
"message": {
|
| 969 |
+
"text": "Auto-property accessor 'FullDocumentation.get' is never used"
|
| 970 |
+
},
|
| 971 |
+
"locations": [
|
| 972 |
+
{
|
| 973 |
+
"physicalLocation": {
|
| 974 |
+
"artifactLocation": {
|
| 975 |
+
"uri": "src/RoslynStone.Core/Models/DocumentationInfo.cs",
|
| 976 |
+
"uriBaseId": "solutionDir",
|
| 977 |
+
"index": 16
|
| 978 |
+
},
|
| 979 |
+
"region": {
|
| 980 |
+
"startLine": 47,
|
| 981 |
+
"startColumn": 40,
|
| 982 |
+
"endLine": 47,
|
| 983 |
+
"endColumn": 44,
|
| 984 |
+
"charOffset": 1222,
|
| 985 |
+
"charLength": 4
|
| 986 |
+
}
|
| 987 |
+
}
|
| 988 |
+
}
|
| 989 |
+
],
|
| 990 |
+
"partialFingerprints": {
|
| 991 |
+
"contextRegionHash/v1": "9D9D88130515518344978A7A436FC588BB9011C9CC80731BBC53DCCA42E55700"
|
| 992 |
+
},
|
| 993 |
+
"properties": {
|
| 994 |
+
"tags": ["C#",".NET 10.0"]
|
| 995 |
+
}
|
| 996 |
+
},
|
| 997 |
+
{
|
| 998 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 999 |
+
"ruleIndex": 11,
|
| 1000 |
+
"level": "warning",
|
| 1001 |
+
"message": {
|
| 1002 |
+
"text": "Auto-property accessor 'IsInitialized.get' is never used"
|
| 1003 |
+
},
|
| 1004 |
+
"locations": [
|
| 1005 |
+
{
|
| 1006 |
+
"physicalLocation": {
|
| 1007 |
+
"artifactLocation": {
|
| 1008 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1009 |
+
"uriBaseId": "solutionDir",
|
| 1010 |
+
"index": 15
|
| 1011 |
+
},
|
| 1012 |
+
"region": {
|
| 1013 |
+
"startLine": 526,
|
| 1014 |
+
"startColumn": 42,
|
| 1015 |
+
"endLine": 526,
|
| 1016 |
+
"endColumn": 46,
|
| 1017 |
+
"charOffset": 17720,
|
| 1018 |
+
"charLength": 4
|
| 1019 |
+
}
|
| 1020 |
+
}
|
| 1021 |
+
}
|
| 1022 |
+
],
|
| 1023 |
+
"partialFingerprints": {
|
| 1024 |
+
"contextRegionHash/v1": "4562D0E53C45AE57860CE4B38FEC0185ED13C65B9BC3578174EA7263F292F2CC"
|
| 1025 |
+
},
|
| 1026 |
+
"properties": {
|
| 1027 |
+
"tags": ["C#",".NET 10.0"]
|
| 1028 |
+
}
|
| 1029 |
+
},
|
| 1030 |
+
{
|
| 1031 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1032 |
+
"ruleIndex": 11,
|
| 1033 |
+
"level": "warning",
|
| 1034 |
+
"message": {
|
| 1035 |
+
"text": "Auto-property accessor 'LastAccessedAt.get' is never used"
|
| 1036 |
+
},
|
| 1037 |
+
"locations": [
|
| 1038 |
+
{
|
| 1039 |
+
"physicalLocation": {
|
| 1040 |
+
"artifactLocation": {
|
| 1041 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1042 |
+
"uriBaseId": "solutionDir",
|
| 1043 |
+
"index": 15
|
| 1044 |
+
},
|
| 1045 |
+
"region": {
|
| 1046 |
+
"startLine": 518,
|
| 1047 |
+
"startColumn": 53,
|
| 1048 |
+
"endLine": 518,
|
| 1049 |
+
"endColumn": 57,
|
| 1050 |
+
"charOffset": 17423,
|
| 1051 |
+
"charLength": 4
|
| 1052 |
+
}
|
| 1053 |
+
}
|
| 1054 |
+
}
|
| 1055 |
+
],
|
| 1056 |
+
"partialFingerprints": {
|
| 1057 |
+
"contextRegionHash/v1": "3CFCCA125E1D5AD305B2BAA0ED02CF9CAC57BBA29553EFB44384A0CCCBC5ADB6"
|
| 1058 |
+
},
|
| 1059 |
+
"properties": {
|
| 1060 |
+
"tags": ["C#",".NET 10.0"]
|
| 1061 |
+
}
|
| 1062 |
+
},
|
| 1063 |
+
{
|
| 1064 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1065 |
+
"ruleIndex": 11,
|
| 1066 |
+
"level": "warning",
|
| 1067 |
+
"message": {
|
| 1068 |
+
"text": "Auto-property accessor 'Linq.get' is never used"
|
| 1069 |
+
},
|
| 1070 |
+
"locations": [
|
| 1071 |
+
{
|
| 1072 |
+
"physicalLocation": {
|
| 1073 |
+
"artifactLocation": {
|
| 1074 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1075 |
+
"uriBaseId": "solutionDir",
|
| 1076 |
+
"index": 15
|
| 1077 |
+
},
|
| 1078 |
+
"region": {
|
| 1079 |
+
"startLine": 458,
|
| 1080 |
+
"startColumn": 33,
|
| 1081 |
+
"endLine": 458,
|
| 1082 |
+
"endColumn": 37,
|
| 1083 |
+
"charOffset": 15459,
|
| 1084 |
+
"charLength": 4
|
| 1085 |
+
}
|
| 1086 |
+
}
|
| 1087 |
+
}
|
| 1088 |
+
],
|
| 1089 |
+
"partialFingerprints": {
|
| 1090 |
+
"contextRegionHash/v1": "DA43DA145A1FA63D540C099897A7767433DFEB259879ABAED726303C76E732F1"
|
| 1091 |
+
},
|
| 1092 |
+
"properties": {
|
| 1093 |
+
"tags": ["C#",".NET 10.0"]
|
| 1094 |
+
}
|
| 1095 |
+
},
|
| 1096 |
+
{
|
| 1097 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1098 |
+
"ruleIndex": 11,
|
| 1099 |
+
"level": "warning",
|
| 1100 |
+
"message": {
|
| 1101 |
+
"text": "Auto-property accessor 'LinqQuery.get' is never used"
|
| 1102 |
+
},
|
| 1103 |
+
"locations": [
|
| 1104 |
+
{
|
| 1105 |
+
"physicalLocation": {
|
| 1106 |
+
"artifactLocation": {
|
| 1107 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1108 |
+
"uriBaseId": "solutionDir",
|
| 1109 |
+
"index": 15
|
| 1110 |
+
},
|
| 1111 |
+
"region": {
|
| 1112 |
+
"startLine": 496,
|
| 1113 |
+
"startColumn": 40,
|
| 1114 |
+
"endLine": 496,
|
| 1115 |
+
"endColumn": 44,
|
| 1116 |
+
"charOffset": 16737,
|
| 1117 |
+
"charLength": 4
|
| 1118 |
+
}
|
| 1119 |
+
}
|
| 1120 |
+
}
|
| 1121 |
+
],
|
| 1122 |
+
"partialFingerprints": {
|
| 1123 |
+
"contextRegionHash/v1": "36033AB899907239B43E26C5891DA2566206290BD4D91244FC0622760A0299AD"
|
| 1124 |
+
},
|
| 1125 |
+
"properties": {
|
| 1126 |
+
"tags": ["C#",".NET 10.0"]
|
| 1127 |
+
}
|
| 1128 |
+
},
|
| 1129 |
+
{
|
| 1130 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1131 |
+
"ruleIndex": 11,
|
| 1132 |
+
"level": "warning",
|
| 1133 |
+
"message": {
|
| 1134 |
+
"text": "Auto-property accessor 'Message.init' is never used"
|
| 1135 |
+
},
|
| 1136 |
+
"locations": [
|
| 1137 |
+
{
|
| 1138 |
+
"physicalLocation": {
|
| 1139 |
+
"artifactLocation": {
|
| 1140 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1141 |
+
"uriBaseId": "solutionDir",
|
| 1142 |
+
"index": 15
|
| 1143 |
+
},
|
| 1144 |
+
"region": {
|
| 1145 |
+
"startLine": 341,
|
| 1146 |
+
"startColumn": 35,
|
| 1147 |
+
"endLine": 341,
|
| 1148 |
+
"endColumn": 40,
|
| 1149 |
+
"charOffset": 11391,
|
| 1150 |
+
"charLength": 5
|
| 1151 |
+
}
|
| 1152 |
+
}
|
| 1153 |
+
}
|
| 1154 |
+
],
|
| 1155 |
+
"partialFingerprints": {
|
| 1156 |
+
"contextRegionHash/v1": "B9AA76362FEBCF1246BBC4E542A1C858E560A27862F4C2FF33C458636EDB8FF8"
|
| 1157 |
+
},
|
| 1158 |
+
"properties": {
|
| 1159 |
+
"tags": ["C#",".NET 10.0"]
|
| 1160 |
+
}
|
| 1161 |
+
},
|
| 1162 |
+
{
|
| 1163 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1164 |
+
"ruleIndex": 11,
|
| 1165 |
+
"level": "warning",
|
| 1166 |
+
"message": {
|
| 1167 |
+
"text": "Auto-property accessor 'MimeType.get' is never used"
|
| 1168 |
+
},
|
| 1169 |
+
"locations": [
|
| 1170 |
+
{
|
| 1171 |
+
"physicalLocation": {
|
| 1172 |
+
"artifactLocation": {
|
| 1173 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1174 |
+
"uriBaseId": "solutionDir",
|
| 1175 |
+
"index": 15
|
| 1176 |
+
},
|
| 1177 |
+
"region": {
|
| 1178 |
+
"startLine": 84,
|
| 1179 |
+
"startColumn": 39,
|
| 1180 |
+
"endLine": 84,
|
| 1181 |
+
"endColumn": 43,
|
| 1182 |
+
"charOffset": 2855,
|
| 1183 |
+
"charLength": 4
|
| 1184 |
+
}
|
| 1185 |
+
}
|
| 1186 |
+
}
|
| 1187 |
+
],
|
| 1188 |
+
"partialFingerprints": {
|
| 1189 |
+
"contextRegionHash/v1": "698A6F4B56A1A64249A50661630B3CD401138CFB3894F6DB3758819AA6B23DE0"
|
| 1190 |
+
},
|
| 1191 |
+
"properties": {
|
| 1192 |
+
"tags": ["C#",".NET 10.0"]
|
| 1193 |
+
}
|
| 1194 |
+
},
|
| 1195 |
+
{
|
| 1196 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1197 |
+
"ruleIndex": 11,
|
| 1198 |
+
"level": "warning",
|
| 1199 |
+
"message": {
|
| 1200 |
+
"text": "Auto-property accessor 'MimeType.get' is never used"
|
| 1201 |
+
},
|
| 1202 |
+
"locations": [
|
| 1203 |
+
{
|
| 1204 |
+
"physicalLocation": {
|
| 1205 |
+
"artifactLocation": {
|
| 1206 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1207 |
+
"uriBaseId": "solutionDir",
|
| 1208 |
+
"index": 15
|
| 1209 |
+
},
|
| 1210 |
+
"region": {
|
| 1211 |
+
"startLine": 210,
|
| 1212 |
+
"startColumn": 39,
|
| 1213 |
+
"endLine": 210,
|
| 1214 |
+
"endColumn": 43,
|
| 1215 |
+
"charOffset": 7058,
|
| 1216 |
+
"charLength": 4
|
| 1217 |
+
}
|
| 1218 |
+
}
|
| 1219 |
+
}
|
| 1220 |
+
],
|
| 1221 |
+
"partialFingerprints": {
|
| 1222 |
+
"contextRegionHash/v1": "698A6F4B56A1A64249A50661630B3CD401138CFB3894F6DB3758819AA6B23DE0"
|
| 1223 |
+
},
|
| 1224 |
+
"properties": {
|
| 1225 |
+
"tags": ["C#",".NET 10.0"]
|
| 1226 |
+
}
|
| 1227 |
+
},
|
| 1228 |
+
{
|
| 1229 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1230 |
+
"ruleIndex": 11,
|
| 1231 |
+
"level": "warning",
|
| 1232 |
+
"message": {
|
| 1233 |
+
"text": "Auto-property accessor 'MimeType.get' is never used"
|
| 1234 |
+
},
|
| 1235 |
+
"locations": [
|
| 1236 |
+
{
|
| 1237 |
+
"physicalLocation": {
|
| 1238 |
+
"artifactLocation": {
|
| 1239 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1240 |
+
"uriBaseId": "solutionDir",
|
| 1241 |
+
"index": 15
|
| 1242 |
+
},
|
| 1243 |
+
"region": {
|
| 1244 |
+
"startLine": 371,
|
| 1245 |
+
"startColumn": 39,
|
| 1246 |
+
"endLine": 371,
|
| 1247 |
+
"endColumn": 43,
|
| 1248 |
+
"charOffset": 12307,
|
| 1249 |
+
"charLength": 4
|
| 1250 |
+
}
|
| 1251 |
+
}
|
| 1252 |
+
}
|
| 1253 |
+
],
|
| 1254 |
+
"partialFingerprints": {
|
| 1255 |
+
"contextRegionHash/v1": "698A6F4B56A1A64249A50661630B3CD401138CFB3894F6DB3758819AA6B23DE0"
|
| 1256 |
+
},
|
| 1257 |
+
"properties": {
|
| 1258 |
+
"tags": ["C#",".NET 10.0"]
|
| 1259 |
+
}
|
| 1260 |
+
},
|
| 1261 |
+
{
|
| 1262 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1263 |
+
"ruleIndex": 11,
|
| 1264 |
+
"level": "warning",
|
| 1265 |
+
"message": {
|
| 1266 |
+
"text": "Auto-property accessor 'NugetPackages.get' is never used"
|
| 1267 |
+
},
|
| 1268 |
+
"locations": [
|
| 1269 |
+
{
|
| 1270 |
+
"physicalLocation": {
|
| 1271 |
+
"artifactLocation": {
|
| 1272 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1273 |
+
"uriBaseId": "solutionDir",
|
| 1274 |
+
"index": 15
|
| 1275 |
+
},
|
| 1276 |
+
"region": {
|
| 1277 |
+
"startLine": 470,
|
| 1278 |
+
"startColumn": 42,
|
| 1279 |
+
"endLine": 470,
|
| 1280 |
+
"endColumn": 46,
|
| 1281 |
+
"charOffset": 15909,
|
| 1282 |
+
"charLength": 4
|
| 1283 |
+
}
|
| 1284 |
+
}
|
| 1285 |
+
}
|
| 1286 |
+
],
|
| 1287 |
+
"partialFingerprints": {
|
| 1288 |
+
"contextRegionHash/v1": "ADA2EB757819BE7AC32E2222968F836D81742DE21B7D182223A772AD86825760"
|
| 1289 |
+
},
|
| 1290 |
+
"properties": {
|
| 1291 |
+
"tags": ["C#",".NET 10.0"]
|
| 1292 |
+
}
|
| 1293 |
+
},
|
| 1294 |
+
{
|
| 1295 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1296 |
+
"ruleIndex": 11,
|
| 1297 |
+
"level": "warning",
|
| 1298 |
+
"message": {
|
| 1299 |
+
"text": "Auto-property accessor 'PackageId.get' is never used"
|
| 1300 |
+
},
|
| 1301 |
+
"locations": [
|
| 1302 |
+
{
|
| 1303 |
+
"physicalLocation": {
|
| 1304 |
+
"artifactLocation": {
|
| 1305 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1306 |
+
"uriBaseId": "solutionDir",
|
| 1307 |
+
"index": 15
|
| 1308 |
+
},
|
| 1309 |
+
"region": {
|
| 1310 |
+
"startLine": 329,
|
| 1311 |
+
"startColumn": 32,
|
| 1312 |
+
"endLine": 329,
|
| 1313 |
+
"endColumn": 36,
|
| 1314 |
+
"charOffset": 11012,
|
| 1315 |
+
"charLength": 4
|
| 1316 |
+
}
|
| 1317 |
+
}
|
| 1318 |
+
}
|
| 1319 |
+
],
|
| 1320 |
+
"partialFingerprints": {
|
| 1321 |
+
"contextRegionHash/v1": "A897DFF662181F9587254BBD1982DA6725246CC252CDCE790B4F0D2BBA167C8A"
|
| 1322 |
+
},
|
| 1323 |
+
"properties": {
|
| 1324 |
+
"tags": ["C#",".NET 10.0"]
|
| 1325 |
+
}
|
| 1326 |
+
},
|
| 1327 |
+
{
|
| 1328 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1329 |
+
"ruleIndex": 11,
|
| 1330 |
+
"level": "warning",
|
| 1331 |
+
"message": {
|
| 1332 |
+
"text": "Auto-property accessor 'ServerStartupTimeoutSeconds.get' is never used"
|
| 1333 |
+
},
|
| 1334 |
+
"locations": [
|
| 1335 |
+
{
|
| 1336 |
+
"physicalLocation": {
|
| 1337 |
+
"artifactLocation": {
|
| 1338 |
+
"uri": "tests/RoslynStone.GradioTests/McpServerFixture.cs",
|
| 1339 |
+
"uriBaseId": "solutionDir",
|
| 1340 |
+
"index": 17
|
| 1341 |
+
},
|
| 1342 |
+
"region": {
|
| 1343 |
+
"startLine": 40,
|
| 1344 |
+
"startColumn": 46,
|
| 1345 |
+
"endLine": 40,
|
| 1346 |
+
"endColumn": 50,
|
| 1347 |
+
"charOffset": 1486,
|
| 1348 |
+
"charLength": 4
|
| 1349 |
+
}
|
| 1350 |
+
}
|
| 1351 |
+
}
|
| 1352 |
+
],
|
| 1353 |
+
"partialFingerprints": {
|
| 1354 |
+
"contextRegionHash/v1": "C38BF4E81B411AE537D7348B60F5E087521530868CCA1C7B00584C5A5AE6A9DB"
|
| 1355 |
+
},
|
| 1356 |
+
"properties": {
|
| 1357 |
+
"tags": ["C#",".NET 10.0"]
|
| 1358 |
+
}
|
| 1359 |
+
},
|
| 1360 |
+
{
|
| 1361 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1362 |
+
"ruleIndex": 11,
|
| 1363 |
+
"level": "warning",
|
| 1364 |
+
"message": {
|
| 1365 |
+
"text": "Auto-property accessor 'SimpleExpression.get' is never used"
|
| 1366 |
+
},
|
| 1367 |
+
"locations": [
|
| 1368 |
+
{
|
| 1369 |
+
"physicalLocation": {
|
| 1370 |
+
"artifactLocation": {
|
| 1371 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1372 |
+
"uriBaseId": "solutionDir",
|
| 1373 |
+
"index": 15
|
| 1374 |
+
},
|
| 1375 |
+
"region": {
|
| 1376 |
+
"startLine": 484,
|
| 1377 |
+
"startColumn": 47,
|
| 1378 |
+
"endLine": 484,
|
| 1379 |
+
"endColumn": 51,
|
| 1380 |
+
"charOffset": 16287,
|
| 1381 |
+
"charLength": 4
|
| 1382 |
+
}
|
| 1383 |
+
}
|
| 1384 |
+
}
|
| 1385 |
+
],
|
| 1386 |
+
"partialFingerprints": {
|
| 1387 |
+
"contextRegionHash/v1": "550EAAC163142DC84C0ED60ACF2EF8ABB8D03D99B681DBDD9BD80A687F74C46E"
|
| 1388 |
+
},
|
| 1389 |
+
"properties": {
|
| 1390 |
+
"tags": ["C#",".NET 10.0"]
|
| 1391 |
+
}
|
| 1392 |
+
},
|
| 1393 |
+
{
|
| 1394 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1395 |
+
"ruleIndex": 11,
|
| 1396 |
+
"level": "warning",
|
| 1397 |
+
"message": {
|
| 1398 |
+
"text": "Auto-property accessor 'Skip.get' is never used"
|
| 1399 |
+
},
|
| 1400 |
+
"locations": [
|
| 1401 |
+
{
|
| 1402 |
+
"physicalLocation": {
|
| 1403 |
+
"artifactLocation": {
|
| 1404 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1405 |
+
"uriBaseId": "solutionDir",
|
| 1406 |
+
"index": 15
|
| 1407 |
+
},
|
| 1408 |
+
"region": {
|
| 1409 |
+
"startLine": 100,
|
| 1410 |
+
"startColumn": 32,
|
| 1411 |
+
"endLine": 100,
|
| 1412 |
+
"endColumn": 36,
|
| 1413 |
+
"charOffset": 3377,
|
| 1414 |
+
"charLength": 4
|
| 1415 |
+
}
|
| 1416 |
+
}
|
| 1417 |
+
}
|
| 1418 |
+
],
|
| 1419 |
+
"partialFingerprints": {
|
| 1420 |
+
"contextRegionHash/v1": "D74124FA9051E4F0AFFD1B4613872ECCE92EF1CE738706E9CB4BBC06C631AEB2"
|
| 1421 |
+
},
|
| 1422 |
+
"properties": {
|
| 1423 |
+
"tags": ["C#",".NET 10.0"]
|
| 1424 |
+
}
|
| 1425 |
+
},
|
| 1426 |
+
{
|
| 1427 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1428 |
+
"ruleIndex": 11,
|
| 1429 |
+
"level": "warning",
|
| 1430 |
+
"message": {
|
| 1431 |
+
"text": "Auto-property accessor 'Statefulness.get' is never used"
|
| 1432 |
+
},
|
| 1433 |
+
"locations": [
|
| 1434 |
+
{
|
| 1435 |
+
"physicalLocation": {
|
| 1436 |
+
"artifactLocation": {
|
| 1437 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1438 |
+
"uriBaseId": "solutionDir",
|
| 1439 |
+
"index": 15
|
| 1440 |
+
},
|
| 1441 |
+
"region": {
|
| 1442 |
+
"startLine": 474,
|
| 1443 |
+
"startColumn": 41,
|
| 1444 |
+
"endLine": 474,
|
| 1445 |
+
"endColumn": 45,
|
| 1446 |
+
"charOffset": 16048,
|
| 1447 |
+
"charLength": 4
|
| 1448 |
+
}
|
| 1449 |
+
}
|
| 1450 |
+
}
|
| 1451 |
+
],
|
| 1452 |
+
"partialFingerprints": {
|
| 1453 |
+
"contextRegionHash/v1": "90A7B1AF878588302DC2A80BD43B0870045C16D7CF47EB03964E7BB91E1B1B2C"
|
| 1454 |
+
},
|
| 1455 |
+
"properties": {
|
| 1456 |
+
"tags": ["C#",".NET 10.0"]
|
| 1457 |
+
}
|
| 1458 |
+
},
|
| 1459 |
+
{
|
| 1460 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1461 |
+
"ruleIndex": 11,
|
| 1462 |
+
"level": "warning",
|
| 1463 |
+
"message": {
|
| 1464 |
+
"text": "Auto-property accessor 'StatusCode.get' is never used"
|
| 1465 |
+
},
|
| 1466 |
+
"locations": [
|
| 1467 |
+
{
|
| 1468 |
+
"physicalLocation": {
|
| 1469 |
+
"artifactLocation": {
|
| 1470 |
+
"uri": "tests/RoslynStone.LoadTests/Program.cs",
|
| 1471 |
+
"uriBaseId": "solutionDir",
|
| 1472 |
+
"index": 5
|
| 1473 |
+
},
|
| 1474 |
+
"region": {
|
| 1475 |
+
"startLine": 247,
|
| 1476 |
+
"startColumn": 29,
|
| 1477 |
+
"endLine": 247,
|
| 1478 |
+
"endColumn": 33,
|
| 1479 |
+
"charOffset": 7678,
|
| 1480 |
+
"charLength": 4
|
| 1481 |
+
}
|
| 1482 |
+
}
|
| 1483 |
+
}
|
| 1484 |
+
],
|
| 1485 |
+
"partialFingerprints": {
|
| 1486 |
+
"contextRegionHash/v1": "01A97160D1DF1D659679D975BE322AFDD50130AB125CC2C3E2655FBA5EEF5C79"
|
| 1487 |
+
},
|
| 1488 |
+
"properties": {
|
| 1489 |
+
"tags": ["C#",".NET 10.0"]
|
| 1490 |
+
}
|
| 1491 |
+
},
|
| 1492 |
+
{
|
| 1493 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1494 |
+
"ruleIndex": 11,
|
| 1495 |
+
"level": "warning",
|
| 1496 |
+
"message": {
|
| 1497 |
+
"text": "Auto-property accessor 'Take.get' is never used"
|
| 1498 |
+
},
|
| 1499 |
+
"locations": [
|
| 1500 |
+
{
|
| 1501 |
+
"physicalLocation": {
|
| 1502 |
+
"artifactLocation": {
|
| 1503 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1504 |
+
"uriBaseId": "solutionDir",
|
| 1505 |
+
"index": 15
|
| 1506 |
+
},
|
| 1507 |
+
"region": {
|
| 1508 |
+
"startLine": 104,
|
| 1509 |
+
"startColumn": 32,
|
| 1510 |
+
"endLine": 104,
|
| 1511 |
+
"endColumn": 36,
|
| 1512 |
+
"charOffset": 3496,
|
| 1513 |
+
"charLength": 4
|
| 1514 |
+
}
|
| 1515 |
+
}
|
| 1516 |
+
}
|
| 1517 |
+
],
|
| 1518 |
+
"partialFingerprints": {
|
| 1519 |
+
"contextRegionHash/v1": "AF36FD27D7A8DA5BAF94E98EF7D861EAC4DF5F1303690D924677717ED7FF3E0F"
|
| 1520 |
+
},
|
| 1521 |
+
"properties": {
|
| 1522 |
+
"tags": ["C#",".NET 10.0"]
|
| 1523 |
+
}
|
| 1524 |
+
},
|
| 1525 |
+
{
|
| 1526 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1527 |
+
"ruleIndex": 11,
|
| 1528 |
+
"level": "warning",
|
| 1529 |
+
"message": {
|
| 1530 |
+
"text": "Auto-property accessor 'TopLevelStatements.get' is never used"
|
| 1531 |
+
},
|
| 1532 |
+
"locations": [
|
| 1533 |
+
{
|
| 1534 |
+
"physicalLocation": {
|
| 1535 |
+
"artifactLocation": {
|
| 1536 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1537 |
+
"uriBaseId": "solutionDir",
|
| 1538 |
+
"index": 15
|
| 1539 |
+
},
|
| 1540 |
+
"region": {
|
| 1541 |
+
"startLine": 462,
|
| 1542 |
+
"startColumn": 47,
|
| 1543 |
+
"endLine": 462,
|
| 1544 |
+
"endColumn": 51,
|
| 1545 |
+
"charOffset": 15620,
|
| 1546 |
+
"charLength": 4
|
| 1547 |
+
}
|
| 1548 |
+
}
|
| 1549 |
+
}
|
| 1550 |
+
],
|
| 1551 |
+
"partialFingerprints": {
|
| 1552 |
+
"contextRegionHash/v1": "DE5635F0924865CB03805C1CE5CED1C875D5C7560CA246DE954A4BA192A18C1D"
|
| 1553 |
+
},
|
| 1554 |
+
"properties": {
|
| 1555 |
+
"tags": ["C#",".NET 10.0"]
|
| 1556 |
+
}
|
| 1557 |
+
},
|
| 1558 |
+
{
|
| 1559 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1560 |
+
"ruleIndex": 11,
|
| 1561 |
+
"level": "warning",
|
| 1562 |
+
"message": {
|
| 1563 |
+
"text": "Auto-property accessor 'VariableDeclaration.get' is never used"
|
| 1564 |
+
},
|
| 1565 |
+
"locations": [
|
| 1566 |
+
{
|
| 1567 |
+
"physicalLocation": {
|
| 1568 |
+
"artifactLocation": {
|
| 1569 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1570 |
+
"uriBaseId": "solutionDir",
|
| 1571 |
+
"index": 15
|
| 1572 |
+
},
|
| 1573 |
+
"region": {
|
| 1574 |
+
"startLine": 488,
|
| 1575 |
+
"startColumn": 50,
|
| 1576 |
+
"endLine": 488,
|
| 1577 |
+
"endColumn": 54,
|
| 1578 |
+
"charOffset": 16452,
|
| 1579 |
+
"charLength": 4
|
| 1580 |
+
}
|
| 1581 |
+
}
|
| 1582 |
+
}
|
| 1583 |
+
],
|
| 1584 |
+
"partialFingerprints": {
|
| 1585 |
+
"contextRegionHash/v1": "15A5AC77BA9A349C70BF49AC7757494954080F9EC5B490D5D848C4CB56D8EE1B"
|
| 1586 |
+
},
|
| 1587 |
+
"properties": {
|
| 1588 |
+
"tags": ["C#",".NET 10.0"]
|
| 1589 |
+
}
|
| 1590 |
+
},
|
| 1591 |
+
{
|
| 1592 |
+
"ruleId": "UnusedAutoPropertyAccessor.Global",
|
| 1593 |
+
"ruleIndex": 11,
|
| 1594 |
+
"level": "warning",
|
| 1595 |
+
"message": {
|
| 1596 |
+
"text": "Auto-property accessor 'Version.get' is never used"
|
| 1597 |
+
},
|
| 1598 |
+
"locations": [
|
| 1599 |
+
{
|
| 1600 |
+
"physicalLocation": {
|
| 1601 |
+
"artifactLocation": {
|
| 1602 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 1603 |
+
"uriBaseId": "solutionDir",
|
| 1604 |
+
"index": 15
|
| 1605 |
+
},
|
| 1606 |
+
"region": {
|
| 1607 |
+
"startLine": 333,
|
| 1608 |
+
"startColumn": 30,
|
| 1609 |
+
"endLine": 333,
|
| 1610 |
+
"endColumn": 34,
|
| 1611 |
+
"charOffset": 11132,
|
| 1612 |
+
"charLength": 4
|
| 1613 |
+
}
|
| 1614 |
+
}
|
| 1615 |
+
}
|
| 1616 |
+
],
|
| 1617 |
+
"partialFingerprints": {
|
| 1618 |
+
"contextRegionHash/v1": "AAC1FB0B7029FCE121684BADAF353DDE7BD39D830E2F854E2BD596F39BBB711F"
|
| 1619 |
+
},
|
| 1620 |
+
"properties": {
|
| 1621 |
+
"tags": ["C#",".NET 10.0"]
|
| 1622 |
+
}
|
| 1623 |
+
},
|
| 1624 |
+
{
|
| 1625 |
+
"ruleId": "UnusedParameter.Local",
|
| 1626 |
+
"ruleIndex": 12,
|
| 1627 |
+
"level": "warning",
|
| 1628 |
+
"message": {
|
| 1629 |
+
"text": "Parameter 'scriptingService' is never used"
|
| 1630 |
+
},
|
| 1631 |
+
"locations": [
|
| 1632 |
+
{
|
| 1633 |
+
"physicalLocation": {
|
| 1634 |
+
"artifactLocation": {
|
| 1635 |
+
"uri": "src/RoslynStone.Infrastructure/Resources/ReplStateResource.cs",
|
| 1636 |
+
"uriBaseId": "solutionDir",
|
| 1637 |
+
"index": 18
|
| 1638 |
+
},
|
| 1639 |
+
"region": {
|
| 1640 |
+
"startLine": 83,
|
| 1641 |
+
"startColumn": 32,
|
| 1642 |
+
"endLine": 83,
|
| 1643 |
+
"endColumn": 48,
|
| 1644 |
+
"charOffset": 3834,
|
| 1645 |
+
"charLength": 16
|
| 1646 |
+
}
|
| 1647 |
+
}
|
| 1648 |
+
}
|
| 1649 |
+
],
|
| 1650 |
+
"partialFingerprints": {
|
| 1651 |
+
"contextRegionHash/v1": "1BBA346407D700F53892BA71452A2582A7EEA45FF9BCC21D0F9BD2E78CFC2A55"
|
| 1652 |
+
},
|
| 1653 |
+
"properties": {
|
| 1654 |
+
"tags": ["C#",".NET 10.0"]
|
| 1655 |
+
}
|
| 1656 |
+
},
|
| 1657 |
+
{
|
| 1658 |
+
"ruleId": "UnusedVariable",
|
| 1659 |
+
"ruleIndex": 13,
|
| 1660 |
+
"level": "warning",
|
| 1661 |
+
"message": {
|
| 1662 |
+
"text": "Local variable 'inspector' is never used"
|
| 1663 |
+
},
|
| 1664 |
+
"locations": [
|
| 1665 |
+
{
|
| 1666 |
+
"physicalLocation": {
|
| 1667 |
+
"artifactLocation": {
|
| 1668 |
+
"uri": "src/RoslynStone.AppHost/AppHost.cs",
|
| 1669 |
+
"uriBaseId": "solutionDir",
|
| 1670 |
+
"index": 19
|
| 1671 |
+
},
|
| 1672 |
+
"region": {
|
| 1673 |
+
"startLine": 39,
|
| 1674 |
+
"startColumn": 9,
|
| 1675 |
+
"endLine": 39,
|
| 1676 |
+
"endColumn": 18,
|
| 1677 |
+
"charOffset": 1813,
|
| 1678 |
+
"charLength": 9
|
| 1679 |
+
}
|
| 1680 |
+
}
|
| 1681 |
+
}
|
| 1682 |
+
],
|
| 1683 |
+
"partialFingerprints": {
|
| 1684 |
+
"contextRegionHash/v1": "7048B045E11EAAAE19DC0FECFE88381756401DFE4BAA2806B0804794EF3E061B"
|
| 1685 |
+
},
|
| 1686 |
+
"properties": {
|
| 1687 |
+
"tags": ["C#",".NET 10.0"]
|
| 1688 |
+
}
|
| 1689 |
+
},
|
| 1690 |
+
{
|
| 1691 |
+
"ruleId": "UnusedVariable",
|
| 1692 |
+
"ruleIndex": 13,
|
| 1693 |
+
"level": "warning",
|
| 1694 |
+
"message": {
|
| 1695 |
+
"text": "Local variable 'uri' is never used"
|
| 1696 |
+
},
|
| 1697 |
+
"locations": [
|
| 1698 |
+
{
|
| 1699 |
+
"physicalLocation": {
|
| 1700 |
+
"artifactLocation": {
|
| 1701 |
+
"uri": "tests/RoslynStone.GradioTests/McpServerFixture.cs",
|
| 1702 |
+
"uriBaseId": "solutionDir",
|
| 1703 |
+
"index": 17
|
| 1704 |
+
},
|
| 1705 |
+
"region": {
|
| 1706 |
+
"startLine": 103,
|
| 1707 |
+
"startColumn": 17,
|
| 1708 |
+
"endLine": 103,
|
| 1709 |
+
"endColumn": 20,
|
| 1710 |
+
"charOffset": 3584,
|
| 1711 |
+
"charLength": 3
|
| 1712 |
+
}
|
| 1713 |
+
}
|
| 1714 |
+
}
|
| 1715 |
+
],
|
| 1716 |
+
"partialFingerprints": {
|
| 1717 |
+
"contextRegionHash/v1": "DBC029ACBDF7C7EC9649220F938D67208D0C14B08AC17A62605018CA579DC9AF"
|
| 1718 |
+
},
|
| 1719 |
+
"properties": {
|
| 1720 |
+
"tags": ["C#",".NET 10.0"]
|
| 1721 |
+
}
|
| 1722 |
+
},
|
| 1723 |
+
{
|
| 1724 |
+
"ruleId": "UsingStatementResourceInitialization",
|
| 1725 |
+
"ruleIndex": 14,
|
| 1726 |
+
"level": "warning",
|
| 1727 |
+
"message": {
|
| 1728 |
+
"text": "Initialize object properties inside the 'using' statement to ensure that the object is disposed if an exception is thrown during initialization"
|
| 1729 |
+
},
|
| 1730 |
+
"locations": [
|
| 1731 |
+
{
|
| 1732 |
+
"physicalLocation": {
|
| 1733 |
+
"artifactLocation": {
|
| 1734 |
+
"uri": "tests/RoslynStone.GradioTests/McpServerFixture.cs",
|
| 1735 |
+
"uriBaseId": "solutionDir",
|
| 1736 |
+
"index": 17
|
| 1737 |
+
},
|
| 1738 |
+
"region": {
|
| 1739 |
+
"startLine": 253,
|
| 1740 |
+
"startColumn": 32,
|
| 1741 |
+
"endLine": 253,
|
| 1742 |
+
"endColumn": 35,
|
| 1743 |
+
"charOffset": 9776,
|
| 1744 |
+
"charLength": 3
|
| 1745 |
+
}
|
| 1746 |
+
}
|
| 1747 |
+
}
|
| 1748 |
+
],
|
| 1749 |
+
"partialFingerprints": {
|
| 1750 |
+
"contextRegionHash/v1": "F7B2AE26D660B537A5F1641E6D228C52D1BA4C8A3DF9D48D24E0E2C485DF6BC8"
|
| 1751 |
+
},
|
| 1752 |
+
"properties": {
|
| 1753 |
+
"tags": ["C#",".NET 10.0"]
|
| 1754 |
+
}
|
| 1755 |
+
},
|
| 1756 |
+
{
|
| 1757 |
+
"ruleId": "UsingStatementResourceInitialization",
|
| 1758 |
+
"ruleIndex": 14,
|
| 1759 |
+
"level": "warning",
|
| 1760 |
+
"message": {
|
| 1761 |
+
"text": "Initialize object properties inside the 'using' statement to ensure that the object is disposed if an exception is thrown during initialization"
|
| 1762 |
+
},
|
| 1763 |
+
"locations": [
|
| 1764 |
+
{
|
| 1765 |
+
"physicalLocation": {
|
| 1766 |
+
"artifactLocation": {
|
| 1767 |
+
"uri": "tests/RoslynStone.GradioTests/McpServerFixture.cs",
|
| 1768 |
+
"uriBaseId": "solutionDir",
|
| 1769 |
+
"index": 17
|
| 1770 |
+
},
|
| 1771 |
+
"region": {
|
| 1772 |
+
"startLine": 305,
|
| 1773 |
+
"startColumn": 32,
|
| 1774 |
+
"endLine": 305,
|
| 1775 |
+
"endColumn": 35,
|
| 1776 |
+
"charOffset": 11913,
|
| 1777 |
+
"charLength": 3
|
| 1778 |
+
}
|
| 1779 |
+
}
|
| 1780 |
+
}
|
| 1781 |
+
],
|
| 1782 |
+
"partialFingerprints": {
|
| 1783 |
+
"contextRegionHash/v1": "BFE553BC5B26D63E5B7524C294589E35A9DE320531F080024E77EEB034709435"
|
| 1784 |
+
},
|
| 1785 |
+
"properties": {
|
| 1786 |
+
"tags": ["C#",".NET 10.0"]
|
| 1787 |
+
}
|
| 1788 |
+
},
|
| 1789 |
+
{
|
| 1790 |
+
"ruleId": "UsingStatementResourceInitialization",
|
| 1791 |
+
"ruleIndex": 14,
|
| 1792 |
+
"level": "warning",
|
| 1793 |
+
"message": {
|
| 1794 |
+
"text": "Initialize object properties inside the 'using' statement to ensure that the object is disposed if an exception is thrown during initialization"
|
| 1795 |
+
},
|
| 1796 |
+
"locations": [
|
| 1797 |
+
{
|
| 1798 |
+
"physicalLocation": {
|
| 1799 |
+
"artifactLocation": {
|
| 1800 |
+
"uri": "tests/RoslynStone.LoadTests/Program.cs",
|
| 1801 |
+
"uriBaseId": "solutionDir",
|
| 1802 |
+
"index": 5
|
| 1803 |
+
},
|
| 1804 |
+
"region": {
|
| 1805 |
+
"startLine": 32,
|
| 1806 |
+
"startColumn": 28,
|
| 1807 |
+
"endLine": 32,
|
| 1808 |
+
"endColumn": 31,
|
| 1809 |
+
"charOffset": 1278,
|
| 1810 |
+
"charLength": 3
|
| 1811 |
+
}
|
| 1812 |
+
}
|
| 1813 |
+
}
|
| 1814 |
+
],
|
| 1815 |
+
"partialFingerprints": {
|
| 1816 |
+
"contextRegionHash/v1": "BD5170AB9A9409CB166C467AA1F273B943D79477CD6DD2E7A6026B9C5339E67A"
|
| 1817 |
+
},
|
| 1818 |
+
"properties": {
|
| 1819 |
+
"tags": ["C#",".NET 10.0"]
|
| 1820 |
+
}
|
| 1821 |
+
}
|
| 1822 |
+
],
|
| 1823 |
+
"tool": {
|
| 1824 |
+
"driver": {
|
| 1825 |
+
"name": "InspectCode",
|
| 1826 |
+
"organization": "JetBrains, Inc",
|
| 1827 |
+
"fullName": "JetBrains Inspect Code 2025.3.0.2",
|
| 1828 |
+
"semanticVersion": "253.0.20251120.23936",
|
| 1829 |
+
"informationUri": "http://www.jetbrains.com/resharper/features/command-line.html",
|
| 1830 |
+
"rules": [
|
| 1831 |
+
{
|
| 1832 |
+
"id": "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract",
|
| 1833 |
+
"fullDescription": {
|
| 1834 |
+
"text": "Conditional access qualifier expression is not null according to nullable reference types' annotations"
|
| 1835 |
+
},
|
| 1836 |
+
"defaultConfiguration": {
|
| 1837 |
+
"level": "warning"
|
| 1838 |
+
},
|
| 1839 |
+
"relationships": [
|
| 1840 |
+
{
|
| 1841 |
+
"target": {
|
| 1842 |
+
"id": "CSHARP.CodeRedundancy",
|
| 1843 |
+
"toolComponent": {
|
| 1844 |
+
"name": "InspectCode"
|
| 1845 |
+
}
|
| 1846 |
+
},
|
| 1847 |
+
"kinds": [
|
| 1848 |
+
"superset"
|
| 1849 |
+
]
|
| 1850 |
+
}
|
| 1851 |
+
]
|
| 1852 |
+
},
|
| 1853 |
+
{
|
| 1854 |
+
"id": "InconsistentNaming",
|
| 1855 |
+
"fullDescription": {
|
| 1856 |
+
"text": "Name doesn't match naming style defined for this kind of symbol"
|
| 1857 |
+
},
|
| 1858 |
+
"help": {
|
| 1859 |
+
"text": "https://www.jetbrains.com/help/resharper/InconsistentNaming.html"
|
| 1860 |
+
},
|
| 1861 |
+
"shortDescription": {
|
| 1862 |
+
"text": "Inconsistent Naming"
|
| 1863 |
+
},
|
| 1864 |
+
"defaultConfiguration": {
|
| 1865 |
+
"level": "warning"
|
| 1866 |
+
},
|
| 1867 |
+
"helpUri": "https://www.jetbrains.com/help/resharper/InconsistentNaming.html",
|
| 1868 |
+
"relationships": [
|
| 1869 |
+
{
|
| 1870 |
+
"target": {
|
| 1871 |
+
"id": "CSHARP.ConstraintViolation",
|
| 1872 |
+
"toolComponent": {
|
| 1873 |
+
"name": "InspectCode"
|
| 1874 |
+
}
|
| 1875 |
+
},
|
| 1876 |
+
"kinds": [
|
| 1877 |
+
"superset"
|
| 1878 |
+
]
|
| 1879 |
+
},
|
| 1880 |
+
{
|
| 1881 |
+
"target": {
|
| 1882 |
+
"id": "VBASIC.ConstraintViolation",
|
| 1883 |
+
"toolComponent": {
|
| 1884 |
+
"name": "InspectCode"
|
| 1885 |
+
}
|
| 1886 |
+
},
|
| 1887 |
+
"kinds": [
|
| 1888 |
+
"superset"
|
| 1889 |
+
]
|
| 1890 |
+
},
|
| 1891 |
+
{
|
| 1892 |
+
"target": {
|
| 1893 |
+
"id": "XAML.ConstraintViolation",
|
| 1894 |
+
"toolComponent": {
|
| 1895 |
+
"name": "InspectCode"
|
| 1896 |
+
}
|
| 1897 |
+
},
|
| 1898 |
+
"kinds": [
|
| 1899 |
+
"superset"
|
| 1900 |
+
]
|
| 1901 |
+
}
|
| 1902 |
+
]
|
| 1903 |
+
},
|
| 1904 |
+
{
|
| 1905 |
+
"id": "NotAccessedField.Local",
|
| 1906 |
+
"fullDescription": {
|
| 1907 |
+
"text": "Field is assigned but its value is never used"
|
| 1908 |
+
},
|
| 1909 |
+
"help": {
|
| 1910 |
+
"text": "https://www.jetbrains.com/help/resharper/NotAccessedField.Local.html"
|
| 1911 |
+
},
|
| 1912 |
+
"shortDescription": {
|
| 1913 |
+
"text": "Non-accessed field: Private accessibility"
|
| 1914 |
+
},
|
| 1915 |
+
"defaultConfiguration": {
|
| 1916 |
+
"level": "warning"
|
| 1917 |
+
},
|
| 1918 |
+
"helpUri": "https://www.jetbrains.com/help/resharper/NotAccessedField.Local.html",
|
| 1919 |
+
"relationships": [
|
| 1920 |
+
{
|
| 1921 |
+
"target": {
|
| 1922 |
+
"id": "CSHARP.CodeSmell",
|
| 1923 |
+
"toolComponent": {
|
| 1924 |
+
"name": "InspectCode"
|
| 1925 |
+
}
|
| 1926 |
+
},
|
| 1927 |
+
"kinds": [
|
| 1928 |
+
"superset"
|
| 1929 |
+
]
|
| 1930 |
+
},
|
| 1931 |
+
{
|
| 1932 |
+
"target": {
|
| 1933 |
+
"id": "VBASIC.CodeSmell",
|
| 1934 |
+
"toolComponent": {
|
| 1935 |
+
"name": "InspectCode"
|
| 1936 |
+
}
|
| 1937 |
+
},
|
| 1938 |
+
"kinds": [
|
| 1939 |
+
"superset"
|
| 1940 |
+
]
|
| 1941 |
+
},
|
| 1942 |
+
{
|
| 1943 |
+
"target": {
|
| 1944 |
+
"id": "XAML.CodeSmell",
|
| 1945 |
+
"toolComponent": {
|
| 1946 |
+
"name": "InspectCode"
|
| 1947 |
+
}
|
| 1948 |
+
},
|
| 1949 |
+
"kinds": [
|
| 1950 |
+
"superset"
|
| 1951 |
+
]
|
| 1952 |
+
}
|
| 1953 |
+
]
|
| 1954 |
+
},
|
| 1955 |
+
{
|
| 1956 |
+
"id": "NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract",
|
| 1957 |
+
"fullDescription": {
|
| 1958 |
+
"text": "'??' condition is never null according to nullable reference types' annotations"
|
| 1959 |
+
},
|
| 1960 |
+
"defaultConfiguration": {
|
| 1961 |
+
"level": "warning"
|
| 1962 |
+
},
|
| 1963 |
+
"relationships": [
|
| 1964 |
+
{
|
| 1965 |
+
"target": {
|
| 1966 |
+
"id": "CSHARP.CodeRedundancy",
|
| 1967 |
+
"toolComponent": {
|
| 1968 |
+
"name": "InspectCode"
|
| 1969 |
+
}
|
| 1970 |
+
},
|
| 1971 |
+
"kinds": [
|
| 1972 |
+
"superset"
|
| 1973 |
+
]
|
| 1974 |
+
}
|
| 1975 |
+
]
|
| 1976 |
+
},
|
| 1977 |
+
{
|
| 1978 |
+
"id": "PossibleMultipleEnumeration",
|
| 1979 |
+
"fullDescription": {
|
| 1980 |
+
"text": "Possible multiple enumeration of IEnumerable or IAsyncEnumerable<T>"
|
| 1981 |
+
},
|
| 1982 |
+
"help": {
|
| 1983 |
+
"text": "https://www.jetbrains.com/help/resharper/PossibleMultipleEnumeration.html"
|
| 1984 |
+
},
|
| 1985 |
+
"shortDescription": {
|
| 1986 |
+
"text": "Possible multiple enumeration"
|
| 1987 |
+
},
|
| 1988 |
+
"defaultConfiguration": {
|
| 1989 |
+
"level": "warning"
|
| 1990 |
+
},
|
| 1991 |
+
"helpUri": "https://www.jetbrains.com/help/resharper/PossibleMultipleEnumeration.html",
|
| 1992 |
+
"relationships": [
|
| 1993 |
+
{
|
| 1994 |
+
"target": {
|
| 1995 |
+
"id": "CSHARP.CodeSmell",
|
| 1996 |
+
"toolComponent": {
|
| 1997 |
+
"name": "InspectCode"
|
| 1998 |
+
}
|
| 1999 |
+
},
|
| 2000 |
+
"kinds": [
|
| 2001 |
+
"superset"
|
| 2002 |
+
]
|
| 2003 |
+
},
|
| 2004 |
+
{
|
| 2005 |
+
"target": {
|
| 2006 |
+
"id": "VBASIC.CodeSmell",
|
| 2007 |
+
"toolComponent": {
|
| 2008 |
+
"name": "InspectCode"
|
| 2009 |
+
}
|
| 2010 |
+
},
|
| 2011 |
+
"kinds": [
|
| 2012 |
+
"superset"
|
| 2013 |
+
]
|
| 2014 |
+
}
|
| 2015 |
+
]
|
| 2016 |
+
},
|
| 2017 |
+
{
|
| 2018 |
+
"id": "PrivateFieldCanBeConvertedToLocalVariable",
|
| 2019 |
+
"fullDescription": {
|
| 2020 |
+
"text": "Private field is always assigned before being used and can be converted into a local variable"
|
| 2021 |
+
},
|
| 2022 |
+
"help": {
|
| 2023 |
+
"text": "https://www.jetbrains.com/help/resharper/PrivateFieldCanBeConvertedToLocalVariable.html"
|
| 2024 |
+
},
|
| 2025 |
+
"shortDescription": {
|
| 2026 |
+
"text": "Private field can be converted into local variable"
|
| 2027 |
+
},
|
| 2028 |
+
"defaultConfiguration": {
|
| 2029 |
+
"level": "warning"
|
| 2030 |
+
},
|
| 2031 |
+
"helpUri": "https://www.jetbrains.com/help/resharper/PrivateFieldCanBeConvertedToLocalVariable.html",
|
| 2032 |
+
"relationships": [
|
| 2033 |
+
{
|
| 2034 |
+
"target": {
|
| 2035 |
+
"id": "CSHARP.CodeRedundancy",
|
| 2036 |
+
"toolComponent": {
|
| 2037 |
+
"name": "InspectCode"
|
| 2038 |
+
}
|
| 2039 |
+
},
|
| 2040 |
+
"kinds": [
|
| 2041 |
+
"superset"
|
| 2042 |
+
]
|
| 2043 |
+
}
|
| 2044 |
+
]
|
| 2045 |
+
},
|
| 2046 |
+
{
|
| 2047 |
+
"id": "RedundantArgumentDefaultValue",
|
| 2048 |
+
"fullDescription": {
|
| 2049 |
+
"text": "Corresponding parameter is optional and has the same value, so the argument could be omitted"
|
| 2050 |
+
},
|
| 2051 |
+
"help": {
|
| 2052 |
+
"text": "https://www.jetbrains.com/help/resharper/RedundantArgumentDefaultValue.html"
|
| 2053 |
+
},
|
| 2054 |
+
"shortDescription": {
|
| 2055 |
+
"text": "Redundant argument with default value"
|
| 2056 |
+
},
|
| 2057 |
+
"defaultConfiguration": {
|
| 2058 |
+
"level": "warning"
|
| 2059 |
+
},
|
| 2060 |
+
"helpUri": "https://www.jetbrains.com/help/resharper/RedundantArgumentDefaultValue.html",
|
| 2061 |
+
"relationships": [
|
| 2062 |
+
{
|
| 2063 |
+
"target": {
|
| 2064 |
+
"id": "CSHARP.CodeRedundancy",
|
| 2065 |
+
"toolComponent": {
|
| 2066 |
+
"name": "InspectCode"
|
| 2067 |
+
}
|
| 2068 |
+
},
|
| 2069 |
+
"kinds": [
|
| 2070 |
+
"superset"
|
| 2071 |
+
]
|
| 2072 |
+
}
|
| 2073 |
+
]
|
| 2074 |
+
},
|
| 2075 |
+
{
|
| 2076 |
+
"id": "RedundantNameQualifier",
|
| 2077 |
+
"fullDescription": {
|
| 2078 |
+
"text": "Redundant use of qualifier for a type name or static member usage"
|
| 2079 |
+
},
|
| 2080 |
+
"help": {
|
| 2081 |
+
"text": "https://www.jetbrains.com/help/resharper/RedundantNameQualifier.html"
|
| 2082 |
+
},
|
| 2083 |
+
"shortDescription": {
|
| 2084 |
+
"text": "Redundant name qualifier"
|
| 2085 |
+
},
|
| 2086 |
+
"defaultConfiguration": {
|
| 2087 |
+
"level": "warning"
|
| 2088 |
+
},
|
| 2089 |
+
"helpUri": "https://www.jetbrains.com/help/resharper/RedundantNameQualifier.html",
|
| 2090 |
+
"relationships": [
|
| 2091 |
+
{
|
| 2092 |
+
"target": {
|
| 2093 |
+
"id": "CSHARP.CodeRedundancy",
|
| 2094 |
+
"toolComponent": {
|
| 2095 |
+
"name": "InspectCode"
|
| 2096 |
+
}
|
| 2097 |
+
},
|
| 2098 |
+
"kinds": [
|
| 2099 |
+
"superset"
|
| 2100 |
+
]
|
| 2101 |
+
}
|
| 2102 |
+
]
|
| 2103 |
+
},
|
| 2104 |
+
{
|
| 2105 |
+
"id": "RedundantTypeArgumentsOfMethod",
|
| 2106 |
+
"fullDescription": {
|
| 2107 |
+
"text": "Specification of method type arguments is redundant because they are inferred from argument types"
|
| 2108 |
+
},
|
| 2109 |
+
"help": {
|
| 2110 |
+
"text": "https://www.jetbrains.com/help/resharper/RedundantTypeArgumentsOfMethod.html"
|
| 2111 |
+
},
|
| 2112 |
+
"shortDescription": {
|
| 2113 |
+
"text": "Redundant type arguments of method"
|
| 2114 |
+
},
|
| 2115 |
+
"defaultConfiguration": {
|
| 2116 |
+
"level": "warning"
|
| 2117 |
+
},
|
| 2118 |
+
"helpUri": "https://www.jetbrains.com/help/resharper/RedundantTypeArgumentsOfMethod.html",
|
| 2119 |
+
"relationships": [
|
| 2120 |
+
{
|
| 2121 |
+
"target": {
|
| 2122 |
+
"id": "CSHARP.CodeRedundancy",
|
| 2123 |
+
"toolComponent": {
|
| 2124 |
+
"name": "InspectCode"
|
| 2125 |
+
}
|
| 2126 |
+
},
|
| 2127 |
+
"kinds": [
|
| 2128 |
+
"superset"
|
| 2129 |
+
]
|
| 2130 |
+
}
|
| 2131 |
+
]
|
| 2132 |
+
},
|
| 2133 |
+
{
|
| 2134 |
+
"id": "RedundantUsingDirective",
|
| 2135 |
+
"fullDescription": {
|
| 2136 |
+
"text": "Using directive is not required by the code and can be safely removed"
|
| 2137 |
+
},
|
| 2138 |
+
"help": {
|
| 2139 |
+
"text": "https://www.jetbrains.com/help/resharper/RedundantUsingDirective.html"
|
| 2140 |
+
},
|
| 2141 |
+
"shortDescription": {
|
| 2142 |
+
"text": "Redundant using directive"
|
| 2143 |
+
},
|
| 2144 |
+
"defaultConfiguration": {
|
| 2145 |
+
"level": "warning"
|
| 2146 |
+
},
|
| 2147 |
+
"helpUri": "https://www.jetbrains.com/help/resharper/RedundantUsingDirective.html",
|
| 2148 |
+
"relationships": [
|
| 2149 |
+
{
|
| 2150 |
+
"target": {
|
| 2151 |
+
"id": "CSHARP.CodeRedundancy",
|
| 2152 |
+
"toolComponent": {
|
| 2153 |
+
"name": "InspectCode"
|
| 2154 |
+
}
|
| 2155 |
+
},
|
| 2156 |
+
"kinds": [
|
| 2157 |
+
"superset"
|
| 2158 |
+
]
|
| 2159 |
+
},
|
| 2160 |
+
{
|
| 2161 |
+
"target": {
|
| 2162 |
+
"id": "ASPX.CodeRedundancy",
|
| 2163 |
+
"toolComponent": {
|
| 2164 |
+
"name": "InspectCode"
|
| 2165 |
+
}
|
| 2166 |
+
},
|
| 2167 |
+
"kinds": [
|
| 2168 |
+
"superset"
|
| 2169 |
+
]
|
| 2170 |
+
}
|
| 2171 |
+
]
|
| 2172 |
+
},
|
| 2173 |
+
{
|
| 2174 |
+
"id": "SuspiciousTypeConversion.Global",
|
| 2175 |
+
"fullDescription": {
|
| 2176 |
+
"text": "A type is converted into another one but there is no type in the solution which is inherited from both. This also applies to 'is' operator and '==' and '!=' comparisons."
|
| 2177 |
+
},
|
| 2178 |
+
"help": {
|
| 2179 |
+
"text": "https://www.jetbrains.com/help/resharper/SuspiciousTypeConversion.Global.html"
|
| 2180 |
+
},
|
| 2181 |
+
"shortDescription": {
|
| 2182 |
+
"text": "Suspicious type conversion or check"
|
| 2183 |
+
},
|
| 2184 |
+
"defaultConfiguration": {
|
| 2185 |
+
"level": "warning"
|
| 2186 |
+
},
|
| 2187 |
+
"helpUri": "https://www.jetbrains.com/help/resharper/SuspiciousTypeConversion.Global.html",
|
| 2188 |
+
"relationships": [
|
| 2189 |
+
{
|
| 2190 |
+
"target": {
|
| 2191 |
+
"id": "CSHARP.CodeSmell",
|
| 2192 |
+
"toolComponent": {
|
| 2193 |
+
"name": "InspectCode"
|
| 2194 |
+
}
|
| 2195 |
+
},
|
| 2196 |
+
"kinds": [
|
| 2197 |
+
"superset"
|
| 2198 |
+
]
|
| 2199 |
+
},
|
| 2200 |
+
{
|
| 2201 |
+
"target": {
|
| 2202 |
+
"id": "VBASIC.CodeSmell",
|
| 2203 |
+
"toolComponent": {
|
| 2204 |
+
"name": "InspectCode"
|
| 2205 |
+
}
|
| 2206 |
+
},
|
| 2207 |
+
"kinds": [
|
| 2208 |
+
"superset"
|
| 2209 |
+
]
|
| 2210 |
+
}
|
| 2211 |
+
]
|
| 2212 |
+
},
|
| 2213 |
+
{
|
| 2214 |
+
"id": "UnusedAutoPropertyAccessor.Global",
|
| 2215 |
+
"fullDescription": {
|
| 2216 |
+
"text": "Accessor in auto-property is never used"
|
| 2217 |
+
},
|
| 2218 |
+
"help": {
|
| 2219 |
+
"text": "https://www.jetbrains.com/help/resharper/UnusedAutoPropertyAccessor.Global.html"
|
| 2220 |
+
},
|
| 2221 |
+
"shortDescription": {
|
| 2222 |
+
"text": "Auto-property accessor is never used: Non-private accessibility"
|
| 2223 |
+
},
|
| 2224 |
+
"defaultConfiguration": {
|
| 2225 |
+
"level": "warning"
|
| 2226 |
+
},
|
| 2227 |
+
"helpUri": "https://www.jetbrains.com/help/resharper/UnusedAutoPropertyAccessor.Global.html",
|
| 2228 |
+
"relationships": [
|
| 2229 |
+
{
|
| 2230 |
+
"target": {
|
| 2231 |
+
"id": "CSHARP.CodeSmell",
|
| 2232 |
+
"toolComponent": {
|
| 2233 |
+
"name": "InspectCode"
|
| 2234 |
+
}
|
| 2235 |
+
},
|
| 2236 |
+
"kinds": [
|
| 2237 |
+
"superset"
|
| 2238 |
+
]
|
| 2239 |
+
},
|
| 2240 |
+
{
|
| 2241 |
+
"target": {
|
| 2242 |
+
"id": "VBASIC.CodeSmell",
|
| 2243 |
+
"toolComponent": {
|
| 2244 |
+
"name": "InspectCode"
|
| 2245 |
+
}
|
| 2246 |
+
},
|
| 2247 |
+
"kinds": [
|
| 2248 |
+
"superset"
|
| 2249 |
+
]
|
| 2250 |
+
}
|
| 2251 |
+
]
|
| 2252 |
+
},
|
| 2253 |
+
{
|
| 2254 |
+
"id": "UnusedParameter.Local",
|
| 2255 |
+
"fullDescription": {
|
| 2256 |
+
"text": "Parameter is never used"
|
| 2257 |
+
},
|
| 2258 |
+
"help": {
|
| 2259 |
+
"text": "https://www.jetbrains.com/help/resharper/UnusedParameter.Local.html"
|
| 2260 |
+
},
|
| 2261 |
+
"shortDescription": {
|
| 2262 |
+
"text": "Unused parameter: Private accessibility"
|
| 2263 |
+
},
|
| 2264 |
+
"defaultConfiguration": {
|
| 2265 |
+
"level": "warning"
|
| 2266 |
+
},
|
| 2267 |
+
"helpUri": "https://www.jetbrains.com/help/resharper/UnusedParameter.Local.html",
|
| 2268 |
+
"relationships": [
|
| 2269 |
+
{
|
| 2270 |
+
"target": {
|
| 2271 |
+
"id": "CSHARP.DeclarationRedundancy",
|
| 2272 |
+
"toolComponent": {
|
| 2273 |
+
"name": "InspectCode"
|
| 2274 |
+
}
|
| 2275 |
+
},
|
| 2276 |
+
"kinds": [
|
| 2277 |
+
"superset"
|
| 2278 |
+
]
|
| 2279 |
+
},
|
| 2280 |
+
{
|
| 2281 |
+
"target": {
|
| 2282 |
+
"id": "VBASIC.DeclarationRedundancy",
|
| 2283 |
+
"toolComponent": {
|
| 2284 |
+
"name": "InspectCode"
|
| 2285 |
+
}
|
| 2286 |
+
},
|
| 2287 |
+
"kinds": [
|
| 2288 |
+
"superset"
|
| 2289 |
+
]
|
| 2290 |
+
}
|
| 2291 |
+
]
|
| 2292 |
+
},
|
| 2293 |
+
{
|
| 2294 |
+
"id": "UnusedVariable",
|
| 2295 |
+
"fullDescription": {
|
| 2296 |
+
"text": "Local variable is never used"
|
| 2297 |
+
},
|
| 2298 |
+
"help": {
|
| 2299 |
+
"text": "https://www.jetbrains.com/help/resharper/UnusedVariable.html"
|
| 2300 |
+
},
|
| 2301 |
+
"shortDescription": {
|
| 2302 |
+
"text": "Unused local variable"
|
| 2303 |
+
},
|
| 2304 |
+
"defaultConfiguration": {
|
| 2305 |
+
"level": "warning"
|
| 2306 |
+
},
|
| 2307 |
+
"helpUri": "https://www.jetbrains.com/help/resharper/UnusedVariable.html",
|
| 2308 |
+
"relationships": [
|
| 2309 |
+
{
|
| 2310 |
+
"target": {
|
| 2311 |
+
"id": "CSHARP.DeclarationRedundancy",
|
| 2312 |
+
"toolComponent": {
|
| 2313 |
+
"name": "InspectCode"
|
| 2314 |
+
}
|
| 2315 |
+
},
|
| 2316 |
+
"kinds": [
|
| 2317 |
+
"superset"
|
| 2318 |
+
]
|
| 2319 |
+
},
|
| 2320 |
+
{
|
| 2321 |
+
"target": {
|
| 2322 |
+
"id": "VBASIC.DeclarationRedundancy",
|
| 2323 |
+
"toolComponent": {
|
| 2324 |
+
"name": "InspectCode"
|
| 2325 |
+
}
|
| 2326 |
+
},
|
| 2327 |
+
"kinds": [
|
| 2328 |
+
"superset"
|
| 2329 |
+
]
|
| 2330 |
+
}
|
| 2331 |
+
]
|
| 2332 |
+
},
|
| 2333 |
+
{
|
| 2334 |
+
"id": "UsingStatementResourceInitialization",
|
| 2335 |
+
"fullDescription": {
|
| 2336 |
+
"text": "Initialize object properties inside the 'using' statement to ensure that the object is disposed if an exception is thrown during initialization"
|
| 2337 |
+
},
|
| 2338 |
+
"help": {
|
| 2339 |
+
"text": "https://www.jetbrains.com/help/resharper/UsingStatementResourceInitialization.html"
|
| 2340 |
+
},
|
| 2341 |
+
"shortDescription": {
|
| 2342 |
+
"text": "Do not use object initializer for 'using' variable: Do not use object initializer for 'using' variable"
|
| 2343 |
+
},
|
| 2344 |
+
"defaultConfiguration": {
|
| 2345 |
+
"level": "warning"
|
| 2346 |
+
},
|
| 2347 |
+
"helpUri": "https://www.jetbrains.com/help/resharper/UsingStatementResourceInitialization.html",
|
| 2348 |
+
"relationships": [
|
| 2349 |
+
{
|
| 2350 |
+
"target": {
|
| 2351 |
+
"id": "CSHARP.CodeSmell",
|
| 2352 |
+
"toolComponent": {
|
| 2353 |
+
"name": "InspectCode"
|
| 2354 |
+
}
|
| 2355 |
+
},
|
| 2356 |
+
"kinds": [
|
| 2357 |
+
"superset"
|
| 2358 |
+
]
|
| 2359 |
+
}
|
| 2360 |
+
]
|
| 2361 |
+
}
|
| 2362 |
+
],
|
| 2363 |
+
"taxa": [
|
| 2364 |
+
{
|
| 2365 |
+
"id": "ASPX",
|
| 2366 |
+
"name": "Aspx"
|
| 2367 |
+
},
|
| 2368 |
+
{
|
| 2369 |
+
"id": "ASPX.CodeRedundancy",
|
| 2370 |
+
"name": "Redundancies in Code",
|
| 2371 |
+
"relationships": [
|
| 2372 |
+
{
|
| 2373 |
+
"target": {
|
| 2374 |
+
"id": "ASPX",
|
| 2375 |
+
"toolComponent": {
|
| 2376 |
+
"name": "InspectCode"
|
| 2377 |
+
}
|
| 2378 |
+
},
|
| 2379 |
+
"kinds": [
|
| 2380 |
+
"superset"
|
| 2381 |
+
]
|
| 2382 |
+
}
|
| 2383 |
+
]
|
| 2384 |
+
},
|
| 2385 |
+
{
|
| 2386 |
+
"id": "CSHARP",
|
| 2387 |
+
"name": "C#"
|
| 2388 |
+
},
|
| 2389 |
+
{
|
| 2390 |
+
"id": "CSHARP.CodeRedundancy",
|
| 2391 |
+
"name": "Redundancies in Code",
|
| 2392 |
+
"relationships": [
|
| 2393 |
+
{
|
| 2394 |
+
"target": {
|
| 2395 |
+
"id": "CSHARP",
|
| 2396 |
+
"toolComponent": {
|
| 2397 |
+
"name": "InspectCode"
|
| 2398 |
+
}
|
| 2399 |
+
},
|
| 2400 |
+
"kinds": [
|
| 2401 |
+
"superset"
|
| 2402 |
+
]
|
| 2403 |
+
}
|
| 2404 |
+
]
|
| 2405 |
+
},
|
| 2406 |
+
{
|
| 2407 |
+
"id": "CSHARP.CodeSmell",
|
| 2408 |
+
"name": "Potential Code Quality Issues",
|
| 2409 |
+
"relationships": [
|
| 2410 |
+
{
|
| 2411 |
+
"target": {
|
| 2412 |
+
"id": "CSHARP",
|
| 2413 |
+
"toolComponent": {
|
| 2414 |
+
"name": "InspectCode"
|
| 2415 |
+
}
|
| 2416 |
+
},
|
| 2417 |
+
"kinds": [
|
| 2418 |
+
"superset"
|
| 2419 |
+
]
|
| 2420 |
+
}
|
| 2421 |
+
]
|
| 2422 |
+
},
|
| 2423 |
+
{
|
| 2424 |
+
"id": "CSHARP.ConstraintViolation",
|
| 2425 |
+
"name": "Constraints Violations",
|
| 2426 |
+
"relationships": [
|
| 2427 |
+
{
|
| 2428 |
+
"target": {
|
| 2429 |
+
"id": "CSHARP",
|
| 2430 |
+
"toolComponent": {
|
| 2431 |
+
"name": "InspectCode"
|
| 2432 |
+
}
|
| 2433 |
+
},
|
| 2434 |
+
"kinds": [
|
| 2435 |
+
"superset"
|
| 2436 |
+
]
|
| 2437 |
+
}
|
| 2438 |
+
]
|
| 2439 |
+
},
|
| 2440 |
+
{
|
| 2441 |
+
"id": "CSHARP.DeclarationRedundancy",
|
| 2442 |
+
"name": "Redundancies in Symbol Declarations",
|
| 2443 |
+
"relationships": [
|
| 2444 |
+
{
|
| 2445 |
+
"target": {
|
| 2446 |
+
"id": "CSHARP",
|
| 2447 |
+
"toolComponent": {
|
| 2448 |
+
"name": "InspectCode"
|
| 2449 |
+
}
|
| 2450 |
+
},
|
| 2451 |
+
"kinds": [
|
| 2452 |
+
"superset"
|
| 2453 |
+
]
|
| 2454 |
+
}
|
| 2455 |
+
]
|
| 2456 |
+
},
|
| 2457 |
+
{
|
| 2458 |
+
"id": "VBASIC",
|
| 2459 |
+
"name": "VB.NET"
|
| 2460 |
+
},
|
| 2461 |
+
{
|
| 2462 |
+
"id": "VBASIC.CodeSmell",
|
| 2463 |
+
"name": "Potential Code Quality Issues",
|
| 2464 |
+
"relationships": [
|
| 2465 |
+
{
|
| 2466 |
+
"target": {
|
| 2467 |
+
"id": "VBASIC",
|
| 2468 |
+
"toolComponent": {
|
| 2469 |
+
"name": "InspectCode"
|
| 2470 |
+
}
|
| 2471 |
+
},
|
| 2472 |
+
"kinds": [
|
| 2473 |
+
"superset"
|
| 2474 |
+
]
|
| 2475 |
+
}
|
| 2476 |
+
]
|
| 2477 |
+
},
|
| 2478 |
+
{
|
| 2479 |
+
"id": "VBASIC.ConstraintViolation",
|
| 2480 |
+
"name": "Constraints Violations",
|
| 2481 |
+
"relationships": [
|
| 2482 |
+
{
|
| 2483 |
+
"target": {
|
| 2484 |
+
"id": "VBASIC",
|
| 2485 |
+
"toolComponent": {
|
| 2486 |
+
"name": "InspectCode"
|
| 2487 |
+
}
|
| 2488 |
+
},
|
| 2489 |
+
"kinds": [
|
| 2490 |
+
"superset"
|
| 2491 |
+
]
|
| 2492 |
+
}
|
| 2493 |
+
]
|
| 2494 |
+
},
|
| 2495 |
+
{
|
| 2496 |
+
"id": "VBASIC.DeclarationRedundancy",
|
| 2497 |
+
"name": "Redundancies in Symbol Declarations",
|
| 2498 |
+
"relationships": [
|
| 2499 |
+
{
|
| 2500 |
+
"target": {
|
| 2501 |
+
"id": "VBASIC",
|
| 2502 |
+
"toolComponent": {
|
| 2503 |
+
"name": "InspectCode"
|
| 2504 |
+
}
|
| 2505 |
+
},
|
| 2506 |
+
"kinds": [
|
| 2507 |
+
"superset"
|
| 2508 |
+
]
|
| 2509 |
+
}
|
| 2510 |
+
]
|
| 2511 |
+
},
|
| 2512 |
+
{
|
| 2513 |
+
"id": "XAML",
|
| 2514 |
+
"name": "XAML"
|
| 2515 |
+
},
|
| 2516 |
+
{
|
| 2517 |
+
"id": "XAML.CodeSmell",
|
| 2518 |
+
"name": "Potential Code Quality Issues",
|
| 2519 |
+
"relationships": [
|
| 2520 |
+
{
|
| 2521 |
+
"target": {
|
| 2522 |
+
"id": "XAML",
|
| 2523 |
+
"toolComponent": {
|
| 2524 |
+
"name": "InspectCode"
|
| 2525 |
+
}
|
| 2526 |
+
},
|
| 2527 |
+
"kinds": [
|
| 2528 |
+
"superset"
|
| 2529 |
+
]
|
| 2530 |
+
}
|
| 2531 |
+
]
|
| 2532 |
+
},
|
| 2533 |
+
{
|
| 2534 |
+
"id": "XAML.ConstraintViolation",
|
| 2535 |
+
"name": "Constraints Violations",
|
| 2536 |
+
"relationships": [
|
| 2537 |
+
{
|
| 2538 |
+
"target": {
|
| 2539 |
+
"id": "XAML",
|
| 2540 |
+
"toolComponent": {
|
| 2541 |
+
"name": "InspectCode"
|
| 2542 |
+
}
|
| 2543 |
+
},
|
| 2544 |
+
"kinds": [
|
| 2545 |
+
"superset"
|
| 2546 |
+
]
|
| 2547 |
+
}
|
| 2548 |
+
]
|
| 2549 |
+
}
|
| 2550 |
+
]
|
| 2551 |
+
}
|
| 2552 |
+
},
|
| 2553 |
+
"invocations": [
|
| 2554 |
+
{
|
| 2555 |
+
"executionSuccessful": true
|
| 2556 |
+
}
|
| 2557 |
+
],
|
| 2558 |
+
"versionControlProvenance": [
|
| 2559 |
+
{
|
| 2560 |
+
"repositoryUri": "https://github.com/dylanlangston/Rosyln-Stone.git",
|
| 2561 |
+
"revisionId": "24a674e63611ca39a1bce2597a41f237d0ff30b3",
|
| 2562 |
+
"branch": "copilot/refactor-gradio-landing-page",
|
| 2563 |
+
"mappedTo": {
|
| 2564 |
+
"uriBaseId": "solutionDir"
|
| 2565 |
+
}
|
| 2566 |
+
}
|
| 2567 |
+
],
|
| 2568 |
+
"originalUriBaseIds": {
|
| 2569 |
+
"solutionDir": {
|
| 2570 |
+
"uri": "file:///workspaces/Rosyln-Stone/",
|
| 2571 |
+
"description": {
|
| 2572 |
+
"text": "Solution Directory"
|
| 2573 |
+
}
|
| 2574 |
+
}
|
| 2575 |
+
},
|
| 2576 |
+
"artifacts": [
|
| 2577 |
+
{
|
| 2578 |
+
"location": {
|
| 2579 |
+
"uri": "tests/RoslynStone.Tests/CriticalBugsTests.cs",
|
| 2580 |
+
"uriBaseId": "solutionDir"
|
| 2581 |
+
},
|
| 2582 |
+
"hashes": {
|
| 2583 |
+
"md5": "5C95B0BD99DD38A884D1427E0716D241",
|
| 2584 |
+
"sha-1": "CD72A14987A5734F4EB312205637F16E924070B8",
|
| 2585 |
+
"sha-256": "7516903EC44E15CFDCE6F7D55AFDF62A521C9EBFD0EBE4C644CC98DE4F7B3800"
|
| 2586 |
+
}
|
| 2587 |
+
},
|
| 2588 |
+
{
|
| 2589 |
+
"location": {
|
| 2590 |
+
"uri": "tests/RoslynStone.Tests/LoadNuGetPackageTests.cs",
|
| 2591 |
+
"uriBaseId": "solutionDir"
|
| 2592 |
+
},
|
| 2593 |
+
"hashes": {
|
| 2594 |
+
"md5": "4EC240A76672CC3AB79776622E88BAD9",
|
| 2595 |
+
"sha-1": "49C99CFA26097F01A87BF711F2E2353F1368A47A",
|
| 2596 |
+
"sha-256": "CB6CB677831D18D86EBDD04454CDE029C99BEDBAF57ADB6ED6E51171AF46AD55"
|
| 2597 |
+
}
|
| 2598 |
+
},
|
| 2599 |
+
{
|
| 2600 |
+
"location": {
|
| 2601 |
+
"uri": "tests/RoslynStone.Tests/McpToolsIntegrationTests.cs",
|
| 2602 |
+
"uriBaseId": "solutionDir"
|
| 2603 |
+
},
|
| 2604 |
+
"hashes": {
|
| 2605 |
+
"md5": "17E3C5EA680B6BDAC423F9D9B304EDB9",
|
| 2606 |
+
"sha-1": "DD1272F58FA8E7AC4654042143CBC00C09C63B3B",
|
| 2607 |
+
"sha-256": "CBD2B50D9F75EF6875AE87BDC7CB1F5DE8A1C118CB344D89111530BFD58D9EB4"
|
| 2608 |
+
}
|
| 2609 |
+
},
|
| 2610 |
+
{
|
| 2611 |
+
"location": {
|
| 2612 |
+
"uri": "tests/RoslynStone.Tests/ReplToolsContextControlTests.cs",
|
| 2613 |
+
"uriBaseId": "solutionDir"
|
| 2614 |
+
},
|
| 2615 |
+
"hashes": {
|
| 2616 |
+
"md5": "2138379ECACA45A32C0128423ABB22E7",
|
| 2617 |
+
"sha-1": "E11CD5565684128B7D969DF5C50EE8056796597C",
|
| 2618 |
+
"sha-256": "220CC8095CFBDB8D9BAA70C201F907CE97197431340BFD0A8FA819D2A30A76FA"
|
| 2619 |
+
}
|
| 2620 |
+
},
|
| 2621 |
+
{
|
| 2622 |
+
"location": {
|
| 2623 |
+
"uri": "src/RoslynStone.Infrastructure/Services/RoslynScriptingService.cs",
|
| 2624 |
+
"uriBaseId": "solutionDir"
|
| 2625 |
+
},
|
| 2626 |
+
"hashes": {
|
| 2627 |
+
"md5": "63743FB83FF4A4ACEC55E84058E25DEB",
|
| 2628 |
+
"sha-1": "DF9B7D5D828D4CED7CDDC65C38957133749A3CF5",
|
| 2629 |
+
"sha-256": "D54B4A9CEDE20DF200697C5BB6CFF94D8B67A6588ABC3CA37D7E830B29814E00"
|
| 2630 |
+
}
|
| 2631 |
+
},
|
| 2632 |
+
{
|
| 2633 |
+
"location": {
|
| 2634 |
+
"uri": "tests/RoslynStone.LoadTests/Program.cs",
|
| 2635 |
+
"uriBaseId": "solutionDir"
|
| 2636 |
+
},
|
| 2637 |
+
"hashes": {
|
| 2638 |
+
"md5": "D1C3B11FDDB6AA9EA04E26CFAB4481D9",
|
| 2639 |
+
"sha-1": "58F15709F1EC0B358BFD1AEAC2EEBD655D6F1E9B",
|
| 2640 |
+
"sha-256": "548CE336B2CBBC0FFF947B9C74F32F0C11C67381D5BEC9B93FA7F5BB465B69EA"
|
| 2641 |
+
}
|
| 2642 |
+
},
|
| 2643 |
+
{
|
| 2644 |
+
"location": {
|
| 2645 |
+
"uri": "src/RoslynStone.Infrastructure/Resources/NuGetSearchResource.cs",
|
| 2646 |
+
"uriBaseId": "solutionDir"
|
| 2647 |
+
},
|
| 2648 |
+
"hashes": {
|
| 2649 |
+
"md5": "80FC8D83502DCF26BC1F525326FFC448",
|
| 2650 |
+
"sha-1": "ACD86CA940947A39B4F2BD83883737C6642879EE",
|
| 2651 |
+
"sha-256": "524290D0A9D8ECEB6A20A2D5257374993BB70B06408728C466D6274E9BB82B17"
|
| 2652 |
+
}
|
| 2653 |
+
},
|
| 2654 |
+
{
|
| 2655 |
+
"location": {
|
| 2656 |
+
"uri": "tests/RoslynStone.AppHost.Tests/AppHostTests.cs",
|
| 2657 |
+
"uriBaseId": "solutionDir"
|
| 2658 |
+
},
|
| 2659 |
+
"hashes": {
|
| 2660 |
+
"md5": "8DBEC24676D2AF0573CB0181C18109B2",
|
| 2661 |
+
"sha-1": "480C3EAA28B66F575D2483815F5A0CCAC34ECB25",
|
| 2662 |
+
"sha-256": "355A8884CC546E6032764598CCBF38B138E65DAFE940562BFB75EFA01D725623"
|
| 2663 |
+
}
|
| 2664 |
+
},
|
| 2665 |
+
{
|
| 2666 |
+
"location": {
|
| 2667 |
+
"uri": "tests/RoslynStone.GradioTests/GradioLandingPageTests.cs",
|
| 2668 |
+
"uriBaseId": "solutionDir"
|
| 2669 |
+
},
|
| 2670 |
+
"hashes": {
|
| 2671 |
+
"md5": "B3F771466CBBD5639B484C03A7357627",
|
| 2672 |
+
"sha-1": "96D9C8C27A85751DC33AC684FE8575A1ACD5D60F",
|
| 2673 |
+
"sha-256": "380B8A2196834C84B1D9F5E61AFDE90EF55FA01674AAD515B19B0A6A8127C883"
|
| 2674 |
+
}
|
| 2675 |
+
},
|
| 2676 |
+
{
|
| 2677 |
+
"location": {
|
| 2678 |
+
"uri": "tests/RoslynStone.Tests/CompilationServiceEdgeCasesTests.cs",
|
| 2679 |
+
"uriBaseId": "solutionDir"
|
| 2680 |
+
},
|
| 2681 |
+
"hashes": {
|
| 2682 |
+
"md5": "6B627F3C92CAB303A13D32A5770BAB62",
|
| 2683 |
+
"sha-1": "E30296A9F62C5033FB9F4A065DADCE187A1ED8EA",
|
| 2684 |
+
"sha-256": "A648A265380CBCCDC02EA6D7DC3E761B8CCBCBD87CB0E42C54C7BC78E8A4DAE6"
|
| 2685 |
+
}
|
| 2686 |
+
},
|
| 2687 |
+
{
|
| 2688 |
+
"location": {
|
| 2689 |
+
"uri": "tests/RoslynStone.Tests/RoslynScriptingServiceTests.cs",
|
| 2690 |
+
"uriBaseId": "solutionDir"
|
| 2691 |
+
},
|
| 2692 |
+
"hashes": {
|
| 2693 |
+
"md5": "FBBBCDC784189505E39C45F02B4ADC2E",
|
| 2694 |
+
"sha-1": "B6CE8143274F6642761EF4226E731F8C3E119A80",
|
| 2695 |
+
"sha-256": "C5A40C98DB8F53D250E7CA917C69925F631C1980464C83332E78C6275A3B1502"
|
| 2696 |
+
}
|
| 2697 |
+
},
|
| 2698 |
+
{
|
| 2699 |
+
"location": {
|
| 2700 |
+
"uri": "tests/RoslynStone.Tests/ResourceTests.cs",
|
| 2701 |
+
"uriBaseId": "solutionDir"
|
| 2702 |
+
},
|
| 2703 |
+
"hashes": {
|
| 2704 |
+
"md5": "80D952DBB9B26FEAB7790106B78B1B6B",
|
| 2705 |
+
"sha-1": "ED268D1E3B4DF2CB512DDF425ACE4956E041D62E",
|
| 2706 |
+
"sha-256": "B33A49D4F644109C34A77E01D42B5D476A4FFCBD078F1DF16A3C0CE2A2D1B22E"
|
| 2707 |
+
}
|
| 2708 |
+
},
|
| 2709 |
+
{
|
| 2710 |
+
"location": {
|
| 2711 |
+
"uri": "tests/RoslynStone.Tests/DiagnosticHelpersTests.cs",
|
| 2712 |
+
"uriBaseId": "solutionDir"
|
| 2713 |
+
},
|
| 2714 |
+
"hashes": {
|
| 2715 |
+
"md5": "ECB8534CDE0FF925145DAA14F4016E3D",
|
| 2716 |
+
"sha-1": "2861017ACA05DFA67EDD93C8306EA5190CEE3F16",
|
| 2717 |
+
"sha-256": "754C434499A83B921ACA5226C720C1DEFA23212DD6F03C7029F1F6CA25F163E4"
|
| 2718 |
+
}
|
| 2719 |
+
},
|
| 2720 |
+
{
|
| 2721 |
+
"location": {
|
| 2722 |
+
"uri": "src/RoslynStone.Api/Program.cs",
|
| 2723 |
+
"uriBaseId": "solutionDir"
|
| 2724 |
+
},
|
| 2725 |
+
"hashes": {
|
| 2726 |
+
"md5": "79A8B34059BB3E002DA6E7B6EF379D0A",
|
| 2727 |
+
"sha-1": "39EE9502FFBC535609416343C15DB5AADD7BBA3B",
|
| 2728 |
+
"sha-256": "3447DBB15107C255B7A60C1B9C3BFB1D5C02A8D5817F53B27DC19D943EA02A29"
|
| 2729 |
+
}
|
| 2730 |
+
},
|
| 2731 |
+
{
|
| 2732 |
+
"location": {
|
| 2733 |
+
"uri": "src/RoslynStone.Infrastructure/Resources/NuGetPackageResource.cs",
|
| 2734 |
+
"uriBaseId": "solutionDir"
|
| 2735 |
+
},
|
| 2736 |
+
"hashes": {
|
| 2737 |
+
"md5": "47CB3DDA4620D15FCDF684A9DBF7C7BF",
|
| 2738 |
+
"sha-1": "D0565ADB998458773D081ADFA7067700064A7723",
|
| 2739 |
+
"sha-256": "4701BC443765AC7EA648EA5070DB7C5ED09215F09ABC462B3BB4076DF92D9BE1"
|
| 2740 |
+
}
|
| 2741 |
+
},
|
| 2742 |
+
{
|
| 2743 |
+
"location": {
|
| 2744 |
+
"uri": "src/RoslynStone.Infrastructure/Models/ResourceResponses.cs",
|
| 2745 |
+
"uriBaseId": "solutionDir"
|
| 2746 |
+
},
|
| 2747 |
+
"hashes": {
|
| 2748 |
+
"md5": "7755AF12DBD1FD6575AC9A5AF2F9FA50",
|
| 2749 |
+
"sha-1": "27885D90BFF93AE968CCD0510197B284B9EA32C2",
|
| 2750 |
+
"sha-256": "E92B640C21E7914589A0AC006BCE181ACB9462A012205DEDD5D60BF209727E2D"
|
| 2751 |
+
}
|
| 2752 |
+
},
|
| 2753 |
+
{
|
| 2754 |
+
"location": {
|
| 2755 |
+
"uri": "src/RoslynStone.Core/Models/DocumentationInfo.cs",
|
| 2756 |
+
"uriBaseId": "solutionDir"
|
| 2757 |
+
},
|
| 2758 |
+
"hashes": {
|
| 2759 |
+
"md5": "1A695E68790B561BB20D216730A0C3D7",
|
| 2760 |
+
"sha-1": "FE467F1A6A9320ACD9ABC6F23BB398D411A88A56",
|
| 2761 |
+
"sha-256": "6AB2703095212C9CC11A5604F3FCAF312DDA2E220A3E3CD89930D4AFDC480421"
|
| 2762 |
+
}
|
| 2763 |
+
},
|
| 2764 |
+
{
|
| 2765 |
+
"location": {
|
| 2766 |
+
"uri": "tests/RoslynStone.GradioTests/McpServerFixture.cs",
|
| 2767 |
+
"uriBaseId": "solutionDir"
|
| 2768 |
+
},
|
| 2769 |
+
"hashes": {
|
| 2770 |
+
"md5": "ABE06BDEBD806FD7815EDD160A78F511",
|
| 2771 |
+
"sha-1": "FF9A3E71FC01E69DB15D4994778404B36BFC433E",
|
| 2772 |
+
"sha-256": "1513DB9C0EB8E41C2467B2BD6369BF070C9774F8C65B1CC16057211E52FAC9F1"
|
| 2773 |
+
}
|
| 2774 |
+
},
|
| 2775 |
+
{
|
| 2776 |
+
"location": {
|
| 2777 |
+
"uri": "src/RoslynStone.Infrastructure/Resources/ReplStateResource.cs",
|
| 2778 |
+
"uriBaseId": "solutionDir"
|
| 2779 |
+
},
|
| 2780 |
+
"hashes": {
|
| 2781 |
+
"md5": "F3B29D7AB47EF16FCA76A37964BE202D",
|
| 2782 |
+
"sha-1": "354E84105491657D974DD41542EF36F427DCBB99",
|
| 2783 |
+
"sha-256": "B0C9E4CE8CCC9C3637BC996FE678C895D52B72BD0247722DFC018B7929A6C7FC"
|
| 2784 |
+
}
|
| 2785 |
+
},
|
| 2786 |
+
{
|
| 2787 |
+
"location": {
|
| 2788 |
+
"uri": "src/RoslynStone.AppHost/AppHost.cs",
|
| 2789 |
+
"uriBaseId": "solutionDir"
|
| 2790 |
+
},
|
| 2791 |
+
"hashes": {
|
| 2792 |
+
"md5": "ADBAD1D2775514ACE4602C6B7F788017",
|
| 2793 |
+
"sha-1": "13D2AD43FF6DA1A6F5A70FF91166AD4BCA3E44B2",
|
| 2794 |
+
"sha-256": "37A4ABDBFAC28B15ED2F5F0D492242AFB61B911A74DA7D78F7494874096716E1"
|
| 2795 |
+
}
|
| 2796 |
+
}
|
| 2797 |
+
],
|
| 2798 |
+
"columnKind": "utf16CodeUnits"
|
| 2799 |
+
}
|
| 2800 |
+
]
|
| 2801 |
+
}
|
build.cake
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
///////////////////////////////////////////////////////////////////////////////
|
| 2 |
+
// ARGUMENTS
|
| 3 |
+
///////////////////////////////////////////////////////////////////////////////
|
| 4 |
+
|
| 5 |
+
var target = Argument("target", "Default");
|
| 6 |
+
var configuration = Argument("configuration", "Debug");
|
| 7 |
+
var version = Argument("version", "1.0.0");
|
| 8 |
+
|
| 9 |
+
///////////////////////////////////////////////////////////////////////////////
|
| 10 |
+
// SETUP / TEARDOWN
|
| 11 |
+
///////////////////////////////////////////////////////////////////////////////
|
| 12 |
+
|
| 13 |
+
Setup(ctx =>
|
| 14 |
+
{
|
| 15 |
+
Information("Running tasks...");
|
| 16 |
+
Information($"Configuration: {configuration}");
|
| 17 |
+
Information($"Version: {version}");
|
| 18 |
+
});
|
| 19 |
+
|
| 20 |
+
Teardown(ctx =>
|
| 21 |
+
{
|
| 22 |
+
Information("Finished running tasks.");
|
| 23 |
+
});
|
| 24 |
+
|
| 25 |
+
///////////////////////////////////////////////////////////////////////////////
|
| 26 |
+
// TASKS
|
| 27 |
+
///////////////////////////////////////////////////////////////////////////////
|
| 28 |
+
|
| 29 |
+
Task("Clean")
|
| 30 |
+
.Does(() =>
|
| 31 |
+
{
|
| 32 |
+
CleanDirectories("./src/**/bin");
|
| 33 |
+
CleanDirectories("./src/**/obj");
|
| 34 |
+
CleanDirectories("./tests/**/bin");
|
| 35 |
+
CleanDirectories("./tests/**/obj");
|
| 36 |
+
CleanDirectories("./artifacts");
|
| 37 |
+
});
|
| 38 |
+
|
| 39 |
+
Task("Restore")
|
| 40 |
+
.IsDependentOn("Clean")
|
| 41 |
+
.Does(() =>
|
| 42 |
+
{
|
| 43 |
+
DotNetRestore("./RoslynStone.sln");
|
| 44 |
+
});
|
| 45 |
+
|
| 46 |
+
Task("Build")
|
| 47 |
+
.IsDependentOn("Restore")
|
| 48 |
+
.Does(() =>
|
| 49 |
+
{
|
| 50 |
+
DotNetBuild("./RoslynStone.sln", new DotNetBuildSettings
|
| 51 |
+
{
|
| 52 |
+
Configuration = configuration,
|
| 53 |
+
NoRestore = true
|
| 54 |
+
});
|
| 55 |
+
});
|
| 56 |
+
|
| 57 |
+
Task("Format")
|
| 58 |
+
.Description("Format code with CSharpier")
|
| 59 |
+
.Does(() =>
|
| 60 |
+
{
|
| 61 |
+
StartProcess("csharpier", new ProcessSettings
|
| 62 |
+
{
|
| 63 |
+
Arguments = "format ."
|
| 64 |
+
});
|
| 65 |
+
});
|
| 66 |
+
|
| 67 |
+
Task("Format-Check")
|
| 68 |
+
.Description("Check code formatting with CSharpier")
|
| 69 |
+
.Does(() =>
|
| 70 |
+
{
|
| 71 |
+
var exitCode = StartProcess("csharpier", new ProcessSettings
|
| 72 |
+
{
|
| 73 |
+
Arguments = "check ."
|
| 74 |
+
});
|
| 75 |
+
|
| 76 |
+
if (exitCode != 0)
|
| 77 |
+
{
|
| 78 |
+
throw new Exception("Code formatting issues found. Run 'dotnet cake --target=Format' to fix.");
|
| 79 |
+
}
|
| 80 |
+
});
|
| 81 |
+
|
| 82 |
+
Task("Python-Format")
|
| 83 |
+
.Description("Format Python code with Ruff")
|
| 84 |
+
.Does(() =>
|
| 85 |
+
{
|
| 86 |
+
StartProcess("bash", new ProcessSettings
|
| 87 |
+
{
|
| 88 |
+
Arguments = "./scripts/format-python.sh"
|
| 89 |
+
});
|
| 90 |
+
});
|
| 91 |
+
|
| 92 |
+
Task("Python-Check")
|
| 93 |
+
.Description("Check Python code quality (Ruff + mypy)")
|
| 94 |
+
.Does(() =>
|
| 95 |
+
{
|
| 96 |
+
var exitCode = StartProcess("bash", new ProcessSettings
|
| 97 |
+
{
|
| 98 |
+
Arguments = "./scripts/check-python-quality.sh"
|
| 99 |
+
});
|
| 100 |
+
|
| 101 |
+
if (exitCode != 0)
|
| 102 |
+
{
|
| 103 |
+
throw new Exception("Python quality checks failed. Run 'dotnet cake --target=Python-Format' to fix formatting issues.");
|
| 104 |
+
}
|
| 105 |
+
});
|
| 106 |
+
|
| 107 |
+
Task("Inspect")
|
| 108 |
+
.Description("Run ReSharper code inspections")
|
| 109 |
+
.IsDependentOn("Build")
|
| 110 |
+
.Does(() =>
|
| 111 |
+
{
|
| 112 |
+
var reportPath = "./artifacts/resharper-report.xml";
|
| 113 |
+
EnsureDirectoryExists("./artifacts");
|
| 114 |
+
|
| 115 |
+
StartProcess("jb", new ProcessSettings
|
| 116 |
+
{
|
| 117 |
+
Arguments = $"inspectcode RoslynStone.sln --output={reportPath} --severity=WARNING"
|
| 118 |
+
});
|
| 119 |
+
|
| 120 |
+
Information($"ReSharper inspection report generated: {reportPath}");
|
| 121 |
+
});
|
| 122 |
+
|
| 123 |
+
Task("Test")
|
| 124 |
+
.IsDependentOn("Build")
|
| 125 |
+
.Does(() =>
|
| 126 |
+
{
|
| 127 |
+
DotNetTest("./RoslynStone.sln", new DotNetTestSettings
|
| 128 |
+
{
|
| 129 |
+
Configuration = configuration,
|
| 130 |
+
NoRestore = true,
|
| 131 |
+
NoBuild = true,
|
| 132 |
+
Loggers = new[] { "console;verbosity=normal" }
|
| 133 |
+
});
|
| 134 |
+
});
|
| 135 |
+
|
| 136 |
+
Task("Test-Coverage")
|
| 137 |
+
.Description("Run tests with code coverage")
|
| 138 |
+
.IsDependentOn("Build")
|
| 139 |
+
.Does(() =>
|
| 140 |
+
{
|
| 141 |
+
EnsureDirectoryExists("./artifacts/coverage");
|
| 142 |
+
|
| 143 |
+
DotNetTest("./RoslynStone.sln", new DotNetTestSettings
|
| 144 |
+
{
|
| 145 |
+
Configuration = configuration,
|
| 146 |
+
NoRestore = true,
|
| 147 |
+
NoBuild = true,
|
| 148 |
+
Loggers = new[] { "console;verbosity=normal" },
|
| 149 |
+
ArgumentCustomization = args => args
|
| 150 |
+
.Append("--collect:\"XPlat Code Coverage\"")
|
| 151 |
+
.Append("--results-directory ./artifacts/coverage")
|
| 152 |
+
});
|
| 153 |
+
|
| 154 |
+
// Parse coverage results and check branch coverage
|
| 155 |
+
var coverageFiles = GetFiles("./artifacts/coverage/**/coverage.cobertura.xml");
|
| 156 |
+
if (coverageFiles.Any())
|
| 157 |
+
{
|
| 158 |
+
// Aggregate coverage from all files to get total metrics
|
| 159 |
+
double totalLinesCovered = 0;
|
| 160 |
+
double totalLinesValid = 0;
|
| 161 |
+
double totalBranchesCovered = 0;
|
| 162 |
+
double totalBranchesValid = 0;
|
| 163 |
+
|
| 164 |
+
foreach (var coverageFile in coverageFiles)
|
| 165 |
+
{
|
| 166 |
+
var xml = System.Xml.Linq.XDocument.Load(coverageFile.FullPath);
|
| 167 |
+
var coverage = xml.Root;
|
| 168 |
+
|
| 169 |
+
if (coverage == null) continue;
|
| 170 |
+
|
| 171 |
+
var linesCoveredAttr = coverage.Attribute("lines-covered");
|
| 172 |
+
var linesValidAttr = coverage.Attribute("lines-valid");
|
| 173 |
+
var branchesCoveredAttr = coverage.Attribute("branches-covered");
|
| 174 |
+
var branchesValidAttr = coverage.Attribute("branches-valid");
|
| 175 |
+
|
| 176 |
+
if (linesCoveredAttr != null && linesValidAttr != null &&
|
| 177 |
+
branchesCoveredAttr != null && branchesValidAttr != null)
|
| 178 |
+
{
|
| 179 |
+
totalLinesCovered += double.Parse(linesCoveredAttr.Value);
|
| 180 |
+
totalLinesValid += double.Parse(linesValidAttr.Value);
|
| 181 |
+
totalBranchesCovered += double.Parse(branchesCoveredAttr.Value);
|
| 182 |
+
totalBranchesValid += double.Parse(branchesValidAttr.Value);
|
| 183 |
+
}
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
if (totalLinesValid == 0)
|
| 187 |
+
{
|
| 188 |
+
Warning("Unable to parse coverage report: no valid lines found");
|
| 189 |
+
return;
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
var lineCoverage = (totalLinesCovered / totalLinesValid) * 100;
|
| 193 |
+
var branchCoverage = totalBranchesValid > 0 ? (totalBranchesCovered / totalBranchesValid) * 100 : 0;
|
| 194 |
+
|
| 195 |
+
Information($"Line Coverage: {lineCoverage:F2}% ({totalLinesCovered}/{totalLinesValid} lines)");
|
| 196 |
+
Information($"Branch Coverage: {branchCoverage:F2}% ({totalBranchesCovered}/{totalBranchesValid} branches)");
|
| 197 |
+
|
| 198 |
+
// Enforce minimum coverage thresholds
|
| 199 |
+
const double MinBranchCoverage = 75.0;
|
| 200 |
+
const double MinLineCoverage = 80.0;
|
| 201 |
+
|
| 202 |
+
if (branchCoverage < MinBranchCoverage)
|
| 203 |
+
{
|
| 204 |
+
Warning($"⚠️ Branch coverage ({branchCoverage:F2}%) is below the minimum threshold of {MinBranchCoverage}%");
|
| 205 |
+
// Note: Not failing the build yet to allow incremental improvements
|
| 206 |
+
// throw new Exception($"Branch coverage ({branchCoverage:F2}%) is below the minimum threshold of {MinBranchCoverage}%");
|
| 207 |
+
}
|
| 208 |
+
else
|
| 209 |
+
{
|
| 210 |
+
Information($"✅ Branch coverage meets the minimum threshold");
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
if (lineCoverage < MinLineCoverage)
|
| 214 |
+
{
|
| 215 |
+
Warning($"⚠️ Line coverage ({lineCoverage:F2}%) is below the minimum threshold of {MinLineCoverage}%");
|
| 216 |
+
}
|
| 217 |
+
else
|
| 218 |
+
{
|
| 219 |
+
Information($"✅ Line coverage meets the minimum threshold");
|
| 220 |
+
}
|
| 221 |
+
}
|
| 222 |
+
});
|
| 223 |
+
|
| 224 |
+
Task("Test-Coverage-Report")
|
| 225 |
+
.Description("Generate HTML coverage report using ReportGenerator")
|
| 226 |
+
.IsDependentOn("Test-Coverage")
|
| 227 |
+
.Does(() =>
|
| 228 |
+
{
|
| 229 |
+
var coverageFiles = GetFiles("./artifacts/coverage/**/coverage.cobertura.xml");
|
| 230 |
+
if (coverageFiles.Any())
|
| 231 |
+
{
|
| 232 |
+
EnsureDirectoryExists("./artifacts/coverage-report");
|
| 233 |
+
|
| 234 |
+
var settings = new ProcessSettings
|
| 235 |
+
{
|
| 236 |
+
Arguments = new ProcessArgumentBuilder()
|
| 237 |
+
.Append($"-reports:{string.Join(";", coverageFiles.Select(f => f.FullPath))}")
|
| 238 |
+
.Append("-targetdir:./artifacts/coverage-report")
|
| 239 |
+
.Append("-reporttypes:Html;Badges")
|
| 240 |
+
};
|
| 241 |
+
|
| 242 |
+
StartProcess("reportgenerator", settings);
|
| 243 |
+
Information("Coverage report generated at ./artifacts/coverage-report/index.html");
|
| 244 |
+
}
|
| 245 |
+
else
|
| 246 |
+
{
|
| 247 |
+
Warning("No coverage files found to generate report");
|
| 248 |
+
}
|
| 249 |
+
});
|
| 250 |
+
|
| 251 |
+
Task("Benchmark")
|
| 252 |
+
.Description("Run benchmarks")
|
| 253 |
+
.IsDependentOn("Build")
|
| 254 |
+
.Does(() =>
|
| 255 |
+
{
|
| 256 |
+
Information("Running benchmarks...");
|
| 257 |
+
Information("Note: Benchmarks can take several minutes to complete.");
|
| 258 |
+
|
| 259 |
+
var benchmarkProject = "./tests/RoslynStone.Benchmarks/RoslynStone.Benchmarks.csproj";
|
| 260 |
+
|
| 261 |
+
DotNetRun(benchmarkProject, new DotNetRunSettings
|
| 262 |
+
{
|
| 263 |
+
Configuration = "Release",
|
| 264 |
+
NoBuild = false,
|
| 265 |
+
ArgumentCustomization = args => args.Append("--filter * --artifacts ./artifacts/benchmarks")
|
| 266 |
+
});
|
| 267 |
+
|
| 268 |
+
Information("Benchmark results saved to ./artifacts/benchmarks");
|
| 269 |
+
});
|
| 270 |
+
|
| 271 |
+
Task("Load-Test")
|
| 272 |
+
.Description("Run load tests against a running HTTP server")
|
| 273 |
+
.IsDependentOn("Build")
|
| 274 |
+
.Does(() =>
|
| 275 |
+
{
|
| 276 |
+
Information("Running load tests...");
|
| 277 |
+
Information("Note: Ensure the API server is running in HTTP mode:");
|
| 278 |
+
Information(" cd src/RoslynStone.Api && MCP_TRANSPORT=http dotnet run");
|
| 279 |
+
Information("");
|
| 280 |
+
|
| 281 |
+
var loadTestProject = "./tests/RoslynStone.LoadTests/RoslynStone.LoadTests.csproj";
|
| 282 |
+
|
| 283 |
+
try
|
| 284 |
+
{
|
| 285 |
+
DotNetRun(loadTestProject, new DotNetRunSettings
|
| 286 |
+
{
|
| 287 |
+
Configuration = configuration,
|
| 288 |
+
NoBuild = true
|
| 289 |
+
});
|
| 290 |
+
}
|
| 291 |
+
catch (Exception ex)
|
| 292 |
+
{
|
| 293 |
+
Warning($"Load test failed: {ex.Message}");
|
| 294 |
+
Warning("Make sure the server is running with: MCP_TRANSPORT=http dotnet run");
|
| 295 |
+
}
|
| 296 |
+
});
|
| 297 |
+
|
| 298 |
+
Task("Pack")
|
| 299 |
+
.Description("Create NuGet packages")
|
| 300 |
+
.IsDependentOn("Build")
|
| 301 |
+
.Does(() =>
|
| 302 |
+
{
|
| 303 |
+
EnsureDirectoryExists("./artifacts/packages");
|
| 304 |
+
|
| 305 |
+
var projects = new[]
|
| 306 |
+
{
|
| 307 |
+
"./src/RoslynStone.Core/RoslynStone.Core.csproj",
|
| 308 |
+
"./src/RoslynStone.Infrastructure/RoslynStone.Infrastructure.csproj"
|
| 309 |
+
};
|
| 310 |
+
|
| 311 |
+
foreach (var project in projects)
|
| 312 |
+
{
|
| 313 |
+
DotNetPack(project, new DotNetPackSettings
|
| 314 |
+
{
|
| 315 |
+
Configuration = configuration,
|
| 316 |
+
OutputDirectory = "./artifacts/packages",
|
| 317 |
+
NoRestore = true,
|
| 318 |
+
NoBuild = true,
|
| 319 |
+
ArgumentCustomization = args => args
|
| 320 |
+
.Append($"/p:Version={version}")
|
| 321 |
+
.Append($"/p:PackageVersion={version}")
|
| 322 |
+
});
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
Information($"NuGet packages created in ./artifacts/packages");
|
| 326 |
+
});
|
| 327 |
+
|
| 328 |
+
Task("Publish-NuGet")
|
| 329 |
+
.Description("Publish NuGet packages to NuGet.org")
|
| 330 |
+
.IsDependentOn("Pack")
|
| 331 |
+
.Does(() =>
|
| 332 |
+
{
|
| 333 |
+
var apiKey = EnvironmentVariable("NUGET_API_KEY");
|
| 334 |
+
if (string.IsNullOrEmpty(apiKey))
|
| 335 |
+
{
|
| 336 |
+
throw new Exception("NUGET_API_KEY environment variable not set");
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
var packages = GetFiles("./artifacts/packages/*.nupkg");
|
| 340 |
+
|
| 341 |
+
foreach (var package in packages)
|
| 342 |
+
{
|
| 343 |
+
DotNetNuGetPush(package.FullPath, new DotNetNuGetPushSettings
|
| 344 |
+
{
|
| 345 |
+
Source = "https://api.nuget.org/v3/index.json",
|
| 346 |
+
ApiKey = apiKey
|
| 347 |
+
});
|
| 348 |
+
}
|
| 349 |
+
});
|
| 350 |
+
|
| 351 |
+
Task("CI")
|
| 352 |
+
.Description("Run all CI tasks: Format check (C# + Python), Build, Inspect, Test with Coverage")
|
| 353 |
+
.IsDependentOn("Format-Check")
|
| 354 |
+
.IsDependentOn("Python-Check")
|
| 355 |
+
.IsDependentOn("Inspect")
|
| 356 |
+
.IsDependentOn("Test-Coverage");
|
| 357 |
+
|
| 358 |
+
Task("Default")
|
| 359 |
+
.IsDependentOn("Build")
|
| 360 |
+
.IsDependentOn("Test");
|
| 361 |
+
|
| 362 |
+
///////////////////////////////////////////////////////////////////////////////
|
| 363 |
+
// EXECUTION
|
| 364 |
+
///////////////////////////////////////////////////////////////////////////////
|
| 365 |
+
|
| 366 |
+
RunTarget(target);
|
docker-compose.yml
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version: '3.8'
|
| 2 |
+
|
| 3 |
+
services:
|
| 4 |
+
# MCP Server with stdio transport (default)
|
| 5 |
+
roslyn-stone-mcp-stdio:
|
| 6 |
+
build:
|
| 7 |
+
context: .
|
| 8 |
+
dockerfile: src/RoslynStone.Api/Dockerfile
|
| 9 |
+
image: roslyn-stone-mcp:latest-stdio
|
| 10 |
+
container_name: roslyn-stone-mcp-stdio
|
| 11 |
+
environment:
|
| 12 |
+
- DOTNET_ENVIRONMENT=Development
|
| 13 |
+
- MCP_TRANSPORT=stdio
|
| 14 |
+
- OTEL_EXPORTER_OTLP_ENDPOINT=http://aspire-dashboard:18889
|
| 15 |
+
- OTEL_SERVICE_NAME=roslyn-stone-mcp-stdio
|
| 16 |
+
- OTEL_RESOURCE_ATTRIBUTES=service.namespace=roslyn-stone,service.instance.id=mcp-stdio-1
|
| 17 |
+
stdin_open: true
|
| 18 |
+
tty: true
|
| 19 |
+
networks:
|
| 20 |
+
- roslyn-stone-network
|
| 21 |
+
depends_on:
|
| 22 |
+
- aspire-dashboard
|
| 23 |
+
|
| 24 |
+
# MCP Server with HTTP transport
|
| 25 |
+
roslyn-stone-mcp-http:
|
| 26 |
+
build:
|
| 27 |
+
context: .
|
| 28 |
+
dockerfile: src/RoslynStone.Api/Dockerfile
|
| 29 |
+
image: roslyn-stone-mcp:latest-http
|
| 30 |
+
container_name: roslyn-stone-mcp-http
|
| 31 |
+
environment:
|
| 32 |
+
- DOTNET_ENVIRONMENT=Development
|
| 33 |
+
- MCP_TRANSPORT=http
|
| 34 |
+
- ASPNETCORE_URLS=http://+:8080
|
| 35 |
+
- OTEL_EXPORTER_OTLP_ENDPOINT=http://aspire-dashboard:18889
|
| 36 |
+
- OTEL_SERVICE_NAME=roslyn-stone-mcp-http
|
| 37 |
+
- OTEL_RESOURCE_ATTRIBUTES=service.namespace=roslyn-stone,service.instance.id=mcp-http-1
|
| 38 |
+
ports:
|
| 39 |
+
- "8080:8080" # MCP HTTP endpoint
|
| 40 |
+
networks:
|
| 41 |
+
- roslyn-stone-network
|
| 42 |
+
depends_on:
|
| 43 |
+
- aspire-dashboard
|
| 44 |
+
|
| 45 |
+
aspire-dashboard:
|
| 46 |
+
image: mcr.microsoft.com/dotnet/aspire-dashboard:10.0
|
| 47 |
+
container_name: aspire-dashboard
|
| 48 |
+
ports:
|
| 49 |
+
- "18888:18888" # Dashboard UI
|
| 50 |
+
- "18889:18889" # OTLP gRPC endpoint
|
| 51 |
+
environment:
|
| 52 |
+
- DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS=true
|
| 53 |
+
networks:
|
| 54 |
+
- roslyn-stone-network
|
| 55 |
+
|
| 56 |
+
networks:
|
| 57 |
+
roslyn-stone-network:
|
| 58 |
+
driver: bridge
|
scripts/check-python-quality.sh
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# Python Code Quality Check Script
|
| 3 |
+
# Run this before committing Python code changes
|
| 4 |
+
|
| 5 |
+
set -e
|
| 6 |
+
|
| 7 |
+
echo "🐍 Running Python code quality checks..."
|
| 8 |
+
echo ""
|
| 9 |
+
|
| 10 |
+
cd "$(dirname "$0")/../src/RoslynStone.GradioModule"
|
| 11 |
+
|
| 12 |
+
echo "1️⃣ Running Ruff formatter check..."
|
| 13 |
+
ruff format --check . || {
|
| 14 |
+
echo "❌ Formatting issues found. Run 'ruff format .' to fix."
|
| 15 |
+
exit 1
|
| 16 |
+
}
|
| 17 |
+
echo "✅ Formatting check passed"
|
| 18 |
+
echo ""
|
| 19 |
+
|
| 20 |
+
echo "2️⃣ Running Ruff linter..."
|
| 21 |
+
ruff check . || {
|
| 22 |
+
echo "❌ Linting issues found. Run 'ruff check --fix' to auto-fix."
|
| 23 |
+
exit 1
|
| 24 |
+
}
|
| 25 |
+
echo "✅ Linting passed"
|
| 26 |
+
echo ""
|
| 27 |
+
|
| 28 |
+
echo "3️⃣ Running mypy type checker..."
|
| 29 |
+
mypy . || {
|
| 30 |
+
echo "⚠️ Type checking found issues (non-blocking for Gradio UI code)"
|
| 31 |
+
}
|
| 32 |
+
echo ""
|
| 33 |
+
|
| 34 |
+
echo "✨ All Python quality checks passed!"
|
scripts/format-python.sh
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# Python Code Formatting and Auto-fix Script
|
| 3 |
+
# Automatically format and fix Python code issues
|
| 4 |
+
|
| 5 |
+
set -e
|
| 6 |
+
|
| 7 |
+
echo "🔧 Formatting and fixing Python code..."
|
| 8 |
+
echo ""
|
| 9 |
+
|
| 10 |
+
cd "$(dirname "$0")/../src/RoslynStone.GradioModule"
|
| 11 |
+
|
| 12 |
+
echo "1️⃣ Running Ruff formatter..."
|
| 13 |
+
ruff format .
|
| 14 |
+
echo "✅ Code formatted"
|
| 15 |
+
echo ""
|
| 16 |
+
|
| 17 |
+
echo "2️⃣ Running Ruff auto-fix..."
|
| 18 |
+
ruff check --fix --unsafe-fixes .
|
| 19 |
+
echo "✅ Auto-fixable issues resolved"
|
| 20 |
+
echo ""
|
| 21 |
+
|
| 22 |
+
echo "✨ Python code formatted and fixed!"
|
| 23 |
+
echo ""
|
| 24 |
+
echo "Run './scripts/check-python-quality.sh' to verify all checks pass."
|
src/RoslynStone.Api/Dockerfile
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Build stage
|
| 2 |
+
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
| 3 |
+
ARG BUILD_CONFIGURATION=Release
|
| 4 |
+
WORKDIR /src
|
| 5 |
+
|
| 6 |
+
# Copy solution and project files for better layer caching
|
| 7 |
+
COPY ["RoslynStone.sln", "./"]
|
| 8 |
+
COPY ["src/RoslynStone.Api/RoslynStone.Api.csproj", "src/RoslynStone.Api/"]
|
| 9 |
+
COPY ["src/RoslynStone.Core/RoslynStone.Core.csproj", "src/RoslynStone.Core/"]
|
| 10 |
+
COPY ["src/RoslynStone.Infrastructure/RoslynStone.Infrastructure.csproj", "src/RoslynStone.Infrastructure/"]
|
| 11 |
+
COPY ["src/RoslynStone.ServiceDefaults/RoslynStone.ServiceDefaults.csproj", "src/RoslynStone.ServiceDefaults/"]
|
| 12 |
+
COPY ["src/RoslynStone.GradioModule/RoslynStone.GradioModule.csproj", "src/RoslynStone.GradioModule/"]
|
| 13 |
+
|
| 14 |
+
# Restore dependencies (this layer will be cached if project files don't change)
|
| 15 |
+
RUN dotnet restore "src/RoslynStone.Api/RoslynStone.Api.csproj"
|
| 16 |
+
|
| 17 |
+
# Copy all source files
|
| 18 |
+
COPY . .
|
| 19 |
+
|
| 20 |
+
# Build the application
|
| 21 |
+
WORKDIR "/src/src/RoslynStone.Api"
|
| 22 |
+
# Build into the standard bin/$(Configuration)/$(TargetFramework) output which publish expects
|
| 23 |
+
RUN dotnet build "RoslynStone.Api.csproj" \
|
| 24 |
+
-c $BUILD_CONFIGURATION \
|
| 25 |
+
--no-restore
|
| 26 |
+
|
| 27 |
+
# Publish stage
|
| 28 |
+
FROM build AS publish
|
| 29 |
+
ARG BUILD_CONFIGURATION=Release
|
| 30 |
+
|
| 31 |
+
# Install CSnakes.Stage tool for Python environment setup
|
| 32 |
+
RUN dotnet tool install --global CSnakes.Stage
|
| 33 |
+
ENV PATH="/root/.dotnet/tools:${PATH}"
|
| 34 |
+
|
| 35 |
+
# Set up Python environment with CSnakes
|
| 36 |
+
# This downloads Python 3.12 redistributable and creates a venv
|
| 37 |
+
RUN setup-python --python 3.12 --venv /app/.venv --verbose
|
| 38 |
+
|
| 39 |
+
# Install UV (fast Python package installer) - only needed in build stage
|
| 40 |
+
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
|
| 41 |
+
ENV PATH="/root/.local/bin:${PATH}"
|
| 42 |
+
|
| 43 |
+
# Install all Python dependencies using UV into the venv
|
| 44 |
+
# This happens at build time, not runtime, for faster container startup
|
| 45 |
+
# --prerelease=allow is needed because gradio 6.0.0 depends on gradio-client 2.0.0.dev3
|
| 46 |
+
# We install dependencies directly rather than using editable install since this is just a script, not a package
|
| 47 |
+
WORKDIR "/src/src/RoslynStone.GradioModule"
|
| 48 |
+
RUN uv pip install \
|
| 49 |
+
"gradio>=6.0.0" \
|
| 50 |
+
"httpx>=0.27.0" \
|
| 51 |
+
"pygments>=2.17.0" \
|
| 52 |
+
"openai>=1.0.0" \
|
| 53 |
+
"anthropic>=0.25.0" \
|
| 54 |
+
"google-generativeai>=0.3.0" \
|
| 55 |
+
"huggingface_hub>=0.20.0" \
|
| 56 |
+
--python /app/.venv/bin/python \
|
| 57 |
+
--prerelease=allow
|
| 58 |
+
|
| 59 |
+
# Verify Gradio is installed correctly
|
| 60 |
+
RUN /app/.venv/bin/python3 -c "import gradio; print(f'Gradio {gradio.__version__} installed successfully')"
|
| 61 |
+
|
| 62 |
+
# Publish the application with ReadyToRun (R2R) for faster startup
|
| 63 |
+
# Note: We cannot use Native AOT because Roslyn requires dynamic code compilation
|
| 64 |
+
# R2R provides a hybrid approach - pre-compiled code with JIT fallback for dynamic scenarios
|
| 65 |
+
WORKDIR "/src/src/RoslynStone.Api"
|
| 66 |
+
RUN dotnet publish "RoslynStone.Api.csproj" \
|
| 67 |
+
-c $BUILD_CONFIGURATION \
|
| 68 |
+
-o /app/publish \
|
| 69 |
+
--no-restore \
|
| 70 |
+
--no-build \
|
| 71 |
+
/p:UseAppHost=false \
|
| 72 |
+
/p:PublishReadyToRun=true \
|
| 73 |
+
/p:PublishSingleFile=false \
|
| 74 |
+
/p:PublishTrimmed=false
|
| 75 |
+
|
| 76 |
+
# Runtime stage
|
| 77 |
+
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
|
| 78 |
+
WORKDIR /app
|
| 79 |
+
|
| 80 |
+
# Create non-root user for security
|
| 81 |
+
RUN groupadd -r appuser && useradd -r -g appuser -m -d /home/appuser appuser
|
| 82 |
+
|
| 83 |
+
# Copy published application
|
| 84 |
+
COPY --from=publish /app/publish .
|
| 85 |
+
COPY src/RoslynStone.Api/entrypoint.sh .
|
| 86 |
+
RUN chmod +x entrypoint.sh
|
| 87 |
+
|
| 88 |
+
# Copy CSnakes Python redistributable from build stage
|
| 89 |
+
COPY --from=publish /root/.config/CSnakes /home/appuser/.config/CSnakes
|
| 90 |
+
|
| 91 |
+
# Some venv scripts created during the publish stage contain shebangs
|
| 92 |
+
# that reference the root user's CSnakes path (/root/.config/CSnakes/...).
|
| 93 |
+
# Create a root-side symlink to the copied location so those shebangs
|
| 94 |
+
# keep working in the runtime image.
|
| 95 |
+
RUN mkdir -p /root/.config \
|
| 96 |
+
&& ln -s /home/appuser/.config/CSnakes /root/.config/CSnakes || true
|
| 97 |
+
|
| 98 |
+
# Copy Python virtual environment with pre-installed Gradio from build stage
|
| 99 |
+
COPY --from=publish /app/.venv .venv
|
| 100 |
+
|
| 101 |
+
# Verify venv was copied correctly
|
| 102 |
+
RUN test -f /app/.venv/bin/python3 && test -f /app/.venv/bin/pip || \
|
| 103 |
+
(echo "ERROR: Virtual environment not copied correctly!" && exit 1)
|
| 104 |
+
|
| 105 |
+
# Create symlink for backward compatibility (some code may reference /app/venv)
|
| 106 |
+
RUN ln -s /app/.venv /app/venv
|
| 107 |
+
# Change ownership to non-root user
|
| 108 |
+
RUN chown -R appuser:appuser /app /home/appuser/.config
|
| 109 |
+
|
| 110 |
+
# Set environment variables for MCP server and Python
|
| 111 |
+
# MCP_TRANSPORT: "stdio" (default) or "http"
|
| 112 |
+
# When using HTTP transport, set ASPNETCORE_URLS to configure listening address
|
| 113 |
+
# DOTNET_RUNNING_IN_CONTAINER tells the app to skip Python dependency installation
|
| 114 |
+
# since dependencies are pre-installed during docker build
|
| 115 |
+
ENV DOTNET_ENVIRONMENT=Production \
|
| 116 |
+
MCP_TRANSPORT=stdio \
|
| 117 |
+
ASPNETCORE_URLS= \
|
| 118 |
+
DOTNET_EnableDiagnostics=0 \
|
| 119 |
+
DOTNET_RUNNING_IN_CONTAINER=true \
|
| 120 |
+
LD_LIBRARY_PATH=/home/appuser/.config/CSnakes/python3.12.9/python/install/lib \
|
| 121 |
+
PYTHONHOME=/home/appuser/.config/CSnakes/python3.12.9/python/install
|
| 122 |
+
|
| 123 |
+
# Switch to non-root user
|
| 124 |
+
USER appuser
|
| 125 |
+
|
| 126 |
+
# The MCP server supports both stdio and HTTP transports
|
| 127 |
+
# - Stdio (default): Set MCP_TRANSPORT=stdio, no ports exposed
|
| 128 |
+
# - HTTP: Set MCP_TRANSPORT=http and ASPNETCORE_URLS=http://+:8080, then EXPOSE 8080
|
| 129 |
+
# In HTTP mode, Gradio landing page will be available at root (/)
|
| 130 |
+
# Telemetry will be sent to the OTEL endpoint configured via environment variables
|
| 131 |
+
|
| 132 |
+
# Example for HTTP mode:
|
| 133 |
+
# ENV MCP_TRANSPORT=http
|
| 134 |
+
# ENV ASPNETCORE_URLS=http://+:8080
|
| 135 |
+
# EXPOSE 8080
|
| 136 |
+
|
| 137 |
+
ENTRYPOINT ["./entrypoint.sh"]
|
| 138 |
+
|
src/RoslynStone.Api/Program.cs
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using CSnakes.Runtime;
|
| 2 |
+
using RoslynStone.Infrastructure.Models;
|
| 3 |
+
using RoslynStone.Infrastructure.Resources;
|
| 4 |
+
using RoslynStone.Infrastructure.Services;
|
| 5 |
+
using RoslynStone.Infrastructure.Tools;
|
| 6 |
+
|
| 7 |
+
// Determine transport mode from environment variable
|
| 8 |
+
// MCP_TRANSPORT: "stdio" (default) or "http"
|
| 9 |
+
var transportMode =
|
| 10 |
+
Environment.GetEnvironmentVariable("MCP_TRANSPORT")?.ToLowerInvariant() ?? "stdio";
|
| 11 |
+
var useHttpTransport = transportMode == "http";
|
| 12 |
+
|
| 13 |
+
// Shared method to configure logging to stderr for both transport modes
|
| 14 |
+
static void ConfigureLogging(ILoggingBuilder logging)
|
| 15 |
+
{
|
| 16 |
+
logging.ClearProviders();
|
| 17 |
+
logging.AddConsole(options =>
|
| 18 |
+
{
|
| 19 |
+
options.LogToStandardErrorThreshold = LogLevel.Trace;
|
| 20 |
+
});
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
// Shared method to register all services
|
| 24 |
+
static void RegisterServices(IServiceCollection services)
|
| 25 |
+
{
|
| 26 |
+
// Register security configuration
|
| 27 |
+
// Use development defaults for now, can be configured via appsettings.json later
|
| 28 |
+
services.AddSingleton(SecurityConfiguration.CreateDevelopmentDefaults());
|
| 29 |
+
|
| 30 |
+
// Register services
|
| 31 |
+
services.AddSingleton<RoslynScriptingService>();
|
| 32 |
+
services.AddSingleton<DocumentationService>();
|
| 33 |
+
services.AddSingleton<CompilationService>();
|
| 34 |
+
services.AddSingleton<AssemblyExecutionService>();
|
| 35 |
+
services.AddSingleton<NuGetService>();
|
| 36 |
+
services.AddSingleton<IReplContextManager, ReplContextManager>();
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
if (useHttpTransport)
|
| 40 |
+
{
|
| 41 |
+
// HTTP Transport Mode - Use WebApplication builder
|
| 42 |
+
var builder = WebApplication.CreateBuilder(args);
|
| 43 |
+
|
| 44 |
+
// Configure logging to stderr for consistency with stdio transport
|
| 45 |
+
// This is a best practice even in HTTP mode
|
| 46 |
+
ConfigureLogging(builder.Logging);
|
| 47 |
+
|
| 48 |
+
// Add Aspire service defaults (OpenTelemetry, health checks, service discovery)
|
| 49 |
+
builder.AddServiceDefaults();
|
| 50 |
+
|
| 51 |
+
// Register all services, command handlers, and query handlers
|
| 52 |
+
RegisterServices(builder.Services);
|
| 53 |
+
|
| 54 |
+
// Configure CSnakes Python environment with UV for Gradio
|
| 55 |
+
var pythonHome = AppContext.BaseDirectory; // Python files are copied to output from GradioModule
|
| 56 |
+
var venvPath = Path.Combine(pythonHome, ".venv");
|
| 57 |
+
|
| 58 |
+
// Dynamically determine Python version for CSnakes path
|
| 59 |
+
var csnakesPythonVersion =
|
| 60 |
+
Environment.GetEnvironmentVariable("CSNAKES_PYTHON_VERSION") ?? "3.12.9";
|
| 61 |
+
var csnakesPythonPath = Path.Combine(
|
| 62 |
+
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
| 63 |
+
".config",
|
| 64 |
+
"CSnakes",
|
| 65 |
+
$"python{csnakesPythonVersion}",
|
| 66 |
+
"python",
|
| 67 |
+
"install",
|
| 68 |
+
"lib"
|
| 69 |
+
);
|
| 70 |
+
var currentLdPath = Environment.GetEnvironmentVariable("LD_LIBRARY_PATH");
|
| 71 |
+
if (string.IsNullOrEmpty(currentLdPath))
|
| 72 |
+
{
|
| 73 |
+
Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", csnakesPythonPath);
|
| 74 |
+
}
|
| 75 |
+
else if (!currentLdPath.Contains(csnakesPythonPath))
|
| 76 |
+
{
|
| 77 |
+
Environment.SetEnvironmentVariable(
|
| 78 |
+
"LD_LIBRARY_PATH",
|
| 79 |
+
$"{csnakesPythonPath}:{currentLdPath}"
|
| 80 |
+
);
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
// Set PATH to include UV location
|
| 84 |
+
var uvPath = Path.Combine(
|
| 85 |
+
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
| 86 |
+
".local",
|
| 87 |
+
"bin"
|
| 88 |
+
);
|
| 89 |
+
var currentPath = Environment.GetEnvironmentVariable("PATH");
|
| 90 |
+
if (!string.IsNullOrEmpty(currentPath) && !currentPath.Contains(uvPath))
|
| 91 |
+
{
|
| 92 |
+
Environment.SetEnvironmentVariable("PATH", $"{uvPath}:{currentPath}");
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
// Check if we're running in a container where dependencies are pre-installed
|
| 96 |
+
// or in development mode where we need to install at runtime
|
| 97 |
+
var isContainer = Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true";
|
| 98 |
+
var skipPythonInstall = Environment.GetEnvironmentVariable("SKIP_PYTHON_INSTALL") == "true";
|
| 99 |
+
var preInstalled = isContainer || skipPythonInstall;
|
| 100 |
+
|
| 101 |
+
// Check if setup-python action set Python3_ROOT_DIR (used in CI)
|
| 102 |
+
var pythonRootDir = Environment.GetEnvironmentVariable("Python3_ROOT_DIR");
|
| 103 |
+
var useEnvVarLocator = !string.IsNullOrEmpty(pythonRootDir);
|
| 104 |
+
|
| 105 |
+
var pythonBuilder = builder
|
| 106 |
+
.Services.WithPython()
|
| 107 |
+
.WithHome(pythonHome)
|
| 108 |
+
.WithVirtualEnvironment(venvPath);
|
| 109 |
+
|
| 110 |
+
// Choose Python locator based on environment:
|
| 111 |
+
// - In CI with setup-python: use environment variable locator (Python3_ROOT_DIR)
|
| 112 |
+
// - Otherwise: use redistributable (downloads Python automatically)
|
| 113 |
+
if (useEnvVarLocator)
|
| 114 |
+
{
|
| 115 |
+
pythonBuilder = pythonBuilder.FromEnvironmentVariable("Python3_ROOT_DIR", "3.12");
|
| 116 |
+
}
|
| 117 |
+
else
|
| 118 |
+
{
|
| 119 |
+
pythonBuilder = pythonBuilder.FromRedistributable(); // Use Python from CSnakes redistributable
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
// Only install dependencies at runtime if not pre-installed (e.g., in Docker)
|
| 123 |
+
// In containers, dependencies are installed during docker build for faster startup
|
| 124 |
+
if (!preInstalled)
|
| 125 |
+
{
|
| 126 |
+
// Set UV_PRERELEASE=allow because gradio 6.0 depends on gradio-client 2.0.0.dev3
|
| 127 |
+
Environment.SetEnvironmentVariable("UV_PRERELEASE", "allow");
|
| 128 |
+
pythonBuilder.WithUvInstaller("pyproject.toml");
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
builder.Services.AddHttpClient();
|
| 132 |
+
|
| 133 |
+
// Get Gradio server port configuration early for YARP setup
|
| 134 |
+
var configuration = builder.Configuration;
|
| 135 |
+
var isHuggingFaceSpace = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SPACE_ID"));
|
| 136 |
+
var defaultGradioPort = isHuggingFaceSpace ? 7861 : 7860;
|
| 137 |
+
var gradioPort = configuration.GetValue<int>("GradioServerPort", defaultGradioPort);
|
| 138 |
+
|
| 139 |
+
// Add YARP reverse proxy for Gradio landing page
|
| 140 |
+
// Proxy ALL traffic except /mcp to Gradio (simplifies routing and catches all assets)
|
| 141 |
+
// ReSharper disable once RedundantTypeArgumentsOfMethod
|
| 142 |
+
builder
|
| 143 |
+
.Services.AddReverseProxy()
|
| 144 |
+
.LoadFromMemory(
|
| 145 |
+
[
|
| 146 |
+
new Yarp.ReverseProxy.Configuration.RouteConfig
|
| 147 |
+
{
|
| 148 |
+
RouteId = "gradio-catchall",
|
| 149 |
+
ClusterId = "gradio-cluster",
|
| 150 |
+
Match = new Yarp.ReverseProxy.Configuration.RouteMatch
|
| 151 |
+
{
|
| 152 |
+
Path = "{**catch-all}", // Catch everything
|
| 153 |
+
},
|
| 154 |
+
Order = 1000, // Low priority - runs after MCP routes
|
| 155 |
+
Transforms = new List<IReadOnlyDictionary<string, string>>
|
| 156 |
+
{
|
| 157 |
+
// Prevent content sniffing attacks - enforce strict MIME type checking
|
| 158 |
+
new Dictionary<string, string>
|
| 159 |
+
{
|
| 160 |
+
{ "ResponseHeader", "X-Content-Type-Options" },
|
| 161 |
+
{ "Append", "nosniff" },
|
| 162 |
+
},
|
| 163 |
+
// Allow iframe embedding in HuggingFace Spaces, SAMEORIGIN for others
|
| 164 |
+
new Dictionary<string, string>
|
| 165 |
+
{
|
| 166 |
+
{ "ResponseHeader", "X-Frame-Options" },
|
| 167 |
+
{ "Append", isHuggingFaceSpace ? "ALLOWALL" : "SAMEORIGIN" },
|
| 168 |
+
},
|
| 169 |
+
// Set permissive CSP for Gradio assets (needs unsafe-inline, unsafe-eval)
|
| 170 |
+
// Gradio uses inline scripts and eval for dynamic UI
|
| 171 |
+
new Dictionary<string, string>
|
| 172 |
+
{
|
| 173 |
+
{ "ResponseHeader", "Content-Security-Policy" },
|
| 174 |
+
{
|
| 175 |
+
"Append",
|
| 176 |
+
"default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: https:; "
|
| 177 |
+
+ "img-src 'self' data: blob: https:; "
|
| 178 |
+
+ "font-src 'self' data: https:; "
|
| 179 |
+
+ "connect-src 'self' https: wss: ws:;"
|
| 180 |
+
},
|
| 181 |
+
},
|
| 182 |
+
new Dictionary<string, string>
|
| 183 |
+
{
|
| 184 |
+
{ "ResponseHeader", "Referrer-Policy" },
|
| 185 |
+
{ "Append", "strict-origin-when-cross-origin" },
|
| 186 |
+
},
|
| 187 |
+
},
|
| 188 |
+
},
|
| 189 |
+
],
|
| 190 |
+
[
|
| 191 |
+
new Yarp.ReverseProxy.Configuration.ClusterConfig
|
| 192 |
+
{
|
| 193 |
+
ClusterId = "gradio-cluster",
|
| 194 |
+
Destinations = new Dictionary<
|
| 195 |
+
string,
|
| 196 |
+
Yarp.ReverseProxy.Configuration.DestinationConfig
|
| 197 |
+
>
|
| 198 |
+
{
|
| 199 |
+
{
|
| 200 |
+
"gradio-destination",
|
| 201 |
+
new Yarp.ReverseProxy.Configuration.DestinationConfig
|
| 202 |
+
{
|
| 203 |
+
Address = $"http://127.0.0.1:{gradioPort}",
|
| 204 |
+
}
|
| 205 |
+
},
|
| 206 |
+
},
|
| 207 |
+
},
|
| 208 |
+
]
|
| 209 |
+
);
|
| 210 |
+
|
| 211 |
+
// WARNING: HTTP transport has no authentication by default.
|
| 212 |
+
// Configure authentication, CORS, and rate limiting before exposing publicly.
|
| 213 |
+
// This server can execute arbitrary C# code.
|
| 214 |
+
builder
|
| 215 |
+
.Services.AddMcpServer()
|
| 216 |
+
.WithHttpTransport()
|
| 217 |
+
.WithPromptsFromAssembly(typeof(GuidancePrompts).Assembly)
|
| 218 |
+
.WithToolsFromAssembly(typeof(ReplTools).Assembly)
|
| 219 |
+
.WithResourcesFromAssembly(typeof(DocumentationResource).Assembly);
|
| 220 |
+
|
| 221 |
+
var app = builder.Build();
|
| 222 |
+
|
| 223 |
+
// Gradio port was already calculated during builder configuration for YARP
|
| 224 |
+
|
| 225 |
+
// Start Gradio landing page using CSnakes in the background
|
| 226 |
+
_ = Task.Run(async () =>
|
| 227 |
+
{
|
| 228 |
+
try
|
| 229 |
+
{
|
| 230 |
+
var env = app.Services.GetRequiredService<IPythonEnvironment>();
|
| 231 |
+
var gradioLauncher = env.GradioLauncher();
|
| 232 |
+
|
| 233 |
+
// Determine the base URL for MCP server connection
|
| 234 |
+
// Priority: BASE_URL env var > ASPNETCORE_URLS > default
|
| 235 |
+
var baseUrl = app.Configuration["BASE_URL"];
|
| 236 |
+
if (string.IsNullOrEmpty(baseUrl))
|
| 237 |
+
{
|
| 238 |
+
// Try to get from ASPNETCORE_URLS (used in HuggingFace Spaces and Docker)
|
| 239 |
+
var aspNetCoreUrls = Environment.GetEnvironmentVariable("ASPNETCORE_URLS");
|
| 240 |
+
if (!string.IsNullOrEmpty(aspNetCoreUrls))
|
| 241 |
+
{
|
| 242 |
+
// ASPNETCORE_URLS can be semicolon-separated, take the first HTTP URL
|
| 243 |
+
// and replace '+' or '*' with 'localhost' for local connections
|
| 244 |
+
var firstUrl = aspNetCoreUrls
|
| 245 |
+
.Split(';')
|
| 246 |
+
.FirstOrDefault(u => u.StartsWith("http://"));
|
| 247 |
+
if (!string.IsNullOrEmpty(firstUrl))
|
| 248 |
+
{
|
| 249 |
+
baseUrl = firstUrl
|
| 250 |
+
.Replace("http://+:", "http://localhost:")
|
| 251 |
+
.Replace("http://*:", "http://localhost:")
|
| 252 |
+
.Replace("http://0.0.0.0:", "http://localhost:");
|
| 253 |
+
}
|
| 254 |
+
}
|
| 255 |
+
}
|
| 256 |
+
|
| 257 |
+
// Final fallback
|
| 258 |
+
if (string.IsNullOrEmpty(baseUrl))
|
| 259 |
+
{
|
| 260 |
+
baseUrl = "http://localhost:7071";
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
// Check if Gradio is installed
|
| 264 |
+
var isInstalled = gradioLauncher.CheckGradioInstalled();
|
| 265 |
+
if (!isInstalled)
|
| 266 |
+
{
|
| 267 |
+
app.Logger.LogWarning("Gradio is not installed in the Python environment");
|
| 268 |
+
return;
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
app.Logger.LogInformation(
|
| 272 |
+
"Starting Gradio server on port {GradioPort} with base URL {BaseUrl}",
|
| 273 |
+
gradioPort,
|
| 274 |
+
baseUrl
|
| 275 |
+
);
|
| 276 |
+
|
| 277 |
+
// Start Gradio server (runs in a thread inside Python)
|
| 278 |
+
var result = gradioLauncher.StartGradioServer(baseUrl, gradioPort);
|
| 279 |
+
app.Logger.LogInformation("Gradio landing page result: {Result}", result);
|
| 280 |
+
|
| 281 |
+
// Give Gradio a moment to start up
|
| 282 |
+
await Task.Delay(2000);
|
| 283 |
+
|
| 284 |
+
app.Logger.LogInformation(
|
| 285 |
+
"Gradio landing page should now be accessible at http://127.0.0.1:{GradioPort}",
|
| 286 |
+
gradioPort
|
| 287 |
+
);
|
| 288 |
+
}
|
| 289 |
+
catch (Exception ex) when (ex is not OperationCanceledException)
|
| 290 |
+
{
|
| 291 |
+
app.Logger.LogError(ex, "Failed to start Gradio landing page");
|
| 292 |
+
}
|
| 293 |
+
});
|
| 294 |
+
|
| 295 |
+
// Security headers will be applied via YARP response transforms (see AddReverseProxy configuration)
|
| 296 |
+
// This ensures headers are only added to proxied Gradio responses, not all endpoints like /mcp or /health
|
| 297 |
+
|
| 298 |
+
// Map YARP reverse proxy routes (Gradio)
|
| 299 |
+
app.MapReverseProxy();
|
| 300 |
+
|
| 301 |
+
// Map default health check endpoints for HTTP transport
|
| 302 |
+
app.MapDefaultEndpoints();
|
| 303 |
+
|
| 304 |
+
// WARNING: This endpoint allows code execution. Add authentication before exposing publicly.
|
| 305 |
+
// Map MCP HTTP endpoints at /mcp
|
| 306 |
+
app.MapMcp("/mcp");
|
| 307 |
+
|
| 308 |
+
await app.RunAsync();
|
| 309 |
+
}
|
| 310 |
+
else
|
| 311 |
+
{
|
| 312 |
+
// Stdio Transport Mode - Use generic Host builder
|
| 313 |
+
var builder = Host.CreateApplicationBuilder(args);
|
| 314 |
+
|
| 315 |
+
// Configure logging to stderr for consistency and to avoid interfering with stdio transport
|
| 316 |
+
// This ensures MCP protocol integrity while preserving OpenTelemetry logging
|
| 317 |
+
ConfigureLogging(builder.Logging);
|
| 318 |
+
|
| 319 |
+
// Add Aspire service defaults (OpenTelemetry, health checks, service discovery)
|
| 320 |
+
// This adds OpenTelemetry logging provider which will also log to stderr
|
| 321 |
+
builder.AddServiceDefaults();
|
| 322 |
+
|
| 323 |
+
// Register all services, command handlers, and query handlers
|
| 324 |
+
RegisterServices(builder.Services);
|
| 325 |
+
|
| 326 |
+
// Configure MCP server with stdio transport
|
| 327 |
+
// Register tools from the Infrastructure assembly where the MCP tools are defined
|
| 328 |
+
builder
|
| 329 |
+
.Services.AddMcpServer()
|
| 330 |
+
.WithStdioServerTransport()
|
| 331 |
+
.WithPromptsFromAssembly(typeof(GuidancePrompts).Assembly)
|
| 332 |
+
.WithToolsFromAssembly(typeof(ReplTools).Assembly)
|
| 333 |
+
.WithResourcesFromAssembly(typeof(DocumentationResource).Assembly);
|
| 334 |
+
|
| 335 |
+
// Build and run the host
|
| 336 |
+
await builder.Build().RunAsync();
|
| 337 |
+
}
|
src/RoslynStone.Api/RoslynStone.Api.csproj
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<Project Sdk="Microsoft.NET.Sdk.Web">
|
| 2 |
+
<PropertyGroup>
|
| 3 |
+
<OutputType>Exe</OutputType>
|
| 4 |
+
<TargetFramework>net10.0</TargetFramework>
|
| 5 |
+
<LangVersion>14</LangVersion>
|
| 6 |
+
<Nullable>enable</Nullable>
|
| 7 |
+
<ImplicitUsings>enable</ImplicitUsings>
|
| 8 |
+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
| 9 |
+
|
| 10 |
+
<!-- Performance optimizations compatible with dynamic compilation -->
|
| 11 |
+
<!-- ReadyToRun (R2R): Pre-compile for faster startup while keeping JIT for dynamic code -->
|
| 12 |
+
<PublishReadyToRun>true</PublishReadyToRun>
|
| 13 |
+
|
| 14 |
+
<!-- Disable AOT and trimming - incompatible with Roslyn's dynamic compilation -->
|
| 15 |
+
<!-- Roslyn requires: reflection, dynamic loading, runtime code generation -->
|
| 16 |
+
<PublishAot>false</PublishAot>
|
| 17 |
+
<PublishTrimmed>false</PublishTrimmed>
|
| 18 |
+
|
| 19 |
+
<!-- Enable optimizations -->
|
| 20 |
+
<DebugType>embedded</DebugType>
|
| 21 |
+
<DebugSymbols>true</DebugSymbols>
|
| 22 |
+
</PropertyGroup>
|
| 23 |
+
|
| 24 |
+
<ItemGroup>
|
| 25 |
+
<PackageReference Include="ModelContextProtocol" Version="0.4.0-preview.3" />
|
| 26 |
+
<PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.4.0-preview.3" />
|
| 27 |
+
<PackageReference Include="CSnakes.Runtime" Version="1.*-*" />
|
| 28 |
+
<PackageReference Include="python" Version="3.12.9" />
|
| 29 |
+
<PackageReference Include="Yarp.ReverseProxy" Version="2.3.0" />
|
| 30 |
+
</ItemGroup>
|
| 31 |
+
|
| 32 |
+
<ItemGroup>
|
| 33 |
+
<ProjectReference Include="..\RoslynStone.Core\RoslynStone.Core.csproj" />
|
| 34 |
+
<ProjectReference Include="..\RoslynStone.Infrastructure\RoslynStone.Infrastructure.csproj" />
|
| 35 |
+
<ProjectReference Include="..\RoslynStone.ServiceDefaults\RoslynStone.ServiceDefaults.csproj" />
|
| 36 |
+
<ProjectReference Include="..\RoslynStone.GradioModule\RoslynStone.GradioModule.csproj" />
|
| 37 |
+
</ItemGroup>
|
| 38 |
+
</Project>
|
src/RoslynStone.Api/entrypoint.sh
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
set -e
|
| 3 |
+
|
| 4 |
+
# Check if running in Hugging Face Spaces
|
| 5 |
+
if [ -n "$SPACE_ID" ]; then
|
| 6 |
+
echo "Detected Hugging Face Space environment. Switching to HTTP transport."
|
| 7 |
+
export MCP_TRANSPORT=http
|
| 8 |
+
# Default to port 7860 if not set (HF Spaces requirement)
|
| 9 |
+
if [ -z "$ASPNETCORE_URLS" ]; then
|
| 10 |
+
export ASPNETCORE_URLS=http://+:7860
|
| 11 |
+
fi
|
| 12 |
+
fi
|
| 13 |
+
|
| 14 |
+
# Verify venv exists and has Gradio installed (should be pre-built in Docker image)
|
| 15 |
+
if [ ! -f /app/.venv/bin/python3 ] || ! /app/.venv/bin/python3 -c "import gradio" 2>/dev/null; then
|
| 16 |
+
echo "WARNING: Virtual environment missing or incomplete. Attempting repair..."
|
| 17 |
+
|
| 18 |
+
# Find the CSnakes redistributable Python interpreter
|
| 19 |
+
PY_INTERP=$(ls /home/appuser/.config/CSnakes/python*/python/install/bin/python3 2>/dev/null | head -n1 || true)
|
| 20 |
+
|
| 21 |
+
if [ -n "$PY_INTERP" ] && [ -x "$PY_INTERP" ]; then
|
| 22 |
+
echo "Found Python interpreter: $PY_INTERP"
|
| 23 |
+
|
| 24 |
+
# Recreate venv only if necessary
|
| 25 |
+
if [ ! -f /app/.venv/bin/python3 ]; then
|
| 26 |
+
echo "Recreating virtualenv at /app/.venv..."
|
| 27 |
+
rm -rf /app/.venv
|
| 28 |
+
"$PY_INTERP" -m venv /app/.venv
|
| 29 |
+
fi
|
| 30 |
+
|
| 31 |
+
# Install Gradio if missing
|
| 32 |
+
if ! /app/.venv/bin/python3 -c "import gradio" 2>/dev/null; then
|
| 33 |
+
echo "Installing Gradio..."
|
| 34 |
+
# Try to use uv if available, otherwise fall back to pip
|
| 35 |
+
if command -v uv &> /dev/null || [ -f /app/.venv/bin/uv ]; then
|
| 36 |
+
UV_BIN=$(command -v uv 2>/dev/null || echo /app/.venv/bin/uv)
|
| 37 |
+
$UV_BIN pip install "gradio>=5.0.0,<6.0.0" --python /app/.venv/bin/python3
|
| 38 |
+
else
|
| 39 |
+
/app/.venv/bin/pip install --quiet "gradio>=5.0.0,<6.0.0"
|
| 40 |
+
fi
|
| 41 |
+
fi
|
| 42 |
+
else
|
| 43 |
+
echo "ERROR: Python redistributable not found and venv is broken!"
|
| 44 |
+
echo "Container may not function correctly."
|
| 45 |
+
fi
|
| 46 |
+
fi
|
| 47 |
+
|
| 48 |
+
# Ensure symlink exists for backward compatibility
|
| 49 |
+
if [ ! -e /app/venv ] && [ -d /app/.venv ]; then
|
| 50 |
+
ln -s /app/.venv /app/venv 2>/dev/null || true
|
| 51 |
+
fi
|
| 52 |
+
|
| 53 |
+
exec dotnet RoslynStone.Api.dll "$@"
|
src/RoslynStone.AppHost/AppHost.cs
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
var builder = DistributedApplication.CreateBuilder(args);
|
| 2 |
+
|
| 3 |
+
// Add the MCP server with HTTP transport
|
| 4 |
+
// This variant exposes HTTP endpoints for MCP protocol communication
|
| 5 |
+
// Make MCP HTTP endpoint port configurable via MCP_HTTP_PORT environment variable (default: 8080)
|
| 6 |
+
var mcpHttpPort = int.TryParse(builder.Configuration["MCP_HTTP_PORT"], out var httpPort)
|
| 7 |
+
? httpPort
|
| 8 |
+
: 8080;
|
| 9 |
+
var mcpServer = builder
|
| 10 |
+
.AddProject<Projects.RoslynStone_Api>("roslyn-stone-mcp")
|
| 11 |
+
.WithEnvironment("MCP_TRANSPORT", "http")
|
| 12 |
+
.WithEnvironment("OTEL_SERVICE_NAME", "roslyn-stone-mcp")
|
| 13 |
+
// Create the standard 'http' endpoint for MCP servers (inspector expects endpoint name 'http')
|
| 14 |
+
.WithHttpEndpoint(port: mcpHttpPort, name: "http")
|
| 15 |
+
.WithExternalHttpEndpoints()
|
| 16 |
+
.PublishAsDockerFile();
|
| 17 |
+
|
| 18 |
+
// Add MCP Inspector for development/testing (only in development mode)
|
| 19 |
+
// The inspector provides a web UI for testing MCP tools via SSE transport
|
| 20 |
+
// Ports can be configured via environment variables: INSPECTOR_UI_PORT and INSPECTOR_PROXY_PORT
|
| 21 |
+
if (
|
| 22 |
+
builder.Configuration["ASPNETCORE_ENVIRONMENT"] == "Development"
|
| 23 |
+
|| builder.Configuration["DOTNET_ENVIRONMENT"] == "Development"
|
| 24 |
+
|| string.IsNullOrEmpty(builder.Configuration["ASPNETCORE_ENVIRONMENT"])
|
| 25 |
+
)
|
| 26 |
+
{
|
| 27 |
+
var inspectorUiPort = int.TryParse(builder.Configuration["INSPECTOR_UI_PORT"], out var uiPort)
|
| 28 |
+
? uiPort
|
| 29 |
+
: 6274;
|
| 30 |
+
var inspectorProxyPort = int.TryParse(
|
| 31 |
+
builder.Configuration["INSPECTOR_PROXY_PORT"],
|
| 32 |
+
out var proxyPort
|
| 33 |
+
)
|
| 34 |
+
? proxyPort
|
| 35 |
+
: 6277;
|
| 36 |
+
|
| 37 |
+
// Add the MCP Inspector as a managed AppHost resource instead of launching the JS inspector via npx.
|
| 38 |
+
// Use server and client ports from configuration and attach to the MCP server created above.
|
| 39 |
+
_ = builder
|
| 40 |
+
.AddMcpInspector(
|
| 41 |
+
"mcp-inspector",
|
| 42 |
+
options =>
|
| 43 |
+
{
|
| 44 |
+
options.ClientPort = inspectorUiPort;
|
| 45 |
+
options.ServerPort = inspectorProxyPort;
|
| 46 |
+
// Default inspector version will be used; override with InspectorVersion if needed.
|
| 47 |
+
}
|
| 48 |
+
)
|
| 49 |
+
// Connect the Inspector to the MCP server resource
|
| 50 |
+
.WithMcpServer(mcpServer)
|
| 51 |
+
// Expose the inspector's HTTP endpoints and mark it as a development-only resource
|
| 52 |
+
.WithExternalHttpEndpoints()
|
| 53 |
+
.ExcludeFromManifest();
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
builder.Build().Run();
|
src/RoslynStone.AppHost/RoslynStone.AppHost.csproj
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<Project Sdk="Aspire.AppHost.Sdk/13.0.0">
|
| 2 |
+
<PropertyGroup>
|
| 3 |
+
<OutputType>Exe</OutputType>
|
| 4 |
+
<TargetFramework>net10.0</TargetFramework>
|
| 5 |
+
<ImplicitUsings>enable</ImplicitUsings>
|
| 6 |
+
<Nullable>enable</Nullable>
|
| 7 |
+
<UserSecretsId>9c3d7413-72a5-4b12-a417-e65956ff9904</UserSecretsId>
|
| 8 |
+
</PropertyGroup>
|
| 9 |
+
|
| 10 |
+
<ItemGroup>
|
| 11 |
+
<ProjectReference Include="..\RoslynStone.Api\RoslynStone.Api.csproj" />
|
| 12 |
+
<ProjectReference
|
| 13 |
+
Include="..\RoslynStone.ServiceDefaults\RoslynStone.ServiceDefaults.csproj"
|
| 14 |
+
IsAspireProjectResource="false"
|
| 15 |
+
/>
|
| 16 |
+
<PackageReference Include="CommunityToolkit.Aspire.Hosting.McpInspector" Version="9.7.1" />
|
| 17 |
+
</ItemGroup>
|
| 18 |
+
</Project>
|
src/RoslynStone.AppHost/appsettings.Development.json
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"Logging": {
|
| 3 |
+
"LogLevel": {
|
| 4 |
+
"Default": "Information",
|
| 5 |
+
"Microsoft.AspNetCore": "Warning"
|
| 6 |
+
}
|
| 7 |
+
}
|
| 8 |
+
}
|
src/RoslynStone.AppHost/appsettings.json
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"Logging": {
|
| 3 |
+
"LogLevel": {
|
| 4 |
+
"Default": "Information",
|
| 5 |
+
"Microsoft.AspNetCore": "Warning",
|
| 6 |
+
"Aspire.Hosting.Dcp": "Warning"
|
| 7 |
+
}
|
| 8 |
+
}
|
| 9 |
+
}
|
src/RoslynStone.Core/RoslynStone.Core.csproj
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<Project Sdk="Microsoft.NET.Sdk">
|
| 2 |
+
<PropertyGroup>
|
| 3 |
+
<TargetFramework>net10.0</TargetFramework>
|
| 4 |
+
<LangVersion>14</LangVersion>
|
| 5 |
+
<ImplicitUsings>enable</ImplicitUsings>
|
| 6 |
+
<Nullable>enable</Nullable>
|
| 7 |
+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
| 8 |
+
|
| 9 |
+
<!-- NuGet Package Metadata -->
|
| 10 |
+
<PackageId>RoslynStone.Core</PackageId>
|
| 11 |
+
<Authors>Dylan Langston</Authors>
|
| 12 |
+
<Description>Core domain models, commands, queries, and CQRS interfaces for RoslynStone - a C# REPL service for LLMs</Description>
|
| 13 |
+
<PackageTags>roslyn;repl;csharp;mcp;llm;ai;scripting</PackageTags>
|
| 14 |
+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
| 15 |
+
<PackageProjectUrl>https://github.com/dylanlangston/Roslyn-Stone</PackageProjectUrl>
|
| 16 |
+
<RepositoryUrl>https://github.com/dylanlangston/Roslyn-Stone</RepositoryUrl>
|
| 17 |
+
<RepositoryType>git</RepositoryType>
|
| 18 |
+
<PackageReadmeFile>README.md</PackageReadmeFile>
|
| 19 |
+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
| 20 |
+
</PropertyGroup>
|
| 21 |
+
|
| 22 |
+
<ItemGroup>
|
| 23 |
+
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
|
| 24 |
+
</ItemGroup>
|
| 25 |
+
|
| 26 |
+
<ItemGroup>
|
| 27 |
+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.14.0" />
|
| 28 |
+
</ItemGroup>
|
| 29 |
+
</Project>
|
src/RoslynStone.GradioModule/RoslynStone.GradioModule.csproj
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<Project Sdk="Microsoft.NET.Sdk">
|
| 2 |
+
<PropertyGroup>
|
| 3 |
+
<TargetFramework>net10.0</TargetFramework>
|
| 4 |
+
<LangVersion>14</LangVersion>
|
| 5 |
+
<Nullable>enable</Nullable>
|
| 6 |
+
<ImplicitUsings>enable</ImplicitUsings>
|
| 7 |
+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
| 8 |
+
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
| 9 |
+
</PropertyGroup>
|
| 10 |
+
|
| 11 |
+
<ItemGroup>
|
| 12 |
+
<AdditionalFiles Include="gradio_launcher.py">
|
| 13 |
+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
| 14 |
+
</AdditionalFiles>
|
| 15 |
+
<None Include="gradio_app.py" CopyToOutputDirectory="Always" />
|
| 16 |
+
<None Include="pyproject.toml" CopyToOutputDirectory="Always" />
|
| 17 |
+
<None Include=".venv\**\*" CopyToOutputDirectory="Always" Condition="Exists('.venv')" />
|
| 18 |
+
</ItemGroup>
|
| 19 |
+
|
| 20 |
+
<ItemGroup>
|
| 21 |
+
<PackageReference Include="CSnakes.Runtime" Version="1.*-*" />
|
| 22 |
+
</ItemGroup>
|
| 23 |
+
</Project>
|
src/RoslynStone.GradioModule/gradio_app.py
ADDED
|
@@ -0,0 +1,1279 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Interactive Gradio UI for Roslyn-Stone MCP Server
|
| 2 |
+
Provides dynamic testing interface for MCP tools, resources, and prompts.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import json
|
| 8 |
+
import os
|
| 9 |
+
from typing import Any
|
| 10 |
+
|
| 11 |
+
import gradio as gr
|
| 12 |
+
import httpx
|
| 13 |
+
from pygments import highlight
|
| 14 |
+
from pygments.formatters import HtmlFormatter
|
| 15 |
+
from pygments.lexers import CSharpLexer, JsonLexer
|
| 16 |
+
|
| 17 |
+
# Maximum iterations for tool calls to prevent infinite loops
|
| 18 |
+
MAX_TOOL_ITERATIONS = 10
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
# MCP Client for HTTP transport
|
| 22 |
+
class McpHttpClient:
|
| 23 |
+
"""Simple MCP HTTP client for interacting with the server."""
|
| 24 |
+
|
| 25 |
+
def __init__(self, base_url: str) -> None:
|
| 26 |
+
"""Initialize the MCP HTTP client.
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
base_url: Base URL of the MCP server.
|
| 30 |
+
"""
|
| 31 |
+
self.base_url = base_url.rstrip("/")
|
| 32 |
+
self.mcp_url = f"{self.base_url}/mcp"
|
| 33 |
+
self.client = httpx.Client(timeout=30.0)
|
| 34 |
+
|
| 35 |
+
def close(self) -> None:
|
| 36 |
+
"""Close the HTTP client and release resources."""
|
| 37 |
+
self.client.close()
|
| 38 |
+
|
| 39 |
+
def __enter__(self) -> McpHttpClient:
|
| 40 |
+
"""Enter context manager."""
|
| 41 |
+
return self
|
| 42 |
+
|
| 43 |
+
def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None:
|
| 44 |
+
"""Exit context manager."""
|
| 45 |
+
self.close()
|
| 46 |
+
|
| 47 |
+
def _send_request(self, method: str, params: dict | None = None) -> dict[str, Any]:
|
| 48 |
+
"""Send a JSON-RPC request to the MCP server."""
|
| 49 |
+
request_data = {"jsonrpc": "2.0", "id": 1, "method": method, "params": params or {}}
|
| 50 |
+
|
| 51 |
+
try:
|
| 52 |
+
response = self.client.post(self.mcp_url, json=request_data)
|
| 53 |
+
response.raise_for_status()
|
| 54 |
+
|
| 55 |
+
# MCP HTTP transport uses Server-Sent Events (SSE) format
|
| 56 |
+
# Response format: "event: message\ndata: {json}\n\n"
|
| 57 |
+
response_text = response.text
|
| 58 |
+
|
| 59 |
+
# Parse SSE format
|
| 60 |
+
if response_text.startswith("event:"):
|
| 61 |
+
lines = response_text.strip().split("\n")
|
| 62 |
+
for line in lines:
|
| 63 |
+
if line.startswith("data: "):
|
| 64 |
+
json_data = line[6:] # Remove "data: " prefix
|
| 65 |
+
result = json.loads(json_data)
|
| 66 |
+
if "error" in result:
|
| 67 |
+
return {"error": result["error"]}
|
| 68 |
+
return result.get("result", {}) # type: ignore[no-any-return]
|
| 69 |
+
else:
|
| 70 |
+
# Fallback to regular JSON
|
| 71 |
+
result = response.json()
|
| 72 |
+
if "error" in result:
|
| 73 |
+
return {"error": result["error"]}
|
| 74 |
+
return result.get("result", {}) # type: ignore[no-any-return]
|
| 75 |
+
|
| 76 |
+
except (httpx.HTTPError, json.JSONDecodeError) as e:
|
| 77 |
+
return {"error": str(e)}
|
| 78 |
+
except Exception as e:
|
| 79 |
+
# Re-raise system-exiting exceptions
|
| 80 |
+
if isinstance(e, (KeyboardInterrupt, SystemExit)):
|
| 81 |
+
raise
|
| 82 |
+
return {"error": str(e)}
|
| 83 |
+
return {"error": "Unknown error"}
|
| 84 |
+
|
| 85 |
+
def list_tools(self) -> list[dict[str, Any]]:
|
| 86 |
+
"""List all available MCP tools."""
|
| 87 |
+
result = self._send_request("tools/list")
|
| 88 |
+
if "error" in result:
|
| 89 |
+
return []
|
| 90 |
+
tools: list[dict[str, Any]] = result.get("tools", [])
|
| 91 |
+
return tools
|
| 92 |
+
|
| 93 |
+
def list_resources(self) -> list[dict[str, Any]]:
|
| 94 |
+
"""List all available MCP resource templates."""
|
| 95 |
+
result = self._send_request("resources/templates/list")
|
| 96 |
+
if "error" in result:
|
| 97 |
+
return []
|
| 98 |
+
templates: list[dict[str, Any]] = result.get("resourceTemplates", [])
|
| 99 |
+
return templates
|
| 100 |
+
|
| 101 |
+
def list_prompts(self) -> list[dict[str, Any]]:
|
| 102 |
+
"""List all available MCP prompts."""
|
| 103 |
+
result = self._send_request("prompts/list")
|
| 104 |
+
if "error" in result:
|
| 105 |
+
return []
|
| 106 |
+
prompts: list[dict[str, Any]] = result.get("prompts", [])
|
| 107 |
+
return prompts
|
| 108 |
+
|
| 109 |
+
def call_tool(self, name: str, arguments: dict[str, Any]) -> dict[str, Any]:
|
| 110 |
+
"""Call an MCP tool with given arguments."""
|
| 111 |
+
return self._send_request("tools/call", {"name": name, "arguments": arguments})
|
| 112 |
+
|
| 113 |
+
def read_resource(self, uri: str) -> dict[str, Any]:
|
| 114 |
+
"""Read an MCP resource."""
|
| 115 |
+
return self._send_request("resources/read", {"uri": uri})
|
| 116 |
+
|
| 117 |
+
def get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> dict[str, Any]:
|
| 118 |
+
"""Get an MCP prompt."""
|
| 119 |
+
return self._send_request("prompts/get", {"name": name, "arguments": arguments or {}})
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def format_csharp_code(code: str) -> str:
|
| 123 |
+
"""Format C# code with syntax highlighting."""
|
| 124 |
+
try:
|
| 125 |
+
formatter = HtmlFormatter(style="monokai", noclasses=True, cssclass="highlight")
|
| 126 |
+
highlighted = highlight(code, CSharpLexer(), formatter)
|
| 127 |
+
return f'<div style="background: #272822; padding: 10px; border-radius: 8px; overflow-x: auto;">{highlighted}</div>'
|
| 128 |
+
except Exception:
|
| 129 |
+
return f'<pre style="background: #272822; color: #f8f8f2; padding: 10px; border-radius: 8px; overflow-x: auto;"><code>{code}</code></pre>'
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def format_json_output(data: object) -> str:
|
| 133 |
+
"""Format JSON output with syntax highlighting."""
|
| 134 |
+
try:
|
| 135 |
+
json_str = json.dumps(data, indent=2)
|
| 136 |
+
formatter = HtmlFormatter(style="monokai", noclasses=True, cssclass="highlight")
|
| 137 |
+
highlighted = highlight(json_str, JsonLexer(), formatter)
|
| 138 |
+
return f'<div style="background: #272822; padding: 10px; border-radius: 8px; overflow-x: auto;">{highlighted}</div>'
|
| 139 |
+
except Exception:
|
| 140 |
+
return f'<pre style="background: #272822; color: #f8f8f2; padding: 10px; border-radius: 8px; overflow-x: auto;"><code>{json.dumps(data, indent=2)}</code></pre>'
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def call_openai_chat(
|
| 144 |
+
messages: list[dict], api_key: str, model: str, tools: list[dict], mcp_client: McpHttpClient
|
| 145 |
+
) -> str:
|
| 146 |
+
"""Call OpenAI API with MCP tools."""
|
| 147 |
+
try:
|
| 148 |
+
import openai
|
| 149 |
+
|
| 150 |
+
client = openai.OpenAI(api_key=api_key)
|
| 151 |
+
|
| 152 |
+
for _ in range(MAX_TOOL_ITERATIONS):
|
| 153 |
+
response = client.chat.completions.create(
|
| 154 |
+
model=model,
|
| 155 |
+
messages=messages, # type: ignore[arg-type]
|
| 156 |
+
tools=tools if tools else None, # type: ignore[arg-type]
|
| 157 |
+
)
|
| 158 |
+
|
| 159 |
+
message = response.choices[0].message
|
| 160 |
+
|
| 161 |
+
# Handle tool calls
|
| 162 |
+
if message.tool_calls:
|
| 163 |
+
for tool_call in message.tool_calls:
|
| 164 |
+
tool_name = tool_call.function.name # type: ignore[union-attr]
|
| 165 |
+
tool_args = json.loads(tool_call.function.arguments) # type: ignore[union-attr]
|
| 166 |
+
|
| 167 |
+
# Call MCP tool
|
| 168 |
+
result = mcp_client.call_tool(tool_name, tool_args)
|
| 169 |
+
|
| 170 |
+
# Add tool result to messages
|
| 171 |
+
messages.append(
|
| 172 |
+
{
|
| 173 |
+
"role": "assistant",
|
| 174 |
+
"content": None,
|
| 175 |
+
"tool_calls": [
|
| 176 |
+
{
|
| 177 |
+
"id": tool_call.id,
|
| 178 |
+
"function": {
|
| 179 |
+
"name": tool_name,
|
| 180 |
+
"arguments": tool_call.function.arguments, # type: ignore[union-attr]
|
| 181 |
+
},
|
| 182 |
+
"type": "function",
|
| 183 |
+
}
|
| 184 |
+
],
|
| 185 |
+
}
|
| 186 |
+
)
|
| 187 |
+
messages.append(
|
| 188 |
+
{
|
| 189 |
+
"role": "tool",
|
| 190 |
+
"tool_call_id": tool_call.id,
|
| 191 |
+
"content": json.dumps(result),
|
| 192 |
+
}
|
| 193 |
+
)
|
| 194 |
+
# Continue loop to make another call with tool results
|
| 195 |
+
continue
|
| 196 |
+
|
| 197 |
+
return message.content or "No response"
|
| 198 |
+
|
| 199 |
+
return "Error: Maximum tool call iterations exceeded"
|
| 200 |
+
except Exception as e:
|
| 201 |
+
return f"Error: {e!s}"
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def call_anthropic_chat(
|
| 205 |
+
messages: list[dict], api_key: str, model: str, tools: list[dict], mcp_client: McpHttpClient
|
| 206 |
+
) -> str:
|
| 207 |
+
"""Call Anthropic API with MCP tools."""
|
| 208 |
+
try:
|
| 209 |
+
import anthropic
|
| 210 |
+
|
| 211 |
+
client = anthropic.Anthropic(api_key=api_key)
|
| 212 |
+
|
| 213 |
+
# Convert messages format
|
| 214 |
+
anthropic_messages = []
|
| 215 |
+
for msg in messages:
|
| 216 |
+
if msg["role"] == "system":
|
| 217 |
+
continue
|
| 218 |
+
anthropic_messages.append({"role": msg["role"], "content": msg["content"]})
|
| 219 |
+
|
| 220 |
+
for _ in range(MAX_TOOL_ITERATIONS):
|
| 221 |
+
response = client.messages.create(
|
| 222 |
+
model=model,
|
| 223 |
+
max_tokens=4096,
|
| 224 |
+
messages=anthropic_messages, # type: ignore[arg-type]
|
| 225 |
+
tools=tools if tools else None, # type: ignore[arg-type]
|
| 226 |
+
)
|
| 227 |
+
|
| 228 |
+
# Handle tool calls
|
| 229 |
+
if response.stop_reason == "tool_use":
|
| 230 |
+
for content in response.content:
|
| 231 |
+
if content.type == "tool_use":
|
| 232 |
+
tool_name = content.name
|
| 233 |
+
tool_args = content.input
|
| 234 |
+
|
| 235 |
+
# Call MCP tool
|
| 236 |
+
result = mcp_client.call_tool(tool_name, tool_args)
|
| 237 |
+
|
| 238 |
+
# Add tool result and continue
|
| 239 |
+
anthropic_messages.append(
|
| 240 |
+
{"role": "assistant", "content": response.content}
|
| 241 |
+
)
|
| 242 |
+
anthropic_messages.append(
|
| 243 |
+
{
|
| 244 |
+
"role": "user",
|
| 245 |
+
"content": [
|
| 246 |
+
{
|
| 247 |
+
"type": "tool_result",
|
| 248 |
+
"tool_use_id": content.id,
|
| 249 |
+
"content": json.dumps(result),
|
| 250 |
+
}
|
| 251 |
+
],
|
| 252 |
+
}
|
| 253 |
+
)
|
| 254 |
+
# Continue loop to make another call with tool results
|
| 255 |
+
continue
|
| 256 |
+
|
| 257 |
+
return response.content[0].text if response.content else "No response" # type: ignore[union-attr]
|
| 258 |
+
|
| 259 |
+
return "Error: Maximum tool call iterations exceeded"
|
| 260 |
+
except Exception as e:
|
| 261 |
+
return f"Error: {e!s}"
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
def call_gemini_chat(
|
| 265 |
+
messages: list[dict], api_key: str, model: str, tools: list[dict], mcp_client: McpHttpClient
|
| 266 |
+
) -> str:
|
| 267 |
+
"""Call Google Gemini API with MCP tools."""
|
| 268 |
+
try:
|
| 269 |
+
import google.generativeai as genai
|
| 270 |
+
|
| 271 |
+
genai.configure(api_key=api_key)
|
| 272 |
+
|
| 273 |
+
model_instance = genai.GenerativeModel(model)
|
| 274 |
+
|
| 275 |
+
# Convert messages to Gemini format
|
| 276 |
+
prompt = "\n\n".join(
|
| 277 |
+
[f"{msg['role']}: {msg['content']}" for msg in messages if msg.get("content")]
|
| 278 |
+
)
|
| 279 |
+
|
| 280 |
+
response = model_instance.generate_content(prompt)
|
| 281 |
+
return response.text # type: ignore[no-any-return]
|
| 282 |
+
except Exception as e:
|
| 283 |
+
return f"Error: {e!s}"
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
def call_huggingface_chat(
|
| 287 |
+
messages: list[dict], api_key: str, model: str, mcp_client: McpHttpClient
|
| 288 |
+
) -> str:
|
| 289 |
+
"""Call HuggingFace Inference API (supports serverless)."""
|
| 290 |
+
try:
|
| 291 |
+
from huggingface_hub import InferenceClient
|
| 292 |
+
|
| 293 |
+
# Use provided key, or fall back to HF_API_KEY/HF_TOKEN from environment (HF Spaces secrets)
|
| 294 |
+
token: str | None = api_key
|
| 295 |
+
if not token:
|
| 296 |
+
token = os.environ.get("HF_API_KEY") or os.environ.get("HF_TOKEN")
|
| 297 |
+
|
| 298 |
+
# Create client (serverless works without token, but rate limited)
|
| 299 |
+
client = InferenceClient(token=token) if token else InferenceClient()
|
| 300 |
+
|
| 301 |
+
# Convert messages to chat format
|
| 302 |
+
chat_messages = []
|
| 303 |
+
for msg in messages:
|
| 304 |
+
if msg.get("content"):
|
| 305 |
+
chat_messages.append({"role": msg["role"], "content": msg["content"]})
|
| 306 |
+
|
| 307 |
+
# Call chat completion
|
| 308 |
+
response: str = ""
|
| 309 |
+
for message in client.chat_completion(
|
| 310 |
+
messages=chat_messages,
|
| 311 |
+
model=model if model else "meta-llama/Llama-3.2-3B-Instruct",
|
| 312 |
+
max_tokens=2048,
|
| 313 |
+
stream=True,
|
| 314 |
+
):
|
| 315 |
+
if message.choices and message.choices[0].delta.content:
|
| 316 |
+
response += message.choices[0].delta.content
|
| 317 |
+
|
| 318 |
+
return response if response else "No response"
|
| 319 |
+
except Exception as e:
|
| 320 |
+
return f"Error: {e!s}"
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
def get_mcp_endpoint_url() -> str:
|
| 324 |
+
"""Get the public MCP endpoint URL for clients to connect to.
|
| 325 |
+
On HuggingFace Spaces, this returns the embed URL.
|
| 326 |
+
Otherwise, returns the configured base URL.
|
| 327 |
+
"""
|
| 328 |
+
# Check for HuggingFace Space
|
| 329 |
+
space_id = os.environ.get("SPACE_ID")
|
| 330 |
+
if space_id:
|
| 331 |
+
# Format: username/repo -> username-repo.hf.space
|
| 332 |
+
space_subdomain = space_id.replace("/", "-").lower()
|
| 333 |
+
return f"https://{space_subdomain}.hf.space/mcp"
|
| 334 |
+
|
| 335 |
+
# Check for custom BASE_URL
|
| 336 |
+
base_url = os.environ.get("BASE_URL")
|
| 337 |
+
if base_url:
|
| 338 |
+
return f"{base_url.rstrip('/')}/mcp"
|
| 339 |
+
|
| 340 |
+
# Check for ASPNETCORE_URLS
|
| 341 |
+
aspnetcore_urls = os.environ.get("ASPNETCORE_URLS")
|
| 342 |
+
if aspnetcore_urls:
|
| 343 |
+
first_url = aspnetcore_urls.split(";")[0]
|
| 344 |
+
# Replace wildcards with localhost for display
|
| 345 |
+
first_url = first_url.replace("http://+:", "http://localhost:")
|
| 346 |
+
first_url = first_url.replace("http://*:", "http://localhost:")
|
| 347 |
+
first_url = first_url.replace("http://0.0.0.0:", "http://localhost:")
|
| 348 |
+
return f"{first_url.rstrip('/')}/mcp"
|
| 349 |
+
|
| 350 |
+
return "http://localhost:7071/mcp"
|
| 351 |
+
|
| 352 |
+
|
| 353 |
+
def create_landing_page(base_url: str | None = None) -> gr.Blocks:
|
| 354 |
+
"""Create the interactive Gradio UI for MCP server testing.
|
| 355 |
+
|
| 356 |
+
Args:
|
| 357 |
+
base_url: The base URL of the MCP server (e.g., http://localhost:7071)
|
| 358 |
+
|
| 359 |
+
Returns:
|
| 360 |
+
A Gradio Blocks interface
|
| 361 |
+
"""
|
| 362 |
+
import atexit
|
| 363 |
+
|
| 364 |
+
if base_url is None:
|
| 365 |
+
base_url = "http://localhost:7071"
|
| 366 |
+
|
| 367 |
+
# Initialize MCP client
|
| 368 |
+
mcp_client = McpHttpClient(base_url)
|
| 369 |
+
|
| 370 |
+
# Get the public MCP endpoint URL for display
|
| 371 |
+
mcp_endpoint = get_mcp_endpoint_url()
|
| 372 |
+
|
| 373 |
+
# Register cleanup to close the HTTP client on exit
|
| 374 |
+
def cleanup() -> None:
|
| 375 |
+
mcp_client.close()
|
| 376 |
+
|
| 377 |
+
atexit.register(cleanup)
|
| 378 |
+
|
| 379 |
+
# CSS for better styling with syntax highlighting support
|
| 380 |
+
pygments_css = HtmlFormatter(style="monokai").get_style_defs(".highlight")
|
| 381 |
+
|
| 382 |
+
custom_css = f"""
|
| 383 |
+
/* Pygments syntax highlighting */
|
| 384 |
+
{pygments_css}
|
| 385 |
+
|
| 386 |
+
/* Main container styling */
|
| 387 |
+
.gradio-container {{
|
| 388 |
+
max-width: 1400px !important;
|
| 389 |
+
margin: auto;
|
| 390 |
+
}}
|
| 391 |
+
|
| 392 |
+
/* Header styling */
|
| 393 |
+
h1 {{
|
| 394 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 395 |
+
-webkit-background-clip: text;
|
| 396 |
+
-webkit-text-fill-color: transparent;
|
| 397 |
+
background-clip: text;
|
| 398 |
+
font-weight: 800 !important;
|
| 399 |
+
font-size: 2.5rem !important;
|
| 400 |
+
}}
|
| 401 |
+
|
| 402 |
+
/* Tab styling */
|
| 403 |
+
.tab-nav button {{
|
| 404 |
+
font-size: 16px !important;
|
| 405 |
+
padding: 12px 24px !important;
|
| 406 |
+
border-radius: 8px 8px 0 0 !important;
|
| 407 |
+
transition: all 0.3s ease;
|
| 408 |
+
}}
|
| 409 |
+
|
| 410 |
+
.tab-nav button[aria-selected="true"] {{
|
| 411 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
| 412 |
+
color: white !important;
|
| 413 |
+
font-weight: 600 !important;
|
| 414 |
+
box-shadow: 0 4px 6px rgba(102, 126, 234, 0.3);
|
| 415 |
+
}}
|
| 416 |
+
|
| 417 |
+
/* Card styling */
|
| 418 |
+
.tool-card, .resource-card, .prompt-card {{
|
| 419 |
+
border: 2px solid #e0e0e0;
|
| 420 |
+
border-radius: 12px;
|
| 421 |
+
padding: 20px;
|
| 422 |
+
margin: 15px 0;
|
| 423 |
+
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
| 424 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
| 425 |
+
transition: transform 0.2s, box-shadow 0.2s;
|
| 426 |
+
}}
|
| 427 |
+
|
| 428 |
+
.tool-card:hover, .resource-card:hover, .prompt-card:hover {{
|
| 429 |
+
transform: translateY(-2px);
|
| 430 |
+
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
|
| 431 |
+
}}
|
| 432 |
+
|
| 433 |
+
/* Button styling */
|
| 434 |
+
button {{
|
| 435 |
+
transition: all 0.3s ease !important;
|
| 436 |
+
}}
|
| 437 |
+
|
| 438 |
+
button:hover {{
|
| 439 |
+
transform: translateY(-1px) !important;
|
| 440 |
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2) !important;
|
| 441 |
+
}}
|
| 442 |
+
|
| 443 |
+
/* Input styling */
|
| 444 |
+
.param-input {{
|
| 445 |
+
margin: 8px 0;
|
| 446 |
+
border-radius: 6px;
|
| 447 |
+
}}
|
| 448 |
+
|
| 449 |
+
/* Code editor styling */
|
| 450 |
+
.code-editor {{
|
| 451 |
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace !important;
|
| 452 |
+
font-size: 14px !important;
|
| 453 |
+
line-height: 1.5 !important;
|
| 454 |
+
}}
|
| 455 |
+
|
| 456 |
+
/* Result box styling */
|
| 457 |
+
.result-box {{
|
| 458 |
+
background-color: #1e1e1e;
|
| 459 |
+
border-radius: 8px;
|
| 460 |
+
padding: 10px;
|
| 461 |
+
font-family: monospace;
|
| 462 |
+
white-space: pre-wrap;
|
| 463 |
+
max-height: 600px;
|
| 464 |
+
overflow-y: auto;
|
| 465 |
+
}}
|
| 466 |
+
|
| 467 |
+
/* Status indicator */
|
| 468 |
+
.status-indicator {{
|
| 469 |
+
display: inline-block;
|
| 470 |
+
width: 10px;
|
| 471 |
+
height: 10px;
|
| 472 |
+
border-radius: 50%;
|
| 473 |
+
background-color: #00ff00;
|
| 474 |
+
box-shadow: 0 0 10px #00ff00;
|
| 475 |
+
animation: pulse 2s infinite;
|
| 476 |
+
}}
|
| 477 |
+
|
| 478 |
+
@keyframes pulse {{
|
| 479 |
+
0%, 100% {{
|
| 480 |
+
opacity: 1;
|
| 481 |
+
}}
|
| 482 |
+
50% {{
|
| 483 |
+
opacity: 0.5;
|
| 484 |
+
}}
|
| 485 |
+
}}
|
| 486 |
+
|
| 487 |
+
/* Highlight important text */
|
| 488 |
+
.highlight-text {{
|
| 489 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 490 |
+
-webkit-background-clip: text;
|
| 491 |
+
-webkit-text-fill-color: transparent;
|
| 492 |
+
background-clip: text;
|
| 493 |
+
font-weight: 600;
|
| 494 |
+
}}
|
| 495 |
+
"""
|
| 496 |
+
|
| 497 |
+
# In Gradio 6.0+, Blocks() constructor simplified - theme and css moved to launch()
|
| 498 |
+
with gr.Blocks(title="Roslyn-Stone MCP Testing UI") as demo:
|
| 499 |
+
# Inject custom CSS via Markdown/HTML component instead
|
| 500 |
+
gr.HTML(f"<style>{custom_css}</style>")
|
| 501 |
+
|
| 502 |
+
# State management for storing tools, resources, and prompts data
|
| 503 |
+
tools_state = gr.State({})
|
| 504 |
+
resources_state = gr.State({})
|
| 505 |
+
prompts_state = gr.State({})
|
| 506 |
+
|
| 507 |
+
gr.Markdown(
|
| 508 |
+
"""
|
| 509 |
+
# 🪨 Roslyn-Stone MCP Server - Interactive Testing UI
|
| 510 |
+
|
| 511 |
+
Welcome to the **interactive testing interface** for Roslyn-Stone MCP Server.
|
| 512 |
+
This UI dynamically loads all available tools, resources, and prompts from the MCP server.
|
| 513 |
+
"""
|
| 514 |
+
)
|
| 515 |
+
|
| 516 |
+
with gr.Tabs():
|
| 517 |
+
# Setup Tab (Welcome page with connection instructions)
|
| 518 |
+
with gr.Tab("🚀 Setup"):
|
| 519 |
+
gr.Markdown(
|
| 520 |
+
f"""
|
| 521 |
+
## Welcome to Roslyn-Stone MCP Server!
|
| 522 |
+
|
| 523 |
+
This is an interactive C# sandbox that provides AI tools through the Model Context Protocol (MCP).
|
| 524 |
+
|
| 525 |
+
### 🔗 MCP Server Endpoint
|
| 526 |
+
|
| 527 |
+
Connect your MCP client to this server using the following endpoint:
|
| 528 |
+
|
| 529 |
+
```
|
| 530 |
+
{mcp_endpoint}
|
| 531 |
+
```
|
| 532 |
+
|
| 533 |
+
---
|
| 534 |
+
|
| 535 |
+
### 📋 Quick Setup Instructions
|
| 536 |
+
|
| 537 |
+
#### Claude Desktop
|
| 538 |
+
|
| 539 |
+
Add to your `claude_desktop_config.json`:
|
| 540 |
+
|
| 541 |
+
```json
|
| 542 |
+
{{
|
| 543 |
+
"mcpServers": {{
|
| 544 |
+
"roslyn-stone": {{
|
| 545 |
+
"command": "npx",
|
| 546 |
+
"args": [
|
| 547 |
+
"mcp-remote",
|
| 548 |
+
"{mcp_endpoint}"
|
| 549 |
+
]
|
| 550 |
+
}}
|
| 551 |
+
}}
|
| 552 |
+
}}
|
| 553 |
+
```
|
| 554 |
+
|
| 555 |
+
#### VS Code with Copilot
|
| 556 |
+
|
| 557 |
+
Add to your VS Code `settings.json`:
|
| 558 |
+
|
| 559 |
+
```json
|
| 560 |
+
{{
|
| 561 |
+
"github.copilot.chat.mcpServers": {{
|
| 562 |
+
"roslyn-stone": {{
|
| 563 |
+
"type": "http",
|
| 564 |
+
"url": "{mcp_endpoint}"
|
| 565 |
+
}}
|
| 566 |
+
}}
|
| 567 |
+
}}
|
| 568 |
+
```
|
| 569 |
+
|
| 570 |
+
#### Using mcp-remote (Any MCP Client)
|
| 571 |
+
|
| 572 |
+
If your client doesn't support HTTP transport directly, use `mcp-remote` as a bridge:
|
| 573 |
+
|
| 574 |
+
```bash
|
| 575 |
+
npx mcp-remote {mcp_endpoint}
|
| 576 |
+
```
|
| 577 |
+
|
| 578 |
+
---
|
| 579 |
+
|
| 580 |
+
### 🛠️ Available Capabilities
|
| 581 |
+
|
| 582 |
+
| Category | Description |
|
| 583 |
+
|----------|-------------|
|
| 584 |
+
| **🔧 Tools** | Execute C# code, validate syntax, search NuGet packages, load assemblies |
|
| 585 |
+
| **📚 Resources** | Access .NET documentation, NuGet package info, REPL state |
|
| 586 |
+
| **💬 Prompts** | Get guidance and best practices for C# development |
|
| 587 |
+
| **🤖 Chat** | Interactive chat with AI using MCP tools (try the Chat tab!) |
|
| 588 |
+
|
| 589 |
+
---
|
| 590 |
+
|
| 591 |
+
### 🔒 Security Note
|
| 592 |
+
|
| 593 |
+
⚠️ This server can execute arbitrary C# code. When self-hosting:
|
| 594 |
+
- Run in isolated containers or sandboxes
|
| 595 |
+
- Implement authentication and rate limiting
|
| 596 |
+
- Restrict network access as needed
|
| 597 |
+
- Monitor resource usage
|
| 598 |
+
|
| 599 |
+
---
|
| 600 |
+
|
| 601 |
+
**Explore the tabs above to test tools, browse resources, view prompts, or chat with AI!**
|
| 602 |
+
"""
|
| 603 |
+
)
|
| 604 |
+
|
| 605 |
+
# Tools Tab
|
| 606 |
+
with gr.Tab("🔧 Tools"):
|
| 607 |
+
gr.Markdown("### Execute MCP Tools")
|
| 608 |
+
gr.Markdown(
|
| 609 |
+
"Tools perform operations like executing C# code, loading NuGet packages, etc."
|
| 610 |
+
)
|
| 611 |
+
|
| 612 |
+
refresh_tools_btn = gr.Button("🔄 Refresh Tools", size="sm")
|
| 613 |
+
tools_status = gr.Markdown("Click 'Refresh Tools' to load available tools...")
|
| 614 |
+
|
| 615 |
+
with gr.Row():
|
| 616 |
+
with gr.Column(scale=1):
|
| 617 |
+
tool_dropdown = gr.Dropdown(
|
| 618 |
+
label="Select Tool", choices=[], interactive=True
|
| 619 |
+
)
|
| 620 |
+
tool_description = gr.Textbox(
|
| 621 |
+
label="Tool Description",
|
| 622 |
+
lines=5,
|
| 623 |
+
interactive=False,
|
| 624 |
+
max_lines=10,
|
| 625 |
+
)
|
| 626 |
+
tool_params_json = gr.Code(
|
| 627 |
+
label="Tool Parameters (JSON)", language="json", value="{}", lines=10
|
| 628 |
+
)
|
| 629 |
+
execute_tool_btn = gr.Button("▶️ Execute Tool", variant="primary", size="lg")
|
| 630 |
+
|
| 631 |
+
with gr.Column(scale=1):
|
| 632 |
+
tool_result = gr.Code(
|
| 633 |
+
label="Tool Result (JSON)", language="json", lines=20, interactive=False
|
| 634 |
+
)
|
| 635 |
+
|
| 636 |
+
# Tool examples with better formatting
|
| 637 |
+
with gr.Accordion("📝 Example Tool Calls", open=True):
|
| 638 |
+
gr.Markdown("### C# Code Execution Examples")
|
| 639 |
+
|
| 640 |
+
gr.HTML("""
|
| 641 |
+
<div style="background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); padding: 15px; border-radius: 8px; margin: 10px 0;">
|
| 642 |
+
<h4>🔹 EvaluateCsharp - Execute simple C# code</h4>
|
| 643 |
+
<pre style="background: #272822; color: #f8f8f2; padding: 10px; border-radius: 4px; overflow-x: auto;"><code>{
|
| 644 |
+
"code": "var x = 10; x * 2",
|
| 645 |
+
"createContext": false
|
| 646 |
+
}</code></pre>
|
| 647 |
+
</div>
|
| 648 |
+
|
| 649 |
+
<div style="background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); padding: 15px; border-radius: 8px; margin: 10px 0;">
|
| 650 |
+
<h4>🔹 ValidateCsharp - Check syntax</h4>
|
| 651 |
+
<pre style="background: #272822; color: #f8f8f2; padding: 10px; border-radius: 4px; overflow-x: auto;"><code>{
|
| 652 |
+
"code": "var x = 10; x * 2"
|
| 653 |
+
}</code></pre>
|
| 654 |
+
</div>
|
| 655 |
+
|
| 656 |
+
<div style="background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); padding: 15px; border-radius: 8px; margin: 10px 0;">
|
| 657 |
+
<h4>🔹 SearchNuGetPackages - Search packages</h4>
|
| 658 |
+
<pre style="background: #272822; color: #f8f8f2; padding: 10px; border-radius: 4px; overflow-x: auto;"><code>{
|
| 659 |
+
"query": "json",
|
| 660 |
+
"skip": 0,
|
| 661 |
+
"take": 10
|
| 662 |
+
}</code></pre>
|
| 663 |
+
</div>
|
| 664 |
+
""")
|
| 665 |
+
|
| 666 |
+
def refresh_tools():
|
| 667 |
+
"""Refresh the list of available tools."""
|
| 668 |
+
tools = mcp_client.list_tools()
|
| 669 |
+
if not tools:
|
| 670 |
+
return (
|
| 671 |
+
"⚠️ No tools found or error connecting to server",
|
| 672 |
+
gr.update(choices=[]),
|
| 673 |
+
{},
|
| 674 |
+
)
|
| 675 |
+
|
| 676 |
+
tool_names = [t.get("name", "Unknown") for t in tools]
|
| 677 |
+
tools_data = {t.get("name"): t for t in tools}
|
| 678 |
+
|
| 679 |
+
return (
|
| 680 |
+
f"✅ Loaded {len(tools)} tools",
|
| 681 |
+
gr.update(choices=tool_names),
|
| 682 |
+
tools_data,
|
| 683 |
+
)
|
| 684 |
+
|
| 685 |
+
def on_tool_selected(tool_name, tools_data):
|
| 686 |
+
"""When a tool is selected, show its description and input schema."""
|
| 687 |
+
if not tool_name or not tools_data:
|
| 688 |
+
return "", "{}"
|
| 689 |
+
|
| 690 |
+
tool = tools_data.get(tool_name, {})
|
| 691 |
+
description = tool.get("description", "No description available")
|
| 692 |
+
|
| 693 |
+
# Extract input schema to help user understand parameters
|
| 694 |
+
input_schema = tool.get("inputSchema", {})
|
| 695 |
+
properties = input_schema.get("properties", {})
|
| 696 |
+
required = input_schema.get("required", [])
|
| 697 |
+
|
| 698 |
+
# Create both example and description in one pass
|
| 699 |
+
example: dict[str, Any] = {}
|
| 700 |
+
param_descriptions = []
|
| 701 |
+
|
| 702 |
+
for prop_name, prop_info in properties.items():
|
| 703 |
+
prop_desc = prop_info.get("description", "")
|
| 704 |
+
prop_type = prop_info.get("type", "string")
|
| 705 |
+
is_required = prop_name in required
|
| 706 |
+
|
| 707 |
+
# Add placeholder values based on type
|
| 708 |
+
if prop_type == "string":
|
| 709 |
+
example[prop_name] = f"<{prop_name}>"
|
| 710 |
+
elif prop_type in {"integer", "number"}:
|
| 711 |
+
example[prop_name] = 0
|
| 712 |
+
elif prop_type == "boolean":
|
| 713 |
+
example[prop_name] = False
|
| 714 |
+
elif prop_type == "array":
|
| 715 |
+
example[prop_name] = []
|
| 716 |
+
elif prop_type == "object":
|
| 717 |
+
example[prop_name] = {}
|
| 718 |
+
|
| 719 |
+
# Build description
|
| 720 |
+
req_marker = "⚠️ REQUIRED" if is_required else "optional"
|
| 721 |
+
param_descriptions.append(f"- `{prop_name}` ({req_marker}): {prop_desc}")
|
| 722 |
+
|
| 723 |
+
example_json = json.dumps(example, indent=2)
|
| 724 |
+
full_description = f"{description}\n\n**Parameters:**\n" + "\n".join(
|
| 725 |
+
param_descriptions
|
| 726 |
+
)
|
| 727 |
+
|
| 728 |
+
return full_description, example_json
|
| 729 |
+
|
| 730 |
+
def execute_tool(tool_name, params_json):
|
| 731 |
+
"""Execute the selected tool with given parameters."""
|
| 732 |
+
if not tool_name:
|
| 733 |
+
return json.dumps({"error": "Please select a tool"}, indent=2)
|
| 734 |
+
|
| 735 |
+
try:
|
| 736 |
+
params = json.loads(params_json) if params_json.strip() else {}
|
| 737 |
+
except json.JSONDecodeError as e:
|
| 738 |
+
return json.dumps({"error": f"Invalid JSON: {e!s}"}, indent=2)
|
| 739 |
+
|
| 740 |
+
result = mcp_client.call_tool(tool_name, params)
|
| 741 |
+
|
| 742 |
+
# Format result nicely
|
| 743 |
+
return json.dumps(result, indent=2)
|
| 744 |
+
|
| 745 |
+
# Wire up tool tab events
|
| 746 |
+
refresh_tools_btn.click(
|
| 747 |
+
fn=refresh_tools, outputs=[tools_status, tool_dropdown, tools_state]
|
| 748 |
+
)
|
| 749 |
+
|
| 750 |
+
tool_dropdown.change(
|
| 751 |
+
fn=on_tool_selected,
|
| 752 |
+
inputs=[tool_dropdown, tools_state],
|
| 753 |
+
outputs=[tool_description, tool_params_json],
|
| 754 |
+
)
|
| 755 |
+
|
| 756 |
+
execute_tool_btn.click(
|
| 757 |
+
fn=execute_tool, inputs=[tool_dropdown, tool_params_json], outputs=[tool_result]
|
| 758 |
+
)
|
| 759 |
+
|
| 760 |
+
# Resources Tab
|
| 761 |
+
with gr.Tab("📚 Resources"):
|
| 762 |
+
gr.Markdown("### Browse MCP Resources")
|
| 763 |
+
gr.Markdown(
|
| 764 |
+
"Resources provide read-only data like documentation and package search results."
|
| 765 |
+
)
|
| 766 |
+
|
| 767 |
+
refresh_resources_btn = gr.Button("🔄 Refresh Resources", size="sm")
|
| 768 |
+
resources_status = gr.Markdown(
|
| 769 |
+
"Click 'Refresh Resources' to load available resources..."
|
| 770 |
+
)
|
| 771 |
+
|
| 772 |
+
with gr.Row():
|
| 773 |
+
with gr.Column(scale=1):
|
| 774 |
+
resource_dropdown = gr.Dropdown(
|
| 775 |
+
label="Select Resource Template", choices=[], interactive=True
|
| 776 |
+
)
|
| 777 |
+
resource_description = gr.Textbox(
|
| 778 |
+
label="Resource Description",
|
| 779 |
+
lines=5,
|
| 780 |
+
interactive=False,
|
| 781 |
+
max_lines=10,
|
| 782 |
+
)
|
| 783 |
+
resource_uri = gr.Textbox(
|
| 784 |
+
label="Resource URI",
|
| 785 |
+
placeholder="e.g., doc://System.String or nuget://search?q=json",
|
| 786 |
+
lines=1,
|
| 787 |
+
)
|
| 788 |
+
read_resource_btn = gr.Button(
|
| 789 |
+
"📖 Read Resource", variant="primary", size="lg"
|
| 790 |
+
)
|
| 791 |
+
|
| 792 |
+
with gr.Column(scale=1):
|
| 793 |
+
resource_result = gr.Code(
|
| 794 |
+
label="Resource Content", language="json", lines=20, interactive=False
|
| 795 |
+
)
|
| 796 |
+
|
| 797 |
+
# Resource examples with enhanced UI
|
| 798 |
+
with gr.Accordion("📚 Example Resource URIs", open=True):
|
| 799 |
+
gr.HTML("""
|
| 800 |
+
<div style="background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); padding: 15px; border-radius: 8px; margin: 10px 0;">
|
| 801 |
+
<h4>📖 Documentation Resources</h4>
|
| 802 |
+
<ul style="list-style-type: none; padding-left: 0;">
|
| 803 |
+
<li style="margin: 8px 0; padding: 8px; background: white; border-radius: 4px;">
|
| 804 |
+
<code style="color: #667eea; font-weight: bold;">doc://System.String</code> - String class documentation
|
| 805 |
+
</li>
|
| 806 |
+
<li style="margin: 8px 0; padding: 8px; background: white; border-radius: 4px;">
|
| 807 |
+
<code style="color: #667eea; font-weight: bold;">doc://System.Linq.Enumerable</code> - LINQ methods
|
| 808 |
+
</li>
|
| 809 |
+
<li style="margin: 8px 0; padding: 8px; background: white; border-radius: 4px;">
|
| 810 |
+
<code style="color: #667eea; font-weight: bold;">doc://System.Collections.Generic.List`1</code> - List<T> docs
|
| 811 |
+
</li>
|
| 812 |
+
</ul>
|
| 813 |
+
</div>
|
| 814 |
+
|
| 815 |
+
<div style="background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); padding: 15px; border-radius: 8px; margin: 10px 0;">
|
| 816 |
+
<h4>📦 NuGet Search Resources</h4>
|
| 817 |
+
<ul style="list-style-type: none; padding-left: 0;">
|
| 818 |
+
<li style="margin: 8px 0; padding: 8px; background: white; border-radius: 4px;">
|
| 819 |
+
<code style="color: #764ba2; font-weight: bold;">nuget://search?q=json</code> - Search JSON packages
|
| 820 |
+
</li>
|
| 821 |
+
<li style="margin: 8px 0; padding: 8px; background: white; border-radius: 4px;">
|
| 822 |
+
<code style="color: #764ba2; font-weight: bold;">nuget://search?q=http&take=5</code> - Search HTTP packages (5 results)
|
| 823 |
+
</li>
|
| 824 |
+
</ul>
|
| 825 |
+
</div>
|
| 826 |
+
|
| 827 |
+
<div style="background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); padding: 15px; border-radius: 8px; margin: 10px 0;">
|
| 828 |
+
<h4>📦 Package Info Resources</h4>
|
| 829 |
+
<ul style="list-style-type: none; padding-left: 0;">
|
| 830 |
+
<li style="margin: 8px 0; padding: 8px; background: white; border-radius: 4px;">
|
| 831 |
+
<code style="color: #764ba2; font-weight: bold;">nuget://packages/Newtonsoft.Json/versions</code> - All versions
|
| 832 |
+
</li>
|
| 833 |
+
<li style="margin: 8px 0; padding: 8px; background: white; border-radius: 4px;">
|
| 834 |
+
<code style="color: #764ba2; font-weight: bold;">nuget://packages/Newtonsoft.Json/readme</code> - Package README
|
| 835 |
+
</li>
|
| 836 |
+
</ul>
|
| 837 |
+
</div>
|
| 838 |
+
|
| 839 |
+
<div style="background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); padding: 15px; border-radius: 8px; margin: 10px 0;">
|
| 840 |
+
<h4>⚙️ REPL State Resources</h4>
|
| 841 |
+
<ul style="list-style-type: none; padding-left: 0;">
|
| 842 |
+
<li style="margin: 8px 0; padding: 8px; background: white; border-radius: 4px;">
|
| 843 |
+
<code style="color: #667eea; font-weight: bold;">repl://state</code> - Current REPL environment info
|
| 844 |
+
</li>
|
| 845 |
+
<li style="margin: 8px 0; padding: 8px; background: white; border-radius: 4px;">
|
| 846 |
+
<code style="color: #667eea; font-weight: bold;">repl://info</code> - REPL capabilities
|
| 847 |
+
</li>
|
| 848 |
+
</ul>
|
| 849 |
+
</div>
|
| 850 |
+
""")
|
| 851 |
+
|
| 852 |
+
def refresh_resources():
|
| 853 |
+
"""Refresh the list of available resources."""
|
| 854 |
+
resources = mcp_client.list_resources()
|
| 855 |
+
if not resources:
|
| 856 |
+
return (
|
| 857 |
+
"⚠️ No resources found or error connecting to server",
|
| 858 |
+
gr.update(choices=[]),
|
| 859 |
+
{},
|
| 860 |
+
)
|
| 861 |
+
|
| 862 |
+
resource_names = [r.get("name", "Unknown") for r in resources]
|
| 863 |
+
resources_data = {r.get("name"): r for r in resources}
|
| 864 |
+
|
| 865 |
+
return (
|
| 866 |
+
f"✅ Loaded {len(resources)} resource templates",
|
| 867 |
+
gr.update(choices=resource_names),
|
| 868 |
+
resources_data,
|
| 869 |
+
)
|
| 870 |
+
|
| 871 |
+
def on_resource_selected(resource_name, resources_data):
|
| 872 |
+
"""When a resource is selected, show its description and example URI."""
|
| 873 |
+
if not resource_name or not resources_data:
|
| 874 |
+
return "", ""
|
| 875 |
+
|
| 876 |
+
resource = resources_data.get(resource_name, {})
|
| 877 |
+
description = resource.get("description", "No description available")
|
| 878 |
+
uri_template = resource.get("uriTemplate", resource.get("uri", ""))
|
| 879 |
+
|
| 880 |
+
return description, uri_template
|
| 881 |
+
|
| 882 |
+
def read_resource(uri):
|
| 883 |
+
"""Read a resource from the MCP server."""
|
| 884 |
+
if not uri:
|
| 885 |
+
return json.dumps({"error": "Please enter a resource URI"}, indent=2)
|
| 886 |
+
|
| 887 |
+
result = mcp_client.read_resource(uri)
|
| 888 |
+
|
| 889 |
+
# Format the result nicely
|
| 890 |
+
if "contents" in result:
|
| 891 |
+
# MCP resource response with contents array
|
| 892 |
+
contents = result["contents"]
|
| 893 |
+
if len(contents) == 1:
|
| 894 |
+
content = contents[0]
|
| 895 |
+
if content.get("mimeType") == "application/json" and "text" in content:
|
| 896 |
+
try:
|
| 897 |
+
parsed = json.loads(content["text"])
|
| 898 |
+
return json.dumps(parsed, indent=2)
|
| 899 |
+
except json.JSONDecodeError:
|
| 900 |
+
return content["text"]
|
| 901 |
+
return content.get("text", json.dumps(content, indent=2))
|
| 902 |
+
return json.dumps(contents, indent=2)
|
| 903 |
+
|
| 904 |
+
return json.dumps(result, indent=2)
|
| 905 |
+
|
| 906 |
+
# Wire up resource tab events
|
| 907 |
+
refresh_resources_btn.click(
|
| 908 |
+
fn=refresh_resources,
|
| 909 |
+
outputs=[resources_status, resource_dropdown, resources_state],
|
| 910 |
+
)
|
| 911 |
+
|
| 912 |
+
resource_dropdown.change(
|
| 913 |
+
fn=on_resource_selected,
|
| 914 |
+
inputs=[resource_dropdown, resources_state],
|
| 915 |
+
outputs=[resource_description, resource_uri],
|
| 916 |
+
)
|
| 917 |
+
|
| 918 |
+
read_resource_btn.click(
|
| 919 |
+
fn=read_resource, inputs=[resource_uri], outputs=[resource_result]
|
| 920 |
+
)
|
| 921 |
+
|
| 922 |
+
# Prompts Tab
|
| 923 |
+
with gr.Tab("💬 Prompts"):
|
| 924 |
+
gr.Markdown("### View MCP Prompts")
|
| 925 |
+
gr.Markdown(
|
| 926 |
+
"Prompts provide guidance and examples for using the MCP server effectively."
|
| 927 |
+
)
|
| 928 |
+
|
| 929 |
+
refresh_prompts_btn = gr.Button("🔄 Refresh Prompts", size="sm")
|
| 930 |
+
prompts_status = gr.Markdown("Click 'Refresh Prompts' to load available prompts...")
|
| 931 |
+
|
| 932 |
+
with gr.Row():
|
| 933 |
+
with gr.Column(scale=1):
|
| 934 |
+
prompt_dropdown = gr.Dropdown(
|
| 935 |
+
label="Select Prompt", choices=[], interactive=True
|
| 936 |
+
)
|
| 937 |
+
prompt_description = gr.Textbox(
|
| 938 |
+
label="Prompt Description",
|
| 939 |
+
lines=3,
|
| 940 |
+
interactive=False,
|
| 941 |
+
max_lines=5,
|
| 942 |
+
)
|
| 943 |
+
get_prompt_btn = gr.Button("📝 Get Prompt", variant="primary", size="lg")
|
| 944 |
+
|
| 945 |
+
with gr.Column(scale=1):
|
| 946 |
+
prompt_result = gr.Textbox(
|
| 947 |
+
label="Prompt Content",
|
| 948 |
+
lines=30,
|
| 949 |
+
interactive=False,
|
| 950 |
+
max_lines=50,
|
| 951 |
+
)
|
| 952 |
+
|
| 953 |
+
def refresh_prompts():
|
| 954 |
+
"""Refresh the list of available prompts."""
|
| 955 |
+
prompts = mcp_client.list_prompts()
|
| 956 |
+
if not prompts:
|
| 957 |
+
return (
|
| 958 |
+
"⚠️ No prompts found or error connecting to server",
|
| 959 |
+
gr.update(choices=[]),
|
| 960 |
+
{},
|
| 961 |
+
)
|
| 962 |
+
|
| 963 |
+
prompt_names = [p.get("name", "Unknown") for p in prompts]
|
| 964 |
+
prompts_data = {p.get("name"): p for p in prompts}
|
| 965 |
+
|
| 966 |
+
return (
|
| 967 |
+
f"✅ Loaded {len(prompts)} prompts",
|
| 968 |
+
gr.update(choices=prompt_names),
|
| 969 |
+
prompts_data,
|
| 970 |
+
)
|
| 971 |
+
|
| 972 |
+
def on_prompt_selected(prompt_name, prompts_data):
|
| 973 |
+
"""When a prompt is selected, show its description."""
|
| 974 |
+
if not prompt_name or not prompts_data:
|
| 975 |
+
return ""
|
| 976 |
+
|
| 977 |
+
prompt = prompts_data.get(prompt_name, {})
|
| 978 |
+
return prompt.get("description", "No description available")
|
| 979 |
+
|
| 980 |
+
def get_prompt(prompt_name):
|
| 981 |
+
"""Get the content of a prompt."""
|
| 982 |
+
if not prompt_name:
|
| 983 |
+
return "Please select a prompt"
|
| 984 |
+
|
| 985 |
+
result = mcp_client.get_prompt(prompt_name)
|
| 986 |
+
|
| 987 |
+
# Extract prompt messages
|
| 988 |
+
if "messages" in result:
|
| 989 |
+
messages = result["messages"]
|
| 990 |
+
content_parts = []
|
| 991 |
+
for msg in messages:
|
| 992 |
+
role = msg.get("role", "unknown")
|
| 993 |
+
content = msg.get("content", {})
|
| 994 |
+
if isinstance(content, dict):
|
| 995 |
+
text = content.get("text", "")
|
| 996 |
+
else:
|
| 997 |
+
text = str(content)
|
| 998 |
+
content_parts.append(f"[{role.upper()}]\n{text}")
|
| 999 |
+
return "\n\n".join(content_parts)
|
| 1000 |
+
|
| 1001 |
+
return json.dumps(result, indent=2)
|
| 1002 |
+
|
| 1003 |
+
# Wire up prompts tab events
|
| 1004 |
+
refresh_prompts_btn.click(
|
| 1005 |
+
fn=refresh_prompts, outputs=[prompts_status, prompt_dropdown, prompts_state]
|
| 1006 |
+
)
|
| 1007 |
+
|
| 1008 |
+
prompt_dropdown.change(
|
| 1009 |
+
fn=on_prompt_selected,
|
| 1010 |
+
inputs=[prompt_dropdown, prompts_state],
|
| 1011 |
+
outputs=[prompt_description],
|
| 1012 |
+
)
|
| 1013 |
+
|
| 1014 |
+
get_prompt_btn.click(
|
| 1015 |
+
fn=get_prompt, inputs=[prompt_dropdown], outputs=[prompt_result]
|
| 1016 |
+
)
|
| 1017 |
+
|
| 1018 |
+
# Chat Tab
|
| 1019 |
+
with gr.Tab("💬 Chat"):
|
| 1020 |
+
gr.Markdown("### Chat with AI using Roslyn-Stone MCP Tools")
|
| 1021 |
+
gr.Markdown("""
|
| 1022 |
+
Connect to various LLM providers and use Roslyn-Stone MCP tools in your conversations.
|
| 1023 |
+
|
| 1024 |
+
**🚀 Free Option:** Use HuggingFace serverless inference (no API key needed, or use HF_API_KEY secret)
|
| 1025 |
+
|
| 1026 |
+
⚠️ **Security Note:** API keys are not stored. HF will use HF_API_KEY/HF_TOKEN secret if available.
|
| 1027 |
+
""")
|
| 1028 |
+
|
| 1029 |
+
with gr.Row():
|
| 1030 |
+
with gr.Column(scale=1):
|
| 1031 |
+
# Provider selection
|
| 1032 |
+
provider = gr.Dropdown(
|
| 1033 |
+
label="LLM Provider",
|
| 1034 |
+
choices=[
|
| 1035 |
+
"HuggingFace (Serverless)",
|
| 1036 |
+
"OpenAI",
|
| 1037 |
+
"Anthropic",
|
| 1038 |
+
"Google Gemini",
|
| 1039 |
+
],
|
| 1040 |
+
value="HuggingFace (Serverless)",
|
| 1041 |
+
interactive=True,
|
| 1042 |
+
)
|
| 1043 |
+
|
| 1044 |
+
# API Key input (session state only, not stored)
|
| 1045 |
+
# For HF, will use HF_API_KEY/HF_TOKEN secret if blank
|
| 1046 |
+
api_key = gr.Textbox(
|
| 1047 |
+
label="API Key (optional for HF)",
|
| 1048 |
+
type="password",
|
| 1049 |
+
placeholder="Enter API key (or leave blank to use HF_API_KEY secret)",
|
| 1050 |
+
lines=1,
|
| 1051 |
+
info="Not stored - HF will use HF_API_KEY secret if blank",
|
| 1052 |
+
)
|
| 1053 |
+
|
| 1054 |
+
# Model selection
|
| 1055 |
+
model = gr.Textbox(
|
| 1056 |
+
label="Model Name",
|
| 1057 |
+
value="meta-llama/Llama-3.2-3B-Instruct",
|
| 1058 |
+
placeholder="e.g., meta-llama/Llama-3.2-3B-Instruct, gpt-4o-mini",
|
| 1059 |
+
lines=1,
|
| 1060 |
+
)
|
| 1061 |
+
|
| 1062 |
+
# Enable MCP tools
|
| 1063 |
+
enable_mcp = gr.Checkbox(
|
| 1064 |
+
label="Enable MCP Tools",
|
| 1065 |
+
value=True,
|
| 1066 |
+
info="Allow the AI to use Roslyn-Stone MCP tools",
|
| 1067 |
+
)
|
| 1068 |
+
|
| 1069 |
+
# Clear button
|
| 1070 |
+
clear_btn = gr.Button("🗑️ Clear Chat", size="sm")
|
| 1071 |
+
|
| 1072 |
+
with gr.Column(scale=2):
|
| 1073 |
+
# Chat interface (Gradio 6.0 removed 'type' parameter)
|
| 1074 |
+
chatbot = gr.Chatbot(label="Chat", height=500)
|
| 1075 |
+
|
| 1076 |
+
# Message input
|
| 1077 |
+
msg = gr.Textbox(
|
| 1078 |
+
label="Message",
|
| 1079 |
+
placeholder="Ask the AI to help with C# code, NuGet packages, or .NET documentation...",
|
| 1080 |
+
lines=2,
|
| 1081 |
+
)
|
| 1082 |
+
|
| 1083 |
+
send_btn = gr.Button("📤 Send", variant="primary", size="lg")
|
| 1084 |
+
|
| 1085 |
+
# Chat examples
|
| 1086 |
+
gr.Markdown("""
|
| 1087 |
+
### Example Prompts
|
| 1088 |
+
|
| 1089 |
+
Try asking the AI to:
|
| 1090 |
+
- "Write a C# program that calculates the Fibonacci sequence"
|
| 1091 |
+
- "Search for JSON parsing NuGet packages"
|
| 1092 |
+
- "Show me documentation for System.Linq.Enumerable.Select"
|
| 1093 |
+
- "Create a C# script that reads a CSV file"
|
| 1094 |
+
- "Validate this C# code: var x = 10; Console.WriteLine(x);"
|
| 1095 |
+
|
| 1096 |
+
The AI can use Roslyn-Stone MCP tools to execute C# code, search packages, and access documentation.
|
| 1097 |
+
""")
|
| 1098 |
+
|
| 1099 |
+
def chat_response(
|
| 1100 |
+
message: str,
|
| 1101 |
+
history: list,
|
| 1102 |
+
provider_name: str,
|
| 1103 |
+
key: str,
|
| 1104 |
+
model_name: str,
|
| 1105 |
+
use_mcp: bool,
|
| 1106 |
+
):
|
| 1107 |
+
"""Handle chat messages and call appropriate LLM (keys not stored)."""
|
| 1108 |
+
if not message:
|
| 1109 |
+
return history, ""
|
| 1110 |
+
|
| 1111 |
+
# Add user message to history (using messages format)
|
| 1112 |
+
history.append({"role": "user", "content": message})
|
| 1113 |
+
|
| 1114 |
+
# Build messages for API
|
| 1115 |
+
messages = [
|
| 1116 |
+
{
|
| 1117 |
+
"role": "system",
|
| 1118 |
+
"content": "You are a helpful AI assistant with access to Roslyn-Stone MCP tools for C# development.",
|
| 1119 |
+
}
|
| 1120 |
+
]
|
| 1121 |
+
for h in history:
|
| 1122 |
+
if h.get("role") and h.get("content"):
|
| 1123 |
+
messages.append({"role": h["role"], "content": h["content"]})
|
| 1124 |
+
|
| 1125 |
+
# Get MCP tools if enabled (note: MCP tool calling only works with OpenAI/Anthropic)
|
| 1126 |
+
tools = []
|
| 1127 |
+
if use_mcp and provider_name in ["OpenAI", "Anthropic"]:
|
| 1128 |
+
mcp_tools = mcp_client.list_tools()
|
| 1129 |
+
for tool in mcp_tools:
|
| 1130 |
+
tools.append(
|
| 1131 |
+
{
|
| 1132 |
+
"type": "function",
|
| 1133 |
+
"function": {
|
| 1134 |
+
"name": tool.get("name"),
|
| 1135 |
+
"description": tool.get("description", ""),
|
| 1136 |
+
"parameters": tool.get("inputSchema", {}),
|
| 1137 |
+
},
|
| 1138 |
+
}
|
| 1139 |
+
)
|
| 1140 |
+
|
| 1141 |
+
# Call appropriate provider (API key used only for this request, not stored)
|
| 1142 |
+
try:
|
| 1143 |
+
if provider_name == "OpenAI":
|
| 1144 |
+
if not key:
|
| 1145 |
+
response_text = "Error: OpenAI API key is required"
|
| 1146 |
+
else:
|
| 1147 |
+
response_text = call_openai_chat(
|
| 1148 |
+
messages, key, model_name, tools, mcp_client
|
| 1149 |
+
)
|
| 1150 |
+
elif provider_name == "Anthropic":
|
| 1151 |
+
if not key:
|
| 1152 |
+
response_text = "Error: Anthropic API key is required"
|
| 1153 |
+
else:
|
| 1154 |
+
response_text = call_anthropic_chat(
|
| 1155 |
+
messages, key, model_name, tools, mcp_client
|
| 1156 |
+
)
|
| 1157 |
+
elif provider_name == "Google Gemini":
|
| 1158 |
+
if not key:
|
| 1159 |
+
response_text = "Error: Google API key is required"
|
| 1160 |
+
else:
|
| 1161 |
+
response_text = call_gemini_chat(
|
| 1162 |
+
messages, key, model_name, tools, mcp_client
|
| 1163 |
+
)
|
| 1164 |
+
elif provider_name in {"HuggingFace (Serverless)", "HuggingFace"}:
|
| 1165 |
+
response_text = call_huggingface_chat(
|
| 1166 |
+
messages, key, model_name, mcp_client
|
| 1167 |
+
)
|
| 1168 |
+
else:
|
| 1169 |
+
response_text = f"Error: Unknown provider {provider_name}"
|
| 1170 |
+
|
| 1171 |
+
history.append({"role": "assistant", "content": response_text})
|
| 1172 |
+
except Exception as e:
|
| 1173 |
+
history.append({"role": "assistant", "content": f"Error: {e!s}"})
|
| 1174 |
+
|
| 1175 |
+
return history, ""
|
| 1176 |
+
|
| 1177 |
+
# Wire up chat events
|
| 1178 |
+
send_btn.click(
|
| 1179 |
+
fn=chat_response,
|
| 1180 |
+
inputs=[msg, chatbot, provider, api_key, model, enable_mcp],
|
| 1181 |
+
outputs=[chatbot, msg],
|
| 1182 |
+
)
|
| 1183 |
+
|
| 1184 |
+
msg.submit(
|
| 1185 |
+
fn=chat_response,
|
| 1186 |
+
inputs=[msg, chatbot, provider, api_key, model, enable_mcp],
|
| 1187 |
+
outputs=[chatbot, msg],
|
| 1188 |
+
)
|
| 1189 |
+
|
| 1190 |
+
clear_btn.click(fn=lambda: ([], ""), outputs=[chatbot, msg])
|
| 1191 |
+
|
| 1192 |
+
# About Tab
|
| 1193 |
+
with gr.Tab("ℹ️ About"):
|
| 1194 |
+
gr.Markdown(
|
| 1195 |
+
f"""
|
| 1196 |
+
## About Roslyn-Stone MCP Server
|
| 1197 |
+
|
| 1198 |
+
**Server URL:** `{base_url}/mcp`
|
| 1199 |
+
|
| 1200 |
+
This interactive UI allows you to:
|
| 1201 |
+
- 🔧 **Execute MCP Tools** - Test C# code execution, package loading, and more
|
| 1202 |
+
- 📚 **Browse Resources** - Access documentation and package information
|
| 1203 |
+
- 💬 **View Prompts** - Get guidance on using the MCP server
|
| 1204 |
+
|
| 1205 |
+
### Connection Information
|
| 1206 |
+
|
| 1207 |
+
This UI connects to the MCP server at `{base_url}/mcp` using HTTP transport.
|
| 1208 |
+
|
| 1209 |
+
For integrating with Claude, VS Code, or other MCP clients, see the
|
| 1210 |
+
[GitHub repository](https://github.com/dylanlangston/Roslyn-Stone) for configuration instructions.
|
| 1211 |
+
|
| 1212 |
+
### What is Roslyn-Stone?
|
| 1213 |
+
|
| 1214 |
+
Roslyn-Stone is a developer- and LLM-friendly C# sandbox for creating single-file
|
| 1215 |
+
utility programs through the Model Context Protocol (MCP). It helps AI systems create
|
| 1216 |
+
runnable C# programs using file-based apps with top-level statements.
|
| 1217 |
+
|
| 1218 |
+
### Features
|
| 1219 |
+
|
| 1220 |
+
- **C# REPL** - Execute C# code with stateful sessions
|
| 1221 |
+
- **NuGet Integration** - Search and load packages dynamically
|
| 1222 |
+
- **Documentation** - Query .NET API documentation
|
| 1223 |
+
- **Validation** - Check C# syntax before execution
|
| 1224 |
+
- **MCP Protocol** - Standards-based AI integration
|
| 1225 |
+
|
| 1226 |
+
### Security Note
|
| 1227 |
+
|
| 1228 |
+
⚠️ This server can execute arbitrary C# code. Deploy with appropriate security measures:
|
| 1229 |
+
- Run in isolated containers/sandboxes
|
| 1230 |
+
- Implement authentication and rate limiting
|
| 1231 |
+
- Restrict network access
|
| 1232 |
+
- Monitor resource usage
|
| 1233 |
+
|
| 1234 |
+
### Links
|
| 1235 |
+
|
| 1236 |
+
- [GitHub Repository](https://github.com/dylanlangston/Roslyn-Stone)
|
| 1237 |
+
- [Model Context Protocol](https://github.com/modelcontextprotocol/specification)
|
| 1238 |
+
- [Roslyn Documentation](https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/)
|
| 1239 |
+
"""
|
| 1240 |
+
)
|
| 1241 |
+
|
| 1242 |
+
gr.Markdown(
|
| 1243 |
+
"""
|
| 1244 |
+
---
|
| 1245 |
+
|
| 1246 |
+
**Status**: 🟢 Connected to MCP server. Use the tabs above to explore and test available tools, resources, and prompts.
|
| 1247 |
+
"""
|
| 1248 |
+
)
|
| 1249 |
+
|
| 1250 |
+
return demo # type: ignore[no-any-return]
|
| 1251 |
+
|
| 1252 |
+
|
| 1253 |
+
def launch_app(
|
| 1254 |
+
base_url: str | None = None, server_port: int = 7860, share: bool = False
|
| 1255 |
+
) -> gr.Blocks:
|
| 1256 |
+
"""Launch the Gradio application.
|
| 1257 |
+
|
| 1258 |
+
Args:
|
| 1259 |
+
base_url: The base URL of the MCP server
|
| 1260 |
+
server_port: Port to run Gradio on
|
| 1261 |
+
share: Whether to create a public link
|
| 1262 |
+
|
| 1263 |
+
Returns:
|
| 1264 |
+
The running Gradio Blocks interface
|
| 1265 |
+
"""
|
| 1266 |
+
demo = create_landing_page(base_url)
|
| 1267 |
+
demo.launch(server_port=server_port, share=share, server_name="127.0.0.1")
|
| 1268 |
+
return demo
|
| 1269 |
+
|
| 1270 |
+
|
| 1271 |
+
if __name__ == "__main__":
|
| 1272 |
+
# Read configuration from environment
|
| 1273 |
+
import os
|
| 1274 |
+
|
| 1275 |
+
base_url = os.environ.get("BASE_URL", "http://localhost:7071")
|
| 1276 |
+
server_port = int(os.environ.get("GRADIO_SERVER_PORT", "7860"))
|
| 1277 |
+
|
| 1278 |
+
# Launch the app
|
| 1279 |
+
launch_app(base_url=base_url, server_port=server_port, share=False)
|
src/RoslynStone.GradioModule/gradio_launcher.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Simple Gradio launcher module for CSnakes integration.
|
| 2 |
+
This module provides a simple function to start the Gradio server.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def start_gradio_server(base_url: str = "http://localhost:7071", server_port: int = 7860) -> str:
|
| 7 |
+
"""Start the Gradio server and return the server URL.
|
| 8 |
+
|
| 9 |
+
Args:
|
| 10 |
+
base_url: The base URL of the MCP server
|
| 11 |
+
server_port: Port to run Gradio on
|
| 12 |
+
|
| 13 |
+
Returns:
|
| 14 |
+
The Gradio server URL
|
| 15 |
+
"""
|
| 16 |
+
try:
|
| 17 |
+
from gradio_app import create_landing_page
|
| 18 |
+
|
| 19 |
+
# Create the landing page
|
| 20 |
+
demo = create_landing_page(base_url)
|
| 21 |
+
|
| 22 |
+
# Launch in a thread to not block
|
| 23 |
+
demo.launch(
|
| 24 |
+
server_port=server_port,
|
| 25 |
+
server_name="127.0.0.1",
|
| 26 |
+
share=False,
|
| 27 |
+
prevent_thread_lock=True,
|
| 28 |
+
show_error=True,
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
return f"http://127.0.0.1:{server_port}"
|
| 32 |
+
except Exception as e:
|
| 33 |
+
return f"Error: {e!s}"
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def check_gradio_installed() -> bool:
|
| 37 |
+
"""Check if Gradio is installed.
|
| 38 |
+
|
| 39 |
+
Returns:
|
| 40 |
+
True if Gradio is available, False otherwise
|
| 41 |
+
"""
|
| 42 |
+
try:
|
| 43 |
+
import importlib.util
|
| 44 |
+
|
| 45 |
+
return importlib.util.find_spec("gradio") is not None
|
| 46 |
+
except ImportError:
|
| 47 |
+
return False
|
src/RoslynStone.GradioModule/pyproject.toml
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[project]
|
| 2 |
+
name = "roslyn-stone-gradio"
|
| 3 |
+
version = "0.5.0"
|
| 4 |
+
description = "Interactive Gradio UI for Roslyn-Stone MCP Server testing with chat interface"
|
| 5 |
+
requires-python = ">=3.9"
|
| 6 |
+
dependencies = [
|
| 7 |
+
"gradio>=6.0.0",
|
| 8 |
+
"httpx>=0.27.0",
|
| 9 |
+
"pygments>=2.17.0",
|
| 10 |
+
"openai>=1.0.0",
|
| 11 |
+
"anthropic>=0.25.0",
|
| 12 |
+
"google-generativeai>=0.3.0",
|
| 13 |
+
"huggingface_hub>=0.20.0",
|
| 14 |
+
]
|
| 15 |
+
|
| 16 |
+
[project.optional-dependencies]
|
| 17 |
+
dev = [
|
| 18 |
+
"ruff>=0.8.0",
|
| 19 |
+
"mypy>=1.13.0",
|
| 20 |
+
"types-pygments>=2.17.0",
|
| 21 |
+
]
|
| 22 |
+
|
| 23 |
+
[build-system]
|
| 24 |
+
requires = ["hatchling"]
|
| 25 |
+
build-backend = "hatchling.build"
|
| 26 |
+
|
| 27 |
+
[tool.ruff]
|
| 28 |
+
# Enable pycodestyle (E/W), Pyflakes (F), isort (I), pydocstyle (D), pyupgrade (UP), and more
|
| 29 |
+
line-length = 100
|
| 30 |
+
target-version = "py39"
|
| 31 |
+
|
| 32 |
+
[tool.ruff.lint]
|
| 33 |
+
select = [
|
| 34 |
+
"E", # pycodestyle errors
|
| 35 |
+
"W", # pycodestyle warnings
|
| 36 |
+
"F", # Pyflakes
|
| 37 |
+
"I", # isort
|
| 38 |
+
"N", # pep8-naming
|
| 39 |
+
"D", # pydocstyle
|
| 40 |
+
"UP", # pyupgrade
|
| 41 |
+
"ANN", # flake8-annotations
|
| 42 |
+
"ASYNC", # flake8-async
|
| 43 |
+
"S", # flake8-bandit
|
| 44 |
+
"B", # flake8-bugbear
|
| 45 |
+
"A", # flake8-builtins
|
| 46 |
+
"COM", # flake8-commas
|
| 47 |
+
"C4", # flake8-comprehensions
|
| 48 |
+
"DTZ", # flake8-datetimez
|
| 49 |
+
"T10", # flake8-debugger
|
| 50 |
+
"EM", # flake8-errmsg
|
| 51 |
+
"ISC", # flake8-implicit-str-concat
|
| 52 |
+
"ICN", # flake8-import-conventions
|
| 53 |
+
"G", # flake8-logging-format
|
| 54 |
+
"INP", # flake8-no-pep420
|
| 55 |
+
"PIE", # flake8-pie
|
| 56 |
+
"Q", # flake8-quotes
|
| 57 |
+
"RSE", # flake8-raise
|
| 58 |
+
"RET", # flake8-return
|
| 59 |
+
"SLF", # flake8-self
|
| 60 |
+
"SIM", # flake8-simplify
|
| 61 |
+
"TID", # flake8-tidy-imports
|
| 62 |
+
"TCH", # flake8-type-checking
|
| 63 |
+
"PTH", # flake8-use-pathlib
|
| 64 |
+
"ERA", # eradicate
|
| 65 |
+
"PL", # Pylint
|
| 66 |
+
"PERF", # Perflint
|
| 67 |
+
"RUF", # Ruff-specific rules
|
| 68 |
+
]
|
| 69 |
+
|
| 70 |
+
ignore = [
|
| 71 |
+
"D100", # Missing docstring in public module (we have module docstrings at top)
|
| 72 |
+
"D104", # Missing docstring in public package
|
| 73 |
+
"D205", # Blank line between summary and description (not needed for short docstrings)
|
| 74 |
+
"ANN001", # Missing type annotation for function argument (Gradio callbacks)
|
| 75 |
+
"ANN202", # Missing return type annotation for private function (internal Gradio callbacks)
|
| 76 |
+
"COM812", # Trailing comma missing (conflicts with formatter)
|
| 77 |
+
"ISC001", # Implicit string concatenation (conflicts with formatter)
|
| 78 |
+
"E501", # Line too long (formatter handles this)
|
| 79 |
+
"PLR0913",# Too many arguments (Gradio callbacks need this)
|
| 80 |
+
"PLR0912",# Too many branches (complex UI logic)
|
| 81 |
+
"PLR0915",# Too many statements (large UI functions)
|
| 82 |
+
"PLR0911",# Too many return statements (error handling patterns)
|
| 83 |
+
"ARG001", # Unused function argument (some callbacks need placeholder args)
|
| 84 |
+
"PLC0415",# Import outside top-level (lazy imports for optional dependencies)
|
| 85 |
+
"TRY300", # Consider moving to else block (readability preference)
|
| 86 |
+
"PERF401",# Use list comprehension (readability over micro-optimization)
|
| 87 |
+
"RUF001", # Ambiguous unicode character (emoji in UI strings is intentional)
|
| 88 |
+
]
|
| 89 |
+
|
| 90 |
+
[tool.ruff.lint.pydocstyle]
|
| 91 |
+
convention = "google"
|
| 92 |
+
|
| 93 |
+
[tool.ruff.lint.per-file-ignores]
|
| 94 |
+
"__init__.py" = ["F401"] # Allow unused imports in __init__.py
|
| 95 |
+
|
| 96 |
+
[tool.ruff.format]
|
| 97 |
+
quote-style = "double"
|
| 98 |
+
indent-style = "space"
|
| 99 |
+
line-ending = "auto"
|
| 100 |
+
|
| 101 |
+
[tool.mypy]
|
| 102 |
+
python_version = "3.9"
|
| 103 |
+
warn_return_any = true
|
| 104 |
+
warn_unused_configs = true
|
| 105 |
+
check_untyped_defs = true
|
| 106 |
+
warn_redundant_casts = true
|
| 107 |
+
warn_unused_ignores = false # Disabled because of third-party library stubs
|
| 108 |
+
warn_no_return = true
|
| 109 |
+
warn_unreachable = true
|
| 110 |
+
strict_equality = true
|
| 111 |
+
# Relaxed settings for Gradio UI code with many callbacks
|
| 112 |
+
disallow_untyped_defs = false
|
| 113 |
+
disallow_incomplete_defs = false
|
| 114 |
+
disallow_untyped_decorators = false
|
| 115 |
+
no_implicit_optional = false
|
| 116 |
+
exclude = ["bin/", "obj/"]
|
| 117 |
+
|
| 118 |
+
[[tool.mypy.overrides]]
|
| 119 |
+
module = [
|
| 120 |
+
"gradio.*",
|
| 121 |
+
"anthropic.*",
|
| 122 |
+
"openai.*",
|
| 123 |
+
"google.generativeai.*",
|
| 124 |
+
"huggingface_hub.*",
|
| 125 |
+
]
|
| 126 |
+
ignore_missing_imports = true
|
src/RoslynStone.Infrastructure/RoslynStone.Infrastructure.csproj
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<Project Sdk="Microsoft.NET.Sdk">
|
| 2 |
+
<PropertyGroup>
|
| 3 |
+
<TargetFramework>net10.0</TargetFramework>
|
| 4 |
+
<LangVersion>14</LangVersion>
|
| 5 |
+
<ImplicitUsings>enable</ImplicitUsings>
|
| 6 |
+
<Nullable>enable</Nullable>
|
| 7 |
+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
| 8 |
+
|
| 9 |
+
<!-- NuGet Package Metadata -->
|
| 10 |
+
<PackageId>RoslynStone.Infrastructure</PackageId>
|
| 11 |
+
<Authors>Dylan Langston</Authors>
|
| 12 |
+
<Description>Infrastructure implementations for RoslynStone - Roslyn scripting services, MCP tools, command/query handlers</Description>
|
| 13 |
+
<PackageTags>roslyn;repl;csharp;mcp;llm;ai;scripting;infrastructure</PackageTags>
|
| 14 |
+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
| 15 |
+
<PackageProjectUrl>https://github.com/dylanlangston/Roslyn-Stone</PackageProjectUrl>
|
| 16 |
+
<RepositoryUrl>https://github.com/dylanlangston/Roslyn-Stone</RepositoryUrl>
|
| 17 |
+
<RepositoryType>git</RepositoryType>
|
| 18 |
+
<PackageReadmeFile>README.md</PackageReadmeFile>
|
| 19 |
+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
| 20 |
+
</PropertyGroup>
|
| 21 |
+
|
| 22 |
+
<ItemGroup>
|
| 23 |
+
<ProjectReference Include="..\RoslynStone.Core\RoslynStone.Core.csproj" />
|
| 24 |
+
</ItemGroup>
|
| 25 |
+
|
| 26 |
+
<ItemGroup>
|
| 27 |
+
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="10.0.0" />
|
| 28 |
+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.14.0" />
|
| 29 |
+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.14.0" />
|
| 30 |
+
<PackageReference Include="ModelContextProtocol" Version="0.4.0-preview.3" />
|
| 31 |
+
<PackageReference Include="NuGet.Protocol" Version="7.0.0" />
|
| 32 |
+
</ItemGroup>
|
| 33 |
+
|
| 34 |
+
<ItemGroup>
|
| 35 |
+
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
|
| 36 |
+
</ItemGroup>
|
| 37 |
+
</Project>
|
src/RoslynStone.Infrastructure/Tools/DocumentationTools.cs
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using System.ComponentModel;
|
| 2 |
+
using ModelContextProtocol.Server;
|
| 3 |
+
using RoslynStone.Infrastructure.Services;
|
| 4 |
+
|
| 5 |
+
namespace RoslynStone.Infrastructure.Tools;
|
| 6 |
+
|
| 7 |
+
/// <summary>
|
| 8 |
+
/// MCP tools for .NET documentation lookup
|
| 9 |
+
/// </summary>
|
| 10 |
+
[McpServerToolType]
|
| 11 |
+
public class DocumentationTools
|
| 12 |
+
{
|
| 13 |
+
/// <summary>
|
| 14 |
+
/// Get XML documentation for a .NET type or method
|
| 15 |
+
/// </summary>
|
| 16 |
+
/// <param name="documentationService">The documentation service for symbol lookup</param>
|
| 17 |
+
/// <param name="symbolName">The fully qualified type or method name</param>
|
| 18 |
+
/// <param name="packageId">Optional NuGet package ID for package-specific types</param>
|
| 19 |
+
/// <param name="cancellationToken">Cancellation token for async operations</param>
|
| 20 |
+
/// <returns>Documentation content in structured format</returns>
|
| 21 |
+
[McpServerTool]
|
| 22 |
+
[Description(
|
| 23 |
+
"Access comprehensive XML documentation for .NET types, methods, properties, and other symbols, including NuGet packages. Returns detailed information including summary, remarks, parameters, return values, exceptions, and code examples. Use this to: understand .NET APIs, learn method signatures, discover available members, and get context-aware help. Supports all .NET 10 types and methods, plus NuGet packages."
|
| 24 |
+
)]
|
| 25 |
+
public static async Task<object> GetDocumentation(
|
| 26 |
+
DocumentationService documentationService,
|
| 27 |
+
[Description(
|
| 28 |
+
"Fully qualified type or method name. Examples: 'System.String', 'System.Linq.Enumerable.Select', 'System.Collections.Generic.List`1'"
|
| 29 |
+
)]
|
| 30 |
+
string symbolName,
|
| 31 |
+
[Description(
|
| 32 |
+
"Optional NuGet package ID for package-specific types. Example: 'Newtonsoft.Json' for 'Newtonsoft.Json.JsonConvert'"
|
| 33 |
+
)]
|
| 34 |
+
string? packageId = null,
|
| 35 |
+
CancellationToken cancellationToken = default
|
| 36 |
+
)
|
| 37 |
+
{
|
| 38 |
+
ArgumentException.ThrowIfNullOrWhiteSpace(symbolName);
|
| 39 |
+
|
| 40 |
+
var doc = await documentationService.GetDocumentationAsync(
|
| 41 |
+
symbolName,
|
| 42 |
+
packageId,
|
| 43 |
+
cancellationToken
|
| 44 |
+
);
|
| 45 |
+
|
| 46 |
+
if (doc == null)
|
| 47 |
+
{
|
| 48 |
+
var exampleMessage =
|
| 49 |
+
packageId != null
|
| 50 |
+
? $"Documentation not found for symbol: {symbolName} in package: {packageId}. Make sure the package exists and contains XML documentation."
|
| 51 |
+
: $"Documentation not found for symbol: {symbolName}. Try well-known types like System.String, System.Linq.Enumerable, System.Collections.Generic.List`1, System.Threading.Tasks.Task, or System.Text.Json.JsonSerializer. For NuGet packages, provide packageId parameter (e.g., packageId: 'Newtonsoft.Json').";
|
| 52 |
+
|
| 53 |
+
return new { found = false, message = exampleMessage };
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
return new
|
| 57 |
+
{
|
| 58 |
+
found = true,
|
| 59 |
+
symbolName = doc.SymbolName,
|
| 60 |
+
summary = doc.Summary,
|
| 61 |
+
remarks = doc.Remarks,
|
| 62 |
+
parameters = doc.Parameters,
|
| 63 |
+
returns = doc.Returns,
|
| 64 |
+
exceptions = doc.Exceptions,
|
| 65 |
+
example = doc.Example,
|
| 66 |
+
};
|
| 67 |
+
}
|
| 68 |
+
}
|
src/RoslynStone.ServiceDefaults/Extensions.cs
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using Microsoft.AspNetCore.Builder;
|
| 2 |
+
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
| 3 |
+
using Microsoft.Extensions.DependencyInjection;
|
| 4 |
+
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
| 5 |
+
using Microsoft.Extensions.Logging;
|
| 6 |
+
using OpenTelemetry;
|
| 7 |
+
using OpenTelemetry.Metrics;
|
| 8 |
+
using OpenTelemetry.Trace;
|
| 9 |
+
|
| 10 |
+
// ReSharper disable CheckNamespace
|
| 11 |
+
namespace Microsoft.Extensions.Hosting;
|
| 12 |
+
|
| 13 |
+
// Adds common Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
|
| 14 |
+
// This project should be referenced by each service project in your solution.
|
| 15 |
+
// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults
|
| 16 |
+
public static class Extensions
|
| 17 |
+
{
|
| 18 |
+
private const string HealthEndpointPath = "/health";
|
| 19 |
+
private const string AlivenessEndpointPath = "/alive";
|
| 20 |
+
|
| 21 |
+
public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder)
|
| 22 |
+
where TBuilder : IHostApplicationBuilder
|
| 23 |
+
{
|
| 24 |
+
builder.ConfigureOpenTelemetry();
|
| 25 |
+
|
| 26 |
+
builder.AddDefaultHealthChecks();
|
| 27 |
+
|
| 28 |
+
builder.Services.AddServiceDiscovery();
|
| 29 |
+
|
| 30 |
+
builder.Services.ConfigureHttpClientDefaults(http =>
|
| 31 |
+
{
|
| 32 |
+
// Turn on resilience by default
|
| 33 |
+
http.AddStandardResilienceHandler();
|
| 34 |
+
|
| 35 |
+
// Turn on service discovery by default
|
| 36 |
+
http.AddServiceDiscovery();
|
| 37 |
+
});
|
| 38 |
+
|
| 39 |
+
// Uncomment the following to restrict the allowed schemes for service discovery.
|
| 40 |
+
// builder.Services.Configure<ServiceDiscoveryOptions>(options =>
|
| 41 |
+
// {
|
| 42 |
+
// options.AllowedSchemes = ["https"];
|
| 43 |
+
// });
|
| 44 |
+
|
| 45 |
+
return builder;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder)
|
| 49 |
+
where TBuilder : IHostApplicationBuilder
|
| 50 |
+
{
|
| 51 |
+
builder.Logging.AddOpenTelemetry(logging =>
|
| 52 |
+
{
|
| 53 |
+
logging.IncludeFormattedMessage = true;
|
| 54 |
+
logging.IncludeScopes = true;
|
| 55 |
+
});
|
| 56 |
+
|
| 57 |
+
builder
|
| 58 |
+
.Services.AddOpenTelemetry()
|
| 59 |
+
.WithMetrics(metrics =>
|
| 60 |
+
{
|
| 61 |
+
metrics
|
| 62 |
+
.AddAspNetCoreInstrumentation()
|
| 63 |
+
.AddHttpClientInstrumentation()
|
| 64 |
+
.AddRuntimeInstrumentation();
|
| 65 |
+
})
|
| 66 |
+
.WithTracing(tracing =>
|
| 67 |
+
{
|
| 68 |
+
tracing
|
| 69 |
+
.AddSource(builder.Environment.ApplicationName)
|
| 70 |
+
.AddAspNetCoreInstrumentation(tracingOptions =>
|
| 71 |
+
// Exclude health check requests from tracing
|
| 72 |
+
tracingOptions.Filter = context =>
|
| 73 |
+
!context.Request.Path.StartsWithSegments(HealthEndpointPath)
|
| 74 |
+
&& !context.Request.Path.StartsWithSegments(AlivenessEndpointPath)
|
| 75 |
+
)
|
| 76 |
+
// Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
|
| 77 |
+
//.AddGrpcClientInstrumentation()
|
| 78 |
+
.AddHttpClientInstrumentation();
|
| 79 |
+
});
|
| 80 |
+
|
| 81 |
+
_ = builder.AddOpenTelemetryExporters();
|
| 82 |
+
|
| 83 |
+
return builder;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
private static TBuilder AddOpenTelemetryExporters<TBuilder>(this TBuilder builder)
|
| 87 |
+
where TBuilder : IHostApplicationBuilder
|
| 88 |
+
{
|
| 89 |
+
var useOtlpExporter = !string.IsNullOrWhiteSpace(
|
| 90 |
+
builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]
|
| 91 |
+
);
|
| 92 |
+
|
| 93 |
+
if (useOtlpExporter)
|
| 94 |
+
{
|
| 95 |
+
builder.Services.AddOpenTelemetry().UseOtlpExporter();
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
// Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
|
| 99 |
+
//if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
|
| 100 |
+
//{
|
| 101 |
+
// builder.Services.AddOpenTelemetry()
|
| 102 |
+
// .UseAzureMonitor();
|
| 103 |
+
//}
|
| 104 |
+
|
| 105 |
+
return builder;
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder)
|
| 109 |
+
where TBuilder : IHostApplicationBuilder
|
| 110 |
+
{
|
| 111 |
+
builder
|
| 112 |
+
.Services.AddHealthChecks()
|
| 113 |
+
// Add a default liveness check to ensure app is responsive
|
| 114 |
+
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
|
| 115 |
+
|
| 116 |
+
return builder;
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
public static WebApplication MapDefaultEndpoints(this WebApplication app)
|
| 120 |
+
{
|
| 121 |
+
// Adding health checks endpoints to applications in non-development environments has security implications.
|
| 122 |
+
// See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments.
|
| 123 |
+
if (app.Environment.IsDevelopment())
|
| 124 |
+
{
|
| 125 |
+
// All health checks must pass for app to be considered ready to accept traffic after starting
|
| 126 |
+
app.MapHealthChecks(HealthEndpointPath);
|
| 127 |
+
|
| 128 |
+
// Only health checks tagged with the "live" tag must pass for app to be considered alive
|
| 129 |
+
app.MapHealthChecks(
|
| 130 |
+
AlivenessEndpointPath,
|
| 131 |
+
new HealthCheckOptions { Predicate = r => r.Tags.Contains("live") }
|
| 132 |
+
);
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
return app;
|
| 136 |
+
}
|
| 137 |
+
}
|
src/RoslynStone.ServiceDefaults/RoslynStone.ServiceDefaults.csproj
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<Project Sdk="Microsoft.NET.Sdk">
|
| 2 |
+
<PropertyGroup>
|
| 3 |
+
<TargetFramework>net10.0</TargetFramework>
|
| 4 |
+
<ImplicitUsings>enable</ImplicitUsings>
|
| 5 |
+
<Nullable>enable</Nullable>
|
| 6 |
+
<IsAspireSharedProject>true</IsAspireSharedProject>
|
| 7 |
+
</PropertyGroup>
|
| 8 |
+
|
| 9 |
+
<ItemGroup>
|
| 10 |
+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
| 11 |
+
|
| 12 |
+
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.0.0" />
|
| 13 |
+
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="10.0.0" />
|
| 14 |
+
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.13.1" />
|
| 15 |
+
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.13.1" />
|
| 16 |
+
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.13.0" />
|
| 17 |
+
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.13.0" />
|
| 18 |
+
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.13.0" />
|
| 19 |
+
</ItemGroup>
|
| 20 |
+
</Project>
|