Today we will do two things: look at webservers and try and make MCP servers. In both cases, we are going to leverage a bit older of a language, COBOL.
Lets get started with Web Servers first...
WebBol
Let's start by cloning down Webbol
builder@DESKTOP-QADGF36:~/Workspaces$ git clone https://github.com/jmsdnns/webbol.git
Cloning into 'webbol'...
remote: Enumerating objects: 41, done.
remote: Counting objects: 100% (41/41), done.
remote: Compressing objects: 100% (28/28), done.
remote: Total 41 (delta 20), reused 34 (delta 13), pack-reused 0 (from 0)
Receiving objects: 100% (41/41), 20.16 KiB | 607.00 KiB/s, done.
Resolving deltas: 100% (20/20), done.
builder@DESKTOP-QADGF36:~/Workspaces$ cd webbol/
Next, I'll need the GNU Cobol compiler, if I don't already have it
$ sudo apt-get install gnucobol
[sudo] password for builder:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
gnucobol is already the newest version (5).
We can now make to compile the app
builder@DESKTOP-QADGF36:~/Workspaces/webbol$ make
cobc -free -c -I. path-utils.cbl
cobc -free -c -I. mime-types.cbl
cobc -free -c -I. file-ops.cbl
cobc -free -c -I. http-handler.cbl
cobc -free -c -I. url-decode.cbl
cobc -free -x -I. webserver.cbl path-utils.o mime-types.o file-ops.o http-handler.o url-decode.o -o webserver
I need something to serve up
$ echo "<HTML><HEAD><TITLE>CobolFTW</TITLE></HEAD><BODY><H1>Hello w0rld</H1></BODY></HTML>" > index.html
I can now fire up webserver to host a web server on port 8080
builder@DESKTOP-QADGF36:~/Workspaces/webbol$ ./webserver
COBOL Web Server Starting...
Press Ctrl+C to stop
Server listening on port 08080
The first time it hung on me and didn't return (then crashed). But the second time it worked
$ ./webserver
COBOL Web Server Starting...
Press Ctrl+C to stop
Server listening on port 08080
Request #00000002:
GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.9
Request #00000004:
GET /favicon.ico HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
sec-ch-ua-platform: "Windows"
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36
sec-ch-ua: "Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"
sec-ch-ua-mobile: ?0
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: image
Referer: http://127.0.0.1:8080/
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.9
MCP server
I made a modified version that should serve up a basic Hello World MCP server
builder@LuiGi:~/Workspaces$ git clone https://github.com/idjohnson/webbol-mcp.git
Cloning into 'webbol-mcp'...
remote: Enumerating objects: 47, done.
remote: Counting objects: 100% (47/47), done.
remote: Compressing objects: 100% (33/33), done.
remote: Total 47 (delta 24), reused 37 (delta 14), pack-reused 0 (from 0)
Receiving objects: 100% (47/47), 23.79 KiB | 11.90 MiB/s, done.
Resolving deltas: 100% (24/24), done.
builder@LuiGi:~/Workspaces$ cd webbol-mcp/
builder@LuiGi:~/Workspaces/webbol-mcp$ make
cobc -free -c -I. path-utils.cbl
<command-line>: warning: ‘_FORTIFY_SOURCE’ redefined
<command-line>: note: this is the location of the previous definition
cobc -free -c -I. mime-types.cbl
<command-line>: warning: ‘_FORTIFY_SOURCE’ redefined
<command-line>: note: this is the location of the previous definition
cobc -free -c -I. file-ops.cbl
<command-line>: warning: ‘_FORTIFY_SOURCE’ redefined
<command-line>: note: this is the location of the previous definition
cobc -free -c -I. http-handler.cbl
<command-line>: warning: ‘_FORTIFY_SOURCE’ redefined
<command-line>: note: this is the location of the previous definition
cobc -free -c -I. url-decode.cbl
<command-line>: warning: ‘_FORTIFY_SOURCE’ redefined
<command-line>: note: this is the location of the previous definition
cobc -free -x -I. webserver.cbl path-utils.o mime-types.o file-ops.o http-handler.o url-decode.o -o webserver
<command-line>: warning: ‘_FORTIFY_SOURCE’ redefined
<command-line>: note: this is the location of the previous definition
On the side, I'll launch the MCP inspector
$ nvm use lts/jod
Now using node v22.20.0 (npm v10.9.3)
$ npx @modelcontextprotocol/inspector
Need to install the following packages:
@modelcontextprotocol/inspector@0.18.0
Ok to proceed? (y) Y
Starting MCP inspector...
⚙️ Proxy server listening on localhost:6277
🔑 Session token: dffd143fd8859f9173dd18cbdcd226d212ad330c48e025076ef0e2aaf8388982
Use this token to authenticate requests or set DANGEROUSLY_OMIT_AUTH=true to disable auth
🚀 MCP Inspector is up and running at:
http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=dffd143fd8859f9173dd18cbdcd226d212ad330c48e025076ef0e2aaf8388982
🌐 Opening browser...
Now I can fire up the webserver
builder@LuiGi:~/Workspaces/webbol-mcp$ ./webserver
COBOL Web Server Starting...
Press Ctrl+C to stop
Server listening on port 08080
I'll be straight - I worked on this for a while. It just is not consistent, at least with HTTPStreamable.
It times out and I think a lot of the issue is how it reads files into memory.
I did stash my work in idjohnson/webbol-mcp in case I (or anyone else) wanted to take back over the charge.
Try two
I hammered at this for another full day. I started to think the method of modifying an existing Cobol repo might be a fools errand. I decided to switch to Aggy and use a requirements driven approach.
I created a requirements.md:
# Goal
Create a basic hello world MCP server using COBOL. It can be httpStreamable or STDIO.
# Requirements
It must:
- Have a Dockerfile and work in a container
- Have all documentation to build and run stored in a README.md
- compile with gnucobol (cobc)
- Create a "test.sh" script that can launch a built docker container on port 8089 and test it for proper MCP server JSON responses
# Examples to use
A Web Server in gnucobol: https://github.com/jmsdnns/webbol.git
A python MCP server: https://github.com/jlowin/fastmcp.git
- hint: review Python implementation and convert to COBOL
MCP Transport documentation with requirements:
- https://modelcontextprotocol.io/specification/2025-03-26/basic/transports
- https://mcp-framework.com/docs/Transports/http-stream-transport/
# MCP Requirements
Should handle connect session (often a POST to / or /mcp)
Should advertise a "hello world" tool that takes in a single string
Tool should then reply with "hello $string"
I fired this up in Aggy
And asked it to execute to plan
My thought is that if I give it some creative freedom and stick to my requirements, it might come up with a better idea that I was using.
I might argue, this is true in life too - if you get a really smart Junior or intern (or take that for wherever you are in your stage of life) and just order them to build to your preconceived ideas, you might not get the best product. But if you step back and just say your rules and requirements, you might be surprised with what they come up with.
Here is my first real gripe with Aggy.. It was launched with WSL but keeps trying to do things with windows. So it's vomiting on powershell.
That said, it did at first get hung up on a used port (so i moved it to 8090) then I noticed it started to really do as I asked (use Docker)
This really worked, it took plenty of time, so I'm curious what my end cost might be, but it wrote and tested things over and over, which I really respect
It took a long while, I wont lie. We tag teamed, Aggy and Me, and finally sorted out all the issues. It was a pointless waste of tokens, but I had to give it some kudos
Some proof it works:
With a few more tweaks, it works in Copilot:
and Gemini CLI
Now remember, sharing is caring, so let's push out a copy for everyone
builder@DESKTOP-QADGF36:~/Workspaces/cobolMcp$ docker build -t idjohnson/cobolmcp:latest .
[+] Building 0.8s (11/11) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 339B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:22.04 0.7s
=> [auth] library/ubuntu:pull token for registry-1.docker.io 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/5] FROM docker.io/library/ubuntu:22.04@sha256:104ae83764a5119017b8e8d6218fa0832b09df65aae7d5a6de29a85d813da2fb 0.0s
=> => resolve docker.io/library/ubuntu:22.04@sha256:104ae83764a5119017b8e8d6218fa0832b09df65aae7d5a6de29a85d813da2fb 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 207B 0.0s
=> CACHED [2/5] RUN apt-get update && apt-get install -y gnucobol gcc make && rm -rf /var/lib/apt/lists/* 0.0s
=> CACHED [3/5] WORKDIR /app 0.0s
=> CACHED [4/5] COPY cobol-mcp/ . 0.0s
=> CACHED [5/5] RUN cobc -x -free -o mcp-server server.cbl mcp-handler.cbl 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:734f9f3b089300a14e209f28fba72c0377bde45943755268eeacfc898ec8420c 0.0s
=> => naming to docker.io/idjohnson/cobolmcp:latest 0.0s
builder@DESKTOP-QADGF36:~/Workspaces/cobolMcp$ docker push idjohnson/cobolmcp:latest
The push refers to repository [docker.io/idjohnson/cobolmcp]
8d40b8071372: Pushed
ceee004e5fea: Pushed
b97bff40fbbf: Pushed
3d4ff65c54e8: Pushed
73974f74b436: Mounted from library/ubuntu
latest: digest: sha256:487e0a0e7f057941d3a6c5ea4309f13e9c3e06273cc98a90178e8b444a5762a1 size: 1364
I really want to use Gemini CLI extensions, but the Docker ones seem limited to STDIO.
I initially tried cheating and firing up an MCP server in docker then trying to use it locally, but it timed out
$ cat gemini-extension.json
{
"name": "cobolMCP",
"version": "1.0.0",
"mcpServers": {
"startdocker": {
"command": "docker",
"args": [
"run",
"--rm",
"-p",
"8090:8090",
"idjohnson/cobolmcp:latest"
]
},
"usedocker": {
"httpUrl": "http://localhost:8090",
"timeout": 5000
}
}
}
I pivoted instead to firing up a Helm chart with an ingress.
I needed to fire up a new A record in GCP CloudDNS
$ gcloud dns --project=myanthosproject2 record-sets create cobolmcp.steeped.icu --zone="steeped
icu" --type="A" --ttl="300" --rrdatas="174.53.161.33"
NAME TYPE TTL DATA
cobolmcp.steeped.icu. A 300 174.53.161.33
Now with some local values
# Default values for cobolmcp.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: idjohnson/cobolmcp
pullPolicy: Always
# Overrides the image tag whose default is the chart appVersion.
tag: "latest"
ingress:
enabled: true
className: "nginx"
annotations:
cert-manager.io/cluster-issuer: gcpleprod2
ingress.kubernetes.io/proxy-body-size: "0"
ingress.kubernetes.io/ssl-redirect: "true"
kubernetes.io/tls-acme: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
hosts:
- host: cobolmcp.steeped.icu
paths:
- path: /
pathType: ImplementationSpecific
tls:
- secretName: cobolmcpgcp-tls
hosts:
- cobolmcp.steeped.icu
resources:
limits:
cpu: 250m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
autoscaling:
enabled: true
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 80
I could install the chart:
$ helm install cobolmcp -f ./values.yaml ./charts/cobolmcp
NAME: cobolmcp
LAST DEPLOYED: Thu Jan 8 14:08:20 2026
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
https://cobolmcp.steeped.icu/
I had to rework my liveness and readiness probes to use TCP over HTTP, but I managed to get a container up
$ kubectl get po -l app.kubernetes.io/instance=cobolmcp
NAME READY STATUS RESTARTS AGE
cobolmcp-5bd67cdf8b-49ww4 1/1 Running 0 34s
And the cert was satisified
$ kubectl get cert | grep mc
cobolmcpgcp-tls True cobolmcpgcp-tls 8m34s
Now to test
builder@DESKTOP-QADGF36:~/Workspaces/cobolMcp$ cat gemini-extension.json
{
"name": "cobolMCP",
"version": "1.0.0",
"mcpServers": {
"cobolMCP": {
"httpUrl": "https://cobolmcp.steeped.icu",
"timeout": 5000
}
}
}
builder@DESKTOP-QADGF36:~/Workspaces/cobolMcp$ gemini extensions link /home/builder/Workspaces/cobolMcp/
Installing extension "cobolMCP".
**The extension you are about to install may have been created by a third-party developer and sourced from a public repository. Google does not vet, endorse, or guarantee the functionality or security of extensions. Please carefully inspect any extension and its source code before installing to understand the permissions it requires and the actions it may perform.**
This extension will run the following MCP servers:
* cobolMCP (remote): https://cobolmcp.steeped.icu
Do you want to continue? [Y/n]: Y
Extension "cobolMCP" linked successfully and enabled.
I can now see that this uses an external server
$ gemini mcp list
Configured MCP servers:
✗ cobolMCP (from cobolMCP): https://cobolmcp.steeped.icu (http) - Disconnected
✗ forgejomcp (from forgejomcp): https://nodejsmcp-511842454269.us-east1.run.app/mcp (http) - Disconnected
✓ nanobanana (from nanobanana): node /home/builder/.gemini/extensions/nanobanana/mcp-server/dist/index.js (stdio) - Connected
✓ nodeServer (from vikunja): docker run -i --rm -e VIKUNJA_URL -e VIKUNJA_USERNAME -e VIKUNJA_PASSWORD harbor.freshbrewed.science/library/vikunjamcp:0.26 (stdio) - Connected
Let's now test it
I now have the code published into https://github.com/idjohnson/cobolMCP.
After a while, even saw it show up on the Gemini CLI Extensions Library
which will show how to install it if you click on the entry
Summary
We started with Webbol which was easy to Dockerize and run in a container. I, at first, spent way too much time trying to modify it into a working MCP server and ultimately gave up (but stashed my branch in a GIT repo https://github.com/idjohnson/webbol-mcp.git).
I moved on to starting fresh and between Gemini CLI and Antigravity (which i call Aggy), and a whole lot of testing and debugging, we built out cobolMCP. It has a Helm chart, a running service in my K8s, a Gemini CLI extension and a public docker image. I call that a win.












Top comments (0)