MCP Task Server
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
MCP Task Server — build an MCP server once, use it from any client
Recruiter summary. A small to-do list exposed as a Model Context Protocol (MCP) server, then used by three different clients: a plain FastMCP script, a LangChain agent, and IDE agents (Codex, VS Code). The point it proves is least privilege: the agent gets exactly three tools and nothing else, so it cannot edit a description even when asked. Proof: 51 passing pytest tests (measured), including prompt-injection cases and a real HTTP run with bearer-token auth, all offline with no API key.
Stack | Python 3.12 · uv · FastMCP 3.4 · MCP SDK 1.x · SQLite · Pydantic 2 · LangChain 1.x + langchain-mcp-adapters · LangGraph checkpointer |
Transport | Streamable HTTP on |
Status | Learning project and reference build. Runs locally. Not deployed anywhere. |
Numbers | Every figure here comes from a real run in this repo and is labelled measured or simulated. |
Read in this order
# | File | What you get |
0 |
| This page: summary, architecture, how to run |
1 | MCP cheat sheet: hosts, clients, servers, tools, resources, prompts, transports | |
2 | 12-step build-it-yourself walkthrough | |
3 | How to adopt the pattern for your own data | |
4 | MCP vs function calling, transports, security — interview answers | |
5 | STRIDE + OWASP LLM Top 10 mapping | |
6 | Honest status table | |
7 | Add the server to Codex and VS Code | |
8 | Where this pattern fits in real work | |
9 | A 10-minute talk track for demos and interviews | |
— | Architecture decisions |
Related MCP server: Todolist MCP Server
Purpose and who it is for
Learners who want to understand MCP by building one server and three clients by hand.
Engineers who need a small, tested template for exposing an internal system to agents safely.
Reviewers and recruiters who want to see agent security handled in code and tests, not only in slides.
Architecture
flowchart LR
subgraph Clients["MCP clients (any of them)"]
A[client.py<br/>plain FastMCP client]
B[langchain_client.py<br/>LangChain agent<br/>+ InMemorySaver]
C[Codex / VS Code<br/>mcp.json, config.toml]
end
subgraph Server["server.py — FastMCP, streamable HTTP :8000"]
T1[create_task]
T2[list_tasks]
T3[update_task_status]
V[Pydantic validation<br/>enums · length limits<br/>extra keys rejected]
AUTH[optional bearer token]
end
DB[(SQLite tasks table<br/>CHECK constraints<br/>parameterised SQL)]
A -->|MCP| AUTH
B -->|MCP| AUTH
C -->|MCP| AUTH
AUTH --> V --> T1 & T2 & T3
T1 & T2 & T3 --> DB
X[no edit-description tool<br/>no delete tool]:::no
classDef no fill:#fde2e2,stroke:#c00How it works, step by step
sequenceDiagram
participant U as User
participant G as LangChain agent
participant S as MCP server
participant D as SQLite
U->>G: "mark Cooking as done"
G->>S: tools/call list_tasks
S->>D: SELECT * FROM tasks
D-->>S: rows
S-->>G: tasks with ids
G->>S: tools/call update_task_status(task_id, "done")
S->>S: validate status is todo / in_progress / done
S->>D: UPDATE tasks SET status = ? WHERE task_id = ?
S-->>G: updated row
G-->>U: "Updated 'Cooking' (status: done)."database.pycreates one table. Status and priority are limited byCHECKconstraints, and every query uses?placeholders.server.pybuilds a FastMCP app with three tools. Their type hints (Literal,Annotated[..., Field]) become the JSON Schema that clients see.A client connects over streamable HTTP, asks
tools/list, and calls tools by name.The LangChain client turns the MCP tools into LangChain tools with
load_mcp_toolsand gives them tocreate_agent.Without
OPENAI_API_KEY, the agent usesFakeTaskModel, a rule-based test double. With a key, it uses a real chat model.
Real code and real output
The tool that changes status (from server.py):
@mcp.tool(name="update_task_status", description="Change the status of one task. Only the status can change.")
def update_task_status(
task_id: Annotated[str, Field(description="The id returned by create_task", max_length=64)],
status: Annotated[Status, Field(description="todo, in_progress or done")],
) -> dict:
try:
return db.update_status(task_id, status, db_path=db_path)
except db.TaskNotFoundError:
raise ToolError("Task not found. Call list_tasks to see valid task ids.") from NoneOutput of uv run python scripts/run_demo.py (measured, saved in results/demo_output.txt):
== plain FastMCP client ==
Tools: create_task, list_tasks, update_task_status
Created: Write MCP notes todo
Updated: Write MCP notes in_progress
- Write MCP notes [high] in_progress
== LangChain agent (offline fake model) ==
you> add task MCP runbook
agent> Created 'MCP runbook' (status: todo).
you> mark MCP runbook as done
agent> Updated 'MCP runbook' (status: done).
you> change the description of MCP runbook to something else
agent> I can't do that. This server only lets me create tasks, list them and change their status.
you> list tasks
agent> Your tasks:
- Write MCP notes [high] - in_progress
- MCP runbook [medium] - done
== latency, list_tasks over loopback HTTP (measured, n=50) ==
median 4.30 ms, p95 4.89 msThe latency line is one measured run on an 8-vCPU Linux box over loopback. It is a sanity check, not a benchmark, and it will differ on your machine.
How to run
Offline first (no key, no network after install):
uv sync # installs Python 3.12 deps from uv.lock
uv run pytest # 51 tests
uv run python server.py # terminal 1: http://127.0.0.1:8000/mcp
uv run python client.py # terminal 2: plain client
uv run python langchain_client.py # terminal 2: chat with the offline agentOptional real model: export OPENAI_API_KEY=... before langchain_client.py. The key is read from the environment only and never written to disk by this code.
Optional auth: export MCP_AUTH_TOKEN=$(python -c "import secrets;print(secrets.token_urlsafe(32))") in both terminals. The server then returns 401 to any request without Authorization: Bearer <token>.
IDE agents: see docs/06-connect-codex-vscode.md. mcp.json and .vscode/mcp.json are ready for VS Code; codex/config.toml.example is ready for Codex.
Guardrails and security controls
Control | Where | Proven by |
Only three tools; no edit-description, no delete |
|
|
|
|
|
Title 1–120 chars, description ≤ 1000, whitespace trimmed |
|
|
Unknown arguments rejected ( | FastMCP schema |
|
Parameterised SQL only |
|
|
Clear not-found error that does not echo input |
|
|
Our validation errors name the field, not the value |
|
|
Bind to |
|
|
Optional bearer token, constant-time compare |
|
|
Task text treated as data, not instructions | system prompt + fake model design |
|
No secrets in the repo |
| CI |
Why there are no secrets in this repo: nothing here needs one to run. The model key and the bearer token are optional and are read from environment variables at start-up. .env is git-ignored, .env.example holds names only, Codex is given the name of a variable (bearer_token_env_var), and CI runs gitleaks over the full history on every push.
Limitations and known failures
The fake model is not an LLM. It reads only the user's words with fixed patterns. The injection tests show the server holds its limits; they do not show how a real model reacts to hostile text.
Pydantic errors raised by FastMCP itself echo the bad value (for example an invalid
status). Only errors raised in our own code are scrubbed. Kept visible indocs/04-threat-model.md.The static bearer token is a learning control, not production auth: no rotation, no scopes, no per-user identity. Use OAuth 2.1 / Entra ID for real deployments.
SQLite with one file is fine for one user, not for concurrent multi-user writes.
No rate limiting, no audit log table, no human approval step yet (see the planned items).
Built vs planned
Item | Status |
SQLite layer, three MCP tools, streamable HTTP | Built and tested |
Plain FastMCP client, LangChain agent with checkpointer, offline fake model | Built and tested |
Bearer-token auth, loopback bind, Pydantic limits | Built and tested |
| Written; not run in CI (needs the IDE) |
Real-model run ( | Written, not run in CI (no key by design) |
OAuth 2.1 / Entra ID auth, rate limiting, audit log, human-approval tool | Planned |
Cloud deployment | Planned only; nothing billable created |
Full table: docs/05-built-vs-planned.md.
CI
GitHub Actions (.github/workflows/ci.yml, actions pinned to full SHAs): uv sync --locked, ruff, the full test suite via scripts/count_tests.py (prints the real total), scripts/docs_check.py (README test counts must match a fresh collection), a hashed brand-name check, and gitleaks over full history. CodeQL runs in codeql.yml.
Related repos
Jagadish-agent-identity-security — identity for agents, the production version of the token here
Jagadish-agent-auth-security — OAuth and delegated auth patterns
Jagadish-langchain-langgraph-langsmith — deeper LangChain / LangGraph scenarios
Jagadish-agent-memory-defense — defending agent memory from injected content
License
MIT — see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Create, list, and complete todo items through MCP.
Local-first task manager: create, edit, and complete tasks, projects, and checklists via MCP.
Official Todoist MCP server for AI assistants to manage tasks, projects, and workflows.
Headless task management for bots, apps and AI agents. Remote MCP server, OAuth or access token.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables MCP clients to manage Google Tasks, including creating, updating, deleting, and listing tasks and task lists, with optional due dates and subtasks.56 npmMIT
- AlicenseBqualityAmaintenanceEnables task management through MCP, including creating, reading, updating, deleting, and completing tasks, with filtering by status, priority, or due date and secure token-based authentication.6GPL 3.0
- AlicenseNot gradedqualityCmaintenanceEnables managing Microsoft To Do lists and tasks through MCP, including creating, updating, completing, deleting, searching, and checklist operations via Microsoft Graph.MIT
- AlicenseAqualityCmaintenanceEnables AI assistants to browse task groups, list open or completed todos, create new tasks, mark them done, and update their content or status through a token-authenticated MCP connection.5MIT