Install
$ agentstack add mcp-deors-workshop-mcp-servers ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
workshop-mcp-servers
Workshop about MCP Servers: from building simple MCP Servers to advanced use cases for interacting and provisioning development platform resources in a self-service way. The workshop features extensively the MCP capabilities within Visual Studio Code provided by the GitHub Copilot extension, and it will use GitHub as the development platform of reference for the examples (although the same principles apply to other platforms or services).
This workshop was first delivered at OpenSlava 2025 with roots in a previous talk at OpenSouthCode 2025 along with Sergio Valverde @svg153.
Introduction and general instructions
The workshop is structured around several activities of growing complexity. To support the learning process, to simplify the deployment & execution of the several MCP servers, and to allow everyone to follow the content at their own pace at home, the solution to every workshop activity will be available in a different folder.
The recommended approach is to follow the instructions in this ``README.md`` file, and rely on the solutions available in each solution folder in case of doubts or when stuck in a given step.
Workshop pre-requisites
These are the pre-requisites for the workshop:
- Visual Studio Code
- GitHub account with GitHub Copilot enabled -- free tier is more than enough for this workshop
- Node.js 24
- Java 25
The rest of stuff, such as dependencies and tools, will be downloaded automatically during the workshop and as they will not require significant bandwidth, you don't need to prepare in advance.
We will be running MCP servers locally, so there is no need to have a cloud account or any other infrastructure/platform ready to host them. However, you are more than welcome to try them out with any platform of your choice.
The workshop can be done without Visual Studio Code, a GitHub account or GitHub Copilot enabled. You may use any other IDE and MCP client of your choice but take into consideration that they might not be supported during the workshop (that is, community-based support based on workshop participants' know-how).
Workshop agenda
This is the agenda for the workshop:
- [workshop-mcp-servers](#workshop-mcp-servers)
- [Introduction and general instructions](#introduction-and-general-instructions)
- [Workshop pre-requisites](#workshop-pre-requisites)
- [Workshop agenda](#workshop-agenda)
- [Workshop activities](#workshop-activities)
- [1. Introduction to Model Context Protocol (MCP)](#1-introduction-to-model-context-protocol-mcp)
- [2. How to leverage MCP to interface with an internal developer platform (IDP)](#2-how-to-leverage-mcp-to-interface-with-an-internal-developer-platform-idp)
- [3. MCP server "Hello World"](#3-mcp-server-hello-world)
- [3.1. Node.js with Typescript and stdio](#31-nodejs-with-typescript-and-stdio)
- [3.1.1. Testing through the command line](#311-testing-through-the-command-line)
- [3.2. Node.js with Typescript and Streamable HTTP](#32-nodejs-with-typescript-and-streamable-http)
- [3.2.1. Testing through the command line](#321-testing-through-the-command-line)
- [3.3. Java with Spring AI and stdio](#33-java-with-spring-ai-and-stdio)
- [3.3.1. Testing considerations](#331-testing-considerations)
- [3.3.2. Testing with MCP Inspector](#332-testing-with-mcp-inspector)
- [3.3.3. Testing with Visual Studio Code and GitHub Copilot extension](#333-testing-with-visual-studio-code-and-github-copilot-extension)
- [3.4. Java with Spring AI and Streamable HTTP](#34-java-with-spring-ai-and-streamable-http)
- [3.4.1. Testing with MCP Inspector](#341-testing-with-mcp-inspector)
- [3.4.2. Testing with Visual Studio Code and GitHub Copilot extension](#342-testing-with-visual-studio-code-and-github-copilot-extension)
- [3.5. Java with Quarkus and stdio](#35-java-with-quarkus-and-stdio)
- [3.6. Java with Quarkus and Streamable HTTP](#36-java-with-quarkus-and-streamable-http)
- [4. MCP server integrating with GitHub](#4-mcp-server-integrating-with-github)
- [4.1. MCP server for GitHub repos](#41-mcp-server-for-github-repos)
- [4.2. MCP server for GitHub issues](#42-mcp-server-for-github-issues)
- [5. Final words](#5-final-words)
Workshop activities
1. Introduction to Model Context Protocol (MCP)
See the diagram below and pay attention to the live presentation:
Three main takeaways:
- MCP Client: The component that has the conversational and agentic capabilities to infer user's intentions and connect with the right tool through the corresponding MCP Server.
- MCP Server: The component that implements tool integrations. It is strongly recommended to follow the 'dumb pipes and smart endpoints' design rule and design MCP servers to be as simple and lightweight as possible.
- Tool: The actual tool that we want to interface with. Typically, this is an API to encapsulate fine-grained services within the target tool/platform (that is, a 'smart endpoint').
2. How to leverage MCP to interface with an internal developer platform (IDP)
See the diagram below and pay attention to the live presentation:
Three main takeaways:
- An Internal Developer Platform is more than "just a portal".
- Platform interfaces are multiple: web, command line, a public API, and, of course, a virtual assistant powered by MCP.
- Every interface exposes and simplifies access to the actual platform services, enabling self-service capabilities (which is a key principle of developer experience) in a consistent way.
See the next diagram and continue attending the live presentation:
Three main takeaways:
- Consistency of the experience is key to DevEx. Do not aim for case-by-case integrations. They may seem simpler at the beginning, but sooner rather than later you will have diverging experiences, duplicated code for the same use cases, more maintenance/operation effort, and a more complex security setup, e.g., for roles and access management.
- Expose the self-service capabilities offered to end users as high-level/edge services. Those edge services will orchestrate all the steps needed to complete the user request.
- I strongly recommend to adopt an IssueOps approach. It is just a bit more effort but it will provide another self-service channel, as well as strong traceability and auditing about who asks and who approves this or that.
3. MCP server "Hello World"
There are two possible transports to configure our MCP servers:
- stdio: The MCP server runs in a local process using stdio/stdout to exchange information.
- Streamable HTTP: The latest model suitable for larger scale deployments, can be executed locally or remotely, and can work with multiple clients simultaneously.
There is a third method now deprecated, SSE or server-sent events. The Internet is full of examples of this transport method so be aware of its state before going on.
We are going to build MCP servers with two tools:
- greetPerson: This tool will just respond with a string greeting a person. The name of the person is an input parameter for the tool.
- greetAudience: This tool will respond with a message suitable to greet the whole audience of a given event. The tool parameters are the event name and its location.
The provided solutions are built in Node.js/Typescript, Java/Spring AI, and Java/Quarkus. The code for the MCP servers are located in these folders:
- ``
01-typescript-mcp-hello-world-stdio``: The "Hello World" MCP server with stdio transport built with Node.js/Typescript. - ``
02-typescript-mcp-hello-world-streamable``: The "Hello World" MCP server with Streamable HTTP transport built with Node.js/Typescript. - ``
03-spring-mcp-hello-world-stdio``: The "Hello World" MCP server with stdio transport built with Java/Spring AI. - ``
04-spring-mcp-hello-world-streamable``: The "Hello World" MCP server with Streamable HTTP transport built with Java/Spring AI. - ``
05-quarkus-mcp-hello-world-stdio``: The "Hello World" MCP server with stdio transport built with Java/Quarkus. - ``
06-quarkus-mcp-hello-world-streamable``: The "Hello World" MCP server with Streamable HTTP transport built with Java/Quarkus.
Testing the MCP servers can be done through multiple channels:
- Through the command line. This is the most direct way as it goes directly to the MCP API and not through the MCP client conversational interface.
- With MCP Inspector. See the official guide here: MCP Inspector
- With Visual Studio Code built-in MCP client provided by the GitHub Copilot extension working in Agent mode.
- With any available MCP client. See a very exhaustive list here: MCP Example Clients
Throughout the workshop we will experiment with those channels.
3.1. Node.js with Typescript and stdio
These are the basic steps that you need to complete the MCP server:
Add this dependency to your ``package.json`` file:
"@modelcontextprotocol/sdk": "^1.19.1"
Import the ``McpServer` and `StdioServerTransport`` types:
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
Import ``zod`` that is used to declare the input parameters:
import { z } from 'zod';
Instantiate the MCP server:
const server = new McpServer({
name: 'Hello World MCP Server (stdio version)',
description: 'A simple MCP server that provides greeting functionalities through stdio.',
version: '1.0.0',
transport: 'stdio'
});
Register the tools:
server.tool(
,
,
,
async ({}) => {
}
);
And, finally, launch the server:
server.connect(new StdioServerTransport()).then(() => {
console.log('MCP server is now running and listening for requests.');
}).catch((err) => {
console.error('Error starting MCP server:', err);
});
Now, let's compile and run the MCP server:
npm run build
node dist/mcp-server.js
3.1.1. Testing through the command line
Now that the MCP server is running, it is time to test it.
The simpler way to test our MCP server with stdio transport is to type the request in Json format in the terminal running the server and looking at the responses in that terminal. These are three key examples for your reference.
To list the tools available in the server:
{"jsonrpc":"2.0","id":"1","method":"tools/list","params":{}}
To greet a person named Alice:
{"jsonrpc":"2.0","id":"2","method":"tools/call","params":{"name":"greetPerson","arguments":{"name":"Alice"}}}
To greet the audience at OpenSlava event in Bratislava:
{"jsonrpc":"2.0","id":"3","method":"tools/call","params":{"name":"greetAudience","arguments":{"event":"OpenSlava","location":"Bratislava"}}}
3.2. Node.js with Typescript and Streamable HTTP
Creating an HTTP-based MCP server is quite more complex without a framework. The full specification requires a lot of different events to be properly handled by our MCP server code. For the sake of simplicity and time, we will create just the most basic possible server, with a minimal subset of the specification, but enough for learning how this works.
To start, let's add Express dependency in addition to MCP:
"express": "^5.1.0"
The server is declared as we learned in the stdio example just by adding the transport type (to be referenced later) and setting the correct transport type:
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
...
const server = new McpServer({
name: 'Hello World MCP Server (streamable version)',
description: 'A simple MCP server that provides greeting functionalities through streamable HTTP.',
version: '1.0.0',
transport: 'streamable-http'
});
Tools are declared exactly the same.
With Express, we will register a couple of routes:
- ``
POST`to the MCP endpoint (e.g.`/stream`or`/mcp``): This will handle session initialization requests and tool calls. - ``
DELETE`to the MCP endpoint (e.g.`/stream`or`/mcp``): This will handle session termination requests. - ``
GET`` is not used by Streamable HTTP but by the deprecated SSE transport. Some servers implement this for compatibility as not all MCP clients are upgraded yet.
To keep track of what's going on with active sessions with our MCP clients, you use an in-memory event store provided by the MCP library:
import { InMemoryEventStore } from "@modelcontextprotocol/sdk/examples/shared/inMemoryEventStore.js";
The high-level workflow is, therefore:
- If session id is not informed in ``
mcp-session-id`` header, the conversation was not initialized properly and the server shall reject the request. - If the session id is not known, start a new session transport and initialize the in-memory event store for that session.
- When the session is terminated, all in-memory resources are freed.
As with any Express application, the server is started listening in a given port:
const expressServer = app.listen(PORT, () => { ...
It is also recommended to capture the ``SIGINT`` event to cleanup resources on server termination:
process.on("SIGINT", async () => { ...
Now you have two paths: try to get the MCP server right on your own with the notes above, or just go grab the code understanding why it does what it does.
However, in real life you would probably go for a higher-level framework such as FastMCP to not have to deal with all the "plumbling".
For more information about the FastMCP framework you can visit its page here: FastMCP at GitHub.com
3.2.1. Testing through the command line
Now that the MCP server is running, it is time to test it. In this case we will be using ``curl`` commands.
We will add some additional complexity to work as a real MCP client would do. First, we must send an initialization request to prepare a session with the MCP server. We will take note of the generated session id and that session id will be reused to process the subsequent tool requests.
To send the initialization request:
curl -X POST http://localhost:3000/stream \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream, application/json" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {
"sampling": {},
"elicitation": {}
},
"clientInfo": {
"name": "my terminal",
"version": "N/A"
}
}
}'
The session id can be located in the MCP server process terminal.
Next, to list the available tools:
curl -X POST http://localhost:3000/stream \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream, application/json" \
-H "mcp-session-id: " \
-d '{
"jsonrpc": "2.0",
"id": "2",
"method": "tools/list",
"params": {}
}'
To greet a person named Alice:
curl -X POST http://localhost:3000/stream \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream, application/json" \
-H "mcp-session-id: " \
-d '{
"jsonrpc": "2.0",
"id": "3",
"method": "tools/call",
"params": {
"name": "greetPerson",
"arguments": {
"name": "Alice"
}
}
}'
To greet the audience at OpenSlava event in Bratislava:
curl -X POST http://localhost:3000/stream \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream, application/json" \
-H "mcp-session-id: " \
-d '{
"jsonrpc": "2.0",
"id": "4",
"method": "tools/call",
"params": {
"name": "greetAudience",
"arguments": {
"event": "OpenSlava",
"location": "Bratislava"
}
}
}'
And, finally, it
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: deors
- Source: deors/workshop-mcp-servers
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.