moltbook-http-mcp 1.1.1 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,8 +1,5 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 Venkatesh
4
- Copyright (c) 2025 Indra
5
-
6
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
4
  of this software and associated documentation files (the "Software"), to deal
8
5
  in the Software without restriction, including without limitation the rights
@@ -19,4 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
16
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
17
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
18
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
- SOFTWARE.
19
+ SOFTWARE.
package/README.md CHANGED
@@ -80,11 +80,39 @@ When run with piped stdin/stdout (e.g. by Cursor), stdio mode is used automatica
80
80
  | API key | `MOLTBOOK_API_KEY` | — | **Required** for all tools except `moltbook_agent_register`. |
81
81
  | MCP port | `-m`, `--mcpPort` | `3003` | Port for the MCP HTTP server (HTTP mode only). |
82
82
  | Stdio | `--stdio` | auto | Use stdin/stdout for MCP (subprocess). Auto if stdin is not a TTY. |
83
+ | HTTPS key | `--key`, `MCP_HTTPS_KEY_PATH` | — | Path to TLS private key PEM; enables HTTPS when used with cert. |
84
+ | HTTPS cert | `--cert`, `MCP_HTTPS_CERT_PATH` | — | Path to TLS certificate PEM; enables HTTPS when used with key. |
83
85
 
84
86
  ```sh
85
87
  moltbook-mcp --help
86
88
  ```
87
89
 
90
+ ### HTTPS on localhost
91
+
92
+ To run the MCP HTTP server over HTTPS on localhost, provide a TLS key and certificate. Both are required.
93
+
94
+ **CLI:**
95
+
96
+ ```sh
97
+ moltbook-mcp --key ./localhost-key.pem --cert ./localhost-cert.pem
98
+ ```
99
+
100
+ **Environment:**
101
+
102
+ ```sh
103
+ export MCP_HTTPS_KEY_PATH=./localhost-key.pem
104
+ export MCP_HTTPS_CERT_PATH=./localhost-cert.pem
105
+ moltbook-mcp
106
+ ```
107
+
108
+ **Generating localhost certs:**
109
+
110
+ - **mkcert** (recommended; trusted in browsers): `mkcert -install` then `mkcert localhost` → `localhost+1.pem` (cert) and `localhost+1-key.pem` (key).
111
+ - **OpenSSL** (self-signed):
112
+ `openssl req -x509 -newkey rsa:4096 -keyout localhost-key.pem -out localhost-cert.pem -days 365 -nodes -subj /CN=localhost`
113
+
114
+ Then point your IDE at `https://localhost:3003/mcp` (or your port).
115
+
88
116
  ---
89
117
 
90
118
  ## Add MoltBook MCP to your IDE
@@ -93,7 +121,7 @@ moltbook-mcp --help
93
121
  2. **Add the MCP server** in your IDE (e.g. Cursor → Settings → MCP). You can use either:
94
122
 
95
123
  **Option A — HTTP (molt)**
96
- Run the server yourself (`moltbook-mcp` or `moltbook-mcp -m 9000`), then point the IDE at the URL:
124
+ Run the server yourself (`moltbook-mcp` or `moltbook-mcp -m 9000`), then point the IDE at the URL. Use `https://` if you started the server with `--key` and `--cert`:
97
125
 
98
126
  ```json
99
127
  {
package/dist/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- "use strict";import{loadEnv as e}from"./utils/env.js";e();import s from"yargs";import{hideBin as t}from"yargs/helpers";import{startServer as i,startStdioServer as a}from"./index.js";const r=s(t(process.argv)).options({mcpPort:{type:"number",default:3003,alias:"m",describe:"Port for MCP HTTP server"},stdio:{type:"boolean",default:!1,describe:"Run MCP over stdin/stdout (for Cursor moltcli / subprocess)"}}).help().alias("h","help").parseSync();r.help&&process.exit(0);const p=r.stdio||!process.stdin.isTTY;p?a({}).catch(o=>{console.error("MCP stdio server error:",o),process.exit(1)}):i({mcpPort:r.mcpPort});
2
+ "use strict";import{loadEnv as s}from"./utils/env.js";s();import o from"yargs";import{hideBin as i}from"yargs/helpers";import{startServer as a,startStdioServer as n}from"./index.js";import{getMCP_HTTPS_CERT_PATH as d,getMCP_HTTPS_KEY_PATH as p}from"./utils/env.js";const e=o(i(process.argv)).options({port:{type:"number",default:3003,alias:"p",describe:"Port for MCP HTTP server"},stdio:{type:"boolean",describe:"Run MCP over stdin/stdout. Default: true when stdin is not a TTY (subprocess), else false. Use --no-stdio to force HTTP server."},auth:{type:"boolean",default:!1,describe:"Require JWT auth on POST /mcp"},key:{type:"string",describe:"Path to TLS private key PEM (enables HTTPS with --cert)"},cert:{type:"string",describe:"Path to TLS certificate PEM (enables HTTPS with --key)"}}).help().alias("h","help").parseSync();e.help&&process.exit(0);const c=e.stdio===!1?!1:e.stdio===!0?!0:!process.stdin.isTTY;if(c)n({}).catch(t=>{console.error("MCP stdio server error:",t),process.exit(1)});else{const t=(e.key??p())||void 0,r=(e.cert??d())||void 0;a({mcpPort:e.port,auth:e.auth,keyPath:t||void 0,certPath:r||void 0})}
@@ -1 +1 @@
1
- "use strict";import{randomUUID as p}from"node:crypto";import{StreamableHTTPServerTransport as u}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{isInitializeRequest as c}from"@modelcontextprotocol/sdk/types.js";import{transports as n}from"./mcp.transports.js";import{createMCPServer as R}from"./mcp.server.js";import{LOGGER as t}from"../utils/logger.js";export const handleRequest=async(e,s,i)=>{t.log("1.Received MCP request:",e.body);const{jsonrpc:d,id:l,method:m,params:f}=e.body;if(d!=="2.0"||!m){s.status(400).json({jsonrpc:"2.0",id:l||null,error:{code:-32600,message:"Invalid Request",data:"Must be valid JSON-RPC 2.0"}});return}try{const r=e.headers["mcp-session-id"];let o;if(r&&n[r])t.log(`Session exists: ${r}`),o=n[r];else if(!r&&c(e.body)){o=new u({sessionIdGenerator:()=>p(),enableJsonResponse:!0,onsessioninitialized:a=>{t.log(`Session initialized with ID: ${a}`),n[a]=o}}),t.log("Connecting to MCP server with CLI params:",i),await R(i).connect(o),await o.handleRequest(e,s,e.body);return}else{t.log("Invalid request - no session ID or not initialization request"),s.status(400).json({jsonrpc:"2.0",error:{code:-32e3,message:"Bad Request: No valid session ID provided"},id:null});return}await o.handleRequest(e,s,e.body)}catch(r){t.error("Error handling MCP request:",r),s.headersSent||s.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:"Internal server error"},id:null})}};
1
+ "use strict";import{randomUUID as g}from"node:crypto";import{StreamableHTTPServerTransport as h}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{isInitializeRequest as R}from"@modelcontextprotocol/sdk/types.js";import{transports as u}from"./mcp.transports.js";import{createMCPServer as v}from"./mcp.server.js";import{getMoltbookApiKey as b,runWithMoltbookApiKey as I}from"../utils/env.js";import{LOGGER as n}from"../utils/logger.js";const m={};function K(e){const r=e.headers.authorization;if(r?.startsWith("Bearer "))return r.slice(7).trim();const o=e.headers["x-api-key"];if(typeof o=="string")return o.trim();const i=e.query.apiKey;return typeof i=="string"?i.trim():""}export const handleRequest=async(e,r,o)=>{n.log("1.Received MCP request:",e.body);const{jsonrpc:i,id:p,method:l}=e.body;if(i!=="2.0"||!l){r.status(400).json({jsonrpc:"2.0",id:p||null,error:{code:-32600,message:"Invalid Request",data:"Must be valid JSON-RPC 2.0"}});return}const t=e.headers["mcp-session-id"],c=K(e),y=t?m[t]:void 0,f=b(),a=y??(c||f||"");if(!a){r.status(401).json({jsonrpc:"2.0",id:p??null,error:{code:-32001,message:"Missing API key. Send Authorization: Bearer <key>, X-API-Key: <key>, or ?apiKey=<key> in the URL."}});return}await I(a,async()=>{try{let s;if(t&&u[t])n.log(`Session exists: ${t}`),s=u[t];else if(!t&&R(e.body)){s=new h({sessionIdGenerator:()=>g(),enableJsonResponse:!0,onsessioninitialized:d=>{n.log(`Session initialized with ID: ${d}`),u[d]=s,m[d]=a}}),n.log("Connecting to MCP server with CLI params:",o),await v(o).connect(s),await s.handleRequest(e,r,e.body);return}else{r.status(400).json({jsonrpc:"2.0",error:{code:-32e3,message:"Bad Request: No valid session ID provided"},id:null});return}await s.handleRequest(e,r,e.body)}catch(s){n.error("Error handling MCP request:",s),r.headersSent||r.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:"Internal server error"},id:null})}})};
@@ -1 +1 @@
1
- "use strict";import{getMOLTBOOK_API_KEY as o}from"../utils/env.js";const r="https://www.moltbook.com/api/v1";export function getMoltbookBase(){return r}export function getApiKey(){const t=o();if(!t)throw new Error("MOLTBOOK_API_KEY is not set");return t}
1
+ "use strict";import{getMoltbookApiKey as o}from"../utils/env.js";const r="https://www.moltbook.com/api/v1";export function getMoltbookBase(){return r}export function getApiKey(){const t=o();if(!t)throw new Error("MOLTBOOK_API_KEY is not set");return t}
@@ -1 +1 @@
1
- "use strict";import{config as a}from"../config.js";const{MOLTBOOK_API_KEY:o}=a,u=(e,i,s)=>{if(!o){s();return}const t=e.headers.authorization,n=e.headers["x-api-key"],r=t?.startsWith("Bearer ")?t.slice(7):n??"";if(!r||r!==o){i.status(401).json({error:"Invalid or missing API key. Use Authorization: Bearer <key> or X-API-Key: <key>."});return}s()};export const useApiKeyAuth=e=>{e.use("/mcp",u)};
1
+ "use strict";import{jwtVerify as p,createRemoteJWKSet as l}from"jose";import{getAppApiKey as d,getAppDomain as u}from"../utils/env.js";export const apiKeyAuth=(e,t,r)=>{const n=d();if(!n){r();return}const s=e.headers.authorization,o=e.headers["x-api-key"],i=s?.startsWith("Bearer ")?s.slice(7):o??"";if(!i||i!==n){t.status(401).json({error:"Invalid or missing API key. Use Authorization: Bearer <key> or X-API-Key: <key>."});return}r()};let a=null;function f(){const e=u();if(!e)return null;if(!a){const t=e.startsWith("http")?`${e}/.well-known/jwks.json`:`https://${e}/.well-known/jwks.json`;a=l(new URL(t))}return a}async function h(e,t,r){const n=u();if(!n){t.status(503).json({error:"JWT auth not configured: APP_DOMAIN is not set"});return}const s=e.headers.authorization;if(!s?.startsWith("Bearer ")){t.status(401).end();return}const o=f();if(!o){t.status(503).json({error:"JWT auth not configured: APP_DOMAIN is not set"});return}try{const i=s.slice(7),{payload:c}=await p(i,o,{issuer:n,audience:"mcp"});e.user=c,r()}catch{t.status(401).end()}}export function requireAuth(e){return e?.auth?h:(t,r,n)=>n()}
@@ -1,2 +1,2 @@
1
- "use strict";import n from"express";import l from"cors";import{StdioServerTransport as d}from"@modelcontextprotocol/sdk/server/stdio.js";import{handleRequest as h}from"../mcp/mcp.server-handler.js";import{createMCPServer as u}from"../mcp/mcp.server.js";import{config as p}from"../config.js";import{getMOLTBOOK_API_KEY as S}from"../utils/env.js";import{LOGGER as a}from"../utils/logger.js";export const startStdioServer=async(s={})=>{const e=new d;await u(s).connect(e),process.stderr.writable&&process.stderr.write(`Moltbook MCP stdio server ready
2
- `)};const P=(s={})=>{const e=n();return e.use(l({origin:"*",exposedHeaders:["Mcp-Session-Id"]})),e.use(n.json()),e.use(n.json({limit:"10mb"})),e.use(n.urlencoded({extended:!0})),e.get("/health",async(o,t)=>{try{const r=S();let i="disconnected",c="not authorized";if(r)try{(await fetch("https://www.moltbook.com/api/v1/agents/status",{headers:{Authorization:`Bearer ${r}`}})).ok&&(i="connected",c="authorized")}catch{}const m={status:"healthy",moltbook:i,auth:c,mcp:"ready",timestamp:new Date().toISOString(),version:p.APP_VERSION||"1.0.0"};t.json(m)}catch(r){t.status(500).json({status:"unhealthy",error:r.message,timestamp:new Date().toISOString()})}}),e.post("/mcp",async(o,t)=>{await h(o,t,s)}),e.get("/mcp",async(o,t)=>{t.status(405).set("Allow","POST").send("Method Not Allowed")}),e.delete("/mcp",async(o,t)=>{a.log("Received DELETE MCP request"),t.writeHead(405).end(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed."},id:null}))}),e.get("/",(o,t)=>{t.json({name:"Moltbook MCP Gateway Server",description:"MCP server for Moltbook: the social network for AI agents. Post, comment, upvote, DMs, communities.",version:p.APP_VERSION||"1.0.0",endpoints:{health:{method:"GET",path:"/health",description:"Health check for all services"},mcp:{method:"POST",path:"/mcp",description:"JSON-RPC endpoint for MCP calls"},mcpMethods:{method:"GET",path:"/mcp/methods",description:"List all available MCP methods"}},architecture:"MCP integration",timestamp:new Date().toISOString()})}),e};export const startServer=(s={})=>{const{mcpPort:e=3003}=s||{};P(s).listen(e,t=>{t&&(a.error("Failed to start server:",t),process.exit(1)),a.log(`0. Moltbook MCP Server listening on port ${e}`)})};process.on("SIGINT",async()=>{a.log("Shutting down server..."),process.exit(0)});
1
+ "use strict";import p from"express";import h from"cors";import m from"fs";import u from"https";import{StdioServerTransport as f}from"@modelcontextprotocol/sdk/server/stdio.js";import{handleRequest as S}from"../mcp/mcp.server-handler.js";import{createMCPServer as y}from"../mcp/mcp.server.js";import{requireAuth as v}from"./app.auth.js";import{getMoltbookApiKey as g,getAppVersion as l}from"../utils/env.js";import{LOGGER as c}from"../utils/logger.js";export const startStdioServer=async(s={})=>{const e=new f;await y(s).connect(e),process.stderr.writable&&process.stderr.write(`Moltbook MCP stdio server ready
2
+ `)};const P=(s={})=>{const e=p();return e.use(h({origin:"*",exposedHeaders:["Mcp-Session-Id"]})),e.use(p.json()),e.use(p.json({limit:"10mb"})),e.use(p.urlencoded({extended:!0})),e.get("/health",async(o,t)=>{try{const n=g();let a="disconnected",r="not authorized";if(n)try{(await fetch("https://www.moltbook.com/api/v1/agents/status",{headers:{Authorization:`Bearer ${n}`}})).ok&&(a="connected",r="authorized")}catch{}const i={status:"healthy",moltbook:a,auth:r,mcp:"ready",timestamp:new Date().toISOString(),version:l()||"1.0.0"};t.json(i)}catch(n){t.status(500).json({status:"unhealthy",error:n.message,timestamp:new Date().toISOString()})}}),e.post("/mcp",v(s),async(o,t)=>{await S(o,t,s)}),e.get("/mcp",async(o,t)=>{t.status(405).set("Allow","POST").send("Method Not Allowed")}),e.delete("/mcp",async(o,t)=>{c.log("Received DELETE MCP request"),t.writeHead(405).end(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed."},id:null}))}),e.get("/",(o,t)=>{t.json({name:"Moltbook MCP Gateway Server",description:"MCP server for Moltbook: the social network for AI agents. Post, comment, upvote, DMs, communities.",version:l()||"1.0.0",endpoints:{health:{method:"GET",path:"/health",description:"Health check for all services"},mcp:{method:"POST",path:"/mcp",description:"JSON-RPC endpoint for MCP calls"},mcpMethods:{method:"GET",path:"/mcp/methods",description:"List all available MCP methods"}},architecture:"MCP integration",timestamp:new Date().toISOString()})}),e};export const startServer=(s={})=>{const{mcpPort:e=3003,keyPath:o,certPath:t}=s||{},n=P(s),a=r=>{r&&(c.error("Failed to start server:",r),process.exit(1));const i=o&&t?"https":"http";c.log(`0. Moltbook MCP Server listening on ${i}://localhost:${e}`)};if(o&&t)try{const r=m.readFileSync(o,"utf8"),i=m.readFileSync(t,"utf8");u.createServer({key:r,cert:i},n).listen(e,()=>a())}catch(r){c.error("HTTPS: failed to read key or cert:",r?.message??r),process.exit(1)}else n.listen(e,a)};process.on("SIGINT",async()=>{c.log("Shutting down server..."),process.exit(0)});
package/dist/utils/env.js CHANGED
@@ -1 +1 @@
1
- "use strict";import{config as n}from"dotenv";let e=!1;export function loadEnv(){e||(n(),e=!0)}export function getMOLTBOOK_API_KEY(){return loadEnv(),process.env.MOLTBOOK_API_KEY??""}export function getMCP_LOGGER(){return loadEnv(),!!process.env.MCP_LOGGER}export function getAppVersion(){return loadEnv(),process.env.npm_package_version??"1.0.0"}
1
+ "use strict";import{AsyncLocalStorage as o}from"node:async_hooks";import{config as i}from"dotenv";let n=!1;export function loadEnv(){n||(i(),n=!0)}const r=new o;export function runWithMoltbookApiKey(e,t){return r.run(e,t)}export function getAppApiKey(){return loadEnv(),process.env.MCP_API_KEY??""}export function getMoltbookApiKey(){loadEnv();const e=r.getStore();return e!==void 0?e:process.env.MOLTBOOK_API_KEY??""}export function getMcpLogger(){return loadEnv(),!!process.env.MCP_LOGGER}export function getAppVersion(){return loadEnv(),process.env.npm_package_version??"1.0.0"}export function getMCP_HTTPS_KEY_PATH(){return loadEnv(),process.env.MCP_HTTPS_KEY_PATH??""}export function getMCP_HTTPS_CERT_PATH(){return loadEnv(),process.env.MCP_HTTPS_CERT_PATH??""}export function getAppDomain(){return loadEnv(),process.env.APP_DOMAIN??""}
@@ -1,2 +1,2 @@
1
- "use strict";import{getMCP_LOGGER as a}from"./env.js";const l=(n,t)=>`\x1B]8;;${t}\x07${n}\x1B]8;;\x07`;function o(){const c=(new Error().stack?.split(`
1
+ "use strict";import{getMcpLogger as a}from"./env.js";const l=(n,t)=>`\x1B]8;;${t}\x07${n}\x1B]8;;\x07`;function o(){const c=(new Error().stack?.split(`
2
2
  `)||[])[3]||"",e=c.match(/\(([^)]+)\)/),s=e?e[1]:c.trim(),i=s.split("/").pop()||"unknown";return l(`${i}`,`${s}`)}const r=a();export const LOGGER={log:(...n)=>{r&&console.log(`[${o()}]`,...n)},info:(...n)=>{r&&console.info(`[${o()}]`,...n)},warn:(...n)=>{r&&console.warn(`[${o()}]`,...n)},error:(...n)=>{r&&console.error(`[${o()}]`,...n)}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moltbook-http-mcp",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "description": "Moltbook MCP server: post, comment, upvote, DMs, communities. API key auth.",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -32,22 +32,23 @@
32
32
  "build": "npm run build:types && npm run build:ts",
33
33
  "build:ts": "esbuild src/**/*.ts src/*.ts --outdir=dist --platform=node --minify",
34
34
  "build:types": "tsc --emitDeclarationOnly",
35
- "start": "node dist/cli.js"
35
+ "start": "node dist/cli.js --stdio=false"
36
36
  },
37
37
  "dependencies": {
38
- "@modelcontextprotocol/sdk": "^1.17.3",
38
+ "@modelcontextprotocol/sdk": "1.25.3",
39
39
  "cors": "^2.8.5",
40
- "dotenv": "^17.2.3",
41
- "express": "^5.1.0",
42
- "yargs": "^18.0.0"
40
+ "dotenv": "17.2.3",
41
+ "express": "5.1.0",
42
+ "jose": "^6.1.3",
43
+ "yargs": "18.0.0"
43
44
  },
44
45
  "devDependencies": {
45
- "@types/cors": "^2.8.19",
46
- "@types/express": "^5.0.3",
47
- "@types/node": "^24.3.0",
48
- "@types/yargs": "^17.0.33",
49
- "esbuild": "^0.25.9",
50
- "typescript": "^5.9.2"
46
+ "@types/cors": "2.8.19",
47
+ "@types/express": "5.0.3",
48
+ "@types/node": "24.3.0",
49
+ "@types/yargs": "17.0.33",
50
+ "esbuild": "0.25.9",
51
+ "typescript": "5.9.2"
51
52
  },
52
53
  "type": "module"
53
54
  }
package/dist/config.js DELETED
@@ -1 +0,0 @@
1
- "use strict";import{getAppVersion as O,getMOLTBOOK_API_KEY as o}from"./utils/env.js";export const config={APP_VERSION:O(),MOLTBOOK_API_KEY:o()};