你最后一个 MCP,用来安排你所有的社交帖子!🤯

2025-05-28

你最后一个 MCP,用来安排你所有的社交帖子!🤯

MCP代表未来

我超爱《最后生还者》系列,不好意思封面设计 😉
MCP 无处不在,而且是有原因的。
它是应用进化的下一步。

无需访问任何应用程序,只需一个聊天窗口即可使用所有功能。Postiz 能通过聊天窗口安排你所有的社交帖子,
感觉就像原生应用一样! 于是,我开始深入研究 Postiz 的代码,并把它添加到聊天窗口!

MCPE


MCP 仓库有点奇怪🧐

每个 MCP 都有一个传输,这是 LLM 用来与我们的系统通信的方法。

目前有两种主要方法:Stdio(基本上是一个命令行)和 SSE。

我不太明白他们为什么选择 SSE——它基本上是一个永无止境的长请求,并将事件流式传输到客户端。

这种方法的问题在于,要将信息发送回服务器,您必须发送另一个 post 请求(因为 SSE 是单向通信),这意味着您必须保持状态。

在他们的例子中,他们将状态保存在应用程序的内存中,你猜怎么着?许多人抱怨内存泄漏,因为当用户断开连接时状态不会被清除。

我会使用 WebSocket。它们内置睡眠模式,无需维护状态。


深入挖掘🪠

我深入研究了 Anthropic TypeScript SDK,并没有感到惊讶。
它感觉很笨重。很多东西在生产环境中根本用不到,比如“资源”。他们要求你把所有东西都全局保存在内存中,这简直是一场灾难。

此外,实现身份验证并从上下文中获取用户以便我们可以获取他们的详细信息也很困难。

我使用 rxjs 的可观察对象实现了自己的“传输”功能——这很有趣。Postiz 是基于 NestJS 构建的,因此在使用 SSE 路由时,一旦断开连接,它就会关闭可观察对象,这样你就可以从内存中删除所有内容了。

import EventEmitter from 'events';
import { finalize, fromEvent, startWith } from 'rxjs';

@Injectable()
export class McpService {
  static event = new EventEmitter();
  constructor(
    private _mainMcp: MainMcp
  ) {
  }

  async runServer(apiKey: string, organization: string) {
    const server = McpSettings.load(organization, this._mainMcp).server();
    const transport = new McpTransport(organization);

    const observer = fromEvent(
      McpService.event,
      `organization-${organization}`
    ).pipe(
      startWith({
        type: 'endpoint',
        data: process.env.NEXT_PUBLIC_BACKEND_URL + '/mcp/' + apiKey + '/messages',
      }),
      finalize(() => {
        transport.close();
      })
    );

    console.log('MCP transport started');
    await server.connect(transport);

    return observer;
  }

  async processPostBody(organization: string, body: object) {
    const server = McpSettings.load(organization, this._mainMcp).server();
    const message = JSONRPCMessageSchema.parse(body);
    const transport = new McpTransport(organization);
    await server.connect(transport);
    transport.handlePostMessage(message);
    return {};
  }
}
Enter fullscreen mode Exit fullscreen mode

装饰器 FTW🎖️

如果您是 NestJS / Laravel / Spring 等 OOP 框架的忠实粉丝,那么这篇文章非常适合您。我创建了一个很酷的装饰器来创建类似 API“端点”的工具。

  @McpTool({ toolName: 'POSTIZ_GET_CONFIG_ID' })
  async preRun() {
    return [
      {
        type: 'text',
        text: `id: ${makeId(10)} Today date is ${dayjs.utc().format()}`,
      },
    ];
  }

  @McpTool({ toolName: 'POSTIZ_PROVIDERS_LIST' })
  async listOfProviders(organization: string) {
    const list = (
      await this._integrationService.getIntegrationsList(organization)
    ).map((org) => ({
      id: org.id,
      name: org.name,
      identifier: org.providerIdentifier,
      picture: org.picture,
      disabled: org.disabled,
      profile: org.profile,
      customer: org.customer
        ? {
            id: org.customer.id,
            name: org.customer.name,
          }
        : undefined,
    }));

    return [{ type: 'text', text: JSON.stringify(list) }];
  }

@McpTool({
    toolName: 'POSTIZ_SCHEDULE_POST',
    zod: {
      type: eenum(['draft', 'scheduled']),
      configId: string(),
      generatePictures: boolean(),
      date: string().describe('UTC TIME'),
      providerId: string().describe('Use POSTIZ_PROVIDERS_LIST to get the id'),
      posts: array(object({ text: string(), images: array(string()) })),
    },
  })
  async schedulePost(
    organization: string,
    obj: {
      type: 'draft' | 'schedule';
      generatePictures: boolean;
      date: string;
      providerId: string;
      posts: { text: string }[];
    }
  ) {
    const create = await this._postsService.createPost(organization, {
      date: obj.date,
      type: obj.type,
      tags: [],
      posts: [
        {
          group: makeId(10),
          value: await Promise.all(
            obj.posts.map(async (post) => ({
              content: post.text,
              id: makeId(10),
              image: !obj.generatePictures
                ? []
                : [
                    {
                      id: makeId(10),
                      path: await this._openAiService.generateImage(
                        post.text,
                        true
                      ),
                    },
                  ],
            }))
          ),
          // @ts-ignore
          settings: {},
          integration: {
            id: obj.providerId,
          },
        },
      ],
    });

    return [
      {
        type: 'text',
        text: `Post created successfully, check it here: ${process.env.FRONTEND_URL}/p/${create[0].postId}`,
      },
    ];
  }
Enter fullscreen mode Exit fullscreen mode

所有代码都可以在 Postiz 中找到:
https://github.com/gitroomhq/postiz-app/tree/main/libraries/nestjs-libraries/src/mcp

这里:
https://github.com/gitroomhq/postiz-app/tree/main/apps/backend/src/mcp


强迫法学硕士 (LLM) 做事💪🏻

如果有一个内置选项可以强制 LLM 在访问我们的内容之前执行不同的事情,那就太好了。

我遇到了一些有趣的问题。每当我告诉 Cursor 为我安排帖子发布时,它都会尝试将其安排到 2024 年。这是该模型最后一次训练。

我需要传递一些配置信息,所以我创建了这个POSTIZ_CONFIGURATION_PRERUN工具。希望LLM在执行操作之前总是会调用它。

但它多次忽略了它(很常见),所以我不得不发挥创造力。
在我的配置中POSTIZ_SCHEDULE_POST,我添加了一个名为 的新属性configId,并将配置工具名称更改为POSTIZ_GET_CONFIG_ID,配置的输出如下:
id: ${makeId(10)} Today date is ${dayjs.utc().format()}

它迫使 LLM 总是提前打电话,并且日期是固定的!:)

这对我来说更好,因为我知道从现在开始它会向我发送 UTC 日期。


用例

我认为它与多套工具结合使用时效果最佳,例如:

  • 将其连接到 Cursor 并要求它安排有关您今天工作的帖子。

  • 将其连接到 Notion 并要求在社交媒体上安排团队的所有最新工作 - 查看Composio MCP

  • 将其连接到任何具有CopilotKit的 SaaS并根据应用程序安排帖子。


Postiz MCP

Postiz是最强大的开源社交媒体调度工具 - 并且现在是唯一提供 MCP 的调度程序(原生提供,而不是通过 Zapier 或类似的东西)

使用新的 MCP,您可以安排来自 Cursor / Windsurf 和 A​​nthropic 客户端的所有帖子。

当然,一切都是100%免费的:)

如果您喜欢它,请不要忘记给我们加星⭐️
https://github.com/gitroomhq/postiz-app

原始

文章来源:https://dev.to/nevodavid/your-last-mcp-to-schedule-all-your-social-posts-al4
PREV
使用内存数据库测试 Node.js + Mongoose 内存数据库的优缺点让我们开始编码吧!
NEXT
我的 React 面试题集锦(第一部分)+10 道 React 面试题,直接来自我的库