module as source doc fixes #2

Merged
aetos merged 1 commits from doc-forge-fixes into main 2026-02-21 16:47:19 +00:00
27 changed files with 68 additions and 258 deletions
Showing only changes of commit d68d3120c3 - Show all commits

View File

@@ -1,29 +1,29 @@
home: mail_intake/index.md home: index.md
groups: groups:
Core API: Core API:
- mail_intake/ingestion/index.md - ingestion/index.md
- mail_intake/ingestion/reader.md - ingestion/reader.md
Domain Models: Domain Models:
- mail_intake/models/index.md - models/index.md
- mail_intake/models/message.md - models/message.md
- mail_intake/models/thread.md - models/thread.md
Provider Adapters: Provider Adapters:
- mail_intake/adapters/index.md - adapters/index.md
- mail_intake/adapters/base.md - adapters/base.md
- mail_intake/adapters/gmail.md - adapters/gmail.md
Authentication & Storage: Authentication & Storage:
- mail_intake/auth/index.md - auth/index.md
- mail_intake/auth/base.md - auth/base.md
- mail_intake/auth/google.md - auth/google.md
- mail_intake/credentials/index.md - credentials/index.md
- mail_intake/credentials/store.md - credentials/store.md
- mail_intake/credentials/pickle.md - credentials/pickle.md
- mail_intake/credentials/redis.md - credentials/redis.md
Normalization & Parsing: Normalization & Parsing:
- mail_intake/parsers/index.md - parsers/index.md
- mail_intake/parsers/body.md - parsers/body.md
- mail_intake/parsers/headers.md - parsers/headers.md
- mail_intake/parsers/subject.md - parsers/subject.md
Configuration & Errors: Configuration & Errors:
- mail_intake/config.md - config.md
- mail_intake/exceptions.md - exceptions.md

View File

@@ -1,3 +1,3 @@
# Mail Intake # mail_intake
::: mail_intake ::: mail_intake

22
fetch_emails.py Normal file
View File

@@ -0,0 +1,22 @@
from mail_intake.ingestion import MailIntakeReader
from mail_intake.adapters import MailIntakeGmailAdapter
from mail_intake.auth import MailIntakeGoogleAuth
from mail_intake.credentials.pickle import PickleCredentialStore
store = PickleCredentialStore(path="token.pickle")
auth = MailIntakeGoogleAuth(
credentials_path="credentials.json",
store=store,
scopes=["https://www.googleapis.com/auth/gmail.readonly"],
)
auth.get_credentials()
adapter = MailIntakeGmailAdapter(auth_provider=auth)
reader = MailIntakeReader(adapter)
for message in reader.iter_messages("from:roshnisingh009@gmail.com"):
print(message.subject, message.from_email)
break
from pdb import set_trace;set_trace()

View File

@@ -1,176 +0,0 @@
"""
Generate a fully-contained MCP bundle by reading Python docstrings directly.
Uses Griffe (the same library mkdocstrings-python uses internally),
but without MkDocs, Markdown, or HTML.
Output:
mcp/
├── index.json
├── nav.json
└── modules/
└── package.module.json
"""
from __future__ import annotations
import json
import argparse
from pathlib import Path
from typing import Iterable
from griffe import GriffeLoader, ModulesCollection, LinesCollection, AliasResolutionError
PROJECT_ROOT = Path(__file__).resolve().parent
DEFAULT_PACKAGE_ROOT = "mail_intake"
DEFAULT_MCP_DIR = PROJECT_ROOT / "mcp"
# -------------------------
# Utilities
# -------------------------
def iter_modules(package_root: Path, package_name: str) -> Iterable[str]:
for py in package_root.rglob("*.py"):
rel = py.relative_to(package_root.parent)
if py.name == "__init__.py":
yield ".".join(rel.parent.parts)
else:
yield ".".join(rel.with_suffix("").parts)
def serialize_object(obj) -> dict:
"""Convert a Griffe object into MCP-friendly JSON (alias-safe)."""
try:
docstring = obj.docstring.value if obj.docstring else None
except AliasResolutionError:
docstring = None
data = {
"name": obj.name,
"kind": obj.kind.value,
"path": obj.path,
"docstring": docstring,
}
# Signature may also trigger alias resolution
try:
if hasattr(obj, "signature") and obj.signature:
data["signature"] = str(obj.signature)
except AliasResolutionError:
pass
# Recurse into members, but never allow alias failure to bubble up
members = {}
if hasattr(obj, "members"):
for name, member in obj.members.items():
if name.startswith("_"):
continue
try:
members[name] = serialize_object(member)
except AliasResolutionError:
continue
if members:
data["members"] = members
return data
# -------------------------
# MCP generation
# -------------------------
def build_mcp(package_root: Path, package_name: str, mcp_root: Path) -> None:
modules_dir = mcp_root / "modules"
modules_dir.mkdir(parents=True, exist_ok=True)
loader = GriffeLoader(
search_paths=[str(package_root.parent)],
modules_collection=ModulesCollection(),
lines_collection=LinesCollection(),
)
nav = []
count = 0
for module in sorted(iter_modules(package_root, package_name)):
loader.load(module)
mod = loader.modules_collection[module]
payload = {
"module": module,
"content": serialize_object(mod),
}
out = modules_dir / f"{module}.json"
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(payload, indent=2), encoding="utf-8")
nav.append({
"module": module,
"resource": f"docs://module/{module}",
})
count += 1
(mcp_root / "nav.json").write_text(
json.dumps(nav, indent=2),
encoding="utf-8",
)
(mcp_root / "index.json").write_text(
json.dumps(
{
"project": package_name,
"type": "docstrings-direct",
"modules_count": count,
"source": "griffe",
},
indent=2,
),
encoding="utf-8",
)
print(f"MCP generated at: {mcp_root}")
# -------------------------
# CLI
# -------------------------
def main() -> None:
parser = argparse.ArgumentParser(
description="Generate MCP directly from Python docstrings (Griffe-based)",
)
parser.add_argument(
"--package-root",
default=DEFAULT_PACKAGE_ROOT,
help="Root Python package name",
)
parser.add_argument(
"--mcp-dir",
type=Path,
default=DEFAULT_MCP_DIR,
help="Output MCP directory",
)
args = parser.parse_args()
package_root = PROJECT_ROOT / args.package_root
if not package_root.exists():
raise FileNotFoundError(package_root)
build_mcp(
package_root=package_root,
package_name=args.package_root,
mcp_root=args.mcp_dir,
)
if __name__ == "__main__":
main()

View File

@@ -1,32 +0,0 @@
from pathlib import Path
import json
from mcp.server.fastmcp import FastMCP
MCP_ROOT = Path("mcp")
mcp = FastMCP("aetoskia-mail-intake-docs")
def read_json(path: Path):
if not path.exists():
return {"error": "not_found", "path": str(path)}
return json.loads(path.read_text())
@mcp.resource("docs://index")
def index():
return read_json(MCP_ROOT / "index.json")
@mcp.resource("docs://nav")
def nav():
return read_json(MCP_ROOT / "nav.json")
@mcp.resource("docs://module/{module}")
def module(module: str):
return read_json(MCP_ROOT / "modules" / f"{module}.json")
@mcp.tool()
def ping() -> str:
return "Pong! (fake tool executed)"
if __name__ == "__main__":
# FastMCP owns the HTTP server
mcp.run(transport="streamable-http")

View File

@@ -1,6 +1,3 @@
site_name: Aetoskia Mail Intake
site_description: Format-agnostic document reading, parsing, and scraping framework
theme: theme:
name: material name: material
palette: palette:
@@ -17,7 +14,6 @@ theme:
- navigation.instant - navigation.instant
- content.code.copy - content.code.copy
- content.code.annotate - content.code.annotate
plugins: plugins:
- search - search
- mkdocstrings: - mkdocstrings:
@@ -35,33 +31,33 @@ plugins:
annotations_path: brief annotations_path: brief
show_root_heading: true show_root_heading: true
group_by_category: true group_by_category: true
site_name: mail_intake
nav: nav:
- Home: mail_intake/index.md - Home: index.md
- Core API: - Core API:
- mail_intake/ingestion/index.md - ingestion/index.md
- mail_intake/ingestion/reader.md - ingestion/reader.md
- Domain Models: - Domain Models:
- mail_intake/models/index.md - models/index.md
- mail_intake/models/message.md - models/message.md
- mail_intake/models/thread.md - models/thread.md
- Provider Adapters: - Provider Adapters:
- mail_intake/adapters/index.md - adapters/index.md
- mail_intake/adapters/base.md - adapters/base.md
- mail_intake/adapters/gmail.md - adapters/gmail.md
- Authentication & Storage: - Authentication & Storage:
- mail_intake/auth/index.md - auth/index.md
- mail_intake/auth/base.md - auth/base.md
- mail_intake/auth/google.md - auth/google.md
- mail_intake/credentials/index.md - credentials/index.md
- mail_intake/credentials/store.md - credentials/store.md
- mail_intake/credentials/pickle.md - credentials/pickle.md
- mail_intake/credentials/redis.md - credentials/redis.md
- Normalization & Parsing: - Normalization & Parsing:
- mail_intake/parsers/index.md - parsers/index.md
- mail_intake/parsers/body.md - parsers/body.md
- mail_intake/parsers/headers.md - parsers/headers.md
- mail_intake/parsers/subject.md - parsers/subject.md
- Configuration & Errors: - Configuration & Errors:
- mail_intake/config.md - config.md
- mail_intake/exceptions.md - exceptions.md