lcc)Turns lcc into a ChatGPT / Codex plugin: the six MCP tools already served over stdio
are also served over streamable HTTP, so ChatGPT can recommend and call them mid-conversation.
The deterministic engine is untouched. src/lcc/chatgpt_server.py is a transport that
reuses lcc.mcp_server.handle_message, so the two transports cannot drift.
| Path | What it is |
|---|---|
src/lcc/chatgpt_server.py |
Streamable-HTTP transport over the existing MCP handlers |
plugins/chatgpt/plugin.json |
Portable plugin manifest + extensions.com.openai store metadata |
plugins/chatgpt/mcp.json |
Registered MCP server mapping |
tests/test_chatgpt_server.py |
Real HTTP round-trips against a live server on an ephemeral port |
lcc mcp --http --host 0.0.0.0 --port 8080
Endpoint: POST /mcp, responses as a single SSE message event. Stdlib only — no new
dependency in the installed package.
Verb contract (all verified against a running server in the test suite):
| Request | Response | Why |
|---|---|---|
POST /mcp |
200 + SSE, or 202 for a notification |
Result delivery |
GET /mcp |
405 |
No server-initiated stream exists; must not hang |
DELETE /mcp |
200 |
Stateless, no session to terminate |
POST /anything-else |
404 |
Unknown endpoint |
| malformed JSON | 400 |
Parse error, as a JSON-RPC error body |
The submission portal requires a public HTTPS origin — a local or testing endpoint is rejected. Put a TLS-terminating proxy in front of the process. Options that fit a stdlib Python service with no extra app code:
# Fly.io
fly launch --now --http-service 8080 # or fly cert add <app>.<domain> for TLS
# Render / Railway
# build: pip install . && lcc mcp --http --host 0.0.0.0 --port $PORT
Do not expose this process directly to the internet as plain HTTP.
Without LCC_HTTP_TOKEN every caller is anonymous and limited to the offline providers:
mechanical for compact and rules for compact_transcript. Asking for jev, auto
or laya (including compact_transcript with no provider, which defaults to jev) is
refused with JSON-RPC error -32602. This stops anyone who reaches the
URL from spending your TypeSafe key or loading Laya weights on the host.
Set LCC_HTTP_TOKEN to require Authorization: Bearer <token> on POST /mcp. Requests
without it get 401; authenticated callers may pick any provider. GET /health and the
domain-verification path stay open. Each request has a 30 s socket timeout.
Plugins with MCP must prove control of the serving domain. The portal shows a verification challenge; place the exact token at the generated well-known URL and return only that one token:
https://<challenge-base-host>/.well-known/openai-apps-challenge
The endpoint must not return JSON, a list, or multiple tokens.
Edit plugins/chatgpt/mcp.json:
{
"mcpServers": {
"lcc": { "type": "streamable-http", "url": "https://your-host/mcp" }
}
}
Then update plugins/chatgpt/plugin.json version and zip the plugin folder
(plugin.json, mcp.json, plus skills/ if you add one) for the portal.
plugin_asdk_app.defaultPrompt examples from plugin.json and check the tool that fires.Discovery is driven by the store metadata, so the phrases in shortDescription and
defaultPrompt are the lever on whether ChatGPT recommends the plugin at all.
readOnlyHint, destructiveHint,
and openWorldHint booleans. All six are read-only. openWorldHint is true only for
compact and compact_transcript, whose jev/nimble providers call a third-party
judge; the default mechanical path is fully local.TOOLS in
mcp_server.py, so they cannot drift from the implementations.compact_transcript takes only the transcript the caller explicitly sends."Read" only; the plugin never writes.These are submission prerequisites that live in the OpenAI dashboard, not in this repo:
SECURITY.md covers
the zero-telemetry posture but is not the per-plugin policy page; plugin.json has no
privacyPolicyURL field set yet.api.apps.write permission, for
whoever owns the submitting org.