Phase 0: FastMCP scaffold with ping tool
Set up the familiar package (src layout, uv/hatchling) with a FastMCP server exposing a read-only ping health tool. Add pytest smoke tests with 80% coverage gate. Flesh out the README with the project vision and roadmap. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
7 files changed
+1864
-1
No files matched your search
@@ -1,3 +1,58 @@
|
|||||||
# familiar
|
# familiar
|
||||||
|
|
||||||
Your familiar across New Eden — a read-only EVE Online MCP server that lets a local AI see your entire account and find anything in EVE.
|
> Your familiar across New Eden — a read-only EVE Online MCP server that lets a local AI see your entire account and find anything in EVE.
|
||||||
|
|
||||||
|
In EVE, your familiar isn't a cat on the windowsill. It's the companion that rides along through New Eden, sees what you see, remembers what you can't, and fetches anything you ask for. `familiar` is exactly that: an [MCP](https://modelcontextprotocol.io) server that gives a local AI (Claude Code, Claude Desktop, Cowork) a complete, read-only window into your EVE Online account and the entire game universe.
|
||||||
|
|
||||||
|
Ask your familiar where your assets are, what your skill queue looks like, what Tritanium sells for in Jita, the safest route to a lowsec system, or whether a fit is worth building. It looks, it finds, it reports back.
|
||||||
|
|
||||||
|
## Principles
|
||||||
|
|
||||||
|
- **Read-only by design.** `familiar` only ever *reads*. No market orders, no fittings changes, no mail sent, no waypoints set. The AI can see everything and break nothing.
|
||||||
|
- **Local-first.** Runs on your own machine. Your refresh token and account data never leave your control.
|
||||||
|
- **Everything, eventually.** The goal is full coverage: every read scope ESI exposes, plus the complete static universe. Built in phases so it actually ships.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
Two data sources sit behind the server:
|
||||||
|
|
||||||
|
- **ESI** (the [EVE Swagger Interface](https://esi.evetech.net/)) — the official REST API. Public endpoints (market, universe, industry indices) need no login. Character and corp data (wallet, assets, skills, industry, contracts, mail, location) come through EVE SSO with **read-only OAuth2 scopes**.
|
||||||
|
- **SDE** (the [Static Data Export](https://developers.eveonline.com/resource/resources)) — the full static dataset of items, blueprints, systems and stations, cached locally for fast lookups and anything name- or recipe-based.
|
||||||
|
|
||||||
|
Built in **Python with FastMCP**, OAuth via PKCE with a localhost callback, refresh token stored encrypted on disk. ESI `Expires` headers are respected for caching, and the SDE is loaded into a local store.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
Built in phases. Each phase is independently useful, so the project stays shippable.
|
||||||
|
|
||||||
|
**Phase 0 — Skeleton**
|
||||||
|
- Repo, project scaffold, FastMCP server that starts and exposes a health/ping tool.
|
||||||
|
- App registered on [developers.eveonline.com](https://developers.eveonline.com/).
|
||||||
|
|
||||||
|
**Phase 1 — Public lookups (no auth)**
|
||||||
|
- Market: prices and order book for any item in any hub, spreads between hubs.
|
||||||
|
- Universe via SDE: items, systems, stations, security, basic route/jumps.
|
||||||
|
- Quick win: useful before any OAuth complexity exists.
|
||||||
|
|
||||||
|
**Phase 2 — Account read (OAuth)**
|
||||||
|
- EVE SSO login flow, encrypted refresh token storage.
|
||||||
|
- Wallet, assets (search across characters), skills + skill queue, current location and clones.
|
||||||
|
|
||||||
|
**Phase 3 — Industry & contracts**
|
||||||
|
- Industry jobs, blueprints (SDE-backed), manufacturing cost/profit lookups.
|
||||||
|
- Personal and corp contracts, market orders.
|
||||||
|
|
||||||
|
**Phase 4 — Everything else**
|
||||||
|
- Mail, planetary interaction, loyalty points, fittings, corp roles.
|
||||||
|
- zKillboard integration for kill/PvP data.
|
||||||
|
|
||||||
|
**Phase 5 — Polish**
|
||||||
|
- Caching, error-limit safety, packaging, setup docs. Optional: prep a public, generic single-user release as a portfolio case.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Phase 0. Just hatched.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
[project]
|
||||||
|
name = "familiar"
|
||||||
|
version = "0.0.1"
|
||||||
|
description = "Your familiar across New Eden — a read-only EVE Online MCP server."
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
license = { file = "LICENSE" }
|
||||||
|
dependencies = [
|
||||||
|
"fastmcp>=2.3.0",
|
||||||
|
"httpx>=0.27.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
familiar = "familiar.__main__:main"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["src/familiar"]
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = [
|
||||||
|
"pytest>=8.0.0",
|
||||||
|
"pytest-cov>=5.0.0",
|
||||||
|
"ruff>=0.6.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
line-length = 100
|
||||||
|
target-version = "py311"
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
testpaths = ["tests"]
|
||||||
|
addopts = "--cov=familiar --cov-report=term-missing --cov-fail-under=80"
|
||||||
|
|
||||||
|
[tool.coverage.run]
|
||||||
|
source = ["familiar"]
|
||||||
|
omit = ["*/__main__.py"]
|
||||||
|
|
||||||
|
[tool.coverage.report]
|
||||||
|
exclude_also = [
|
||||||
|
"if __name__ == .__main__.:",
|
||||||
|
]
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
"""familiar — a read-only EVE Online MCP server."""
|
||||||
|
|
||||||
|
__version__ = "0.0.1"
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
"""Entry point: `python -m familiar` or the `familiar` console script.
|
||||||
|
|
||||||
|
Runs the MCP server over stdio, which is how Claude Desktop / Claude Code /
|
||||||
|
Cowork launch and talk to it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .server import mcp
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
mcp.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
"""The familiar MCP server.
|
||||||
|
|
||||||
|
Phase 0: a server that starts and exposes a single health/ping tool.
|
||||||
|
Later phases register more tools (market, universe, account, ...) onto this
|
||||||
|
same `mcp` instance.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import datetime as _dt
|
||||||
|
|
||||||
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
|
from . import __version__
|
||||||
|
|
||||||
|
mcp: FastMCP = FastMCP(
|
||||||
|
name="familiar",
|
||||||
|
instructions=(
|
||||||
|
"familiar is a read-only window into an EVE Online account and the New "
|
||||||
|
"Eden universe. It only ever reads — it never places orders, sends mail, "
|
||||||
|
"sets waypoints, or changes anything in-game. Use it to look up market "
|
||||||
|
"prices, the static universe (items, systems, stations), and — once the "
|
||||||
|
"user has authenticated — their assets, skills, wallet, and location."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool
|
||||||
|
def ping() -> dict[str, str]:
|
||||||
|
"""Health check. Confirms the familiar server is alive and reports its version.
|
||||||
|
|
||||||
|
Returns the server name, version, and current UTC time. Useful as a first
|
||||||
|
call to verify the MCP connection is working before trying real lookups.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"status": "ok",
|
||||||
|
"server": "familiar",
|
||||||
|
"version": __version__,
|
||||||
|
"time_utc": _dt.datetime.now(_dt.timezone.utc).isoformat(),
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
"""Phase 0 smoke tests."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from familiar import __version__
|
||||||
|
from familiar.server import mcp, ping
|
||||||
|
|
||||||
|
|
||||||
|
def test_ping_reports_ok():
|
||||||
|
result = ping()
|
||||||
|
assert result["status"] == "ok"
|
||||||
|
assert result["server"] == "familiar"
|
||||||
|
assert result["version"] == __version__
|
||||||
|
assert "time_utc" in result
|
||||||
|
|
||||||
|
|
||||||
|
def test_server_exposes_ping_tool():
|
||||||
|
tools = asyncio.run(mcp.list_tools())
|
||||||
|
assert "ping" in {t.name for t in tools}
|
||||||
Reference in new issue
Block a user