Skip to main content
Glama
README.md
# own-mcp-server

A personal MCP (Model Context Protocol) server that wraps my own projects as
tools Claude can call directly from chat/Cowork — a meta-project for
connecting the rest of my work into one place, one wrapped project at a
time. Built to learn MCP hands-on by running one, not by reading about it.

First wrapped project: **Internship Copilot**
(`../Internship_Copilot`) — job posting analysis, fit scoring, and
application tracking.

## Setup

```bash
uv sync
```

Internship Copilot's LLM backend (completions + embeddings) is configured
independently, via its own `.env` — see its README. `own-mcp-server` doesn't
care which backend is behind it.

Registered with Claude Code at user scope:

```bash
claude mcp add --scope user own-mcp-server -- uv run --directory <this dir> own-mcp-server
```

## Transports

Two ways to run it:

- **stdio** (`own-mcp-server`) — a local subprocess Claude Code launches
  directly. No networking, no auth needed; simplest for local dev.
- **streamable-http** (`own-mcp-server-http`) — an HTTP server for remote
  clients (claude.ai web chat can't reach a stdio server on your machine).
  Gated by a static bearer token (`MCP_BEARER_TOKEN`) checked in
  `http_auth.py`'s ASGI middleware, rather than the MCP SDK's built-in OAuth
  machinery — appropriate for a single trusted client, not a multi-tenant
  service. The bind host is configurable (`MCP_HTTP_HOST`) since where
  `127.0.0.1` is "safe" depends on where the process's actual network
  isolation boundary is (bare host vs. a container network behind a reverse
  proxy) — see the docstring in `server.py`.

## Architecture

Every wrapped project lives under `src/own_mcp_server/modules/<name>/` and
exposes a single `register(mcp: MCPServer) -> None` function. `server.py`
just imports each module and calls `register` — adding project #2 means
writing a new module package, not touching the core.

## MCP concepts, as learned building this

- **Tool** = a callable action (`analyze_posting`, `add_application`).
  Claude decides when to invoke it based on the description/docstring.
- **Resource** = readable data Claude can pull in as context without it
  being a callable action (`profile://current`, `applications://all`).
- **Transport**: started with stdio (a local subprocess Claude Code
  launches directly) — no networking or auth to build for a first server.
  HTTP/SSE would matter if this needed to be reachable remotely.
- The SDK in use here (`mcp` 2.0.0) renamed the old `FastMCP` class to
  `MCPServer` (`from mcp.server import MCPServer`) — worth knowing if
  following older MCP tutorials that reference `FastMCP`.

## Status

- Core loop implemented for real: `analyze_posting`, `prep_interview`,
  `list_applications`, `get_application`, `add_application`,
  `update_application_status`, `delete_application`, plus the
  `profile://current` and `applications://all` resources.
- Still stubbed (raise `NotImplementedError`): CV import
  (`draft_profile_from_cv`), batch ingestion (`batch_analyze_postings`),
  profile CRUD beyond read (`add_profile_skill`, `add_profile_project`),
  and the eval harness (`run_evaluation`).