Conversation
Refactors cosmic-mcp into a shared createServer() factory consumed by both
the existing stdio binary (src/stdio.ts) and a new HTTP entry (src/http.ts)
that hosts the MCP over streamable-HTTP at /v1/buckets/{slug}.
Per-request multi-tenancy via AsyncLocalStorage: the bucket key is taken
from the Authorization header, a per-request Cosmic SDK client is built,
and tool handlers resolve it via getCosmicClient() unchanged.
HTTP entry adds spec items: GET / descriptor, GET /healthz, RFC 9728
/.well-known/oauth-protected-resource, 401 with WWW-Authenticate,
Origin allowlist for DNS-rebinding protection, 404 stub for the reserved
/v1/account namespace, and SIGTERM graceful shutdown.
Adds Dockerfile + docker-bake.hcl and a GitHub Actions deploy workflow
mirroring cosmic-backend's pattern: push to staging deploys to
mcp.cosmic-staging.com, push to main deploys to mcp.cosmicjs.com.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…l override Hosted MCP needs to talk to the staging Cosmic backend from the staging ECS task and the prod backend from the prod task. Reads COSMIC_API_ENVIRONMENT (production|staging) from env, falls back to production. If COSMIC_API_URL + COSMIC_UPLOAD_URL are both set, those override as a custom escape hatch (useful for migrations or pointing the hosted MCP at a non-default workers URL). Co-authored-by: Cursor <cursoragent@cursor.com>
…list
The Cosmic SDK throws plain objects (e.g. { status, message }) rather
than Error instances, so the existing 'instanceof Error ? message :
"Unknown error"' pattern collapsed every SDK failure to a useless
"Unknown error" with no signal in the response or in CloudWatch.
Adds src/errors.ts with:
- formatToolError: extracts message/status from plain-object throws,
falls back to JSON.stringify, and unconditionally console.errors
the raw payload so we can debug from logs even when the client only
sees the surface message.
- isNotFoundError: detects Cosmic's 404 plain-object throws so list
tools (objects, media, object types) can return an empty page
instead of an isError result. Get/update/delete tools still treat
404 as a real error since the caller asked for a specific resource.
Co-authored-by: Cursor <cursoragent@cursor.com>
…header) Cosmic enforces separate read and write keys: the read key is rejected on writes, the write key is rejected on reads. The previous design used a single bearer as both, which 401'd on every read tool when callers sent a write key (and vice versa). Bearer is now `<read_key>` for read-only access or `<read_key>:<write_key>` for full access. Clients that can't colon-pack their token can send the write key via X-Cosmic-Write-Key. Updates the GET / descriptor, the OAuth PRM auth_model field, and the README. Drops the old X-Cosmic-Key-Scope header which was meaningless given the two-key model. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the staging deploy automation in the workflow, but the README is the public-facing surface and should advertise only the production endpoint. Co-authored-by: Cursor <cursoragent@cursor.com>
Pushes the @cosmicjs/mcp package to npm whenever a vX.Y.Z tag lands on main (or via workflow_dispatch with an explicit tag). The job validates the tag matches package.json version, builds, and runs `npm publish --provenance --access public`. Requires an NPM_TOKEN repo secret with publish access. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a hosted Cosmic MCP endpoint deployed on AWS ECS Fargate, alongside the existing stdio binary. Single MCP server, two transports.
https://mcp.cosmicjs.com/v1/buckets/{bucket-slug}npx @cosmicjs/mcpdriven by env varsAuthenticate with
Authorization: Bearer <read_key>for read-only tools orAuthorization: Bearer <read_key>:<write_key>for full access. The write key may also be sent viaX-Cosmic-Write-Key.What changed
Code
createServer()factory (src/server.ts) consumed by bothsrc/stdio.ts(existingnpxbinary) and the newsrc/http.tsHTTP entry.AsyncLocalStorage: bucket key parsed fromAuthorization, fresh Cosmic SDK client per request, tool handlers unchanged.<read_key>(read-only) or<read_key>:<write_key>(full);X-Cosmic-Write-Keyaccepted as a fallback.apiEnvironmentis now wired through to the SDK;COSMIC_API_URL+COSMIC_UPLOAD_URLprovide a custom override.formatToolErrorextracts{ status, message }from Cosmic SDK plain-object throws, logs the raw error, and returns the actual message to the client. 404s on list endpoints (objects, media, object types) are coerced to empty pages instead of being treated as errors.GET /descriptor,GET /healthz,GET /.well-known/oauth-protected-resource(RFC 9728),POST/GET/DELETE /v1/buckets/:slug, 404 stub for the reserved/v1/accountnamespace.WWW-Authenticateon 401s,SIGTERMgraceful shutdown, per-request transport in stateless mode.Build / deploy
Dockerfile(multi-stage,oven/bunbuild,node:20-alpineruntime,tiniPID 1).docker-bake.hcl+.dockerignore..github/workflows/deploy.ymlbuilds on every push and deploys to ECS on push tomain. Auto-scales the service from 0 if needed and waits for service stability before smoke-testing/healthz.Validation
End-to-end JSON-RPC suite executed against an internal pre-production deployment:
GET /descriptorGET /healthzGET /.well-known/oauth-protected-resourceGET /v1/accountPOST /v1/buckets/foono authWWW-Authenticateinitialize+tools/listcosmic_types_list(read key only)cosmic_types_create+cosmic_objects_create+cosmic_objects_listround-tripcosmic_ai_generate_textlong callTest plan after merge
mainfinishes greencurl https://mcp.cosmicjs.com/returns the descriptorcurl https://mcp.cosmicjs.com/healthzreturnsokmcp.cosmicjs.comto Cursor / Claude Desktop with a real bucket and confirmtools/listand a read tool workDeferred to v1.1
Rate limiting per bearer/bucket, dashboards +