Skip to main content
Glama

carry

CI License: Apache-2.0

Carry a context pack between your Claude surfaces. Push your voice rules, system facts, or style guide from Claude Code at your desk; read them from Claude mobile on the go. One small remote MCP server, always live, no manual re-sync.

carry exists because there is no public API to write Claude Project knowledge, so keeping your mobile Claude in sync with the instructions you maintain in a repo means either re-uploading files by hand (which drifts the moment you do) or this: a server both surfaces attach to, holding one current pack per namespace.

  Claude Code (your repo)                         Claude mobile / web / desktop
  compiles + pushes the pack                      connector attached once via web
            │                                              │
            │  push_context (write token)                  │  get_context (read: token or OAuth)
            ▼                                              ▼
        ┌─────────────────────────────────────────────────────┐
        │   carry  —  the current pack for your namespace       │
        └─────────────────────────────────────────────────────┘

Because Claude's remote MCP connectors are account-brokered (you add the URL once on claude.ai web and it appears on your phone, desktop, and web on the same account), every surface reads the live pack. Update the pack from your desk, and the next get_context anywhere has it.

What you'd put in a pack

carry holds one current pack per namespace — context you author, not memory it accumulates. Good packs are things you want identical on every surface:

  • Voice, style, and brand. Draft on your phone in the same voice your terminal writes in — tone, banned words, formatting rules.

  • Coding standards. Your CLAUDE.md conventions — stack, naming, testing style, "never do X" — on hand when you ask Claude about code from any device or repo.

  • Project context. Current architecture, the domain glossary, key decisions, so any surface answers with your project's facts instead of generic ones.

  • Who you are. Your bio, stack, and tools, so you stop re-introducing yourself at the start of every mobile session.

  • A team's house rules. One shared read pack — API conventions, style guide, onboarding facts — that everyone's Claude reads the same way.

  • Slash commands & Claude config, across machines. Push your .claude/commands files, then carry pull them onto another machine — your command library and CLAUDE.md travel without a git remote (see the CLI below).

What it is not for: fast-changing, per-conversation memory. carry is a deterministic pack you control, not an accumulating memory store.

Related MCP server: claude-sync

Why not a slash command or CLAUDE.md?

Because those are local and single-surface. A slash command lives in .claude/commands on one machine, and CLAUDE.md is read only by Claude Code — your phone and claude.ai never see either. carry is the cross-surface, single-source layer they can't be: one pack you update at your desk and read on demand from mobile, web, desktop, and the CLI. On Claude Code alone you may not need it; carry earns its place the moment you leave the terminal.

Try it (no deploy)

npm install
npm run demo

Boots a throwaway in-memory server, pushes a sample pack with the write token, and reads it back with the read token — the whole loop on localhost, nothing written to disk.

Self-hosted — you run it, nobody hosts it for you

carry is deploy-your-own. There is no shared "carry cloud" and no maintainer footing a bill for your usage. You stand up your own instance (a one-click Render Blueprint is included), set your own tokens, and own your data. The optional OAuth mode uses your authorization server (e.g. your own free-tier WorkOS AuthKit tenant) — again, your account, not the maintainer's. Running it costs you only what your host charges (often $0 to kick the tires; a few dollars a month for a persistent disk). See docs/deploy.md.

What it exposes (MCP)

Stateless Streamable HTTP on POST /mcp (a fresh MCP server per request; no session state to leak). Plus GET /healthz.

  • get_context — return the current pack for your namespace. Read this before drafting.

  • push_context — replace the pack. Requires a write token.

  • carry://context — the same pack as an MCP resource.

Auth model

carry supports two authentication paths. Static tokens are the default; OAuth is opt-in and off unless you configure it.

1. Static bearer tokens (default — the write path, and a simple read path). A namespace owns two secrets: a read token and a write token. They must differ, so a leaked read connector can never overwrite your pack. The token is the namespace — the model never supplies one, so it can't touch anyone else's pack. This is all you need for Claude Code (push with the write token) and for any MCP client that lets you paste an Authorization: Bearer header. No external service required.

2. OAuth 2.1 protected-resource mode (optional — for the claude.ai connector). The claude.ai web connector dialog is OAuth-only on personal accounts (there's no field to paste a bearer read token). To attach carry as a mobile/web/desktop connector, enable OAuth: carry becomes an RFC 9728 protected resource that validates JWTs (issuer + audience + expiry) against your authorization server's JWKS. OAuth callers are granted read scope only — pushing always requires the static write token. OAuth is enabled solely by setting CARRY_OAUTH_ISSUER; leave it unset and nothing about the static-token behavior changes. See docs/connector-auth-spike.md for why this path exists.

Lock it to yourself (OAuth mode). By default, carry accepts any user who can authenticate to your configured authorization server / tenant. For a single-user self-host that tenant is just you, so this is usually fine. If your tenant has other users, set an allowlist so only you can read the pack: CARRY_OAUTH_ALLOWED_SUBS and/or CARRY_OAUTH_ALLOWED_EMAILS (comma-separated; a caller passes if its sub or email is listed). Unset = open to the whole tenant.

Run locally

npm install
cp .env.example .env        # then set CARRY_NAMESPACES with your own tokens
npm run dev                 # starts on :8080 (tsx watch)

Verify:

npm run smoke               # storage + auth cores, no network
npm run typecheck
npm test                    # full vitest suite

The CLI (compile + push a pack)

carry ships a small CLI (bin: carry, or node dist/cli.js after npm run build) that compiles one or more Markdown files into a single pack, stamps its metadata (source, title, gitHash, builtAt), and pushes it:

# Build the CLI once
npm run build

# Push a pack (write token from $CARRY_WRITE_TOKEN or --token)
node dist/cli.js push --url https://your-instance.example.com/mcp \
  --from ./context/identity.md ./context/voice.md --title "My Claude context"

# Read the current pack back (read/write token from env or --token)
node dist/cli.js get --url https://your-instance.example.com/mcp

# Pull the pack back into files — the inverse of push. Splits by source marker and
# writes each file into the target dir. Sync your commands/config across machines.
node dist/cli.js pull --url https://your-instance.example.com/mcp --to ./.claude/commands

Tokens are read from CARRY_WRITE_TOKEN / CARRY_READ_TOKEN in the environment (never passed on the command line unless you use --token). carry pull reduces every source name to a basename, so a pack can never write outside your target directory.

Configuration

All configuration is via environment variables (see .env.example):

Variable

Required

Purpose

CARRY_NAMESPACES

yes

JSON array of {namespace, readToken, writeToken}. Read ≠ write. The single source of auth. Server fails loudly on bad config.

PORT

no

HTTP port. Default 8080; hosts like Render inject it.

CARRY_DB_URL

no

Durable storage. Unset = in-memory (dev; lost on restart). file:/data/carry.db for an on-disk SQLite/libSQL file, or libsql://… for hosted Turso.

CARRY_DB_AUTH_TOKEN

no

Auth token for a hosted Turso libsql:// URL.

CARRY_RATE_LIMIT_PER_MIN

no

Fixed-window limit on POST /mcp, keyed by IP and token. Default 120.

CARRY_OAUTH_ISSUER

no

On-switch for OAuth mode. Your authorization server / AuthKit domain (JWT iss). Unset = OAuth disabled.

CARRY_OAUTH_AUDIENCE

if issuer set

carry's canonical MCP URL, registered as the OAuth resource indicator (JWT aud).

CARRY_OAUTH_NAMESPACE

if >1 namespace

Which namespace OAuth callers map to (read scope). Defaults to the sole namespace when there's only one.

CARRY_OAUTH_JWKS_URL

no

Override for the JWKS endpoint. Defaults to ${issuer}/oauth2/jwks.

CARRY_OAUTH_ALLOWED_SUBS

no

Lock-to-user allowlist by token sub (comma-separated). Unset = any authenticated tenant user.

CARRY_OAUTH_ALLOWED_EMAILS

no

Lock-to-user allowlist by email claim (comma-separated, case-insensitive). A caller passes if its sub or email is listed.

Deploy your own

The included render.yaml Blueprint stands up a Docker web service with a persistent disk for the libSQL file. Step-by-step (fork → tokens → deploy → attach the connector) is in docs/deploy.md; day-two operations (token rotation, backups) are in ops/runbook.md.

Roadmap

  • Lock-to-user — allowlist a specific sub/email in OAuth mode (closes the known limitation above).

  • Multi-tenant — namespace provisioning + per-namespace tokens in a DB behind the ContextStore interface, minimal signup, isolation tests.

  • An accumulating memory layer as an alternative to a single deterministic pack.

How it's built

TypeScript (strict, ESM, Node ≥20). Express entry, the official MCP SDK's Streamable HTTP transport in stateless mode, a ContextStore interface with in-memory and libSQL implementations selected by env, structured per-request logging (never logs tokens or content), and a fixed-window rate limiter. Auth is a small, separately tested module (static tokens + optional jose-backed JWT verification). The test suite runs offline (injected local JWKS for the OAuth path).

Contributing

Issues and PRs welcome — see CONTRIBUTING.md.

License

Apache-2.0. See LICENSE.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server for portable context management across AI assistants, providing tools to store and retrieve persistent context, instructions, and execute sandboxed bash commands with automatic git commits, using OAuth 2.1 and magic link authentication.
    1
    Apache 2.0
  • A
    license
    Not graded
    quality
    D
    maintenance
    Cross-machine context sharing MCP server for Claude Code, enabling shared memory of decisions, patterns, and knowledge across multiple Claude Code instances on different machines.
    39
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Persistent context layer for AI coding assistants. Sync memory files from Claude Code to ChatGPT, Gemini, Cursor, Windsurf, and Copilot via a native MCP endpoint. Bearer token auth, JSON-RPC 2.0 over stateless HTTP. Free tier available.

View all related MCP servers

Related MCP Connectors

  • A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…

  • One shared context your team's AI tools read & write over MCP. No re-explaining. Free.

  • A MCP server built for developers enabling Git based project management with project and personal…

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/emtcmca/carry'

If you have feedback or need assistance with the MCP directory API, please join our Discord server