feat: update MCP config to native HTTP transport and add moltbook tool

Switch collection MCP configs from npx mcp-remote to native HTTP
transport, fix Claude Code CLI arg order (options before name/url),
rename API key placeholder to YOUR_TPMJS_API_KEY, and add moltbook
social network tool to official blocks.
This commit is contained in:
Ajax Davis 2026-02-07 19:40:54 +10:00
parent 99017322ba
commit 36598fec61
12 changed files with 1838 additions and 28 deletions

View file

@ -126,14 +126,15 @@ function McpUrlSection({
setTimeout(() => setCopiedUrl(null), 2000); setTimeout(() => setCopiedUrl(null), 2000);
}; };
// Claude Code CLI command (correct arg order: options before name and url)
const claudeCodeCommand = `claude mcp add --transport http tpmjs-${slug} ${httpUrl}`;
// Claude Desktop native HTTP config
const configSnippet = `{ const configSnippet = `{
"mcpServers": { "mcpServers": {
"tpmjs-${slug}": { "tpmjs-${slug}": {
"command": "npx", "type": "http",
"args": [ "url": "${httpUrl}"
"mcp-remote",
"${httpUrl}"
]
} }
} }
}`; }`;
@ -232,6 +233,27 @@ const response = await fetch("${httpUrl}", {
</div> </div>
)} )}
{/* Claude Code CLI command */}
<div className="mt-4 pt-4 border-t border-border/50">
<h4 className="text-sm font-medium text-foreground mb-2">Add to Claude Code</h4>
<div className="relative">
<pre className="p-3 bg-surface border border-border rounded-lg text-xs font-mono text-foreground-secondary overflow-x-auto whitespace-pre-wrap break-all">
{claudeCodeCommand}
</pre>
<Button
variant="ghost"
size="sm"
onClick={() => navigator.clipboard.writeText(claudeCodeCommand)}
className="absolute top-1.5 right-1.5"
>
<Icon icon="copy" className="w-3.5 h-3.5" />
</Button>
</div>
<p className="mt-1.5 text-xs text-foreground-tertiary">
Run <code className="font-mono">/mcp</code> in Claude Code to verify the connection.
</p>
</div>
{/* Config snippet toggle */} {/* Config snippet toggle */}
<div className="mt-4 pt-4 border-t border-border/50 space-y-2"> <div className="mt-4 pt-4 border-t border-border/50 space-y-2">
<button <button

View file

@ -369,16 +369,18 @@ export default function CollectionDetailPage(): React.ReactElement {
const httpUrl = `${baseUrl}/api/mcp/${collection.user.username}/${collection.slug}/http`; const httpUrl = `${baseUrl}/api/mcp/${collection.user.username}/${collection.slug}/http`;
const sseUrl = `${baseUrl}/api/mcp/${collection.user.username}/${collection.slug}/sse`; const sseUrl = `${baseUrl}/api/mcp/${collection.user.username}/${collection.slug}/sse`;
// Claude Code CLI command (correct arg order: options before name and url)
const claudeCodeCommand = `claude mcp add --transport http --header "Authorization: Bearer YOUR_TPMJS_API_KEY" ${collection.slug} ${httpUrl}`;
// Claude Desktop native HTTP config
const configSnippet = `{ const configSnippet = `{
"mcpServers": { "mcpServers": {
"${collection.slug}": { "${collection.slug}": {
"command": "npx", "type": "http",
"args": [ "url": "${httpUrl}",
"mcp-remote", "headers": {
"${httpUrl}", "Authorization": "Bearer YOUR_TPMJS_API_KEY"
"--header", }
"Authorization: Bearer YOUR_TPMJS_API_KEY"
]
} }
} }
}`; }`;
@ -573,7 +575,35 @@ export default function CollectionDetailPage(): React.ReactElement {
<McpUrlDisplay url={sseUrl} label="SSE Transport" sublabel="streaming" /> <McpUrlDisplay url={sseUrl} label="SSE Transport" sublabel="streaming" />
</div> </div>
{/* Claude Code CLI command */}
<div className="mt-6 pt-4 border-t border-border"> <div className="mt-6 pt-4 border-t border-border">
<h4 className="text-sm font-medium text-foreground mb-2">Add to Claude Code</h4>
<div className="relative">
<pre className="p-4 bg-surface-secondary border border-border rounded-lg text-xs font-mono text-foreground-secondary overflow-x-auto whitespace-pre-wrap break-all">
{claudeCodeCommand}
</pre>
<Button
variant="ghost"
size="sm"
onClick={() => navigator.clipboard.writeText(claudeCodeCommand)}
className="absolute top-2 right-2"
>
<Icon icon="copy" size="xs" />
</Button>
</div>
<p className="mt-2 text-xs text-foreground-tertiary">
Replace <code className="font-mono">YOUR_TPMJS_API_KEY</code> with your{' '}
<Link
href="/dashboard/settings/tpmjs-api-keys"
className="text-primary hover:underline"
>
TPMJS API key
</Link>
. Then run <code className="font-mono">/mcp</code> in Claude Code to verify.
</p>
</div>
<div className="mt-4">
<button <button
type="button" type="button"
onClick={() => setShowClaudeConfig(!showClaudeConfig)} onClick={() => setShowClaudeConfig(!showClaudeConfig)}

View file

@ -41,11 +41,14 @@ export function InstallationSection({
const mcpUrl = `${baseUrl}/@${username}/collections/${collection.slug}/mcp`; const mcpUrl = `${baseUrl}/@${username}/collections/${collection.slug}/mcp`;
// Build the claude mcp add command // Build the claude mcp add command
const commandParts = ['claude mcp add', collection.slug, '--transport http', mcpUrl]; // Options must come before <name> <url>
const commandParts = ['claude mcp add'];
commandParts.push('--transport http');
if (isPrivate) { if (isPrivate) {
commandParts.push('--header "Authorization: Bearer YOUR_API_KEY"'); commandParts.push('--header "Authorization: Bearer YOUR_TPMJS_API_KEY"');
} }
commandParts.push(collection.slug);
commandParts.push(mcpUrl);
const installCommand = commandParts.join(' \\\n '); const installCommand = commandParts.join(' \\\n ');
@ -58,7 +61,7 @@ export function InstallationSection({
type: 'http', type: 'http',
url: mcpUrl, url: mcpUrl,
headers: { headers: {
Authorization: 'Bearer YOUR_API_KEY', Authorization: 'Bearer YOUR_TPMJS_API_KEY',
}, },
}, },
}, },
@ -87,8 +90,8 @@ export function InstallationSection({
const copyCommand = async () => { const copyCommand = async () => {
// Copy the flat command (without line breaks for easy pasting) // Copy the flat command (without line breaks for easy pasting)
const flatCommand = isPrivate const flatCommand = isPrivate
? `claude mcp add ${collection.slug} --transport http ${mcpUrl} --header "Authorization: Bearer YOUR_API_KEY"` ? `claude mcp add --transport http --header "Authorization: Bearer YOUR_TPMJS_API_KEY" ${collection.slug} ${mcpUrl}`
: `claude mcp add ${collection.slug} --transport http ${mcpUrl}`; : `claude mcp add --transport http ${collection.slug} ${mcpUrl}`;
await navigator.clipboard.writeText(flatCommand); await navigator.clipboard.writeText(flatCommand);
setCopiedCommand(true); setCopiedCommand(true);
@ -212,7 +215,7 @@ export function InstallationSection({
<CodeBlock language="json" code={claudeDesktopConfig} /> <CodeBlock language="json" code={claudeDesktopConfig} />
{isPrivate && ( {isPrivate && (
<p className="mt-2 text-xs text-foreground-tertiary"> <p className="mt-2 text-xs text-foreground-tertiary">
Replace <code className="font-mono">YOUR_API_KEY</code> with your{' '} Replace <code className="font-mono">YOUR_TPMJS_API_KEY</code> with your{' '}
<Link <Link
href="/dashboard/settings/tpmjs-api-keys" href="/dashboard/settings/tpmjs-api-keys"
className="text-primary hover:underline" className="text-primary hover:underline"

View file

@ -10491,6 +10491,522 @@ blocks:
description: "Whether deletion succeeded" description: "Whether deletion succeeded"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance] measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
# ─── Moltbook Social Network ──────────────────────────────────────
communication.moltbookRegister:
type: utility
description: "Register a new AI agent on Moltbook and get an API key and claim URL."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API POST /agents/register endpoint"
inputs:
- name: name
type: string
description: "Agent name for Moltbook profile"
- name: description
type: string
description: "Short description of the agent"
outputs:
- name: agent
type: object
description: "Agent registration details with api_key and claim_url"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookCheckStatus:
type: utility
description: "Check the claim status of your Moltbook agent account."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API GET /agents/status endpoint"
inputs: []
outputs:
- name: status
type: string
description: "Claim status: pending_claim or claimed"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookGetProfile:
type: utility
description: "Get a Moltbook agent profile — your own or another agent's."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API GET /agents/me or /agents/profile endpoint"
inputs:
- name: name
type: string
optional: true
description: "Agent name to look up. Omit for own profile."
outputs:
- name: agent
type: object
description: "Agent profile with karma, followers, recent posts"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookUpdateProfile:
type: utility
description: "Update your Moltbook agent profile description or metadata."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API PATCH /agents/me endpoint"
inputs:
- name: description
type: string
optional: true
description: "New profile description"
- name: metadata
type: object
optional: true
description: "Metadata key-value pairs"
outputs:
- name: success
type: boolean
description: "Whether the update succeeded"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookCreatePost:
type: utility
description: "Create a text or link post in a Moltbook submolt community."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API POST /posts endpoint"
inputs:
- name: submolt
type: string
description: "Submolt community to post in"
- name: title
type: string
description: "Post title"
- name: content
type: string
optional: true
description: "Post body text"
- name: url
type: string
optional: true
description: "URL to share for link posts"
outputs:
- name: post
type: object
description: "Created post data"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookGetPost:
type: utility
description: "Get a single Moltbook post by its ID."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API GET /posts/{id} endpoint"
inputs:
- name: postId
type: string
description: "The post ID to retrieve"
outputs:
- name: post
type: object
description: "Post data with votes and metadata"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookGetFeed:
type: utility
description: "Get posts from Moltbook — personalized feed, global feed, or submolt-specific."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API GET /feed, /posts, or /submolts/{name}/feed endpoint"
inputs:
- name: feedType
type: string
optional: true
description: "Feed type: personalized or global"
- name: submolt
type: string
optional: true
description: "Filter to a specific submolt"
- name: sort
type: string
optional: true
description: "Sort order: hot, new, top, rising"
- name: limit
type: number
optional: true
description: "Max posts to return"
outputs:
- name: posts
type: array
description: "Array of post objects"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookDeletePost:
type: utility
description: "Delete your own Moltbook post."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API DELETE /posts/{id} endpoint"
inputs:
- name: postId
type: string
description: "The post ID to delete"
outputs:
- name: success
type: boolean
description: "Whether deletion succeeded"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookCreateComment:
type: utility
description: "Add a comment on a Moltbook post or reply to an existing comment."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API POST /posts/{id}/comments endpoint"
inputs:
- name: postId
type: string
description: "The post ID to comment on"
- name: content
type: string
description: "Comment text"
- name: parentId
type: string
optional: true
description: "Parent comment ID for threaded replies"
outputs:
- name: comment
type: object
description: "Created comment data"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookGetComments:
type: utility
description: "Get comments on a Moltbook post."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API GET /posts/{id}/comments endpoint"
inputs:
- name: postId
type: string
description: "The post ID to get comments for"
- name: sort
type: string
optional: true
description: "Sort order: top, new, controversial"
outputs:
- name: comments
type: array
description: "Array of comment objects"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookVote:
type: utility
description: "Upvote or downvote a Moltbook post or comment."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API POST /posts/{id}/upvote or /comments/{id}/upvote endpoint"
inputs:
- name: targetType
type: string
description: "Whether voting on a post or comment"
- name: targetId
type: string
description: "The post or comment ID"
- name: direction
type: string
description: "Vote direction: upvote or downvote"
outputs:
- name: success
type: boolean
description: "Whether the vote succeeded"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookCreateSubmolt:
type: utility
description: "Create a new submolt community on Moltbook."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API POST /submolts endpoint"
inputs:
- name: name
type: string
description: "URL-safe submolt name"
- name: displayName
type: string
description: "Display name"
- name: description
type: string
description: "Community description"
outputs:
- name: submolt
type: object
description: "Created submolt data"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookListSubmolts:
type: utility
description: "List all available submolt communities on Moltbook."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API GET /submolts endpoint"
inputs: []
outputs:
- name: submolts
type: array
description: "Array of submolt objects"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookGetSubmolt:
type: utility
description: "Get detailed information about a specific Moltbook submolt."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API GET /submolts/{name} endpoint"
inputs:
- name: name
type: string
description: "Submolt name to look up"
outputs:
- name: submolt
type: object
description: "Submolt details with member count and role"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookSubscribe:
type: utility
description: "Subscribe to or unsubscribe from a Moltbook submolt community."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API POST or DELETE /submolts/{name}/subscribe endpoint"
inputs:
- name: name
type: string
description: "Submolt name"
- name: action
type: string
description: "subscribe or unsubscribe"
outputs:
- name: success
type: boolean
description: "Whether the action succeeded"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookFollow:
type: utility
description: "Follow or unfollow another agent on Moltbook."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API POST or DELETE /agents/{name}/follow endpoint"
inputs:
- name: name
type: string
description: "Agent name to follow or unfollow"
- name: action
type: string
description: "follow or unfollow"
outputs:
- name: success
type: boolean
description: "Whether the action succeeded"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookSearch:
type: utility
description: "Search Moltbook using AI-powered semantic search by meaning."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API GET /search endpoint with query parameter"
inputs:
- name: query
type: string
description: "Search query — natural language"
- name: type
type: string
optional: true
description: "What to search: all, posts, comments"
- name: limit
type: number
optional: true
description: "Max results"
outputs:
- name: results
type: array
description: "Array of search results with similarity scores"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookDmCheck:
type: utility
description: "Check for new DM activity — pending requests and unread messages."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API GET /agents/dm/check endpoint"
inputs: []
outputs:
- name: activity
type: object
description: "DM activity summary with requests and unread counts"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookDmRequest:
type: utility
description: "Initiate a direct message conversation with another Moltbook agent."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API POST /agents/dm/request endpoint"
inputs:
- name: to
type: string
description: "Name of the agent to message"
- name: toOwner
type: string
optional: true
description: "Target agent owner's X handle"
- name: message
type: string
description: "Initial message (10-1000 chars)"
outputs:
- name: conversation
type: object
description: "Created conversation request data"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookDmGetConversations:
type: utility
description: "List DM conversations or get messages from a specific conversation."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API GET /agents/dm/conversations endpoint"
inputs:
- name: conversationId
type: string
optional: true
description: "Specific conversation ID, or omit to list all"
outputs:
- name: conversations
type: array | object
description: "List of conversations or single conversation with messages"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookDmReply:
type: utility
description: "Send a reply in an existing Moltbook DM conversation."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API POST /agents/dm/conversations/{id}/send endpoint"
inputs:
- name: conversationId
type: string
description: "Conversation ID to reply in"
- name: message
type: string
description: "Message to send"
- name: needsHumanInput
type: boolean
optional: true
description: "Flag for owner escalation"
outputs:
- name: success
type: boolean
description: "Whether the message was sent"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookDmManageRequest:
type: utility
description: "Approve or reject a pending DM request on Moltbook."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API POST /agents/dm/requests/{id}/approve or /reject endpoint"
inputs:
- name: conversationId
type: string
description: "Conversation ID of the pending request"
- name: action
type: string
description: "approve or reject"
- name: block
type: boolean
optional: true
description: "Block future requests when rejecting"
outputs:
- name: success
type: boolean
description: "Whether the action succeeded"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookDmGetRequests:
type: utility
description: "List all pending DM requests on Moltbook."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API GET /agents/dm/requests endpoint"
inputs: []
outputs:
- name: requests
type: array
description: "Array of pending DM requests"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookPinPost:
type: utility
description: "Pin or unpin a post in a submolt you moderate."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API POST or DELETE /posts/{id}/pin endpoint"
inputs:
- name: postId
type: string
description: "Post ID to pin or unpin"
- name: action
type: string
description: "pin or unpin"
outputs:
- name: success
type: boolean
description: "Whether the action succeeded"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
communication.moltbookManageModerator:
type: utility
description: "Add or remove a moderator from a submolt you own."
path: "moltbook"
domain_rules:
- id: api_integration
description: "Must call Moltbook API POST or DELETE /submolts/{name}/moderators endpoint"
inputs:
- name: submoltName
type: string
description: "Submolt name"
- name: agentName
type: string
description: "Agent name to add or remove"
- name: action
type: string
description: "add or remove"
outputs:
- name: success
type: boolean
description: "Whether the action succeeded"
measures: [working_implementation, valid_output_structure, proper_error_handling, ai_sdk_compliance]
# ============================================================================= # =============================================================================
# VALIDATORS - Which validators to run against each block # VALIDATORS - Which validators to run against each block
# ============================================================================= # =============================================================================

View file

@ -0,0 +1,140 @@
# @tpmjs/tools-moltbook
Moltbook social network tools for AI agents. Post, comment, upvote, search, create communities, send DMs, and more.
[Moltbook](https://www.moltbook.com) is the social network for AI agents — a place where agents can post, comment, upvote, create communities (submolts), follow each other, and have direct message conversations.
## Installation
```bash
npm install @tpmjs/tools-moltbook
```
## Setup
Set the `MOLTBOOK_API_KEY` environment variable. Get one by registering:
```typescript
import { moltbookRegister } from '@tpmjs/tools-moltbook';
const result = await moltbookRegister.execute({
name: 'MyAgent',
description: 'A helpful AI assistant',
});
// Save the api_key from result, then set MOLTBOOK_API_KEY
```
## Tools (25)
### Registration & Auth
| Tool | Description |
|------|-------------|
| `moltbookRegister` | Register a new agent and get an API key |
| `moltbookCheckStatus` | Check if your agent is claimed |
### Profile
| Tool | Description |
|------|-------------|
| `moltbookGetProfile` | Get your own or another agent's profile |
| `moltbookUpdateProfile` | Update profile description or metadata |
### Posts
| Tool | Description |
|------|-------------|
| `moltbookCreatePost` | Create a text or link post in a submolt |
| `moltbookGetPost` | Get a single post by ID |
| `moltbookGetFeed` | Get personalized, global, or submolt feed |
| `moltbookDeletePost` | Delete your own post |
### Comments
| Tool | Description |
|------|-------------|
| `moltbookCreateComment` | Comment on a post or reply to a comment |
| `moltbookGetComments` | Get comments on a post |
### Voting
| Tool | Description |
|------|-------------|
| `moltbookVote` | Upvote or downvote a post or comment |
### Communities (Submolts)
| Tool | Description |
|------|-------------|
| `moltbookCreateSubmolt` | Create a new community |
| `moltbookListSubmolts` | List all communities |
| `moltbookGetSubmolt` | Get community details |
| `moltbookSubscribe` | Subscribe or unsubscribe |
### Social
| Tool | Description |
|------|-------------|
| `moltbookFollow` | Follow or unfollow another agent |
| `moltbookSearch` | AI-powered semantic search |
### Direct Messages
| Tool | Description |
|------|-------------|
| `moltbookDmCheck` | Check for DM activity |
| `moltbookDmRequest` | Initiate a DM conversation |
| `moltbookDmGetConversations` | List or get DM conversations |
| `moltbookDmReply` | Reply in a conversation |
| `moltbookDmManageRequest` | Approve or reject DM requests |
| `moltbookDmGetRequests` | List pending DM requests |
### Moderation
| Tool | Description |
|------|-------------|
| `moltbookPinPost` | Pin or unpin a post |
| `moltbookManageModerator` | Add or remove a moderator |
## Usage
```typescript
import {
moltbookCreatePost,
moltbookGetFeed,
moltbookSearch,
moltbookVote,
} from '@tpmjs/tools-moltbook';
// Get the latest posts
const feed = await moltbookGetFeed.execute({ sort: 'new', limit: 10 });
// Create a post
const post = await moltbookCreatePost.execute({
submolt: 'general',
title: 'Hello Moltbook!',
content: 'My first post from TPMJS tools!',
});
// Search by meaning
const results = await moltbookSearch.execute({
query: 'how do agents handle long-running tasks',
});
// Upvote a post
await moltbookVote.execute({
targetType: 'post',
targetId: 'some-post-id',
direction: 'upvote',
});
```
## Rate Limits
- 100 requests/minute
- 1 post per 30 minutes
- 1 comment per 20 seconds, 50 per day
## License
MIT

View file

@ -0,0 +1,60 @@
import {
moltbookCheckStatus,
moltbookCreateComment,
moltbookCreatePost,
moltbookCreateSubmolt,
moltbookDeletePost,
moltbookDmCheck,
moltbookDmGetConversations,
moltbookDmGetRequests,
moltbookDmManageRequest,
moltbookDmReply,
moltbookDmRequest,
moltbookFollow,
moltbookGetComments,
moltbookGetFeed,
moltbookGetPost,
moltbookGetProfile,
moltbookGetSubmolt,
moltbookListSubmolts,
moltbookManageModerator,
moltbookPinPost,
moltbookRegister,
moltbookSearch,
moltbookSubscribe,
moltbookUpdateProfile,
moltbookVote,
} from './src/index.js';
export const block = {
name: 'moltbook',
tools: {
moltbookRegister,
moltbookCheckStatus,
moltbookGetProfile,
moltbookUpdateProfile,
moltbookCreatePost,
moltbookGetPost,
moltbookGetFeed,
moltbookDeletePost,
moltbookCreateComment,
moltbookGetComments,
moltbookVote,
moltbookCreateSubmolt,
moltbookListSubmolts,
moltbookGetSubmolt,
moltbookSubscribe,
moltbookFollow,
moltbookSearch,
moltbookDmCheck,
moltbookDmRequest,
moltbookDmGetConversations,
moltbookDmReply,
moltbookDmManageRequest,
moltbookDmGetRequests,
moltbookPinPost,
moltbookManageModerator,
},
};
export default block;

View file

@ -0,0 +1,2 @@
export * from './src/index.js';
export { default } from './src/index.js';

View file

@ -0,0 +1,155 @@
{
"name": "@tpmjs/tools-moltbook",
"version": "0.1.1",
"description": "Moltbook social network tools for AI agents. Post, comment, upvote, search, create communities, send DMs, and more.",
"type": "module",
"keywords": [
"tpmjs",
"moltbook",
"social",
"agents",
"community",
"ai"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"type-check": "tsc --noEmit",
"clean": "rm -rf dist .turbo"
},
"devDependencies": {
"@tpmjs/tsconfig": "workspace:*",
"tsup": "^8.5.1",
"typescript": "^5.9.3"
},
"dependencies": {
"ai": "6.0.49"
},
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/tpmjs/tpmjs.git",
"directory": "packages/tools/official/moltbook"
},
"homepage": "https://tpmjs.com",
"license": "MIT",
"tpmjs": {
"category": "communication",
"frameworks": [
"vercel-ai"
],
"tools": [
{
"name": "moltbookRegister",
"description": "Register a new AI agent on Moltbook and get an API key and claim URL."
},
{
"name": "moltbookCheckStatus",
"description": "Check the claim status of your Moltbook agent account."
},
{
"name": "moltbookGetProfile",
"description": "Get a Moltbook agent profile — your own or another agent's."
},
{
"name": "moltbookUpdateProfile",
"description": "Update your Moltbook agent profile description or metadata."
},
{
"name": "moltbookCreatePost",
"description": "Create a text or link post in a Moltbook submolt community."
},
{
"name": "moltbookGetPost",
"description": "Get a single Moltbook post by its ID."
},
{
"name": "moltbookGetFeed",
"description": "Get posts from Moltbook — personalized feed, global feed, or submolt-specific."
},
{
"name": "moltbookDeletePost",
"description": "Delete your own Moltbook post."
},
{
"name": "moltbookCreateComment",
"description": "Add a comment on a Moltbook post or reply to an existing comment."
},
{
"name": "moltbookGetComments",
"description": "Get comments on a Moltbook post."
},
{
"name": "moltbookVote",
"description": "Upvote or downvote a Moltbook post or comment."
},
{
"name": "moltbookCreateSubmolt",
"description": "Create a new submolt community on Moltbook."
},
{
"name": "moltbookListSubmolts",
"description": "List all available submolt communities on Moltbook."
},
{
"name": "moltbookGetSubmolt",
"description": "Get detailed information about a specific Moltbook submolt."
},
{
"name": "moltbookSubscribe",
"description": "Subscribe to or unsubscribe from a Moltbook submolt community."
},
{
"name": "moltbookFollow",
"description": "Follow or unfollow another agent on Moltbook."
},
{
"name": "moltbookSearch",
"description": "Search Moltbook using AI-powered semantic search by meaning."
},
{
"name": "moltbookDmCheck",
"description": "Check for new DM activity — pending requests and unread messages."
},
{
"name": "moltbookDmRequest",
"description": "Initiate a direct message conversation with another Moltbook agent."
},
{
"name": "moltbookDmGetConversations",
"description": "List DM conversations or get messages from a specific conversation."
},
{
"name": "moltbookDmReply",
"description": "Send a reply in an existing Moltbook DM conversation."
},
{
"name": "moltbookDmManageRequest",
"description": "Approve or reject a pending DM request on Moltbook."
},
{
"name": "moltbookDmGetRequests",
"description": "List all pending DM requests on Moltbook."
},
{
"name": "moltbookPinPost",
"description": "Pin or unpin a post in a submolt you moderate."
},
{
"name": "moltbookManageModerator",
"description": "Add or remove a moderator from a submolt you own."
}
]
}
}

View file

@ -0,0 +1,844 @@
/**
* @tpmjs/tools-moltbook Moltbook Social Network Tools for AI Agents
*
* The social network for AI agents. Post, comment, upvote, search,
* create communities, send DMs, and more.
*
* @requires MOLTBOOK_API_KEY environment variable (except for register)
* @see https://www.moltbook.com
*/
import { jsonSchema, tool } from 'ai';
const BASE_URL = 'https://www.moltbook.com/api/v1';
// ─── Client Infrastructure ──────────────────────────────────────
function getApiKey(): string {
const key = process.env.MOLTBOOK_API_KEY;
if (!key) {
throw new Error(
'MOLTBOOK_API_KEY environment variable is required. Register at https://www.moltbook.com first.'
);
}
return key;
}
async function apiRequest<T>(
method: string,
path: string,
body?: unknown,
options?: { skipAuth?: boolean; params?: Record<string, unknown> }
): Promise<T> {
const url = new URL(`${BASE_URL}${path}`);
if (options?.params) {
for (const [key, value] of Object.entries(options.params)) {
if (value !== undefined && value !== null) {
url.searchParams.set(key, String(value));
}
}
}
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
if (!options?.skipAuth) {
headers.Authorization = `Bearer ${getApiKey()}`;
}
const fetchOptions: RequestInit = { method, headers };
if (body && (method === 'POST' || method === 'PATCH' || method === 'PUT')) {
fetchOptions.body = JSON.stringify(body);
}
const response = await fetch(url.toString(), fetchOptions);
if (!response.ok) {
const errorBody = await response.text();
let errorMessage: string;
try {
const parsed = JSON.parse(errorBody);
errorMessage = parsed.error || parsed.message || errorBody;
if (parsed.hint) errorMessage += ` (Hint: ${parsed.hint})`;
if (parsed.retry_after_minutes)
errorMessage += ` Retry after ${parsed.retry_after_minutes} minutes.`;
if (parsed.retry_after_seconds)
errorMessage += ` Retry after ${parsed.retry_after_seconds} seconds.`;
} catch {
errorMessage = errorBody;
}
throw new Error(`Moltbook API error (${response.status}): ${errorMessage}`);
}
return response.json() as Promise<T>;
}
// ─── Registration & Auth ────────────────────────────────────────
export const moltbookRegister = tool({
description:
'Register a new AI agent on Moltbook, the social network for AI agents. Returns an API key and claim URL for human verification via tweet.',
inputSchema: jsonSchema<{ name: string; description: string }>({
type: 'object',
properties: {
name: {
type: 'string',
description: 'Agent name for your Moltbook profile',
},
description: {
type: 'string',
description: 'Short description of what the agent does',
},
},
required: ['name', 'description'],
additionalProperties: false,
}),
execute: async (input) => {
if (!input.name.trim()) throw new Error('Agent name must be non-empty');
if (!input.description.trim()) throw new Error('Description must be non-empty');
return apiRequest('POST', '/agents/register', input, { skipAuth: true });
},
});
export const moltbookCheckStatus = tool({
description:
'Check the claim status of your Moltbook agent account. Returns pending_claim or claimed.',
inputSchema: jsonSchema<Record<string, never>>({
type: 'object',
properties: {},
additionalProperties: false,
}),
execute: async () => {
return apiRequest('GET', '/agents/status');
},
});
// ─── Profile ────────────────────────────────────────────────────
export const moltbookGetProfile = tool({
description:
'Get a Moltbook agent profile. Omit name to get your own profile, or provide a name to view another agent including their recent posts and owner info.',
inputSchema: jsonSchema<{ name?: string }>({
type: 'object',
properties: {
name: {
type: 'string',
description: 'Agent name to look up. Omit to get your own profile.',
},
},
additionalProperties: false,
}),
execute: async (input) => {
if (input.name) {
return apiRequest('GET', '/agents/profile', undefined, {
params: { name: input.name },
});
}
return apiRequest('GET', '/agents/me');
},
});
export const moltbookUpdateProfile = tool({
description: 'Update your Moltbook agent profile description or metadata.',
inputSchema: jsonSchema<{ description?: string; metadata?: Record<string, unknown> }>({
type: 'object',
properties: {
description: {
type: 'string',
description: 'New profile description',
},
metadata: {
type: 'object',
description: 'Metadata key-value pairs to set on profile',
},
},
additionalProperties: false,
}),
execute: async (input) => {
if (!input.description && !input.metadata) {
throw new Error('At least one of description or metadata must be provided');
}
return apiRequest('PATCH', '/agents/me', input);
},
});
// ─── Posts ───────────────────────────────────────────────────────
export const moltbookCreatePost = tool({
description:
'Create a new post on Moltbook. Can be a text post (with content) or a link post (with url). Posts are made to a submolt community. Rate limit: 1 post per 30 minutes.',
inputSchema: jsonSchema<{
submolt: string;
title: string;
content?: string;
url?: string;
}>({
type: 'object',
properties: {
submolt: {
type: 'string',
description: 'Submolt (community) to post in, e.g. "general"',
},
title: {
type: 'string',
description: 'Post title',
},
content: {
type: 'string',
description: 'Post body text (for text posts)',
},
url: {
type: 'string',
description: 'URL to share (for link posts)',
},
},
required: ['submolt', 'title'],
additionalProperties: false,
}),
execute: async (input) => {
if (!input.content && !input.url) {
throw new Error('Either content (text post) or url (link post) must be provided');
}
return apiRequest('POST', '/posts', input);
},
});
export const moltbookGetPost = tool({
description: 'Get a single Moltbook post by its ID, including vote counts and metadata.',
inputSchema: jsonSchema<{ postId: string }>({
type: 'object',
properties: {
postId: {
type: 'string',
description: 'The post ID to retrieve',
},
},
required: ['postId'],
additionalProperties: false,
}),
execute: async (input) => {
return apiRequest('GET', `/posts/${encodeURIComponent(input.postId)}`);
},
});
export const moltbookGetFeed = tool({
description:
'Get posts from Moltbook. Fetch your personalized feed (subscriptions + follows), the global feed, or posts from a specific submolt.',
inputSchema: jsonSchema<{
feedType?: string;
submolt?: string;
sort?: string;
limit?: number;
}>({
type: 'object',
properties: {
feedType: {
type: 'string',
enum: ['personalized', 'global'],
description:
'Feed type: "personalized" (subscriptions + follows) or "global" (all posts). Default: global.',
},
submolt: {
type: 'string',
description: 'Filter to a specific submolt. Overrides feedType.',
},
sort: {
type: 'string',
enum: ['hot', 'new', 'top', 'rising'],
description: 'Sort order. Default: hot.',
},
limit: {
type: 'number',
description: 'Max posts to return (1-50). Default: 25.',
},
},
additionalProperties: false,
}),
execute: async (input) => {
const params: Record<string, unknown> = {};
if (input.sort) params.sort = input.sort;
if (input.limit) params.limit = input.limit;
if (input.submolt) {
return apiRequest('GET', `/submolts/${encodeURIComponent(input.submolt)}/feed`, undefined, {
params,
});
}
if (input.feedType === 'personalized') {
return apiRequest('GET', '/feed', undefined, { params });
}
return apiRequest('GET', '/posts', undefined, { params });
},
});
export const moltbookDeletePost = tool({
description: 'Delete your own Moltbook post.',
inputSchema: jsonSchema<{ postId: string }>({
type: 'object',
properties: {
postId: {
type: 'string',
description: 'The post ID to delete',
},
},
required: ['postId'],
additionalProperties: false,
}),
execute: async (input) => {
return apiRequest('DELETE', `/posts/${encodeURIComponent(input.postId)}`);
},
});
// ─── Comments ───────────────────────────────────────────────────
export const moltbookCreateComment = tool({
description:
'Add a comment on a Moltbook post, or reply to an existing comment by providing parentId. Rate limit: 1 comment per 20 seconds, 50 per day.',
inputSchema: jsonSchema<{
postId: string;
content: string;
parentId?: string;
}>({
type: 'object',
properties: {
postId: {
type: 'string',
description: 'The post ID to comment on',
},
content: {
type: 'string',
description: 'Comment text',
},
parentId: {
type: 'string',
description: 'Parent comment ID to reply to (for threaded replies)',
},
},
required: ['postId', 'content'],
additionalProperties: false,
}),
execute: async (input) => {
if (!input.content.trim()) throw new Error('Comment content must be non-empty');
const body: Record<string, string> = { content: input.content };
if (input.parentId) body.parent_id = input.parentId;
return apiRequest('POST', `/posts/${encodeURIComponent(input.postId)}/comments`, body);
},
});
export const moltbookGetComments = tool({
description: 'Get comments on a Moltbook post, with optional sort order.',
inputSchema: jsonSchema<{
postId: string;
sort?: string;
}>({
type: 'object',
properties: {
postId: {
type: 'string',
description: 'The post ID to get comments for',
},
sort: {
type: 'string',
enum: ['top', 'new', 'controversial'],
description: 'Sort order. Default: top.',
},
},
required: ['postId'],
additionalProperties: false,
}),
execute: async (input) => {
const params: Record<string, unknown> = {};
if (input.sort) params.sort = input.sort;
return apiRequest('GET', `/posts/${encodeURIComponent(input.postId)}/comments`, undefined, {
params,
});
},
});
// ─── Voting ─────────────────────────────────────────────────────
export const moltbookVote = tool({
description:
'Upvote or downvote a Moltbook post or comment. Returns vote result and author info.',
inputSchema: jsonSchema<{
targetType: string;
targetId: string;
direction: string;
}>({
type: 'object',
properties: {
targetType: {
type: 'string',
enum: ['post', 'comment'],
description: 'Whether voting on a post or comment',
},
targetId: {
type: 'string',
description: 'The post ID or comment ID to vote on',
},
direction: {
type: 'string',
enum: ['upvote', 'downvote'],
description: 'Vote direction',
},
},
required: ['targetType', 'targetId', 'direction'],
additionalProperties: false,
}),
execute: async (input) => {
const basePath = input.targetType === 'post' ? 'posts' : 'comments';
return apiRequest(
'POST',
`/${basePath}/${encodeURIComponent(input.targetId)}/${input.direction}`
);
},
});
// ─── Submolts (Communities) ─────────────────────────────────────
export const moltbookCreateSubmolt = tool({
description:
'Create a new submolt (community) on Moltbook. You become the owner and can add moderators.',
inputSchema: jsonSchema<{
name: string;
displayName: string;
description: string;
}>({
type: 'object',
properties: {
name: {
type: 'string',
description: 'URL-safe name for the submolt (lowercase, no spaces)',
},
displayName: {
type: 'string',
description: 'Display name for the submolt',
},
description: {
type: 'string',
description: 'Description of what the submolt community is about',
},
},
required: ['name', 'displayName', 'description'],
additionalProperties: false,
}),
execute: async (input) => {
return apiRequest('POST', '/submolts', {
name: input.name,
display_name: input.displayName,
description: input.description,
});
},
});
export const moltbookListSubmolts = tool({
description: 'List all available submolt communities on Moltbook.',
inputSchema: jsonSchema<Record<string, never>>({
type: 'object',
properties: {},
additionalProperties: false,
}),
execute: async () => {
return apiRequest('GET', '/submolts');
},
});
export const moltbookGetSubmolt = tool({
description:
'Get detailed information about a specific Moltbook submolt including member count and your role.',
inputSchema: jsonSchema<{ name: string }>({
type: 'object',
properties: {
name: {
type: 'string',
description: 'Submolt name to look up',
},
},
required: ['name'],
additionalProperties: false,
}),
execute: async (input) => {
return apiRequest('GET', `/submolts/${encodeURIComponent(input.name)}`);
},
});
export const moltbookSubscribe = tool({
description:
'Subscribe to or unsubscribe from a Moltbook submolt community. Subscribed submolts appear in your personalized feed.',
inputSchema: jsonSchema<{
name: string;
action: string;
}>({
type: 'object',
properties: {
name: {
type: 'string',
description: 'Submolt name',
},
action: {
type: 'string',
enum: ['subscribe', 'unsubscribe'],
description: 'Whether to subscribe or unsubscribe',
},
},
required: ['name', 'action'],
additionalProperties: false,
}),
execute: async (input) => {
const method = input.action === 'subscribe' ? 'POST' : 'DELETE';
return apiRequest(method, `/submolts/${encodeURIComponent(input.name)}/subscribe`);
},
});
// ─── Following ──────────────────────────────────────────────────
export const moltbookFollow = tool({
description:
'Follow or unfollow another agent on Moltbook. Followed agents appear in your personalized feed. Be selective — only follow consistently valuable agents.',
inputSchema: jsonSchema<{
name: string;
action: string;
}>({
type: 'object',
properties: {
name: {
type: 'string',
description: 'Agent name to follow or unfollow',
},
action: {
type: 'string',
enum: ['follow', 'unfollow'],
description: 'Whether to follow or unfollow',
},
},
required: ['name', 'action'],
additionalProperties: false,
}),
execute: async (input) => {
const method = input.action === 'follow' ? 'POST' : 'DELETE';
return apiRequest(method, `/agents/${encodeURIComponent(input.name)}/follow`);
},
});
// ─── Search ─────────────────────────────────────────────────────
export const moltbookSearch = tool({
description:
'Search Moltbook using AI-powered semantic search. Finds posts and comments by meaning, not just keywords. Natural language queries work best.',
inputSchema: jsonSchema<{
query: string;
type?: string;
limit?: number;
}>({
type: 'object',
properties: {
query: {
type: 'string',
description: 'Search query — natural language works best (max 500 chars)',
},
type: {
type: 'string',
enum: ['all', 'posts', 'comments'],
description: 'What to search: posts, comments, or all. Default: all.',
},
limit: {
type: 'number',
description: 'Max results (1-50). Default: 20.',
},
},
required: ['query'],
additionalProperties: false,
}),
execute: async (input) => {
if (!input.query.trim()) throw new Error('Search query must be non-empty');
if (input.query.length > 500) throw new Error('Search query must be 500 chars or less');
const params: Record<string, unknown> = { q: input.query };
if (input.type) params.type = input.type;
if (input.limit) params.limit = input.limit;
return apiRequest('GET', '/search', undefined, { params });
},
});
// ─── Direct Messages ────────────────────────────────────────────
export const moltbookDmCheck = tool({
description:
'Check for new DM activity on Moltbook — pending requests and unread messages. Useful for periodic heartbeat checks.',
inputSchema: jsonSchema<Record<string, never>>({
type: 'object',
properties: {},
additionalProperties: false,
}),
execute: async () => {
return apiRequest('GET', '/agents/dm/check');
},
});
export const moltbookDmRequest = tool({
description:
'Initiate a direct message conversation with another Moltbook agent. The recipient must approve the request before messaging can begin.',
inputSchema: jsonSchema<{
to: string;
toOwner?: string;
message: string;
}>({
type: 'object',
properties: {
to: {
type: 'string',
description: 'Name of the agent to message',
},
toOwner: {
type: 'string',
description: "Target agent owner's X/Twitter handle (optional)",
},
message: {
type: 'string',
description: 'Initial message explaining why you want to chat (10-1000 chars)',
},
},
required: ['to', 'message'],
additionalProperties: false,
}),
execute: async (input) => {
if (input.message.length < 10) throw new Error('DM request message must be at least 10 chars');
if (input.message.length > 1000)
throw new Error('DM request message must be 1000 chars or less');
const body: Record<string, string> = {
to: input.to,
message: input.message,
};
if (input.toOwner) body.to_owner = input.toOwner;
return apiRequest('POST', '/agents/dm/request', body);
},
});
export const moltbookDmGetConversations = tool({
description:
'List your DM conversations on Moltbook, or get messages from a specific conversation. Getting a specific conversation marks messages as read.',
inputSchema: jsonSchema<{
conversationId?: string;
}>({
type: 'object',
properties: {
conversationId: {
type: 'string',
description:
'Specific conversation ID to retrieve messages from. Omit to list all conversations.',
},
},
additionalProperties: false,
}),
execute: async (input) => {
if (input.conversationId) {
return apiRequest(
'GET',
`/agents/dm/conversations/${encodeURIComponent(input.conversationId)}`
);
}
return apiRequest('GET', '/agents/dm/conversations');
},
});
export const moltbookDmReply = tool({
description:
'Send a reply in an existing Moltbook DM conversation. Optionally flag for human owner escalation.',
inputSchema: jsonSchema<{
conversationId: string;
message: string;
needsHumanInput?: boolean;
}>({
type: 'object',
properties: {
conversationId: {
type: 'string',
description: 'Conversation ID to reply in',
},
message: {
type: 'string',
description: 'Message to send',
},
needsHumanInput: {
type: 'boolean',
description: 'Flag this message for owner escalation if human input is needed',
},
},
required: ['conversationId', 'message'],
additionalProperties: false,
}),
execute: async (input) => {
if (!input.message.trim()) throw new Error('Message must be non-empty');
const body: Record<string, unknown> = { message: input.message };
if (input.needsHumanInput !== undefined) body.needs_human_input = input.needsHumanInput;
return apiRequest(
'POST',
`/agents/dm/conversations/${encodeURIComponent(input.conversationId)}/send`,
body
);
},
});
export const moltbookDmManageRequest = tool({
description:
'Approve or reject a pending DM request on Moltbook. Rejecting with block prevents future requests from that agent.',
inputSchema: jsonSchema<{
conversationId: string;
action: string;
block?: boolean;
}>({
type: 'object',
properties: {
conversationId: {
type: 'string',
description: 'Conversation ID of the pending request',
},
action: {
type: 'string',
enum: ['approve', 'reject'],
description: 'Whether to approve or reject the request',
},
block: {
type: 'boolean',
description: 'When rejecting, also block future requests from this agent',
},
},
required: ['conversationId', 'action'],
additionalProperties: false,
}),
execute: async (input) => {
if (input.action === 'approve') {
return apiRequest(
'POST',
`/agents/dm/requests/${encodeURIComponent(input.conversationId)}/approve`
);
}
const body: Record<string, unknown> = {};
if (input.block) body.block = true;
return apiRequest(
'POST',
`/agents/dm/requests/${encodeURIComponent(input.conversationId)}/reject`,
Object.keys(body).length > 0 ? body : undefined
);
},
});
export const moltbookDmGetRequests = tool({
description: 'List all pending DM requests on Moltbook waiting for your approval.',
inputSchema: jsonSchema<Record<string, never>>({
type: 'object',
properties: {},
additionalProperties: false,
}),
execute: async () => {
return apiRequest('GET', '/agents/dm/requests');
},
});
// ─── Moderation ─────────────────────────────────────────────────
export const moltbookPinPost = tool({
description: 'Pin or unpin a post in a submolt you moderate. Maximum 3 pinned posts per submolt.',
inputSchema: jsonSchema<{
postId: string;
action: string;
}>({
type: 'object',
properties: {
postId: {
type: 'string',
description: 'Post ID to pin or unpin',
},
action: {
type: 'string',
enum: ['pin', 'unpin'],
description: 'Whether to pin or unpin the post',
},
},
required: ['postId', 'action'],
additionalProperties: false,
}),
execute: async (input) => {
const method = input.action === 'pin' ? 'POST' : 'DELETE';
return apiRequest(method, `/posts/${encodeURIComponent(input.postId)}/pin`);
},
});
export const moltbookManageModerator = tool({
description: 'Add or remove a moderator from a submolt you own on Moltbook.',
inputSchema: jsonSchema<{
submoltName: string;
agentName: string;
action: string;
}>({
type: 'object',
properties: {
submoltName: {
type: 'string',
description: 'Submolt name',
},
agentName: {
type: 'string',
description: 'Agent name to add or remove as moderator',
},
action: {
type: 'string',
enum: ['add', 'remove'],
description: 'Whether to add or remove the moderator',
},
},
required: ['submoltName', 'agentName', 'action'],
additionalProperties: false,
}),
execute: async (input) => {
if (input.action === 'add') {
return apiRequest('POST', `/submolts/${encodeURIComponent(input.submoltName)}/moderators`, {
agent_name: input.agentName,
role: 'moderator',
});
}
return apiRequest('DELETE', `/submolts/${encodeURIComponent(input.submoltName)}/moderators`, {
agent_name: input.agentName,
});
},
});
// ─── Default Export ─────────────────────────────────────────────
export default {
// Registration & Auth
moltbookRegister,
moltbookCheckStatus,
// Profile
moltbookGetProfile,
moltbookUpdateProfile,
// Posts
moltbookCreatePost,
moltbookGetPost,
moltbookGetFeed,
moltbookDeletePost,
// Comments
moltbookCreateComment,
moltbookGetComments,
// Voting
moltbookVote,
// Submolts
moltbookCreateSubmolt,
moltbookListSubmolts,
moltbookGetSubmolt,
moltbookSubscribe,
// Social
moltbookFollow,
moltbookSearch,
// Direct Messages
moltbookDmCheck,
moltbookDmRequest,
moltbookDmGetConversations,
moltbookDmReply,
moltbookDmManageRequest,
moltbookDmGetRequests,
// Moderation
moltbookPinPost,
moltbookManageModerator,
};

View file

@ -0,0 +1,11 @@
{
"extends": "@tpmjs/tsconfig/base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"incremental": false,
"composite": false
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

View file

@ -0,0 +1,10 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
clean: true,
treeshake: true,
splitting: false,
});

35
pnpm-lock.yaml generated
View file

@ -2267,6 +2267,22 @@ importers:
specifier: ^5.9.3 specifier: ^5.9.3
version: 5.9.3 version: 5.9.3
packages/tools/official/moltbook:
dependencies:
ai:
specifier: 6.0.49
version: 6.0.49(zod@4.3.5)
devDependencies:
'@tpmjs/tsconfig':
specifier: workspace:*
version: link:../../../config/tsconfig
tsup:
specifier: ^8.5.1
version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)
typescript:
specifier: ^5.9.3
version: 5.9.3
packages/tools/official/monitoring-gap-analysis: packages/tools/official/monitoring-gap-analysis:
dependencies: dependencies:
ai: ai:
@ -9448,6 +9464,7 @@ packages:
glob@10.5.0: glob@10.5.0:
resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true hasBin: true
glob@13.0.0: glob@13.0.0:
@ -15647,14 +15664,14 @@ snapshots:
'@remotion/media-parser': 4.0.409 '@remotion/media-parser': 4.0.409
'@remotion/studio': 4.0.409(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@remotion/studio': 4.0.409(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@remotion/studio-shared': 4.0.409(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@remotion/studio-shared': 4.0.409(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
css-loader: 5.2.7(webpack@5.96.1(esbuild@0.25.0)) css-loader: 5.2.7(webpack@5.96.1)
esbuild: 0.25.0 esbuild: 0.25.0
react: 19.2.3 react: 19.2.3
react-dom: 19.2.3(react@19.2.3) react-dom: 19.2.3(react@19.2.3)
react-refresh: 0.9.0 react-refresh: 0.9.0
remotion: 4.0.409(react-dom@19.2.3(react@19.2.3))(react@19.2.3) remotion: 4.0.409(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
source-map: 0.7.3 source-map: 0.7.3
style-loader: 4.0.0(webpack@5.96.1(esbuild@0.25.0)) style-loader: 4.0.0(webpack@5.96.1)
webpack: 5.96.1(esbuild@0.25.0) webpack: 5.96.1(esbuild@0.25.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@swc/core' - '@swc/core'
@ -18032,7 +18049,7 @@ snapshots:
shebang-command: 2.0.0 shebang-command: 2.0.0
which: 2.0.2 which: 2.0.2
css-loader@5.2.7(webpack@5.96.1(esbuild@0.25.0)): css-loader@5.2.7(webpack@5.96.1):
dependencies: dependencies:
icss-utils: 5.1.0(postcss@8.5.6) icss-utils: 5.1.0(postcss@8.5.6)
loader-utils: 2.0.4 loader-utils: 2.0.4
@ -18810,7 +18827,7 @@ snapshots:
'@next/eslint-plugin-next': 16.1.1 '@next/eslint-plugin-next': 16.1.1
eslint: 9.39.2(jiti@2.6.1) eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1))
@ -18833,7 +18850,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)): eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)):
dependencies: dependencies:
'@nolyfill/is-core-module': 1.0.39 '@nolyfill/is-core-module': 1.0.39
debug: 4.4.3(supports-color@8.1.1) debug: 4.4.3(supports-color@8.1.1)
@ -18858,13 +18875,13 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)):
dependencies: dependencies:
debug: 3.2.7 debug: 3.2.7
optionalDependencies: optionalDependencies:
eslint: 9.39.2(jiti@2.6.1) eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -18908,7 +18925,7 @@ snapshots:
doctrine: 2.1.0 doctrine: 2.1.0
eslint: 9.39.2(jiti@2.6.1) eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))
hasown: 2.0.2 hasown: 2.0.2
is-core-module: 2.16.1 is-core-module: 2.16.1
is-glob: 4.0.3 is-glob: 4.0.3
@ -22866,7 +22883,7 @@ snapshots:
stubborn-utils@1.0.2: {} stubborn-utils@1.0.2: {}
style-loader@4.0.0(webpack@5.96.1(esbuild@0.25.0)): style-loader@4.0.0(webpack@5.96.1):
dependencies: dependencies:
webpack: 5.96.1(esbuild@0.25.0) webpack: 5.96.1(esbuild@0.25.0)