ft_mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ft_mcpget the current system time"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ft_mcp
A Model Context Protocol (MCP) server implemented from scratch in Python, without an MCP SDK.
MCP is an open, JSON-RPC 2.0 protocol that lets an AI host — such as Claude Desktop or the MCP Inspector — discover and invoke tools exposed by an external server over a standard transport. This project implements the server side of that protocol directly: the stdio framing, the JSON-RPC routing, the lifecycle handshake, schema validation, and the tool layer.
The implementation deliberately avoids an MCP SDK so the protocol mechanics stay explicit
rather than hidden behind a library. The core (stdio transport) depends only on the Python
standard library; aiohttp is needed only for the optional HTTP/SSE transport.
Scope
Transports: stdio (primary); Streamable HTTP + SSE (optional).
Lifecycle:
initialize/notifications/initializedwith protocol-version negotiation and capability advertisement.Tools: registry,
tools/list,tools/call, JSON Schema (draft 2020-12) validation. Two reference tools:get_system_timeandcalculate_file_hash.Additional primitives:
prompts/list,prompts/get,resources/list,resources/read.Diagnostics: structured logging and per-method timing, written to stderr only.
Supported protocol versions: 2025-11-25 and 2025-03-26.
Related MCP server: Developer Toolbox MCP
Requirements
Python 3.10 or newer. The stdio server needs nothing else.
Installation
git clone https://github.com/alsaeed3/ft_mcp.git
cd ft_mcp
pip install -e . # core (stdio) only
pip install -e ".[http]" # add the optional HTTP/SSE transport
pip install -e ".[dev]" # add pytestInstallation is optional; the server also runs directly from the source tree with
python -m ft_mcp.
Usage
The server reads JSON-RPC messages from stdin, writes responses to stdout, and logs to stderr.
python -m ft_mcpA full session — handshake, tool discovery, and a tool call — driven by hand:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25"}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
'{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_system_time","arguments":{}}}' \
| python -m ft_mcpConnecting a host
MCP Inspector:
npx @modelcontextprotocol/inspector python -m ft_mcpClaude Desktop — add the server to claude_desktop_config.json:
{
"mcpServers": {
"ft_mcp": {
"command": "python",
"args": ["-m", "ft_mcp"],
"cwd": "/absolute/path/to/ft_mcp"
}
}
}Architecture
The server is organized into three layers, kept independent of one another so each can be reasoned about and tested on its own. The same router serves both transports unchanged.
┌──────────────────────────────────────────────┐
stdin ─────▶ │ Transport (ft_mcp/transport) │
│ newline-delimited framing; flush-after-write │
stdout ◀───── │ stdout carries protocol bytes only │
└───────────────────────┬──────────────────────┘
│ one UTF-8 line per message
▼
┌──────────────────────────────────────────────┐
│ JSON-RPC 2.0 router (ft_mcp/router) │
│ parse → classify → dispatch │
│ lifecycle gate; error taxonomy │
└───────────────────────┬──────────────────────┘
│ validated params
▼
┌──────────────────────────────────────────────┐
│ Business logic (ft_mcp/tools) │
│ tool registry, schemas, prompts, resources │
└──────────────────────────────────────────────┘Two constraints shape the transport layer. First, stdout is the protocol channel, so it carries nothing but newline-terminated JSON-RPC messages, flushed after each write; all diagnostics go to stderr. Second, stdin is read without blocking the event loop, so a long-running tool does not stall message intake.
A more detailed description — the lifecycle state machine, message classification, the error taxonomy, and the security model — is in docs/ARCHITECTURE.md.
Tools
Tool | Arguments | Description |
| (none) | Returns the current UTC time as an ISO 8601 string. Its schema sets |
|
| Streams a file in 64 KiB chunks and returns its hex digest. The path is canonicalized and confined to an allowed root. |
Each tool declares a JSON Schema (draft 2020-12) inputSchema. The router validates
tools/call arguments against it and returns -32602 on a mismatch before the handler
runs.
Error handling
The server distinguishes protocol errors from tool errors, because a host treats them differently.
Category | Example | Response |
Protocol | truncated JSON |
|
Protocol | missing |
|
Protocol | unknown method or tool |
|
Protocol | schema-invalid params |
|
Protocol | unexpected handler exception |
|
Tool logic | hashing a missing file | success result with |
Tool failures are returned as successful responses carrying isError: true rather than
JSON-RPC errors, so the model receives the message as feedback. Application-defined codes
(for example the HTTP transport's invalid-session -32000) stay outside the reserved
-32768..-32000 range.
Security
Tool inputs are treated as untrusted. calculate_file_hash resolves paths with
os.path.realpath (so symlinks are followed to their target before the containment
check), performs a boundary-aware check against an allowed root, and opens the file with
O_NOFOLLOW to avoid a time-of-check/time-of-use swap. The HTTP transport binds to
loopback, validates the Origin header against loopback for browser clients
(DNS-rebinding protection), and echoes only the validated origin in CORS headers.
Optional HTTP/SSE transport
pip install -e ".[http]"
python -m ft_mcp --http --host 127.0.0.1 --port 8080GET /mcp/sse opens an event stream and announces a POST endpoint; POST /mcp/messages
accepts JSON-RPC and correlates each reply back onto the matching SSE session.
Testing
pip install -e ".[dev]"
python -m pytest -qThe suite in tests/test_protocol.py runs the server as a
subprocess and asserts its responses at the protocol boundary — parse and invalid-request
errors, the initialize handshake, tools/list and tools/call, tool errors surfacing as
isError, stdout purity, and clean shutdown on EOF.
Project layout
ft_mcp/
├── __main__.py # entry point and handler wiring (stdio / --http)
├── log.py # stderr-only logger
├── transport/
│ ├── stdio.py # async stdin reader, flush-after-write writer
│ └── http.py # optional HTTP + SSE transport
├── router/
│ ├── dispatch.py # JSON-RPC parse, classify, dispatch, validation
│ ├── lifecycle.py # session state machine
│ └── errors.py # error codes and helper
└── tools/
├── registry.py # tool dataclass and registry
├── system_time.py # get_system_time
├── file_hash.py # calculate_file_hash
├── prompts.py # prompts and resources
└── telemetry.py # per-method timing → stderr
tests/test_protocol.py # protocol compliance suiteLicense
Available Tools
2 toolscalculate_file_hashA
Calculate the hash of a file using the specified algorithm
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Path to the file to hash | |
| algorithm | Yes | Hashing algorithm to use |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full burden. It states the action but does not disclose any behavioral traits such as file size limits, permissions, side effects, or potential errors. However, the operation is simple and non-destructive, so the gap is moderate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence with no redundancy or wasted words. It is concise and front-loaded with the essential action.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has only 2 required parameters and no output schema or nested objects, the description is largely complete. However, it could mention the return format (e.g., hex string) for full clarity. Still, it is adequate for selection and invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% with each parameter described. The description adds minimal extra meaning beyond the schema, only tying 'specified algorithm' to the parameter. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'calculate', the resource 'file', and the result 'hash', with the specified algorithm. It is specific and distinct from the sibling tool 'get_system_time'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit guidance on when to use or prerequisites, but the context is straightforward and the sibling tool is unrelated, so the purpose is self-explanatory.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_system_timeA
Get the current system time in ISO 8601 format
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so the description carries the burden. It states the output format, which is adequate for a simple read operation. No additional behavioral traits like idempotency or side effects need disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence, no wasted words. Perfectly concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter tool with a simple output, the description is complete. No output schema is needed; the format is specified.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, and the schema coverage is 100%. The description adds no parameter info because none exist.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb (Get) and the resource (current system time) with format specification (ISO 8601). It distinguishes from the sibling 'calculate_file_hash' which is unrelated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies when to use (when needing current time in ISO 8601). No explicit when-not or alternatives, but given the sibling is unrelated, this is sufficient.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
TDQS
The two tools, file hashing and system time, are in completely different domains with no overlap, making them easily distinguishable.
Both tools follow a clear verb_noun pattern with snake_case, providing a consistent naming convention.
With only two unrelated tools, the server feels thin and lacks a cohesive purpose; a more focused set would be appropriate.
Each domain has only a single tool, missing common operations (e.g., different hash algorithms or time zone variants), resulting in an incomplete surface.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
A simple MCP server built with FastMCP and python
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
MCP server for progressive tool usage at any scale (see https://klavis.ai)
A basic MCP server to operate on the Postman API.
Related MCP Servers
- -licenseNot gradedqualityNot gradedmaintenanceA basic MCP server built with FastMCP framework that provides example tools including message echoing and server information retrieval. Supports both stdio and HTTP transports for integration with various MCP clients.
- FlicenseNot gradedqualityCmaintenanceA production-ready MCP server providing file, system, math, and text utilities through a simple CLI client.
- FlicenseNot gradedqualityBmaintenanceA Python-based MCP server providing essential utility tools: web fetching, current time, and calculator functionality.
- AlicenseNot gradedqualityCmaintenanceA minimal MCP server providing tools for time, calculations, sandboxed file operations, and safe URL fetching over stateless HTTP.16ISC
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/alsaeed3/ft_mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server