Skip to main content
Glama
tharun52

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)` |