Estoy bastante adicto a la serie The Last of Us.Lo siento por la portada.
MCPs están en todas partes y por una buena razón.Es el siguiente paso en la evolución de las aplicaciones.
Ser capaz de usar todo desde un solo chat sin acceder a ninguna aplicación.Postalespara programar todas sus publicaciones sociales desde el chat! ¡Así, empecé a cavar en el código de Postiz y añadió a él!
El repositorio de MCP es un poco extraño
Cada MCP tiene un transporte, que es el método que los LLM utilizan para hablar con nuestro sistema.
There are two primary methods at the momentStdio, que es básicamente una línea de comandos, y SSE.
No entiendo realmente por qué eligieron SSE: es básicamente una solicitud larga que nunca termina y transmite eventos al cliente.
El problema con este método es que para enviar información de vuelta al servidor, debe enviar otra solicitud de correo (ya que SSE es una comunicación unidireccional), lo que significa que debe mantener el estado.
En su ejemplo, mantienen el estado en la memoria de la aplicación, y adivinan qué?Muchos se quejan de fugas de memoria porque el estado no se borra cuando el usuario se desconecta.
Usaría WebSockets. Tienen un modo de sueño integrado, y no tiene que mantener un estado para ello.
Digamos en
He cavado en el SDK de tipografía antropoide y no me sorprendió. Se siente clunky. Muchas cosas no se están utilizando en la producción, como "Recursos". La forma en que requieren que mantenga todo en la memoria global es un desastre que espera que suceda.
Además, es difícil implementar la autenticación y sacar al usuario del contexto para que podamos obtener sus detalles.
Postiz está construido con NestJS, por lo que cuando se utiliza una ruta SSE, cierra la observable una vez que se desconecta, lo que le permite eliminar todo de la memoria.
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 {};
}
}
Decoradores ️
Esto es para ti si eres un gran fan de frameworks OOP como NestJS/Laravel/Spring. Creé un decorador cool para crear herramientas como API "endpoints".
@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}`,
},
];
}
Todo el código se puede encontrar en Postiz aquí:https://github.com/gitroomhq/postiz-app/tree/main/libraries/nestjs-libraries/src/mcp
y aquí:https://github.com/gitroomhq/postiz-app/tree/main/apps/backend/src/mcp
El LLM tiene que hacer las cosas.
Sería bueno tener una opción integrada para forzar al LLM a hacer cosas diferentes antes de acceder a nuestras cosas.
Cada vez que le dije a Cursor que programara un post para mí, trataba de programarlo para 2024.
Necesitaba pasar algunos detalles de config, así que creé elPOSTIZ_CONFIGURATION_PRERUN
Esperemos que el LLM siempre lo llame antes de hacer las cosas.
Pero lo ignoró muchas veces (típico), así que tuve que ser creativo.POSTIZ_SCHEDULE_POST
, añadió una nueva propiedad llamadaconfigId
y cambió el nombre de la herramienta config aPOSTIZ_GET_CONFIG_ID.
El resultado de la config es:id: ${makeId(10)} Today date is ${dayjs.utc().format()}
Esto obligó al LLM a llamarlo siempre antes, y la fecha estaba fijada! :)
Fue aún mejor para mí porque sabía que me enviaría fechas UTC a partir de ahora.
Casos de uso
Creo que funciona mejor cuando se combina con varios conjuntos de herramientas, por ejemplo:
- y
- Conéctelo a Cursor y pídele que programa una publicación sobre su trabajo hoy. y
- Conecte a Notion y pídele que planifique todo el trabajo más reciente del equipo en las redes sociales - compruebe los MCP de Composio. y
- Connect it to any SaaS that has CopilotKit and schedule posts based on the app. y
Página de MCP
Postaleses la herramienta de programación de redes sociales de código abierto más robusta - y ahora el único programador que ofrece MCP (nativamente, no con Zapier o algo así)
Con el nuevo MCP, puedes programar todas tus publicaciones desde los clientes Cursor/Windsurf y Anthropic.
Todo es 100% gratis :)
Si te gusta, por favor no te olvides de estrellarnos ⭐️HTTPS://github.com/gitroomhq/postiz-app