Resources

Expose data sources and dynamic content generators to your MCP clients.

Resources give an LLM read‑only access to text, JSON, binaries, or files. Templates extend the idea by letting clients request parameterised URIs that generate data on the fly. In Hyperia both are primarily declared with the @mcp.resource decorator (for dynamic content) or mcp.add_resource() (for static assets).


What Are Resources?

When a client issues resources/read for a URI:

  1. Hyperia finds the matching resource definition.

  2. If the resource is dynamic (a function), it executes.

  3. The returned content is packaged (text, JSON, bytes…) and streamed back.

This mechanism lets LLMs inspect configuration, fetch user docs, read database rows, or access freshly computed summaries relevant to a conversation.


Defining Resources with @resource

import json
from hyperia import Hyperia

mcp = Hyperia("DataServer")

# Static string
@mcp.resource("resource://greeting")
def greeting() -> str:
    return "Hello from Hyperia!"

# JSON dict auto‑serialised
@mcp.resource("data://config")
def cfg() -> dict:
    return {"theme": "dark", "version": "1.2.0"}

Key points

  • URI – the unique identifier (resource://greeting).

  • Lazy – functions run only when the URI is requested.

  • Metadata – name & description inferred from the function by default.

Return mapping → content: • strTextResourceContents text/plaindict/list/BaseModel → JSON string application/jsonbytes → base64 BlobResourceContents (specify mime_type!) • None → empty list (204‑like).


Custom Metadata

@mcp.resource(
    uri="data://app-status",
    name="ApplicationStatus",
    description="Current health status of the app.",
    mime_type="application/json",
    tags={"monitoring", "status"},
)
def app_status() -> dict:
    return {"status": "ok", "uptime": 12345}
  • uri (required)

  • name, description override inferred values

  • mime_type explicit for non‑text

  • tags help clients filter/group


Accessing MCP Context

Inject a Context param for request metadata, logging, resource reads, sampling, etc.

from hyperia import Hyperia, Context

mcp = Hyperia("DataServer")

@mcp.resource("resource://system-status")
async def system(ctx: Context) -> dict:
    return {"status": "operational", "request_id": ctx.request_id}

Async Resources

Use async def for I/O‑heavy reads.

import aiofiles
from hyperia import Hyperia

mcp = Hyperia("DataServer")

@mcp.resource("file:///app/log.txt", mime_type="text/plain")
async def read_log() -> str:
    async with aiofiles.open("/app/log.txt", mode="r") as f:
        return await f.read()

Static Assets with mcp.add_resource()

from pathlib import Path
from hyperia import Hyperia
from hyperia.resources import FileResource, TextResource, DirectoryResource

mcp = Hyperia("DataServer")

# Static file
readme = Path("README.md").resolve()
if readme.exists():
    mcp.add_resource(
        FileResource(
            uri=f"file://{readme.as_posix()}",
            path=readme,
            mime_type="text/markdown",
        )
    )

# Pre‑defined text
mcp.add_resource(
    TextResource(
        uri="resource://notice",
        text="Maintenance Sunday 02:00 UTC",
        tags={"notification"},
    )
)

# Directory listing as JSON
data_dir = Path("./app_data").resolve()
if data_dir.is_dir():
    mcp.add_resource(
        DirectoryResource(
            uri="resource://data-files",
            path=data_dir,
            recursive=False,
        )
    )

Common classes: TextResource, BinaryResource, FileResource, HttpResource (requires httpx), DirectoryResource.

Custom Storage Key

special = TextResource(uri="resource://special")
mcp.add_resource(special, key="internal://v2/special")

Resource Templates (Parameterized URIs)

Templates let you embed placeholders in the URI that map directly to function parameters.

from hyperia import Hyperia

mcp = Hyperia("DataServer")

@mcp.resource("weather://{city}/current")
def weather(city: str) -> dict:
    return {"city": city.title(), "temp": 22, "unit": "c"}

@mcp.resource("repos://{owner}/{repo}/info")
def repo_info(owner: str, repo: str) -> dict:
    return {"owner": owner, "repo": repo, "stars": 120}
  • Request weather://london/current{city="london"}

  • Request repos://Hyperia/hyperia/info{owner, repo}

Wildcard Params {param*} (Hyperia extension)

@mcp.resource("path://{filepath*}")
def read_path(filepath: str) -> str:
    return f"Reading {filepath}"

Matches any depth: path://docs/api/v1/index.md.

Defaults & Multiple Templates

@mcp.resource("users://email/{email}")
@mcp.resource("users://name/{name}")
def lookup(name: str | None = None, email: str | None = None) -> dict:
    ...

Error Handling

Raise exceptions or hyperia.ResourceError.

from hyperia import Hyperia, ResourceError

mcp = Hyperia("DataServer")

@mcp.resource("resource://safe-error")
def fail() -> str:
    raise ResourceError("Unable to retrieve data – file not found")

Server Behaviour Flags

Duplicate URIs

mcp = Hyperia(on_duplicate_resources="error")

Options: "warn" (default), "error", "replace", "ignore".

Last updated