dino-mcp
A production-grade MCP server with interactive HTML dashboard UI for exploring dinosaur facts, data, and filtering, written in Go.
README
<p align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/badge/🦕-dino--mcp-8b5cf6?style=for-the-badge&labelColor=1a1a2e&color=a78bfa"> <img alt="dino-mcp" src="https://img.shields.io/badge/🦕-dino--mcp-8b5cf6?style=for-the-badge&labelColor=f0f0ff&color=7c3aed"> </picture> </p>
<p align="center"> <em>A production-grade MCP server with interactive HTML dashboard UI — written in Go, styled for delight.</em> </p>
<div align="center">
</div>
📋 Table of Contents
- What — and Why
- Quick Start
- Architecture at a Glance
- Features
- Try It
- Tool Reference
- Claude Desktop Integration
- Development
- Documentation Map
- Project Status
🦕 What — and Why
dino-mcp is a reference implementation of the Model Context Protocol (MCP) in Go that demonstrates every layer of the modern MCP stack:
| Layer | Implementation | Why it matters |
|---|---|---|
| Transport | stdio + Streamable HTTP |
Works in Claude Desktop AND web browsers |
| MCP Apps | @modelcontextprotocol/ext-apps App class |
Interactive HTML UIs in Claude Desktop iframes |
| Tools | dino_think, dino_ask, dino_dashboard |
Typed Go handlers, structured JSON results |
| Resources | //go:embed HTML → text/html;profile=mcp-app |
Self-contained ~11MB binary, zero deps at runtime |
Whether you're building an MCP server from scratch, learning the MCP Apps protocol, or need a Go — Gin — ext-apps SDK integration blueprint, this project has you covered.
⚡ Quick Start
# Clone & enter
git clone https://github.com/shennawardana23/mcp-dino.git && cd mcp-dino
# Build & run in one shot (≈2 seconds)
make build-fast && make dev-http
# Open the standalone dashboard
open http://localhost:9010/dashboard
<details> <summary><b>Expected output</b> — click to expand</summary>
=== dino-mcp server ===
Transport: http
Listening on :9010
[GIN] 2026/06/21 - 12:30:00 | 200 | 4.2ms | ::1 | GET "/dashboard"
[GIN] 2026/06/21 - 12:30:01 | 200 | 2.1ms | ::1 | GET "/api/dinosaurs"
</details>
🏗 Architecture at a Glance
flowchart TB
subgraph CLI["CLI Layer"]
STDIO["stdio subcommand"]
HTTP["http subcommand"]
end
subgraph SERVER["Server (internal/server/)"]
GIN["Gin Router :9010"]
MCPH["MCP StreamableHTTPHandler"]
CORS["CORS Middleware"]
TOOLS["Tools: think · ask · dashboard"]
RES["Resources: //go:embed HTML"]
end
subgraph UI["View (ui/src/)"]
APP["ext-apps App class"]
POST["postMessage protocol"]
end
subgraph FALLBACK["Standalone Fallback"]
DASH["/dashboard (HTML)"]
API["/api/dinosaurs (JSON)"]
end
CLI --> GIN
GIN --> CORS
CORS --> MCPH
MCPH --> TOOLS
TOOLS --> RES
RES --> APP
APP --> POST
MCPH -.->|"MCP Apps"| APP
GIN -.->|"direct route"| DASH
GIN -.->|"direct route"| API
style CLI fill:#1a1a2e,color:#e0e0e0,stroke:#2d2a44
style SERVER fill:#1a1a2e,color:#e0e0e0,stroke:#2d2a44
style UI fill:#1a1a2e,color:#e0e0e0,stroke:#2d2a44
style FALLBACK fill:#1a1a2e,color:#e0e0e0,stroke:#2d2a44
style STDIO fill:#2d2a44,color:#a78bfa
style HTTP fill:#2d2a44,color:#a78bfa
style GIN fill:#0099e5,color:#fff
style MCPH fill:#a78bfa,color:#fff
style TOOLS fill:#22c55e,color:#fff
style RES fill:#22c55e,color:#fff
style APP fill:#facc15,color:#000
style POST fill:#facc15,color:#000
style DASH fill:#f87171,color:#fff
style API fill:#f87171,color:#fff
Data flows through three pipes:
| Pipe | Protocol | Client | Use case |
|---|---|---|---|
| MCP Tools | JSON-RPC over stdio | Claude Desktop | Text tools (dino_think, dino_ask) |
| MCP Apps | JSON-RPC over stdio + postMessage | Claude Desktop iframe | Interactive UI (dino_dashboard) |
| Standalone | HTTP GET | Browser | Direct access (/dashboard, /api/dinosaurs) |
✨ Features
<details open> <summary><b>MCP Standards Compliance</b></summary>
| Feature | Status | Notes |
|---|---|---|
Tools (tools/list, tools/call) |
✅ Complete | 3 typed tools with structured JSON responses |
Resources (resources/list, resources/read) |
✅ Complete | //go:embed HTML served at ui:// URIs |
| MCP Apps protocol | ✅ Complete | _meta.ui.resourceUri + ui/initialize handshake |
| stdio transport | ✅ | Claude Desktop, Cursor, Copilot |
| Streamable HTTP | ✅ | MCP Inspector, curl, browser, tunnel |
| SSE transport | ❌ Removed | Deprecated in MCP spec v2025-11-25 |
</details>
<details> <summary><b>Developer Experience</b></summary>
- 3-second build cycle —
make build-fast && make dev-http - 7 integration tests —
make testexercises every protocol method - Interactive debugging —
make test-inspectorlaunches MCP Inspector - Remote testing —
make run-tunnelcreates a publictrycloudflare.comURL - No API keys — all dinosaur data is built into the binary
- Zero runtime deps — single static binary with embedded HTML
</details>
<details> <summary><b>Interactive Dashboard</b></summary>
The dino_dashboard tool renders an HTML card grid inside Claude Desktop's iframe:
- Filter by diet — Carnivore, Herbivore, or show All
- Filter by period — Triassic, Jurassic, Cretaceous
- 12 dinosaur species — from T-Rex to Velociraptor
- Fallback mode — open directly at
http://localhost:9010/dashboard
Note: the filter is applied server-side at the time the tool is called. Once opened with a specific filter, the in-app filter buttons can only narrow further within that same result set — they can't widen back out to species the initial call excluded.
The HTML view is built with the official @modelcontextprotocol/ext-apps SDK and communicates via JSON-RPC over postMessage.
</details>
🎮 Try It
In Claude Desktop
Show me the dinosaur dashboard with carnivores
→ Claude detects the MCP App → renders an iframe → you see filterable dinosaur cards
In your browser
open http://localhost:9010/dashboard
→ Standalone HTML with all dinosaur data fetched from the built-in REST API
With MCP Inspector
make test-inspector
→ Opens http://localhost:5173 → connects to http://localhost:9010/mcp
Via curl
# Initialize
curl -s -X POST http://localhost:9010/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}' \
| python3 -m json.tool
# List tools
SID="<session-id-from-above>"
curl -s -X POST http://localhost:9010/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: $SID" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| python3 -m json.tool
# Call dino_think
curl -s -X POST http://localhost:9010/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: $SID" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"dino_think","arguments":{}}}' \
| python3 -m json.tool
🔧 Tool Reference
| Tool | Type | Input | Output | Example Prompt |
|---|---|---|---|---|
dino_think |
Text | {} |
Random fact + species JSON | "Tell me a dinosaur fact" |
dino_ask |
Text | {"question": "..."} |
Answer + question JSON | "What did T-Rex eat?" |
dino_dashboard |
MCP App | {"filter": "Carnivore"} |
HTML iframe + JSON data | "Show me carnivore dinosaurs" |
dino_askcurrently returns the same general dinosaur-era overview regardless of the question asked — it doesn't yet branch on the question text. Tracked as a known limitation.
Example dino_think response:
{
"content": [
{ "type": "text", "text": "🦕 Did you know? The Velociraptor was only about the size of a turkey!" }
],
"structuredContent": {
"fact": "The Velociraptor was only about the size of a turkey",
"species": "Velociraptor"
}
}
Example dino_dashboard response:
{
"content": [
{ "type": "text", "text": "Displaying dinosaur dashboard with 4 dinosaurs (filter: Carnivore)" }
],
"structuredContent": {
"filter": "Carnivore",
"dinosaurs": [
{
"name": "Tyrannosaurus Rex",
"period": "Cretaceous",
"diet": "Carnivore",
"length": "40 ft (12 m)",
"weight": "9 tons (8,000 kg)",
"funFact": "T-Rex had the strongest bite of any land animal ever",
"imageStyle": "bg-red-900"
}
],
"timestamp": "2026-06-21T12:00:00Z"
}
}
💬 Claude Desktop Integration
CLI Mode (stdin/stdout)
Locate the binary and add to your claude_desktop_config.json:
{
"mcpServers": {
"dino-mcp": {
"command": "/absolute/path/to/mcp-dino/bin/dino-mcp",
"args": ["stdio"]
}
}
}
After saving, restart Claude Desktop. You'll see hammer icons (🔨) on tools when chatting — click to invoke directly, or let Claude decide.
HTTP Mode (for debugging)
make dev-http
# Server starts on :9010
🛠 Development
Prerequisites
| Tool | Version | Purpose |
|---|---|---|
| Go | ≥ 1.25 | Server binary |
| Node.js | ≥ 18 | UI build (Vite) |
| cloudflared | any | Tunnel for remote testing |
Commands
# Build — three options
make build # Full: Vite UI + Go binary
make build-fast # Quick: Go binary only (reuses existing UI)
make build-ui # Vite UI only
# Run
make dev-http # HTTP mode with verbose logging
make run-stdio # stdio mode for Claude Desktop
make run-tunnel # HTTP + Cloudflare Tunnel
# Test & verify
make test # 7 integration tests — all must pass
make test-inspector # Launch MCP Inspector in browser
make lint # go vet + go fmt
# Utility
make help # All targets with descriptions
make clean # Remove all build artifacts
Project Structure
mcp-dino/
├── bin/ # Go build output (~11MB static binary)
├── cmd/dino-mcp/main.go # CLI entry point (stdio | http | help)
├── internal/
│ ├── server/
│ │ └── server.go # Composition root: mcp.Server + Gin + CORS
│ ├── tools/
│ │ ├── tools.go # Shared types, constants, helpers
│ │ ├── think.go # RegisterThink (dino_think tool)
│ │ ├── ask.go # RegisterAsk (dino_ask tool)
│ │ └── dashboard.go # RegisterDashboardTool + 12 dino species + REST API
│ └── resources/
│ ├── dashboard.go # RegisterDashboardResource + //go:embed HTML
│ └── dashboard_ui.html # Vite-built HTML (354KB)
├── ui/
│ └── src/
│ └── mcp-app.ts # ext-apps App class + postMessage
├── docs/ # Diátaxis documentation (see below)
├── test_mcp.sh # 7 integration tests
├── AGENTS.md # AI agent instructions (canonical)
├── ARCHITECTURE.md # C4 diagrams + sequence flows
├── TECH_DESIGN.md # Interface contracts + data model
├── Makefile # All targets
├── go.mod + go.sum # Go dependencies
└── README.md # ← you are here
🗺 Documentation Map
dino-mcp uses the Diátaxis framework — four documentation modes, each serving a different need.
| For this audience | Start here | Audience |
|---|---|---|
| 👋 New to the project | Quick Start | Everyone |
| 🧑💻 Adding a tool | Your First Tool | Developers |
| 🦕 Adding a dinosaur | Add a Dinosaur | Content editors |
| 🧪 Testing with Inspector | Test with Inspector | QA / Developers |
| 🔍 Reference needed | CLI Reference | Operators |
| 🏗 Understanding design | Architecture | Architects |
| 🤖 Implementing via AI | AGENTS.md | AI coding agents |
| 📚 Deep architecture | ARCHITECTURE.md | Senior engineers |
| 📐 Technical specs | TECH_DESIGN.md | Implementation teams |
| ⏳ Development history | MEMORY.md | All contributors |
| 📋 Roadmap | PLAN.md | Stakeholders |
| ⚖️ Design trade-offs | DESIGN.md | Architects |
| 🎯 Skills reference | SKILL.md | Developers / AI agents |
| 🤝 How to contribute | CONTRIBUTOR.md | Contributors |
| 📜 Code of conduct | CODE_CONDUCT.md | Community |
| 📄 ADRs | docs/adr/ | Decision historians |
| 🤖 LLM full context | llms-full.txt | AI agents (RAG) |
📊 Project Status
MVP ── Production ── Enhanced UI ── Ecosystem ── Advanced
● ○ ○ ○
| Phase | Status | Highlights |
|---|---|---|
| MVP | ✅ Complete | 3 tools, MCP Apps UI, 7 tests, docs |
| Production | 🔄 In progress | Go unit tests, CI, rate limiting, Docker |
| Enhanced UI | 📅 Planned | Real-time data, comparison, timeline |
| Ecosystem | 📅 Planned | Homebrew, GitHub releases, MCP Registry |
| Advanced | 💭 Future | Streaming tool inputs, WebSocket sync |
Build Metrics
| Metric | Value |
|---|---|
| Binary size | ~11 MB (compressed) |
| Binary type | Mach-O 64-bit arm64 |
| Go version | 1.25 |
| MCP SDK version | v1.7.0 |
| Dependencies | 30+ Go modules (all indirect) |
| UI bundle | 354 KB embedded HTML (single-file Vite) |
| Test coverage | 7/7 integration tests passing (shell-based; no Go unit tests yet) |
📖 Further Reading
| Resource | Link |
|---|---|
| MCP Specification | spec.modelcontextprotocol.io |
| MCP Go SDK | github.com/modelcontextprotocol/go-sdk |
| MCP Apps Protocol | modelcontextprotocol.io/docs/apps/overview |
| ext-apps SDK | github.com/modelcontextprotocol/ext-apps |
| Gin Web Framework | github.com/gin-gonic/gin |
| Go Programming Language | go.dev |
<p align="center"> Built with ❤️ using <a href="https://go.dev">Go</a>, <a href="https://gin-gonic.com">Gin</a>, <a href="https://github.com/modelcontextprotocol/go-sdk">MCP Go SDK</a>, and <a href="https://github.com/modelcontextprotocol/ext-apps">@modelcontextprotocol/ext-apps</a> </p>
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。