Published: Feb 5, 2026 by Isaac Johnson
I came across a PowerPoint MCP Server recently and really wanted an excuse to try it out. I often have to make presentations and was curious if this MCP server could help with that initial blank page draft.
In testing, I found the GenAI maps were less than stellar, so I also went down the path of figuring out how to pair the PowerPoint MCP (or PowerPoints without MCP) with a Google Maps MCP server. To be clear, the official Google provided Google Maps MCP is just for building apps with Google Maps API integration, so I needed to find someone else’s that would give images of proper maps.
PowerPoint MCP Server
Let’s check out this PowerPoint MCP Server
Let’s build and push a container we can use
builder@DESKTOP-QADGF36:~/Workspaces/Office-PowerPoint-MCP-Server$ docker build -t idjohnson/pptmcpserver:0.1 .
[+] Building 32.6s (11/11) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 551B 0.0s
=> [internal] load metadata for docker.io/library/python:3.10-alpine 1.6s
=> [auth] library/python:pull token for registry-1.docker.io 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build context 1.5s
=> => transferring context: 141.40MB 1.5s
=> [1/5] FROM docker.io/library/python:3.10-alpine@sha256:ea547229518fa221a8d60b1adc91e47e3221a6373a71a91f06ed1d7fa34c401 3.7s
=> => resolve docker.io/library/python:3.10-alpine@sha256:ea547229518fa221a8d60b1adc91e47e3221a6373a71a91f06ed1d7fa34c401 0.0s
=> => sha256:ea547229518fa221a8d60b1adc91e47e3221a6373a71a91f06ed1d7fa34c401a 10.30kB / 10.30kB 0.0s
=> => sha256:b29a9f3cd1815a09c0119313057233ba1cf5f53ea4503a9354869633f8b70235 1.74kB / 1.74kB 0.0s
=> => sha256:e1a246636f1712461df9af7c2f088eb3dc4ddd77f26c7a888de0cfcd41baa7b9 5.27kB / 5.27kB 0.0s
=> => sha256:589002ba0eaed121a1dbf42f6648f29e5be55d5c8a6ee0f8eaa0285cc21ac153 3.86MB / 3.86MB 0.5s
=> => sha256:f90604e94319d348761f865d4a16561f5ae3d93fea7ea068ca6f215a3baf936a 460.94kB / 460.94kB 0.4s
=> => sha256:633bde9d174c0a50e324b145dadf7364656fc3d96a15511d45b33bdb00f4616d 15.45MB / 15.45MB 1.3s
=> => sha256:c4918ce376411d4c0d81192a4dee977ad7f1ea463ba710c0bcec061aa5062c40 247B / 247B 0.6s
=> => extracting sha256:589002ba0eaed121a1dbf42f6648f29e5be55d5c8a6ee0f8eaa0285cc21ac153 0.4s
=> => extracting sha256:f90604e94319d348761f865d4a16561f5ae3d93fea7ea068ca6f215a3baf936a 0.4s
=> => extracting sha256:633bde9d174c0a50e324b145dadf7364656fc3d96a15511d45b33bdb00f4616d 1.7s
=> => extracting sha256:c4918ce376411d4c0d81192a4dee977ad7f1ea463ba710c0bcec061aa5062c40 0.0s
=> [2/5] RUN apk add --no-cache gcc musl-dev libffi-dev 5.9s
=> [3/5] WORKDIR /app 0.0s
=> [4/5] COPY . . 0.6s
=> [5/5] RUN pip install --no-cache-dir -r requirements.txt 19.7s
=> exporting to image 0.9s
=> => exporting layers 0.9s
=> => writing image sha256:6847fef3928c9800c342290d0a8706eaf9c9ce6550bdb80ea57c8ba4b4c5ca33 0.0s
=> => naming to docker.io/idjohnson/pptmcpserver:0.1 0.0s
builder@DESKTOP-QADGF36:~/Workspaces/Office-PowerPoint-MCP-Server$ docker push idjohnson/pptmcpserver:0.1
The push refers to repository [docker.io/idjohnson/pptmcpserver]
cd9901b53b26: Pushed
40b78d12bf40: Pushed
29e424eded7f: Pushed
1348eae8b714: Pushed
0523c6a9aa9d: Mounted from library/python
af4a4dfb8ce7: Mounted from library/python
a93991383927: Mounted from library/python
989e799e6349: Mounted from library/python
0.1: digest: sha256:29759fffad0cdd8f884b72771230c609e2eb4894e9f8bde3d6faa6a5a0e630fa size: 2000
I created a gemini-extension.json file
builder@DESKTOP-QADGF36:~/Workspaces/Office-PowerPoint-MCP-Server$ cat ./gemini-extension.json
{
"extensionId": "powerpointmcp",
"name": "PowerpointMCP",
"version": "1.0.1",
"description": "An MCP server for working with Powerpoint files, running in a Docker container.",
"mcpServers": {
"recipe-maker": {
"name": "PPT MCP Server",
"transportType": "stdio",
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"idjohnson/pptmcpserver:0.1"
]
}
}
}
I also tried firing up docker
builder@DESKTOP-QADGF36:~/Workspaces/Office-PowerPoint-MCP-Server$ cat gemini-extension.json
{
"extensionId": "powerpointmcp",
"name": "PowerpointMCP",
"version": "1.0.1",
"description": "An MCP server for working with Powerpoint files, running in a Docker container.",
"mcpServers": {
"powerpointmcp": {
"httpUrl": "http://localhost:8000/mcp"
}
}
}
and installing it with a localhost route
builder@DESKTOP-QADGF36:~/Workspaces/Office-PowerPoint-MCP-Server$ gemini extensions install /home/builder/Workspaces/Office-PowerPoint-MCP-Server/
Loaded cached credentials.
Installing extension "PowerpointMCP".
This extension will run the following MCP servers:
* powerpointmcp (remote): http://localhost:8000/mcp
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.
Do you want to continue? [Y/n]: Y
Extension "PowerpointMCP" installed successfully and enabled.
I fired it up in Docker
$ docker run -d --rm -p 8000:8000 idjohnson/pptmcpserver:0.1 --transport http
2dcb17b0aa90efd56815ecb90c290035faaa3c10a68a4676030f233f855e9fa6
but all my testing showed it was not allowing ingress
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ wget http://127.0.0.1/mcp
--2026-01-30 16:28:42-- http://127.0.0.1/mcp
Connecting to 127.0.0.1:80... failed: Connection refused.
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ wget http://127.0.0.1:8000/mcp
--2026-01-30 16:28:47-- http://127.0.0.1:8000/mcp
Connecting to 127.0.0.1:8000... connected.
HTTP request sent, awaiting response... Read error (Connection reset by peer) in headers.
Retrying.
--2026-01-30 16:28:48-- (try: 2) http://127.0.0.1:8000/mcp
Connecting to 127.0.0.1:8000... connected.
HTTP request sent, awaiting response... Read error (Connection reset by peer) in headers.
Retrying.
^C
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ wget http://127.0.0.1:8000/initialize
--2026-01-30 16:28:54-- http://127.0.0.1:8000/initialize
Connecting to 127.0.0.1:8000... connected.
HTTP request sent, awaiting response... Read error (Connection reset by peer) in headers.
Retrying.
--2026-01-30 16:28:55-- (try: 2) http://127.0.0.1:8000/initialize
Connecting to 127.0.0.1:8000... connected.
HTTP request sent, awaiting response... Read error (Connection reset by peer) in headers.
Retrying.
^C
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ wget http://127.0.0.1:8000
--2026-01-30 16:28:58-- http://127.0.0.1:8000/
Connecting to 127.0.0.1:8000... connected.
HTTP request sent, awaiting response... Read error (Connection reset by peer) in headers.
Retrying.
--2026-01-30 16:28:59-- (try: 2) http://127.0.0.1:8000/
Connecting to 127.0.0.1:8000... connected.
HTTP request sent, awaiting response... Read error (Connection reset by peer) in headers.
Retrying.
^C
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ docker logs bold_black
INFO: Started server process [1]
INFO: Waiting for application startup.
[01/30/26 22:27:52] INFO StreamableHTTP streamable_http_manager.py:116
session manager
started
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ wget http://127.0.0.1:8000/
--2026-01-30 16:29:42-- http://127.0.0.1:8000/
Connecting to 127.0.0.1:8000... connected.
HTTP request sent, awaiting response... Read error (Connection reset by peer) in headers.
Retrying.
--2026-01-30 16:29:43-- (try: 2) http://127.0.0.1:8000/
Connecting to 127.0.0.1:8000... connected.
HTTP request sent, awaiting response... Read error (Connection reset by peer) in headers.
Retrying.
^C
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ curl -X POST http://127.0.0.1:8000/
curl: (56) Recv failure: Connection reset by peer
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ curl -X POST http://127.0.0.1:8000/mcp
curl: (56) Recv failure: Connection reset by peer
According to the code, it should work with STDIO as well, so I next set about to use that in a Gemini CLI config
$ cat gemini-extension.json
{
"extensionId": "powerpointmcp",
"name": "PowerpointMCP",
"version": "1.0.1",
"description": "An MCP server for working with Powerpoint files, running in a Docker container.",
"mcpServers": {
"powerpointmcp": {
"name": "Powerpoint MCP",
"transportType": "stdio",
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"idjohnson/pptmcpserver:0.1",
"--transport",
"stdio"
]
}
}
}
Then added it with a linked extension path
builder@DESKTOP-QADGF36:~/Workspaces/Office-PowerPoint-MCP-Server$ gemini extensions install /home/builder/Workspaces/Office-PowerPoint-MCP-Server/
Loaded cached credentials.
Installing extension "PowerpointMCP".
This extension will run the following MCP servers:
* powerpointmcp (local): docker run -i --rm idjohnson/pptmcpserver:0.1 --transport stdio
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.
Do you want to continue? [Y/n]: Y
Extension "PowerpointMCP" installed successfully and enabled.
This time it loaded
Here I’ll ask it to create a Powerpoint for a trip I’m planning later this summer in the BWCA
The one nuance of using Docker was the fact the files were stuck on the container.
I like Docker because of the dependency management, but this might post a challenge with our LLMs
I had Gemini add a download option then asked for a similar upload one too
I can see they were loaded:
But as we can see when using them, the MCP server ended up choking on large image uploads
Let’s try with Python next.
I’ll uninstall the last
builder@DESKTOP-QADGF36:~/Workspaces/Office-PowerPoint-MCP-Server$ gemini extension uninstall PowerpointMCP
Loaded cached credentials.
Extension "PowerpointMCP" successfully uninstalled.
I’ll setup a local Python3 virtual environment and load it with any dependencies
builder@DESKTOP-QADGF36:~/Workspaces/Office-PowerPoint-MCP-Server$ python -m venv venv
builder@DESKTOP-QADGF36:~/Workspaces/Office-PowerPoint-MCP-Server$ source venv/bin/activate
(venv) builder@DESKTOP-QADGF36:~/Workspaces/Office-PowerPoint-MCP-Server$ pip install -r requirements.txt
Collecting python-pptx (from -r requirements.txt (line 2))
Downloading python_pptx-1.0.2-py3-none-any.whl.metadata (2.5 kB)
Collecting Pillow (from -r requirements.txt (line 3))
Downloading pillow-12.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (8.8 kB)
Collecting fonttools (from -r requirements.txt (line 4))
Downloading fonttools-4.61.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (114 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 114.2/114.2 kB 2.1 MB/s eta 0:00:00
Collecting mcp[cli] (from -r requirements.txt (line 1))
Downloading mcp-1.26.0-py3-none-any.whl.metadata (89 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.5/89.5 kB 9.7 MB/s eta 0:00:00
Collecting anyio>=4.5 (from mcp[cli]->-r requirements.txt (line 1))
Using cached anyio-4.12.1-py3-none-any.whl.metadata (4.3 kB)
Collecting httpx-sse>=0.4 (from mcp[cli]->-r requirements.txt (line 1))
Using cached httpx_sse-0.4.3-py3-none-any.whl.metadata (9.7 kB)
Collecting httpx>=0.27.1 (from mcp[cli]->-r requirements.txt (line 1))
Using cached httpx-0.28.1-py3-none-any.whl.metadata (7.1 kB)
Collecting jsonschema>=4.20.0 (from mcp[cli]->-r requirements.txt (line 1))
Using cached jsonschema-4.26.0-py3-none-any.whl.metadata (7.6 kB)
Collecting pydantic-settings>=2.5.2 (from mcp[cli]->-r requirements.txt (line 1))
Using cached pydantic_settings-2.12.0-py3-none-any.whl.metadata (3.4 kB)
Collecting pydantic<3.0.0,>=2.11.0 (from mcp[cli]->-r requirements.txt (line 1))
Using cached pydantic-2.12.5-py3-none-any.whl.metadata (90 kB)
Collecting pyjwt>=2.10.1 (from pyjwt[crypto]>=2.10.1->mcp[cli]->-r requirements.txt (line 1))
Downloading pyjwt-2.11.0-py3-none-any.whl.metadata (4.0 kB)
Collecting python-multipart>=0.0.9 (from mcp[cli]->-r requirements.txt (line 1))
Downloading python_multipart-0.0.22-py3-none-any.whl.metadata (1.8 kB)
Collecting sse-starlette>=1.6.1 (from mcp[cli]->-r requirements.txt (line 1))
Using cached sse_starlette-3.2.0-py3-none-any.whl.metadata (12 kB)
Collecting starlette>=0.27 (from mcp[cli]->-r requirements.txt (line 1))
Using cached starlette-0.52.1-py3-none-any.whl.metadata (6.3 kB)
Collecting typing-extensions>=4.9.0 (from mcp[cli]->-r requirements.txt (line 1))
Using cached typing_extensions-4.15.0-py3-none-any.whl.metadata (3.3 kB)
Collecting typing-inspection>=0.4.1 (from mcp[cli]->-r requirements.txt (line 1))
Using cached typing_inspection-0.4.2-py3-none-any.whl.metadata (2.6 kB)
Collecting uvicorn>=0.31.1 (from mcp[cli]->-r requirements.txt (line 1))
Using cached uvicorn-0.40.0-py3-none-any.whl.metadata (6.7 kB)
Collecting python-dotenv>=1.0.0 (from mcp[cli]->-r requirements.txt (line 1))
Using cached python_dotenv-1.2.1-py3-none-any.whl.metadata (25 kB)
Collecting typer>=0.16.0 (from mcp[cli]->-r requirements.txt (line 1))
Downloading typer-0.21.1-py3-none-any.whl.metadata (16 kB)
Collecting XlsxWriter>=0.5.7 (from python-pptx->-r requirements.txt (line 2))
Downloading xlsxwriter-3.2.9-py3-none-any.whl.metadata (2.7 kB)
Collecting lxml>=3.1.0 (from python-pptx->-r requirements.txt (line 2))
Downloading lxml-6.0.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl.metadata (3.6 kB)
Collecting idna>=2.8 (from anyio>=4.5->mcp[cli]->-r requirements.txt (line 1))
Using cached idna-3.11-py3-none-any.whl.metadata (8.4 kB)
Collecting certifi (from httpx>=0.27.1->mcp[cli]->-r requirements.txt (line 1))
Using cached certifi-2026.1.4-py3-none-any.whl.metadata (2.5 kB)
Collecting httpcore==1.* (from httpx>=0.27.1->mcp[cli]->-r requirements.txt (line 1))
Using cached httpcore-1.0.9-py3-none-any.whl.metadata (21 kB)
Collecting h11>=0.16 (from httpcore==1.*->httpx>=0.27.1->mcp[cli]->-r requirements.txt (line 1))
Using cached h11-0.16.0-py3-none-any.whl.metadata (8.3 kB)
Collecting attrs>=22.2.0 (from jsonschema>=4.20.0->mcp[cli]->-r requirements.txt (line 1))
Using cached attrs-25.4.0-py3-none-any.whl.metadata (10 kB)
Collecting jsonschema-specifications>=2023.03.6 (from jsonschema>=4.20.0->mcp[cli]->-r requirements.txt (line 1))
Using cached jsonschema_specifications-2025.9.1-py3-none-any.whl.metadata (2.9 kB)
Collecting referencing>=0.28.4 (from jsonschema>=4.20.0->mcp[cli]->-r requirements.txt (line 1))
Using cached referencing-0.37.0-py3-none-any.whl.metadata (2.8 kB)
Collecting rpds-py>=0.25.0 (from jsonschema>=4.20.0->mcp[cli]->-r requirements.txt (line 1))
Using cached rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (4.1 kB)
Collecting annotated-types>=0.6.0 (from pydantic<3.0.0,>=2.11.0->mcp[cli]->-r requirements.txt (line 1))
Using cached annotated_types-0.7.0-py3-none-any.whl.metadata (15 kB)
Collecting pydantic-core==2.41.5 (from pydantic<3.0.0,>=2.11.0->mcp[cli]->-r requirements.txt (line 1))
Using cached pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (7.3 kB)
Collecting cryptography>=3.4.0 (from pyjwt[crypto]>=2.10.1->mcp[cli]->-r requirements.txt (line 1))
Downloading cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl.metadata (5.7 kB)
Collecting click>=8.0.0 (from typer>=0.16.0->mcp[cli]->-r requirements.txt (line 1))
Using cached click-8.3.1-py3-none-any.whl.metadata (2.6 kB)
Collecting shellingham>=1.3.0 (from typer>=0.16.0->mcp[cli]->-r requirements.txt (line 1))
Using cached shellingham-1.5.4-py2.py3-none-any.whl.metadata (3.5 kB)
Collecting rich>=10.11.0 (from typer>=0.16.0->mcp[cli]->-r requirements.txt (line 1))
Downloading rich-14.3.1-py3-none-any.whl.metadata (18 kB)
Collecting cffi>=2.0.0 (from cryptography>=3.4.0->pyjwt[crypto]>=2.10.1->mcp[cli]->-r requirements.txt (line 1))
Using cached cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (2.6 kB)
Collecting markdown-it-py>=2.2.0 (from rich>=10.11.0->typer>=0.16.0->mcp[cli]->-r requirements.txt (line 1))
Using cached markdown_it_py-4.0.0-py3-none-any.whl.metadata (7.3 kB)
Collecting pygments<3.0.0,>=2.13.0 (from rich>=10.11.0->typer>=0.16.0->mcp[cli]->-r requirements.txt (line 1))
Using cached pygments-2.19.2-py3-none-any.whl.metadata (2.5 kB)
Collecting pycparser (from cffi>=2.0.0->cryptography>=3.4.0->pyjwt[crypto]>=2.10.1->mcp[cli]->-r requirements.txt (line 1))
Using cached pycparser-3.0-py3-none-any.whl.metadata (8.2 kB)
Collecting mdurl~=0.1 (from markdown-it-py>=2.2.0->rich>=10.11.0->typer>=0.16.0->mcp[cli]->-r requirements.txt (line 1))
Using cached mdurl-0.1.2-py3-none-any.whl.metadata (1.6 kB)
Downloading python_pptx-1.0.2-py3-none-any.whl (472 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 472.8/472.8 kB 11.8 MB/s eta 0:00:00
Downloading pillow-12.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (7.0 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.0/7.0 MB 40.6 MB/s eta 0:00:00
Downloading fonttools-4.61.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (5.0 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.0/5.0 MB 51.4 MB/s eta 0:00:00
Using cached anyio-4.12.1-py3-none-any.whl (113 kB)
Using cached httpx-0.28.1-py3-none-any.whl (73 kB)
Using cached httpcore-1.0.9-py3-none-any.whl (78 kB)
Using cached httpx_sse-0.4.3-py3-none-any.whl (9.0 kB)
Using cached jsonschema-4.26.0-py3-none-any.whl (90 kB)
Downloading lxml-6.0.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl (5.2 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.2/5.2 MB 30.0 MB/s eta 0:00:00
Using cached pydantic-2.12.5-py3-none-any.whl (463 kB)
Using cached pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.1 MB)
Using cached pydantic_settings-2.12.0-py3-none-any.whl (51 kB)
Downloading pyjwt-2.11.0-py3-none-any.whl (28 kB)
Using cached python_dotenv-1.2.1-py3-none-any.whl (21 kB)
Downloading python_multipart-0.0.22-py3-none-any.whl (24 kB)
Using cached sse_starlette-3.2.0-py3-none-any.whl (12 kB)
Using cached starlette-0.52.1-py3-none-any.whl (74 kB)
Downloading typer-0.21.1-py3-none-any.whl (47 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 47.4/47.4 kB 6.2 MB/s eta 0:00:00
Using cached typing_extensions-4.15.0-py3-none-any.whl (44 kB)
Using cached typing_inspection-0.4.2-py3-none-any.whl (14 kB)
Using cached uvicorn-0.40.0-py3-none-any.whl (68 kB)
Downloading xlsxwriter-3.2.9-py3-none-any.whl (175 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 175.3/175.3 kB 16.2 MB/s eta 0:00:00
Downloading mcp-1.26.0-py3-none-any.whl (233 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 233.6/233.6 kB 22.5 MB/s eta 0:00:00
Using cached annotated_types-0.7.0-py3-none-any.whl (13 kB)
Using cached attrs-25.4.0-py3-none-any.whl (67 kB)
Using cached click-8.3.1-py3-none-any.whl (108 kB)
Downloading cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl (4.5 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.5/4.5 MB 60.3 MB/s eta 0:00:00
Using cached h11-0.16.0-py3-none-any.whl (37 kB)
Using cached idna-3.11-py3-none-any.whl (71 kB)
Using cached jsonschema_specifications-2025.9.1-py3-none-any.whl (18 kB)
Using cached referencing-0.37.0-py3-none-any.whl (26 kB)
Downloading rich-14.3.1-py3-none-any.whl (309 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 310.0/310.0 kB 34.9 MB/s eta 0:00:00
Using cached rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (390 kB)
Using cached shellingham-1.5.4-py2.py3-none-any.whl (9.8 kB)
Using cached certifi-2026.1.4-py3-none-any.whl (152 kB)
Using cached cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (215 kB)
Downloading markdown_it_py-4.0.0-py3-none-any.whl (87 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 87.3/87.3 kB 6.3 MB/s eta 0:00:00
Using cached pygments-2.19.2-py3-none-any.whl (1.2 MB)
Using cached mdurl-0.1.2-py3-none-any.whl (10.0 kB)
Using cached pycparser-3.0-py3-none-any.whl (48 kB)
Installing collected packages: XlsxWriter, typing-extensions, shellingham, rpds-py, python-multipart, python-dotenv, pyjwt, pygments, pycparser, Pillow, mdurl, lxml, idna, httpx-sse, h11, fonttools, click, certifi, attrs, annotated-types, uvicorn, typing-inspection, referencing, python-pptx, pydantic-core, markdown-it-py, httpcore, cffi, anyio, starlette, rich, pydantic, jsonschema-specifications, httpx, cryptography, typer, sse-starlette, pydantic-settings, jsonschema, mcp
Successfully installed Pillow-12.1.0 XlsxWriter-3.2.9 annotated-types-0.7.0 anyio-4.12.1 attrs-25.4.0 certifi-2026.1.4 cffi-2.0.0 click-8.3.1 cryptography-46.0.4 fonttools-4.61.1 h11-0.16.0 httpcore-1.0.9 httpx-0.28.1 httpx-sse-0.4.3 idna-3.11 jsonschema-4.26.0 jsonschema-specifications-2025.9.1 lxml-6.0.2 markdown-it-py-4.0.0 mcp-1.26.0 mdurl-0.1.2 pycparser-3.0 pydantic-2.12.5 pydantic-core-2.41.5 pydantic-settings-2.12.0 pygments-2.19.2 pyjwt-2.11.0 python-dotenv-1.2.1 python-multipart-0.0.22 python-pptx-1.0.2 referencing-0.37.0 rich-14.3.1 rpds-py-0.30.0 shellingham-1.5.4 sse-starlette-3.2.0 starlette-0.52.1 typer-0.21.1 typing-extensions-4.15.0 typing-inspection-0.4.2 uvicorn-0.40.0
[notice] A new release of pip is available: 23.3.1 -> 26.0
[notice] To update, run: pip install --upgrade pip
Now I can add it directly to Gemini CLI as an MCP server (not via extension)
$ gemini mcp add PowerpointMCP /home/builder/Workspaces/Office-PowerPoint-MCP-Server/venv/bin/python /home/builder/Workspaces/Office-PowerPoint-MCP-Server/ppt_mcp_server.py
Loaded cached credentials.
MCP server "PowerpointMCP" added to project settings. (stdio)
i can see it listed which is a good sign it is working
This time it worked just fine:
Without the MCP Server
I was curious - would Gemini CLI sort out some way to make me a PPTX if I didn’t have that MCP server.
(venv) builder@DESKTOP-QADGF36:~/Workspaces/Office-PowerPoint-MCP-Server$ gemini mcp list
Loaded cached credentials.
Configured MCP servers:
✓ PowerpointMCP: /home/builder/Workspaces/Office-PowerPoint-MCP-Server/venv/bin/python /home/builder/Workspaces/Office-PowerPoint-MCP-Server/ppt_mcp_server.py (stdio) - Connected
✓ recipe-maker (from RecipeMaker): docker run -i --rm -v /tmp:/tmp idjohnson/recipemakermcp:0.5 (stdio) - Connected
✓ workout-maker (from WorkoutMaker): docker run -i --rm idjohnson/workoutmcp:0.3 (stdio) - Connected
✗ 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
✓ perlMCP (from perlmcp): https://perlmcp.steeped.icu/mcp (http) - 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
(venv) builder@DESKTOP-QADGF36:~/Workspaces/Office-PowerPoint-MCP-Server$ gemini mcp remove PowerpointMCP
Loaded cached credentials.
Server "PowerpointMCP" removed from project settings.
(venv) builder@DESKTOP-QADGF36:~/Workspaces/Office-PowerPoint-MCP-Server$
Now here we see both a PPT and a PPT with images generated
For review, the PowerPoint from this latest no-MCP version:
and the one using the PowerPoint MCP server
I think the big issue I have presently are that the maps are really junk. They are just invented art.
Google Maps Extension
Let’s start by trying to add the Google Maps API by way of Gemini CLI Extension
I added it
(venv) builder@DESKTOP-QADGF36:~/Workspaces/bwcaPPT$ gemini extensions install https://github.com/googlemaps/platform-ai
Loaded cached credentials.
Installing extension "google-maps-platform".
This extension will run the following MCP servers:
* google-maps-platform-code-assist (local): npx -y @googlemaps/code-assist-mcp@latest
This extension will append info to your gemini.md context using GEMINI.md
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.
Do you want to continue? [Y/n]: Y
Extension "google-maps-platform" installed successfully and enabled.
(venv) builder@DESKTOP-QADGF36:~/Workspaces/bwcaPPT$
This gave me:
google-maps-platform-code-assist (from google-maps-platform) - Ready (2 tools, 1 resource)
Tools:
- retrieve-google-maps-platform-docs
- retrieve-instructions
Resources:
- instructions (mcp://google-maps-platform-code-assist/instructions) [text/plain]
I’m thinking this is really not that great - at best, maybe I get instructions on using Maps API in code:
This repository contains the Google Maps Platform Code Assist toolkit, a Model Context Protocol (MCP) server that enhances the responses from large language models (LLMs) used for developing applications with the Google Maps Platform by grounding them in the official, up-to-date documentation and code samples.
With some searching I found this google maps mcp server that seems to have what I wanted.
I create a GCP API key with the requisite permissions and APIs enabled
I think I can add it with a single command line:
$ gemini mcp add googlemapsmcp node /home/builder/Workspaces/google-maps-mcp/dist/index.js --env GOOGLE_MAPS_API_KEY=asdfsadfsadfsadfsadfsafsadfa
Loaded cached credentials.
MCP server "googlemapsmcp" added to project settings. (stdio)
which creates a settings file locally:
(venv) builder@DESKTOP-QADGF36:~/Workspaces/google-maps-mcp$ cat .gemini/settings.json
{
"mcpServers": {
"googlemapsmcp": {
"command": "node",
"args": [
"/home/builder/Workspaces/google-maps-mcp/dist/index.js"
],
"env": {
"GOOGLE_MAPS_API_KEY": "sadfsadfsafdsadfsadf"
}
}
}
}
Which I can now see it there
Let’s try building out a route with this new MCP server
This is far more in line with what I was seeking
Bing Maps
People often forget that Microsoft has a pretty good mapping alternative to Google Maps, they just forget to advertise it. I never figured out why since it has some unique timeline and birdseye features.
While they have a decent REST API, they lack an MCP server.
Let’s do them a solid and build one out.
I’ll use those Agent Skills from Anthropic as we did earlier this week
builder@DESKTOP-QADGF36:~/Workspaces$ git clone https://github.com/anthropics/skills.git
Cloning into 'skills'...
remote: Enumerating objects: 391, done.
remote: Counting objects: 100% (101/101), done.
remote: Compressing objects: 100% (50/50), done.
remote: Total 391 (delta 55), reused 51 (delta 51), pack-reused 290 (from 1)
Receiving objects: 100% (391/391), 3.05 MiB | 10.87 MiB/s, done.
Resolving deltas: 100% (111/111), done.
builder@DESKTOP-QADGF36:~/Workspaces$ cd ~/.gemini/skills/
builder@DESKTOP-QADGF36:~/.gemini/skills$ ln -s ../../Workspaces/skills/skills/mcp-builder mcp-builder
My Gemini CLI prompt is a bit large, but I think it’s pretty clear that of which I am asking:
I would like to leverage the mcp-builder skill to create an MCP Server that can connect to the Bing MAPS REST service │ │ (https://learn.microsoft.com/en-us/bingmaps/rest-services/imagery/get-a-static-map). The first tool I would like │ │ implemented is get static map (https://learn.microsoft.com/en-us/bingmaps/rest-services/imagery/get-a-static-map) which │ │ might also need the metadata (https://learn.microsoft.com/en-us/bingmaps/rest-services/imagery/get-imagery-metadata). │ │ The goal is to produce an MCP Server for Bing Maps we can use to get proper satellite images of areas. If we need a │ │ Bing Maps API key, please document steps in setup.md.
It immediately picked up my ask for the MCP server and built out the directories
It suggested it was done a lot faster than I expected
Sidebar on Costs
You see me use Gemini CLI a lot, so again, let’s review the costs
IFF you are paying retail, and not using a Gemini Pro subscription but rather a paid GCP account, it would be about 13 cents.
That said, you can get a pretty generous amount for free under the “Free” Gemini Developer API plan.
I bring this up not to be a shill for Google, rather, in some other slack chats of which I take part, I hear of colleagues burning through $100s on tokens to build things and it saddens me they are spending so much money unnecessarily.
Back to building…
It seems they are moving from Bing Maps to Azure Maps in the near future, but for now, the API setup portal still lives
However, it will not let me create new keys so I’m stuck here and will need to try the new APIs
I’m a little more iffy on this Azure Maps because in one way, it says that it costs me US$4.50 for under 500k maps and another it says 5000 map tiles are free. Maybe it means it’s just a flat rate of US$4.50 a month plus transaction costs for creator?
They have a link to “Understanding Azure Maps transactions” that leads to an empty page with “Bad Request” - not looking good Microsoft
There really is just one pricing tier
I’m going to bit the bullet and try this one. Once created, I can get my API key from settings/authentication
I’m now going to go back to the MCP repo and do some setup. With Python, it almost always starts with a Virtual Env and loading requirements:
builder@DESKTOP-QADGF36:~/Workspaces/bingMapsMCP$ python -m venv venv
builder@DESKTOP-QADGF36:~/Workspaces/bingMapsMCP$ source venv/bin/activate
(venv) builder@DESKTOP-QADGF36:~/Workspaces/bingMapsMCP$ pip install -r requirements.txt
The setup suggests
{
"mcpServers": {
"bing-maps": {
"command": "python",
"args": ["/absolute/path/to/bing_maps_mcp.py"],
"env": {
"BING_MAPS_API_KEY": "your_api_key_here"
}
}
}
}
A fast test shows it is working as a STDIO server
As you recall form earlier, we simply added the Office PowerPoint MCP server using: gemini mcp add PowerpointMCP /home/builder/Workspaces/Office-PowerPoint-MCP-Server/venv/bin/python /home/builder/Workspaces/Office-PowerPoint-MCP-Server/ppt_mcp_server.py
We can do similar here:
(venv) builder@DESKTOP-QADGF36:~/Workspaces/bingMapsMCP$ gemini mcp add \
bingMapsMCP /home/builder/Workspaces/bingMapsMCP/venv/bin/python \
/home/builder/Workspaces/bingMapsMCP/bing_maps_mcp.py --env \
BING_MAPS_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Loaded cached credentials.
MCP server "bingMapsMCP" added to project settings. (stdio)
Which made the appropriate Gemini CLI settings file
(venv) builder@DESKTOP-QADGF36:~/Workspaces/bingMapsMCP$ cat .gemini/settings.json
{
"mcpServers": {
"bingMapsMCP": {
"command": "/home/builder/Workspaces/bingMapsMCP/venv/bin/python",
"args": [
"/home/builder/Workspaces/bingMapsMCP/bing_maps_mcp.py"
],
"env": {
"BING_MAPS_API_KEY": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}
}
}
I can now see it in my list
I also went and added the PowerPoint MCP server (gemini mcp add PowerpointMCP /home/builder/Workspaces/Office-PowerPoint-MCP-Server/venv/bin/python /home/builder/Workspaces/Office-PowerPoint-MCP-Server/ppt_mcp_server.py) so we could test both
Now my first shot gave me 401 errors
I want to debug this with the MCP inspector (perhaps the APIs have changed) and I need to point it to use the new Azure Maps APIs.
I have found that by pointing the GenAI tool at the REST documentation that is current, we can skip a lot of churn.
Gemini did a good job updating, including renaming the python file to azure_maps_mcp.py and changing the ENV var to AZURE_MAPS_SUBSCRIPTION_KEY.
I fired up a new test
But it vomited on some typing errors
MCP tool 'get_static_map' reported tool error for function call: │
│ {"name":"get_static_map","args":{"params":{"imagerySet":"AerialWithLabels","centerPoint":"47.93907,-91.47923","zoomLevel":13,"map │
│ Size":"800,600"}}} with response: │
│ [{"functionResponse":{"name":"get_static_map","response":{"content":[{"type":"text","text":"Error executing tool get_static_map: │
│ 1 validation error for get_static_mapOutput\nresult\n Input should be a valid string [type=string_type, input_value={'format': │
│ 'png', 'width'...0Tbjz/AAAAAElFTkSuQmCC'}, input_type=dict]\n For further information visit │
│ https://errors.pydantic.dev/2.12/v/string_type"}],"isError":true}}}]
I killed it too fast the first time as I noticed it was already finding and fixing the errors. Funny thing is when i tried a second time, it just bypassed the MCP server
The resulting PPTX was a good start. Better than Google Maps in that it showed the parking lot turnaround so I could tell easily where the EP30 would be
I still wanted my MCP server to work, so I then had Gemini work against the error to correct
The fixes seemed to work and I could see base64 encoded images coming back
I ended up popping over to another computer, so I was able to test in pure Linux as well
$ builder@LuiGi:~/Workspaces/bingMapsMCP$ python -m venv venv && source venv/bin/activate && pip install -r requirements.txt
(venv) builder@LuiGi:~/Workspaces/bingMapsMCP$ gemini mcp add \
bingMapsMCP /home/builder/Workspaces/bingMapsMCP/venv/bin/python \
/home/builder/Workspaces/bingMapsMCP/azure_maps_mcp.py --env \
AZURE_MAPS_SUBSCRIPTION_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
MCP server "bingMapsMCP" added to project settings. (stdio)
While I went back and forth, I didn’t really make much more progress and when Gemini CLI would have an error, it would just work around it with a python script.
I decided that while it isn’t 100% perfect - it sometimes vomits on pins and timeouts, I’ll share the MCP server as it stands so others can role with it:
https://forgejo.freshbrewed.science/builderadmin/bingMapsMCP
Testing in Copilot
However it had nothing but issues.
With Claude Haiku it just would fail
With GPT 5 it just coded around the MCP server. When I told it the exact name of the MCP server, it just used the mcp.json to fetch the API key in it’s own Python
Summary
This started with my goal to try out a PowerPoint MCP server to see how well it can work. I tried it with docker and then directly with python. Because this app needs to add images, update local PowerPoint files and more, it seems the python approach makes more sense. I compare this to just asking Gemini CLI to create PowerPoints. To do that, Gemini CLI will often use Python and a PPTX library to generate PowerPoints with code. Though, I think the MCP server is slightly better in appearance.
In making a BWCA route, I found the general “create a map” or image from Nanobanana was rather pointless - they bared no resemblance to the lakes in question. This brought me to a Google Maps MCP server which did a fantastic job. Yes, it took a bit of legwork to create a proper Service account with permissions, but it was worth it for the proper images.
I’m not sure what the issue was with Copilot. Copilot seems just as happy to whip up some python to run, provided you have a valid key. I welcome any ideas of anything I may have missed there.

































