Catalog npm
PACKAGE EXPLORERView in registry

3m4-mcp

npm

MCP server for the 3m4 URL shortener: create and manage short links, read click analytics, with role-based tool access and audit logging.

3m4mcpmodel-context-protocolrbacurl-shortener
Versions collected2
Direct dependencies8From release 0.1.0
Last observed20 Oct, 19:48 UTC

README

Current package documentation

3m4-mcp

Model Context Protocol server for the 3m4 URL shortener. Lets an AI agent (Claude Code, Claude Desktop, Cursor, the MCP Inspector, your own app) create and manage short links and read click analytics — with role-based tool access, a human approval step for destructive actions, and an audit trail.

npx 3m4-mcp        # stdio (local hosts)
https://3m4.net/mcp  # Streamable HTTP (remote hosts)

Quick start

  1. Create an API key in the dashboard (/dashboard/settings) and pick a role.
  2. Register the server with your host.

Claude Code

claude mcp add 3m4 --scope user \
  --env M4_API_KEY=m4_live_… \
  -- npx -y 3m4-mcp

Cursor / Claude Desktop / any JSON config

{
  "mcpServers": {
    "3m4": {
      "command": "npx",
      "args": ["-y", "3m4-mcp"],
      "env": { "M4_API_KEY": "m4_live_…" }
    }
  }
}

Remote (Streamable HTTP) — no install, the key travels as a Bearer token:

claude mcp add --transport http 3m4 https://3m4.net/mcp \
  --header "Authorization: Bearer m4_live_…"

Then ask: "shorten https://example.com/some/long/url and give me the QR", "which of my links got the most clicks this month?", "pause the link c7Xk2".

Configuration

VariableDefaultMeaning
M4_API_KEY—Required for stdio. The 3m4 API key (m4_live_…).
M4_API_URLhttps://3m4.net/apiAPI base URL. Self-hosters point this at their instance.
M4_AUDIT_LOGstderrstderr, none, or a file path (JSON lines).
M4_MCP_PORT3333HTTP entrypoint only.

Roles and tools (RBAC)

Each API key has a role. The server asks the API for the key's scopes once (GET /apikeys/me) and only registers the tools those scopes allow, so a read-only key never even lists delete_link. Every call is re-checked at call time, and the API enforces the same scopes independently — three layers, and a bug in the server can hide access but never grant it.

ToolScopeviewereditoradmin
list_linkslinks:read✓✓✓
get_linklinks:read✓✓✓
get_link_analyticsanalytics:read✓✓✓
get_account_overviewanalytics:read✓✓✓
get_usageusage:read✓✓✓
create_linklinks:write✓✓
update_linklinks:write✓✓
delete_linklinks:delete✓
get_audit_logaudit:read✓

Roles are defined once in packages/shared/api_key_roles.json and shared by the API, the dashboard and this server.

Also exposed: resources m4://usage and m4://links/{id}, and the link_report prompt.

Human approval for destructive actions

delete_link is irreversible, so it never runs on the model's say-so alone:

  • On hosts that support MCP elicitation (Claude Code, Inspector) the user gets a native confirmation dialog showing exactly which link will be deleted.
  • On hosts that don't (and always over HTTP, which is stateless), the tool refuses with confirmation_required until the model calls it again with confirm: true — which it can only honestly do after asking the user.

Audit trail

Two complementary logs:

  • Server-side (audit_events table, GET /account/audit, tool get_audit_log): every mutation and every authorization denial, written in the same transaction as the change.
  • Agent-side (M4_AUDIT_LOG, JSON lines): every tool call the agent attempted, including ones refused locally that never reached the API, with duration, outcome (ok|denied|error|cancelled|pending) and compacted args.

Design notes

  • Structured outputs. Every tool declares an outputSchema and returns structuredContent; hosts can validate and chain results without parsing prose.
  • Errors are results. API errors come back as isError with the stable code (limit_reached, insufficient_scope, not_found…) so the model can recover instead of crashing.
  • stdio hygiene. stdout is the protocol channel; all logging goes to stderr.
  • Stateless HTTP. Each request builds a server bound to the caller's key; tenants can never share state. The /apikeys/me lookup is cached 60 s per key.

Development

bun install
bun test            # in-memory MCP client ↔ server against a fake API
bun run build
M4_API_KEY=… M4_API_URL=http://localhost:8080/api bun run inspect   # MCP Inspector
M4_API_KEY=… M4_API_URL=http://localhost:8080/api bun run dev:http  # http://localhost:3333/mcp

The Docker image (packages/mcp-server/Dockerfile) runs the HTTP flavour and is wired into deploy/docker-compose.yml as the mcp service, exposed by Caddy at /mcp.

License

MIT © Enrique Magallon Alvarez

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 2

Collected releases
VersionPublishedDirect dependenciesOpen version
0.1.0Registry latest15 Oct 20268
0.0.0-stage15 Oct 20260

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
devace-93/3m4

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 19:48 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:d5a61ea4bedf79704eb6b87a9032b77f9b6412bfd4a4efdd3da11840615411b9