DevTools MCP
by shxheerkhn
README.md
# DevTools MCP
A small developer-utility MCP server, built to learn the Model Context
Protocol end-to-end: server implementation, local testing, using an existing
MCP, public deployment, and Smithery publishing.
## 1. Overview
DevTools MCP exposes four small developer tools over the Model Context
Protocol: explaining an error message, validating/formatting JSON,
generating a regex from a description, and summarizing text with an LLM
(Groq). A minimal TypeScript/Vite dashboard lets you exercise the tools from
a browser, connecting as a real MCP client.
## 2. Why MCP
MCP standardizes how an LLM host (Claude Desktop, an IDE, an agent) discovers
and calls tools, instead of every project inventing its own bespoke
tool-calling API. Building a real MCP server - not a REST API with an MCP
label slapped on - was the primary learning goal of this project.
## 3. Architecture
```
MCP Client
|
MCP Protocol
|
DevTools MCP Server
|-- explain_error (local/deterministic)
|-- format_json (local/deterministic)
|-- generate_regex (local/deterministic)
`-- summarize_text
|
Groq API
|
GPT-OSS 120B
```
The server (`server/server.py`) is an `mcp.server.MCPServer` (MCP Python SDK
v2). It runs over **stdio** for local testing (MCP Inspector, `Client(mcp)`)
and over **Streamable HTTP** (`/mcp`) for remote/browser access. The
TypeScript frontend (`frontend/`) is a genuine MCP client: it uses
`@modelcontextprotocol/sdk`'s `Client` + `StreamableHTTPClientTransport` to
talk to the server directly over Streamable HTTP (CORS-enabled on the
server), not a hand-rolled REST bridge.
## 4. Tools
| Tool | Inputs | What it does |
|---|---|---|
| `explain_error` | `error_message`, `language_or_framework?` | Matches the error against a library of common error patterns (Python/JS/general) and returns a likely cause + practical fix. Local/deterministic. |
| `format_json` | `json_text` | Validates JSON and returns a pretty-printed version, or a precise parse error (line/column). Local/deterministic. |
| `generate_regex` | `description` | Matches the description against a small library of common regex patterns (email, URL, IPv4, date, UUID, etc.) and returns the pattern plus an explanation. Local/deterministic. |
| `summarize_text` | `text`, `max_length?` | Calls Groq (`openai/gpt-oss-120b`) to produce a concise summary. Handles missing credentials, timeouts, and API errors gracefully. |
## 5. Project structure
```
devtools-mcp/
├── server/
│ ├── server.py # MCPServer + tool registration + ASGI app
│ ├── tools.py # explain_error / format_json / generate_regex logic
│ ├── ai.py # Groq-backed summarize_text logic
│ └── tests/
│ └── test_server.py # pytest suite using the SDK's in-memory Client
├── frontend/
│ ├── index.html
│ ├── src/
│ │ ├── main.ts # real MCP client (StreamableHTTPClientTransport)
│ │ └── style.css
│ ├── package.json
│ ├── tsconfig.json
│ └── vite.config.ts
├── .env.example
├── .gitignore
├── requirements.txt
├── render.yaml # optional Render Blueprint
├── README.md
└── EXISTING_MCP_EXPERIENCE.md
```
## 6. Prerequisites
- Python 3.10+
- Node.js 18+ and npm (for the frontend, and to run the MCP Inspector via `npx`)
- A [Groq](https://console.groq.com/) API key (only required for `summarize_text`)
- (Optional, for deployment) A [Render](https://render.com/) account and a
[Smithery](https://smithery.ai/) account
## 7. Installation
```bash
git clone <this-repo>
cd devtools-mcp
python3 -m venv .venv
. .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
```
## 8. Environment variables
Copy `.env.example` to `.env` and fill in what you need:
```
GROQ_API_KEY= # required for summarize_text
GROQ_MODEL=openai/gpt-oss-120b
MCP_ALLOWED_HOSTS= # only needed when deployed behind a real hostname
MCP_ALLOWED_ORIGINS= # comma-separated browser origins allowed via CORS
```
`.env` is git-ignored. Never commit real secrets.
## 9. Local setup
Stdio (default, for local MCP clients):
```bash
python -m server.server
```
Streamable HTTP (for the frontend, or any HTTP-based MCP client), local only:
```bash
uvicorn server.server:app --host 127.0.0.1 --port 8000
```
`MCP_ALLOWED_HOSTS` can stay unset locally - the SDK's built-in
localhost-only DNS-rebinding protection covers `127.0.0.1`/`localhost`
automatically. Health check: `curl http://127.0.0.1:8000/health`.
## 10. MCP Inspector testing
```bash
# Against stdio:
uv run mcp dev server/server.py # requires uv; or: npx @modelcontextprotocol/inspector
# Against a running Streamable HTTP server:
npx @modelcontextprotocol/inspector --cli http://127.0.0.1:8000/mcp --method tools/list
```
This was run against the local Streamable HTTP server during development and
verified all four tools are discoverable with correct input/output schemas
(see "Testing" below for the exact results).
## 11. Frontend setup
```bash
cd frontend
npm install
npm run dev # http://localhost:5173
```
In the running dashboard, set the server URL field to your MCP server's
`/mcp` endpoint (default `http://localhost:8000/mcp`), click **Connect**,
pick a tool, fill in the form, and click **Run**. For local use, start the
backend with `MCP_ALLOWED_ORIGINS=http://localhost:5173` so CORS allows it.
Production build: `npm run build` (outputs to `frontend/dist/`).
## 12. Groq setup
1. Create an API key at [console.groq.com](https://console.groq.com/).
2. Set `GROQ_API_KEY` (and optionally `GROQ_MODEL`, default
`openai/gpt-oss-120b`) in your `.env` or your deployment platform's
environment variables.
3. No other LLM provider is used anywhere in this project.
## 13. Existing MCP experience
See [`EXISTING_MCP_EXPERIENCE.md`](./EXISTING_MCP_EXPERIENCE.md) for the
required demonstration of using an existing MCP server (Context7) - what it
is, how it was connected, the actual query run, and what was learned.
## 14. Deployment to Render
Render's native Python runtime is used (no Docker required).
**Dashboard setup:**
1. Push this repo to GitHub.
2. In Render: New → Web Service → connect the repo.
3. Runtime: Python 3. Build command: `pip install -r requirements.txt`.
Start command: `uvicorn server.server:app --host 0.0.0.0 --port $PORT`.
4. Set environment variables: `GROQ_API_KEY`, `GROQ_MODEL`,
`MCP_ALLOWED_HOSTS=<your-service>.onrender.com,<your-service>.onrender.com:*`,
and `MCP_ALLOWED_ORIGINS=<your-frontend-origin>` (if you also deploy the
frontend).
5. Deploy. The MCP endpoint will be `https://<your-service>.onrender.com/mcp`.
A `render.yaml` Blueprint is included as a convenience for the same setup.
**Manual verification step required:** actually deploying requires a Render
account and was not performed as part of this response - see the
completion report for what remains a manual step.
## 15. Smithery publishing
The current Smithery CLI supports publishing an already-hosted remote MCP
server URL directly (no Docker/container packaging needed for this path):
```bash
npm install -g smithery
smithery auth login
smithery mcp publish "https://<your-service>.onrender.com/mcp" -n "<your-org>/devtools-mcp"
```
After publishing, verify the four tools are exposed:
```bash
smithery mcp add "https://<your-service>.onrender.com/mcp" --id devtools-mcp
smithery tool list devtools-mcp
```
**Manual step required:** this needs a Smithery account and a live, publicly
reachable Render deployment first; it was not performed as part of this
response.
## 16. Public MCP usage
Once deployed, any Streamable HTTP MCP client can connect to:
```
https://<your-service>.onrender.com/mcp
```
Example with the SDK's `Client`:
```python
from mcp import Client
from mcp.client.streamable_http import streamable_http_client
async with streamable_http_client("https://<your-service>.onrender.com/mcp") as (r, w, _):
async with Client(r, w) as client:
await client.initialize()
print(await client.list_tools())
```
## 17. Testing
Actually run in this environment:
```bash
pytest server/tests/ -v
```
Result: **11 passed** - tool discovery; valid, malformed, and empty
`format_json` input; matched and unmatched `explain_error` patterns
(including empty input); `generate_regex` for a known pattern (with a live
regex match check) and an unmatched description; `summarize_text` with a
missing `GROQ_API_KEY` and with empty input.
Also actually run (manual, outside pytest):
- `uvicorn server.server:app` started successfully; `/health` returned
`{"status":"ok",...}`.
- A raw `initialize` JSON-RPC POST to `/mcp` returned `200`.
- The real MCP Inspector CLI (`npx @modelcontextprotocol/inspector --cli`)
connected over Streamable HTTP, listed all four tools with correct
schemas, and successfully called `generate_regex`, `explain_error`, and
`format_json` (both valid and invalid JSON), and `summarize_text`
(correctly reported the missing-API-key error, since no real Groq key was
available in this environment).
- Transport security verified: a request with a spoofed `Host` header
correctly received `421 Misdirected Request`.
- CORS preflight verified: an `OPTIONS /mcp` request with
`Origin: http://localhost:5173` returned `200` with the correct
`access-control-*` headers once `MCP_ALLOWED_ORIGINS` was set.
- Frontend: `npx tsc --noEmit` passed with no errors; `npm run build`
succeeded and produced `frontend/dist/`.
**Not verified** (requires external accounts/credentials not available in
this environment): a real `summarize_text` call with a live Groq API key,
the Render deployment itself, and Smithery publishing/listing.
## 18. Limitations
- `summarize_text` was only tested end-to-end for its error paths; it has
not been called with real Groq credentials.
- Render deployment and Smithery publishing require manual steps with your
own accounts (see sections 14-15) and have not been performed here.
- `explain_error` and `generate_regex` use small, hand-written pattern
libraries, not an LLM - they're deliberately simple/deterministic per the
project's scope, so they won't recognize every possible error or pattern
description.
- The frontend has no authentication and is meant for local/demo use, per
the project's explicit "no accounts/auth" scope.
## 19. Learning outcomes
- **What MCP is:** a standardized protocol separating "providing context/
actions to an LLM" from "the LLM interaction itself," so a server built
once (like this one) works with any compliant client.
- **Host / client / server:** the host is the LLM application (Claude
Desktop, or the app behind the browser dashboard); the client is the
MCP-speaking component inside it (the SDK's `Client`, or our frontend's
`StreamableHTTPClientTransport`-based client); the server is what we
built - it never talks to a model directly.
- **Tools vs. resources vs. prompts:** tools are model-controlled (the LLM
decides to call `format_json`); resources are application-controlled data
loads; prompts are user-invoked templates. This project only needed tools.
- **Tool discovery and invocation:** a client calls `tools/list` to learn
what's available (name, description, JSON-schema inputs/outputs, all
derived automatically from Python type hints and docstrings), then
`tools/call` to invoke one by name with arguments.
- **Why MCP vs. a plain REST API:** a REST API needs a bespoke integration
per client; an MCP server describes its own capabilities and schemas, so
any MCP-aware host can use it without custom glue code - demonstrated
directly by connecting the same server to both the MCP Inspector and our
own hand-built frontend client with zero server-side changes.
- **Where the LLM fits:** only inside `summarize_text`, which calls out to
Groq. The rest of the server is plain deterministic code - a useful
reminder that "MCP server" and "AI application" are not the same thing.
- **Deployment realities:** Streamable HTTP servers default to
localhost-only Host/Origin allowlisting for safety, and that has to be
explicitly opened up (`TransportSecuritySettings`) once deployed behind a
real hostname - confirmed hands-on by triggering and then fixing a `421`.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues