Decide what the workflow actually needs.
Imagine you want a daily project summary. A status lookup may be enough. Editing a record or emailing a client creates different consequences. Before connecting the agent, classify the tools you intend to expose as READ, WRITE, or SEND. A tool can belong in more than one category; reading can also disclose sensitive information.
Our demo exposes one tool: get_project_status. It echoes the supplied project name alongside a fixed, fictional status. Its function accesses no files, accounts, or APIs. The package runtime still executes on your computer, and installation requires downloads.
Download the connection review checklist to record names, inputs, side effects, data destinations, credentials, and review requirements for your own workflow.
Use an empty practice folder.
Install Python and Node.js, then open PowerShell in a new, empty folder. The pinned Python MCP SDK 2.3.0 requires Python 3.10 or newer; Inspector 2.10.1 requires Node 22.19.0 or newer. This exercise ran on Windows with Python 3.13.15 and Node 24.14.0.
python --version
node --version
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install 'mcp==2.3.0'The .venv keeps Python dependencies in the practice folder. It does not sandbox filesystem access or remove operating-system permissions. Calling its interpreter directly avoids activation and execution-policy changes. Version pins make the selected packages reproducible; their dependencies may change.
Download and review the complete example.
Download demo-mcp-server.py, or use the following command in your practice folder. It downloads Python code; review it before continuing.
Invoke-WebRequest -Uri 'https://jabsystems.io/ai-anonymous/Social/demo-mcp-server.py' -OutFile .\demo-mcp-server.py
Get-Content -LiteralPath .\demo-mcp-server.py"""Fictional MCP inspection exercise. No files, accounts, or APIs are accessed."""
from mcp.server import MCPServer
from mcp.types import ToolAnnotations
server = MCPServer("AI Anonymous inspection demo")
@server.tool(annotations=ToolAnnotations(read_only_hint=True, open_world_hint=False))
def get_project_status(project: str) -> dict:
"""Return a fictional project status for inspection practice, not live work data."""
return {
"project": project,
"status": "awaiting human review",
"fictional": True,
"source": "hard-coded demonstration data",
}
if __name__ == "__main__":
server.run()
This uses the version 2 SDK: MCPServer from mcp.server. Its Python annotation names use underscores; the protocol serializes them as readOnlyHint and openWorldHint. The default transport is stdio, so Inspector launches the program and exchanges protocol messages through its input/output streams. Running the Python file by itself waits for messages; it is not an interactive menu.
Inspect the advertised contract.
The Inspector CLI supports tools/list. npm.cmd invokes npm’s Windows launcher directly. The explicit package and binary names keep the command clear; npm installs Inspector and its dependencies into its cache.
npm.cmd exec --yes --package=@modelcontextprotocol/inspector@2.10.1 -- mcp-inspector --cli .\.venv\Scripts\python.exe .\demo-mcp-server.py --method tools/list
Write-Host "Inspector exit code: $LASTEXITCODE"Observed: Inspector exited with code 0 and returned exactly one tool, get_project_status, with a required string input named project, readOnlyHint: true, and openWorldHint: false. Download the observed catalog.
Read the name, description, input schema, and annotations together. An unrestricted path, destination, or command input may deserve more scrutiny than the tool name suggests. Schemas describe accepted input; they do not prove that the implementation handles it safely.
A catalog is a snapshot: other servers may paginate their lists or change exposed tools with credentials and configuration. Review all pages for the actual caller and repeat after relevant changes. This one-tool demo returned no pagination cursor.
Call the fictional lookup yourself.
After reading the implementation, supply the synthetic project name demo. This does not ask an agent to choose or call anything.
npm.cmd exec --yes --package=@modelcontextprotocol/inspector@2.10.1 -- mcp-inspector --cli .\.venv\Scripts\python.exe .\demo-mcp-server.py --method tools/call --tool-name get_project_status --tool-arg 'project=demo'
Write-Host "Inspector exit code: $LASTEXITCODE"Inspector exited with code 0 and returned isError: false. Its text content contained the following JSON, shown here as the decoded payload:
{
"project": "demo",
"status": "awaiting human review",
"fictional": true,
"source": "hard-coded demonstration data"
}Download the full observed response. The status is fixed demonstration data from our code, not a real project update or model-generated answer. A successful call establishes that this sample worked in this environment.
A hint does not remove a permission.
The MCP annotation specification treats these values as hints and warns that untrusted server annotations should not drive tool-use decisions. A read-only hint can describe intent; it cannot disable write access. Likewise, a prompt asking an agent to avoid sending messages does not remove a send tool or revoke its credentials.
For a real connection, our proposed review workflow is:
- Read the implementation and origin: inspect the server package and its configuration before running it. Launching arbitrary code is itself an action.
- Scope the tools and credentials: expose only the operations the job needs and use appropriately limited accounts. Confirm restrictions in the service and runtime.
- Review data destinations: determine what the server and model host receive. A lookup can return sensitive content even without modifying anything.
- Use supported host controls: configure tool approvals and isolation where available, and verify how that host actually behaves.
- Recheck after changes: record server version, credentials, tool catalog, and review date. A changed connection deserves a new review.
These steps are a practical proposal. We did not exercise a third-party server, agent host, account permission system, sandbox, or approval flow for this article.
Verification boundary: Windows, Python 3.13.15, Node 24.14.0, Python MCP SDK 2.3.0, Inspector 2.10.1, the downloadable server, tool listing, and direct fictional tool call were exercised. No language model, real account, agent action, performance improvement, or security guarantee was tested.
If the example stops.
- Python or npm missing: install the prerequisites, reopen PowerShell, and confirm version output.
- Unsupported Node version: use Node 22.19.0 or newer for this pin.
- Import error: confirm the explicit
.venvinterpreter hasmcp==2.3.0. Older FastMCP tutorials use a different API. - Server seems to wait: run the Inspector command, not the Python file alone. Inspector is the client speaking stdio.
- Protocol parse error after adding logging: do not print casual messages to stdout; stdio reserves it for protocol traffic. Use stderr for diagnostics.
- Tool call returns an error: compare the name and required input with the catalog. Exit status and
isErrorshould both be checked.
Follow the contract and the implementation.
Sources include the MCP tools specification, annotation schema, Inspector release, versioned CLI documentation, versioned Python SDK, and Python tool documentation.
Manual social delivery: the graphic, teaser caption, alt text, and full article URL are prepared for the user to post. Nothing is scheduled or published to social media, and no profile-link change is claimed.
