dad-jokes-mcp
Enables fetching and managing dad jokes with tools for random jokes, multiple jokes, categories, and persistent storage.
README
Agent instructions: See
AGENTS.mdfor architecture, commands, and how to add tools.
Available Tools
| Tool Name | Description | Input | Output | Example | curl |
|---|---|---|---|---|---|
get_random_joke |
Fetch a random dad joke and save it | None | Joke text | @dad-jokes-mcp get_random_joke |
curl -X POST http://localhost:5000/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_random_joke","arguments":{}}}' |
get_multiple_jokes |
Fetch multiple random dad jokes at once | count (1-20, default: 5) |
Array of jokes | @dad-jokes-mcp get_multiple_jokes count=10 |
curl -X POST http://localhost:5000/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_multiple_jokes","arguments":{"count":10}}}' |
server_status |
Show server version, sources count, stored jokes, tools | None | JSON status | @dad-jokes-mcp server_status |
curl -X POST http://localhost:5000/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"server_status","arguments":{}}}' |
get_all_jokes |
View all jokes stored in www/jokes.json | None | All saved jokes | @dad-jokes-mcp get_all_jokes |
curl -X POST http://localhost:5000/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_all_jokes","arguments":{}}}' |
clear_jokes |
Delete all saved jokes | None | Confirmation | @dad-jokes-mcp clear_jokes |
curl -X POST http://localhost:5000/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"clear_jokes","arguments":{}}}' |
get_joke_category |
Get joke from category | category (enum) |
Joke from category | @dad-jokes-mcp get_joke_category category=Programming |
curl -X POST http://localhost:5000/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_joke_category","arguments":{"category":"Programming"}}}' |
fill_jokes_batch |
Ensure at least N jokes are stored; fetches if needed | count (1-20, default: 5) |
N jokes from pool | @dad-jokes-mcp fill_jokes_batch count=10 |
curl -X POST http://localhost:5000/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"fill_jokes_batch","arguments":{"count":10}}}' |
add_jokes |
Fetch and store N new jokes unconditionally | count (1-20, default: 5) |
N new jokes added | @dad-jokes-mcp add_jokes count=10 |
curl -X POST http://localhost:5000/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"add_jokes","arguments":{"count":10}}}' |
add_joke |
Add a custom joke manually | text (string) |
Confirmation | @dad-jokes-mcp add_joke text="Why..." |
curl -X POST http://localhost:5000/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"add_joke","arguments":{"text":"Why did the chicken cross the road?"}}}' |
clean_jokes |
Remove null/empty entries from the pool | None | Cleanup result | @dad-jokes-mcp clean_jokes |
curl -X POST http://localhost:5000/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"clean_jokes","arguments":{}}}' |
How MCP Tool Routing Works
The Short Answer
Nej, det er ikke hardkodet. Det er dynamisk routing baseret pΓ₯ tool-navn.
Hvad der sker nΓ₯r du skriver:
@dad-jokes-mcp get_multiple_jokes count=10
Step 1: PA parser din besked
- PA ser
@dad-jokes-mcp= server navn - PA ser
get_multiple_jokes= tool navn - PA ser
count=10= argument
Step 2: PA laver en JSON-RPC request
{
"jsonrpc": "2.0",
"id": 123,
"method": "tools/call",
"params": {
"name": "get_multiple_jokes",
"arguments": {
"count": 10
}
}
}
Step 3: MCP Server modtager request
Serveren (dad_jokes_mcp.mjs) modtager JSON via POST /mcp:
if (request.method === "tools/call") {
const { name, arguments: args } = request.params;
// name = "get_multiple_jokes"
// args = { count: 10 }
**Step 4: Server matcher tool-navn og udfΓΈrer**
Serveren bruger et **handler lookup-objekt** i stedet for if/else:
```javascript
const handlers = {
get_random_joke: async () => { /* ... */ },
get_multiple_jokes: async () => { /* ... */ }, // β MATCH!
get_all_jokes: async () => { /* ... */ },
clear_jokes: async () => { /* ... */ },
get_joke_category: async () => { /* ... */ },
fill_jokes_batch: async () => { /* ... */ },
add_jokes: async () => { /* ... */ },
add_joke: async () => { /* ... */ },
clean_jokes: async () => { /* ... */ },
};
const handler = handlers[name];
if (handler) {
toolResponse = await handler(args);
} else {
toolResponse = { error: { code: -32601, message: `Tool not found: ${name}` } };
}
Step 5: Server returnerer resultat
{
"jsonrpc": "2.0",
"id": 123,
"result": {
"content": [
{
"type": "text",
"text": "π 10 Dad Jokes:\n\n1. ...\n2. ..."
}
]
}
}
Step 6: PA viser resultatet
PA modtager resultatet og viser det for brugeren.
Hvorfor er det ikke hardkodet?
- Tool-navne er defineret dynamisk - I
tools/listmetoden returnerer serveren hvilke tools der findes - Arguments er dynamiske - Hver tool kan have forskellige inputs
- Routing er baseret pΓ₯ string matching -
if (name === "tool_name")matcher kun hvis navn passer
Hvis du tilfΓΈjede en ny tool
Du skulle:
- TilfΓΈje den til
tools/listresponse:
{
name: "my_new_tool",
description: "What it does",
inputSchema: { /* ... */ }
}
- TilfΓΈje en key i
handlers-objektet itools/call:
my_new_tool: async () => {
return { content: [{ type: "text", text: "Result" }] };
},
- PA ville automatisk detektere den næste gang den kalder
tools/list!
MCP Protocol Flow
PA Client MCP Server (dad_jokes_mcp.js)
β β
ββ POST /mcp (initialize) ββββββββββββββββ€
β β
β βββββ { serverInfo, capabilities } βββββ€
β β
ββ POST /mcp (tools/list) ββββββββββββββββ€
β β
β βββββ { tools: [...] } βββββββββββββββββ€
β β
ββ POST /mcp (tools/call) ββββββββββββββββ€
β { name: "get_multiple_jokes", ... } β
β β
β βββββ { result: { content: [...] } } βββ€
β β
API Details
MCP Endpoint: /mcp
Method: POST
Headers:
Content-Type: application/jsonMcp-Session-Id(response header - auto-generated)
Request Format: JSON-RPC 2.0
Example request body for calling a tool:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_random_joke",
"arguments": {}
}
}
Dad Jokes MCP Server
A Model Context Protocol (MCP) server that fetches and manages dad jokes via Streamable HTTP transport. Jokes are automatically saved to persistent storage.
Features
- π― Streamable HTTP Transport - Uses MCP 2024-11-05 protocol
- πΎ Persistent Storage - Jokes automatically saved to
www/jokes.json - π Multiple Joke Sources - Fetches from 9+ joke APIs
- π³ Docker Ready - Full Docker setup with volume mounts
- π§ 10 Tools:
get_random_joke- Fetch a random dad joke and save itget_multiple_jokes- Fetch multiple jokes at onceserver_status- View server status, version, and statsget_all_jokes- View all saved jokesclear_jokes- Clear saved jokesget_joke_category- Get jokes by category (Programming, Knock-knock, General, Chuck Norris)fill_jokes_batch- Ensure at least N jokes are storedadd_jokes- Fetch and store N new jokesadd_joke- Add a custom joke manuallyclean_jokes- Remove null/empty entries
Quick Start
Prerequisites
- Docker & Docker Compose
- Node.js 20+ (for local development)
Run with Docker
docker compose up -d
The server will start on http://localhost:5000/ (frontend) with MCP endpoint at /mcp
MCP Configuration
Add to your MCP client config (Gemini, Page Assist, etc):
{
"mcp": {
"servers": {
"dad-jokes": {
"url": "http://localhost:5000/mcp"
}
}
}
}
Testing
See docs/test.md for the full test suite, JSON fixtures, and gotchas.
Test with curl
# Initialize
curl -X POST http://localhost:5000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
# List tools
curl -X POST http://localhost:5000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
# Get a random joke
curl -X POST http://localhost:5000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_random_joke","arguments":{}}}'
Project Structure
dad-jokes-mcp/
βββ dad_jokes_mcp.mjs # Main server code
βββ package.json # Node.js dependencies
βββ Dockerfile # Docker image definition
βββ docker-compose.yml # Docker Compose config
βββ .dockerignore # Docker build exclusions
βββ www/
β βββ jokes.json # Persistent jokes storage
βββ .git/ # Git repository
Jokes Storage
Jokes are automatically saved to www/jokes.json with the following structure:
[
"Chuck Norris can understand women.",
"Why did the scarecrow win an award? He was outstanding in his field.",
"..."
]
The file persists across container restarts due to Docker volume mounting.
API Details
MCP Endpoint: /mcp
Method: POST
Headers:
Content-Type: application/jsonMcp-Session-Id(response header - auto-generated)
Request Format: JSON-RPC 2.0
Example request body for calling a tool:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_random_joke",
"arguments": {}
}
}
Environment Variables
PORT- Server port (default: 5000)
Docker Volumes
./www:/app/www- Mounts localwww/directory for persistent joke storage
Development
Local Development (without Docker)
npm install
PORT=5000 node dad_jokes_mcp.mjs
Build Docker Image
docker compose build
View Logs
docker logs dad-jokes-mcp-server -f
Troubleshooting
MCP Validation Failed
If you see "Legacy MCP SSE endpoints are not supported", ensure your MCP client is configured to use the Streamable HTTP endpoint:
http://localhost:5000/mcp β Correct
http://localhost:5000/sse β Deprecated (SSE)
No Jokes Saved
- Check Docker volume mount:
docker compose config | grep volumes - Verify
www/directory exists:ls -la www/ - Check container logs:
docker logs dad-jokes-mcp-server
API Connection Issues
- Ensure server is running:
curl http://localhost:5000/ - Check port availability:
netstat -an | grep 5000 - Verify firewall allows port 5000
Performance
- Joke fetching: ~1-3 seconds (with retries)
- JSON parsing: <100ms
- File I/O: ~50-200ms depending on disk speed
License
MIT
Author
Michael G. Nielsen
Dad jokes duh π
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.