timer-mcp
by tharun52
README.md
# โฑ๏ธ timer-mcp
<p align="center">
<img src="https://img.shields.io/badge/MCP-streamable--http-blue?style=for-the-badge" />
<img src="https://img.shields.io/badge/FastMCP-3.x-6c5ce7?style=for-the-badge" />
<img src="https://img.shields.io/badge/Python-3.12-3776AB?style=for-the-badge&logo=python&logoColor=white" />
<img src="https://img.shields.io/badge/Docker-ARM64-2496ED?style=for-the-badge&logo=docker&logoColor=white" />
<img src="https://img.shields.io/badge/AWS-AgentCore-FF9900?style=for-the-badge&logo=amazonaws&logoColor=white" />
</p>
<p align="center">
A minimal MCP server that gives AI agents the ability to <strong>wait</strong>.<br/>
One tool. One job. Works locally and on AWS AgentCore Runtime.
</p>
---
## ๐ค The problem it solves
Agents often need to pause between actions โ waiting for a resource to become ready, spacing out API calls, or inserting a deliberate delay in a workflow. Without a timer tool, an agent has no way to wait.
A naive implementation that holds the HTTP connection open for the full duration **fails on AgentCore**: the platform's sidecar proxy enforces a hard ~60 second timeout on individual MCP tool calls, returning:
```
MCP error -32001: Request timed out
```
---
## โ๏ธ How it works
The server caps `MAX_DURATION` at **55 seconds per call** when running on AgentCore โ safely under the proxy limit. For longer waits the agent calls the tool multiple times sequentially, which it works out automatically from the tool description:
```
wait(55) โ "Waited 55 second(s)."
wait(35) โ "Waited 35 second(s)."
โโโโโโโโโโโโโโโโโโโโโโ
total: 90s across 2 calls
```
> **Locally** there is no proxy, so the default `MAX_DURATION` of 840 seconds applies and a single call can hold for up to 14 minutes.
---
## ๐ ๏ธ Tool reference
| Tool | Argument | Description |
|:---|:---|:---|
| `wait` | `seconds: int` | Pause for the specified duration (capped at `MAX_DURATION`). For longer waits, call multiple times sequentially. |
---
## ๐ง Environment variables
| Variable | Default | Description |
|:---|:---|:---|
| `HOST` | `0.0.0.0` | Bind address |
| `PORT` | `8000` | Listen port |
| `MAX_DURATION` | `840` | Max seconds per call. **Set to `55` on AgentCore.** |
---
## ๐ณ Run locally
### Start the server
Build the image
```bash
docker build -t timer-mcp .
```
Run the container
```
docker run -d -p 8000:8000 \
-e HOST=0.0.0.0 \
-e PORT=8000 \
-e MAX_DURATION=840 \
timer-mcp
```
Server is available at `http://localhost:8000/mcp`.
---
### ๐ Connect to your MCP client
Add the following to your MCP client config and restart:
```json
{
"mcpServers": {
"timer": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://localhost:8000/mcp", "--allow-http"]
}
}
}
```
| Client | Config file |
|:---|:---|
| **Claude Desktop** | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| **Claude Code** | `~/.claude/mcp.json` (global) or `.claude/mcp.json` (project) |
| **Cursor** | `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project) |
| **VS Code** | `~/.vscode/mcp.json` (global) or `.vscode/mcp.json` (project) |
---
### ๐งช Smoke test
```bash
# Initialize a session
SESSION=$(curl -si -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.1"}}}' \
| grep -i 'mcp-session-id' | awk '{print $2}' | tr -d '\r\n')
# Call wait(5) โ should take ~5s and return "Waited 5 second(s)."
time curl -s -N -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: $SESSION" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"wait","arguments":{"seconds":5}}}'
```
---
## โ๏ธ Deploy to AWS AgentCore
### Prerequisites
- Docker with buildx (ARM64 support)
- AWS CLI with a profile that has ECR and AgentCore permissions
- An ECR repository (`timer-mcp`)
- An AgentCore execution role with ECR pull access
### 1. Push the image to ECR
Uncomment the ECR authentication block in `deploy.sh`, then run:
```bash
bash deploy.sh
```
This builds the ARM64 image and pushes it to:
```
<account-id>.dkr.ecr.us-east-1.amazonaws.com/timer-mcp:latest
```
### 2. Create the AgentCore runtime
```bash
aws bedrock-agentcore-control create-agent-runtime \
--region us-east-1 \
--agent-runtime-name "timer_mcp" \
--agent-runtime-artifact '{
"containerConfiguration": {
"containerUri": "<account-id>.dkr.ecr.us-east-1.amazonaws.com/timer-mcp:latest"
}
}' \
--role-arn "arn:aws:iam::<account-id>:role/<execution-role>" \
--network-configuration '{"networkMode": "PUBLIC"}' \
--protocol-configuration '{"serverProtocol": "MCP"}' \
--environment-variables '{"HOST":"0.0.0.0","PORT":"8000","MAX_DURATION":"55"}' \
--authorizer-configuration '{
"customJWTAuthorizer": {
"discoveryUrl": "https://cognito-idp.<region>.amazonaws.com/<pool-id>/.well-known/openid-configuration",
"allowedClients": ["<client-id>"]
}
}'
```
### 3. Update an existing runtime
AgentCore pulls the image fresh on every invocation โ pushing a new `latest` tag to ECR is enough for code changes, no runtime update needed.
To update environment variables:
```bash
aws bedrock-agentcore-control update-agent-runtime \
--agent-runtime-id <runtime-id> \
--region us-east-1 \
--agent-runtime-artifact '{"containerConfiguration":{"containerUri":"<account-id>.dkr.ecr.us-east-1.amazonaws.com/timer-mcp:latest"}}' \
--role-arn "arn:aws:iam::<account-id>:role/<execution-role>" \
--network-configuration '{"networkMode":"PUBLIC"}' \
--protocol-configuration '{"serverProtocol":"MCP"}' \
--environment-variables '{"HOST":"0.0.0.0","PORT":"8000","MAX_DURATION":"55"}'
```
---
## โ ๏ธ AgentCore proxy timeout
> **This limitation does not apply to local Docker runs.**
AgentCore's sidecar proxy enforces a hard **~60 second** per-request timeout on MCP `tools/call` requests. This is **not configurable** via `lifecycleConfiguration` (which only governs session/microVM lifetime). Any `wait` call exceeding this receives `MCP error -32001: Request timed out`.
Setting `MAX_DURATION=55` on the runtime leaves a 5-second safety margin. The agent chains calls automatically for longer waits:
| Total wait | Calls |
|:---|:---|
| โค 55s | 1 |
| 56s โ 110s | 2 |
| 111s โ 165s | 3 |
| N seconds | `ceil(N / 55)` |
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues