Sigils are currently resolved inside the Django application through core.sigil_resolver
, which inspects the SigilRoot
configuration, looks up model instances, and falls back to a gway
subprocess for unknown roots. The resolver supports nested sigils, dynamic model lookups, environment variables, and per-thread context provided through core.sigil_context
. Sigil metadata is surfaced in the admin via the Sigil Builder view, which lists known roots and lets administrators experiment with resolution.
The product roadmap calls for exposing the same capabilities outside of Django via OpenAI's Model Context Protocol (MCP) so connectors can resolve sigils over SSE.
gway
fallback so existing external integrations remain functional, but make it optional when MCP adoption is complete.The server will expose the following MCP constructs:
Type | Name | Purpose |
---|---|---|
Tool | resolveSigils |
Resolve one or more sigils inside a text payload using the current session context. Input schema: { "text": string, "context": {"model": {"app_label.ModelName": "pk"}}, "options": {"skipUnknown": bool}} . Returns { "resolved": string, "metadata": {"unresolved": [string]}} . |
Tool | resolveSingle |
Resolve a single sigil token and return its value or unresolved form. Thin wrapper that validates the sigil shape before delegating to resolveSigils for reuse. |
Tool | describeSigilRoot |
Provide metadata for a given sigil root (fields, context type) leveraging the same data that powers the Sigil Builder view. |
Tool | setContext |
Update the thread-local sigil context using set_context to mimic how the resolver infers instances from request state. |
Resource | sigilRoots (optional streaming) |
Publish changes to SigilRoot objects so long-lived MCP sessions can react to new roots without polling. Implemented via Django signals. |
The server’s list_tools
handler simply advertises the tools above, while call_tool
dispatches to a per-session service class that wraps resolve_sigils
and _resolve_token
logic so every invocation reuses the battle-tested paths.
manage.py mcp_sigil_server
└── SigilResolverServer (mcp.server.sse.SseServer)
├── SigilSessionState (per-connection context)
│ └── SigilContextAdapter ↔ core.sigil_context
├── SigilResolverService
│ └── uses core.sigil_resolver.resolve_sigils
└── SigilRootCatalog (queries SigilRoot + caches metadata)
mcp_sigil_server
imports django.setup()
, instantiates an SseServer
from the modelcontextprotocol
reference implementation, and serves it over an HTTP listener (default 127.0.0.1:8800
).accept_session
, capture authentication headers, initialize a SigilSessionState
, and register a connection-specific logger so unresolved sigils continue to log warnings via logger.warning
just like the in-process resolver.resolve_sigils
function. Before each call, apply any session context by temporarily invoking set_context
/ clear_context
. After resolution, detect which tokens remained unresolved by diffing the output against the input and by checking the metadata returned from _failed_resolution
.describeSigilRoot
and resource streaming by reusing the query logic from core.sigil_builder
(select related content type, list fields, etc.)._resolve_with_gway
untouched; the MCP server simply imports resolve_sigils
, so the fallback path still shells out when no root is found.modelcontextprotocol
(Python MCP reference SDK) to requirements.txt
and vendor reusable schemas in a small core/mcp/schemas.py
module to avoid duplicating JSON schema definitions across handlers.settings.MCP_SIGIL_SERVER = {"host": "127.0.0.1", "port": 8800, "api_key": "..."}
. The management command reads these values and optionally the --public
flag to bind to 0.0.0.0
behind an ingress controller.Authorization: Bearer <token>
header on createSession
. Reject the request unless the token matches settings.MCP_SIGIL_API_KEYS
(list) so multiple connectors can be provisioned without redeploying the service.options.maxRequestsPerMinute
) enforced inside the session state. Upgrade to Redis-backed limits later if required.core/mcp/__init__.py
, core/mcp/server.py
, and core/mcp/schemas.py
with Pydantic models to validate tool arguments before passing them to Django.SigilResolverService
that exposes resolve_text
, resolve_single
, set_context
, and describe_root
. It should call resolve_sigils
and interact with SigilRoot
directly.SseServer
inside the management command. Handle lifecycle events (session start/end) to clear context using clear_context
to prevent leaks.curl
traces to validate the SSE handshake.start.sh
or a systemd unit) so the MCP server can run alongside Django in production.tests/test_sigil_resolution.py
to ensure parity with in-process resolution.list_tools
and call_tool
, and verifies resolution and authorization behavior.filter_field
rules).ToolError
responses so clients get actionable messages. Unexpected exceptions should surface as InternalError
while still logging stack traces.prompt
registrations so LLM clients can request example instructions for building sigils.jobs
once the reference SDK stabilizes.gway
fallback with a dedicated MCP tool that proxies to whatever eventually supersedes gway, allowing observability and retries over the same channel.