Skip to content

Publish and list the MCP servers on Smithery / Claude / Cursor with reproducible manifests #27

Open

Description

@harrymove-ctrl

Context

docs/marketplace-listings.md has finished copy for Smithery, Claude Desktop, and Cursor, but submissions are pending, the repo isn't public, there is no machine-readable manifest (server.json / smithery.yaml), and the write toolset is clone-only — packages/mcp/package.json is "private": true with no bin. Only the hosted read MCP is npx/mcp-remote friendly. The roadmap §5/§6 calls for one-command install and registry manifests so listings are reproducible.

Goal / user story

As a developer discovering ContextMEM on Smithery/Claude/Cursor, I want to install it in one command and have the listing reflect the real, current tool set (the extract/recall/remember triad), so I can connect it to my agent in under a minute.

Acceptance criteria

  • packages/mcp is publishable as @contextmem/mcp (remove private: true, add a bin entry + build output) so npx @contextmem/mcp starts the local/write stdio server; env requirements (MEMWAL_*, TATUM_API_KEY, CONTEXTMEM_RUNS_DIR) are documented.
  • A server.json (MCP registry schema) and smithery.yaml are committed describing both the stdio (write) server and the hosted (read) URL server, listing the triad tools with descriptions.
  • The Claude/Cursor/Smithery install snippets in docs/marketplace-listings.md are verified against the live hosted URL and the new npx command; the hosted snippet keeps the Authorization: Bearer <read-token> header.
  • The share-page / Publish install snippet bug is fixed: private-namespace snippets must include --header Authorization: Bearer <read-token> (today's ShareMcpInstall omits it → silent 401); add a one-line note that public namespaces need no token.
  • A short docs/ install page (or section) documents both paths: hosted read via mcp-remote, full triad via npx @contextmem/mcp, with copy-paste blocks for Claude Desktop, Cursor, and Smithery.
  • Submission steps are captured but gated behind explicit human approval (per the existing note at the top of marketplace-listings.md); repo-public flip is a documented prerequisite, not done automatically.

Implementation notes

  • Touch packages/mcp/package.json (publish config, bin, build script — it currently only typechecks), add server.json + smithery.yaml at repo or package root, and update docs/marketplace-listings.md.
  • Reuse the install-snippet shape from PublishPanel (apps/web main.tsx:449-456) when fixing ShareMcpInstall (main.tsx:2028).
  • Smithery hosted URL is already https://contextmem-hosted-namespace-mcp.petlofi.workers.dev/mcp?namespace=<namespace>; verify it still resolves and that server.json points at it.
  • Gotcha: the write server needs Node (Tatum/tar/packProofBundle); make the npx package clearly the Node path and keep the hosted URL as the zero-install read path.
  • Do not change repo visibility or actually submit — leave those as a checklist for a human, matching the existing approval note.

Sui Overflow angle

Discoverability is how a hackathon project becomes ecosystem infrastructure: being installable from Smithery/Claude/Cursor lets any Sui agent builder pull in Walrus-backed context + Sui-anchored receipts with one command, amplifying the Sui-native story beyond the demo and seeding adoption of the extract/recall/remember triad as a reusable Nexus node.

Dependencies

Soft-depends on the canonical triad issue (listings should describe the triad). Independent of the on-chain receipt work.

Part of the ContextMEM roadmap (#4) • Sui Overflow build.

Activity

  1. added this to the Sui Overflow milestone on Jun 16, 2026
  2. added
    P2Post-demo: distribution, polish, and reliability
    mcpModel Context Protocol servers and tools
    onboardingUser and developer onboarding / first-run experience
    platformBackend platform plumbing: Worker, D1, queues, secrets, metering
    on Jun 16, 2026
  3. harrymove-ctrl commented on Jun 19, 2026

    @harrymove-ctrl
    OwnerAuthor

    Implementation Plan

    Approach
    Make packages/mcp a real, publishable npm package so npx @contextmem/mcp starts the full write/stdio server (the Node-only path that needs tar/Tatum/MemWal), while keeping the hosted read server (/mcp?namespace=…) as the zero-install path. Because the stdio server imports three workspace:*, private:true packages (@contextmem/core|walrus|memwal), the cleanest single-package publish is to bundle those workspace deps into dist/ with tsup and externalize only true npm runtime deps — avoids publishing four coordinated packages. Add machine-readable manifests (server.json per the MCP registry schema + smithery.yaml) describing both servers, fix the real bug (ShareMcpInstall omits the Authorization header → silent 401 on private namespaces), verify the docs snippets against live endpoints, and leave repo-public/submission as a human-gated checklist.

    Prerequisites & dependencies

    • Soft-dep: the canonical triad issue (extract/recall/remember naming). Listings/manifests should describe the triad framing; map it onto the real tool names that exist today (see Step 6). Not blocking — proceed with current names + triad grouping.
    • npm publish access to the @contextmem scope (org + 2FA/automation token). If unavailable, fall back to publishing all four packages or using an alternate scope — decide before Step 1.
    • No new Cloudflare bindings/secrets required; this issue is packaging + docs + one UI/worker fix. (Harbor/Sui/Vectorize are out of scope.)
    • Verify the canonical hosted URL. Docs use https://contextmem-hosted-namespace-mcp.petlofi.workers.dev/mcp?namespace=<namespace> but the roadmap names contextmem-backend.petlofi.workers.dev. Confirm which Worker actually serves /mcp (Step 5) before freezing it in server.json.
    • Human approval gate stays in force per docs/marketplace-listings.md:3 — do NOT submit or flip repo visibility.

    Step-by-step

    1. Add a dedicated bin entry (avoids the symlink start bug). Create packages/mcp/src/bin.ts that imports createLocalContextMemMcpServer and unconditionally await server.connect(new StdioServerTransport()). Do not rely on the existing import.meta.url === \file://${process.argv[1]}`guard atpackages/mcp/src/index.ts:324— npm installs the bin as a symlink, soprocess.argv[1](symlink) ≠import.meta.url(realpath) and the server silently won't start undernpx. Keep the guard in index.tsforbun packages/mcp/src/index.ts` dev use.

    2. Make the package publishable + build to dist/. Edit packages/mcp/package.json:

      • Remove "private": true (:4); add "publishConfig": { "access": "public" }.
      • Add "bin": "dist/bin.js" (string bin ⇒ npx @contextmem/mcp runs it directly regardless of bin name), "main": "dist/index.js", "files": ["dist"].
      • Replace the no-op "build": "tsc -p tsconfig.json --noEmit" (:13) with "build": "tsup"; add "prepublishOnly": "tsup".
      • Add devDep tsup and declare the externalized runtime deps as real dependencies (see Step 3).
        Create packages/mcp/tsup.config.ts: entry: ["src/bin.ts", "src/index.ts", "src/hosted.ts"], format: ["esm"], target: "node20", banner: { js: "#!/usr/bin/env node" } (or chmod +x in build), noExternal: [/^@contextmem\//] (inline the three workspace packages so they don't need publishing), and keep large/native deps external.
    3. Resolve the dependency graph for the bundle. Inline @contextmem/core|walrus|memwal (noExternal). Keep external + list under dependencies: @modelcontextprotocol/sdk, zod, @mysten/sui, plus the heavy transitive deps core pulls in — @mozilla/readability, turndown, jsdom, cheerio, fast-xml-parser, nanoid, p-limit, css-tree. Make playwright an optionalDependency and confirm it is lazy-imported (screenshots are optional; a hard top-level import would force a browser download on every npx). If core imports playwright at module load, gate it behind a dynamic import() so the stdio server boots without it.

    4. Fix the ShareMcpInstall header bug (acceptance docs: add product roadmap (Harbor storage + Talus on-chain direction) #4). In apps/web/src/main.tsx:2028-2042:

      • Add a visibility?: "private" | "public" prop. For private, build the Claude snippet with the mcp-remote --header arg, mirroring the working PublishPanel/docs shape: args: ["-y","mcp-remote", mcpUrl, "--header", "Authorization: Bearer <read-token>"], and the Cursor object with a headers: { Authorization: "Bearer <read-token>" } key. For public, keep the current header-less snippets.
      • Add the one-line note: "Public namespaces need no token; private namespaces require Authorization: Bearer <read-token> (generate one in /app/namespaces)."
      • Plumb visibility in: add visibility: "private" | "public" to ShareLinkResponse.share (main.tsx:527), pass it at the call site main.tsx:1800/1742, and have the Worker include it in the /api/share-links/:id response (apps/api/src/worker.ts). If adding the field to the share endpoint is undesirable, default to emitting the <read-token> placeholder + note unconditionally (still correct, just shows a removable header for public). Reuse the snippet shape referenced at main.tsx:449-456 (the server-generated snippets that already carry the token in PublishPanel, rendered at main.tsx:5868-5883).
    5. Verify live endpoints (acceptance fix(ci): force bun + pin wrangler in Deploy workflow #3). With the hosted Worker confirmed (Prereqs), run an MCP initialize + tools/list handshake against …/mcp?namespace=<a-public-namespace> (e.g. npx -y mcp-remote <url> or a raw JSON-RPC POST) and against a private namespace with the Authorization: Bearer <read-token> header to prove the header path works and the header-less path 401s. Then smoke-test npx @contextmem/mcp locally via npm pack (Step 9) and an MCP tools/list.

    6. Author server.json (acceptance Memory knowledge graph: 3D neural constellation + grounded entity panel #2). Create server.json at repo root (registry-publishable). Use $schema: https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json, reverse-DNS name (e.g. io.github.harrymove-ctrl/contextmem), description, version, repository. Two entries:

      • packages[]: { registryType: "npm", identifier: "@contextmem/mcp", version, transport: { type: "stdio" }, runtimeHint: "npx", environmentVariables: [...] } documenting MEMWAL_ACCOUNT_ID, MEMWAL_AUTHORIZATION (isSecret), MEMWAL_MCP_URL (optional), TATUM_API_KEY (isSecret), CONTEXTMEM_RUNS_DIR (optional) — exact names confirmed in packages/memwal/src/index.ts and packages/walrus/src/storage.ts.
      • remotes[]: { type: "streamable-http", url: "https://contextmem-hosted-namespace-mcp.petlofi.workers.dev/mcp?namespace={namespace}", variables: { namespace: {...} }, headers: [{ name: "Authorization", description: "Bearer <read-token> for private namespaces", isRequired: false, isSecret: true }] }.
        Describe the triad (extract/recall/remember) in the description and reference the concrete tools: write/stdio = scrape_markdown, crawl_site, extract_*, ai_query_website, remember_site_snapshot, upload_proof_to_walrus, recall_site_context, query_site_memory (full list in index.ts); hosted/read = context_info, list_context, read_context, search_context, recall_memory, get_context_bundle, restore_site_memory (from hosted.ts:78-267). Validate against the schema with the registry mcp-publisher validate CLI.
    7. Author smithery.yaml (acceptance Memory knowledge graph: 3D neural constellation + grounded entity panel #2). Create smithery.yaml at repo root for the stdio server:

      startCommand:
        type: stdio
        configSchema:
          type: object
          properties:
            memwalAccountId: { type: string }
            memwalAuthorization: { type: string }
            tatumApiKey: { type: string }
            contextmemRunsDir: { type: string, default: "runs" }
        commandFunction: |
          (config) => ({ command: "npx", args: ["-y", "@contextmem/mcp"], env: {
            MEMWAL_ACCOUNT_ID: config.memwalAccountId,
            MEMWAL_AUTHORIZATION: config.memwalAuthorization,
            TATUM_API_KEY: config.tatumApiKey,
            CONTEXTMEM_RUNS_DIR: config.contextmemRunsDir }})

      The hosted read URL is listed on Smithery as a Remote server via server.json/Smithery's remote-add flow (note this in docs, not in this yaml).

    8. Update docs (acceptance fix(ci): force bun + pin wrangler in Deploy workflow #3, Spike: run @mysten/seal encrypt+decrypt inside a Cloudflare Worker #5). Edit docs/marketplace-listings.md: keep the existing Smithery (:24-31) and Claude Desktop (:42-57) snippets — both already include Authorization: Bearer <read-token> — but (a) update them to the verified canonical URL from Step 5, (b) add an explicit npx @contextmem/mcp write-server block with env vars, (c) refresh the tool list to the triad. Create docs/install.md documenting both paths with copy-paste blocks for Claude Desktop, Cursor, and Smithery: hosted read via npx -y mcp-remote <url> --header "Authorization: Bearer <token>", and full triad via npx @contextmem/mcp. Add a "Submission checklist (HUMAN APPROVAL REQUIRED)" section listing repo-public flip → npm publish → Smithery/Claude/Cursor submission, explicitly gated (acceptance Build HarborClient: reserve→sign→finalize bucket, multipart upload, status poll, download #6), matching marketplace-listings.md:3.

    9. Publish dry-run (no real publish). cd packages/mcp && bun run build && npm pack, install the tarball in a temp dir, run npx <tarball> and assert an MCP tools/list succeeds with the 20 write tools. Leave the actual npm publish and registry submission to the human gate.

    New / changed files

    • packages/mcp/src/bin.ts (new — stdio entry)
    • packages/mcp/tsup.config.ts (new)
    • packages/mcp/package.json (edit — unprivate, bin, build, files, deps, publishConfig)
    • server.json (new — MCP registry manifest, repo root)
    • smithery.yaml (new — repo root)
    • docs/install.md (new — dual-path install page)
    • docs/marketplace-listings.md (edit — verified snippets, npx block, triad, gated checklist)
    • apps/web/src/main.tsx (edit — ShareMcpInstall header fix :2028, ShareLinkResponse.share.visibility :527, call site :1800)
    • apps/api/src/worker.ts (edit — add visibility to /api/share-links/:id response)
    • packages/mcp/src/index.ts (no functional change; entry guard retained)

    Test plan

    • Build/pack (proves publishable): bun run --filter @contextmem/mcp build emits dist/bin.js with shebang + exec bit; npm pack → install tarball in a clean dir → npx @contextmem/mcp boots and tools/list returns the 20 write tools (round-trip assert). Confirms private:true removal + bin work.
    • Manifest validation: mcp-publisher validate server.json passes against the registry schema; smithery.yaml parses (Smithery CLI or YAML lint).
    • Live endpoint (real proof): MCP initialize+tools/list against the hosted URL succeeds for a public namespace without a header, returns 401 for a private namespace without a header, and succeeds for the same private namespace with Authorization: Bearer <read-token>.
    • UI regression: Vitest/typecheck green; manual — open a private share page, copy the Claude snippet, confirm it now contains the --header Authorization: Bearer <read-token> line and the public-vs-private note; public share page snippet stays header-less.
    • CI: bun-check.yml (typecheck + test + build) stays green with the new tsup build wired into the package build script.

    Risks & gotchas

    • Bin symlink start bug: the import.meta.url === file://${process.argv[1]} guard breaks under npm's symlinked bin — hence the dedicated bin.ts (Step 1). Verify on a real install, not just bun run.
    • Bundle bloat / native deps: playwright (browser download), jsdom, cheerio are heavy; a stray top-level playwright import forces a multi-hundred-MB install on every npx. Make it optionalDependencies + lazy-import; verify the server boots without playwright present.
    • Workspace-dep publishing: bundling (noExternal: @contextmem/*) sidesteps publishing four packages, but any npm dep the bundled code uses at runtime must be in @contextmem/mcp's dependencies or the install breaks. Audit transitive imports of core/walrus/memwal. Fallback: publish all four packages with their own dist + exports.
    • Node-only write path: packProofBundle shells tar via child_process (Node) — server.json/docs must clearly mark @contextmem/mcp as the Node path and the hosted URL as the zero-install read path (issue note).
    • URL drift: docs vs roadmap name two different Workers — freezing the wrong one in server.json ships a dead listing. Verify (Step 5) before commit.
    • Human gate: do not npm publish, submit, or flip repo visibility — only the dry-run (npm pack). Honor marketplace-listings.md:3.

    Acceptance-criteria mapping

    • Publishable @contextmem/mcp + bin + build + env docs → Steps 1, 2, 3, 9 (env names also in Step 6/8).
    • server.json + smithery.yaml describing stdio + hosted, triad tools → Steps 6, 7.
    • Verify Claude/Cursor/Smithery snippets vs live URL + npx; hosted keeps Bearer header → Steps 5, 8.
    • Fix ShareMcpInstall header bug + public-no-token note → Step 4.
    • Short docs/ install page, both paths, copy-paste blocks → Step 8 (docs/install.md).
    • Submission steps captured but human-gated; repo-public a documented prereq → Step 8 (gated checklist), reinforced across Prereqs/Risks.

    Effort
    M — ~2-3 person-days. ~1 day packaging/bundling + dependency audit (highest-risk), ~0.5 day manifests + validation, ~0.5 day ShareMcpInstall/worker visibility fix + verify, ~0.5 day docs + endpoint verification.


    Implementation plan generated for execution (planning phase). Part of the roadmap (#4).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Post-demo: distribution, polish, and reliabilitymcpModel Context Protocol servers and toolsonboardingUser and developer onboarding / first-run experienceplatformBackend platform plumbing: Worker, D1, queues, secrets, metering

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions