A collection of upcoming CFPs (call for papers) from across the internet and around the world.
The post Call For Papers Listings for 9/11 appeared first on Leon Adato.
A collection of upcoming CFPs (call for papers) from across the internet and around the world.
The post Call For Papers Listings for 9/11 appeared first on Leon Adato.
Job postings that came across my desk, slack, email, discord, etc this week.
The post Job listings for week ending 9/11 appeared first on Leon Adato.
This article provides a step by step migration guide for a Python MCP server from the MCP Python SDK 1.x (FastMCP) to 2.x (MCPServer), followed by a step by step deployment of Gemma 4 E2B to a Cloud Run hosted GPU enabled system. A suite of Python MCP tools is built to simplify management of the vLLM hosted deployment.
https://github.com/xbill9/gemma4-dev/tree/main/gpu-2B-cloudrun-devops-agent
Nothing in the repository changed. A fresh install did.
The project's requirements.txt listed mcp with no version bound, so the next pip install resolved the 2.x line. The server stopped importing:
python3 -c "from mcp.server.fastmcp import FastMCP"
raise ModuleNotFoundError(_MESSAGE, name=__name__)
ModuleNotFoundError: No module named 'mcp.server.fastmcp'. This is mcp 2.x, where FastMCP was renamed to MCPServer (from mcp.server.mcpserver import MCPServer) and other APIs changed; see the migration guide at https://py.sdk.modelcontextprotocol.io/v2/migration/#fastmcp-renamed-to-mcpserver or pin 'mcp<2' to keep running v1 code.
It showed up in Claude Code first, and less helpfully. The MCP server was listed as failed with Connection closed: Claude Code launched it with python3, it died on the import, and stdio closed before the handshake.
That error message deserves credit. The 2.x package still ships a mcp/server/fastmcp.py, and its only job is to raise it. A bare No module named would have sent everyone hunting for a broken install. ✅
Two fixes, and the error message names both.
Pin mcp<2
|
Migrate to MCPServer
|
|
|---|---|---|
| Code change | none | import and class name |
| Where the fix lives | every interpreter that runs the server | the repository |
| Shared system Python | downgrades mcp for everything on it |
nothing global changes |
| Future fixes | the v1 maintenance line | the current line |
The migration guide says the v1.x line keeps receiving critical bug fixes and security patches, so pinning is legitimate. This rig rules it out for a different reason: these projects install into one system Python, with no virtualenvs, so a pin in one project is a downgrade for every other project on the machine. Migrating keeps the change inside the repository.
Requires-Python >=3.10
us-east4)<project>-bucket for the model weightsThe official migration guide opens with a table of the changes almost every project hits. Here they are against this server — stdio transport, @mcp.tool() tools, one @mcp.resource(), and no client code:
| Change | First symptom | This server |
|---|---|---|
FastMCP renamed to MCPServer
|
No module named 'mcp.server.fastmcp' |
❌ hit |
httpx replaced by httpx2
|
No module named 'httpx' |
⚠️ exposed, already safe |
| Sync handlers run on a worker thread |
get_running_loop() raises in a def handler |
⚠️ handlers move threads |
| camelCase fields renamed to snake_case | 'Tool' object has no attribute 'inputSchema' |
✅ not used |
Resource URIs are str, not AnyUrl
|
'str' object has no attribute 'host' |
✅ tests already call str()
|
| Transport parameters moved off the constructor | unexpected keyword argument 'port' |
✅ name only |
McpError renamed to MCPError
|
cannot import name 'McpError' |
✅ not used |
Three rows deserve more than a table cell.
mcp no longer installs httpx. v2 depends on httpx2, a fork of httpx, instead. server.py does import httpx for its HTTP probes and its benchmark, and the guide warns what happens next: a ModuleNotFoundError whose traceback never mentions mcp. This server escaped only because requirements.txt already listed httpx on its own line. If your server imports httpx and never declared it, declare it now.
Sync handlers changed threads. In v1 a plain def tool ran inline on the event loop, so a blocking call stalled every other request on the server. v2 runs sync handlers on a worker thread. The only thing that breaks is code that expects the loop's thread — asyncio.get_running_loop() in a def handler now raises. This server has none, so its sync tools gain concurrency for free. The move covers def handlers only: an async def tool that makes a blocking call still blocks the loop, in v1 and v2 alike.
The server's version went blank. In v1 an unversioned server reported the installed mcp version as its own. In v2 it reports an empty string. Nothing breaks, but it shows up in the handshake in Step 5.
The guide lists the everyday surface that carries over, and it is most of a typical FastMCP server:
@mcp.tool(), @mcp.resource() and @mcp.prompt() take the same arguments and handler signatureslist_tools() and list_resources() return the same listslifespan= works as beforeFor this server that means no tool function changed. Every tool body is exactly what it was.
Measure before editing. Five greps cover everything in the table above:
grep -c "^@mcp\.\(tool\|resource\)" server.py
grep -A1 "^@mcp\." server.py | grep -c "^def"
grep -n "get_running_loop\|asyncio.run(" server.py || echo "(no matches)"
grep -n "MCP_\|dotenv" server.py || echo "(no matches)"
grep -n "^import httpx" server.py; grep -n "^httpx" requirements.txt
28
12
(no matches)
(no matches)
13:import httpx
14:httpx
28 handlers, 12 of them sync, none touching the event loop, and httpx declared.
The MCP_ grep checks one more change. v2 no longer reads MCP_* environment variables or a .env file into server settings. The guide points out that constructor arguments always took precedence, so those variables rarely did anything anyway. This server reads its own configuration from GOOGLE_CLOUD_PROJECT, VLLM_BASE_URL and friends, so nothing changes.
One check a grep cannot do: v2 inserted title and description into the constructor's positional parameters. A v1 call like FastMCP("Demo", "You answer questions…") still runs on v2, but the second string silently becomes the title and stops reaching the model as instructions. Keep the name positional and pass everything else by keyword. This server passes only the name.
The whole code change:
-from mcp.server.fastmcp import FastMCP
+from mcp.server.mcpserver import MCPServer
from openai import AsyncOpenAI
...
-# Initialize FastMCP server
-mcp = FastMCP("Self-Hosted vLLM DevOps Agent")
+# Initialize MCP server (mcp 2.x; FastMCP was renamed MCPServer)
+mcp = MCPServer("Self-Hosted vLLM DevOps Agent")
@mcp.tool(), @mcp.resource(), mcp.run() and every tool body stay as they are. Other submodules moved the same way — mcp.server.fastmcp.* is now mcp.server.mcpserver.*, and ctx.fastmcp is now ctx.mcp_server — but this server uses neither.
This one cost a test run. The project has a Claude Code hook that runs ruff format and ruff check --fix after every edit. Change the import line first and, for a moment, MCPServer is imported but unused. The hook deletes it:
cat server.py
ruff check --fix --diff server.py
from mcp.server.mcpserver import MCPServer
mcp = FastMCP("demo")
--- server.py
+++ server.py
@@ -1,3 +1,2 @@
-from mcp.server.mcpserver import MCPServer
mcp = FastMCP("demo")
Would fix 1 error.
The next edit renames the class, and the file now has no import at all:
mcp = MCPServer("Self-Hosted vLLM DevOps Agent")
^^^^^^^^^
NameError: name 'MCPServer' is not defined
Make both changes in one edit, or change the usage first. Any editor that runs ruff check --fix on save will do the same thing.
Code that imports mcp.server.mcpserver cannot run on 1.x, so the requirement should say so:
-mcp
+mcp>=2
The guide's own example also caps the major version, mcp>=2,<3. That is the safer line if you would rather meet 3.x on purpose than by pip install. Keep httpx on its own line in the same file, for the reason above.
make lint
ruff check .
All checks passed!
ruff format --check .
14 files already formatted
mypy .
Success: no issues found in 6 source files
make test
----------------------------------------------------------------------
Ran 28 tests in 1.058s
OK
The suite compares the registered tool set against a hard-coded list through mcp.list_tools(). That call is on the guide's unchanged list, and it is the test that would catch a tool silently failing to register after the rename. ✅
Unit tests call Python. A client speaks JSON-RPC over stdio, so test that too. Hold stdin open with sleep: with a bare printf pipe the server saw end-of-input and exited after answering only initialize.
{ printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
'{"jsonrpc":"2.0","id":3,"method":"resources/list","params":{}}'; sleep 5; } \
| python3 server.py 2>/dev/null
The responses are JSON; summarised:
initialize OK: name='Self-Hosted vLLM DevOps Agent' version='' proto 2025-06-18
tools/list OK: 27 tools -> cloudrun_analyze_cloud_logging, cloudrun_analyze_gpu_logs, cloudrun_check_gpu_quotas, cloudrun_deploy, ...
resources/list OK: ['config://vllm-deployment-template']
🟢 27 tools and the resource, matching the suite's expected list. Note version='' — the unversioned-server change from the table. Pass version="..." to MCPServer(...) if a client or dashboard displays it.
Keep this snippet. It is the fastest way to tell "my server is broken" from "my client config is broken."
Claude Code reads .mcp.json. Nothing in it changes for the migration — it launches server.py with the system python3:
{
"mcpServers": {
"cloudrun-devops": {
"command": "python3",
"args": ["/home/xbill/gemma4-dev/gpu-2B-cloudrun-devops-agent/server.py"],
"env": {
"GOOGLE_CLOUD_PROJECT": "aisprint-491218",
"GOOGLE_CLOUD_LOCATION": "us-east4",
"VLLM_BASE_URL": "https://gpu-2b-l4-devops-agent-289270257791.us-east4.run.app",
"MODEL_NAME": "/mnt/models/gemma-4-E2B-it"
}
}
}
}
If the server failed at startup earlier in the session, reconnect it from /mcp or start a new session to pick up the fixed code.
Cloud Run mounts the bucket read-only at /mnt/models through GCS FUSE, so the weights go to GCS once. Download to a real disk rather than /tmp, which on this host is a RAM-backed tmpfs smaller than the model:
hf download google/gemma-4-E2B-it --local-dir ~/hf-downloads/gemma-4-E2B-it
gcloud storage rsync ~/hf-downloads/gemma-4-E2B-it gs://aisprint-491218-bucket/gemma-4-E2B-it \
--recursive --exclude='^\.cache/'
gcloud storage ls -l gs://aisprint-491218-bucket/gemma-4-E2B-it/
Average throughput: 41.4MiB/s
4954 2026-09-10T14:51:14Z gs://aisprint-491218-bucket/gemma-4-E2B-it/config.json
10246621918 2026-09-10T14:55:13Z gs://aisprint-491218-bucket/gemma-4-E2B-it/model.safetensors
32169626 2026-09-10T14:51:34Z gs://aisprint-491218-bucket/gemma-4-E2B-it/tokenizer.json
TOTAL: 9 objects, 10278849571 bytes (9.57GiB)
Check the architecture, not the folder name. The same bucket already held a gemma-2b-it/ folder that looked like the answer and was the original Gemma, which the gemma4 parsers cannot serve. config.json settles it:
gcloud storage cat gs://aisprint-491218-bucket/gemma-4-E2B-it/config.json \
| python3 -c 'import json,sys; c=json.load(sys.stdin); t=c["text_config"]; print(c["model_type"], c["architectures"], "hidden", t["hidden_size"], "layers", t["num_hidden_layers"])'
gemma4 ['Gemma4ForConditionalGeneration'] hidden 1536 layers 35
The deploy-vllm target in the Makefile is the single source of truth for the vLLM and Cloud Run flags:
| Flag | Value | Why |
|---|---|---|
--gpu-type |
nvidia-l4 |
one L4 per instance |
--concurrency |
4 |
requests Cloud Run sends one instance |
--max-num-seqs |
8 |
vLLM's batch ceiling |
--tool-call-parser, --reasoning-parser
|
gemma4 |
Gemma 4 tool calling breaks without either |
--no-allow-unauthenticated |
callers need an identity token |
make deploy
Deploying container to Cloud Run service [gpu-2b-l4-devops-agent] in project [aisprint-491218] region [us-east4]
Deploying new service...
Creating Revision....................done
Routing traffic.....done
Done.
Service [gpu-2b-l4-devops-agent] revision [gpu-2b-l4-devops-agent-00001-ssq] has been deployed and is serving 100 percent of traffic.
Service URL: https://gpu-2b-l4-devops-agent-289270257791.us-east4.run.app
gcloud run deploy returns only once the startup probe passes, and the probe waits initialDelaySeconds=180 before its first check. Expect several minutes.
The default autoscales between zero and one instance, which means a cold GPU start after the service goes idle. A demo cannot wait for that. The Makefile takes a SCALING variable:
SCALING ?= auto
ifeq ($(SCALING),auto)
SCALING_FLAGS = --scaling=auto --max-instances=1 --min-instances=0
else
SCALING_FLAGS = --scaling=$(SCALING)
endif
make deploy SCALING=1
gcloud run services describe gpu-2b-l4-devops-agent --region us-east4 --format='yaml(metadata.annotations)'
run.googleapis.com/manualInstanceCount: '1'
run.googleapis.com/scalingMode: manual
gcloud only accepts a positive instance count for manual scaling, so this cannot pin the service at zero. The min and max flags are passed only in auto mode, because gcloud's help does not say how they combine with a fixed count. One L4 now runs until you change it.
Plain make deploy, and the cloudrun_deploy and cloudrun_update_scaling tools, all pass --scaling=auto on purpose — a service stuck in manual scaling at zero returns 503 to every request. So any of them quietly takes a demo back to scale-to-zero.
Ask vLLM what it loaded:
curl -s -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
https://gpu-2b-l4-devops-agent-289270257791.us-east4.run.app/v1/models | python3 -m json.tool
{
"object": "list",
"data": [
{
"id": "/mnt/models/gemma-4-E2B-it",
"object": "model",
"owned_by": "vllm",
"max_model_len": 16384
}
]
}
The model id is the mount path, not the Hugging Face repo id. The container is started with --model=/mnt/models/<path>, so that path is the name the OpenAI API expects.
Then ask the agent for cloudrun_verify_model_health:
✅ Model health check PASSED.
Model: /mnt/models/gemma-4-E2B-it
Response: 'Hello! Yes, I am working. I am Gemma 4, a Large La...'
Latency: 2.48 seconds.
That answer came through the migrated server: Claude Code called the tool over MCP, and the tool called vLLM. 🟢
Ask the agent for cloudrun_run_benchmark with its defaults: one warmup request, then 20 requests at each concurrency level of 1, 2, 4 and 8, up to 128 output tokens each, one fixed prompt at temperature 0.
| Concurrency | Req/s | Tokens/s | Avg latency | P95 latency |
|---|---|---|---|---|
| 1 | 0.39 | 49.63 | 2.58 s | 2.59 s |
| 2 | 0.75 | 95.36 | 2.68 s | 2.74 s |
| 4 | 1.47 | 188.46 | 2.71 s | 2.78 s |
| 8 | 1.48 | 189.53 | 4.86 s | 5.47 s |
Every request at every level succeeded. Three readings:
From 1 to 4, throughput scales almost linearly. 188.46 tokens/s is 3.8x the single-stream 49.63 (arithmetic), while average latency moves from 2.58 s to 2.71 s. The L4 is nowhere near full at 4.
From 4 to 8, it stops. Throughput rises 0.6% (arithmetic) while average latency goes from 2.71 s to 4.86 s. Half the requests are waiting.
The ceiling is a Cloud Run setting, not the GPU. One instance accepts --concurrency=4 requests at a time, and vLLM would batch up to --max-num-seqs=8. The next sweep worth running redeploys with --concurrency=8 and measures the L4 instead of the setting.
And the SDK version cannot move any of these numbers. The benchmark's HTTP calls go from server.py to vLLM; MCP only carries the one tool call that starts them. The sweep is here to show the migrated server driving the whole lifecycle, not to measure the SDK.
make destroy
Not run for this article — the demo service is still up. It deletes the Cloud Run service; the weights stay in the bucket for the next deploy.
# exposure
grep -rn "mcp.server.fastmcp" .
grep -A1 "^@mcp\." server.py | grep -c "^def"
grep -n "^import httpx" server.py; grep -n "^httpx" requirements.txt
# the rename, in ONE edit
# from mcp.server.fastmcp import FastMCP -> from mcp.server.mcpserver import MCPServer
# FastMCP("name") -> MCPServer("name")
# requirements.txt: mcp -> mcp>=2 (or mcp>=2,<3), and declare httpx if you import it
make lint && make test
# stdio smoke test: hold stdin open
{ printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"p","version":"0"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'; sleep 5; } | python3 server.py 2>/dev/null
# deploy, demo, teardown
make deploy
make deploy SCALING=1
make destroy
The goal of this article was to move a Python MCP server off FastMCP and onto the MCP Python SDK 2.x without changing what it does. The key to the solution was measuring exposure against the migration guide before editing, which reduced the change to one import and one class name. The migration results were:
mcp 2.x no longer installs httpx; this server was safe only because it declared httpx itself--concurrency=4
Scope: mcp 2.2.0 on Python 3.14.7, with the 1.30.0 wheel as the v1 reference. One Cloud Run instance with one NVIDIA L4 in us-east4, vLLM v0.26.0-cu129, Gemma 4 E2B, in manual scaling at one fixed instance during the sweep. One sweep of 20 requests per level, a single fixed prompt, 128 max output tokens. It measures the serving stack, not the SDK.
The strategy for using MCP for migrating a Python MCP server to the MCP SDK 2.x was validated with an incremental step by step approach.
mcp 2.2.0 (mcp-types 2.2.0), Python 3.14.7, ruff 0.16.6, Google Cloud SDK 583.0.0, vLLM v0.26.0 on one NVIDIA L4, Cloud Run us-east4.
What are the latest trends uncovered in the 2026 Django Developers Survey? How are Django users employing LLMs in their development process? Christopher Trudeau is back on the show this week with another batch of PyCoder’s Weekly articles and projects.
We discuss a summary by Will Vincent about this year’s Django Developers Survey. The survey draws on responses from nearly 3,500 developers across more than 40 countries. It provides a look at how developers are adapting to AI workflows and which tools they’re embracing.
We dig into a post from Brett Cannon about reproducible builds in Python. It covers the importance of creating an independently verifiable, repeatable build of CPython and other associated tools. The post explains why Python isn’t there yet and outlines the direction of the work still needed.
We also share other articles and projects from the Python community, including recent releases, upcoming PSF elections, Polars vs SQL differences nobody is talking about, creating Django unsubscribe links without a Login, a library to make measurement units easier through Pydantic, and a tool to convert if-else code to match statements.
This episode is sponsored by Six Feet Up.
Video Course Spotlight: How to Get Started With Ollama
Learn how to install Ollama, pull local models, and connect them to your Python code using the chat and text generation interfaces.
Topics:
if-else Code to match StatementsNews:
Show Links:
token = signing.dumps(recipient.pk, salt=UNSUBSCRIBE_SALT). The token itself is the credential, and it ships with Django out of the box.python/prebuilt-cpython repo already exists, and the PSF has been building an official prebuilt relocatable CPython distribution since October 2025. Covers what’s actually being built, what it means for uv/ruff/python-build-standalone, and why the Astral upstream patches and PSF alternative aren’t in conflict.Projects:
Additional Links:
Level up your Python skills with our expert-led courses:
Support the podcast & join our community of Pythonistas
In this episode, we refer to Vasco's Product Owner episodes, where Product Owners share their own lessons from the role.
Read the full Show Notes and search through the world's largest audio library on Agile and Scrum directly on the Scrum Master Toolbox Podcast website: http://bit.ly/SMTP_ShowNotes.
"No, you cannot have this, but this is why." - Sheik Meeajaun
Sheik's model for a great Product Owner came from a mentor he worked with early in his career. That PO owned his slice of the product, delivered consistently, and taught Sheik the power of the "justified no." A strong PO does not simply reject stakeholder requests. They explain the trade-off, ask what should be removed from the sprint, and make business value visible. If a stakeholder wants urgent work, the PO can ask them to get agreement from the person whose work would be displaced. That shifts the conversation from pressure to prioritization. For Sheik, great Product Owners understand that their job is to bring value to the business and delight customers. They care deeply enough about the product to protect it from random requests, HiPPO decisions, and backlog noise. They know success is not only delivery. It is the visible appreciation that comes when people recognize a product decision created real value.
Self-reflection Question: Does your Product Owner have a practical way to say no that protects value without turning every request into conflict?
Read the full Show Notes and search through the world's largest audio library on Agile and Scrum directly on the Scrum Master Toolbox Podcast website: http://bit.ly/SMTP_ShowNotes.
"A Product Owner's primary job is to bring value." - Sheik Meeajaun
The anti-pattern Sheik warns about is the Product Owner as executor. This PO does not own the product, does not know the customers deeply enough, and does not push back when leadership changes direction. They become a project manager with a backlog, accepting whatever the highest-paid person in the room asks for next. The team then loses coherence, the product loses a clear direction, and the Scrum Master is left helping the team manage the consequences of weak ownership. Sheik is careful not to blame only the individual. Many POs are placed in the role without mentoring, without a clear understanding of product ownership, and without the organizational support to say no. The result is predictable: a mountain of requests, no clear value conversation, and a team delivering work without a strong product story behind it.
Self-reflection Question: Where is your Product Owner being treated as an order taker instead of the person accountable for product value?
[The Scrum Master Toolbox Podcast Recommends]
Angela thought she was just there to coach a team. But now, she's caught in the middle of a corporate espionage drama that could make or break the future of digital banking. Can she help the team regain their mojo and outwit their rivals, or will the competition crush their ambitions? As alliances shift and the pressure builds, one thing becomes clear: this isn't just about the product—it's about the people.
🚨 Will Angela's coaching be enough? Find out in Shift: From Product to People—the gripping story of high-stakes innovation and corporate intrigue.
[The Scrum Master Toolbox Podcast Recommends]
About Sheik Meeajaun
Sheik is a seasoned product and Agile leader with over 20 years of experience scaling innovative, customer-centric digital solutions. A certified Scrum and Agile expert, he bridges strategy and execution, driving high-performance teams at enterprises like Rabobank and citizenM. As a hands-on builder, Sheik created Scrumling—a free, interactive Agile training platform—and ScrumJobs.net, a niche job board for Agile professionals. His passion lies in transforming theory into impactful, real-world results.
You can link with Sheik Meeajaun on LinkedIn.
You can also explore Scrumling, ScrumJobs.net, and Simatech.
The Experimental Windows 11 build released September 8, Build 26340.9354, includes an official Microsoft mock-up showing the new Resume hovercard, but it surprisingly shows a new mouse pointer. It’s slightly broader, softer around the lower-left side, and more symmetrical, without the thin tail-like extension we saw for decades in Windows.
Microsoft hasn’t said it changed the system cursor. The release notes for this build do mention fixing a known issue where cursor customization doesn’t work correctly, which we first reported. But nothing about a redesign.

But there’s history here. Back in June, there were reports that Microsoft was internally testing a new Windows 11 mouse pointer as part of the Windows 11 modernization effort. However, Microsoft hadn’t shown the design publicly back then.
Three months later, we may finally have our first public glimpse of what Microsoft was testing. The new cursor will be based on this mock-up. The size and shadow you see here may be different as well when it finally arrives.
Microsoft’s Fluent UI System Icons repository contains a Cursor icon with a noticeably cleaner, wider shape than the traditional Windows arrow. The 32px light PDF version is particularly close in proportion to the shape in Microsoft’s new screenshot, an outlined pointer with a straighter left edge and a more geometric body.

Of course, this resemblance isn’t enough to prove Windows has adopted it. It’s just a design-system icon. Microsoft’s current developer documentation still describes the standard pointer as the “arrow cursor,” and the Windows App SDK lists Arrow as the standard northwest-pointing shape. Microsoft already has a modern cursor-shaped Fluent asset.
The design language itself isn’t new. Fluent System Icons has existed since 2020, and the repository’s commit history stretches back so far. To be honest, it’s high time that Microsoft applies this language to the one element users see all day.

Windows 11 has replaced old Settings pages, dialogs, and File Explorer pieces with modern WinUI over the past year, including a modernized mouse indicator that highlights your cursor when you press Ctrl. The pointer’s shape, meanwhile, has stayed the same arrow for years, with customization limited to size, color, and pointer schemes.
I’ve seen Microsoft use much more stylized cursors in presentations and promotional material over the years and always wondered why the real pointer still looked so plain.

macOS and several Linux desktop environments already have modern softer pointers as part of their visual language for years, while Windows 11 mostly just lets you change its color and size, while still looking like a 32-bit element.
I still remember using ridiculous custom pointers growing up, including banana cursors that made a shared family PC feel like mine. Those are, somehow, still downloadable today. Microsoft has long supported swapping in custom .cur and .ani pointer files, which is what makes Windows, well, Windows!
Note that Build 26340.9354 doesn’t officially announce a new cursor. But the June report of an internally tested pointer redesign, and now this September screenshot showing a pointer that looks different, and a Fluent cursor design in Microsoft’s icon library, all look like we are remarkably close to it.
Personally, I like the new-looking pointer more. It is wider, cleaner, and more symmetrical, and I particularly prefer the lack of the old tail-like extension. Now I just want Microsoft to stop making us guess and ship it already.
The post Is this Windows 11’s new modern cursor? Microsoft quietly shows a redesigned pointer mock-up appeared first on Windows Latest