npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wahlu/mcp-server

v0.11.3

Published

MCP server for the Wahlu social media management API

Readme

@wahlu/mcp-server

The Model Context Protocol adapter for Wahlu's agent-first public API. Its hosted production inventory is exactly twenty-one intention-level tools. The local stdio package adds a twenty-second local-only tool, upload_media_from_file.

The isolated held inventory exposes exactly these eleven tools: get_context, list_targets, get_platform_capabilities, import_media_from_url, get_media, create_media_repair_derivative, create_draft, preflight_draft, create_schedule, get_schedule, and get_publish_run_receipt.

| Category | Tools | |----------|-------| | Agent discovery | get_context, list_targets, get_target_dynamic_options, get_platform_capabilities | | Media acquisition | import_media_from_url, list_media, upload_media, get_media, create_media_repair_derivative, upload_media_from_file (local stdio only) | | Content | list_content_items, get_content_item, create_draft, update_draft_tiktok_privacy, preflight_draft | | Schedules | list_schedules, create_schedule, get_schedule, get_publish_run_receipt, reschedule_schedule, cancel_schedule | | Cleanup | cleanup_provider_publications (production only; exact receipt-bound cleanup) |

Every tool delegates to @wahlu/api-client. There is no generic HTTP transport, database or storage access, second API retry implementation, generic polling loop, execution, approval, or generic retry tool. Schedule mutations require exact confirmation and stable idempotency; cancellation is limited to unsent Schedules with no execution history. The local file tool alone supports MCP cancellation and a bounded readiness window. Production cleanup is limited to the exact public receipt authority and supported provider publications.

For a selected TikTok target, call get_target_dynamic_options immediately before choosing privacy, then pass one returned value with the same brand_id and integration_id to update_draft_tiktok_privacy. Wahlu re-queries TikTok before changing only that existing draft and rejects stale or unsupported values. Neither tool creates a replacement draft, Schedule, job, queue entry, or provider post. Dynamic-option discovery requires integrations:write: its live provider-backed refresh may rotate stored credentials, update provider-effect lease state, or mark the integration for reauthorisation. Both tools are deliberately absent from the exact-eleven held inventory.

API key

Create a Wahlu API key in the dashboard under Settings > API Keys, grant only the scopes the workflow needs, and restrict it to the intended brands where possible.

The executable reads these environment variables:

| Variable | Required | Default | |----------|----------|---------| | WAHLU_API_KEY | Yes | — | | WAHLU_API_URL | No | https://api.wahlu.com | | WAHLU_MEDIA_URL | No | https://media.wahlu.com |

Only override the two URLs for a Wahlu development or self-hosted environment. HTTPS is required except for an explicit loopback development origin.

Connect an agent

The npm package is a local stdio MCP server. The examples below use npx, so a compatible Node.js runtime must be available to the host application.

Local agents can call upload_media_from_file with an absolute caller-owned file path. When the file's context is known, provide a meaningful name with the real file extension and an optional factual description. Omit the description when the context is insufficient; the tool does not generate or guess one. Hosted HTTP MCP never accepts local paths.

The tool fingerprints the exact logical upload from its content digest and metadata, not its local path. For one bounded local-server session, a retry of that same logical upload reuses the first caller's idempotency key even if an agent invents a new key or the file has moved. The process-local registry expires entries after one hour and keeps at most 256; changing the content or upload metadata remains a distinct upload.

Slow PUTs honour MCP cancellation and emit throttled standard byte-progress notifications when the caller supplies a progress token. If the PUT connection ends ambiguously, the tool reads the known canonical Media once before deciding whether the upload failed, so accepted bytes are not reported as a new or missing Media.

After upload, the tool follows only that Media for up to 15 minutes, with at least 15 seconds between reads and any longer API-recommended delay respected. It emits stage progress when supported, stops immediately at ready, failed, stuck, or cancellation, and treats queued or processing as honest successful results when the window ends. Use the returned progress.recommended_poll_after_ms before checking again. The separate get_media tool always performs exactly one read and never polls.

Uploading media that has no public URL

upload_media covers any image or video that is not already on a public URL, on hosted connectors as well as locally, with no shared filesystem. Always supply brand_id, file_name, content_type, the exact size and a stable idempotency_key. How the bytes travel depends on how big they are:

8 MB or less — send the bytes. Put them in content as {"type": "base64", "data": "…"} or an MCP embedded resource. The declared type and size are verified against the real bytes, and the call returns the finished media item.

Larger, including most video — stream them. Omit content and send content_sha256, the lowercase hex SHA-256 of the file. The call returns a short-lived upload capability bound to that exact media ID, content type, byte count and digest:

{ "id": "…", "status": "uploading",
  "upload": { "method": "PUT", "url": "https://media.wahlu.com/uploads/…", "headers": { … },
              "expires_at": "…" } }

PUT the file to that URL with the returned headers, then call get_media with the same ID to confirm it is ready. The URL is on Wahlu's own media origin — no storage-provider URL, object path or provider credential is ever exposed. Wahlu re-sniffs the content type and re-checks the byte count on arrival, so a payload that disagrees with what was declared is rejected. Normal media policy applies: 25 MB for images, 500 MB for video.

Either way the returned ID can be used directly in a draft's media_ids or thumbnail_media_id.

Codex

Add Wahlu from a terminal, then restart the active Codex surface:

codex mcp add wahlu --env WAHLU_API_KEY=wahlu_live_replace_me -- npx -y @wahlu/mcp-server

Confirm it with codex mcp list or /mcp. Codex CLI, the IDE extension, and the Codex desktop app share the same MCP configuration. See the Codex MCP guide.

Claude Code

Add Wahlu at user scope and confirm the connection:

claude mcp add --scope user wahlu --env WAHLU_API_KEY=wahlu_live_replace_me -- npx -y @wahlu/mcp-server
claude mcp get wahlu

Use /mcp inside Claude Code to inspect its tools. On native Windows, configure the command as cmd /c npx -y @wahlu/mcp-server if npx cannot be launched directly. See Anthropic's Claude Code MCP guide.

Claude Desktop and Cowork

For local Claude Desktop, add this server to claude_desktop_config.json through Developer settings and restart Claude Desktop:

{
  "mcpServers": {
    "wahlu": {
      "command": "npx",
      "args": ["-y", "@wahlu/mcp-server"],
      "env": {
        "WAHLU_API_KEY": "wahlu_live_replace_me"
      }
    }
  }
}

Check the connection under Developer settings or Connectors in the composer. Local Cowork sessions can use locally configured MCP servers when that capability is enabled. Remote Cowork sessions cannot run this local npm process; they require a publicly reachable remote MCP connector. See Anthropic's guides for local MCP servers and remote MCP connectors.

ChatGPT web and hosted Work mode

This local stdio npm package does not connect to ChatGPT web or its hosted Work mode. Those hosted surfaces use Wahlu's OAuth-backed ChatGPT App at https://mcp.wahlu.com/mcp. It uses the existing Wahlu authorization service and the same workspace, brand, tenant, entitlement, and scope checks as the hosted API. Do not paste a local npx command or an API key into ChatGPT web.

To test the private app in ChatGPT Developer Mode:

  1. In a web browser, open chatgpt.com. Do not use the ChatGPT desktop/Codex MCP settings for this flow.
  2. On the ChatGPT website, open Settings → Security and login and turn on Developer mode.
  3. Open ChatGPT Plugins, select the plus button, and enter https://mcp.wahlu.com/mcp as the MCP server URL.
  4. Complete the in-browser Wahlu sign-in and consent flow. Never enter a Wahlu API key in ChatGPT.
  5. Add the app to a new conversation and ask: “Show the latest media for my selected Wahlu brand.”
  6. Check that only that brand's media appears, previews load from media.wahlu.com, and an empty brand shows a useful empty state.
  7. After any tool, schema, OAuth, or widget-resource change, open the app detail page and select Refresh before retesting.

An existing local Codex MCP entry such as wahlu_-private- is separate from this web connection. Leave it unchanged; the private ChatGPT review does not require deleting, disabling, uninstalling, or recreating it.

The production hosted surface includes live target-bound TikTok privacy discovery, exact existing-draft privacy updates, explicit reviewed repair derivatives, and exact receipt lookup and receipt-bound provider cleanup. The isolated candidate/live-test surface supports eleven held tools and cannot advertise or use cleanup or publish:execute. It creates Schedules through the normal Wahlu permission checks: pending_review stays held, while an approved Schedule additionally requires publish:execute and may publish later through the established pipeline. The repository's chatgpt-app-submission.json is a submission-form draft; no app has been submitted or published from this repository.

The hosted app adds six compact result widgets: integration health for list_targets, the media library for list_media, media detail for get_media, post review for create_draft, the calendar for list_schedules, and post status shared by get_schedule and get_publish_run_receipt. They render scoped tool results; the media widgets use opaque Wahlu media delivery URLs supplied only through widget _meta, redacted from model-visible structuredContent and text content, without fetching a second API or exposing storage paths. Each media widget keeps the exact authenticated Wahlu app link primary and the raw media download secondary. Deterministic descriptor, resource, CSP, schema, and safety checks live in the apps/mcp and packages/mcp-server suites, so most changes can be verified without a live OAuth client or production deployment. See OpenAI's MCP server guide, authentication guide, and Developer Mode testing guide.

Library-only held HTTP adapter

The package also exports createHeldMcpHttpHandler for local, injected integration tests and future authenticated resource-server wiring. It accepts only loopback peers and a loopback Host, serves only /mcp, and creates fresh authority, client, MCP server, and transport state for every POST. It never reads WAHLU_API_KEY or another process-wide identity.

Callers must inject an immutable request authority, a fresh client for the held eleven-tool inventory, the canonical remote media URL validator, and a bounded application audit sink. There is no anonymous/default authority or header-based test-account bypass. The held adapter keeps get_publish_run_receipt read-only by stripping cleanup authority and cleanup links, omits the cleanup tool, and constrains create_schedule to pending_review; it cannot represent approved or publish:execute.

This adapter is deliberately stateless. It does not issue MCP session IDs and returns 405 for GET or DELETE. SSE, resumability, event storage, tasks, prompts, resources, sampling, elicitation, server notifications, OAuth parsing, legacy HTTP+SSE, and any approval, execution, queue, provider, or publishing tool are absent. OAuth, public hosting, and durable deployment wiring are separate work and are not configured by this package.

Held Schedule workflow

  1. Call get_context to inspect the workspace, accessible brands, and exact API-key scopes.
  2. Call list_targets for the selected brand and repair any reported blockers.
  3. Call get_platform_capabilities before constructing platform-specific content.
  4. Call import_media_from_url with a public HTTP(S) URL and stable idempotency_key.
  5. Call get_media once when its current readiness is needed. The tool does not poll.
  6. Call create_draft with canonical content and a stable idempotency_key.
  7. Call preflight_draft with the intended targets, time, and explicit approval state.
  8. Call create_schedule with an explicit approval_status and stable idempotency_key.
  9. Call get_schedule once to inspect its canonical status, blocker, and next action.
  10. If a known Schedule has a publish run, call get_publish_run_receipt to inspect its redacted per-platform outcomes. The held projection does not include cleanup authority or cleanup links.

A pending_review Schedule is held with APPROVAL_PENDING. It creates no execution or job and has no publishing-provider effect. An explicit approved Schedule additionally requires publish:execute and may later cause an external publication. Tool descriptions, static worst-case metadata, and result-specific safety fields preserve that distinction.

Structured success preserves the API's canonical meta object. HTTP status and idempotency replay truth are separate under transport. A replay returns the existing resource and is not reported as a new write. Errors remain structured failures rather than inferred success.

Example prompts

Use get_context, list_targets, and get_platform_capabilities to show what this key can safely do.

Import this public campaign image with a stable key, then read the media item once.

Create a draft, preflight it, and create a pending_review Schedule with stable idempotency keys.
Then read the Schedule once and report its blocker and next action. Do not approve or publish it.

Documentation

License

MIT