Observed since 20 Oct, 19:48 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
MCP server for the 3m4 URL shortener: create and manage short links, read click analytics, with role-based tool access and audit logging.
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)
/dashboard/settings) and pick a role.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".
| Variable | Default | Meaning |
|---|---|---|
M4_API_KEY | — | Required for stdio. The 3m4 API key (m4_live_…). |
M4_API_URL | https://3m4.net/api | API base URL. Self-hosters point this at their instance. |
M4_AUDIT_LOG | stderr | stderr, none, or a file path (JSON lines). |
M4_MCP_PORT | 3333 | HTTP entrypoint only. |
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.
| Tool | Scope | viewer | editor | admin |
|---|---|---|---|---|
list_links | links:read | ✓ | ✓ | ✓ |
get_link | links:read | ✓ | ✓ | ✓ |
get_link_analytics | analytics:read | ✓ | ✓ | ✓ |
get_account_overview | analytics:read | ✓ | ✓ | ✓ |
get_usage | usage:read | ✓ | ✓ | ✓ |
create_link | links:write | ✓ | ✓ | |
update_link | links:write | ✓ | ✓ | |
delete_link | links:delete | ✓ | ||
get_audit_log | audit: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.
delete_link is irreversible, so it never runs on the model's say-so alone:
confirmation_required until the model calls it again with
confirm: true — which it can only honestly do after asking the user.Two complementary logs:
audit_events table, GET /account/audit, tool
get_audit_log): every mutation and every authorization denial, written in
the same transaction as the change.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.outputSchema and returns
structuredContent; hosts can validate and chain results without parsing prose.isError with the stable
code (limit_reached, insufficient_scope, not_found…) so the model can
recover instead of crashing./apikeys/me lookup is cached 60 s per key.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.
MIT © Enrique Magallon Alvarez
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.1.0Registry latest | 15 Oct 2026 | 8 | |
| 0.0.0-stage | 15 Oct 2026 | 0 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 19:48 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:d5a61ea4bedf79704eb6b87a9032b77f9b6412bfd4a4efdd3da11840615411b9