Skip to main content
Glama
joaquinsoza

Remote MCP Server on Cloudflare

by joaquinsoza
index.ts4.83 kB
import { McpAgent } from "agents/mcp"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { Hono } from "hono"; import { layout, homeContent } from "./utils"; import * as xApi from "./x-api"; type Bindings = Env; const app = new Hono<{ Bindings: Bindings; }>(); type Props = { bearerToken: string; }; type State = null; export class MyMCP extends McpAgent<Bindings, State, Props> { server = new McpServer({ name: "Demo", version: "1.0.0", }); async init() { // Get user profile tool this.server.tool( "getUserProfile", "Get a user's profile information on twitter or x.com", { username: z.string(), }, async ({ username }) => { try { const user = await xApi.getUserProfile( this.env, username ); console.log("🚀 | user:", user) return { content: [ { type: "text", text: `User Profile for @${username}: Name: ${user.name} Description: ${user.description} Followers: ${user.public_metrics?.followers_count} Following: ${user.public_metrics?.following_count} Tweets: ${user.public_metrics?.tweet_count}`, }, ], }; } catch (error: any) { return { content: [ { type: "text", text: `Error fetching user profile: ${error.message}`, }, ], }; } } ); // Post tweet tool this.server.tool( "postTweet", "Post a new tweet on x.com", { text: z.string().max(280), }, async ({ text }) => { try { const tweet = await xApi.postTweet(this.env,text); return { content: [ { type: "text", text: `Tweet posted successfully! Tweet ID: ${tweet.id}`, }, ], }; } catch (error: any) { return { content: [ { type: "text", text: `Error posting tweet: ${error.message}`, }, ], }; } } ); // Search tweets tool this.server.tool( "searchTweets", "Search for recent tweets on x.com", { query: z.string(), count: z.number().min(10).max(100).default(10), }, async ({ query, count }) => { try { const { tweets, users } = await xApi.searchTweets( this.env, query, count ); // const formattedTweets = tweets.map((tweet, index) => { // const author = users.find((u) => u.id === tweet.authorId); // return { // position: index + 1, // author: { // username: author?.username || "unknown", // }, // content: tweet.text, // metrics: tweet.metrics, // url: `https://twitter.com/${author?.username}/status/${tweet.id}`, // }; // }); // return { // content: [ // { // type: "text", // text: `Found ${ // tweets.length // } tweets for query "${query}":\n\n${formattedTweets // .map( // (t) => // `${t.position}. @${t.author.username}: ${t.content}\n` + // ` Likes: ${t.metrics.likes}, RTs: ${t.metrics.retweets}, Replies: ${t.metrics.replies}\n` + // ` URL: ${t.url}\n` // ) // .join("\n")}`, // }, // ], // }; return { content: [tweets, users] }; } catch (error: any) { return { content: [ { type: "text", text: `Error searching tweets: ${error.message}`, }, ], }; } } ); } } // Render a basic homepage placeholder to make sure the app is up app.get("/", async (c) => { const content = await homeContent(c.req.raw); return c.html(layout(content, "X.com MCP Server")); }); app.mount("/", (req, env, ctx) => { // This could technically be pulled out into a middleware function, but is left here for clarity const authHeader = req.headers.get("authorization"); if (!authHeader) { return new Response("Unauthorized", { status: 401 }); } ctx.props = { bearerToken: authHeader, // could also add arbitrary headers/parameters here to pass into the MCP client }; return MyMCP.mount("/sse").fetch(req, env, ctx); }); export default app;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/joaquinsoza/x-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server