Skip to content

Commit fa363a4

Browse files
authored
Update all MCP servers to support both stdio and HTTP in one app, instead of separate apps like console app and web app (#30)
* Add hybrid app to run both stdio and http apps * Refactor hybrid app * Add hybrid app for todo-list * Update todo-list app to be run in both stdio and http * Update azd template for todo-list MCP server * Add XML comments to todo-list app * Update markdown-to-html MCP server * Update sample mcp.json files * Update README * Update launchSettings.json * Update awesome-copilot MCP server to run both stdio and http in one app * Update README docs * Update GHA workflows * Update based on copilot's suggestions
1 parent ef72ac3 commit fa363a4

File tree

80 files changed

+4671
-1007
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+4671
-1007
lines changed

.github/workflows/build-awesome-copilot.yaml

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ jobs:
1212
strategy:
1313
matrix:
1414
image_name: [ 'awesome-copilot' ]
15-
extension: [ 'stdio' ]
1615

1716
permissions:
1817
contents: write
@@ -22,28 +21,5 @@ jobs:
2221

2322
with:
2423
image_name: ${{ matrix.image_name }}
25-
extension: ${{ matrix.extension }}
26-
force: false
27-
secrets: inherit
28-
29-
build-and-push-image-http:
30-
uses: ./.github/workflows/build-container.yaml
31-
32-
needs: build-and-push-image-stdio
33-
34-
strategy:
35-
matrix:
36-
image_name: [ 'awesome-copilot' ]
37-
extension: [ 'http' ]
38-
39-
permissions:
40-
contents: write
41-
packages: write
42-
attestations: write
43-
id-token: write
44-
45-
with:
46-
image_name: ${{ matrix.image_name }}
47-
extension: ${{ matrix.extension }}
4824
force: false
4925
secrets: inherit

.github/workflows/build-container.yaml

Lines changed: 6 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ on:
66
image_name:
77
required: true
88
type: string
9-
extension:
10-
required: true
11-
type: string
129
force:
1310
required: false
1411
type: boolean
@@ -35,16 +32,6 @@ jobs:
3532
- name: Checkout repository
3633
uses: actions/checkout@v4
3734

38-
- name: Identify multi-platform support
39-
id: platform
40-
shell: bash
41-
run: |
42-
if [ "${{ inputs.image_name }}" = "todo-list" ]; then
43-
echo "supports_multiplatform=false" >> $GITHUB_OUTPUT
44-
else
45-
echo "supports_multiplatform=true" >> $GITHUB_OUTPUT
46-
fi
47-
4835
- name: Pull the latest metadata.json
4936
if: inputs.image_name == 'awesome-copilot'
5037
shell: bash
@@ -84,7 +71,7 @@ jobs:
8471
git config --global user.name "GitHub Actions"
8572
git config --global user.email "[email protected]"
8673
87-
git add awesome-copilot/src/McpAwesomeCopilot.Common/metadata.json
74+
git add awesome-copilot/src/McpSamples.AwesomeCopilot.HybridApp/metadata.json
8875
8976
if git diff --staged --quiet; then
9077
echo "No changes to commit"
@@ -99,7 +86,7 @@ jobs:
9986
id: check-dockerfile
10087
shell: bash
10188
run: |
102-
if [ -f "${{ github.workspace }}/${{ inputs.image_name }}/Dockerfile.${{ inputs.extension }}" ]; then
89+
if [ -f "${{ github.workspace }}/Dockerfile.${{ inputs.image_name }}" ]; then
10390
echo "exists=true" >> $GITHUB_OUTPUT
10491
else
10592
echo "exists=false" >> $GITHUB_OUTPUT
@@ -133,51 +120,23 @@ jobs:
133120
- name: Build and push Docker image - multi-platform
134121
if: |
135122
(env.BUILD_IMAGE == 'true' || inputs.force == true) &&
136-
steps.check-dockerfile.outputs.exists == 'true' &&
137-
steps.platform.outputs.supports_multiplatform == 'true'
123+
steps.check-dockerfile.outputs.exists == 'true'
138124
id: push-multiplatform
139125
uses: docker/build-push-action@v6
140126
with:
141127
platforms: linux/amd64,linux/arm64
142128
push: true
143129
context: ${{ github.workspace }}/${{ inputs.image_name }}
144-
file: ${{ github.workspace }}/${{ inputs.image_name }}/Dockerfile.${{ inputs.extension }}
145-
tags: '${{ steps.meta.outputs.tags }},${{ env.REGISTRY }}/${{ env.REPOSITORY }}/${{ inputs.image_name }}:${{ inputs.extension }}'
130+
file: ${{ github.workspace }}/Dockerfile.${{ inputs.image_name }}
131+
tags: '${{ steps.meta.outputs.tags }},${{ env.REGISTRY }}/${{ env.REPOSITORY }}/${{ inputs.image_name }}:latest'
146132
labels: ${{ steps.meta.outputs.labels }}
147133

148134
- name: Generate artifact attestation - multi-platform
149135
if: |
150136
(env.BUILD_IMAGE == 'true' || inputs.force == true) &&
151-
steps.check-dockerfile.outputs.exists == 'true' &&
152-
steps.platform.outputs.supports_multiplatform == 'true'
137+
steps.check-dockerfile.outputs.exists == 'true'
153138
uses: actions/attest-build-provenance@v2
154139
with:
155140
subject-name: ${{ env.REGISTRY }}/${{ env.REPOSITORY }}/${{ inputs.image_name }}
156141
subject-digest: ${{ steps.push-multiplatform.outputs.digest }}
157142
push-to-registry: true
158-
159-
- name: Build and push Docker image - amd64 only
160-
if: |
161-
(env.BUILD_IMAGE == 'true' || inputs.force == true) &&
162-
steps.check-dockerfile.outputs.exists == 'true' &&
163-
steps.platform.outputs.supports_multiplatform == 'false'
164-
id: push-amd64
165-
uses: docker/build-push-action@v6
166-
with:
167-
platforms: linux/amd64
168-
push: true
169-
context: ${{ github.workspace }}/${{ inputs.image_name }}
170-
file: ${{ github.workspace }}/${{ inputs.image_name }}/Dockerfile.${{ inputs.extension }}
171-
tags: '${{ steps.meta.outputs.tags }},${{ env.REGISTRY }}/${{ env.REPOSITORY }}/${{ inputs.image_name }}:${{ inputs.extension }}'
172-
labels: ${{ steps.meta.outputs.labels }}
173-
174-
- name: Generate artifact attestation - amd64 only
175-
if: |
176-
(env.BUILD_IMAGE == 'true' || inputs.force == true) &&
177-
steps.check-dockerfile.outputs.exists == 'true' &&
178-
steps.platform.outputs.supports_multiplatform == 'false'
179-
uses: actions/attest-build-provenance@v2
180-
with:
181-
subject-name: ${{ env.REGISTRY }}/${{ env.REPOSITORY }}/${{ inputs.image_name }}
182-
subject-digest: ${{ steps.push-amd64.outputs.digest }}
183-
push-to-registry: true

.github/workflows/build.yaml

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ jobs:
1111

1212
strategy:
1313
matrix:
14-
image_name: [ 'markdown-to-html', 'todo-list' ]
15-
extension: [ 'stdio', 'http' ]
14+
image_name: [ 'awesome-copilot', 'markdown-to-html', 'todo-list' ]
1615

1716
permissions:
1817
contents: write
@@ -22,50 +21,5 @@ jobs:
2221

2322
with:
2423
image_name: ${{ matrix.image_name }}
25-
extension: ${{ matrix.extension }}
26-
force: true
27-
secrets: inherit
28-
29-
build-and-push-image-awesome-copilot-stdio:
30-
uses: ./.github/workflows/build-container.yaml
31-
32-
needs: build-and-push-image
33-
34-
strategy:
35-
matrix:
36-
image_name: [ 'awesome-copilot']
37-
extension: [ 'stdio' ]
38-
39-
permissions:
40-
contents: write
41-
packages: write
42-
attestations: write
43-
id-token: write
44-
45-
with:
46-
image_name: ${{ matrix.image_name }}
47-
extension: ${{ matrix.extension }}
48-
force: true
49-
secrets: inherit
50-
51-
build-and-push-image-awesome-copilot-http:
52-
uses: ./.github/workflows/build-container.yaml
53-
54-
needs: build-and-push-image-awesome-copilot-stdio
55-
56-
strategy:
57-
matrix:
58-
image_name: [ 'awesome-copilot' ]
59-
extension: [ 'http' ]
60-
61-
permissions:
62-
contents: write
63-
packages: write
64-
attestations: write
65-
id-token: write
66-
67-
with:
68-
image_name: ${{ matrix.image_name }}
69-
extension: ${{ matrix.extension }}
7024
force: true
7125
secrets: inherit

Dockerfile.awesome-copilot

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# syntax=docker/dockerfile:1
2+
3+
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
4+
5+
COPY ./shared/McpSamples.Shared /source/shared/McpSamples.Shared
6+
COPY ./awesome-copilot/src/McpSamples.AwesomeCopilot.HybridApp /source/awesome-copilot/src/McpSamples.AwesomeCopilot.HybridApp
7+
8+
WORKDIR /source/awesome-copilot/src/McpSamples.AwesomeCopilot.HybridApp
9+
10+
ARG TARGETARCH
11+
RUN case "$TARGETARCH" in \
12+
"amd64") RID="linux-musl-x64" ;; \
13+
"arm64") RID="linux-musl-arm64" ;; \
14+
*) RID="linux-musl-x64" ;; \
15+
esac && \
16+
dotnet publish -c Release -o /app -r $RID --self-contained false
17+
18+
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
19+
20+
WORKDIR /app
21+
22+
COPY --from=build /app .
23+
24+
USER $APP_UID
25+
26+
ENTRYPOINT ["dotnet", "McpSamples.AwesomeCopilot.HybridApp.dll"]

Dockerfile.awesome-copilot-azure

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# syntax=docker/dockerfile:1
2+
3+
FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
4+
5+
COPY ./shared/McpSamples.Shared /source/shared/McpSamples.Shared
6+
COPY ./awesome-copilot/src/McpSamples.AwesomeCopilot.HybridApp /source/awesome-copilot/src/McpSamples.AwesomeCopilot.HybridApp
7+
8+
WORKDIR /source/awesome-copilot/src/McpSamples.AwesomeCopilot.HybridApp
9+
10+
RUN dotnet publish -c Release -o /app --self-contained false
11+
12+
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
13+
14+
WORKDIR /app
15+
16+
COPY --from=build /app .
17+
18+
USER $APP_UID
19+
20+
ENTRYPOINT ["dotnet", "McpSamples.AwesomeCopilot.HybridApp.dll"]

Dockerfile.markdown-to-html

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# syntax=docker/dockerfile:1
2+
3+
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
4+
5+
COPY ./shared/McpSamples.Shared /source/shared/McpSamples.Shared
6+
COPY ./markdown-to-html/src/McpSamples.MarkdownToHtml.HybridApp /source/markdown-to-html/src/McpSamples.MarkdownToHtml.HybridApp
7+
8+
WORKDIR /source/markdown-to-html/src/McpSamples.MarkdownToHtml.HybridApp
9+
10+
ARG TARGETARCH
11+
RUN case "$TARGETARCH" in \
12+
"amd64") RID="linux-musl-x64" ;; \
13+
"arm64") RID="linux-musl-arm64" ;; \
14+
*) RID="linux-musl-x64" ;; \
15+
esac && \
16+
dotnet publish -c Release -o /app -r $RID --self-contained false
17+
18+
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
19+
20+
WORKDIR /app
21+
22+
COPY --from=build /app .
23+
24+
USER $APP_UID
25+
26+
ENTRYPOINT ["dotnet", "McpSamples.MarkdownToHtml.HybridApp.dll"]

Dockerfile.markdown-to-html-azure

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# syntax=docker/dockerfile:1
2+
3+
FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
4+
5+
COPY ./shared/McpSamples.Shared /source/shared/McpSamples.Shared
6+
COPY ./markdown-to-html/src/McpSamples.MarkdownToHtml.HybridApp /source/markdown-to-html/src/McpSamples.MarkdownToHtml.HybridApp
7+
8+
WORKDIR /source/markdown-to-html/src/McpSamples.MarkdownToHtml.HybridApp
9+
10+
RUN dotnet publish -c Release -o /app --self-contained false
11+
12+
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
13+
14+
WORKDIR /app
15+
16+
COPY --from=build /app .
17+
18+
USER $APP_UID
19+
20+
ENTRYPOINT ["dotnet", "McpSamples.MarkdownToHtml.HybridApp.dll"]

Dockerfile.todo-list

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# syntax=docker/dockerfile:1
2+
3+
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
4+
5+
COPY ./shared/McpSamples.Shared /source/shared/McpSamples.Shared
6+
COPY ./todo-list/src/McpSamples.TodoList.HybridApp /source/todo-list/src/McpSamples.TodoList.HybridApp
7+
8+
WORKDIR /source/todo-list/src/McpSamples.TodoList.HybridApp
9+
10+
ARG TARGETARCH
11+
RUN case "$TARGETARCH" in \
12+
"amd64") RID="linux-musl-x64" ;; \
13+
"arm64") RID="linux-musl-arm64" ;; \
14+
*) RID="linux-musl-x64" ;; \
15+
esac && \
16+
dotnet publish -c Release -o /app -r $RID --self-contained false
17+
18+
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
19+
20+
WORKDIR /app
21+
22+
COPY --from=build /app .
23+
24+
USER $APP_UID
25+
26+
ENTRYPOINT ["dotnet", "McpSamples.TodoList.HybridApp.dll"]

Dockerfile.todo-list-azure

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# syntax=docker/dockerfile:1
2+
3+
FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
4+
5+
COPY ./shared/McpSamples.Shared /source/shared/McpSamples.Shared
6+
COPY ./todo-list/src/McpSamples.TodoList.HybridApp /source/todo-list/src/McpSamples.TodoList.HybridApp
7+
8+
WORKDIR /source/todo-list/src/McpSamples.TodoList.HybridApp
9+
10+
RUN dotnet publish -c Release -o /app --self-contained false
11+
12+
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
13+
14+
WORKDIR /app
15+
16+
COPY --from=build /app .
17+
18+
USER $APP_UID
19+
20+
ENTRYPOINT ["dotnet", "McpSamples.TodoList.HybridApp.dll"]

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ This repository contains .NET samples ranging from building your own MCP impleme
1919

2020
## 📋 Sample Projects
2121

22-
| Sample Name | Description |
23-
|-------------|-------------|
24-
| [Awesome Copilot](./awesome-copilot/) | MCP servers that retrieve GitHub Copilot customization files from [awesome-copilot](https://github.com/github/awesome-copilot). |
25-
| [Markdown to HTML](./markdown-to-html/) | MCP servers that convert markdown text to HTML. |
26-
| [To-do List](./todo-list/) | MCP servers that manage to-do list items. |
22+
| Sample Name | Install | Description |
23+
|-----------------------------------------|---------|-------------------------------------------------------------------------------------------------------------------------|
24+
| [Awesome Copilot](./awesome-copilot/) | [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%7B%22name%22%3A%22awesome-copilot%22%2C%22gallery%22%3Afalse%2C%22command%22%3A%22docker%22%2C%22args%22%3A%5B%22run%22%2C%22-i%22%2C%22--rm%22%2C%22ghcr.io%2Fmicrosoft%2Fmcp-dotnet-samples%2Fawesome-copilot%3Alatest%22%5D%7D)<br/>[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect?url=vscode-insiders%3Amcp%2Finstall%3F%7B%22name%22%3A%22awesome-copilot%22%2C%22gallery%22%3Afalse%2C%22command%22%3A%22docker%22%2C%22args%22%3A%5B%22run%22%2C%22-i%22%2C%22--rm%22%2C%22ghcr.io%2Fmicrosoft%2Fmcp-dotnet-samples%2Fawesome-copilot%3Alatest%22%5D%7D) | MCP servers that retrieve GitHub Copilot customization files from [awesome-copilot](https://github.com/github/awesome-copilot). |
25+
| [Markdown to HTML](./markdown-to-html/) | [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%7B%22name%22%3A%22markdown-to-html%22%2C%22gallery%22%3Afalse%2C%22command%22%3A%22docker%22%2C%22args%22%3A%5B%22run%22%2C%22-i%22%2C%22--rm%22%2C%22ghcr.io%2Fmicrosoft%2Fmcp-dotnet-samples%2Fmarkdown-to-html%3Alatest%22%5D%7D)<br/>[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect?url=vscode-insiders%3Amcp%2Finstall%3F%7B%22name%22%3A%22markdown-to-html%22%2C%22gallery%22%3Afalse%2C%22command%22%3A%22docker%22%2C%22args%22%3A%5B%22run%22%2C%22-i%22%2C%22--rm%22%2C%22ghcr.io%2Fmicrosoft%2Fmcp-dotnet-samples%2Fmarkdown-to-html%3Alatest%22%5D%7D) | MCP servers that convert markdown text to HTML. |
26+
| [To-do List](./todo-list/) | [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%7B%22name%22%3A%22todo-list%22%2C%22gallery%22%3Afalse%2C%22command%22%3A%22docker%22%2C%22args%22%3A%5B%22run%22%2C%22-i%22%2C%22--rm%22%2C%22ghcr.io%2Fmicrosoft%2Fmcp-dotnet-samples%2Ftodo-list%3Alatest%22%5D%7D)<br/>[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect?url=vscode-insiders%3Amcp%2Finstall%3F%7B%22name%22%3A%22todo-list%22%2C%22gallery%22%3Afalse%2C%22command%22%3A%22docker%22%2C%22args%22%3A%5B%22run%22%2C%22-i%22%2C%22--rm%22%2C%22ghcr.io%2Fmicrosoft%2Fmcp-dotnet-samples%2Ftodo-list%3Alatest%22%5D%7D) | MCP servers that manage to-do list items. |
2727

2828
## 🛠️ Getting Started
2929

0 commit comments

Comments
 (0)