Add ci build workflow (#31)
* Add ci build workflow * Using cake build * Update readme
This commit is contained in:
parent
257a97a659
commit
80daa5b73d
12 changed files with 303 additions and 20 deletions
13
.config/dotnet-tools.json
Normal file
13
.config/dotnet-tools.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"cake.tool": {
|
||||
"version": "5.0.0",
|
||||
"commands": [
|
||||
"dotnet-cake"
|
||||
],
|
||||
"rollForward": false
|
||||
}
|
||||
}
|
||||
}
|
||||
43
.github/workflows/ci.yml
vendored
Normal file
43
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
name: .NET Build, Test and publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "master" ]
|
||||
paths: [ 'src/**' ]
|
||||
pull_request:
|
||||
branches: [ "main", "master" ]
|
||||
paths: [ 'src/**' ]
|
||||
release:
|
||||
types: [published,edited]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
|
||||
- name: Fetch all history for all tags and branches
|
||||
run: git fetch --prune --unshallow
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v2
|
||||
with:
|
||||
dotnet-version: |
|
||||
8.0.x
|
||||
9.0.x
|
||||
|
||||
- name: Run Cake script
|
||||
uses: cake-build/cake-action@v1
|
||||
with:
|
||||
verbosity: Diagnostic
|
||||
env:
|
||||
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY}}
|
||||
SONAR_LOGIN: ${{ secrets.SONAR_LOGIN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1,3 +1,8 @@
|
|||
# Cake tools
|
||||
[Tt]ools/
|
||||
|
||||
# Build output
|
||||
[Bb]uildArtifacts/
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
|
|
|
|||
2
GitVersion.yml
Normal file
2
GitVersion.yml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
mode: ContinuousDeployment
|
||||
commit-message-incrementing: Disabled
|
||||
|
|
@ -1,5 +1,14 @@
|
|||
# mcpdotnet
|
||||
[](https://www.nuget.org/packages/mcpdotnet/)
|
||||

|
||||
[](LICENSE)
|
||||
|
||||
[](https://sonarcloud.io/summary/new_code?id=PederHP_mcpdotnet)
|
||||
[](https://sonarcloud.io/summary/new_code?id=PederHP_mcpdotnet)
|
||||
[](https://sonarcloud.io/summary/new_code?id=PederHP_mcpdotnet)
|
||||
[](https://sonarcloud.io/summary/new_code?id=PederHP_mcpdotnet)
|
||||
[](https://sonarcloud.io/summary/new_code?id=PederHP_mcpdotnet)
|
||||
[](https://sonarcloud.io/summary/new_code?id=PederHP_mcpdotnet)
|
||||
|
||||
A .NET implementation of the Model Context Protocol (MCP), enabling .NET applications to connect to and interact with MCP clients and servers.
|
||||
|
||||
|
|
|
|||
179
build.cake
Normal file
179
build.cake
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
#tool "dotnet:?package=GitVersion.Tool&version=6.1.0"
|
||||
#tool "nuget:?package=dotnet-sonarscanner&version=9.2.1"
|
||||
|
||||
#addin "nuget:?package=Cake.Sonar&version=1.1.33"
|
||||
|
||||
var target = Argument("target", "Default");
|
||||
var sonarLoginToken = Argument("sonarLogin", EnvironmentVariable("SONAR_LOGIN") ?? "");
|
||||
var nugetApiKey = Argument("nugetApiKey", EnvironmentVariable("NUGET_API_KEY") ?? "");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Build Variables
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
var solution = "./mcpdotnet.sln";
|
||||
var project = "./src/mcpdotnet.csproj";
|
||||
var outputDir = MakeAbsolute(Directory("./buildArtifacts/"));
|
||||
var outputDirNuget = outputDir.Combine("NuGet");
|
||||
var sonarProjectKey = "PederHP_mcpdotnet";
|
||||
var sonarUrl = "https://sonarcloud.io";
|
||||
var sonarOrganization = "pederhp";
|
||||
|
||||
var outputDirTemp = outputDir.Combine("Temp");
|
||||
var outputDirTests = outputDirTemp.Combine("Tests");
|
||||
|
||||
var codeCoverageResultFilePath = MakeAbsolute(outputDirTests).Combine("**/").CombineWithFilePath("coverage.opencover.xml");
|
||||
var testResultsPath = MakeAbsolute(outputDirTests).CombineWithFilePath("*.trx");
|
||||
|
||||
var nugetPublishFeed = "https://api.nuget.org/v3/index.json";
|
||||
|
||||
var isLocalBuild = BuildSystem.IsLocalBuild;
|
||||
var isMainBranch = StringComparer.OrdinalIgnoreCase.Equals("refs/heads/main", BuildSystem.GitHubActions.Environment.Workflow.Ref);
|
||||
var isPullRequest = BuildSystem.GitHubActions.Environment.PullRequest.IsPullRequest;
|
||||
var runSonar = !string.IsNullOrWhiteSpace(sonarLoginToken);
|
||||
|
||||
var gitHubEvent = EnvironmentVariable("GITHUB_EVENT_NAME");
|
||||
var isReleaseCreation = string.Equals(gitHubEvent, "release");
|
||||
GitVersion versionInfo = null;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// TASKS
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
Setup(context =>
|
||||
{
|
||||
Information($"Local build: {isLocalBuild}");
|
||||
Information($"Main branch: {isMainBranch}");
|
||||
Information($"Pull request: {isPullRequest}");
|
||||
Information($"Run sonar: {runSonar}");
|
||||
Information($"ref: {BuildSystem.GitHubActions.Environment.Workflow.Ref}");
|
||||
Information($"Is release creation: {isReleaseCreation}");
|
||||
});
|
||||
|
||||
Task("Clean")
|
||||
.Description("Removes the output directory")
|
||||
.Does(() =>
|
||||
{
|
||||
|
||||
EnsureDirectoryDoesNotExist(outputDir, new DeleteDirectorySettings
|
||||
{
|
||||
Recursive = true,
|
||||
Force = true
|
||||
});
|
||||
CreateDirectory(outputDir);
|
||||
});
|
||||
|
||||
|
||||
Task("Version")
|
||||
.Description("Retrieves the current version from the git repository")
|
||||
.Does(() =>
|
||||
{
|
||||
versionInfo = GitVersion(new GitVersionSettings
|
||||
{
|
||||
UpdateAssemblyInfo = false
|
||||
});
|
||||
|
||||
Information("Version: " + versionInfo.FullSemVer);
|
||||
});
|
||||
|
||||
Task("Build")
|
||||
.IsDependentOn("Clean")
|
||||
.IsDependentOn("Version")
|
||||
.Does(() =>
|
||||
{
|
||||
var msBuildSettings = new DotNetMSBuildSettings()
|
||||
{
|
||||
Version = versionInfo.AssemblySemVer,
|
||||
InformationalVersion = versionInfo.InformationalVersion,
|
||||
PackageVersion = versionInfo.SemVer
|
||||
}.WithProperty("PackageOutputPath", outputDirNuget.FullPath);
|
||||
|
||||
var settings = new DotNetBuildSettings
|
||||
{
|
||||
Configuration = "Release",
|
||||
MSBuildSettings = msBuildSettings
|
||||
};
|
||||
|
||||
DotNetBuild(solution, settings);
|
||||
});
|
||||
|
||||
Task("Test")
|
||||
.IsDependentOn("Build")
|
||||
.Does(() =>
|
||||
{
|
||||
var settings = new DotNetTestSettings
|
||||
{
|
||||
Configuration = "Release",
|
||||
Loggers = new[] { "trx;" },
|
||||
ResultsDirectory = outputDirTests,
|
||||
Collectors = new[] { "XPlat Code Coverage" },
|
||||
Filter = "(Execution!=Manual)",
|
||||
ArgumentCustomization = a => a.Append("-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover"),
|
||||
NoBuild = true
|
||||
};
|
||||
|
||||
DotNetTest(solution, settings);
|
||||
});
|
||||
|
||||
Task("SonarBegin")
|
||||
.WithCriteria(runSonar)
|
||||
.Does(() =>
|
||||
{
|
||||
SonarBegin(new SonarBeginSettings
|
||||
{
|
||||
Key = sonarProjectKey,
|
||||
Url = sonarUrl,
|
||||
Organization = sonarOrganization,
|
||||
Token = sonarLoginToken,
|
||||
UseCoreClr = true,
|
||||
VsTestReportsPath = testResultsPath.ToString(),
|
||||
OpenCoverReportsPath = codeCoverageResultFilePath.ToString()
|
||||
});
|
||||
});
|
||||
|
||||
Task("SonarEnd")
|
||||
.WithCriteria(runSonar)
|
||||
.Does(() =>
|
||||
{
|
||||
SonarEnd(new SonarEndSettings
|
||||
{
|
||||
Token = sonarLoginToken
|
||||
});
|
||||
});
|
||||
|
||||
Task("Publish")
|
||||
.WithCriteria(isReleaseCreation)
|
||||
.IsDependentOn("Test")
|
||||
.IsDependentOn("Version")
|
||||
.Description("Pushes the created NuGet packages to nuget.org")
|
||||
.Does(() =>
|
||||
{
|
||||
Information($"Upload packages from {outputDirNuget.FullPath}");
|
||||
|
||||
// Get the paths to the packages ordered by the file names in order to get the nupkg first.
|
||||
var packages = GetFiles(outputDirNuget.CombineWithFilePath("*.*nupkg").ToString()).OrderBy(x => x.FullPath).ToArray();
|
||||
|
||||
if (packages.Length == 0)
|
||||
{
|
||||
Error("No packages found to upload");
|
||||
return;
|
||||
}
|
||||
|
||||
// Push the package and symbols
|
||||
foreach (var package in packages)
|
||||
{
|
||||
DotNetNuGetPush(package, new DotNetNuGetPushSettings
|
||||
{
|
||||
Source = nugetPublishFeed,
|
||||
ApiKey = nugetApiKey,
|
||||
SkipDuplicate = true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Task("Default")
|
||||
.IsDependentOn("SonarBegin")
|
||||
.IsDependentOn("Test")
|
||||
.IsDependentOn("SonarEnd")
|
||||
.IsDependentOn("Publish");
|
||||
|
||||
RunTarget(target);
|
||||
13
build.ps1
Normal file
13
build.ps1
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
Set-Location -LiteralPath $PSScriptRoot
|
||||
|
||||
$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = '1'
|
||||
$env:DOTNET_CLI_TELEMETRY_OPTOUT = '1'
|
||||
$env:DOTNET_NOLOGO = '1'
|
||||
|
||||
dotnet tool restore
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
|
||||
dotnet cake @args
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
12
build.sh
Executable file
12
build.sh
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euox pipefail
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
|
||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
export DOTNET_NOLOGO=1
|
||||
|
||||
dotnet tool restore
|
||||
|
||||
dotnet cake "$@"
|
||||
|
|
@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestServerWithHosting", "sa
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{2A77AF5C-138A-4EBB-9A13-9205DCD67928}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -64,8 +66,11 @@ Global
|
|||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{FF41F619-833D-4FA2-8C53-04D0A1D5AA61} = {2A77AF5C-138A-4EBB-9A13-9205DCD67928}
|
||||
{76E295FA-9E85-7B99-332A-2CDBFCD5860A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||
{CA0BB450-1903-2F92-A68D-1285976551D6} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||
{7C229573-A085-4ECC-8131-958CDA2BE731} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||
{67BEF596-AD39-4BDD-B505-31DE7FF0C8D2} = {2A77AF5C-138A-4EBB-9A13-9205DCD67928}
|
||||
{6499876E-2F76-44A8-B6EB-5B889C6E9B7F} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using OpenAI;
|
|||
|
||||
namespace McpDotNet.Extensions.AI.Tests;
|
||||
|
||||
[Trait("Execution", "Manual")]
|
||||
public class IntegrationTests
|
||||
{
|
||||
private const string OpenAIKey = ""; // Provide your own key when running the test. Do not commit it.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using McpDotNet.Protocol.Types;
|
|||
|
||||
namespace McpDotNet.Tests;
|
||||
|
||||
[Trait("Execution", "Manual")]
|
||||
public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture>
|
||||
{
|
||||
private readonly ClientIntegrationTestFixture _fixture;
|
||||
|
|
@ -18,7 +19,7 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
|
|||
public async Task ConnectAndPing_Stdio_EverythingServer()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
|
||||
// Act
|
||||
var client = await _fixture.Factory.GetClientAsync("everything");
|
||||
await client.PingAsync(CancellationToken.None);
|
||||
|
|
@ -47,7 +48,7 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
|
|||
public async Task ListTools_Stdio_EverythingServer()
|
||||
{
|
||||
// arrange
|
||||
|
||||
|
||||
// act
|
||||
var client = await _fixture.Factory.GetClientAsync("everything");
|
||||
var tools = await client.ListToolsAsync();
|
||||
|
|
@ -62,7 +63,7 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
|
|||
public async Task CallTool_Stdio_EchoServer()
|
||||
{
|
||||
// arrange
|
||||
|
||||
|
||||
// act
|
||||
var client = await _fixture.Factory.GetClientAsync("everything");
|
||||
var result = await client.CallToolAsync(
|
||||
|
|
@ -184,7 +185,7 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
|
|||
public async Task ReadResource_Stdio_BinaryResource()
|
||||
{
|
||||
// arrange
|
||||
|
||||
|
||||
// act
|
||||
var client = await _fixture.Factory.GetClientAsync("everything");
|
||||
// Even numbered resources are binary in the everything server (despite the docs saying otherwise)
|
||||
|
|
@ -214,11 +215,11 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
|
|||
// act
|
||||
var client = await _fixture.Factory.GetClientAsync("everything");
|
||||
var result = await client.GetCompletionAsync(new Reference
|
||||
{
|
||||
Type = "ref/resource",
|
||||
Uri = "test://static/resource/1"
|
||||
},
|
||||
"argument_name", "1",
|
||||
{
|
||||
Type = "ref/resource",
|
||||
Uri = "test://static/resource/1"
|
||||
},
|
||||
"argument_name", "1",
|
||||
CancellationToken.None
|
||||
);
|
||||
|
||||
|
|
@ -249,7 +250,7 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
|
|||
Type = "ref/prompt",
|
||||
Name = "irrelevant"
|
||||
},
|
||||
argumentName:"style", argumentValue:"fo",
|
||||
argumentName: "style", argumentValue: "fo",
|
||||
CancellationToken.None
|
||||
);
|
||||
|
||||
|
|
@ -293,7 +294,7 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
|
|||
// Set up the sampling handler
|
||||
int samplingHandlerCalls = 0;
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
client.SamplingHandler = async (_,_) =>
|
||||
client.SamplingHandler = async (_, _) =>
|
||||
{
|
||||
samplingHandlerCalls++;
|
||||
return new CreateMessageResult
|
||||
|
|
@ -312,10 +313,10 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
|
|||
// Call the server's sampleLLM tool which should trigger our sampling handler
|
||||
var result = await client.CallToolAsync(
|
||||
"sampleLLM",
|
||||
new Dictionary<string, object>
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
["prompt"] = "Test prompt",
|
||||
["maxTokens"] = 100
|
||||
["prompt"] = "Test prompt",
|
||||
["maxTokens"] = 100
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
using McpDotNet.Client;
|
||||
using System.Text.Json;
|
||||
using McpDotNet.Client;
|
||||
using McpDotNet.Configuration;
|
||||
using McpDotNet.Protocol.Messages;
|
||||
using McpDotNet.Protocol.Transport;
|
||||
using McpDotNet.Protocol.Types;
|
||||
using McpDotNet.Tests.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace McpDotNet.Tests;
|
||||
|
||||
[Trait("Execution", "Manual")]
|
||||
public class SseIntegrationTests
|
||||
{
|
||||
[Fact]
|
||||
|
|
@ -19,7 +19,7 @@ public class SseIntegrationTests
|
|||
builder.AddConsole()
|
||||
.SetMinimumLevel(LogLevel.Debug));
|
||||
|
||||
await using TestSseServer server = new(logger:loggerFactory.CreateLogger<TestSseServer>());
|
||||
await using TestSseServer server = new(logger: loggerFactory.CreateLogger<TestSseServer>());
|
||||
await server.StartAsync();
|
||||
|
||||
|
||||
|
|
@ -111,7 +111,7 @@ public class SseIntegrationTests
|
|||
ClientInfo = new()
|
||||
{
|
||||
Name = "IntegrationTestClient",
|
||||
Version = "1.0.0"
|
||||
Version = "1.0.0"
|
||||
},
|
||||
Capabilities = new()
|
||||
{
|
||||
|
|
@ -228,7 +228,7 @@ public class SseIntegrationTests
|
|||
await using TestSseServer server = new(logger: loggerFactory.CreateLogger<TestSseServer>());
|
||||
await server.StartAsync();
|
||||
|
||||
|
||||
|
||||
var defaultOptions = new McpClientOptions
|
||||
{
|
||||
ClientInfo = new() { Name = "IntegrationTestClient", Version = "1.0.0" }
|
||||
|
|
|
|||
Loading…
Reference in a new issue