DEV Community

Cover image for From a Git clone to a working MCP server: a 30-minute Codex walkthrough
tony tong
tony tong

Posted on

From a Git clone to a working MCP server: a 30-minute Codex walkthrough

Most MCP tutorials assume you're starting from scratch. In reality, you usually have a working tool or library and just want to expose it as a callable tool to an LLM agent. Here's the path I take that gets it done in 30 minutes of real work.

Step 1: Pick one tool, not five

Don't expose your whole API surface. Pick the one function a coding agent would actually call. For a docs site, that's search_docs. For a database, that's run_query. For an internal service, that's lookup_user. One tool, clear input schema, real value.

Step 2: Write the tool description like a docstring

The model will only call the tool well if the description is sharp. I write three sentences:

  1. What it does (verb + noun)
  2. When to use it (the user signal that should trigger it)
  3. What it returns (shape, not values)

Example:

search_docs(query, top_k=5): Search the company docs index. Use when the user asks a factual question about internal systems, processes, or past decisions. Returns a list of {title, url, snippet} sorted by relevance.

That's the whole tool spec. No ambiguity, no prose.

Step 3: Run Codex locally first

OpenAI Codex CLI is the fastest way to validate the tool works end-to-end:

codex --approval-mode suggest
Enter fullscreen mode Exit fullscreen mode

Drop into a sandboxed directory, ask Codex to use the tool. If it picks the tool, calls it correctly, and uses the result in its answer, you're done. If it doesn't, the description is bad — go back to step 2.

Step 4: Wire it as an MCP server

Now wrap the tool in a real MCP server. The minimum is a FastMCP instance with one @mcp.tool():

from mcp.server.fastmcp import FastMCP
mcp = FastMCP("internal-docs")

@mcp.tool(description="Search the company docs index...")
def search_docs(query: str, top_k: int = 5) -> list[dict]:
    return docs_index.search(query, top_k=top_k)

if __name__ == "__main__":
    mcp.run(transport="stdio")
Enter fullscreen mode Exit fullscreen mode

Add it to your client's MCP config (Claude Desktop, Cursor, etc.). Restart, ask the same question. If it works in both Codex CLI and the host client, you have a real MCP server.

Step 5: Evals, not vibes

The last 10 minutes is an eval. Three questions your tool should answer correctly, three it should refuse to answer. If you can't list them, your tool isn't done — it's just running.

A small diagram

Here's the loop I end up with. Tools, model, results, repeat.

The boring truth is that the description is 80% of the work. Once that's right, the wiring is half an hour.

Top comments (0)